@import url("../../css/case-chrome.css?v=4");
@import url("../../css/device-morph.css?v=3");

/* =====================================================================
   FREIGHT INTELLIGENCE — case study page
   Vanilla HTML/CSS/JS + Motion (motion.dev). Reuses tokens.css for base
   colour/type/spacing so this page reads as part of the same site, adds
   --fi-* locals for the section-specific geometry the design brief calls
   for (see design.md this was built from).
   ===================================================================== */

:root {
  /* The case study sits on white, as on the board — the site's page tone
     is warmer and made the light media boxes read as dirty against it. */
  --fi-bg-page:       #ffffff;
  --fi-bg-surface:    var(--surface-card);
  --fi-bg-inset:       var(--surface-sunken);
  --fi-border:        var(--line);
  --fi-border-strong: var(--line-strong);
  --fi-text-primary:  var(--ink-strong);
  --fi-text-secondary:var(--ink-muted);
  --fi-text-tertiary: var(--ink-soft);
  --fi-text-invert:   #ffffff;
  --fi-overlay-scrim: linear-gradient(180deg, rgba(0,0,0,0) 35%, rgba(0,0,0,0.55) 100%);
  --fi-accent:        #ff5a4d;
  --fi-accent-approach: #6ee07a;
  --fi-glow-friction: radial-gradient(circle, rgba(255,90,77,0.28) 0%, rgba(255,90,77,0) 70%);
  --fi-glow-approach: radial-gradient(circle, rgba(110,224,122,0.55) 0%, rgba(110,224,122,0) 72%);
  --fi-shadow-card:   0 1px 2px rgba(0,0,0,0.04);
  --fi-shadow-panel:  0 24px 60px rgba(0,0,0,0.10);

  /* The media boxes sit a step below the now-white page. Exported shots
     already carry roughly this tone baked in, so they blend rather than
     sitting on a visible plate. */
  --fi-media-bg: #f4f4f4;
  --fi-impact-tint: #fdf1bb;
  --fi-impact-neutral: linear-gradient(180deg, #e9e9e9 0%, #ffffff 58%);

  --fi-container: 1600px;
  --fi-gutter: clamp(18px, 2.2vw, 36px);
  --fi-r-card: 12px;
  --fi-r-panel: 20px;
  --fi-r-media: 16px;
  --fi-section-gap: clamp(80px, 10vw, 140px);
  --fi-stack-xl: 56px;

  --fi-f-hero: clamp(40px, 5.4vw, 68px);
  --fi-f-section: clamp(28px, 3.9vw, 42px);
  /* A real ramp rather than a near-flat one: 1.6vw only moved this 6px
     across the entire range, so the lead statement read the same size on a
     phone as on a desktop. */
  --fi-f-statement: clamp(18px, .7vw + 12.5px, 25px);
  --fi-f-lead: clamp(13.8px, .28vw + 11.5px, 15.5px);
  --fi-f-body: clamp(14px, .32vw + 11px, 16px);
  --fi-f-card-title: 15px;
  --fi-f-card-body: 12.5px;
  --fi-f-micro: 11px;

  --fi-ease-out-expo: var(--ease-out);        /* was a second copy of the same curve */
  --fi-ease-out-quart: var(--ease-out-quart); /* was a third name for the same curve */
}

[data-theme="dark"] {
  --fi-bg-page:       #0b0b0b;
  --fi-bg-surface:    #141414;
  --fi-bg-inset:       #1a1a1a;
  --fi-border:        #262626;
  --fi-border-strong: #333333;
  /* Card titles, numerals and headings all read from this token; #fafafa
     rendered as light grey against the page's near-black surfaces, so the
     dark theme takes pure white to match the homepage cards. */
  --fi-text-primary:  #ffffff;
  --fi-text-secondary:#a1a1a1;
  --fi-text-tertiary: #6b6b6b;
  --fi-overlay-scrim: linear-gradient(180deg, rgba(0,0,0,0) 30%, rgba(0,0,0,0.75) 100%);
  --fi-accent:        #ffffff;
  --fi-accent-approach: #ffffff;
  --fi-glow-friction: radial-gradient(circle, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0) 70%);
  --fi-glow-approach: radial-gradient(circle, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0) 72%);
  --fi-shadow-card:   0 1px 2px rgba(0,0,0,0.5);
  --fi-shadow-panel:  0 24px 60px rgba(0,0,0,0.6);
}
[data-theme="dark"] {
  --fi-media-bg: var(--fi-bg-inset);
  --fi-impact-tint: #1f1c12;
  --fi-impact-neutral: #151515;
}
[data-theme="dark"] .fi-dash,
[data-theme="dark"] .fi-report-mini,
[data-theme="dark"] .fi-cards-mini,
[data-theme="dark"] .fi-storyboard { filter: grayscale(1) contrast(1.05) brightness(0.9); }
[data-theme="dark"] .fi-solution__panel { background: linear-gradient(135deg,#3a3a3a 0%,#171717 55%,#2b2b2b 100%); }

/* Everything below is scoped to .fi-root so this stylesheet can be loaded
   into index.html — where the case study now opens as an in-document
   overlay — without restyling the site around it. .fi-root is the <body>
   on the standalone page and the overlay container when embedded, so a
   single rule serves both. Only these four blocks needed scoping; every
   custom property here is --fi-* prefixed and therefore purely additive. */
.fi-root, .fi-root *, .fi-root *::before, .fi-root *::after { box-sizing: border-box; }

.fi-root {
  margin: 0;
  background: var(--fi-bg-page);
  color: var(--fi-text-primary);
  font-family: var(--font-display);
  -webkit-font-smoothing: antialiased;
  transition: background-color .26s ease, color .26s ease;
}

.fi-root .skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: #000; color: #fff; padding: 10px 16px; border-radius: 8px;
}
.fi-root .skip-link:focus { left: 16px; top: 16px; }

/* ---- Page chrome: progress rail, theme toggle, close ---- */
.fi-progress {
  position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 100;
  background: transparent; opacity: 0; transition: opacity .2s ease;
}
.fi-progress span {
  display: block; height: 100%; width: 100%; background: var(--fi-text-primary);
  transform: scaleX(0); transform-origin: left;
}
.fi-progress.is-visible { opacity: 1; }

.fi-chrome {
  position: fixed; top: 24px; right: 24px; z-index: 100;
  display: flex; align-items: center; gap: 12px;
}
.fi-theme, .fi-close {
  width: 34px; height: 34px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.16);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,0.24);
  color: #fff;
  cursor: pointer;
  transition: transform .16s var(--fi-ease-out-quart), background-color .24s ease;
}
.fi-close svg, .fi-theme__icon { width: 14px; height: 14px; stroke: currentColor; stroke-width: 1.8; fill: none; }
.fi-theme:hover, .fi-close:hover { transform: scale(1.06); background: rgba(255,255,255,0.24); }
.fi-chrome.is-on-light .fi-theme,
.fi-chrome.is-on-light .fi-close {
  background: rgba(0,0,0,0.06); border-color: rgba(0,0,0,0.12); color: var(--fi-text-primary);
}
.fi-theme__sun-rays, .fi-theme__moon { transition: opacity .2s ease, transform .26s var(--fi-ease-out-quart); transform-origin: 12px 12px; }
.fi-theme__moon { opacity: 0; transform: rotate(-40deg) scale(.6); }
[data-theme="dark"] .fi-theme__sun-core,
[data-theme="dark"] .fi-theme__sun-rays { opacity: 0; transform: rotate(40deg) scale(.6); }
[data-theme="dark"] .fi-theme__moon { opacity: 1; transform: rotate(0) scale(1); }
.fi-theme__sun-core, .fi-theme__sun-rays path, .fi-theme__moon { stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; fill: none; }
.fi-theme__moon { fill: currentColor; stroke: none; }

/* ---- Shared containers ---- */
.fi-section, .fi-intro, .fi-closing{
  width: 100%;
  max-width: var(--fi-container);
  margin-inline: auto;
  padding-inline: var(--fi-gutter);
}
@media (max-width: 768px) {
  :root { --fi-gutter: 20px; }
}
@media (max-width: 480px) {
  :root { --fi-gutter: 16px; }
}
.fi-section { margin-top: var(--fi-section-gap); }

/* ---- 01 Hero ---- */
.fi-hero { position: relative; }
.fi-hero__media {
  position: relative; width: 100%;
  /* Full-bleed cover on every device. svh (not vh/dvh) so the band keeps a
     stable height while mobile browser chrome shows and hides — dvh would
     resize the hero mid-scroll, and the expand morph in js/selected-work.js
     measures this exact value to land on. */
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: var(--fi-bg-inset);
}
.fi-hero__media-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.fi-media-placeholder {
  position: absolute; inset: 0;
  background:
    repeating-linear-gradient(135deg, var(--fi-bg-inset) 0 14px, var(--fi-bg-surface) 14px 28px);
  display: flex; align-items: center; justify-content: center;
  text-align: center;
}
.fi-media-placeholder::after {
  content: attr(data-placeholder-label);
  font-size: 11px; color: var(--fi-text-tertiary); max-width: 60%;
  letter-spacing: -.005em;
}
/* Frosted band across the bottom of the cover, dissolving upward.
   The mask is what makes it blend: it fades the element — INCLUDING its
   backdrop-filter, since a mask clips the filtered result too — from fully
   opaque at the bottom to fully transparent at its top edge, so the blur
   melts into the sharp photo instead of ending on a hard horizontal seam.
   The gradient underneath only supplies the darkening that keeps the
   overlay copy legible; the blur alone would not guarantee contrast over
   an arbitrarily bright frame. */
