/* ==========================================================================
   Wolfpack case studies — shared stylesheet
   Navy #000B29 · coral #F95954 (rationed) · Roboto 700 headings · Montserrat body

   This is the generalized descendant of
   sm3-specific-pages/sm3-assets/css/sm3-case.css, which came from
   hire/assets/css/hire.css, which came from rates/css/rates.css — the pattern
   library. Every case study in case_studies/ loads this file, so the chrome,
   the type scale, and the scroll-reveal timing are identical across all of
   them by construction rather than by anyone remembering.

   WHAT CHANGED FROM sm3-case.css, and why. The SetMaster sheet carried one
   deliberate palette exception: anywhere the product was pictured, the frame
   used SetMaster's own near-black surface and its magenta/cyan Out/In
   identity, so the product looked like itself. That exception was earned by a
   product having a visual identity. A case study about financial modeling has
   no product to look like, so carrying the magenta/cyan pair forward would be
   borrowing a semantic as decoration, which is exactly what the SM3 header
   comment forbids. It is dropped. What survives is the neutral dark FIGURE
   GROUND (--fig-*), kept so a screenshot sits in a frame of the same visual
   weight it would have on the SetMaster page. The SetMaster .trow table is
   replaced by the generic .dtable below.

   CORAL DISCIPLINE, in one place so it cannot drift. Coral is allowed on:
     1. the nav CTA                    4. the closing CTA
     2. the hero rule                  5. link hover
     3. the pull-quote rule            6. the focus ring
   Nothing else. Not a section heading, not a stat value, not a subhead, not a
   sidebar label, not a table row. Where coral is a FILL, text on it is navy
   (5.8:1), never white (fails AA).

   Refusals the long-form report added to that list on 2026-08-11 (#172), each
   a place the amber original used its accent and this sheet does not: not a
   source-link glyph, not a deal-value step in the transaction map, not a macro
   band, not a highlighted table row, not a part number, not an era card, not
   the "open full width" affordance. The count is STILL SIX. Adding the report
   components below moved it not at all, which is the test those components had
   to pass.

   NO NEW HUES IN PAGE CHROME. This sheet introduces zero colors beyond the navy
   system and the neutral figure ground anywhere a reader meets the page itself
   — headings, rules, tables, chips, buttons, links. A table that needs to mark
   one row as the important one does it with weight, a background lift, and a
   mono chip. Do not add an accent inline in a page.

   ONE EXCEPTION, EARNED AND SCOPED: HUE MAY ENCODE A CATEGORY INSIDE A FIGURE.
   Added 2026-08-15 (#199) for the transaction map's four lanes, and written
   here because sm3-case.css's one exception was written down the same way.
   The rule the exception encodes:

     * a figure whose data has a genuinely CATEGORICAL variable may spend hue on
       it, in tokens declared here and contrast-measured here — never inline in
       a page, and never on anything that is not a figure;
     * the hue must be REDUNDANT with something already in the figure (the map's
       lane hue repeats what vertical position already says), so a reader who
       cannot separate two of the hues loses nothing;
     * it may not touch the coral ration, the navy chrome, or a magnitude scale.

   That last clause is the load-bearing one, and the map is why. See
   --map-l0..l3 below.

   TWO CONSEQUENCES OF "NO NEW HUES" THE REPORT COMPONENTS HAD TO SOLVE, both
   worth knowing before reaching for a color:
     * DIRECTION (a metric up or down) is carried by the glyph — ▲ ▼ ▬ — and by
       weight, never by a green and a red. See .trend.
     * MAGNITUDE (the map's five-step deal-value scale) is carried by a
       sequential ramp built along the figure ground's own neutral axis, dark to
       light on a dark surface. See --fig-ramp-1..4. That is an ordered scale in
       one hue, which is what the variable actually is; a categorical palette
       would make a magnitude unreadable. It is an extension of an existing
       ramp, not a new hue.
       IT STAYS NEUTRAL NOW THAT THE LANES ARE COLORED, and that is the whole
       design of the figure: HUE = which lane (category), LIGHTNESS = how big
       (magnitude), on the same dot, and the two never swap. Putting the value
       scale into hue as well would leave the map with two categorical-looking
       encodings and no readable magnitude.
   ========================================================================== */

