/* Shared styles for tomdegay.com
   Rules that every page renders identically. Anything a page styles
   differently -- or that no other page has -- stays in that page's own
   <style> block, which loads after this file. */

@font-face {
  font-family: "Fakt Blond SemiBold";
  src: url("/FaktProSemiBold.woff2") format("woff2"),
       url("/FaktProSemiBold.woff") format("woff");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Fakt Blond";
  src: url("/FaktProBlond.woff2") format("woff2"),
       url("/FaktProBlond.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "FaktPro-SemiBold";
  src: url("/FaktProSemiBold.woff2") format("woff2"),
       url("/FaktProSemiBold.woff") format("woff");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

:root { color-scheme: dark; }

/* Every <img> carries its intrinsic width/height so the browser can reserve
   the right box before the file arrives. Those attributes are presentational
   hints for the width and height PROPERTIES, though, not just an aspect ratio
   -- so without this the attribute height would win wherever CSS sets a width
   and leaves height alone, squashing the image and defeating aspect-ratio.
   No rule anywhere sets a height on an img selector, so this only ever
   overrides the attribute. */
img { height: auto; }

::selection {
  background: rgba(0, 216, 170, 0.28);
  color: #fff;
}

/* Cross-page fade. Every page opts in here, which browsers require of
   both ends of a navigation; nav.js then skips it unless one end is a
   light-themed page, where the jump from black would be jarring.
   Browsers without cross-document view transitions just navigate. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-old(root),
  ::view-transition-new(root) { animation-duration: 450ms; animation-timing-function: ease; }
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: #131313;
}

.display {
  font-family: "Fakt Blond SemiBold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 600;
}

a {
  color: #fff;
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 255, 255, 0.22);
  padding-bottom: 1px;
  transition: border-color 0.15s ease, opacity 0.15s ease;
}

a:hover, a:focus-visible { border-color: rgba(255, 255, 255, 0.9); }

a:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.6);
  outline-offset: 4px;
  border-radius: 1px;
}

a[target="_blank"], .site-footer-links a { position: relative; }

a[target="_blank"]::after, .site-footer-links a::after {
  content: "→";
  position: absolute;
  right: -1.1em;
  top: 50%;
  transform: translate(-4px, -50%);
  opacity: 0;
  transition: transform 0.18s ease, opacity 0.18s ease;
  pointer-events: none;
  will-change: transform, opacity;
}

a[target="_blank"]:hover::after, a[target="_blank"]:focus-visible::after,
.site-footer-links a:hover::after, .site-footer-links a:focus-visible::after {
  opacity: 1;
  transform: translate(0, -50%);
}

/* -- suppress the external-link arrow inside running text -- */
.about-copy a[target="_blank"]::after, .site-note a[target="_blank"]::after {
  content: none;
}

.intro {
  color: rgba(255, 255, 255, 0.82);
  font-size: 18px;
  line-height: 1.6;
  margin: 0;
  max-width: 620px;
}

.hero-video { margin-bottom: clamp(32px, 5vw, 48px); }

.hero-video video {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 8px;
}

@media (max-width: 640px) {
  .hero-video {
    position: relative;
    left: 50%;
    width: 100vw;
    margin-left: -50vw;
    aspect-ratio: 838 / 1010;
    overflow: hidden;
  }

  .hero-video video {
    position: absolute;
    width: 190.93vw;
    height: auto;
    max-width: none;
    left: -13.13vw;
    top: 0;
    border-radius: 0;
  }
}

.job { margin: 0 0 8px; }

.job-company {
  color: #fff;
  font-size: clamp(20px, 2.8vw, 26px);
  line-height: 1.2;
}

.job-keywords {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 16px 0 0;
}

hr.rule:has(+ .back-to-top-row) { margin-bottom: clamp(8px, 1.2vw, 14px); }

.case-footer { margin-top: clamp(48px, 7vw, 76px); }

.back-to-top-row {
  max-width: 46rem;
  margin: 0;
  text-align: right;
}

.back-to-top {
  font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  border-bottom: none;
  white-space: nowrap;
}

.back-to-top:hover, .back-to-top:focus-visible {
  color: #fff;
  border-bottom: none;
}

.case-footer .eyebrow { margin-top: 0; }

.case-footer .case-block { margin-top: 0; }

.contact-header {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: clamp(32px, 5vw, 48px);
}

.about-photo {
  display: block;
  width: 100%;
  max-width: 620px;
  min-width: 0;
  flex: 1 1 320px;
  border-radius: 12px;
}

.about-copy p {
  color: rgba(255, 255, 255, 0.82);
  font-size: clamp(18px, 2.3vw, 22px);
  line-height: 33px;
  margin: 0 0 clamp(26px, 3.4vw, 34px);
  max-width: 620px;
}

.about-copy p:last-child { margin-bottom: 0; }

.about-copy a {
  color: #fff;
  border-bottom-color: rgba(255, 255, 255, 0.4);
}

.about-copy a:hover, .about-copy a:focus-visible {
  border-color: rgba(255, 255, 255, 0.9);
}

/* -- Site footer: copyright left, links right. footer.js appends it as a
   sibling straight after <main>, not inside it, so it can share the nav
   bar's own width formula here instead of each page's own (different)
   main max-width -- consistent everywhere rather than per-page. The
   padding-bottom replaces the page-bottom whitespace main's own bottom
   padding used to provide back when the footer lived inside it.

   .site-footer mirrors .site-nav-inner's own box model (max-width, then
   padding inside it) so its children land on the same inset as the nav's
   brand/links. The divider hr has no children to inset via padding --
   a border-top renders at the padding box's OUTER edge regardless of
   padding, so it needs its max-width computed as the nav's content width
   directly (min(cap, 100%) minus the gutter twice), not the nav's own
   outer cap. min()/100% (not a bare 1500px) matters here: below that
   width .site-nav-inner is itself capped by the viewport, not 1500px,
   so hardcoding 1500 would overshoot the nav's actual content width at
   1024-1500px viewports specifically -- confirmed in the browser, not
   assumed. Uses the same small-print scale as .site-note. -- */
.site-footer {
  max-width: 900px;
  margin-left: auto;
  margin-right: auto;
  padding-left: clamp(24px, 5vw, 56px);
  padding-right: clamp(24px, 5vw, 56px);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px 28px;
  margin-top: clamp(24px, 3.5vw, 32px);
  padding-bottom: clamp(48px, 6vw, 70px);
}

hr.rule:has(+ .site-footer) {
  max-width: calc(min(900px, 100%) - 2 * clamp(24px, 5vw, 56px));
  margin-left: auto;
  margin-right: auto;
}

@media (min-width: 1024px) {
  .site-footer {
    max-width: 1500px;
    padding-left: clamp(32px, 4vw, 72px);
    padding-right: clamp(32px, 4vw, 72px);
  }

  hr.rule:has(+ .site-footer) {
    max-width: calc(min(1500px, 100%) - 2 * clamp(32px, 4vw, 72px));
  }
}

.site-footer p { margin: 0; }

.site-footer-copy {
  color: rgba(255, 255, 255, 0.5);
  font-size: clamp(14px, 1.6vw, 15px);
  line-height: 1.65;
}

.site-footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 30px;
  font-size: clamp(14px, 1.6vw, 15px);
  line-height: 1.65;
}

/* A real underline rather than the site's usual border-bottom: the border sits
   under the whole inline box, which leaves ~5.75px of descent space below text
   with no descenders. text-underline-offset can sit tight to the baseline. */
.site-footer-links a {
  color: rgba(255, 255, 255, 0.72);
  border-bottom: none;
  padding-bottom: 0;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  text-decoration-color: rgba(255, 255, 255, 0.25);
  transition: color 0.15s ease, text-decoration-color 0.15s ease;
}

.site-footer-links a:hover, .site-footer-links a:focus-visible {
  color: #fff;
  text-decoration-color: rgba(255, 255, 255, 0.9);
}

/* -- "About this site" footnote -- */
.site-note {
  margin-top: clamp(48px, 8vw, 88px);
  padding-top: clamp(28px, 4vw, 36px);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  max-width: 620px;
}

.site-note .eyebrow { margin: 0 0 14px; }

.site-note p {
  margin: 0;
  color: rgba(255, 255, 255, 0.5);
  font-size: clamp(14px, 1.6vw, 15px);
  line-height: 1.65;
}

.site-note a {
  color: rgba(255, 255, 255, 0.72);
  border-bottom-color: rgba(255, 255, 255, 0.25);
}

.site-note a:hover, .site-note a:focus-visible {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.9);
}

