/* ============================================================================
   AIFF — ALL INDIA FOOTBALL FEDERATION · WEBSITE REDESIGN CASE STUDY
   Seven sections: hero, previous website, challenge, new structure, final
   website, responsive experience, outcome.

   Written mobile-first for real: every rule outside a media query targets a
   ~390px canvas, and larger screens only ever ADD to it through min-width
   blocks. Nothing here is a desktop layout being folded back down. The two
   set pieces — the information architecture diagram and the phone strip —
   change SHAPE at 768px rather than shrinking, because a shrunken radial
   diagram and a three-across phone row are both unreadable on a phone.

   Layered on top of style.css, which already supplies the design tokens, the
   topbar/footer chrome, the reveal system and the .btn set.

   EVERY IMAGE ON THIS PAGE IS A REAL CAPTURE — of the live site, of its 2016
   record on the Internet Archive, or licensed football photography. There are
   no mocked-up components, no specimen interfaces and no placeholder slots,
   so there is no ghost-panel machinery here either. If you add an image,
   add the file with it.

   COLOUR — two roles now, not three:
     navy    the ground. The whole page sits on it, so "deep blue background"
             is the body, not a decorative panel.
     red     the site's one accent, #b162fe (--primary-red, aliased here as
             --cs-red). It carries BOTH layers: the editorial one — eyebrows,
             rules, numerals, the diagram, the "after" marker — and the
             controls, the topbar, the footer and every .btn, exactly as on
             the Delhivery, Siemens and Warner Bros. studies.

   This page previously carried an AIFF orange as a third role. It was removed
   because four case studies each borrowing their client's colour left the set
   reading as four different sites rather than one portfolio. The client is
   named in type, not in hue. Do not reintroduce a per-client accent here.

   One consequence worth knowing before you touch the ground: red on this navy
   is a ~3.8:1 pair, so the accent is for LABELS AND MARKS ONLY — eyebrows,
   numerals, rules, markers, the em inside the outcome statement. Body copy
   stays on --cs-white/--cs-grey. If a future edit wants red running text
   here, lighten the type to --red-bright rather than darkening the ground.

   Sections 3-4 (primitives and editorial furniture) repeat the same
   primitives as casestudy-delhivery.css and casestudy-siemens.css at the same
   values, retuned from warm grey to cool blue-grey for this ground. They are
   duplicated rather than shared because those files also carry their own
   page layouts. FOUR case studies now repeat this block — it has earned a
   shared casestudy.css; see the note at the foot of this file.

     1  Page tokens & gutter
     2  Chrome adjustments
     3  Editorial furniture — eyebrow, headings, lede, copy, meta
     4  Hero
     5  Browser frame (shared by sections 2, 5 and 6)
     6  Previous website — comparison & gains
     7  Challenge
     8  New structure — pillars & IA diagram
     9  Final website — screenshot showcase
    10  Responsive experience — desktop + phone frames
    11  Outcome & CTA
    12  TABLET         min-width: 768px
    13  DESKTOP        min-width: 1024px
    14  LARGE DESKTOP  min-width: 1440px
    15  Reduced motion
   ========================================================================= */

/* --------------------------------------------------------- 1 tokens & gutter */
:root {
  /* The ground. Dark enough to sit beside the shared #070707 chrome without
     a visible seam, blue enough to read as navy rather than as black. */
  --aiff-navy:   #070d17;
  --aiff-panel:  #0b1524;   /* raised surfaces — browser frames, nodes */
  --aiff-panel-2:#0e1b2d;   /* the one step above that, used sparingly */

  /* The site accent. Same value and same names as the other three case
     studies. Labels and marks only on this ground — never body copy; see the
     contrast note at the top of this file. */
  --cs-red:    #b162fe;
  --cs-red-lo: rgba(177, 98, 254, .38);   /* hairlines, borders, diagram strokes */

  /* Type colours, cooled for a blue ground — the warm greys the other case
     studies use turn muddy over navy. */
  --cs-white:  #f2f6fb;
  --cs-grey:   #9dabbd;
  --cs-dim:    #66748a;
  --cs-line:   rgba(226, 236, 248, .1);
  --cs-line-2: rgba(226, 236, 248, .2);

  /* The uppercase letter-spaced label family, in two steps. These carry all
     the metadata on the page — eyebrows, captions, tags, URLs — and they are
     the sizes that decide whether a phone reads as considered or as a
     shrunken desktop. The MOBILE values are the largest: 10.9px and 11.8px
     here, stepping DOWN to 9.6px and 10.6px only once there is a pointer and
     a big viewport. Never hard-code a size below --fs-micro anywhere on this
     page; put it on this scale instead so the floor stays in one place. */
  --fs-micro: .68rem;     /* 10.9px on mobile */
  --fs-mini:  .74rem;     /* 11.8px on mobile */

  --cs-space: 5.5rem;     /* 88px — mobile section rhythm */
  --cs-h1:    2.6rem;     /* 41.6px */
  --cs-h2:    1.95rem;
  --cs-h3:    1.2rem;

  /* 22px of horizontal breathing room on mobile — style.css drops the global
     gutter to 18.4px under 640px, which is tighter than this page wants. */
  --gutter: 1.375rem;
}