:root {
  --navy:      #000B29;
  --coral:     #F95954;
  --white:     #FFFFFF;
  --muted:     #BFC2CA;
  --faint:     #808594;   /* 5.3:1 on navy — AA for normal text, labels only */
  --surface:   #0A1435;
  --surface-2: #101B3F;
  --line:      #222E52;
  --radius:    4px;
  --measure:   1100px;
  --mono:      ui-monospace, "Cascadia Mono", "Roboto Mono", Consolas, monospace;

  /* Neutral figure ground. Screenshots of spreadsheets and dashboards are
     mostly light, and a light image on navy fights the page; a near-black mat
     lets the image be the brightest thing in its own frame. Scoped to .shot,
     .ph, .dtable, and .figframe — never page chrome, never page type.

     .figframe joined that list on 2026-08-11 (#172), and the reason is the one
     the ground was created for. A chart drawn in HTML is the alternative to a
     screenshot OF a chart; putting it anywhere else would make the two read as
     different kinds of object when they are the same kind of object. The rule
     the scope actually encodes is "anything standing where a captured image
     would stand", and a rendered figure stands exactly there. */
  --fig-bg:    #0A0A0A;
  --fig-row:   #1A1A1A;
  --fig-lift:  #23262E;   /* the one emphasized row in a .dtable */
  --fig-line:  #2A2A2A;
  --fig-text:  #E6E6E6;
  --fig-dim:   #9C9C9C;

  /* The sequential ramp, four steps, dark to light. Magnitude only — never a
     category, never a page element. Measured against --fig-row #1A1A1A, the
     lighter of the two surfaces a mark can land on, because the darker one is
     the easy case:
       --fig-ramp-1  3.6:1     --fig-ramp-3  10.6:1
       --fig-ramp-2  6.6:1     --fig-ramp-4  16.7:1
     Step 1 is the binding constraint. It sits at 3.6:1 rather than nearer the
     3:1 floor deliberately: a 0.66rem dot is a small mark, and the headroom is
     what survives a lane tint changing later. Do not darken it to widen the
     ramp — add contrast at the light end instead, which is free.
     The FIFTH state, "undisclosed", is deliberately NOT a step: it is the
     absence of a value, drawn as an unfilled ring outside the ramp entirely.
     Bucketing it as "smallest" would be a claim the sources do not make. */
  --fig-ramp-1: #6E737E;   /* < $100m       */
  --fig-ramp-2: #98A0AE;   /* $100 – 499m   */
  --fig-ramp-3: #C3C9D4;   /* $500 – 999m   */
  --fig-ramp-4: #FFFFFF;   /* ≥ $1bn        */

  /* ── The transaction map's four lane hues ───────────────────────────────
     Categorical, not ordered: which lane a transaction landed in is a name, not
     a quantity, and there is no sense in which retail is "more" than software.
     So these are four separated hues of deliberately similar lightness — the
     opposite construction to the ramp above, and the reason the two can share a
     dot without fighting.

     THE HUE IS REDUNDANT, WHICH IS WHAT MAKES IT SAFE. A lane is already
     identified by vertical position and by a name printed inside it. The hue
     adds a fourth cue for tracking a lane across a plot that is 1600px wide at
     its narrowest; a reader who cannot tell the teal from the amber reads the
     figure exactly as well as before. Nothing here is the sole carrier of any
     fact — deliberately, and that is the bar a future figure has to clear
     before it spends hue.

     Four values per lane. Every ratio is measured against #1A1A1A, which is
     LIGHTER than any lane surface these actually land on, so each is a floor:

       hue   the lane name and the dot's ring        7.1 – 8.7 : 1
       ink   the 42 event labels, paler so a wall     10.9 – 11.2 : 1
             of them does not shout
       tint  the lane's own surface wash
       line  the hairline between two lanes

     .map__lab b — the dollar figure inside a label — stays --fig-text #E6E6E6
     at 16.7:1, brighter than every ink, so the money still reads first.

     NOT USED BY THE MACRO BANDS, which stay a neutral white wash. A band is a
     shock, not a lane, and giving it a hue would enroll it in a scale it is not
     part of.

     THE ROSE IS NOT CORAL, AND IT IS NOT AN ACCENT. #E08BB4 is a desaturated
     rose on a near-black figure ground; #F95954 is the brand accent and appears
     on this page exactly six times, none of them inside a figure. Said here
     because "there is pink in the map now" is the shape of a future session
     deciding the ration has moved. It has not. The ration is still SIX. */
  --map-l0:      #E2B04A;                    /* macro & regulatory      8.7:1 */
  --map-l0-ink:  #E3CDA3;                    /*                        11.2:1 */
  --map-l0-tint: rgba(226, 176, 74, 0.07);
  --map-l0-line: rgba(226, 176, 74, 0.32);

  --map-l1:      #7EA6F6;                    /* software & platform     7.2:1 */
  --map-l1-ink:  #BCCDF2;                    /*                        10.9:1 */
  --map-l1-tint: rgba(126, 166, 246, 0.08);
  --map-l1-line: rgba(126, 166, 246, 0.32);

  --map-l2:      #5FC2A6;                    /* hardware manufacturing  8.1:1 */
  --map-l2-ink:  #ABD9CA;                    /*                        11.2:1 */
  --map-l2-tint: rgba(95, 194, 166, 0.07);
  --map-l2-line: rgba(95, 194, 166, 0.32);

  --map-l3:      #E08BB4;                    /* retail & marketplace    7.1:1 */
  --map-l3-ink:  #EFC0D6;                    /*                        10.9:1 */
  --map-l3-tint: rgba(224, 139, 180, 0.07);
  --map-l3-line: rgba(224, 139, 180, 0.32);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; }

body {
  background: var(--navy);
  color: var(--white);
  font-family: 'Montserrat', system-ui, sans-serif;
  font-size: 16px;         /* never smaller: 16px stops iOS zooming on focus */
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }

a { color: var(--white); }
a:hover { color: var(--coral); }
a:focus-visible { outline: 2px solid var(--coral); outline-offset: 3px; }

h1, h2, h3, h4 { font-family: 'Roboto', system-ui, sans-serif; font-weight: 700; line-height: 1.15; }