.about-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px 24px;
  margin-bottom: clamp(32px, 5vw, 48px);
}

.about-top {
  display: flex;
  flex-direction: column;
}

/* -- LinkedIn / CV as dark button modules, matching the YouTube link cards -- */
p.links a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 156px;
  padding: 10px 15px;
  border: 1px solid rgb(33, 33, 33);
  border-radius: 8px;
  background-color: rgb(33, 33, 33);
  color: #fff;
  font-family: "Fakt Blond SemiBold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 600;
  font-size: 15px;
  line-height: 1;
  white-space: nowrap;
  transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.08s ease;
}

p.links a span {
  display: block;
  transform: translateY(2px);
}

p.links a:hover, p.links a:focus-visible {
  background-color: rgb(48, 48, 48);
  border-color: rgb(48, 48, 48);
}

p.links a:active { transform: translateY(1px); }

p.links a svg {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  opacity: 0.7;
}

p.links a[target="_blank"]::after { content: none; }

@media (max-width: 1099px) {
  /* -- below 1100px the right-hand gutter is too narrow for the button
  modules, so drop the links below the photo in normal flow and
  stop them sticking -- */
  .about-header {
    order: 0;
    margin-bottom: 20px;
  }

  .about-copy { order: 3; }

  .site-note { order: 4; }
}

/* -- hero -- */
.case-icon {
  display: block;
  height: 42px;
  width: auto;
  margin-bottom: 16px;
}

@media (prefers-reduced-motion: no-preference) {
  .case-icon-trace {
    stroke-dasharray: 150 150;
    animation: case-icon-draw .95s cubic-bezier(.22, 1, .36, 1) both;
  }
}

.kicker {
  display: block;
  font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  color: #00d8aa;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 2px;
  word-spacing: -3px;
  text-transform: uppercase;
  margin: 32px 0 18px;
}

.kicker a {
  color: inherit;
  border-bottom: 1px solid rgba(0, 216, 170, 0.45);
}