/* ------------------------------------------------------- 2 chrome adjustments */
/* style.css makes the topbar transparent under 640px because the homepage hero
   sits behind it. Here it is a plain bar over the navy, so it needs a
   background — and it needs THIS page's navy, not the site's near-black, or a
   1px seam shows along the bottom of the bar. */
@media (max-width: 640px) {
  .topbar { background: var(--aiff-navy); }
}
.cs-body--aiff .topbar { background: var(--aiff-navy); }

.cs-brand { display: flex; align-items: center; gap: .6rem; min-height: 44px; }
.cs-brand__arrow {
  width: 1.3rem; height: .6rem; color: var(--cs-grey);
  transition: transform .3s var(--ease), color .3s var(--ease);
}
.cs-brand:hover .cs-brand__arrow,
.cs-brand:focus-visible .cs-brand__arrow { transform: translateX(-5px); color: var(--primary-red); }

.cs-body--aiff { background: var(--aiff-navy); }
.cs-body--aiff .foot { background: var(--aiff-navy); }

/* ================================================== 3 editorial furniture */
.cs-sec {
  padding-block: var(--cs-space);
  border-top: 1px solid var(--cs-line);
}
.cs-sec__head { margin-bottom: 2.75rem; }

.cs-eyebrow {
  display: flex; align-items: center; gap: .8rem;
  margin-bottom: 1.35rem;
  font-family: var(--font-cond); font-weight: 500;
  font-size: var(--fs-micro); letter-spacing: .22em; text-transform: uppercase;
  color: var(--cs-red);
}
.cs-eyebrow__rule { width: 1.6rem; height: 1px; background: var(--cs-red); flex: none; }

.cs-h1, .cs-h2, .cs-h3 {
  color: var(--cs-white);
  font-family: var(--font-cond); font-weight: 400;
  letter-spacing: -.012em;
  text-wrap: balance;
}
.cs-h1 { font-size: var(--cs-h1); line-height: 1.05; max-width: 16ch; }
.cs-h2 { font-size: var(--cs-h2); line-height: 1.12; max-width: 26ch; }
.cs-h3 {
  font-size: var(--cs-h3); line-height: 1.25; max-width: 26ch;
  font-weight: 500;
}

.cs-lede {
  margin-top: 1.35rem;
  font-size: 1rem; line-height: 1.7;
  color: var(--cs-grey);
  max-width: 48ch;
}

.cs-copy { margin-top: 1.5rem; display: grid; gap: 1.1rem; }
.cs-copy p {
  font-size: .95rem; line-height: 1.75;
  color: var(--cs-grey);
  max-width: 60ch;
}

.cs-meta {
  display: grid; gap: 1.35rem;
  margin-top: 2.5rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--cs-line);
}
.cs-meta dt {
  font-family: var(--font-cond); font-weight: 500;
  font-size: var(--fs-micro); letter-spacing: .2em; text-transform: uppercase;
  color: var(--cs-dim);
  margin-bottom: .45rem;
}
.cs-meta dd {
  font-family: var(--font-cond); font-weight: 300;
  font-size: .95rem; letter-spacing: .02em; line-height: 1.5;
  color: var(--cs-white);
}

/* ================================================================== 4 hero */
/* Full-bleed stadium frame with the copy sitting on it. The section is the
   stacking context; the media is pinned behind and the shell rides on top.
   Nothing is layered over the photograph — the platform's structure is the
   subject of section 8's diagram, and stating it twice weakened both. */
.cs-hero {
  position: relative;
  isolation: isolate;
  display: flex; flex-direction: column; justify-content: flex-end;
  min-height: 86vh;
  padding-block: 4.5rem 3rem;
  overflow: hidden;
}
/* svh where supported: on mobile browsers vh includes the retracting URL bar,
   which makes the hero taller than the screen on first paint. */
@supports (height: 1svh) { .cs-hero { min-height: 86svh; } }

.cs-hero__media { position: absolute; inset: -6% 0; z-index: -2; }
.cs-hero__media picture { position: absolute; inset: 0; }
.cs-hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: center 32%; }

/* Two scrims, not one. The vertical pass buys legibility for the copy block;
   the horizontal pass keeps the frame's right side readable as a photograph
   instead of flattening the whole thing to navy. Both are tinted blue rather
   than neutral so the stadium light reads as floodlight, not as grey. */