.wrap { max-width: var(--measure); margin: 0 auto; padding: 0 24px; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* Small monospace labels — the "engineering document" voice */
.k {
  font-family: var(--mono);
  font-size: 0.86rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

/* ==========================================================================
   Skip link — these are long documents; a keyboard reader needs the escape
   ========================================================================== */

.skip {
  position: absolute; left: 24px; top: -60px; z-index: 30;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 10px 18px;
  font-size: 0.875rem; font-weight: 600; text-decoration: none;
  transition: top 0.15s ease;
}
.skip:focus { top: 12px; color: var(--white); }

/* ==========================================================================
   Sticky nav + scroll-spy rail
   ========================================================================== */

.nav {
  position: sticky; top: 0; z-index: 20;
  background: rgba(0, 11, 41, 0.92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.nav__inner {
  max-width: var(--measure); margin: 0 auto; padding: 0 24px;
  display: flex; align-items: center; gap: 26px; height: 58px;
}
.nav__brand {
  display: flex; align-items: center; gap: 10px;
  text-decoration: none; margin-right: auto;
}
.nav__brand:hover { color: var(--white); }
.nav__logo { width: 30px; height: 30px; border-radius: var(--radius); }
/* No white-space: nowrap. The wordmark is "Wolfpack Data & Strategy" on every page
   here, and at phone width it has to share the bar with the coral CTA — nowrap
   would push the nav into horizontal overflow rather than wrapping to a second
   line. Matches rates/ and ai-coaching/, which have carried the full string
   beside a CTA since they shipped. */
.nav__wordmark {
  font-family: 'Roboto', sans-serif; font-weight: 700;
  font-size: 0.8125rem; letter-spacing: 0.12em; text-transform: uppercase;
}
.nav__links { display: flex; gap: 20px; list-style: none; }
.nav__links a {
  font-family: var(--mono);
  font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase;
  text-decoration: none; color: var(--faint);
  padding: 4px 0; border-bottom: 1px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
  white-space: nowrap;
}
.nav__links a:hover { color: var(--white); }
/* Scroll-spy marks the current section in white with a hairline, not coral —
   the ration is spent, and a moving accent would be noise. */
.nav__links a[aria-current="true"] { color: var(--white); border-bottom-color: var(--faint); }
.nav__cta {
  font-size: 0.8125rem; font-weight: 700; text-decoration: none;
  background: var(--coral); color: var(--navy) !important;
  padding: 8px 16px; border-radius: var(--radius); white-space: nowrap;
}
.nav__cta:hover { filter: brightness(1.08); }

/* ==========================================================================
   Hero
   ========================================================================== */

.hero { border-bottom: 1px solid var(--line); }
.hero__inner { padding: 76px 0 60px; }

.hero__kicker { display: block; margin-bottom: 20px; }

.hero__title {
  font-size: clamp(2rem, 5vw, 3.4rem);
  letter-spacing: 0.005em;
  line-height: 1.06;
  max-width: 20ch;
  margin-bottom: 26px;
}

.hero__stand {
  font-size: clamp(1.0625rem, 1.8vw, 1.25rem);
  color: var(--muted);
  max-width: 58ch;
  padding-bottom: 26px;
  border-bottom: 3px solid var(--coral);   /* coral use 2 of 6 */
}

/* Stat tiles. On a practitioner-thesis page these carry BUILD AND COST facts,
   never impact metrics: there is no instrumented client outcome to report, and
   inventing one would be the single most damaging thing a case study could do. */
.stats {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px;
  list-style: none; margin-top: 34px;
}
.stats li {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 20px 22px;
}
.stats b {
  display: block; font-family: 'Roboto', sans-serif; font-weight: 700;
  font-size: 1.375rem; line-height: 1.2; margin-bottom: 8px;
  font-variant-numeric: tabular-nums;
}
.stats span {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint); line-height: 1.5;
  display: block;
}

/* ==========================================================================
   Sections + prose
   ========================================================================== */

.section { padding: 72px 0; scroll-margin-top: 70px; }
.section--line { border-top: 1px solid var(--line); }
.section__title { font-size: clamp(1.5rem, 3vw, 2.05rem); margin-bottom: 28px; }
/* A part number above a section title. The gap belongs to the pair, so it lives
   here rather than as an inline style repeated at every one of a dozen section
   heads — which is what the consolidation case study was doing until #195. */
.k + .section__title { margin-top: 14px; }
.section__sub {
  font-size: 1.125rem; margin: 44px 0 18px;
  font-family: 'Roboto', sans-serif; font-weight: 700;
}

/* The reading column. ~68ch is the measure the summary section on hire/ uses,
   and these pages are mostly prose, so it is the default rather than a special
   case. */
.prose { max-width: 68ch; }
.prose p { color: var(--muted); font-size: 1.0625rem; }
/* Vertical rhythm opened up 2026-08-15 (#182), Ry's ruling: the pages read too
   crowded at 18px paragraph gaps, and the standalone list components (.numlist,
   .readout, .rtable) sat against prose with little or no margin — he had begun
   hand-inserting <br> stopgaps, which is the signal the sheet owes the spacing.
   Spacing lives here, never as <br> in a page. Section rhythm remains
   deliberately unspecified in the site brief (X-4); these are element tunings. */
.prose p + p { margin-top: 26px; }
.prose strong { color: var(--white); font-weight: 600; }
.prose a { color: var(--muted); }
.prose a:hover { color: var(--coral); }   /* coral use 5 of 6 */

/* The thesis. ONE pull quote per page, presented as the conclusion the
   argument around it supports rather than as a claim standing alone. Two pull
   quotes is shouting; use .verdict for the second emphasis. */
.pull {
  max-width: 62ch; margin: 34px 0;
  padding: 4px 0 4px 26px;
  border-left: 3px solid var(--coral);     /* coral use 3 of 6 */
  font-family: 'Roboto', sans-serif; font-weight: 700;
  font-size: clamp(1.25rem, 2.4vw, 1.625rem);
  line-height: 1.3;
}

/* An attributed line, quieter than .pull. This is where a first-person verdict
   goes when the page already spent its one pull quote. */
.verdict {
  max-width: 66ch; margin-top: 26px; padding: 22px 26px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 1.0625rem; color: var(--muted);
}
.verdict cite {
  display: block; margin-top: 10px; font-style: normal;
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--faint);
}

/* ==========================================================================
   Figures
   ========================================================================== */

.figure { margin: 34px 0; }
.figure--wide { max-width: none; }

.shot {
  display: block; width: 100%; aspect-ratio: 16 / 9;
  object-fit: cover; object-position: 50% 0;
  background: var(--fig-bg);
  border: 1px solid var(--fig-line);
  border-radius: var(--radius);
}
.shot--43 { aspect-ratio: 4 / 3; }

.figure figcaption {
  margin-top: 12px;
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint); line-height: 1.6;
}

/* Designed placeholder. Sized at the exact final aspect ratio, so dropping the
   real file in changes nothing about the layout. Never a gray box, never a
   "coming soon". It is decorative until it holds a real asset, hence
   aria-hidden in the markup. */
.ph {
  display: grid; place-content: center; gap: 8px; text-align: center;
  width: 100%; aspect-ratio: 16 / 9; padding: 24px;
  background: var(--fig-bg);
  border: 1px dashed var(--fig-line);
  border-radius: var(--radius);
}
.ph--43 { aspect-ratio: 4 / 3; }
.ph__id {
  font-family: var(--mono); font-size: 1.125rem; letter-spacing: 0.16em;
  color: var(--fig-dim);
}
.ph__what {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: #5E5E5E; max-width: 44ch;
}
.ph__dim {
  font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.1em;
  color: #4A4A4A;
}

/* ==========================================================================
   .dtable — a small data table built in HTML rather than screenshotted.
   Inherited from the SetMaster page's ruling on its transition row: it is a
   table, so it is a table. Sharp at every density, readable by a screen
   reader, weightless, and it cannot go stale against an edit to the source
   spreadsheet the way a captured image does. Reach for this whenever the
   figure you want is four numbers, not a user interface.
   ========================================================================== */

.dtable {
  position: relative;                  /* see the trap documented on .rtable */
  background: var(--fig-bg);
  border: 1px solid var(--fig-line);
  border-radius: var(--radius);
  padding: 22px 24px;
  overflow-x: auto;                    /* wide content scrolls itself */
}
.dtable table {
  width: 100%; min-width: 520px;
  border-collapse: collapse;
  font-family: var(--mono); font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;  /* what makes a borderless grid read */
  color: var(--fig-text);
}
/* No gridlines and no cell borders: separation comes from striping, spacing,
   and column alignment, not from rules. */
.dtable th, .dtable td { border: 0; padding: 9px 12px; text-align: left; }

/* Density is the point, and a table stretched to 100% is not dense: the short
   numeric columns inflate and the row stops reading as one line. Shrink-
   wrapping every short column pushes the slack into the label column, which is
   where the long values actually are. */
.dtable .num { width: 1%; white-space: nowrap; text-align: right; }

.dtable .col {
  font-size: 0.625rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--fig-dim); font-weight: 400;
  border-bottom: 1px solid var(--fig-line);
}
.dtable .col.num { text-align: right; }
.dtable tbody tr:nth-child(odd) td { background: var(--fig-row); }

/* The one row that matters, marked with weight and a lift rather than a hue.
   See the NO NEW HUES note at the top of this file. */
.dtable tbody tr.is-lead td { background: var(--fig-lift); color: var(--white); font-weight: 700; }

.dtable .rank {
  display: inline-block;
  font-size: 0.5625rem; letter-spacing: 0.14em; text-transform: uppercase;
  font-weight: 400; color: var(--fig-dim);
  border: 1px solid var(--fig-line); border-radius: 3px;
  padding: 3px 7px; white-space: nowrap;
}
.dtable tbody tr.is-lead .rank { color: var(--white); border-color: #4A4E58; }

.dtable__note {
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--fig-line);
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.06em;
  color: var(--fig-dim); line-height: 1.6;
}

/* ==========================================================================
   Sidebars — run alongside a section, never inside the argument.
   Each drops without affecting the spine.
   ========================================================================== */

.aside {
  margin: 34px 0;
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--line);
  border-radius: var(--radius);
  padding: 26px 30px;
  max-width: 68ch;
}
.aside__label { display: block; margin-bottom: 12px; font-size: 0.6875rem; }
.aside__title { font-size: 1.0625rem; margin-bottom: 12px; }
.aside p { font-size: 0.9375rem; color: var(--muted); }
.aside p + p { margin-top: 12px; }

