/* McKinsey — enterprise asset platform. Scoped to .case--mckinsey.
   The page is built from the shared block vocabulary in styles.css. What lives
   here are junctions — where two blocks meet on this page — plus the scenes the
   vocabulary names but does not style: the platform diagrams, the relaunch
   stepper, the role map, the model fixes, the scope bands and the year ledger.
   Family names are listed in TASK-71-mckinsey-platform-frames-RESULT.md. */

.case--mckinsey {
  overflow-x: clip;
  /* the hatched ground for every "not built / removed" mark on the page now
     lives in tokens.css as --hatch (raised there by TASK-101) */
  --scene-gap: clamp(28px, 4vw, 56px);
}
/* figures carry the UA 40px side margin; on this page everything aligns to the wrap */
.case--mckinsey figure { margin-inline: 0; }

/* ── A chapter heading breaks on this page, it does not run the measure ── */
/* wide enough that the line breaks the frames set with a <br> are the ones that
   happen — at 22ch the outputs' headings picked up a third line of their own */
.case--mckinsey .head--chapter { max-width: 30ch; }

/* ── The number of a hard part keeps the accent the page reads it in ── */
.case--mckinsey .list--numbered > li::before { color: var(--accent-text); }

/* ── Closing statement ── */
.case--mckinsey .interlude .prose { max-width: 62ch; font-size: var(--fs-lead); line-height: 1.4; }

/* TASK-104 · the TEMP plaque and every variant it switched are gone. Chapter 01
   is form A, the year is layout A, and the page has a single state. The .lanes
   rules that drew chapter 01's band variant went with it. */

/* ══ nda — the material notice ════════════════════════════════════════════
   The one line on the page that keeps recreated screens from reading as a
   claim on someone else's work. It is a plain banner in the hero: mark and
   note side by side, always open, scrolling away with the rest of the hero.
   Nothing parks, nothing collapses, nothing follows the reader — Pavlo chose
   this on 29.07 («просто статична, вот там как баннерочек и всё»), and the
   three placements that were on the TEMP plaque are gone with their JS.
   No full-width strip across the top of the page — ruled out by name. ═════ */

/* Block-level, not inline-flex: as an inline box in the hero it would sit on a
   line and collect that line's leading under itself, which reads as a stray
   few pixels between the banner and whatever follows. `fit-content` keeps it
   from stretching to the measure when the text is short. */
.case--mckinsey .nda {
  display: flex; align-items: stretch;
  width: fit-content; max-width: 78ch;
  border: var(--rule-bold) solid var(--rule);
  background: var(--white);
}
.case--mckinsey .nda__mark {
  flex: none; display: grid; place-items: center;
  padding: 8px 12px; border-right: var(--rule-bold) solid var(--rule);
  background: var(--bg-invert); color: var(--text-invert);
  font-family: var(--font-label); font-weight: var(--wt-med);
  font-size: var(--fs-caption); letter-spacing: var(--tr-label);
  text-transform: uppercase;
}
.case--mckinsey .nda__text {
  margin: 0; padding: 8px 14px;
  font-family: var(--font-label); font-size: var(--fs-caption);
  letter-spacing: var(--tr-label); line-height: 1.5;
  color: var(--text-muted);
}
/* credit → banner. The hero's own scale runs 16 (eyebrow → title) and 32
   (standfirst → credit); the banner is a different kind of object from the
   credit above it, not a third line of it, so it takes the step above the
   largest interval the hero already uses. Measured in the RESULT file. */
.case--mckinsey .case-hero > .nda { margin-top: var(--sp-7); }

/* ══ shared scene parts ════════════════════════════════════════════════════ */

/* a connector: line plus head, horizontal by default */
.case--mckinsey .arrow {
  display: flex; align-items: center;
  min-width: 28px; color: var(--rule);
}
.case--mckinsey .arrow::before { content: ""; flex: 1 1 auto; height: 2px; background: currentColor; }
.case--mckinsey .arrow::after {
  content: ""; flex: none; width: 0; height: 0;
  border: 5px solid transparent; border-left: 9px solid currentColor;
}
/* `arrow--left` used to reverse the head for the wireflow's right-to-left order.
   That order is gone (fix list 4), and with it the only user of the modifier. */
.case--mckinsey .arrow--blue   { color: var(--data-blue); }
/* a connector that is context, not the point being made */
.case--mckinsey .arrow--quiet { color: var(--gray-4); }
/* a state plate: what the roadmap did with an ask */
.case--mckinsey .plate {
  display: inline-block; margin: 0 0 var(--sp-3); padding: 5px 10px;
  font-family: var(--font-label); font-weight: var(--wt-med);
  font-size: var(--fs-caption); letter-spacing: var(--tr-label);
  text-transform: uppercase;
}
.case--mckinsey .plate--first { background: var(--bg-invert); color: var(--text-invert); }
.case--mckinsey .plate--later { border: var(--rule-hair) solid var(--rule); }
.case--mckinsey .plate--none  { border: var(--rule-hair) solid var(--rule-muted); background: var(--hatch); }