.cs-hero::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(to top,
      var(--aiff-navy) 0%, rgba(7, 13, 23, .95) 24%, rgba(7, 13, 23, .66) 54%,
      rgba(7, 13, 23, .34) 80%, rgba(7, 13, 23, .5) 100%),
    linear-gradient(to right, rgba(7, 13, 23, .82) 0%, rgba(7, 13, 23, .28) 58%, transparent 100%);
}

/* Slow settle on load — 2.6s, once, then it rests. Started by CSS because
   there is nothing to wait for. casestudy-aiff.js layers an optional scroll
   parallax on the same element via --py; the two compose because the
   animation targets scale and the parallax targets translate. */
.cs-hero__media img { animation: aiff-settle 2.6s var(--ease-out) both; }
@keyframes aiff-settle { from { transform: scale(1.08); } to { transform: scale(1); } }
.cs-hero__media { transform: translate3d(0, var(--py, 0px), 0); will-change: transform; }

.cs-hero__inner { position: relative; z-index: 1; width: 100%; }
.cs-hero .cs-lede { max-width: 44ch; }
.cs-hero__cta { margin-top: 2.25rem; }
.cs-hero__cta .btn { min-height: 44px; }

/* ======================================================= 5 browser frame */
/* Shared by the comparison (section 6), the showcase (9) and the responsive
   block (10). Real chrome around a real capture.

   Frames that carry a full-page screenshot also carry data-fit-image, so
   casestudy-aiff.js adopts the file's true ratio and NOTHING is cropped — a
   website capture that loses its edges loses its content. The 16:10 below is
   the shape used by the frames that hold a deliberately-cropped 16:10 file
   (the comparison pair and the responsive desktop shot).

   No margin of its own: the wrapper decides spacing, because the same frame
   appears at four different rhythms on this page. */
.mock {
  border: 1px solid var(--cs-line-2);
  background: var(--aiff-panel);
  box-shadow: 0 40px 70px -38px rgba(0, 0, 0, .95);
  transition: transform .5s var(--ease-out), border-color .4s var(--ease);
}
.mock__chrome {
  display: flex; align-items: center; gap: .4rem;
  padding: .6rem .8rem;
  border-bottom: 1px solid var(--cs-line);
  background: var(--aiff-panel-2);
}
.mock__dot {
  width: .42rem; height: .42rem; border-radius: 50%;
  background: rgba(157, 171, 189, .3);
  flex: none;
}
.mock__dot:first-child { background: var(--cs-red-lo); }
.mock__url {
  margin-left: .7rem;
  padding: .22rem .7rem;
  border: 1px solid var(--cs-line);
  font-family: var(--font-cond); font-weight: 300;
  font-size: var(--fs-micro); letter-spacing: .1em;
  color: var(--cs-grey);
  /* long paths must not push the frame wider than its column */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mock__shot {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--aiff-panel);
}
.mock__shot img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
}

/* ==================================================== 6 previous website */
/* The one before-and-after. Both frames hold a matched 16:10 crop of each
   homepage's top, so the pair is like for like. */
.cmp { display: grid; gap: 2.25rem; margin-bottom: 3rem; }
.cmp__item { margin: 0; }

.cmp__tag {
  display: inline-flex; align-items: center; gap: .55rem;
  margin-bottom: .85rem;
  font-family: var(--font-cond); font-weight: 500;
  font-size: var(--fs-micro); letter-spacing: .2em; text-transform: uppercase;
}
.cmp__tag::before {
  content: ""; width: .4rem; height: .4rem; border-radius: 50%; flex: none;
}
.cmp__tag--before { color: var(--cs-dim); }
.cmp__tag--before::before { background: var(--cs-dim); }
.cmp__tag--after { color: var(--cs-red); }
.cmp__tag--after::before {
  background: var(--cs-red);
  box-shadow: 0 0 10px 1px rgba(177, 98, 254, .7);
}

.cmp__item figcaption {
  margin-top: .9rem;
  font-family: var(--font-cond); font-weight: 300;
  font-size: var(--fs-mini); letter-spacing: .1em; text-transform: uppercase;
  line-height: 1.6;
  color: var(--cs-dim);
}

/* What changed. Four statements on hairlines — no cards. */
.gains { display: grid; gap: 0; }
.gains__item {
  padding-block: 1.5rem;
  border-top: 1px solid var(--cs-line);
}
.gains__item:last-child { border-bottom: 1px solid var(--cs-line); }
.gains__item p {
  margin-top: .55rem;
  font-size: .9rem; line-height: 1.7;
  color: var(--cs-grey);
  max-width: 44ch;
}