/* A short set of numbers, set as a readout rather than a headline. */
.readout {
  display: flex; flex-wrap: wrap; gap: 10px;
  list-style: none; margin: 26px 0 20px;
}
.readout li {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 3px; padding: 7px 12px;
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   Honesty list — the limits of the thing the page is arguing for.
   Chips are gray, deliberately. A red chip would read as an error state; these
   are boundaries, stated plainly.
   ========================================================================== */

.status { display: grid; gap: 14px; list-style: none; max-width: 74ch; }
.status li {
  display: grid; grid-template-columns: 120px 1fr; gap: 18px;
  align-items: baseline;
  padding: 16px 0; border-top: 1px solid var(--line);
}
/* Promoted out of .status on 2026-08-11 (#172) so the report tables could use
   the same object rather than a near-match. The declarations are unchanged;
   .status only re-states the two that are specific to sitting in a 120px
   grid column. */
.chip {
  display: inline-block;
  font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--faint);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 5px 10px; white-space: nowrap;
}
.status .chip { display: block; text-align: center; }
.status p { font-size: 0.9375rem; color: var(--muted); }

/* ==========================================================================
   Plain lists
   ========================================================================== */

/* Standalone lists carry their own margins, the same ruling .numlist and
   .readout took on 2026-08-15 (#182) and .plainlist was missed by — the
   consolidation case study had been patching the seam with an inline
   `style="margin-top:18px"` on every occurrence, which is the <br> stopgap in
   another costume. Spacing lives in the sheet (#195). */
.plainlist { display: grid; gap: 12px; list-style: none; max-width: 74ch; margin: 28px 0; }
.plainlist li {
  position: relative; padding-left: 22px;
  font-size: 0.9375rem; color: var(--muted);
}
.plainlist li::before { content: "▸"; position: absolute; left: 0; color: var(--faint); }

/* ==========================================================================
   Closing block
   ========================================================================== */

.close {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 40px 42px;
}
.close__title { font-size: clamp(1.4rem, 2.6vw, 1.9rem); margin-bottom: 14px; }
.close__lede { color: var(--muted); max-width: 62ch; margin-bottom: 26px; }
.close__ctas { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }

.btn {
  display: inline-block; text-decoration: none; font-weight: 700;
  font-size: 0.9375rem; line-height: 1.4;
  padding: 12px 30px; border-radius: var(--radius);
  white-space: nowrap;
}
.btn--solid { background: var(--coral); color: var(--navy); }   /* coral use 4 of 6 */
.btn--solid:hover { color: var(--navy); filter: brightness(1.08); }
.btn--ghost { border: 1px solid var(--line); color: var(--white); }
.btn--ghost:hover { border-color: var(--faint); color: var(--white); }

/* ==========================================================================
   Fine print
   ========================================================================== */

.tm {
  margin-top: 30px; padding-top: 22px; border-top: 1px solid var(--line);
  font-size: 0.8125rem; color: var(--faint); max-width: 78ch;
}

/* ==========================================================================
   Footer
   ========================================================================== */

.foot { border-top: 1px solid var(--line); padding: 36px 0 48px; }
.foot__inner {
  display: flex; flex-wrap: wrap; gap: 10px 30px;
  justify-content: space-between; align-items: baseline;
}
.foot__wordmark {
  font-family: 'Roboto', sans-serif; font-weight: 700; font-size: 0.8125rem;
  letter-spacing: 0.12em; text-transform: uppercase;
}
.foot__meta { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.08em; color: var(--faint); }
.foot__meta a { color: var(--muted); }

/* ==========================================================================
   Scroll reveal
   ========================================================================== */

/* Scoped to .js so the page renders COMPLETE with JavaScript off or broken.
   Same rule the hire/ and SetMaster pages keep, for the same reason: a
   document that needs JavaScript to be readable is a broken document. */
.js .reveal {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.5s cubic-bezier(.2,.6,.2,1),
              transform 0.5s cubic-bezier(.2,.6,.2,1);
  transition-delay: var(--d, 0ms);
}
.js .reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .reveal { opacity: 1; transform: none; transition: none; }
  .skip { transition: none; }
}

/* ==========================================================================
   LONG-FORM REPORT COMPONENTS
   Added 2026-08-11 (#172) for the "Consolidation Under Pressure" case study,
   which carries a ~6,000-word market-intelligence report inside the case-study
   wrapper: eleven numbered parts, seven data tables, four rendered figures, and
   forty-three cited transactions.

   They live here, in the shared sheet, rather than in that page — the folder
   rule, and the reason for it is visible in the source this page was rebuilt
   from. That source shipped its transaction map twice, once embedded and once
   standalone, as two copies of the same 200 lines. The copies had already
   drifted: the standalone plotted the 2025 tariff event and the embedded one
   did not, so the same figure told two different stories depending on which tab
   you opened it in. Nothing could see it. Everything below is written to be
   inherited by the next report-shaped case study rather than re-derived, and
   the map is now ONE script reading ONE dataset.

   WHAT WAS DELIBERATELY NOT INHERITED from that source, since a derived design
   should say what it refused:
     * Its amber accent, on all fourteen of its uses. Coral is at six and spent.
     * Its green/red direction pair and its cyan/red macro bands. Direction is a
       glyph here (.trend) and the bands are a neutral wash. A semantic color
       pair is atomic and neither half earned its way in alone.
     * Its serif body face. This site is Montserrat and already carries it.
     * Its sticky left margin rail of part numbers. The nav above already does
       wayfinding on every page in this folder, and a second, parallel
       navigation device is one more thing to keep in sync for no reader
       benefit. The part numbers survive as .k kickers, in the flow.
   ========================================================================== */

/* The currency parenthetical. Every monetary figure in this document appears in
   its reported currency with the counter-currency beside it, and that is an
   editorial standard rather than a decoration — so it gets a named treatment
   and reads as one object with the figure it follows, not as a second figure. */