/* ══ mck-scene — one scene per step of a walk stepper ══════════════════════
   The walk steppers (block 2 and, later, the three years) follow the shared
   model the runbook describes and Collibra runs: the frame is pinned on the
   left at the height of the viewport, the words scroll past it on the right.
   The scene is whatever fills the frame — a screen of the product, or the
   wireflow drawing. Scenes stack in one grid cell, so the frame never changes
   size between steps. ══════════════════════════════════════════════════════ */

.case--mckinsey .mck-scene { margin: 0; }
/* the screen is shown whole, never cropped: it is fitted into the frame, and
   the rule sits on the picture itself so the border hugs what is visible
   rather than the empty box around it. */
.case--mckinsey .mck-scene > img {
  display: block;
  max-width: 100%; max-height: 100%;
  margin-inline: auto;
  object-fit: contain;
  border: var(--rule-hair) solid var(--rule-muted);
  background: var(--white);
}

/* ══ bf — blockframe: one screen reduced to its zones ══════════════════════
   Interface zones as plain rectangles, nothing written inside them: text is a
   flat bar, never a letter. Hierarchy comes from the fill, the stroke is one
   hairline everywhere, and the only colour on the drawing is the arrow that
   carries the asset. Ratio and greyscale follow the research in
   portfolio-edit-package.md §4.2, read against Broadsheet: radius 0, greys
   from tokens.css. ════════════════════════════════════════════════════════ */

.case--mckinsey .bf { margin: 0; }
.case--mckinsey .bf__name {
  margin-bottom: var(--sp-3);
  font-family: var(--font-display); font-weight: var(--wt-bold);
  font-size: var(--fs-h4); line-height: 1; letter-spacing: var(--tr-label);
  text-transform: uppercase; color: var(--text);
}
.case--mckinsey .bf__win {
  display: grid; grid-template-rows: 11% minmax(0, 1fr);
  aspect-ratio: 4 / 3;
  border: var(--rule-hair) solid var(--rule-muted);
  background: var(--white);
}
.case--mckinsey .bf__bar   { background: var(--gray-2); }
.case--mckinsey .bf__cols  { display: grid; grid-template-columns: 21% minmax(0, 1fr); min-height: 0; }
.case--mckinsey .bf__nav   { background: var(--gray-7); border-right: var(--rule-hair) solid var(--rule-muted); }
.case--mckinsey .bf__main  { display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 7%; padding: 7%; min-height: 0; }
/* text, at the level of greeking this drawing works at: a bar, never a letter */
.case--mckinsey .bf__lines { display: grid; gap: 5px; align-content: start; }
.case--mckinsey .bf__lines i { display: block; height: 6px; background: var(--gray-4); }
.case--mckinsey .bf__lines i + i { height: 4px; background: var(--gray-5); }
.case--mckinsey .bf__rows  { display: grid; grid-template-rows: repeat(4, minmax(0, 1fr)); gap: 6px; min-height: 0; }
.case--mckinsey .bf__row   { background: var(--gray-7); border: var(--rule-hair) solid var(--rule-muted); }
.case--mckinsey .bf__cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr)); gap: 6px; min-height: 0; }
.case--mckinsey .bf__card  { background: var(--gray-7); border: var(--rule-hair) solid var(--rule-muted); }
/* the two ends of the carry: the card that is taken, and the row it becomes */
.case--mckinsey .bf__card--mark,
.case--mckinsey .bf__row--mark { background: var(--white); border: var(--rule-bold) solid var(--data-blue); }
.case--mckinsey .bf__note { margin-top: var(--sp-3); font-family: var(--font-label); font-size: var(--fs-caption); letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--text-faint); }

/* ══ flow — three blockframes, connected ═══════════════════════════════════ */

.case--mckinsey .flow { position: relative; }
/* the wires are drawn over the row by JS, from marked tile to marked tile. The
   overlay carries no text, so a viewBox in the row's own pixels is safe here
   (the rule learned on TASK-62 is about type inside a fixed viewBox). */
.case--mckinsey .flow__wires {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none;
}
.case--mckinsey .flow:not(.is-wired) .flow__wires { display: none; }
/* colour is set once per wire and read by the line and its head through
   currentColor: `var()` is not substituted inside a presentation attribute,
   so the script writes geometry only and the page keeps the palette */
.case--mckinsey .flow__wire--lead  { color: var(--gray-4); }
.case--mckinsey .flow__wire--carry { color: var(--data-blue); }
.case--mckinsey .flow__wire path { fill: none; stroke: currentColor; stroke-width: 2; }
.case--mckinsey .flow__wire polygon { fill: currentColor; }
/* No casing under the line. A white casing was tried first and it cut the two
   cards the wire crosses in half, which reads as a rendering fault rather than
   as a wire; crossing them openly reads as one line laid over the drawing. */
/* wired, the connector is the drawn wire; the column's arrow is the fallback
   for no-JS and for the stacked layout, and stands down when the wire is up */
.case--mckinsey .flow.is-wired .flow__link .arrow { display: none; }
/* and the link column gives the middle of the drawing to the wire. The label
   rides just above the wire it names, a step smaller than the window notes, so
   it reads as belonging to the connector rather than to the row of notes at the
   foot of the drawing (Pavlo, 30.07: «я бы поднял выше и уменьшил текст»).
   One word per move: the column is ~96px wide, and anything longer stacked into
   four lines of mono there. The script measures where the wire band starts. */