/* ============================================================= 7 challenge */
/* Three statements on hairlines. No cards. */
.chal { display: grid; gap: 0; }
.chal__item {
  padding-block: 1.6rem;
  border-top: 1px solid var(--cs-line);
}
.chal__item:last-child { border-bottom: 1px solid var(--cs-line); }
.chal__num {
  display: block; margin-bottom: .7rem;
  font-family: var(--font-cond); font-weight: 500;
  font-size: var(--fs-mini); letter-spacing: .2em;
  color: var(--cs-red);
}
.chal__item p {
  margin-top: .6rem;
  font-size: .925rem; line-height: 1.7;
  color: var(--cs-grey);
  max-width: 46ch;
}

/* ========================================================= 8 new structure */
.pillars { display: grid; gap: 0; margin-bottom: 3.5rem; }
.pillars__item {
  padding-block: 1.6rem;
  border-top: 1px solid var(--cs-line);
}
.pillars__item:last-child { border-bottom: 1px solid var(--cs-line); }
.pillars__item p {
  margin-top: .6rem;
  font-size: .925rem; line-height: 1.7;
  color: var(--cs-grey);
  max-width: 46ch;
}

/* ---- the information architecture diagram -------------------------------
   An explanatory case-study visual, not a mock interface: every node is a
   real top-level area of the live site.

   MOBILE SHAPE: a vertical connected system. AIFF is the head of a hairline
   spine and the eight areas hang off it, each with its own tick. This is the
   primary shape, not a fallback — a radial diagram at 360px is a thicket.
   The radial version is built in section 12.                               */
.eco { margin: 0; }
.eco__stage { position: relative; }

.eco__lines { display: none; }

.eco__core {
  display: inline-flex; align-items: center;
  margin-bottom: 1.35rem;
  padding: .75rem 1.35rem;
  border: 1px solid var(--cs-red-lo);
  background: var(--aiff-panel-2);
  box-shadow: 0 0 40px -12px rgba(177, 98, 254, .5);
}
.eco__core span {
  font-family: var(--font-display); font-weight: 400;
  font-size: 1.5rem; letter-spacing: .04em; line-height: 1;
  color: var(--cs-white);
}

.eco__nodes {
  position: relative;
  display: grid; gap: .1rem;
  padding-left: 1.9rem;
}
/* the spine */
.eco__nodes::before {
  content: ""; position: absolute;
  left: .5rem; top: -1.35rem; bottom: 1.1rem;
  width: 1px; background: var(--cs-line-2);
}
.eco__node {
  position: relative;
  padding-block: .8rem;
  border-top: 1px solid var(--cs-line);
}
.eco__node:first-child { border-top: 0; }
/* the tick out to the spine, plus its node dot */
.eco__node::before {
  content: ""; position: absolute;
  left: -1.4rem; top: 1.5rem; width: .95rem; height: 1px;
  background: var(--cs-line-2);
}
.eco__node::after {
  content: ""; position: absolute;
  left: -1.55rem; top: 1.28rem; width: .45rem; height: .45rem;
  border-radius: 50%;
  background: var(--cs-red);
  box-shadow: 0 0 12px 1px rgba(177, 98, 254, .55);
}
.eco__node span {
  display: block;
  font-family: var(--font-cond); font-weight: 500;
  font-size: .92rem; letter-spacing: .03em;
  color: var(--cs-white);
}
.eco__node em {
  display: block; margin-top: .18rem;
  font-style: normal;
  font-family: var(--font-cond); font-weight: 300;
  font-size: .72rem; letter-spacing: .06em;
  color: var(--cs-dim);
}

.eco__cap {
  margin-top: 2rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--cs-line);
  font-size: .85rem; line-height: 1.7;
  color: var(--cs-dim);
  max-width: 54ch;
}

/* ========================================================= 9 final website */
.shot { margin: 0; }
.shot figcaption {
  margin-top: .95rem;
  font-family: var(--font-cond); font-weight: 300;
  font-size: var(--fs-mini); letter-spacing: .1em; text-transform: uppercase;
  line-height: 1.6;
  color: var(--cs-grey);
}
.shot--lead { margin-bottom: 3rem; }
.shot--lead figcaption { color: var(--cs-white); max-width: 46ch; }

.shots { display: grid; gap: 2.5rem; }

/* ================================================ 10 responsive experience */
.resp { display: grid; gap: 2.5rem; }
.resp__desk { margin: 0; }
.resp__desk figcaption {
  margin-top: .9rem;
  font-family: var(--font-cond); font-weight: 300;
  font-size: var(--fs-mini); letter-spacing: .14em; text-transform: uppercase;
  color: var(--cs-dim);
}

/* MOBILE SHAPE: a horizontal scroll strip. Three phones stacked vertically on
   a 390px canvas runs to nearly three screens of height, and three side by
   side leaves each about 100px wide — unreadable. A snap strip keeps them at
   a size where the interface inside is legible and is the gesture a phone
   user already expects.

   tabindex is NOT set here: the strip holds images only, so there is nothing
   inside it to reach by keyboard, and a focusable div with no focusable
   content is a dead tab stop. On desktop (section 13) it becomes a plain
   grid and the scroll behaviour is dropped entirely. */