.fx {
  font-family: var(--mono); font-size: 0.86em;
  color: var(--faint); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* An ordered list where the number is a marker rather than punctuation. */
.numlist {
  display: grid; gap: 18px; list-style: none;
  margin: 28px 0;
  max-width: 74ch; counter-reset: numlist;
}
.numlist > li {
  position: relative; padding-left: 34px; counter-increment: numlist;
  font-size: 0.9375rem; color: var(--muted);
}
.numlist > li::before {
  content: counter(numlist, decimal-leading-zero);
  position: absolute; left: 0; top: 0.15em;
  font-family: var(--mono); font-size: 0.75rem; font-weight: 600;
  color: var(--faint); font-variant-numeric: tabular-nums;
}
.numlist strong, .plainlist strong { color: var(--white); font-weight: 600; }

/* Callouts. The sheet already had .pull — ONE per page, coral rule, the thesis —
   and .verdict, the attributed panel. This document needed a third register: a
   line lifted out of the argument that is neither the page's single thesis nor
   anybody's quote. So .callout is quieter than .pull by every means except
   size: navy rule instead of coral, body face instead of Roboto.
   .callout--big is the same object at the size .pull would be, for the two
   lines in the report that carry a section on their own.
   The ordering rule: a page gets one .pull. It may get several callouts. If a
   callout is tempting to promote to .pull, promote it and demote the old one. */
.callout {
  max-width: 66ch; margin: 30px 0;
  padding: 2px 0 2px 22px;
  border-left: 2px solid var(--faint);
  font-size: 1.0625rem; line-height: 1.5; color: var(--white);
}
.callout--big {
  font-size: clamp(1.1875rem, 2.2vw, 1.4375rem);
  line-height: 1.35; font-weight: 600; letter-spacing: -0.006em;
}

/* The second note variant. The neutral one is .aside. This one is for a note
   that qualifies or limits the argument around it rather than extending it —
   "the honest limit of this argument", "the counter-force, and it is winning".
   The difference is a brighter left rule and, in the markup, a different label
   in .aside__label. A red rule would read as an error state; these are
   caveats, stated plainly, which is the same reasoning .status's gray chips
   already carry. */
.aside--warn { border-left-color: var(--muted); }

/* A row of equal cards. Built for the report's three eras, kept generic. */
.cards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 18px; list-style: none; margin: 30px 0;
}
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 22px 24px;
  display: flex; flex-direction: column; gap: 10px;
}
.card__k { display: block; }
.card h3 { font-size: 1.0625rem; }
.card p { font-size: 0.9375rem; color: var(--muted); }

/* ==========================================================================
   .iconchips — icon chips for a page that documents an ICON SYSTEM.
   Added 2026-08-13 (#174) for the Wolfpack AI Command case study, whose
   workspace-layer passage describes one icon per database with meaning pushed
   onto a governed color legend. That passage needs something to look at, and
   the alternative — a screenshot of a real workspace — would carry client and
   project names into a public page.

   NO NEW HUES still holds for this sheet, and the once-open color question is
   SETTLED: Ry ruled (2026-08-13) that the four semantic icon colors appear as
   figure CONTENT — the real Notion database icons, committed as SVG files in
   ../img/ (notion- prefix). The hues live in those files, never here; this
   sheet still carries nothing beyond the navy system and the neutral figure
   ground. The slot's border went dashed → solid with that ruling, because
   dashed was the placeholder signifier and the slots are no longer empty.

   Generic on purpose: the next case study that documents a visual system
   inherits it rather than re-deriving it inline.

   The grid is a fixed 2×2, not auto-fit (Ry, 2026-08-13, #180): auto-fit broke
   the four chips into a ragged 3 + 1 at desktop width, orphaning the last one.
   Two fixed columns pair the chips at every width above the 640px breakpoint,
   where they collapse to one column like .stats.
   ========================================================================== */

.iconchips {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px; list-style: none; margin: 28px 0; max-width: 74ch;
}
.iconchip {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 18px 20px;
  display: flex; flex-direction: column; gap: 10px;
}
.iconchip__slot {
  width: 2.25rem; height: 2.25rem; flex: none;
  background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--radius);
  display: grid; place-items: center;
}
.iconchip__slot img { width: 1.25rem; height: 1.25rem; display: block; }
.iconchip b { font-family: 'Roboto', sans-serif; font-weight: 700; font-size: 0.9375rem; }
.iconchip p { font-size: 0.875rem; line-height: 1.5; color: var(--muted); }

/* .stats extensions. The tiles already carried a value and a label; a report
   reading also carries the converted figure and a direction. Markup order sets
   visual order, so nothing here overrides .stats itself. */
.stats--auto { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); }
.stats .stat__k { margin-bottom: 10px; }
.stats .stat__x { color: var(--faint); font-variant-numeric: tabular-nums; margin-bottom: 8px; }
.stats .trend  { color: var(--muted); letter-spacing: 0.06em; }

/* Direction, carried by the glyph and by weight — never by a green and a red.
   ▲ ▼ ▬ survive a grayscale print, a color-vision difference, and this sheet's
   ban on new hues, all at once, and the arrow was doing the work in every
   design that also colored it. .trend--flat is the one that needs help: it is
   the report's own subject (gear, flat and taxed), so it takes the emphasis
   the other two do not. */
.trend--flat { color: var(--white); font-weight: 600; }

/* ==========================================================================
   .rtable — the long-form report table.
   This is NOT .dtable. .dtable is a FIGURE: a small borderless readout that
   stands where a screenshot would stand, which is why it sits on the near-black
   figure ground. These tables are the document's body — forty-three
   transactions with prose in two of their five columns — and a page whose body
   is mostly near-black slabs stops looking like this site. So .rtable is built
   from the navy system and .dtable keeps the figure ground. The split to
   remember: a figure gets the figure ground, a table the reader reads gets the
   page.
   ========================================================================== */

.rtable {
  position: relative;                  /* load-bearing — see below */
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow-x: auto;                    /* wide content scrolls itself */
  max-width: 100%; min-width: 0;
  margin: 28px 0 34px;
}

/* THE THIRD TRAP, found by measurement on this build and worth the paragraph.
   `overflow-x: auto` clips a wide table, but it does NOT clip an absolutely
   positioned descendant whose containing block is somewhere else — and with no
   positioned ancestor anywhere up the chain, "somewhere else" is the initial
   containing block, i.e. the page. `.visually-hidden` is `position: absolute`,
   and this table puts one in every unsourced row's Src cell (the sentence a
   screen reader gets in place of the dash). Those spans took their static
   position ~640px into the table, escaped every scroller and every `overflow`
   above them, and gave the whole document 269px of horizontal scroll at 390px
   wide — while `.rtable` itself measured a correct 327px and every ancestor
   measured a correct 375px. Nothing looked wrong anywhere except the page.
   `position: relative` here makes .rtable the containing block, so the
   overflow rule that was already in place actually applies to them.
   The tell, if this ever happens again: putting `overflow: hidden` on an
   ancestor does not fix it. That means the box is not in that ancestor's
   clipping chain at all. */
/* CELL PADDING AND COLUMN RULES, retuned 2026-08-15 (#195), Ry's ruling: these
   tables were "difficult to read and not recognizable as tables". Two separate
   faults, and both had the same cause — a component borrowed from .dtable's
   habits without noticing that .dtable is a different object.

   1. AIR. 11px of vertical padding is right for .dtable, which is four numbers
      in a frame. It is wrong for a forty-three row register whose cells hold
      sentences: at 1.5 line-height a two-line cell put its own lines further
      apart than it sat from the row above, so the eye stopped grouping by row.
      Rows now get more padding than the text inside them has leading.
   2. STRUCTURE. .dtable earns its no-gridlines rule honestly — short numeric
      columns, hard right alignment, and a caption saying it is a figure. This
      table has prose in two of five columns, so alignment alone gives the eye
      no column edge to track along, and it read as indented paragraphs. It now
      carries a hairline between columns, in --line, the same weight as the row
      rules it already had.

   This is still NO NEW HUES and no new weight: the rule is the hairline the
   sheet already uses everywhere. .dtable is deliberately NOT changed — it is a
   figure and its borderless density is the point. */