.case--mckinsey .flow.is-wired .flow__link { position: relative; padding-block: 0; }
.case--mckinsey .flow.is-wired .flow__link .label {
  position: absolute; left: var(--sp-2); right: var(--sp-2);
  bottom: calc(100% - var(--flow-wire-top, 50%) + var(--sp-3));
  font-size: var(--fs-micro);
}

.case--mckinsey .flow__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(96px, 10vw, 150px) minmax(0, 1fr) clamp(96px, 10vw, 150px) minmax(0, 1fr);
  align-items: start;
}
/* the connectors sit level with the blocks they join, not with the middle of
   the drawing: the arrow carries one block to its twin. The link stretches to
   the height of a figure and then gives back the caption above and the note
   below, so what stays centred is the window itself. */
.case--mckinsey .flow__link {
  align-self: stretch;
  display: grid; align-content: center; justify-items: center; gap: var(--sp-3);
  padding: calc(var(--fs-h4) + var(--sp-3) + 46px) var(--sp-2) calc(var(--sp-3) + var(--fs-caption) * 1.4);
}
.case--mckinsey .flow__link .arrow { width: 100%; }
.case--mckinsey .flow__link .label { text-align: center; line-height: 1.4; }
.case--mckinsey .flow__link--carry .label { color: var(--text); }

/* ══ stepper — one mechanic for every pinned frame on this page ════════════
   The stepper device the vocabulary names; its CSS lives with the page, as it
   does on Collibra and Light. Two things are decided here once and inherited
   by every stepper on the page (block 2, the relaunch, the three years):

   · the frames of a stepper stack in one grid cell, so the frame is as tall as
     its tallest step and never changes height between steps — 0px by build,
     not by tuning;
   · the switch itself is instant, per sticky-chapter-runbook.md §2. What
     animates is the content inside the frame, which is the reading of the rule
     the run order settled on before this task started.

   Without JavaScript and below 900px every stepper degrades to the static
   series its steps already are. ═══════════════════════════════════════════ */

.case--mckinsey .sticky-chapter__steps { display: none; }
.case--mckinsey .sticky-chapter__header { margin-bottom: var(--r-block); }
/* the eyebrow of a chapter header owns the same interval it owns in the hero
   (case.css: `.case-hero > .label`), so a label above a chapter heading reads
   at one rhythm across the page. Without it the two sat flush. */
.case--mckinsey .sticky-chapter__header > .label { margin-bottom: var(--sp-4); }

/* ── walk stepper: the shared model, pinned frame beside a column of steps ──
   Only the steppers that show screens of the product use it (Pavlo's call,
   TASK-86): the relaunch chapter draws schemes and keeps its own form. Below
   900px there is no pinned frame at all — JS moves each scene into its step,
   so the words and the picture stay together in one column. */
.case--mckinsey .mck-walk .sticky-chapter__steps { display: block; }
.case--mckinsey .mck-walk .sticky-step { padding: var(--sp-6) 0; }
.case--mckinsey .mck-walk .sticky-step + .sticky-step { border-top: var(--rule-hair) solid var(--rule-muted); }
.case--mckinsey .mck-walk .sticky-step__head {
  margin: var(--sp-3) 0 0;
  font-family: var(--font-display); font-weight: var(--wt-bold);
  font-size: var(--fs-h4); line-height: 1.05; letter-spacing: var(--tr-tight);
  text-transform: uppercase;
}
.case--mckinsey .mck-walk .sticky-step .prose { margin-top: var(--sp-4); font-size: var(--fs-small); line-height: 1.55; }
/* below 900px the moved-in scene is a picture in the flow of its step */
.case--mckinsey .mck-walk .sticky-step .mck-scene { margin-top: var(--sp-5); }