.phones {
  display: flex; gap: 1rem;
  margin: 0; padding: 0 var(--gutter) .5rem;
  margin-inline: calc(-1 * var(--gutter));
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}
.phone {
  flex: 0 0 auto;
  width: min(62vw, 260px);
  scroll-snap-align: center;
}

/* The device. A rounded bezel with a hairline speaker slot — enough to read
   as a phone without becoming an illustration of one. */
.phone__frame {
  position: relative;
  border: 1px solid var(--cs-line-2);
  border-radius: 1.35rem;
  padding: .45rem;
  background: var(--aiff-panel-2);
  box-shadow: 0 30px 60px -34px rgba(0, 0, 0, .95);
  overflow: hidden;
}
.phone__frame::before {
  content: ""; position: absolute; z-index: 2;
  left: 50%; top: .85rem; transform: translateX(-50%);
  width: 2.4rem; height: .22rem; border-radius: .22rem;
  background: rgba(226, 236, 248, .22);
}
.phone__frame img {
  display: block;
  width: 100%; height: auto;
  border-radius: 1rem;
  aspect-ratio: 9 / 19.5;
  object-fit: cover; object-position: top center;
}
.phone__cap {
  margin-top: .75rem;
  text-align: center;
  font-family: var(--font-cond); font-weight: 300;
  font-size: var(--fs-mini); letter-spacing: .14em; text-transform: uppercase;
  color: var(--cs-dim);
}

.resp__note {
  margin-top: 2.5rem;
  padding-left: 1.1rem;
  border-left: 1px solid var(--cs-red-lo);
  font-size: .9rem; line-height: 1.75;
  color: var(--cs-grey);
  max-width: 56ch;
}

/* ============================================================== 11 outcome */
/* The stadium close. Same construction as the hero — media pinned behind,
   scrims over it, shell on top — but darker, because this one carries a lot
   more type. */
.cs-sec--out {
  position: relative;
  isolation: isolate;
  padding-block: var(--cs-space);
  overflow: hidden;
}
.out__media { position: absolute; inset: 0; z-index: -2; }
.out__media picture { position: absolute; inset: 0; }
.out__media img { width: 100%; height: 100%; object-fit: cover; object-position: center 40%; }
.cs-sec--out::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(to bottom, var(--aiff-navy) 0%, rgba(7, 13, 23, .93) 18%,
                   rgba(7, 13, 23, .9) 62%, var(--aiff-navy) 100%),
    radial-gradient(80% 60% at 50% 0%, rgba(177, 98, 254, .1), transparent 70%);
}
.out__inner { position: relative; z-index: 1; }

.out__stmt {
  font-family: var(--font-cond); font-weight: 400;
  font-size: 2.1rem; line-height: 1.1; letter-spacing: -.015em;
  color: var(--cs-white);
  text-wrap: balance;
  max-width: 18ch;
}
.out__stmt em { font-style: normal; color: var(--cs-red); }

.out__copy { margin-top: 1.75rem; }

.sum {
  display: grid; gap: 0;
  margin-top: 2.75rem;
}
.sum li {
  padding-block: .8rem;
  border-top: 1px solid var(--cs-line);
  font-family: var(--font-cond); font-weight: 300;
  font-size: .88rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--cs-white);
}
.sum li:last-child { border-bottom: 1px solid var(--cs-line); }

.out__role {
  margin-top: 2.5rem;
  padding-left: 1.1rem;
  border-left: 2px solid var(--cs-red);
  font-family: var(--font-cond); font-weight: 300;
  font-size: 1.05rem; line-height: 1.55; letter-spacing: .01em;
  color: var(--cs-white);
  max-width: 46ch;
}

.cs-cta { margin-top: 3rem; }
.cs-cta__actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: .7rem 1.1rem;
}
.cs-cta__actions .btn { min-height: 44px; }

.cs-next {
  margin-top: 3.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--cs-line);
}
.cs-next__label {
  margin-bottom: .6rem;
  font-family: var(--font-cond); font-weight: 500;
  font-size: var(--fs-micro); letter-spacing: .22em; text-transform: uppercase;
  color: var(--cs-dim);
}
.cs-next__link {
  display: inline-flex; align-items: center; gap: 1rem;
  min-height: 44px;
  font-family: var(--font-cond); font-weight: 400;
  font-size: 1.85rem; line-height: 1; letter-spacing: -.01em;
  color: var(--cs-white);
  transition: color .3s var(--ease);
}
.cs-next__link .arrow { width: 2rem; color: var(--cs-red); transition: transform .35s var(--ease); }
.cs-next__link:hover { color: var(--red-bright); }
.cs-next__link:hover .arrow { transform: translateX(6px); }