.fi-hero__scrim { position: absolute; inset: 0; pointer-events: none; }

/* The frost is a blurred DUPLICATE of the cover rather than a
   backdrop-filter. That is not a stylistic preference: applying any mask
   to a backdrop-filtered element — or to any ancestor of one — makes the
   browser silently drop the backdrop effect, so a masked frost renders as
   no frost at all. Blurring a copy of the same image, positioned and
   cropped identically, is visually indistinguishable and masks cleanly,
   which is what lets the top edge dissolve into the sharp photo instead
   of ending on a seam.

   The box is overscanned past the media on all sides so blur() has real
   pixels to sample at the edges; without it the frost washes out toward
   transparent exactly where the copy needs to be densest. .fi-hero__media
   clips the overflow. */
.fi-hero__scrim::before {
  content: "";
  position: absolute;
  inset: -80px;
  background: url("../../assets/case-covers/freight-banner.webp") center / cover no-repeat;
  filter: blur(26px) saturate(0.92) brightness(0.62);
  -webkit-mask-image: linear-gradient(to bottom, transparent 44%, rgba(0,0,0,0.45) 60%, rgba(0,0,0,0.88) 74%, #000 86%);
  mask-image: linear-gradient(to bottom, transparent 44%, rgba(0,0,0,0.45) 60%, rgba(0,0,0,0.88) 74%, #000 86%);
}

/* Darkening pass on top of the frost. The blur alone cannot guarantee
   contrast — this cover is a pale studio shot — so the copy would sit on
   light grey without it. Fades to fully transparent well above the frost's
   own fade so the two never form a visible edge together. */
.fi-hero__scrim::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(8, 9, 11, 0) 42%,
    rgba(8, 9, 11, 0.30) 64%,
    rgba(8, 9, 11, 0.62) 82%,
    rgba(8, 9, 11, 0.80) 100%
  );
}
.fi-hero__content {
  position: absolute; left: var(--fi-gutter); bottom: 36px; right: var(--fi-gutter); color: var(--fi-text-invert);
}
@media (max-width: 768px) { .fi-hero__content { bottom: 24px; } }
.fi-root #fiHero .fi-hero__content h1.fi-hero__title {
  font-size: var(--fi-f-hero); font-weight: 500; line-height: 1.02; letter-spacing: -0.035em; margin: 0;
  color: #fff !important;
}
.fi-hero__meta {
  list-style: none; display: flex; gap: 40px; margin: 28px 0 0; padding: 0; flex-wrap: wrap;
}
.fi-hero__meta li { display: flex; flex-direction: column; gap: 6px; }
/* Sized from the shared scale in tokens.css rather than this page's local
   11px --fi-f-micro. An earlier pass moved these onto tokens but picked
   --fs-label and --fs-small, which are BOTH 0.75rem — so the label and its
   value still rendered at an identical 12px and the pair read as one flat
   block. tokens.css maps the step above as "Medium 14px -> --fs-meta",
   which is what a meta value is, so the value takes that and the label
   keeps --fs-label. Contrast follows the same idea: the label is the quiet
   part, the value is the content worth reading. */
.fi-meta__label {
  font-family: var(--font-body);
  font-size: var(--fs-label);
  line-height: 1.2;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.58);
}
.fi-meta__value {
  font-family: var(--font-body);
  font-size: var(--fs-meta);
  line-height: 1.45;
  /* Was a one-off -0.005em; --track-body is the scale's universal body
     tracking and the value is body copy. */
  letter-spacing: var(--track-body);
  color: rgba(255, 255, 255, 0.96);
}
@media (max-width: 768px) { .fi-hero__meta { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } }

/* ---- 02 Intro ---- */
.fi-intro {
  max-width: min(960px, 100%);
  text-align: center;
  padding-top: clamp(64px, 7vw, 104px);
}
.fi-statement {
  font-size: var(--fi-f-statement);
  line-height: 1.4;
  letter-spacing: var(--track-body, -.02em);
  line-height: 1.38;
  font-weight: 550;
  margin: 0 auto;
  max-width: 100%;
  text-wrap: balance;
  overflow-wrap: anywhere;
}

.fi-body {
  font-size: var(--fi-f-body);
  line-height: 1.68;
  color: var(--fi-text-secondary);
  max-width: 76ch;
  margin: 18px auto 0;
  text-wrap: pretty;
  overflow-wrap: anywhere;
}
.fi-body:first-of-type { margin-top: 32px; }

/* ---- Section header pattern ---- */
.fi-section-head {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(300px, 7fr);
  /* Heading and lede centre against each other rather than both hanging
     from the top, where a two-line lede sat visibly high beside the
     single-line heading. */
  align-items: center;
  gap: clamp(24px, 4vw, 72px);
  margin-bottom: var(--fi-stack-xl);
}
.fi-section-head h2 {
  font-size: var(--fi-f-section); font-weight: 400; letter-spacing: var(--track-tight, -.055em); margin: 0; line-height: 1;
}
.fi-root .fi-body,
.fi-root .fi-showcase__copy p,
.fi-root .fi-impact-copy p,
.fi-root .fi-closing__sub,
.fi-root .fi-section-head p { letter-spacing: var(--track-body, -.02em); }
.fi-section-head p {
  /* Runs to the end of its column, which lines up with the right edge of
     the media below it; the 58ch measure stopped it well short. */
  width: 100%;
  max-width: none;
  font-size: var(--fi-f-lead);
  line-height: 1.6;
  color: var(--fi-text-tertiary);
  margin: 0;
  text-wrap: pretty;
  overflow-wrap: anywhere;
}
@media (max-width: 1024px) {
  .fi-section-head { grid-template-columns: minmax(220px, 4fr) minmax(0, 6fr); }
}
@media (max-width: 768px) {
  .fi-section-head { grid-template-columns: 1fr; }
  .fi-root .fi-body,
.fi-root .fi-showcase__copy p,
.fi-root .fi-impact-copy p,
.fi-root .fi-closing__sub,
.fi-root .fi-section-head p { letter-spacing: var(--track-body, -.02em); }
.fi-section-head p { width: 100%; margin-top: 14px; }
  .fi-intro { text-align: left; }
  .fi-statement, .fi-body { max-width: none; margin-inline: 0; }
  .fi-statement { text-wrap: pretty; }
}

/* ---- 03 Challenge ---- */
/* tokens.css is loaded on this page too, and its square-corner sweep zeroes
   border-radius through an `[id] [class]` tier. Everything in this diagram
   is a circle by definition, so the three round parts are exempted at id
   specificity — no number of classes can outrank that sweep. */
:root body #fiChallenge .fi-radial__pulse i,
:root body #fiChallenge .fi-radial__core,
:root body #fiChallenge .fi-spoke__node {
  border-radius: 50% !important;
}

/* ---- Challenge: radial friction map -------------------------------------
   Five nodes on a semicircle opening upward from a single core, each with
   its label on the outside. The stage is a SQUARE: every node sits on a
   circle of radius 50% of it, so one --x/--y pair per node (set in the
   markup) places all five without any trigonometry at runtime. The labels
   overhang that square, which is why the stage is capped well short of the
   column width and overflow is left visible. */
.fi-challenge { display: block; }

.fi-radial {
  display: flex;
  justify-content: center;
  width: 100%;
  padding: clamp(18px, 3vw, 40px) 0 clamp(6px, 1.5vw, 18px);
}
.fi-radial__stage {
  position: relative;
  width: min(38vw, 360px);
  aspect-ratio: 1;
  /* Room for the labels either side, and for the pulse that runs past the
     node circle. Margin, not padding, so the square itself stays square. */
  margin: clamp(76px, 9vw, 116px) clamp(200px, 23vw, 280px) clamp(40px, 5vw, 70px);
}

/* Concentric pulse. Each ring is a share of the node circle, the faintest
   furthest out, so the core reads as the source. */
.fi-radial__orb { position: absolute; inset: 0; display: block; }
.fi-radial__pulse { position: absolute; inset: 0; }
.fi-radial__pulse i {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--ring) * 26%);
  aspect-ratio: 1;
  /* `translate`, not a transform: Motion animates `scale` on these, and a
     transform write would drop the centring offset with it. The two are
     separate properties and compose without fighting. */
  translate: -50% -50%;
  border-radius: 50%;
  background: rgba(255, 90, 77, calc(0.15 / var(--ring)));
  will-change: transform, opacity;
}
[data-theme="dark"] .fi-radial__pulse i {
  background: rgba(255, 120, 105, calc(0.17 / var(--ring)));
}