@media (min-width: 900px) {
  /* the stage is a <figure>, so it carries the browser's own block margin
     (19px top and bottom at this font size). Inside the pinned layout that
     margin is visible as a strip of page showing through: the chapter's rule
     draws at the top of the body, the inset field starts 19px lower, and at
     the foot the field stops 19px short while the column's rule runs on. */
  .case--mckinsey .sticky-chapter.is-enhanced .sticky-chapter__stage {
    position: sticky; top: 0;
    display: grid; align-content: center;
    height: 100vh; margin: 0;
  }
  .case--mckinsey .sticky-chapter.is-enhanced .sticky-chapter__viewport { display: grid; min-height: 0; }
  .case--mckinsey .sticky-chapter.is-enhanced [data-frame] {
    grid-area: 1 / 1; margin: 0;
    visibility: hidden;
  }
  .case--mckinsey .sticky-chapter.is-enhanced [data-frame].is-active { visibility: visible; }
  .case--mckinsey .sticky-chapter.is-enhanced .sticky-chapter__steps { display: block; padding-bottom: 42vh; }
  .case--mckinsey .sticky-chapter.is-enhanced .sticky-step { min-height: 76vh; }

  /* ── the walk model at full width: frame left, steps right ──────────────
     Widths are the shared model's (1.42fr / 0.58fr, floor 340px on the column).
     The light field on the left is the only thing that separates the frame from
     the column of steps: the black rule along the top of the body and the black
     divider between field and column are gone (Pavlo, 29.07 — «эти чёрные
     линии, они наслаиваются»). The screenshot keeps its own hairline; that is a
     different level and is not touched here. The tail under the last step keeps
     the stage pinned until that step has reached the middle of the screen. */
  .case--mckinsey .mck-walk.is-enhanced .sticky-chapter__body {
    display: grid;
    grid-template-columns: minmax(0, 1.42fr) minmax(340px, 0.58fr);
    align-items: start;
  }
  .case--mckinsey .mck-walk.is-enhanced .sticky-chapter__stage {
    grid-template-rows: minmax(0, 1fr);
    justify-items: center;
    min-width: 0;
    padding: clamp(20px, 3vw, 48px);
    background: var(--bg-inset);
  }
  .case--mckinsey .mck-walk.is-enhanced .sticky-chapter__viewport {
    width: 100%; min-height: 0;
    display: grid; align-items: center; justify-items: center;
  }
  .case--mckinsey .mck-walk.is-enhanced .sticky-chapter__steps {
    padding: 0 clamp(28px, 4vw, 64px) 42vh;
  }
  .case--mckinsey .mck-walk.is-enhanced .sticky-step {
    display: flex; flex-direction: column; justify-content: center;
    padding: 0; border-top: 0;
    opacity: 0.28;
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
  }
  .case--mckinsey .mck-walk.is-enhanced .sticky-step.is-active { opacity: 1; transform: translateX(8px); }
  /* the scene fills the frame region; the picture inside it keeps its ratio */
  .case--mckinsey .mck-walk.is-enhanced .mck-scene { display: grid; align-content: center; width: 100%; height: 100%; }

  /* ── the wireflow inside the pinned frame ───────────────────────────────
     The drawing came from a full-width block and now has 936px instead of
     1310. Three things follow from that and none of them are cosmetic:
     the connector columns give width back to the windows; the name is set at
     the caption scale the narrower window can carry; and the name reserves two
     lines for everyone, because "Publisher space" wraps and would otherwise
     drop its window a line below the two beside it. */
  /* the connector column has to hold its longest word at the label scale —
     "CONNECTED" is 9 characters of mono, ~70px — or the words spill onto the
     windows beside them. 12% of the frame is 112px, which clears it. */
  .case--mckinsey .mck-walk.is-enhanced .flow__row {
    grid-template-columns: minmax(0, 1fr) clamp(100px, 12%, 140px) minmax(0, 1fr) clamp(100px, 12%, 140px) minmax(0, 1fr);
  }
  .case--mckinsey .mck-walk.is-enhanced .bf__name {
    min-height: 2.1em;
    font-size: var(--fs-body); line-height: 1.05;
  }
  .case--mckinsey .mck-walk.is-enhanced .bf__note { min-height: 2.8em; }
  /* the link's top padding is written against the name + window of the wide
     layout; here the name is shorter, so the arrow would sit above its blocks.
     Only the fallback arrow needs it — wired, the label goes to the annotation
     band and takes the offset the script measured. */
  .case--mckinsey .mck-walk.is-enhanced .flow__link {
    padding-top: calc(2.1em + var(--sp-3) + 46px);
  }
  .case--mckinsey .mck-walk.is-enhanced .flow.is-wired .flow__link {
    padding-top: 0;
  }

  /* the scene switches at once; its parts arrive. A keyframe, not a transition:
     the frame is visibility:hidden until it is active, and an animation replays
     reliably on every activation where an interpolation would not start. */
  .case--mckinsey .sticky-chapter.is-enhanced [data-frame].is-active > * {
    animation: mck-rise 420ms var(--ease) backwards;
  }
  .case--mckinsey .sticky-chapter.is-enhanced [data-frame].is-active > :nth-child(2) { animation-delay: 90ms; }
  .case--mckinsey .sticky-chapter.is-enhanced [data-frame].is-active > :nth-child(3) { animation-delay: 160ms; }
  .case--mckinsey .sticky-chapter.is-enhanced [data-frame].is-active > :nth-child(4) { animation-delay: 220ms; }
}

@keyframes mck-rise { from { opacity: 0; transform: translateY(12px); } }
/* TASK-121 · the one change of the year chapter that is not a ride. Everything
   else the chapter does, the scroll does: the copy and the screens travel on a
   spring and nothing appears or disappears. The year alone cannot travel — it
   is the anchor — so it changes, by opacity and a sixteenth of its own height.
   mck-lift went with the frame swap, and mck-quiet went with the screen fade
   once the screens started riding instead. */
@keyframes mck-turn  { from { opacity: 0; transform: translateY(0.06em); } }

/* ══ matrix (4A) — four role rows read across ══════════════════════════════ */

.case--mckinsey .output-block { margin-top: var(--r-chapter-in); }
/* a heading owns the air under it, but .prose is a margin-free box: where a
   block runs to a second and third paragraph, the interval has to be stated */
.case--mckinsey .output-block > .prose + .prose { margin-top: var(--sp-5); }