.rtable table {
  border-collapse: collapse; width: 100%; min-width: 44rem;
  font-size: 0.875rem; line-height: 1.5;
}
.rtable th {
  font-family: var(--mono); font-size: 0.6875rem; font-weight: 400;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint);
  text-align: left; vertical-align: bottom; white-space: nowrap;
  padding: 16px 20px; background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}
.rtable td {
  padding: 18px 20px; vertical-align: top; color: var(--muted);
  border-bottom: 1px solid var(--line);
}
/* The column edge. `+` rather than a border on every cell, so the table has no
   outer rule fighting the panel border that is already around it. */
.rtable th + th, .rtable td + td { border-left: 1px solid var(--line); }
.rtable tbody tr:last-child td { border-bottom: 0; }
.rtable strong { color: var(--white); font-weight: 600; }
.rtable a { color: var(--muted); }

.rtable .num {
  text-align: right; font-family: var(--mono);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.rtable th.num { text-align: right; }
.rtable .num i { font-style: normal; display: block; font-size: 0.9em; color: var(--faint); }
.rtable .yr {
  font-family: var(--mono); font-size: 0.78rem;
  color: var(--faint); white-space: nowrap;
}

/* The rows the argument rests on, marked with weight and a lift rather than a
   hue — the same ruling .dtable's .is-lead carries, and the same class name so
   there is one idea here and not two. */
.rtable tbody tr.is-lead td { background: var(--surface-2); color: var(--white); }
.rtable tbody tr.is-lead td.num i { color: var(--muted); }

/* ── The Src gutter ───────────────────────────────────────────────────────
   One glyph per row, pointing at the press release or filing the row is drawn
   from. Thirty-one of forty-three rows have one; the other twelve show a dash
   meaning no primary source was verified, and that row rests on secondary
   coverage. THAT DISTINCTION IS THE COLUMN'S ENTIRE PURPOSE — a reader
   deciding how much weight to put on a figure is reading this column, so it is
   never merged into another, never hidden at a breakpoint, and never dropped
   because it is narrow.
   The visible label is an icon, so every link carries a title AND an aria-label
   naming the publisher, and every dash carries a visually-hidden sentence. An
   arrow nobody can name is a link a screen reader announces as "link". */
.rtable th.src, .rtable td.src {
  width: 1%; white-space: nowrap; text-align: center;
  padding-left: 12px; padding-right: 16px;
}
.srclink {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.65rem; height: 1.65rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--muted); text-decoration: none;
  font-size: 0.8rem; line-height: 1;
}
/* The hover is the sheet's one link hover, coral use 5 of 6 — it is inherited
   from the global `a:hover`, not re-declared, so this adds no accent use. */
.srclink:hover { border-color: var(--faint); background: var(--surface-2); }
.nosrc { font-family: var(--mono); color: var(--faint); }

/* A note that belongs to the table above it rather than to the prose after it —
   the counterpart of .dtable__note, which .rtable had no equivalent of until a
   table needed to say what its own numbers are NOT (#195).
   It is a SIBLING of .rtable, not a child, and that is deliberate: .rtable is
   the horizontal scroller, so a note inside it would slide out of view exactly
   when a reader scrolled to the column the note was explaining. Pairing it with
   .rtable--noted, which drops the table panel's bottom corners, makes the two
   read as one object while leaving the note pinned to the page. */
.rtable--noted { margin-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.rtable__note {
  margin: 0 0 34px;
  background: var(--surface);
  border: 1px solid var(--line); border-top: 0;
  border-radius: 0 0 var(--radius) var(--radius);
  padding: 16px 20px;
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.04em;
  line-height: 1.7; color: var(--faint);
}
.rtable__note strong { color: var(--muted); font-weight: 600; }

/* ==========================================================================
   .figframe — the panel a rendered figure sits in.
   Sibling of .dtable, same figure ground, for charts drawn in HTML rather than
   captured: the revenue meters, the entry-to-exit dumbbells, the Reverb
   ownership diagram, and the transaction map.
   ========================================================================== */

.figframe {
  background: var(--fig-bg);
  border: 1px solid var(--fig-line);
  border-radius: var(--radius);
  padding: 24px 26px;
  min-width: 0;                        /* see the note on .rtable's ancestors */
  display: flex; flex-direction: column; gap: 22px;
}
.figframe__head {
  display: flex; flex-wrap: wrap; gap: 8px 24px;
  align-items: baseline; justify-content: space-between;
}
.figframe__t {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--fig-dim);
}
.figframe__sub {
  font-size: 0.875rem; line-height: 1.5; color: var(--fig-dim);
  margin-top: 6px; max-width: 62ch;
}
.figframe__key {
  display: flex; flex-wrap: wrap; gap: 8px 22px;
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--fig-dim);
  border-top: 1px solid var(--fig-line); padding-top: 14px;
}
.figframe__key i {
  display: inline-block; width: 0.66rem; height: 0.66rem; border-radius: 50%;
  border: 1.5px solid var(--fig-dim); margin-right: 8px; vertical-align: -1px;
}
/* The affordance to the full-width map. Navy-ghost, never coral: it is a
   secondary route to a figure this page already shows, not a call to action. */
.openfull {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--fig-text);
  text-decoration: none; white-space: nowrap;
  border: 1px solid var(--fig-line); border-radius: var(--radius);
  padding: 8px 14px;
}
.openfull:hover { border-color: var(--fig-dim); color: var(--fig-text); }

/* ── Meters ─────────────────────────────────────────────────────────────── */
.meters { display: flex; flex-direction: column; gap: 16px; }
.meter { display: grid; grid-template-columns: minmax(8rem, 11rem) 1fr; gap: 6px 18px; align-items: center; }
.meter__l { font-size: 0.8125rem; font-weight: 600; line-height: 1.3; color: var(--fig-text); }
.meter__l span {
  display: block; margin-top: 3px;
  font-family: var(--mono); font-size: 0.625rem; font-weight: 400;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--fig-dim);
}
.meter__track {
  position: relative; height: 1.75rem; display: flex; align-items: center;
  background: var(--fig-row); border-radius: 2px;
}
/* One fill for all three bars. They are the same variable measured on three
   populations, so coloring them differently would encode a category that is
   not in the data. */
.meter__fill { height: 100%; background: var(--fig-text); border-radius: 2px; min-width: 2px; }
.meter__n {
  font-family: var(--mono); font-size: 0.8125rem; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--fig-text);
  padding-left: 10px; white-space: nowrap;
}
.meter__n i { font-style: normal; font-weight: 400; color: var(--fig-dim); }
/* The scale has to start where the bars start, and the bars start after a
   fluid `minmax(8rem, 11rem)` label column. So the scale repeats the meter's
   own grid and leaves column one empty, rather than guessing the offset with a
   margin — a margin would be right at exactly one container width. */