.fi-radial__core {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 23%;
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 18px 46px rgba(255, 90, 77, .28);
  color: #ff5a4d;
}
.fi-radial__core svg { width: 34%; height: auto; stroke-width: 1.7; }
[data-theme="dark"] .fi-radial__core {
  background: #f7f7f7;
  box-shadow: 0 18px 52px rgba(0, 0, 0, .55);
}

.fi-radial__spokes { position: absolute; inset: 0; margin: 0; padding: 0; list-style: none; }

/* One rule places every node: the centre plus its own --x/--y.

   The spoke itself is a zero-size anchor point and the node is centred on
   it, so the node lands exactly on the computed coordinate whichever side
   its label hangs off. Laying the two out with flex instead let the label
   push the node off its own anchor — the top node sat 39px below the
   circle, because a column box centres on the pair, not on the node. */
.fi-spoke {
  --node: clamp(48px, 5.2vw, 64px);
  --lead: clamp(12px, 1.4vw, 20px);
  position: absolute;
  top: calc(50% - var(--y));
  left: calc(50% + var(--x));
  z-index: 2;
  width: 0;
  height: 0;
}
.fi-spoke__node {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: var(--node);
  height: var(--node);
  translate: -50% -50%;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 12px 30px rgba(20, 20, 20, .10);
  color: #ff5a4d;
}
.fi-spoke__node svg { width: 42%; height: auto; stroke-width: 1.7; }
[data-theme="dark"] .fi-spoke__node {
  background: #f7f7f7;
  box-shadow: 0 14px 34px rgba(0, 0, 0, .5);
}

.fi-spoke__copy {
  position: absolute;
  top: 50%;
  width: clamp(178px, 19vw, 236px);
  /* `translate` again, for the same reason as the nodes: the label's
     entrance animates x/y, and a transform write would drop this centring
     offset with it. */
  translate: 0 -50%;
}
.fi-spoke__copy h4 {
  margin: 0 0 6px;
  font-size: clamp(15px, 1.15vw, 17px);
  font-weight: 600;
  letter-spacing: -.015em;
  line-height: 1.25;
}
.fi-spoke__copy p {
  margin: 0;
  font-size: clamp(13px, .95vw, 14.5px);
  line-height: 1.5;
  opacity: .62;
}

/* Which side the label hangs off its node. */
.fi-spoke--left .fi-spoke__copy {
  right: calc(var(--node) / 2 + var(--lead));
  text-align: right;
}
.fi-spoke--right .fi-spoke__copy {
  left: calc(var(--node) / 2 + var(--lead));
  text-align: left;
}
.fi-spoke--top .fi-spoke__copy {
  top: auto;
  bottom: calc(var(--node) / 2 + var(--lead));
  left: 50%;
  width: clamp(200px, 21vw, 260px);
  translate: -50% 0;
  text-align: center;
}

/* ---- Phone / tablet: the radial becomes a list ---------------------------
   Five labelled nodes on a circle cannot be read at phone width — the copy
   either overlaps or shrinks past legibility. The pulse stays as the
   section's image, and the frictions become an ordinary vertical list,
   which is what the content is underneath the geometry. */
@media (max-width: 900px) {
  .fi-radial { padding: 0; display: block; }
  .fi-radial__stage {
    width: auto;
    aspect-ratio: auto;
    margin: 0;
  }
  /* The orb stops filling the stage and becomes a square block of its own,
     so the list can sit underneath it rather than on top of it. */
  .fi-radial__orb {
    position: relative;
    inset: auto;
    width: min(56vw, 210px);
    aspect-ratio: 1;
    margin: 0 auto clamp(26px, 6vw, 40px);
  }
  .fi-radial__spokes {
    position: static;
    display: grid;
    gap: clamp(10px, 2.6vw, 14px);
  }
  /* The anchor-point geometry is undone: each spoke becomes an ordinary
     row, so nothing here depends on --x/--y any more. */
  .fi-spoke {
    position: static;
    display: flex;
    align-items: flex-start;
    gap: 14px;
    width: auto;
    height: auto;
    padding: clamp(14px, 3.4vw, 18px);
    border: 1px solid rgba(128, 128, 128, .16);
    border-radius: 18px;
    text-align: left;
  }
  .fi-spoke__node {
    position: static;
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    translate: none;
    transform: none;
    box-shadow: 0 8px 20px rgba(20, 20, 20, .10);
  }
  .fi-spoke__copy,
  .fi-spoke--left .fi-spoke__copy,
  .fi-spoke--right .fi-spoke__copy,
  .fi-spoke--top .fi-spoke__copy {
    position: static;
    inset: auto;
    width: auto;
    flex: 1 1 auto;
    translate: none;
    transform: none;
    text-align: left;
  }
}

/* ---- 04 Approach ---- */
.fi-approach-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
@media (max-width: 1024px) { .fi-approach-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 640px) { .fi-approach-grid { grid-template-columns: 1fr; } }
.fi-approach-card {
  background: var(--fi-bg-surface); border: 1px solid var(--fi-border); border-radius: var(--fi-r-card);
  padding: 32px 28px 36px; min-height: 280px; box-shadow: var(--fi-shadow-card);
  transition: transform .2s var(--fi-ease-out-quart), box-shadow .2s ease;
}
.fi-approach-card:hover { transform: translateY(-4px); box-shadow: 0 12px 32px rgba(0,0,0,0.07); }
[data-theme="dark"] .fi-approach-card:hover { box-shadow: 0 12px 32px rgba(0,0,0,0.6); }
.fi-blob {
  width: 76px; height: 76px; border-radius: 50%; background: var(--fi-glow-approach);
  display: flex; align-items: center; justify-content: center; color: var(--fi-text-primary);
}
.fi-blob svg { width: 24px; height: 24px; }
.fi-approach-card h3 { font-size: var(--fi-f-card-title); font-weight: 500; margin: 64px 0 10px; }
.fi-approach-card p { font-size: var(--fi-f-card-body); line-height: 1.6; color: var(--fi-text-tertiary); margin: 0; }

/* ---- 05 Solution ---- */
.fi-solution { display: grid; grid-template-columns: .32fr .68fr; gap: 40px; align-items: start; }
@media (max-width: 900px) { .fi-solution { grid-template-columns: 1fr; } }
.fi-accordion { display: flex; flex-direction: column; }
.fi-accordion__item {
  display: block; text-align: left; background: none; border: none; padding: 0; cursor: pointer;
  font-family: inherit; color: var(--fi-text-secondary); margin-bottom: 26px; position: relative;
}
.fi-accordion__item:not(:first-child) { margin-top: 0; }
.fi-accordion__item + .fi-accordion__item { margin-top: 0; }
.fi-accordion__item ~ .fi-accordion__item:nth-child(2) { margin-top: 48px; }
.fi-accordion__title { display: block; font-size: 14px; line-height: 1.4; }
.fi-accordion__item.is-active { color: var(--fi-text-primary); padding-left: 0; }
.fi-accordion__item.is-active .fi-accordion__title { font-size: var(--fi-f-card-title); font-weight: 500; }
/* Collapses on grid-template-rows (0fr → 1fr) rather than max-height.

   Two reasons. Animating max-height and margin-top drove layout on every
   frame of the open; and the 140px ceiling it needed was a guess — this
   copy already runs close to it, so any longer entry, a narrower column
   or a larger type scale would have silently clipped the text with no
   visual cue that anything was missing. A grid row resolves to the
   content's own height, so it animates to exactly the right size and can
   never truncate. The spacing that used to be an animated margin-top is
   now padding inside the collapsing row, so it comes and goes with it
   instead of pushing layout separately.

   Where fr interpolation is unsupported the row simply snaps open —
   no animation, but the content is complete and correct, which is the
   better failure. */
.fi-accordion__body {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  font-size: var(--fi-f-card-body); line-height: 1.6; color: var(--fi-text-tertiary); max-width: 260px;
  transition: grid-template-rows .32s var(--fi-ease-out-quart), opacity .32s ease;
}
/* min-height:0 defeats a grid item's automatic minimum size, which would
   otherwise hold the row open at content height and defeat the collapse.
   The grid item itself must also carry NO padding or margin: those are
   outside the content box that min-height:0 collapses, so they survive
   into the track and hold the row open — with the 10px spacing here the
   closed accordion measured 10px instead of 0. The spacing therefore sits
   on an inner element, inside the clipped area, where it collapses with
   the row. */
.fi-accordion__body-inner { overflow: hidden; min-height: 0; }
.fi-accordion__body-text { display: block; padding-top: 10px; }
.fi-accordion__item.is-active .fi-accordion__body { grid-template-rows: 1fr; opacity: 1; }
.fi-accordion__progress {
  display: none !important;
}