/* ══ the accent in prose (TASK-104) ════════════════════════════════════════
   Long paragraphs on this page gave the eye nothing to catch: one step between
   the lead and the body and no change of any kind inside a paragraph, so the
   two body paragraphs of the method block read as arbitrarily smaller rather
   than as a level (Pavlo, 30.07).

   The device is the one already proven on Hearing (.hear-walk__lede b): the
   only orange in the system allowed to carry words, at regular weight. Weight
   stays regular for two reasons — a bolded phrase reflows the paragraph every
   time the step changes, and orange plus bold plus a run of words is what a
   link looks like. Orange is not an action colour anywhere in this system, so
   as a plain colour change it cannot be mistaken for one.

   The rule for what gets it: the phrase the reader should carry away if they
   read nothing else in that paragraph — read the accents alone down the
   chapter and they form its summary. One per paragraph, never a number on its
   own (a figure already carries weight; bolding it is the cheapest accent
   there is) and never a whole sentence.

   Scoped to these two places, not to .prose everywhere: the hero credit uses
   <b> for the role line and must keep its own treatment. */
.case--mckinsey .yr .prose b,
.case--mckinsey .output-block > .prose b {
  color: var(--accent-text);
  font-weight: var(--wt-reg);
}
.case--mckinsey .matrix { margin-top: var(--r-block); }

.case--mckinsey .matrix__head,
.case--mckinsey .matrix__row {
  display: grid;
  grid-template-columns: minmax(180px, 230px) minmax(0, 1fr) minmax(0, 1fr) minmax(190px, 250px);
  column-gap: clamp(20px, 2.6vw, 36px);
}
.case--mckinsey .matrix__head { padding-bottom: var(--sp-3); border-bottom: var(--rule-hair) solid var(--rule); }
.case--mckinsey .matrix__row { padding: var(--sp-6) 0; border-bottom: var(--rule-hair) solid var(--rule-muted); align-items: start; }
.case--mckinsey .matrix__row:last-of-type { border-bottom: var(--rule-bold) solid var(--rule); }
.case--mckinsey .matrix__name {
  margin: 0;
  font-family: var(--font-display); font-weight: var(--wt-bold);
  font-size: var(--fs-h4); line-height: 1.02; text-transform: uppercase;
}
.case--mckinsey .matrix__mark { display: block; height: 2px; margin: var(--sp-2) 0; background: var(--rule-muted); }
.case--mckinsey .matrix__mark--blue   { background: var(--data-blue); }
.case--mckinsey .matrix__mark--teal   { background: var(--data-teal); }
.case--mckinsey .matrix__mark--violet { background: var(--data-violet); }
.case--mckinsey .matrix__cell .prose { font-size: var(--fs-small); line-height: 1.5; }
.case--mckinsey .matrix__key { display: none; }
.case--mckinsey .matrix__row--quiet .matrix__name,
.case--mckinsey .matrix__row--quiet .prose { color: var(--text-muted); }
.case--mckinsey .matrix__foot {
  display: grid;
  grid-template-columns: minmax(180px, 230px) minmax(0, 1fr);
  column-gap: clamp(20px, 2.6vw, 36px);
  margin-top: var(--sp-5);
}
.case--mckinsey .matrix__foot .prose { max-width: 78ch; }

/* ══ yr — the three-year chapter ═══════════════════════════════════════════
   TASK-121 rebuilt this chapter around one sentence: the year is an anchor and
   the material is what moves. Three floors on the deck — the year mark, the
   reel of copy under it, the rack of screens beside them — and only the reel
   travels. It travels with the scroll, continuously, so the chapter reads as a
   page being pulled past a fixed number rather than as seven slides being
   redrawn. The year turns three times, not seven: inside a year the copy and
   the screen change under a mark that holds.

   What follows in this section is the shape with no mechanic at all — a phone,
   a reader without JavaScript, a machine asking for reduced motion. Seven
   blocks in reading order, each with its own "Year N" stamp and its own screen.
   The deck is built on top of that inside the media query below, and every part
   of it is reversible: `case-mckinsey.js` puts the screens back into their
   blocks the moment it stands down. ═════════════════════════════════════════ */

.case--mckinsey .yr__stamp {
  margin: 0 0 var(--sp-3);
  font-family: var(--font-label); font-weight: var(--wt-med);
  font-size: var(--fs-caption); letter-spacing: var(--tr-label);
  text-transform: uppercase; color: var(--text-muted);
}
.case--mckinsey .yr__head {
  margin: 0;
  font-family: var(--font-display); font-weight: var(--wt-bold);
  font-size: var(--fs-h4); line-height: 1.05; letter-spacing: var(--tr-tight);
  text-transform: uppercase;
}
.case--mckinsey .yr .prose { margin-top: var(--sp-4); font-size: var(--fs-small); line-height: 1.55; }
.case--mckinsey .yr__shot { margin: var(--sp-5) 0 0; }
/* the pinned mark belongs to the deck; with no deck the stamps carry the year */
.case--mckinsey .yrs .yr__year { display: none; }
/* a series of blocks needs its joints drawn — but only while they are a series.
   On the deck the reel's own gap is the interval and a rule would ride past the
   gate on every step. */