.meter__scale {
  display: grid; grid-template-columns: minmax(8rem, 11rem) 1fr; gap: 0 18px;
  border-top: 1px solid var(--fig-line); padding-top: 8px;
}
.meter__scale::before { content: ""; }
.meter__ticks {
  display: flex; justify-content: space-between;
  font-family: var(--mono); font-size: 0.625rem; color: var(--fig-dim);
  font-variant-numeric: tabular-nums;
}

/* ── Dumbbells: entry price → exit price ─────────────────────────────────
   Three marks, distinguished by SHAPE rather than by hue, because the sheet has
   no hue to spend and because shape survives a grayscale print:
     ring          entry price
     filled disc   exit price
     dashed ring   on the market, unsold
   Every mark is also labeled with its own figure at its own position, so the
   shapes are a convenience for scanning and never the only carrier. */
.dumbs { display: flex; flex-direction: column; gap: 26px; }
.dumb { display: flex; flex-direction: column; gap: 8px; }
.dumb__h { display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 12px; }
.dumb__h b { font-family: 'Roboto', sans-serif; font-size: 0.9375rem; font-weight: 700; color: var(--fig-text); }
.dumb__h span {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--fig-dim);
}
.dumb__track { position: relative; height: 2.75rem; }
.dumb__line { position: absolute; top: 50%; height: 2px; background: var(--fig-line); transform: translateY(-50%); }
.dumb__line--open { background: none; border-top: 2px dashed var(--fig-line); height: 0; }
.dot {
  position: absolute; top: 50%; transform: translate(-50%, -50%);
  width: 0.8rem; height: 0.8rem; border-radius: 50%; box-sizing: border-box;
}
.dot--in   { border: 2px solid var(--fig-text); background: var(--fig-bg); }
.dot--out  { background: var(--fig-text); }
.dot--open { border: 2px dashed var(--fig-dim); background: var(--fig-bg); }
.dlab {
  position: absolute; white-space: nowrap;
  font-family: var(--mono); font-size: 0.75rem; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.dlab i { font-style: normal; font-weight: 400; color: var(--fig-dim); }
.dlab--in  { top: 0; transform: translateX(-50%); color: var(--fig-dim); }
.dlab--out { bottom: 0; transform: translateX(-50%); color: var(--fig-text); }

/* ── The ownership diagram ──────────────────────────────────────────────── */
.story { display: flex; flex-direction: column; }
.story__row { display: grid; grid-template-columns: 3.4rem minmax(0, 1fr); gap: 16px; align-items: stretch; }
.story__yr {
  font-family: var(--mono); font-size: 0.9375rem; font-weight: 600;
  color: var(--fig-dim); font-variant-numeric: tabular-nums; padding-top: 20px;
}
.story__body { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; padding: 14px 0; }
.node {
  display: flex; flex-direction: column; gap: 3px; min-width: 8rem;
  padding: 10px 13px; border: 1px solid var(--fig-line);
  border-radius: var(--radius); background: var(--fig-row);
}
.node b { font-family: 'Roboto', sans-serif; font-size: 0.9375rem; font-weight: 700; color: var(--fig-text); }
.node span {
  font-family: var(--mono); font-size: 0.5625rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--fig-dim);
}
/* The acquired company, marked with a brighter hairline. Not an accent. */
.node--tgt { border-color: var(--fig-dim); }
.node--grp { border-style: dashed; }
.arrow { display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 6rem; flex: 1; }
.arrow em {
  font-family: var(--mono); font-style: normal; font-size: 0.8125rem;
  font-weight: 700; font-variant-numeric: tabular-nums; color: var(--fig-text);
}
.arrow i { font-family: var(--mono); font-style: normal; font-size: 0.625rem; color: var(--fig-dim); }
.arrow__shaft { width: 100%; height: 0; border-top: 2px solid var(--fig-line); position: relative; }
.arrow__shaft::after {
  content: ""; position: absolute; right: -1px; top: -4px;
  border-left: 7px solid var(--fig-line);
  border-top: 4px solid transparent; border-bottom: 4px solid transparent;
}
.story__mid { display: grid; grid-template-columns: 3.4rem minmax(0, 1fr); gap: 16px; }
.story__owns {
  padding: 0 0 4px 14px; border-left: 2px solid var(--fig-line);
  font-family: var(--mono); font-size: 0.75rem; line-height: 1.9; color: var(--fig-dim);
}
.story__owns b { font-family: 'Roboto', sans-serif; font-weight: 700; font-size: 0.875rem; color: var(--fig-text); }
.story__out {
  display: flex; flex-wrap: wrap; gap: 14px 34px; align-items: baseline;
  border-top: 1px solid var(--fig-line); margin-top: 8px; padding-top: 18px;
}
.story__out div { display: flex; flex-direction: column; gap: 2px; }
.story__out b {
  font-family: 'Roboto', sans-serif; font-size: 1.5rem; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--fig-text);
}
.story__out span {
  font-family: var(--mono); font-size: 0.5625rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--fig-dim);
}

/* ==========================================================================
   .map — the transaction map.
   Time on x, four industry lanes on y, one dot per event, label packed into the
   lowest free track in its lane. Everything positioned here is written by
   case-study-assets/js/map.js at render time; nothing is hand-placed, which is
   what lets the same figure survive a resize, a font change and a repalette.

   THE 1780px TRAP, worth keeping written down. The plot is wider than any
   viewport by design and lives in .map__scroll. A `grid-template-columns: 1fr`
   track has an implicit `min-width: auto`, so it will happily grow to 1780px
   and take the whole page's text columns with it, squeezing them into a strip.
   Every ancestor between a grid or flex track and .map__scroll needs
   `min-width: 0` — which is why .figframe and .rtable both declare it.
   ========================================================================== */

/* Full-bleed band, for content whose whole purpose is to be wider than the
   1100px reading measure. Exactly one thing uses it — the standalone
   transaction map — and a case study that reaches for it for a decorative
   reason is reaching for the wrong thing. */
.bleed { padding: 0 24px; }

.map__scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; min-width: 0; }
.map { position: relative; padding-top: 6px; }

/* ── Lane identity ────────────────────────────────────────────────────────
   One rule per lane, and it sets custom properties rather than colors. map.js
   stamps data-lane on BOTH the lane surface and every event that belongs to it
   — two elements that are siblings in the DOM, not parent and child, because
   every position here is absolute — so a shared attribute is what keeps their
   colors from being set in two places and drifting.
   The map is the only figure that carries these; see the exception in the
   header before adding a second one. */