.fi-solution__panel {
  aspect-ratio: 16 / 10; border-radius: var(--fi-r-panel); overflow: hidden; position: relative;
  background: linear-gradient(135deg,#ffe08a 0%,#ff8a7a 35%,#7ab8ff 70%,#a6f0c6 100%);
  background-size: 220% 220%;
  box-shadow: var(--fi-shadow-panel);
}

/* Intermediate widths keep the editorial grid but spend less space on
   ornamental gaps, allowing the content to use the viewport cleanly. */
@media (max-width: 1180px) {
  .fi-challenge { gap: clamp(28px, 4vw, 54px); }
  .fi-card { padding: 22px 22px; }
  .fi-solution { gap: 28px; grid-template-columns: minmax(240px, .34fr) minmax(0, .66fr); }
  .fi-approach-grid, .fi-impact-blocks, .fi-impact-media { gap: 14px; }
}

@media (max-width: 900px) {
  .fi-solution { grid-template-columns: 1fr; gap: 30px; }
  .fi-solution__panel { width: 100%; }
  .fi-accordion__item ~ .fi-accordion__item:nth-child(2) { margin-top: 22px; }
}

@media (max-width: 640px) {
  .fi-section-head { margin-bottom: 36px; }
  .fi-friction-eyebrow { margin-bottom: 20px; }
  .fi-card { padding: 22px 20px; }
  .fi-impact-blocks { grid-template-columns: 1fr; gap: 24px; }
  .fi-solution__panel { aspect-ratio: 4 / 3; border-radius: 14px; }
  .fi-dash { top: 24px; left: 24px; }
  .fi-dash__kpis { grid-template-columns: repeat(3, 1fr); }
  .fi-kpi:nth-child(n+4) { display: none; }
  .fi-closing { padding-top: 88px; padding-bottom: 88px; }
  
  .fi-storyboard { gap: 12px; padding: 20px; }
  .fi-storyboard__browser { width: 64%; }
  .fi-storyboard__tablet { width: 32%; padding: 8px; }
}
.fi-dash {
  position: absolute; top: 44px; left: 44px; right: -8px; bottom: -8px;
  border-top-left-radius: var(--fi-r-media); overflow: hidden; box-shadow: var(--fi-shadow-panel);
  background: #101317; color: #cfd6de; font-size: 10px; display: flex; flex-direction: column;
}
.fi-dash__topbar { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border-bottom: 1px solid rgba(255,255,255,.08); }
.fi-dash__brand { font-weight: 500; color: #fff; }
.fi-dash__nav { display: flex; gap: 8px; }
.fi-dash__nav i { width: 24px; height: 6px; border-radius: 3px; background: rgba(255,255,255,.14); display: block; }
.fi-dash__body { display: flex; flex: 1; }
.fi-dash__side { width: 34px; padding: 12px 0; display: flex; flex-direction: column; gap: 14px; align-items: center; border-right: 1px solid rgba(255,255,255,.08); }
.fi-dash__side i { width: 14px; height: 14px; border-radius: 4px; background: rgba(255,255,255,.14); display: block; }
.fi-dash__main { flex: 1; padding: 12px; display: flex; flex-direction: column; gap: 10px; overflow: hidden; }
.fi-dash__kpis { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
.fi-kpi { background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08); border-radius: 6px; padding: 6px 8px; }
.fi-kpi span { display: block; color: rgba(255,255,255,.45); font-size: 8px; margin-bottom: 3px; }
.fi-kpi strong { font-size: 12px; color: #fff; font-weight: 500; }
.fi-dash__row { display: grid; grid-template-columns: 1.4fr 1fr; gap: 8px; flex: 1; min-height: 0; }
.fi-dash__map, .fi-dash__chart, .fi-dash__table, .fi-dash__donut {
  background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08); border-radius: 6px; overflow: hidden;
}
.fi-dash__map svg, .fi-dash__chart svg { width: 100%; height: 100%; display: block; }
.fi-dash__route { stroke: #6ee43f; stroke-width: 1.6; fill: none; }
.fi-dash__map circle { fill: #6ee43f; }
.fi-dash__chart polyline { stroke: #7ab8ff; }
.fi-dash__table { display: flex; flex-direction: column; gap: 5px; padding: 8px; justify-content: center; }
.fi-dash__table i { height: 5px; border-radius: 3px; background: rgba(255,255,255,.1); display: block; }
.fi-dash__donut { display: flex; align-items: center; justify-content: center; }
.fi-dash__donut svg { width: 60%; }
.fi-donut__track { stroke: rgba(255,255,255,.1); }
.fi-donut__value { stroke: #6ee43f; stroke-linecap: round; }

/* Slow drifting mesh on the gradient frame */
@keyframes fi-mesh-drift { to { background-position: 100% 100%; } }
.fi-solution__panel { animation: fi-mesh-drift 18s linear infinite alternate; }

/* ---- 06 Impact ---- */
.fi-impact-blocks { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-bottom: 24px; }
@media (max-width: 768px) { .fi-impact-blocks { grid-template-columns: 1fr 1fr; } }
.fi-impact-block h3 { font-size: var(--fi-f-card-title); font-weight: 500; margin: 0 0 8px; }
.fi-impact-block p { font-size: var(--fi-f-card-body); line-height: 1.6; color: var(--fi-text-tertiary); margin: 0; }

.fi-impact-media { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
.fi-impact-media__item--1 { grid-column: 1; aspect-ratio: 3 / 4; }
.fi-impact-media__item--2 { grid-column: 2 / 4; aspect-ratio: 3 / 2; }
.fi-impact-media__item--3 { grid-column: 4; aspect-ratio: 3 / 4; }
.fi-impact-media__item { border-radius: var(--fi-r-media); overflow: hidden; background: var(--fi-bg-inset); }
@media (max-width: 768px) {
  .fi-impact-media { grid-auto-flow: column; grid-auto-columns: 78%; overflow-x: auto; scroll-snap-type: x mandatory; grid-template-columns: none; }
  .fi-impact-media__item { grid-column: auto !important; scroll-snap-align: start; aspect-ratio: 4/5 !important; }
}
.fi-impact-media__item .fi-media-placeholder { position: static; height: 100%; }

.fi-report-mini { height: 100%; background: linear-gradient(160deg,#0f3d38,#0a2624); color: #bfe9df; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.fi-report-mini__head { font-size: 11px; font-weight: 500; color: #e7fbf5; }
.fi-report-mini__stats { display: flex; gap: 8px; font-size: 15px; font-weight: 500; color: #fff; }
.fi-report-mini__chart svg { width: 100%; height: 40px; }
.fi-report-mini__chart polyline { stroke: #6ee43f; }
.fi-report-mini__rows { display: flex; flex-direction: column; gap: 6px; margin-top: auto; }
.fi-report-mini__rows i { height: 6px; border-radius: 3px; background: rgba(255,255,255,.12); display: block; }

.fi-cards-mini { height: 100%; background: linear-gradient(160deg,#efe9ff,#d9d3fb); display: flex; flex-direction: column; gap: 10px; padding: 14px; }
[data-theme="dark"] .fi-cards-mini { background: var(--fi-bg-inset); }
.fi-cards-mini__card { flex: 1; background: rgba(255,255,255,.7); border-radius: 8px; padding: 10px; display: flex; flex-direction: column; gap: 8px; }
[data-theme="dark"] .fi-cards-mini__card { background: rgba(255,255,255,.06); }
.fi-cards-mini__card i:first-child { height: 18px; width: 60%; border-radius: 4px; background: rgba(80,60,180,.25); display: block; }
.fi-cards-mini__card i:last-child { flex: 1; border-radius: 6px; background: rgba(80,60,180,.14); display: block; }

/* ---- 07 Closing ---- */
.fi-closing { text-align: center; padding-top: 120px; padding-bottom: 120px; }
.fi-closing .fi-statement { color: var(--fi-text-primary) !important; }
.fi-closing .fi-statement > span { color: inherit !important; }
.fi-closing__sub { font-size: 13px; color: var(--fi-text-tertiary); max-width: 560px; margin: 16px auto 0; line-height: 1.6; }

.fi-storyboard { height: 100%; display: flex; align-items: center; justify-content: center; gap: 24px; padding: 40px; }
.fi-storyboard__browser, .fi-storyboard__tablet { background: #262626; border-radius: 10px; overflow: hidden; }
.fi-storyboard__browser { width: 58%; }
.fi-storyboard__tablet { width: 30%; padding: 14px; }
.fi-storyboard__chrome { display: flex; gap: 5px; padding: 8px 10px; }
.fi-storyboard__chrome i { width: 7px; height: 7px; border-radius: 50%; background: #444; display: block; }
.fi-storyboard__panel { padding: 0 14px 16px; }
.fi-storyboard__panel span, .fi-storyboard__tablet > span { display: block; font-size: 10px; color: #9a9a9a; margin-bottom: 8px; }
.fi-storyboard__tiles { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
.fi-storyboard__tiles--tablet { grid-template-columns: repeat(2, 1fr); }
.fi-storyboard__tiles i { aspect-ratio: 1; border-radius: 5px; display: block; }
.fi-root .t1 { background: linear-gradient(135deg,#4a3b2e,#7a5c3e); }
.fi-root .t2 { background: linear-gradient(135deg,#2e4a3b,#5c8a6e); }
.fi-root .t3 { background: linear-gradient(135deg,#3b2e4a,#6e5c8a); }
.fi-root .t4 { background: linear-gradient(135deg,#4a2e2e,#8a5c5c); }
.fi-root .t5 { background: linear-gradient(135deg,#2e3b4a,#5c748a); }

/* ---- 08 Continue exploring ---- */

@media (max-width: 640px) {
  .fi-root .fi-impact-blocks { grid-template-columns: 1fr; gap: 24px; }
  .fi-root .fi-closing { padding-top: 88px; padding-bottom: 88px; }
  
  .fi-root .fi-storyboard { gap: 12px; padding: 20px; }
  .fi-root .fi-storyboard__browser { width: 64%; }
  .fi-root .fi-storyboard__tablet { width: 32%; padding: 8px; }
}

/* ---- Reduced motion: strip everything to a plain fade, content stays put ---- */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .12s !important; }
}

/* =====================================================================
   2026 refinement: fixed case chrome, a single red illustration language,
   stateful solution artwork, and the four-outcome Impact bento.
   ===================================================================== */
[data-theme="dark"] {
  --fi-accent: #ff665b;
  --fi-glow-friction: radial-gradient(circle, rgba(255,102,91,.24) 0%, rgba(255,102,91,0) 70%);
}

[data-theme="dark"] .fi-chrome.is-on-light .fi-theme,
[data-theme="dark"] .fi-chrome.is-on-light .fi-close,
[data-theme="dark"] .fi-theme,
[data-theme="dark"] .fi-close {
  background: rgba(18,18,18,.78);
  border-color: rgba(255,255,255,.24);
  color: #fff;
  box-shadow: 0 8px 28px rgba(0,0,0,.28);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}
[data-theme="dark"] .fi-chrome.is-on-light .fi-theme:hover,
[data-theme="dark"] .fi-chrome.is-on-light .fi-close:hover,
[data-theme="dark"] .fi-theme:hover,
[data-theme="dark"] .fi-close:hover { background: rgba(36,36,36,.92); }

.fi-intro { max-width: var(--fi-container); }
/* Both run the full intro width: the ch measures were wrapping a short
   statement onto three lines and the body onto more than it needs. */
.fi-intro .fi-statement { max-width: 100%; }
.fi-intro .fi-body { max-width: 100%; }

/* The hero frame now stays optically locked. JS no longer moves its image
   or copy, and this explicit transform prevents stale inline values from a
   cached module from affecting a freshly injected overlay. */
.fi-hero__media-img,
.fi-hero__content { transform: none !important; }

/* Challenge illustration: fills carry the form, so the orbit rings and
   nodes need no outlines or ornamental corner glow. */
.fi-arc { stroke: none; }
[data-theme="dark"] .fi-arc { stroke: none; }
[data-theme="dark"] .fi-arc--outer { fill: rgba(255,102,91,.075); }
[data-theme="dark"] .fi-arc--middle { fill: rgba(255,102,91,.13); }
[data-theme="dark"] .fi-arc--inner { fill: rgba(255,102,91,.23); }
.fi-diagram__center,
.fi-chip__inner {
  border: 0;
  box-shadow: none;
}
.fi-chip.is-highlight .fi-chip__inner {
  border: 0;
  box-shadow: none;
  background: color-mix(in srgb, var(--fi-accent) 12%, var(--fi-bg-surface));
}

/* Solution state ----------------------------------------------------- */
.fi-accordion__item {
  transition: color .34s ease, transform .46s var(--fi-ease-out-quart);
}
.fi-accordion__title {
  transform: translateY(0);
  transition: opacity .32s ease, transform .48s var(--fi-ease-out-quart), font-size .32s var(--fi-ease-out-quart);
}
.fi-accordion__item:not(.is-active) .fi-accordion__title { opacity: .62; }
.fi-accordion__item:not(.is-active):hover .fi-accordion__title { opacity: .9; transform: translateX(4px); }
.fi-accordion__item.is-active .fi-accordion__title { opacity: 1; transform: translateY(0); }
.fi-accordion__body {
  transition: grid-template-rows .52s var(--fi-ease-out-quart), opacity .34s ease;
}
.fi-accordion__body-text {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity .34s .02s ease, transform .5s var(--fi-ease-out-quart);
}
.fi-accordion__item.is-active .fi-accordion__body-text { opacity: 1; transform: translateY(0); transition-delay: .12s; }

.fi-solution__panel {
  background: linear-gradient(145deg, #fff1ef 0%, #f4f5f6 68%, #ffe3df 100%);
  animation: none;
  box-shadow: 0 24px 70px rgba(45,25,22,.11);
}
[data-theme="dark"] .fi-solution__panel {
  background: linear-gradient(145deg, #241918 0%, #131518 64%, #1e1414 100%);
}
.fi-solution-art {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: clamp(18px, 3vw, 42px);
  opacity: 0;
  visibility: hidden;
  transform: scale(.975) translateY(8px);
  transition: opacity .36s ease, visibility 0s .36s, transform .52s var(--fi-ease-out-quart);
}
.fi-solution-art.is-active {
  opacity: 1;
  visibility: visible;
  transform: scale(1) translateY(0);
  transition-delay: .06s, 0s, .06s;
}
.fi-solution-art svg,
.fi-impact-svg { width: 100%; height: 100%; overflow: visible; }

/* Shared SVG material system. Red is the only chromatic accent. */
.fi-svg-shell { fill: var(--fi-bg-surface); stroke: var(--fi-border); stroke-width: 1.2; }
.fi-svg-card { fill: color-mix(in srgb, var(--fi-bg-surface) 76%, var(--fi-bg-inset)); }
.fi-svg-soft { fill: color-mix(in srgb, var(--fi-accent) 9%, var(--fi-bg-surface)); }
.fi-svg-accent { fill: var(--fi-accent); }
.fi-svg-draw,
.fi-svg-accent-line,
.fi-svg-check {
  fill: none;
  stroke: var(--fi-text-secondary);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.fi-svg-accent-line { stroke: var(--fi-accent); }
.fi-svg-check { stroke: #fff; stroke-width: 4; }
.fi-svg-bars rect { fill: var(--fi-accent); transform-origin: center bottom; }

.is-active .fi-svg-draw,
.is-active .fi-svg-accent-line,
.fi-impact-tile.is-animated .fi-svg-draw,
.fi-impact-tile.is-animated .fi-svg-accent-line,
.fi-impact-tile.is-animated .fi-svg-check {
  stroke-dasharray: 720;
  stroke-dashoffset: 720;
  animation: fi-svg-draw .9s var(--fi-ease-out-quart) forwards;
}
.is-active .fi-svg-rise,
.fi-impact-tile.is-animated .fi-svg-rise { animation: fi-svg-rise .7s var(--fi-ease-out-quart) both; }
.is-active .fi-svg-pop,
.fi-impact-tile.is-animated .fi-svg-pop { animation: fi-svg-pop .62s var(--fi-ease-out-expo) both; }
.is-active .fi-svg-bars rect { animation: fi-svg-bar .72s var(--fi-ease-out-quart) both; }
.is-active .fi-svg-bars rect:nth-child(2) { animation-delay: .08s; }
.is-active .fi-svg-bars rect:nth-child(3) { animation-delay: .16s; }
.is-active .fi-svg-bars rect:nth-child(4) { animation-delay: .24s; }
.fi-delay-1 { animation-delay: .1s !important; }
.fi-delay-2 { animation-delay: .2s !important; }
.fi-delay-3 { animation-delay: .3s !important; }
@keyframes fi-svg-draw { to { stroke-dashoffset: 0; } }
@keyframes fi-svg-rise { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fi-svg-pop { 0% { opacity: 0; transform: scale(.45); } 72% { opacity: 1; transform: scale(1.12); } 100% { opacity: 1; transform: scale(1); } }
@keyframes fi-svg-bar { from { opacity: .2; transform: scaleY(.12); } to { opacity: 1; transform: scaleY(1); } }

/* Impact bento ------------------------------------------------------- */
.fi-impact-bento {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(220px, 1fr)) minmax(250px, .8fr);
  gap: 18px;
}
.fi-impact-tile {
  position: relative;
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--fi-border);
  border-radius: var(--fi-r-panel);
  background: var(--fi-bg-surface);
  padding: clamp(22px, 2.2vw, 34px);
  opacity: 0;
  transform: translateY(24px) scale(.985);
  transition: border-color .35s ease, transform .5s var(--fi-ease-out-quart);
}
.fi-impact-tile.is-animated { opacity: 1; transform: translateY(0) scale(1); }
.fi-impact-tile--unified { grid-column: 1 / span 7; grid-row: 1 / span 2; }
.fi-impact-tile--handoff { grid-column: 8 / -1; grid-row: 1; background: color-mix(in srgb, var(--fi-accent) 5%, var(--fi-bg-surface)); }
.fi-impact-tile--scale { grid-column: 8 / -1; grid-row: 2; }
.fi-impact-tile--devices { grid-column: 1 / -1; grid-row: 3; background: color-mix(in srgb, var(--fi-accent) 4%, var(--fi-bg-surface)); }
.fi-impact-copy { position: relative; z-index: 2; }
.fi-impact-copy h3 { margin: 0 0 7px; font-size: clamp(18px, 1.6vw, 24px); font-weight: 500; letter-spacing: -.025em; }
.fi-impact-copy p { margin: 0; color: var(--fi-text-tertiary); font-size: var(--fi-f-card-body); line-height: 1.5; }
.fi-impact-svg { display: block; min-height: 0; margin-top: 14px; }
.fi-impact-tile--unified .fi-impact-svg { height: calc(100% - 58px); min-height: 310px; }
.fi-impact-tile--handoff .fi-impact-svg,
.fi-impact-tile--scale .fi-impact-svg { height: 145px; }
.fi-impact-tile--devices { display: grid; grid-template-columns: minmax(210px, .32fr) minmax(0, .68fr); align-items: center; gap: 24px; }
.fi-impact-tile--devices .fi-impact-svg { height: 210px; margin: 0; }
@media (hover: hover) and (pointer: fine) {
  .fi-impact-tile:hover { transform: translateY(-4px); border-color: color-mix(in srgb, var(--fi-accent) 28%, var(--fi-border)); }
}

@media (max-width: 900px) {
  .fi-impact-bento { grid-template-columns: 1fr 1fr; grid-template-rows: auto; }
  .fi-impact-tile--unified,
  .fi-impact-tile--handoff,
  .fi-impact-tile--scale,
  .fi-impact-tile--devices { grid-column: auto; grid-row: auto; }
  .fi-impact-tile--unified { grid-column: 1 / -1; }
  .fi-impact-tile--devices { grid-column: 1 / -1; }
}
@media (max-width: 640px) {
  .fi-impact-bento { grid-template-columns: 1fr; gap: 14px; }
  .fi-impact-tile--unified,
  .fi-impact-tile--devices { grid-column: auto; }
  .fi-impact-tile--devices { display: block; }
  .fi-impact-tile--unified .fi-impact-svg { min-height: 230px; }
  .fi-impact-tile--devices .fi-impact-svg { height: 180px; margin-top: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .fi-solution-art { transition: none; }
  .fi-svg-draw, .fi-svg-accent-line, .fi-svg-check { stroke-dashoffset: 0 !important; }
  .fi-impact-tile { opacity: 1; transform: none; }
}

/* ---------------------------------------------------------------------
   Cover morph destination.

   index.html opts the site into MPA view transitions and the Work grid
   hands `case-study-cover` to whichever card links to a detail page.
   That name has to be claimed on BOTH sides or the browser skips the
   transition and rejects its promise — which is where the
   "AbortError: Transition was skipped" in the console came from. This is
   the receiving half.
   --------------------------------------------------------------------- */
.fi-hero__media-img { view-transition-name: case-study-cover; }

/* =====================================================================
   Cards carry no stroke

   The plates are already separated from the page by their own surface
   colour; the 1px outline on top of that read as a drawn box. Removed
   from every card surface — approach, friction, and the impact tiles —
   in both themes.
   ===================================================================== */
.fi-approach-card,
.fi-card,
.fi-impact-tile {
  border: 0 !important;
}

/* =====================================================================
   Challenge diagram — the chips and hub are circles

   Both were authored as border-radius: 50% above, but tokens.css runs an
   id-scoped square-radius sweep with !important over everything inside a
   page section, which flattened them into squares. Beating it needs an id
   plus repeated attribute selectors — the same lift used for the FAQ icon
   in css/playground-all.css.
   ===================================================================== */
:root body #fiDiagram .fi-chip__inner[class][class],
:root body #fiDiagram .fi-diagram__center[class][class],
:root body #fiChallenge .fi-chip__inner[class][class],
:root body #fiChallenge .fi-diagram__center[class][class] {
  border-radius: 50% !important;
}

/* =====================================================================
   Display type matches the homepage

   The homepage puts every section title in Zalando Sans Expanded 600 via
   --font-title, and keeps Geist for body copy, card titles and the mono
   eyebrow labels. This page was setting everything from --font-display
   (Geist), so its headings read in the body face. Same split applied
   here: page title and section titles take the display cut, everything
   else is unchanged.
   ===================================================================== */
.fi-root #fiHero .fi-hero__content h1.fi-hero__title,
.fi-root .fi-section-head h2,
.fi-root #fiNextTitle {
  font-family: var(--font-title) !important;
  font-weight: 600 !important;
}

/* =====================================================================
   Round controls stay round

   tokens.css squares everything inside a page section with an id-scoped
   !important sweep. Anything authored as a circle — the hover bubble on
   the next-case cards, the theme and close controls, any play/media
   button added later — has to out-specify it. Listed together so a new
   round control only needs its class adding here rather than a fresh
   rule of its own.
   ===================================================================== */

:root body .fi-play[class][class],
:root body .fi-media-play[class][class],
:root body .fi-round[class][class],
:root body .fi-theme-toggle[class][class],
:root body .fi-close[class][class] {
  border-radius: 50% !important;
}

/* The other half of the cover morph: a cross-document view transition
   runs only when both documents opt in. The homepage declares the same
   in css/showcase-sections.css. */
@view-transition { navigation: auto; }

::view-transition-group(case-study-cover) {
  animation-duration: .62s;
  animation-timing-function: var(--fi-ease-out-expo);
}
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: .42s;
}

/* =====================================================================
   05 Solution — work cards + design-system band
   Mirrors the Figma board: four 669×456 media cards in a 2×2 grid with
   the caption above the art, then the design system in two rows whose
   column split matches the board (72/28, then 46/54).
   ===================================================================== */
.fi-showcase {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(28px, 3.4vw, 52px) clamp(20px, 2.4vw, 40px);
}
.fi-showcase__copy h3 {
  margin: 0 0 8px;
  font-size: var(--fi-f-card-title);
  font-weight: 600;
  letter-spacing: var(--track-heading, -.035em);
}
.fi-showcase__copy p {
  margin: 0;
  /* Runs the full width of the card's media below it — the 62ch measure
     was breaking these two-line blurbs well short of the image edge. */
  max-width: none;
  font-size: var(--fi-f-card-body);
  line-height: 1.6;
  color: var(--fi-text-tertiary);
}
.fi-showcase__media {
  margin: 0 0 18px;
  position: relative;
  aspect-ratio: 669 / 456;
  border-radius: var(--fi-r-media);
  overflow: hidden;
  background: var(--fi-media-bg);
  border: 0;
}
.fi-showcase__media img { width: 100%; height: 100%; display: block; }
/* Each card is exported at the board's own 669×456 frame, backdrop and
   inset included, so the box matches that ratio exactly and the image
   fills it edge to edge — no second crop of our own. */
.fi-showcase__media img { object-fit: cover; object-position: center; }

.fi-ds { margin-top: clamp(48px, 5vw, 86px); }
.fi-ds__grid {
  margin-bottom: 18px;
  display: grid;
  grid-template-columns: repeat(24, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 24px);
}
/* The band is the grey ground; each panel is a white card on it, with
   its title set in the page's own type rather than baked into the art. */
.fi-ds__grid {
  background: var(--fi-media-bg);
  padding: clamp(14px, 1.6vw, 24px);
}
:root body #fi-content .fi-ds__grid[class] { border-radius: var(--fi-r-tile) !important; }
.fi-ds__panel {
  margin: 0;
  padding: clamp(16px, 1.8vw, 28px);
  overflow: hidden;
  background: var(--fi-bg-surface);
  border: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(14px, 1.6vw, 24px);
}
.fi-ds__panel figcaption {
  font-size: var(--fi-tile-title);
  font-weight: 600;
  letter-spacing: var(--track-heading, -.035em);
  color: var(--fi-text-primary);
}
.fi-ds__panel img { margin-top: auto; }
.fi-ds__panel img { width: 100%; height: auto; display: block; object-fit: contain; }
.fi-ds__panel--color  { grid-column: span 17; }
.fi-ds__panel--icons  { grid-column: span 7; }
.fi-ds__panel--type   { grid-column: span 11; }
.fi-ds__panel--inputs { grid-column: span 13; }

@media (max-width: 900px) {
  .fi-showcase { grid-template-columns: 1fr; }
  .fi-ds__panel--color,
  .fi-ds__panel--icons,
  .fi-ds__panel--type,
  .fi-ds__panel--inputs { grid-column: 1 / -1; }
}

/* The corners here are deliberately larger than --fi-r-media and, where
   the browser supports it, continuous rather than circular: a plain
   circular arc meets the straight edge at a visible kink, which is what
   read as "sharp". corner-shape blends that junction; browsers without
   it keep the same radius and simply lose the smoothing. */
.fi-root {
  --fi-r-tile: 24px;
  --fi-r-shot: 16px;
  --fi-tile-pad: clamp(18px, 1.9vw, 28px);
  /* One title size and one body size across all three tiles — the wide
     tile no longer gets its own step. Both are capped so the narrowest
     column still holds "Scalable Foundation" on a single line. */
  --fi-tile-title: clamp(16px, 1.42vw, 20px);
  --fi-tile-body: clamp(13px, 1.05vw, 15px);
  --fi-tile-lh: 1.55;
}
@supports (corner-shape: squircle) {
  .fi-root .fi-impact-tile,
  .fi-impact-img,
  .fi-impact-tile__art { corner-shape: squircle; }
}

/* =====================================================================
   06 Impact — three tiles at the board's 350 / 713 / 273 proportions
   Each tile is a stage: screenshots positioned by percentage (the same
   offsets as Figma), a fade that dissolves their lower half into the
   tile, and the captions resting on top of that fade.
   ===================================================================== */
.fi-root .fi-impact-bento {
  display: grid;
  grid-template-columns: 350fr 713fr 330fr;
  /* The bento this replaced was a three-row layout and its row template
     is still in the cascade above; every tile now sits in row 1, so the
     two reserved rows were holding ~1000px of empty grid under them. */
  grid-template-rows: auto;
  gap: clamp(14px, 1.6vw, 24px);
}
.fi-root .fi-impact-tile {
  position: relative;
  grid-column: auto;
  grid-row: auto;
  display: flex;
  flex-direction: column;
  gap: var(--fi-tile-pad);
  /* One inset on every side. The tile used to be a fixed 350/498 box with
     the copy floating inside it, which left a tall dead band under the
     last line once the titles were aligned; its height now comes from its
     own content. */
  padding: var(--fi-tile-pad);
  border-radius: var(--fi-r-tile);
  overflow: hidden;
  --fi-tile-bg: var(--fi-impact-tint);
  background: var(--fi-tile-bg);
  background-repeat: no-repeat;
  border: 0;
  box-shadow: var(--fi-shadow-card);
}
/* The warm tiles either side define their own edge with colour; this one
   resolves to white at the bottom, so a hairline keeps it from bleeding
   into the page. */
.fi-root .fi-impact-tile--wide {
  --fi-tile-bg: var(--fi-impact-neutral);
  --fi-tile-fade: #ffffff;
}
/* Stated at id specificity: the same tokens.css sweep that flattens
   corners also zeroes borders, and a class-level rule here loses to it. */
:root body #fi-content .fi-impact-tile--wide[class] {
  border: 1px solid var(--fi-border) !important;
}
.fi-root .fi-impact-tile--wide  .fi-impact-tile__art { min-height: 62cqi; }
.fi-root .fi-impact-tile--scale .fi-impact-tile__art { min-height: 165cqi; }
/* The tiles are grid items and stretch to the tallest one, so the art —
   not the padding — absorbs the difference. That keeps the inset equal on
   all four sides of every tile no matter how long its copy runs. */
.fi-impact-tile__art {
  position: relative;
  margin: calc(-1 * var(--fi-tile-pad)) calc(-1 * var(--fi-tile-pad)) 0;
  border-radius: var(--fi-r-tile) var(--fi-r-tile) 0 0;
  flex: 1 1 auto;
  /* Floor taken from the board's own art band (289 against each tile's
     width), so the screenshots keep their scale instead of collapsing to
     whatever the shortest column allows. */
  min-height: 128cqi;
  overflow: hidden;
  border-radius: var(--fi-r-shot);
}
.fi-impact-img {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  height: auto;
  display: block;
  border-radius: var(--fi-r-shot);
  /* These exports are the rectangles' image FILLS — flat, fully opaque
     white, because the rounded corners and drop shadow were Figma effects
     on the node and effects are not part of a fill. The corner comes back
     from the radius above; the shadow has to be re-stated here, or the
     white sheets read as one merged blob instead of a stack. */
  box-shadow: 0 1px 2px rgba(16, 16, 16, .05), 0 12px 32px rgba(16, 16, 16, .10);
}
/* These three sheets were re-exported with their corner and shadow
   composited into the PNG (transparent margins), so the CSS pair above
   would double them. */
.fi-impact-img--baked { box-shadow: none; }
/* The screenshots run under the copy rather than stopping above it, so
   the fade — not a hard edge — is what separates art from caption. */
.fi-impact-tile__fade {
  position: absolute;
  /* Sits inside the art box and dissolves the lower third of the sheets
     into the tile, so the band ends in colour rather than a cut edge. */
  inset: 68% 0 0;
  background: linear-gradient(180deg, transparent 0%, var(--fi-tile-fade, var(--fi-tile-bg)) 72%);
  pointer-events: none;
}
/* The tile copy uses the site's own type tokens rather than a scale
   derived from the board: --fs-lede for the title, --fs-meta for the
   body, the same pairing the homepage cards use. */
.fi-root .fi-impact-tile { container-type: inline-size; }
.fi-impact-captions {
  position: static;
  padding: 0;
  display: grid;
  align-content: start;
  /* Reserves the height of the longest copy (a title plus five lines of
     --fs-meta) on every tile, so the art bands end level and the titles
     share one baseline while each tile keeps the same inset all round. */
  min-height: calc(var(--fi-tile-title) * 1.25 + .55em + 5 * var(--fi-tile-lh) * var(--fi-tile-body));
  gap: clamp(18px, 6.7cqi, 40px);
}
.fi-impact-captions--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.fi-root .fi-impact-copy h3 {
  font-size: var(--fi-tile-title);
  font-weight: 600;
  line-height: 1.25;
  white-space: nowrap;
  letter-spacing: -.02em;
  margin-bottom: .5em;
  color: var(--fi-text-primary);
}
/* The tint behind this copy is a light yellow, where the site's muted
   grey drops to roughly 3:1. Mixing the body toward the heading colour
   keeps the hierarchy but clears AA comfortably on every tile. */
.fi-root .fi-impact-copy p {
  font-size: var(--fi-tile-body);
  line-height: var(--fi-tile-lh);
  color: color-mix(in srgb, var(--fi-text-primary) 82%, transparent);
}

/* Below the bento's own width the overlap stops working: the copy is
   taller than the fade can cover, so the tile unstacks — art on top at a
   fixed ratio, copy beneath it in normal flow — instead of letting text
   land on the screenshots. */
@media (max-width: 900px) {
  .fi-root .fi-impact-bento { grid-template-columns: 1fr; }
  .fi-root .fi-impact-tile__art { flex: none; aspect-ratio: 4 / 3; }
  .fi-root .fi-impact-copy h3 { white-space: normal; }
  .fi-impact-captions--pair { grid-template-columns: 1fr; gap: 14px; }
}

/* ---- 07 Closing media ---- */
/* The closing frame now carries the real report export, so it takes that
   image's own proportions (840 × 549 on the board) instead of a 16/9 crop
   that would cut the KPI row off. */

/* ---------------------------------------------------------------------
   Impact tiles: opting out of the site-wide square sweep

   tokens.css flattens every corner on the site with
   `border-radius: 0 !important` across four specificity tiers, the top
   one id-scoped (`:root body [id] [class][class]`). These tiles sit
   inside main#fi-content, so that tier was winning — which is why the
   tiles and screenshots rendered sharp however large a radius was set
   below. The opt-out therefore has to carry an id of its own plus
   !important; it is deliberately narrow, naming only the impact tiles so
   the rest of the page keeps the square language.
   ------------------------------------------------------------------ */
:root body #fi-content .fi-impact-tile[class][class],
:root body #fi-content .fi-impact-tile__art[class][class],
:root body #fi-content .fi-showcase__media[class][class],
:root body #fi-content .fi-ds__panel[class][class]{
  border-radius: var(--fi-r-tile) !important;
}
:root body #fi-content .fi-impact-img[class][class],
:root body #fi-content .fi-showcase__media img[class],
:root body #fi-content .fi-ds__panel img[class]{
  border-radius: var(--fi-r-shot) !important;
}
:root body #fi-content .fi-impact-img--baked[class][class] { border-radius: 0 !important; }
@supports (corner-shape: squircle) {
  .fi-root .fi-showcase__media,
  .fi-root .fi-ds__panel{ corner-shape: squircle; }
}
/* An inset panel's art sits on the panel's own padding, so it keeps the
   smaller shot radius; a bleed crop is clipped by the container itself
   and must not round twice inside it. */
:root body #fi-content .fi-showcase__media--bleed img[class],
:root body #fi-content .fi-showcase__media--offset img[class] {
  border-radius: 0 !important;
}

/* A deeper, earlier fade than the one the live frame uses: it starts at a
   third of the way down and is fully page colour before the text begins. */

/* The case study also opens as an overlay inside the homepage, where the
   host page's own surface (#f4f4f4) was showing through — the same tone
   the Solution media boxes use, so the cards merged into the background.
   Both the overlay shell and the content column state the page colour. */
:root body .fi-overlay[class],
:root body #fi-content[id] {
  background: var(--fi-bg-page) !important;
}

/* ---------------------------------------------------------------------
   04 Approach — tall cards, one gradient mark each, copy on the floor
   ------------------------------------------------------------------ */
/* Each card is a subgrid of the row: mark, title, body sit in three
   shared tracks, so the titles share a baseline and the marks share a
   centre no matter how long each blurb runs. Reserving a height per card
   could not do this — the longest blurb still pushed its own title up. */
.fi-root .fi-approach-grid {
  grid-template-rows: 1fr auto auto;
}
.fi-root .fi-approach-card {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;
  row-gap: 10px;
  aspect-ratio: auto;
  min-height: clamp(430px, 34vw, 540px);
  padding: clamp(20px, 2.2vw, 34px);
  background: var(--fi-media-bg);
  border: 0;
  box-shadow: none;
}
/* Where subgrid is unavailable the card falls back to the flow it had:
   still legible, just without the shared baseline. */
@supports not (grid-template-rows: subgrid) {
  .fi-root .fi-approach-card { display: flex; flex-direction: column; }
  .fi-root .fi-approach-card h3 { margin-top: auto; }
}
:root body #fi-content .fi-approach-card[class] { border-radius: var(--fi-r-tile) !important; }
.fi-root .fi-blob {
  width: 100%;
  height: auto;
  display: grid;
  place-items: center;
  margin: 0;
  /* Air above and below the mark as well as around it: the taller card is
     the point, and the mark should read as floating in that space rather
     than filling it. */
  padding: clamp(20px, 3vw, 52px) 0;
  background: none;
  border-radius: 0;
}
.fi-root .fi-blob img {
  width: clamp(84px, 9vw, 132px);
  height: auto;
  display: block;
}
.fi-root .fi-approach-card h3 {
  margin: 0;
  font-size: var(--fi-tile-title);
  font-weight: 600;
  letter-spacing: var(--track-heading, -.035em);
}
.fi-root .fi-approach-card p {
  margin: 0;
  font-size: var(--fi-tile-body);
  line-height: var(--fi-tile-lh);
  color: color-mix(in srgb, var(--fi-text-primary) 82%, transparent);
}
@media (max-width: 900px) {
  .fi-root .fi-approach-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fi-root .fi-approach-card { aspect-ratio: auto; }
}
@media (max-width: 560px) {
  .fi-root .fi-approach-grid { grid-template-columns: 1fr; }
}

/* Every section here was painting the site's grey surface (#f4f4f4) of its
   own accord, which is what showed through between the white page and the
   cards. The case study is white end to end; components that need to sit
   apart (media boxes, the design-system band, the impact tiles) carry
   their own colour. */
:root body #fi-content > :is(header, section, footer)[class] {
  background: var(--fi-bg-page) !important;
}

/* ---------------------------------------------------------------------
   Dark theme: the artwork stays light
   The design-system panels and the impact tiles hold light-mode UI
   screenshots. Inverting their surfaces put white cards on near-black
   plates and turned their type white on white, so these two keep their
   light-mode colours — and re-state the ink they were designed against —
   whatever theme the page is in.
   ------------------------------------------------------------------ */
[data-theme="dark"] .fi-root .fi-ds__panel,
[data-theme="dark"] .fi-root .fi-impact-tile {
  --fi-text-primary: #222222;
  --fi-text-secondary: #555555;
  color: var(--fi-text-primary);
}
[data-theme="dark"] .fi-root .fi-ds__panel { background: #ffffff; }
[data-theme="dark"] .fi-root .fi-ds__grid { background: var(--fi-media-bg); }
[data-theme="dark"] .fi-root .fi-impact-tile {
  --fi-impact-tint: #fdf1bb;
  --fi-impact-neutral: linear-gradient(180deg, #e9e9e9 0%, #ffffff 58%);
}

/* The intro is the only block that is pure text, so it gets its own
   margins rather than running to the section gutter like the media
   sections do — the statement and the two paragraphs read as a centred
   column with air either side. */
.fi-root .fi-intro {
  padding-inline: clamp(var(--fi-gutter), 9vw, 190px);
}
@media (max-width: 720px) {
  .fi-root .fi-intro { padding-inline: var(--fi-gutter); }
}

/* ---------------------------------------------------------------------
   Live dot + the iPhone shell
   ------------------------------------------------------------------ */
/* The site-wide sweep squares every corner, including this 7px dot. */

/* The intro is the page's first block of prose and was set in the meta
   grey used for captions — too light to lead with. The statement takes
   full ink, the paragraphs a step below it rather than three. */
.fi-root .fi-intro .fi-statement,
.fi-root .fi-intro .fi-statement span { color: #1b1b1b; }
.fi-root .fi-intro .fi-body {
  color: color-mix(in srgb, var(--fi-text-primary) 78%, transparent);
}
[data-theme="dark"] .fi-root .fi-intro .fi-statement,
[data-theme="dark"] .fi-root .fi-intro .fi-statement span { color: #f2f2f2; }
[data-theme="dark"] .fi-root .fi-intro .fi-body {
  color: color-mix(in srgb, var(--fi-text-primary) 74%, transparent);
}

/* ---------------------------------------------------------------------
   Section heads: centred stack

   The two-column head (title left, lede right) put the two halves at
   opposite ends of a 1300px row. They now read as one centred block —
   title, then the line beneath it — held to the same measure as the
   intro copy above, so every section opens on the same column.
   ------------------------------------------------------------------ */
.fi-root .fi-section-head {
  display: block !important;
  text-align: center;
  padding-inline: clamp(var(--fi-gutter), 9vw, 190px);
}
.fi-root .fi-section-head h2 {
  margin: 0 auto;
  text-align: center;
}
.fi-root .fi-section-head p {
  width: 100%;
  max-width: none;
  margin: clamp(10px, 1vw, 16px) auto 0;
  text-align: center;
  text-wrap: pretty;
}
@media (max-width: 720px) {
  .fi-root .fi-section-head { padding-inline: var(--fi-gutter); }
}

/* The band's own label: it sits inside the grey plate, top-left, above
   the four panels rather than stranded under them. */
.fi-ds__grid > .fi-showcase__copy {
  grid-column: 1 / -1;
  margin-bottom: clamp(4px, .6vw, 10px);
}
.fi-ds__grid > .fi-showcase__copy h3 { margin-bottom: 6px; }

/* ---------------------------------------------------------------------
   Solution cards follow the design-system band

   Each card is now the grey plate itself: its label sits top-left inside
   that plate and the art sits under it, the same reading order the band
   below uses. The media box keeps the art's own ground rather than
   painting a second one.
   ------------------------------------------------------------------ */
.fi-root .fi-showcase__item {
  --fi-card-pad: clamp(16px, 1.8vw, 26px);
  display: flex;
  flex-direction: column;
  padding: var(--fi-card-pad);
  background: var(--fi-media-bg);
  border-radius: var(--fi-r-tile);
  overflow: hidden;
}
.fi-root .fi-showcase__item > .fi-showcase__copy {
  order: 0;
  margin-bottom: clamp(12px, 1.4vw, 20px);
}
.fi-root .fi-showcase__item > .fi-showcase__media {
  order: 1;
  margin: 0;
  background: transparent;
}
:root body #fi-content .fi-showcase__item[class] { border-radius: var(--fi-r-tile) !important; }

/* The first two cards' art sits against the plate's edge rather than
   floating inside it: the navigation shot snaps to the bottom-right
   corner, the annotated dashboard runs to the right edge. Negative
   margins cancel the card's own padding on those sides only. */
.fi-root .fi-showcase__item:nth-child(1) .fi-showcase__media {
  margin-right: calc(var(--fi-card-pad) * -1);
  margin-bottom: calc(var(--fi-card-pad) * -1);
  border-radius: 0 0 var(--fi-r-tile) 0;
  /* The card stretches to the grid row, so a fixed-ratio box left a band
     of plate under the art. It takes whatever height is left instead. */
  flex: 1 1 auto;
  min-height: 0;
  aspect-ratio: auto;
}
.fi-root .fi-showcase__item:nth-child(1) .fi-showcase__media img {
  object-fit: cover;
  object-position: right bottom;
}
.fi-root .fi-showcase__item:nth-child(2) .fi-showcase__media {
  margin-right: calc(var(--fi-card-pad) * -1);
  border-radius: 0;
}
/* Slightly larger, anchored right, so the annotations keep their margin
   on the left while the report meets the edge. */
.fi-root .fi-showcase__item:nth-child(2) .fi-showcase__media img {
  width: 108%;
  max-width: none;
  object-fit: contain;
  object-position: right center;
}

/* ---- Live prototype ---------------------------------------------------
   The stage itself is css/device-morph.css; this only places it in the
   closing section. */
.fi-proto { width: 100%; max-width: 1120px; margin: 56px auto 0; padding-bottom: clamp(30px, 4vw, 70px); }
@media (max-width: 760px) { .fi-proto { margin-top: 36px; } }