.case--mckinsey .yrs:not(.is-enhanced) .yr + .yr {
  margin-top: var(--sp-7); padding-top: var(--sp-7);
  border-top: var(--rule-hair) solid var(--rule-muted);
}

/* TASK-121 · the site scrolls smoothly (styles.css sets it on <html>), which
   leaves the reel's spring chasing a target that is itself still moving and
   makes any measurement of the mechanic irreproducible. The editorial rules
   keep scroll-behavior at auto anyway (§Движение: inertia is a library's job,
   not the browser's). Local to this page — the shared file is not touched from
   here, and this declaration wins because this sheet is loaded last. */
html { scroll-behavior: auto; }

/* ── the screen, whole ───────────────────────────────────────────────────
   The frame gets the height that is left in the pinned stage and the picture
   keeps its own proportion inside it; the rule sits on the picture, so it hugs
   what is visible rather than the box around it (the same reading as
   .mck-scene). The 66vh cap is what leaves the caption, the column's own air
   and the body's top padding inside 100vh on a 13" screen — the source is
   4320px wide, so at every viewport the picture is still oversampled.

   The container carries the screens' own ratio (4320/3072 = 1.406) so the box
   has its height before the lazy picture arrives. Without it the frame collapsed
   to nothing until the image loaded and the page grew under the reader —
   measured, not guessed: the chapter could not even be reached by scrolling,
   because the document was ~4000px shorter than it ends up being. */
/* TASK-104 hotfix · the picture used to break out of its frame and print over
   the caption. Cause, measured at 1440x700: the cap was written as
   `max-height: 66vh` on the box plus `max-height: 100%` on the image — and a
   percentage max-height resolves against the parent's *height*, which this box
   never had (its height came from aspect-ratio). So the limit silently did
   nothing: box 462px, image 650px, 153px of overhang straight across the
   caption. Capping the width instead makes the constraint real — the box is
   never wider than a 66vh-tall frame of this ratio can be, so its own
   aspect-ratio keeps the height inside the viewport with no percentage to
   resolve. The image then fills a box that is exactly its own proportion,
   which is what lets the hairline hug the picture rather than a letterboxed
   area around it. */
/* the cap sits on the figure, not on the picture box inside it: the caption is
   a sibling of that box, so capping the box alone left the caption running the
   full column width and starting to the left of the picture it labels. */
.case--mckinsey .yr__shot {
  min-width: 0;
  width: min(100%, calc(66vh * 1.406));
  margin-inline: auto;
}
.case--mckinsey .yr__shot .frame__shot {
  display: grid;
  width: 100%;
  aspect-ratio: 1.406;
}
.case--mckinsey .yr__shot img {
  width: 100%; height: 100%; object-fit: contain;
  border: var(--rule-hair) solid var(--rule-muted);
  background: var(--white);
}

/* ══ the year mark ═════════════════════════════════════════════════════════
   TASK-121 · the mark is now one node for the whole chapter instead of one per
   step, and that is the fix for the defect Pavlo saw: the old mark sat inside
   each frame, above copy of a different height every time, so an instant frame
   swap teleported the number up to 38px. A mark outside the frames cannot move
   at all — the stillness is structural, not a value someone has to keep right.

   The numeral gets its own floor, the recipe the moodboard measured off
   Archivio Armani: the number sits at the top of a band twice its own height
   and the lower half stays empty, which is what gives a number this size its
   air without a grey tone anywhere. The rule closes the floor and starts the
   reel under it.

   Every number of the deck is declared here, on the section, so the CSS below
   reads no literal. The values are Pavlo's own, off the tuner of this task on
   31.07 — measured at 1440 and written back in the units the page needs.

   His one substantive change to the recipe was the floor: a whole numeral of
   empty band became a single interval of the spacing scale, so the line closes
   right under the digit instead of a numeral's height below it. The moodboard's version of this device gives the
   number half a column; on our page the column is 340px wide and the empty band
   read as a hole rather than as air. Taken as his call on the live page, which
   is where the device is either working or not. */
.case--mckinsey .yrs {
  /* 114px at 1440, his pick, kept proportional across the range */
  --yr-size: clamp(85px, 7.92vw, 120px);
  --yr-lh: 1;                             /* Armani sets these at 1.0 flat */
  --yr-weight: var(--wt-bold);
  /* The interval around the cut, taken from the 4px spacing scale rather than
     from the numeral's own size [Паша, 31.07: «откуда у нас такие цифры, 22.81,
     у нас не по сетке всё»]. It was 0.2 of the numeral, which produced 22.81px
     — a number belonging to the type scale, standing in a place that belongs to
     the spacing scale. --sp-5 is 24px and reads the same. One value sets both
     sides of the line: the year stands this far above it, the copy this far
     under it. */
  --yr-lede: var(--sp-5);                 /* year → line → copy, both sides */
  /* 56 -> 172 across two rounds on the slider [Паша, 31.07: «расстояние между
     блоками сделать побольше»]. Distance is what the reader reads the ride by:
     with more of it the same scroll produces more travel, which is half of why
     a scroll stops feeling stuck. */
  --yr-gap: 188px;                     /* between two blocks of the reel */
  /* 0.75 -> 0.55. It was set while a mask was also fading the edges; with the
     mask gone, opacity is the only thing saying which block is being read
     [Паша: «те тексты, которые не в фокусе, тоже делать немножко прозрачными»]. */
  --yr-dim: 0.55;                         /* the neighbours, in opacity */
  --yr-quiet: 360ms;                      /* the year turn, and the dimming */
  --yr-step-vh: 54vh;                  /* scroll length of one step */
  --yr-shot-vh: 60vh;                     /* the screen's own height on the rack */
  --yr-shot-gap: 40px;                    /* between two screens */
}
.case--mckinsey .yr__year-word {
  display: block; margin-bottom: var(--sp-2);
  font-family: var(--font-label); font-weight: var(--wt-med);
  font-size: var(--fs-caption); letter-spacing: var(--tr-label);
  text-transform: uppercase; color: var(--text-muted);
}
/* letterfit: a display numeral takes --tr-caps at any size, by material and not
   by level (skill §1). --tr-display, the token this element used to be the only
   user of, was set against a 38px numeral and does not follow the curve up. */