.kicker a:hover, .kicker a:focus-visible { border-color: #00d8aa; }

p.subtitle {
  margin: 18px 0 0;
  color: rgba(255, 255, 255, 0.68);
  font-size: 20px;
  line-height: 1.5;
  max-width: 54ch;
}

p.keywords {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 16px 0 0;
}

.keyword {
  display: inline-block;
  /* Fakt Blond SemiBold's ascent/descent split isn't symmetric (see the
     LinkedIn/CV button note above), so a symmetric 9/9 padding leaves the
     ink sitting visibly high. 10/8 was re-measured Sept 2026 and centres it
     on both desktop and iOS -- previously iOS alone needed 11px/7px via
     @supports(-webkit-touch-callout), but that's gone now they match. */
  padding: 10px 13px 8px;
  border-radius: 6px;
  background: rgb(40, 40, 40);
  color: rgba(255, 255, 255, 0.72);
  font-family: "Fakt Blond SemiBold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 600;
  font-size: 13px;
  line-height: 1;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.job-desc {
  color: rgba(255, 255, 255, 0.82);
  font-size: clamp(18px, 2.3vw, 22px);
  line-height: 30px;
  max-width: 55ch;
  margin: 18px 0 0;
}

/* -- meta row -- */
.meta-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 28px 32px;
  margin: 40px 0 0;
}

.meta-item .eyebrow { margin-bottom: 8px; }

.meta-item p {
  margin: 0;
  color: #fff;
  font-size: 14px;
  line-height: 1.5;
}

.timeframe-with-donut {
  display: flex;
  align-items: center;
  gap: 12px;
}

.timeframe-with-donut p {
  flex: 1;
  min-width: 0;
}

.timeframe-donut {
  flex: 0 0 auto;
  display: block;
}

.timeframe-donut circle {
  fill: none;
  stroke-width: 4;
}

.timeframe-donut-track { stroke: rgba(255, 255, 255, 0.15); }

.timeframe-donut-value {
  stroke: #00d8aa;
  stroke-linecap: round;
  stroke-dasharray: 180;
  stroke-dashoffset: 180;
  animation: timeframe-donut-fill 900ms cubic-bezier(0.33, 1, 0.68, 1) 200ms forwards;
}

@media (prefers-reduced-motion: reduce) {
  .timeframe-donut-value {
    animation: none;
    stroke-dashoffset: var(--donut-offset, 0);
  }
}

/* -- key points -- */
.key-points {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.key-points li {
  display: flex;
  align-items: baseline;
  gap: 14px;
  color: #fff;
  font-size: 22px;
  line-height: 33px;
  font-family: "Fakt Blond", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 400;
}

.key-points li::before {
  content: "→";
  flex: 0 0 auto;
  color: #00d8aa;
}

@media (min-width: 1100px) {
  .key-points-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  }

  .key-points { grid-column: 2 / -1; }
}

.stat {
  margin: 0;
  padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.2);
}