.out__credit {
  display: grid; gap: .25rem;
  margin-top: 3rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--cs-line);
  font-size: .74rem; line-height: 1.65;
  color: var(--cs-dim);
  max-width: 70ch;
}

/* ========================================================= 12 TABLET 768px */
@media (min-width: 768px) {
  :root {
    --gutter: 2.25rem;
    --cs-space: 7rem;
    --cs-h1: 3.6rem;
    --cs-h2: 2.6rem;
    --cs-h3: 1.35rem;

    /* the label scale steps DOWN as the viewport grows — see the note on the
       tokens in section 1 */
    --fs-micro: .64rem;   /* 10.2px */
    --fs-mini:  .7rem;    /* 11.2px */
  }

  .cs-hero { min-height: 80vh; padding-block: 6rem 3.5rem; }
  @supports (height: 1svh) { .cs-hero { min-height: 80svh; } }

  /* Client, Platform and Completed Through are short; Scope is a four-part
     string, so it takes the remaining track rather than being squeezed into a
     quarter of it. The 1fr is the SECOND track because that is where Scope
     lands in source order (Client, Scope, Platform, Completed Through) — the
     previous version put it third, which handed the slack to Platform. */
  .cs-meta {
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    gap: 2.5rem;
  }

  /* ---- previous website ---- */
  .cmp { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2rem; }
  .gains { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 2.5rem; }
  .gains__item:nth-child(2) { border-bottom: 0; }
  .gains__item:nth-last-child(-n+2) { border-bottom: 1px solid var(--cs-line); }

  /* ---- challenge ---- */
  .chal { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 2rem; }
  .chal__item { border-bottom: 1px solid var(--cs-line); }

  /* ---- new structure ---- */
  .pillars { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 2rem; }
  .pillars__item { border-bottom: 1px solid var(--cs-line); }

  /* ---- THE DIAGRAM GOES RADIAL ----
     The stage becomes a fixed-ratio box, the spine and its ticks are dropped,
     and every node is placed at the --x/--y it was carrying all along. The
     SVG line layer switches on with it.

     The SVG shares those percentages exactly: viewBox 0 0 100 100 +
     preserveAspectRatio="none" means one SVG unit IS one percent of the
     stage in each axis, so the spokes stay welded to the boxes at every
     width. vector-effect="non-scaling-stroke" (set in the markup) keeps the
     hairlines 1px through the non-uniform scale. */
  .eco__stage {
    aspect-ratio: 1.5;
    display: grid;
    place-items: center;
  }

  .eco__lines {
    display: block;
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    overflow: visible;
  }
  .eco__ring ellipse {
    fill: none;
    stroke: rgba(226, 236, 248, .11);
    stroke-width: 1;
  }
  .eco__spokes line {
    stroke: var(--cs-red-lo);
    stroke-width: 1;
  }
  /* ---- the lines arriving ----
     A staggered fade, spokes first from the centre outward, then the ring
     closing around them.

     NOT A stroke-dashoffset DRAW, and that is a measured decision rather than
     a stylistic one. A dash draw needs the dash length to mean "one whole
     shape", which needs pathLength normalisation — and `vector-effect:
     non-scaling-stroke` resolves stroke-dash* in DEVICE space instead, so
     pathLength is bypassed and the dash stops doing anything at all.
     Rasterising the ring both ways at 1352x773 measured it: with the vector
     effect on, the "hidden" state still painted 2253 pixels against the
     drawn state's 2508 — no visible animation. With it off the dash worked
     (56 pixels hidden, 27770 drawn) but the 1-unit stroke went through the
     1.62:1 non-uniform scale and rendered as a fat, uneven wedge.

     The two are mutually exclusive, and crisp hairlines matter more here
     than the specific flavour of entrance. Do not "fix" this back to a dash
     without re-running that measurement.

     THE DEFAULT IS VISIBLE. Hidden is opted into by `.js`, the class the
     inline script in <head> puts on <html>, exactly as style.css gates its
     reveals. Without that gate a visitor with JavaScript off would get a
     diagram of eight labels and no connections at all — and the connections
     are the entire point of the section. */
  .eco__lines line,
  .eco__lines ellipse {
    opacity: 1;
    transition: opacity .75s var(--ease-out);
  }
  .js .eco__lines line,
  .js .eco__lines ellipse { opacity: 0; }
  .js .eco__stage.is-drawn line,
  .js .eco__stage.is-drawn ellipse { opacity: 1; }
  /* the ring lands last and slowest, so the eight spokes read as arriving
     first and the ring as closing around them */
  .eco__ring ellipse { transition-duration: 1.4s; transition-delay: .55s; }
  .eco__spokes line:nth-child(1) { transition-delay: .10s; }
  .eco__spokes line:nth-child(2) { transition-delay: .18s; }
  .eco__spokes line:nth-child(3) { transition-delay: .26s; }
  .eco__spokes line:nth-child(4) { transition-delay: .34s; }
  .eco__spokes line:nth-child(5) { transition-delay: .42s; }
  .eco__spokes line:nth-child(6) { transition-delay: .50s; }
  .eco__spokes line:nth-child(7) { transition-delay: .58s; }
  .eco__spokes line:nth-child(8) { transition-delay: .66s; }

  .eco__core {
    position: absolute; left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    margin: 0;
    padding: 1.1rem 1.9rem;
    z-index: 2;
  }
  .eco__core span { font-size: 2rem; }

  .eco__nodes {
    position: absolute; inset: 0;
    display: block;
    padding: 0;
  }
  .eco__nodes::before { display: none; }

  .eco__node {
    position: absolute;
    left: var(--x); top: var(--y);
    transform: translate(-50%, -50%);
    width: 9.5rem;
    padding: .6rem .7rem;
    border: 1px solid var(--cs-line);
    background: var(--aiff-panel);
    text-align: center;
  }
  .eco__node:first-child { border-top: 1px solid var(--cs-line); }
  .eco__node::before,
  .eco__node::after { display: none; }
  .eco__node span { font-size: .78rem; }
  .eco__node em { font-size: .62rem; line-height: 1.4; }

  .eco__cap { margin-top: 2.5rem; }

  /* ---- final website ---- */
  .shots { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.25rem; }
  .shot--wide { grid-column: 1 / -1; }
  /* The wide frame holds a 1280x1111 file across a full-width track, which
     would run it to nearly a screen and a half of height. Cap it and anchor
     the crop to the top — this is the only frame on the page that crops, and
     it crops the foot of a portrait grid rather than any content the caption
     is pointing at. */
  .shot--wide .mock__shot { aspect-ratio: 16 / 8; }

  /* ---- responsive ---- */
  /* The strip stops scrolling and becomes a plain three-across row. */
  .phones {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1.5rem;
    margin-inline: 0; padding: 0;
    overflow: visible;
  }
  .phone { width: auto; }

  /* ---- outcome ---- */
  .out__stmt { font-size: 3.1rem; max-width: 17ch; }
  .sum { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 2.5rem; }
  .sum li:nth-last-child(-n+2) { border-bottom: 1px solid var(--cs-line); }
  .cs-next__link { font-size: 2.6rem; }
  .out__credit { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .25rem 1.5rem; }
}