.case--mckinsey .yr__year-num {
  display: block;
  font-family: var(--font-display); font-weight: var(--yr-weight);
  font-size: var(--yr-size); line-height: var(--yr-lh);
  letter-spacing: var(--tr-caps); word-spacing: var(--ws-caps);
  font-variant-numeric: tabular-nums;
}

@media (min-width: 900px) {
  /* ── the deck ───────────────────────────────────────────────────────────
     Two columns and two rows: the mark on the upper left, the reel in the
     column under it, the screens in a rack that spans both rows and centres
     against the whole stage. The stage stretches instead of centring its
     content, because the gate can only be measured if it has a height of its
     own to be measured against.

     The top padding is not symmetry, it is clearance: the page's own index bar
     (.case-back.case-idx) is sticky at the top of the viewport and its lower
     edge sits at 60px, so a mark starting at 36px would be printed on by the
     index sign — measured, and visible in the first screenshot as a diamond
     glued to the word YEAR. */
  .case--mckinsey .yrs.is-enhanced .sticky-chapter__stage {
    align-content: stretch;
    grid-template-rows: minmax(0, 1fr);
    /* no padding at the foot: the gate's cut has to land on the edge of the
       window, not 36px above it — see the gate below */
    padding: clamp(68px, 8vh, 88px) 0 0;
  }
  .case--mckinsey .yrs.is-enhanced .sticky-chapter__viewport {
    grid-template-columns: clamp(240px, 26%, 350px) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    column-gap: clamp(32px, 4vw, 56px);
    overflow: hidden;
  }
  /* No rule under the numeral [Паша, 31.07: «полоску под годом можно убрать»].
     It was there to close the year's floor while the floor was a numeral high;
     at 0.2 the band is short enough that the interval alone reads as a break,
     and a line across a 340px column plus a mask edge right under it was two
     devices doing one job. */
  .case--mckinsey .yrs.is-enhanced .yr__year {
    display: block;
    grid-column: 1; grid-row: 1; align-self: start;
    margin: 0;
    padding-bottom: var(--yr-lede);
  }

  /* ── the reel ───────────────────────────────────────────────────────────
     The gate is the window the reel rides behind, and it cuts rather than
     fades [Паша, 31.07: «это исчезновение аккуратное — это всё-таки не в духе
     брутализма... тонкая линия, и под неё скроллить, как будто она просто
     срезает всё это»]. So the soft mask that used to sit on both edges is gone
     and the line is doing the work instead: a hairline under the year, the type
     running under it, and nothing softening the join. The device only reads if
     the cut is a declared edge — a gradient says "this is ending", a rule says
     "this is where the page is cut".

     The lower edge takes no line, and the gate runs to the very bottom of the
     stage so that cut lands on the edge of the screen. A rule there would close
     the column into a box, which the style rules forbid outright, and a cut
     floating 36px above the window bottom reads as a mistake.

     The transform is written by the spring in `case-mckinsey.js` and carries no
     transition of its own: a CSS interpolation on the same property would fight
     the spring for it every frame. */
  .case--mckinsey .yrs.is-enhanced .yrs__gate {
    grid-column: 1; grid-row: 2;
    min-height: 0; overflow: hidden;
    /* grey, not black [Паша, 31.07]. The cut is a working edge, not a heading
       rule: black would make the strongest line of the column belong to a piece
       of machinery rather than to the year standing over it. */
    border-top: var(--rule-hair) solid var(--rule-muted);
  }
  /* The lede: how far under the cut the active block's heading stands. It is
     the year's own floor turned downwards — the same distance that separates
     the numeral from the line above it, so the line sits in a symmetrical
     interval and the heading reads as the next thing after the year rather
     than as the top of a separate column. The reel is anchored by this padding
     (see `measure` in case-mckinsey.js), which is why one value sets both the
     resting place of the copy and the air under the rule. */
  .case--mckinsey .yrs.is-enhanced .yrs__reel {
    display: grid; row-gap: var(--yr-gap);
    padding-top: var(--yr-lede);
    transform: translateY(var(--yr-y, 0px));
    will-change: transform;
  }
  .case--mckinsey .yrs.is-enhanced .yr {
    opacity: var(--yr-dim);
    transition: opacity var(--yr-quiet) var(--ease);
  }
  .case--mckinsey .yrs.is-enhanced .yr.is-active { opacity: 1; }
  /* the stamp is the fallback's way of saying the year; on the deck the pinned
     mark says it. It leaves the page but not the accessibility tree, so a block
     still reads "Year 2, heading, prose" in the order it is written. */
  .case--mckinsey .yrs.is-enhanced .yr__stamp {
    position: absolute; width: 1px; height: 1px; margin: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }

  /* ── the rack ───────────────────────────────────────────────────────────
     The screens ride too, sideways [Паша, 31.07: «скопировать просто в той
     листовке scroll, потому что получилось»]. The device is the one from the
     Light case (TASK-119): seven screens standing in a row behind a window,
     travelling on the same spring that carries the copy, with the neighbours
     showing at the edges. What went with it is the swap — no fade, no arrival,
     nothing appears. The screen was the one part of this chapter that was still
     being redrawn rather than moved, and that is exactly what Pavlo disliked.

     Sideways rather than down, because a 540px-tall screen riding vertically
     through a 700px window spends most of the journey cut off at both edges.
     Sideways it is whole the whole time and only its position changes.

     The width is set in vh so the screen keeps its proportion against the
     stage rather than against the column: at 60vh the picture is 540px tall
     and 759 wide, which leaves the neighbours a visible strip on both sides of
     a 914px window. */
  .case--mckinsey .yrs.is-enhanced .yrs__shots {
    grid-column: 2; grid-row: 1 / -1;
    align-self: center; min-width: 0; overflow: hidden;
  }
  .case--mckinsey .yrs.is-enhanced .yrs__rack {
    display: flex; align-items: center; gap: var(--yr-shot-gap);
    transform: translateX(var(--yr-x, 0px));
    will-change: transform;
  }
  .case--mckinsey .yrs.is-enhanced .yr__shot {
    flex: 0 0 auto;
    width: calc(var(--yr-shot-vh) * 1.406);
    margin: 0;
    opacity: var(--yr-dim);
    transition: opacity var(--yr-quiet) var(--ease);
  }
  .case--mckinsey .yrs.is-enhanced .yr__shot.is-active { opacity: 1; }

  .case--mckinsey .yrs.is-enhanced .sticky-step { min-height: var(--yr-step-vh); }

  /* ── the one thing left that is not a ride ──────────────────────────────
     The year cannot ride: it is the thing everything else is measured against.
     So it is the only element in the chapter that still changes rather than
     moves, and it does it in the smallest way there is — opacity and a
     fraction of its own height, so a 114px numeral shifts by 7px rather than
     by the 12px a 15px paragraph would take. */
  .case--mckinsey .yrs.is-enhanced .yr__year-num.is-turn {
    animation: mck-turn var(--yr-quiet) var(--ease) backwards;
  }
}