[data-lane="0"] { --lane: var(--map-l0); --lane-ink: var(--map-l0-ink); --lane-tint: var(--map-l0-tint); --lane-line: var(--map-l0-line); }
[data-lane="1"] { --lane: var(--map-l1); --lane-ink: var(--map-l1-ink); --lane-tint: var(--map-l1-tint); --lane-line: var(--map-l1-line); }
[data-lane="2"] { --lane: var(--map-l2); --lane-ink: var(--map-l2-ink); --lane-tint: var(--map-l2-tint); --lane-line: var(--map-l2-line); }
[data-lane="3"] { --lane: var(--map-l3); --lane-ink: var(--map-l3-ink); --lane-tint: var(--map-l3-tint); --lane-line: var(--map-l3-line); }

/* ── Layering, and why it is explicit ─────────────────────────────────────
   Everything in this figure is absolutely positioned inside one stacking
   context, so before these z-indexes paint order was document order — bands,
   then gridlines, then lanes on top of both. That was survivable only while
   half the lanes had no background: a band or a gridline crossing an opaque
   lane surface was hidden, so a band that spans all four lanes was drawn as
   stripes. Now that every lane carries a tint, the layers are named:
     0  lane surfaces      2  events
     1  bands, gridlines   3  lane names, year labels */
.map__lane  { position: relative; z-index: 0; background: var(--lane-tint); border-top: 1px solid var(--lane-line); }
.map__band  { position: absolute; z-index: 1; border-radius: 2px; pointer-events: none; background: rgba(255, 255, 255, 0.08); }
.map__grid  { position: absolute; z-index: 1; width: 1px; background: rgba(255, 255, 255, 0.06); }
.map__ev    { position: absolute; z-index: 2; display: flex; align-items: center; gap: 7px; height: 1.35rem; white-space: nowrap; }

/* 0.08, not lower. The band has to stay legible over four different lane
   tints, and it is the same weight range as the tints themselves — below about
   this the band stops existing on the lighter lanes. */
.map__band span {
  position: absolute; top: -1.3rem; left: 4px;
  font-family: var(--mono); font-size: 0.625rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--fig-dim);
  white-space: nowrap;
}
/* The bands are macro shocks, not transactions, and must not read as marks. A
   flat NEUTRAL wash across the lanes with its label in a reserved strip above
   them does that with no shape and no hue in common with a dot — and neutrality
   is doing more work now that the lanes are colored, because a fifth hue here
   would read as a fifth category. */
.map__band::before {
  content: ""; position: absolute; inset: 0;
  border-left: 1px dashed rgba(255, 255, 255, 0.22);
  border-right: 1px dashed rgba(255, 255, 255, 0.22);
}
/* The time axis is shared by every lane, so it stays neutral. It runs along the
   TOP as well as the bottom (#199): the plot is 1600px wide at its narrowest
   and taller than a phone, so dating an event in the first lane meant tracking
   to the far edge of the figure and then all the way down. */
.map__yr {
  position: absolute; z-index: 3; transform: translateX(-50%);
  font-family: var(--mono); font-size: 0.6875rem; color: var(--fig-dim);
  font-variant-numeric: tabular-nums;
}
.map__yr--top { top: 5px; }
.map__yr--btm { bottom: 6px; }

.map__lname {
  position: absolute; left: 10px; top: 7px; z-index: 3;
  font-family: var(--mono); font-size: 0.625rem; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--lane);
}
.map__ev--left { flex-direction: row-reverse; }
/* Uniform size. Size carries no meaning here — the readability of forty-two
   labels does, and a varying radius costs label room to say nothing. The ring
   carries the LANE hue and the fill carries the VALUE step, which is the whole
   two-variable scheme on one mark; it is also what keeps the palest fill
   visible against the lane tint, and what makes an unfilled dot read as "no
   value" rather than as "invisible". */
.map__dot {
  width: 0.66rem; height: 0.66rem; border-radius: 50%;
  border: 1.5px solid var(--lane, var(--fig-dim)); flex: none; box-sizing: border-box;
}
.map__lab { font-family: var(--mono); font-size: 0.6875rem; color: var(--lane-ink, var(--fig-dim)); }
.map__lab b { font-weight: 600; color: var(--fig-text); }
.map__note {
  font-family: var(--mono); font-size: 0.6875rem; line-height: 1.75;
  color: var(--fig-dim); max-width: 80ch;
}
/* What a reader with JavaScript off sees in place of the plot. The document
   loses nothing: every transaction the map places is also a row in the tables
   below it, with its own source link.
   Hidden by a class map.js sets on the SCROLLER once it has actually drawn —
   deliberately not by reveal.js's `.js` class, which is never added when
   prefers-reduced-motion is set. Keyed to the `.js` class this fallback would
   sit under a fully-drawn map for every reader who has asked for less motion. */
.map__fallback { padding: 26px 0; font-size: 0.9375rem; color: var(--fig-dim); max-width: 66ch; }
.map__scroll.is-drawn .map__fallback { display: none; }

/* ==========================================================================
   Source list
   ========================================================================== */

.srclist { display: grid; gap: 10px; max-width: 88ch; }
.srclist h3 {
  font-family: var(--mono); font-size: 0.6875rem; font-weight: 400;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--faint);
  margin-top: 26px; padding-bottom: 8px; border-bottom: 1px solid var(--line);
}
.srclist h3:first-child { margin-top: 0; }
.srclist p { font-size: 0.875rem; line-height: 1.5; color: var(--muted); }
.srclist cite { font-style: normal; }
.srclist a { color: var(--muted); font-family: var(--mono); font-size: 0.8125rem; }

/* The document's own masthead facts — period, focus, issue date, basis, FX. */
.docmeta {
  display: flex; flex-wrap: wrap; gap: 10px 34px;
  list-style: none; margin-top: 26px; padding-top: 20px;
  border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint);
}
.docmeta b { color: var(--muted); font-weight: 600; }

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 1040px) {
  .nav__links { display: none; }
  .stats { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 640px) {
  .hero__inner { padding: 54px 0 46px; }
  .section { padding: 54px 0; }
  .stats { grid-template-columns: 1fr; }
  .iconchips { grid-template-columns: 1fr; }
  .close { padding: 30px 24px; }
  .aside { padding: 22px 20px; }
  .dtable { padding: 18px 16px; }
  .status li { grid-template-columns: 1fr; gap: 8px; }
  .status .chip { justify-self: start; }
  .close__ctas .btn { width: 100%; text-align: center; }
  .btn { white-space: normal; padding: 12px 20px; }
  .pull { padding-left: 18px; }

  /* Report components */
  .figframe { padding: 18px 16px; }
  .callout { padding-left: 16px; }
  .meter { grid-template-columns: 1fr; gap: 4px; }
  .meter__scale { grid-template-columns: 1fr; }
  .meter__scale::before { display: none; }
  .story__row, .story__mid { grid-template-columns: 1fr; gap: 0; }
  .story__yr { padding-top: 14px; }
  .arrow { min-width: 4.5rem; }
  .rtable { margin: 16px 0 26px; }
}