/* ======================================================= 13 DESKTOP 1024px */
@media (min-width: 1024px) {
  :root {
    /* style.css narrows the shell to 900px under `max-width: 1024px`, which
       overlaps this block at exactly 1024 and would otherwise leave the
       desktop layout ~100px narrower than it is designed for. Reclaim the
       site's wide shell from here up. */
    --shell: 1480px;

    --gutter: 3rem;
    --cs-space: 9.5rem;
    --cs-h1: 4.6rem;
    --cs-h2: 3.2rem;
    --cs-h3: 1.5rem;

    /* the floor's lowest step — only reached where there is a mouse pointer
       and a large viewport */
    --fs-micro: .6rem;    /* 9.6px */
    --fs-mini:  .66rem;   /* 10.6px */
  }

  /* ---- hero ---- */
  .cs-hero { min-height: min(92vh, 860px); padding-block: 8rem 4rem; }
  @supports (height: 1svh) { .cs-hero { min-height: min(92svh, 860px); } }
  /* the copy occupies the left half of the frame; the right half stays image */
  .cs-hero__copy { max-width: 56%; }
  .cs-hero .cs-lede { font-size: 1.12rem; max-width: 46ch; }

  /* Split head: label in a narrow left column, heading and copy on the right.
     The same shape the Delhivery and Siemens studies use. */
  .cs-sec__head--split {
    display: grid;
    grid-template-columns: 15rem 1fr;
    gap: 0 3rem;
    margin-bottom: 4rem;
  }
  .cs-sec__head--split .cs-eyebrow { grid-column: 1; margin-bottom: 0; padding-top: .5rem; }
  .cs-sec__head--split .cs-h2 { grid-column: 2; }
  .cs-sec__head--split .cs-copy { grid-column: 2; }

  /* ---- previous website ---- */
  .cmp { gap: 3rem; margin-bottom: 4.5rem; }
  .gains { gap: 0 3rem; grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .gains__item { padding-block: 1.8rem; border-bottom: 1px solid var(--cs-line); }

  /* ---- challenge ---- */
  .chal { gap: 0 4rem; }
  .chal__item { padding-block: 2rem; }

  /* ---- new structure ---- */
  .pillars { gap: 0 4rem; margin-bottom: 5rem; }
  .pillars__item { padding-block: 2rem; }

  .eco__stage { aspect-ratio: 1.62; }
  .eco__core { padding: 1.4rem 2.4rem; }
  .eco__core span { font-size: 2.6rem; }
  .eco__node { width: 11.5rem; padding: .75rem .9rem; }
  .eco__node span { font-size: .88rem; }
  .eco__node em { font-size: .68rem; }

  /* ---- browser frames ---- */
  .mock__chrome { padding: .75rem 1rem; }
  .mock__dot { width: .5rem; height: .5rem; }
  .mock__url { font-size: .68rem; padding: .3rem 1rem; }
  /* a small lift on hover — pointer-fine only, so a touch device never gets a
     transform stuck on after a tap */
  @media (pointer: fine) {
    .mock:hover { transform: translateY(-6px); border-color: var(--cs-red-lo); }
  }

  /* ---- final website ---- */
  /* The lead frame is the page's largest image and it is a tall full-page
     capture, so it runs in a narrower column than the grid below it rather
     than at the full 1480px shell — at full width it would stand well over
     two screens high on its own. */
  .shot--lead { margin-bottom: 5rem; }
  .shot--lead .mock { max-width: 62rem; margin-inline: auto; }
  .shot--lead figcaption { max-width: 62rem; margin-inline: auto; }
  .shots { gap: 3rem 2.5rem; }

  /* ---- responsive ----
     The desktop frame stays full width and the three phones sit in a centred
     row beneath it. Putting the phones in a side column instead was tried and
     dropped: it squeezed each one to about 190px, which is under half the
     390px viewport they were captured at, and the interface inside stopped
     being legible — which defeats the point of the section. */
  .resp { gap: 4rem; }
  .phones { max-width: 62rem; margin-inline: auto; gap: 2rem; }
  .resp__note { margin-top: 3.5rem; font-size: .95rem; }

  /* ---- outcome ---- */
  .out__stmt { font-size: 4.4rem; max-width: 15ch; }
  .out__copy { max-width: 58ch; }
  .sum { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 3rem; margin-top: 4rem; }
  .sum li { padding-block: 1rem; }
  .sum li:nth-last-child(-n+3) { border-bottom: 1px solid var(--cs-line); }
  .out__role { margin-top: 4rem; font-size: 1.3rem; }
  .cs-cta { margin-top: 4.5rem; }
  .cs-next { margin-top: 5rem; }
  .cs-next__link { font-size: 3rem; }
  .out__credit { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 4rem; }
}

/* ================================================= 14 LARGE DESKTOP 1440px */
@media (min-width: 1440px) {
  :root {
    --gutter: 4rem;
    --cs-space: 11.5rem;
    --cs-h1: 5.6rem;
    --cs-h2: 3.9rem;
    --cs-h3: 1.7rem;
  }

  .cs-hero__copy { max-width: 52%; }
  .eco__stage { aspect-ratio: 1.75; }
  .eco__core span { font-size: 3.2rem; }
  .eco__node { width: 13rem; }
  .shot--lead .mock,
  .shot--lead figcaption { max-width: 70rem; }
  .out__stmt { font-size: 5.6rem; }
  .cs-next__link { font-size: 3.6rem; }
}

/* ======================================================= 15 reduced motion */
/* Everything that moves is switched off here rather than shortened. The
   diagram's lines park FULLY DRAWN — the diagram is content, so it must be
   complete whether or not it was allowed to animate. casestudy-aiff.js also
   checks the same query and skips the parallax and the draw trigger. */
@media (prefers-reduced-motion: reduce) {
  .cs-hero__media img { animation: none; }
  .cs-hero__media { transform: none; }
  .js .eco__lines line,
  .js .eco__lines ellipse {
    opacity: 1;
    transition: none;
  }
  .mock { transition: none; }
  .mock:hover { transform: none; }
  .phones { scroll-behavior: auto; }
  .cs-next__link:hover .arrow { transform: none; }
  .cs-brand:hover .cs-brand__arrow { transform: none; }
}

/* ----------------------------------------------------------------------------
   REFACTOR NOTE
   Section 3 above (.cs-sec, .cs-eyebrow, .cs-h1/h2/h3, .cs-lede, .cs-meta)
   now exists in four page stylesheets: delhivery, siemens, warnerbros and
   this one. The values agree; only the grey ramp differs, and that difference
   is expressible as four custom properties. Lift them into a shared
   casestudy.css loaded between style.css and each page's own sheet, leave the
   ramp overrides in the page sheets, and delete the copies. Doing it now
   costs an hour; doing it after the fifth study costs an afternoon of diffing
   four files that have quietly drifted.
   ------------------------------------------------------------------------- */