/* ══ responsive ════════════════════════════════════════════════════════════ */

@media (max-width: 899px) {
  /* every scene stacks; the connectors turn to point down the page */
  .case--mckinsey .arrow {
    width: 32px; min-width: 0; height: 12px; margin: var(--sp-4) auto;
    transform: rotate(90deg);
  }

  /* the wireflow turns down the page, and the drawn wires stand down with it:
     stacked, the connector is the arrow in its own column again.
     The arrow has to give up the `width: 100%` it takes in the wide layout:
     rotated a quarter turn, a full-column arrow is ~390px long and, since a
     transform does not resize the layout box, it runs straight through the
     windows above and below it. Found by looking at 390, not in the code. */
  .case--mckinsey .flow__row { grid-template-columns: 1fr; }
  .case--mckinsey .flow__link { margin-top: 0; padding: var(--sp-4) 0; }
  .case--mckinsey .flow__link .arrow { width: 32px; }
  .case--mckinsey .bf { display: flex; flex-direction: column; }

  /* on a phone the screen is a picture in the flow of its step, at column
     width: the vh cap belongs to the pinned frame and there is none here */
  .case--mckinsey .yr__shot img { width: 100%; max-height: none; }

  .case--mckinsey .matrix__head { display: none; }
  .case--mckinsey .matrix__row { grid-template-columns: 1fr; row-gap: var(--sp-5); }
  .case--mckinsey .matrix__key { display: block; margin-bottom: var(--sp-2); }
  .case--mckinsey .matrix__foot { grid-template-columns: 1fr; row-gap: var(--sp-2); }
}

/* Hero title · the real duration is two and a half years (TASK-103 C3), and that
   phrase is long enough that a hand-placed <br> lands wrong on a phone — it took
   the title to four lines at 390. Balancing reproduces the authored composition
   ("Two and a half years / on one platform.") from the tablet width up and keeps
   the plain break below it, where balancing would split the number phrase. */
@media (min-width: 600px) {
  .case--mckinsey .case-hero .head--page { text-wrap: balance; }
}

@media (prefers-reduced-motion: reduce) {
  .case--mckinsey *,
  .case--mckinsey *::before,
  .case--mckinsey *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
  }
}