.stat-value {
  display: block;
  color: #00d8aa;
  font-weight: 400;
  font-size: clamp(28px, 4vw, 40px);
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

/* Shared rolling-number treatment; edge shading fades after settling. */
.stat-dial { display: inline-flex; height: 1.05em; vertical-align: top; }
@property --stat-edge-alpha {
  syntax: '<number>';
  inherits: false;
  initial-value: 0;
}
.stat-wheel {
  display: inline-block;
  width: 0.62em;
  height: 1.05em;
  overflow: hidden;
  mask-image: linear-gradient(rgba(0, 0, 0, var(--stat-edge-alpha)), #000 18%, #000 82%, rgba(0, 0, 0, var(--stat-edge-alpha)));
  -webkit-mask-image: linear-gradient(rgba(0, 0, 0, var(--stat-edge-alpha)), #000 18%, #000 82%, rgba(0, 0, 0, var(--stat-edge-alpha)));
  transition: --stat-edge-alpha 320ms ease-out;
}
.stat-wheel.is-settled { --stat-edge-alpha: 1; }
.stat-wheel-track { display: block; }
.stat-wheel-digit { display: block; height: 1.05em; line-height: 1.05; text-align: center; }
.stat-dial-suffix { line-height: 1.05; }

.stat-label {
  display: block;
  margin: 8px 0 0;
  color: rgba(255, 255, 255, 0.55);
  font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.5;
}

@media (min-width: 820px) {
  .case-blocks.approach-blocks { grid-template-columns: repeat(3, 1fr); }
}

.approach-blocks .case-block {
  border-color: rgb(33, 33, 33);
  background-color: rgb(33, 33, 33);
}

.approach-blocks .case-block:hover, .approach-blocks .case-block:focus-visible {
  border-color: rgb(48, 48, 48);
  background-color: rgb(48, 48, 48);
  transform: none;
}

.approach-blocks .case-block-media { aspect-ratio: 5 / 3; }

/* Image-only cards: the title sits over the picture, bottom left, in the page
   black (#131313) -- the three pictures are pale embossed paper, so it reads
   dark on light. No text strip below the image. */
.approach-blocks .case-block.has-image {
  position: relative;
  padding: 0;
}

.approach-blocks .case-block-media { margin-bottom: 0; }

.approach-blocks .case-block.has-image .case-block-text {
  position: absolute;
  bottom: 0;
  left: 0;
  padding: clamp(16px, 4vw, 28px);
}

/* One column below 820px, so the cards grow with the viewport -- the title
   grows with them (20px at a phone, 32px at the widest single column)
   rather than staying at the 18-22px of the three-up row. */
.approach-blocks .case-block-title {
  color: #131313;
  font-size: clamp(20px, 5vw, 32px);
}

@media (min-width: 820px) {
  .approach-blocks .case-block.has-image .case-block-text { padding: clamp(16px, 2vw, 24px); }
  .approach-blocks .case-block-title { font-size: clamp(18px, 2.4vw, 22px); }
}

form { max-width: 480px; }

.field { margin-bottom: 28px; }

.field input, .field textarea {
  width: 100%;
  background: transparent;
  border: none;
  border-bottom: 1px solid rgba(255, 255, 255, 0.3);
  color: #fff;
  font-family: inherit;
  font-size: 17px;
  padding: 8px 0 10px;
  outline: none;
  transition: border-color 0.15s ease;
}

.field textarea {
  resize: vertical;
  min-height: 90px;
  line-height: 1.4;
}

.field input::placeholder, .field textarea::placeholder {
  color: rgba(255, 255, 255, 0.32);
}

.field input:focus, .field textarea:focus {
  border-bottom-color: rgba(255, 255, 255, 0.9);
}

button[type="submit"] span {
  display: block;
  transform: translateY(2px);
}

button[type="submit"]:active { transform: translateY(1px); }

button[type="submit"]:disabled {
  cursor: default;
  opacity: 0.6;
}

.case-block .eyebrow { margin: 0 0 6px; }

.case-body h2 { max-width: 46rem; }

/* Available to screen readers and to the document outline, but not painted.
   index.html uses it for its h1: the homepage has no visible page title, so
   without this its sections would start at h2 with no h1 above them. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

h3 {
  font-family: "Fakt Blond SemiBold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 600;
  margin: 0 0 20px;
  color: #fff;
  font-size: clamp(18px, 2.3vw, 22px);
  line-height: 1.3;
}

.case-body h3 { max-width: 46rem; }

h4 {
  font-family: "Fakt Blond SemiBold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 600;
  margin: 28px 0 12px;
  color: #fff;
  font-size: clamp(16px, 2vw, 18px);
  line-height: 1.35;
}

.case-body h4 { max-width: 46rem; }

.subsection {
  max-width: 46rem;
}

.subsection + .subsection {
  margin-top: clamp(40px, 6vw, 56px);
  padding-top: clamp(40px, 6vw, 56px);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}

.case-body p {
  max-width: 46rem;
  color: rgba(255, 255, 255, 0.82);
  font-size: 18px;
  line-height: 1.6;
  margin: 0 0 20px;
}

.case-body p.see-also {
  margin-top: 22px;
  font-size: 15px;
}

.case-body p:last-child { margin-bottom: 0; }

.case-body blockquote, .case-pair blockquote {
  margin: 28px 0 24px;
  padding-left: 22px;
  border-left: 2px solid #00d8aa;
  color: #fff;
  font-size: 20px;
  line-height: 1.5;
  max-width: 46rem;
}

.case-body blockquote:last-child { margin-bottom: 0; }

.case-body blockquote cite, .case-pair blockquote cite {
  display: block;
  margin-top: 12px;
  font-style: normal;
  font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.55);
}

.ui-video.is-tall {
  width: fit-content;
  max-width: 100%;
}

.ui-video.is-small { max-width: 30rem; }

.ui-video + p, .video-panel + p { margin-top: clamp(24px, 3.5vw, 36px); }

/* A portrait clip is centred within the full-measure column rather than
   shrinking the column to its own width. The panel is the wrapper, not
   .ui-video itself, so the expand button stays anchored to the video's
   top-right corner instead of flying out to the panel's. */
.video-panel {
  display: flex;
  justify-content: center;
  max-width: 46rem;
  margin-top: 28px;
}

.video-panel .ui-video { margin-top: 0; }

.ui-video.is-tall video {
  width: auto;
  max-width: 100%;
  max-height: clamp(360px, 62vh, 560px);
}

.diagram svg {
  display: block;
  width: 100%;
  height: auto;
}

.figure.is-full { max-width: none; }

.figure figcaption {
  margin: 10px 0 0;
  color: rgba(255, 255, 255, 0.55);
  font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 14px;
  line-height: 1.5;
}

.figure figcaption a {
  color: rgba(255, 255, 255, 0.8);
  border-bottom-color: rgba(255, 255, 255, 0.35);
}

.figure figcaption a:hover, .figure figcaption a:focus-visible {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.9);
}

.figure figcaption a[target="_blank"]::after { content: none; }

/* -- image gallery: large image + thumbnail strip, click to enlarge -- */
.gallery {
  margin: clamp(20px, 3vw, 28px) 0 clamp(24px, 4vw, 36px);
  max-width: 736px;
}

.gallery-frame { position: relative; }

.gallery-main {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  background: #161616;
  border-radius: 6px;
  cursor: zoom-in;
}

/* outgoing image, sits over .gallery-main and dissolves to reveal the new one */
.gallery-fade {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

@media (prefers-reduced-motion: reduce) {
  .gallery-fade { display: none; }
}

.gallery-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease, background 0.15s ease, transform 0.08s ease;
}

.gallery-frame:hover .gallery-nav, .gallery-frame:focus-within .gallery-nav {
  opacity: 1;
}

.gallery-nav:hover, .gallery-nav:focus-visible {
  background: rgba(0, 0, 0, 0.8);
}

.gallery-nav:active { transform: translateY(-50%) translateY(1px); }

.gallery-nav svg { display: block; }

.gallery-nav-prev svg { transform: translateX(-1px); }

.gallery-nav-next svg { transform: translateX(1px); }

.gallery-nav-prev { left: 10px; }

.gallery-nav-next { right: 10px; }

@media (hover: none) {
  .gallery-nav { opacity: 0.9; }
}

.gallery .gallery-count {
  margin: 12px 0 0;
  color: #fff;
  font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
}

.gallery .gallery-caption {
  margin: 10px 0 0;
  max-width: none;
  color: rgba(255, 255, 255, 0.55);
  font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
}

.gallery-count + .gallery-caption { margin-top: 5px; }

.gallery-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.gallery-thumb {
  flex: 0 0 auto;
  width: 84px;
  height: 56px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 4px;
  background: none;
  overflow: hidden;
  cursor: pointer;
  opacity: 0.5;
  transition: opacity 0.15s ease, border-color 0.15s ease, transform 0.08s ease;
}

.gallery-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.gallery-thumb:hover, .gallery-thumb:focus-visible { opacity: 0.85; }

.gallery-thumb:active { transform: translateY(1px); }

.gallery-thumb.is-active {
  opacity: 1;
  border-color: #00d8aa;
}

@media (max-width: 560px) {
  .gallery-thumb {
    width: 64px;
    height: 44px;
  }
}

.figure-strip-imgs img[src$="validation-operation.png"] {
  object-position: 100% 50%;
}

.figure-strip-4 .figure-strip-imgs {
  grid-template-columns: repeat(2, 1fr);
}

/* -- resources -- */
.resources-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.resources-list a { font-size: clamp(15.5px, 1.7vw, 17px); }

/* -- sticky section nav (wide desktop only), matching the case study pages -- */
.toc {
  display: none;
  position: relative;
}

@media (min-width: 1100px) {
  .content {
    grid-column: 2;
    grid-row: 1;
    min-width: 0;
  }

  /* grid-row: 1 is load-bearing, not decorative -- .content (DOM-first,
     now column 2) advances the sparse auto-placement cursor past column 1
     before .side-rail (DOM-second, column 1) is placed. The cursor never
     moves backward, so without an explicit row .side-rail can't backfill
     row 1 and drops to a row of its own, thousands of pixels down the
     page. Confirmed directly in the browser -- reasoning about grid
     auto-placement from memory kept saying this would just work. */
  .side-rail {
    grid-column: 1;
    grid-row: 1;
    position: sticky;
    top: 100px;
    display: flex;
    flex-direction: column;
    gap: 40px;
  }
}

/* Sits at the page bottom (after the explicit hr + back-to-top link that
   now precede it, so it no longer needs a divider of its own) and fills
   the full content width, unlike the narrow 46rem column above it. */
.related {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-top: clamp(24px, 3.5vw, 32px);
}

@media (max-width: 560px) {
  .related { grid-template-columns: 1fr; }
}

.related a {
  display: block;
  overflow: hidden;
  border: 1px solid rgb(33, 33, 33);
  border-radius: 8px;
  background-color: rgb(33, 33, 33);
  color: #fff;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.related a:hover, .related a:focus-visible {
  background-color: rgb(48, 48, 48);
  border-color: rgb(48, 48, 48);
}

.related-thumb {
  display: block;
  width: 100%;
  aspect-ratio: 5 / 2;
  object-fit: cover;
  transition: transform 0.3s ease;
}

.related a:hover .related-thumb, .related a:focus-visible .related-thumb {
  transform: scale(1.04);
}

.related-title {
  display: block;
  padding: 12px 15px 14px;
  font-family: "Fakt Blond SemiBold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 600;
  color: #fff;
  font-size: 15px;
  line-height: 1.35;
}

.toc a {
  display: block;
  padding: 8px 0 8px 16px;
  border-left: 2px solid rgba(255, 255, 255, 0.14);
  border-bottom: none;
  font-size: 16px;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.55);
  transition: color 0.15s ease, border-color 0.15s ease;
}

.toc a:hover, .toc a:focus-visible {
  color: #fff;
  border-left-color: rgba(255, 255, 255, 0.6);
}

.toc a.is-active { color: #00d8aa; }

.toc-indicator {
  position: absolute;
  left: 0;
  top: 0;
  width: 2px;
  height: 0;
  background: #00d8aa;
  opacity: 0;
  pointer-events: none;
  transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1), height 0.32s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease;
}

.toc-indicator.is-visible { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .toc-indicator { transition: none; }
}

.principles {
  display: flex;
  flex-direction: column;
  gap: clamp(40px, 6vw, 64px);
}

.principles .rule {
  width: 100%;
  margin: 0;
}

.principle-title {
  color: #fff;
  font-size: clamp(18px, 2.3vw, 22px);
  line-height: 1.3;
  margin: 0 0 clamp(14px, 1.8vw, 18px);
  max-width: 32ch;
}

.principle p {
  color: rgba(255, 255, 255, 0.82);
  font-size: 18px;
  line-height: 1.6;
  margin: 0;
  max-width: 736px;
}

.principle p.note {
  color: rgba(255, 255, 255, 0.7);
  font-size: clamp(15.5px, 1.7vw, 17px);
  line-height: 1.65;
}

.principle p.principle-applied {
  font-size: clamp(15px, 1.7vw, 17px);
  margin-top: clamp(16px, 2vw, 22px);
}

/* .principle p sets color: rgba(255,255,255,0.82) on every <p>,
   including ones nested inside a blockquote for multi-line quotes --
   an inherited value always loses to an explicit one on the element
   itself, so those paragraphs were rendering at 0.82 instead of the
   blockquote's own #fff. .principle blockquote p is more specific
   (an extra type selector) than .principle p, so this wins reliably
   rather than by source-order luck. color: inherit rather than a
   hardcoded #fff, so it always matches whatever the blockquote itself
   is set to. */
.principle blockquote p { color: inherit; }

.principle blockquote p + p { margin-top: 10px; }

.case-block-kicker-icon {
  display: block;
  height: 14px;
  width: auto;
  flex: 0 0 auto;
}

.case-block-subtitle {
  display: block;
  color: rgba(255, 255, 255, 0.68);
  font-size: clamp(15px, 1.7vw, 17px);
  line-height: 1.5;
  margin-top: 8px;
  max-width: 54ch;
}

.case-block-keywords {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
}

.case-block-keyword {
  display: inline-block;
  padding: 10px 13px 8px;
  border-radius: 6px;
  background: rgb(48, 48, 48);
  color: rgba(255, 255, 255, 0.72);
  font-family: "Fakt Blond SemiBold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 600;
  font-size: 13px;
  line-height: 1;
  letter-spacing: 0.02em;
  white-space: nowrap;
  transition: background-color 0.2s ease;
}

.case-block:hover .case-block-keyword, .case-block:focus-visible .case-block-keyword {
  background: rgb(64, 64, 64);
}

.case-studies-heading { margin-top: 0; }

.job-company a[target="_blank"]::after { content: none; }

/* -- case block with lead image -- */
.case-block.has-image {
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  gap: 0;
  padding: 0 0 28px;
  overflow: hidden;
}

.case-block-media {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: center top;
  margin-bottom: 16px;
  transition: transform 0.3s ease;
}

/* Branded logo cards (Zonefully, Image Insight) are centred compositions,
not top-anchored UI screenshots -- override the default top crop. */
.case-block-media[src$="zonefully-card.jpg"], .case-block-media[src$="image-insight-card.jpg"] {
  object-position: center;
}

.case-block.has-image .case-block-text { padding: 0 22px; }

/* -- light card: Image Insight and Zonefully (the non-Quantemplate studies)
   sit on white, with the usual white/grey type and chips flipped to the page
   black. The kicker stays green, a touch deeper than the site teal. -- */
.case-block.is-light {
  background-color: #fff;
  border-color: #fff;
}

.case-block.is-light:hover, .case-block.is-light:focus-visible {
  background-color: rgb(238, 238, 238);
  border-color: rgb(238, 238, 238);
}

.case-block.is-light .case-block-title { color: #131313; }
.case-block.is-light .case-block-subtitle { color: rgba(19, 19, 19, 0.68); }
.case-block.is-light .case-block-kicker { color: #00cb9f; }

/* Next-study cards use the current page's theme accent. */
.case-footer .case-block .case-block-kicker { color: var(--accent, #00d8aa); }

.case-block.is-light .case-block-keyword {
  background: rgba(19, 19, 19, 0.08);
  color: rgba(19, 19, 19, 0.72);
}

.case-block.is-light:hover .case-block-keyword, .case-block.is-light:focus-visible .case-block-keyword {
  background: rgba(19, 19, 19, 0.14);
}

.case-block.has-image:hover .case-block-media, .case-block.has-image:focus-visible .case-block-media {
  transform: scale(1.03);
}

.case-block.has-thumb .case-block-text { min-width: 0; }

.case-block.has-thumb:hover .case-block-thumb, .case-block.has-thumb:focus-visible .case-block-thumb {
  transform: scale(1.03);
}

/* -- site nav -- */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 50;
}

/* Frosted bar background lives on a pseudo-element so .site-nav itself is
not a backdrop-filter root — otherwise the open mobile menu panel (a
descendant that has its own backdrop-filter) can't blur the page. */
.site-nav::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: rgba(19, 19, 19, 0.86);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.site-nav-inner {
  max-width: 900px;
  margin: 0 auto;
  padding: 14px clamp(24px, 5vw, 56px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px 24px;
  min-height: 56px;
}

/* Matches index.html's --band/--gutter landing-page override (same breakpoint
   and values) so the nav doesn't visibly resize when navigating to or from
   the landing page. If those custom properties ever change, update here too. */
@media (min-width: 1024px) {
  .site-nav-inner {
    max-width: 1500px;
    padding-left: clamp(32px, 4vw, 72px);
    padding-right: clamp(32px, 4vw, 72px);
  }
}

.site-nav-brand {
  font-size: 24px;
  color: #fff;
  border-bottom: none;
  display: inline-block;
  transform-origin: 50% 55%;
}

@keyframes brand-wiggle {
  0%   { transform: rotate(0); }
  25%  { transform: rotate(-2.4deg); }
  55%  { transform: rotate(1.5deg); }
  80%  { transform: rotate(-0.6deg); }
  100% { transform: rotate(0); }
}

.site-nav-brand:hover { animation: brand-wiggle 0.4s ease-in-out; }

@media (prefers-reduced-motion: reduce) {
  .site-nav-brand:hover { animation: none; }
}

.site-nav-links {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 28px;
}

.site-nav-links a {
  font-family: "Fakt Blond SemiBold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 18px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.65);
  border-bottom: none;
}

.site-nav-links a:hover, .site-nav-links a:focus-visible { color: #fff; }

.site-nav-links a.is-current { color: #00d8aa; }

.nav-item { position: relative; }

.nav-dropdown {
  position: absolute;
  top: 100%;
  left: 50%;
  padding-top: 14px;
  min-width: 360px;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -6px);
  transition: opacity 0.12s ease, transform 0.12s ease, visibility 0.12s ease;
  z-index: 60;
}

/* Approach popover holds short items -> shrink to content instead of the 360px case-studies width */
.nav-dropdown--tight { min-width: 0; }

.nav-dropdown--tight .nav-dropdown-card-title { white-space: nowrap; }

.nav-item:hover .nav-dropdown, .nav-item:focus-within .nav-dropdown, .nav-item.is-open .nav-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

.nav-item:hover .nav-dropdown { transition-delay: 0.14s; }

/* Escape sets .is-dismissed (see nav.js). Without this the rule above keeps the
   panel on screen while the pointer rests on the item or focus sits inside it,
   so the key looks broken. Same specificity as that rule, declared after it, so
   it wins. Desktop only -- below 640px the dropdown is expanded inline and must
   stay visible. */
@media (min-width: 641px) {
  .nav-item.is-dismissed .nav-dropdown {
    opacity: 0;
    visibility: hidden;
  }
}

.nav-dropdown-panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  background: #1a1a1a;
  border-radius: 10px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
}

.nav-dropdown-card {
  display: block;
  padding: 14px 16px 12px;
  border-radius: 8px;
  background-color: transparent;
  transition: background-color 0.2s ease;
}

.nav-dropdown-card:hover, .nav-dropdown-card:focus-visible {
  background-color: rgba(255, 255, 255, 0.08);
}

.nav-dropdown-card-kicker {
  display: flex;
  align-items: center;
  gap: 5px;
  font-family: "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  color: #00d8aa;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 2px;
  word-spacing: -3px;
  text-transform: uppercase;
  margin-bottom: 6px;
}

.nav-dropdown-card-kicker-icon {
  display: block;
  height: 12px;
  width: auto;
  flex: 0 0 auto;
}

.nav-dropdown-card-title {
  display: block;
  font-family: "Fakt Blond SemiBold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 600;
  color: #fff;
  font-size: 15px;
  line-height: 1.3;
}

.nav-dropdown-divider {
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  margin: 4px 0;
}

/* -- mobile: collapse the nav links behind a hamburger (<= 640px) -- */
.nav-toggle {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  margin: -10px -12px -10px 0;
  padding: 0;
  background: none;
  border: none;
  color: #fff;
  cursor: pointer;
}

.nav-toggle-bar {
  display: block;
  width: 22px;
  height: 2px;
  background: currentColor;
  transition: transform 0.45s cubic-bezier(0.68, -0.55, 0.27, 1.55), opacity 0.2s ease;
}

.nav-toggle-bar:nth-child(2) {
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.site-nav.is-open .nav-toggle-bar:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.site-nav.is-open .nav-toggle-bar:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}

.site-nav.is-open .nav-toggle-bar:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

@media (max-width: 640px) {
  .nav-toggle { display: flex; }

  .site-nav-inner { flex-wrap: nowrap; }

  .site-nav-links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: none;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 0;
    max-height: 80vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 6px clamp(24px, 5vw, 56px) 18px;
    background: rgba(19, 19, 19, 0.86);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-top: 1px solid rgba(255, 255, 255, 0.12);
  }

  .site-nav.is-open .site-nav-links { display: flex; }

  .site-nav-links > a, .nav-item > a {
    display: block;
    padding: 14px 0;
    font-size: 20px;
  }

  .site-nav-links > a + a, .nav-item {
    border-top: 1px solid rgba(255, 255, 255, 0.1);
  }

  .nav-dropdown {
    position: static;
    min-width: 0;
    padding: 0 0 10px;
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: none;
  }

  /* the desktop open-state rule is more specific and would otherwise
  still slide the panel -50% when the item is hovered/focused here */
  .nav-item:hover .nav-dropdown, .nav-item:focus-within .nav-dropdown, .nav-item.is-open .nav-dropdown {
    transform: none;
  }

  .nav-dropdown-panel {
    gap: 2px;
    padding: 0;
    background: none;
    box-shadow: none;
  }

  .nav-dropdown-card { padding: 9px 0 9px 16px; }
}

@media (prefers-reduced-motion: no-preference) {
  /* The download arrow settles towards its stationary tray. */
  a[download] .download-arrow { transition: transform .2s ease; }

  a[download]:focus-visible .download-arrow { transform: translateY(2px); }
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  a[download]:hover .download-arrow { transform: translateY(2px); }
}

/* -- hero entrance animation (word-by-word rise) --
.hero-armed is set on <html> by an inline head script only when
prefers-reduced-motion is not set; the body script splits the
statement into .word spans, staggers them, then adds .hero-play. */
.hero-armed #about-heading {
  opacity: 0;
  transform: translateY(8px);
}

.hero-play #about-heading {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

/* -- scroll reveal: fade the card grids in as they enter the viewport --
.reveal-armed is set on <html> pre-paint (only when motion is welcome);
the body script adds .is-revealed per card with a per-row --reveal-delay. */
.reveal-armed .case-blocks .case-block { opacity: 0; }

.reveal-armed .case-blocks .case-block.is-revealed {
  animation: card-fade-in 1.1s ease var(--reveal-delay, 0ms) both;
}

@media (prefers-reduced-motion: reduce) {
  .reveal-armed .case-blocks .case-block, .reveal-armed .case-blocks .case-block.is-revealed {
    opacity: 1;
    animation: none;
  }
}

/* -- key points: stagger in as the list enters view -- */
.head-armed .key-points li {
  opacity: 0;
  transform: translateY(8px);
}

.key-points li.kp-in {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}

@media (prefers-reduced-motion: reduce) {
  .head-armed .key-points li {
    opacity: 1;
    transform: none;
  }
}

/* Animated schema flow; the original artwork remains the static fallback. */
.schema-pulse, .schema-halo { pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .schema-pulse, .schema-halo { display: none; }
}

@media (prefers-reduced-motion: no-preference) {
  /* Let each corner move outwards without scaling its stroke. */
  .ui-video-expand .expand-corner { transition: transform .2s ease; }

  .ui-video-expand:focus-visible .expand-corner {
    transform: translate(var(--dx), var(--dy));
  }
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .ui-video-expand:hover .expand-corner {
    transform: translate(var(--dx), var(--dy));
  }
}

.green-stage [fill], .green-stage [stroke] {
  transition:fill .585s ease,stroke .585s ease;
}

.green-pipeline.is-flow-active .green-stage [fill]:not([fill="none"]):not(.stage-bg) {
  fill:#8af4d2;
}

.green-pipeline.is-flow-active .green-stage .stage-bg { fill:#091d1b; }

.green-pipeline.is-flow-active .green-stage [stroke] { stroke:#8af4d2; }

.green-pipeline.is-flow-active .green-stage [fill], .green-pipeline.is-flow-active .green-stage [stroke] {
  transition-delay:var(--stage-delay);
}

@media(prefers-reduced-motion:reduce) {
  .green-pipeline .green-stage [fill], .green-pipeline .green-stage [stroke] {
    transition:none;
  }
}

.green-trace {
  opacity:0;
  stroke-dasharray:18 118;
  stroke-dashoffset:18;
  pointer-events:none;
}

@media(prefers-reduced-motion:no-preference) {
  .green-pipeline.is-flow-active .green-trace {
    animation:pipeline-trace 1.105s linear var(--trace-delay) both;
  }
}

@keyframes validation-rule-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (prefers-reduced-motion: no-preference) {
  .validation-card.is-flow-active .validation-rule {
    animation: validation-rule-fade .55s ease-out var(--rule-delay) both;
  }
  .validation-card.is-flow-active .validation-arc {
    animation: validation-ring 2.1s cubic-bezier(.4,0,.2,1) var(--ring-delay) both;
  }
}

.feeds-line {
  stroke-dasharray:100;
  stroke-dashoffset:100;
}

.feeds-pulse {
  stroke-dasharray:12 112;
  stroke-dashoffset:12;
  opacity:0;
}

.feeds-module {
  transform-box:fill-box;
  transform-origin:center;
}

.feeds-arrival { opacity:0; }

@media(prefers-reduced-motion:no-preference) {
  .feeds-card.is-flow-active .feeds-line {
    animation:feeds-line-in var(--feed-duration) linear var(--feed-delay) both;
  }

  .feeds-card.is-flow-active .feeds-pulse {
    animation:feeds-travel var(--feed-duration) linear var(--feed-delay) both;
  }

  .feeds-card.is-flow-active .feeds-module {
    animation:feeds-pop .5s ease var(--arrival) both;
  }

  .feeds-card.is-flow-active .feeds-arrival {
    animation:feeds-arrive .85s ease-out var(--arrival) both;
  }
}

@media(prefers-reduced-motion:reduce) {
  .feeds-line { stroke-dashoffset:0; }
}

.mapping-row { opacity:0; }

.mapping-progress {
  transform:scaleX(0);
  transform-box:fill-box;
  transform-origin:left center;
}

.mapping-progress-left { transform:scaleX(.6); }

@media(prefers-reduced-motion:no-preference) {
  .mapping-card.is-flow-active .mapping-row {
    animation:mapping-row-in .3s ease var(--mapping-delay) both;
  }

  .mapping-card.is-flow-active .mapping-progress-left {
    animation:mapping-fill-left 3.4s ease both;
  }

  .mapping-card.is-flow-active .mapping-progress-right {
    animation:mapping-fill-right 3.4s ease both;
  }
}

@media(prefers-reduced-motion:reduce) {
  .mapping-row { opacity:1; }

  .mapping-progress { transform:none; }
}

/* Reset finished artwork before an explicitly requested hover replay. */
.is-flow-reset .green-stage [fill], .is-flow-reset .green-stage [stroke] {
  transition: none !important;
}
