/* ── assets/css/tokens.css ── */
/* ============================================================
   tokens.css — Design tokens (single source of truth)
   Aditya Kumar portfolio. No build step: plain CSS custom properties.
   Change a value here and it updates everywhere. Do not hard-code
   colors, fonts, or motion in components — reference these vars.

   Brand guardrails (see personality.md): calm, controlled distinctiveness,
   AI-visual intensity low. BANNED: purple, neon, cyberpunk, clutter.
   Corners are intentionally sharp (radius 0) except pills.
   ============================================================ */

:root {
  /* ---- Color: light theme (default) ---- */
  --bg:   #FAFAF8;            /* page background (warm off-white) */
  --ink:  #141412;           /* primary text / strong surfaces   */
  --mut:  #6E6E66;           /* muted / secondary text           */
  --ln:   rgba(20,20,18,.14);/* hairline borders & grid dots     */
  --card: #FFFFFE;           /* card surface                     */
  --soft: #F1F0EB;           /* soft fill (chips, wells)         */
  --acc:  #D9482B;           /* accent (rust-red)                */
  /* Approved accent alternatives (swap --acc to retheme):
     rust  #D9482B  ·  green #1F6E5C  ·  blue #2456D6           */
  --acc-ink: #FFFFFF;        /* text on accent background        */
  --acc-text: #C13D22;       /* accent for SMALL text (≤16px) — same hue,
                                darkened for legibility; dots/tags/borders/
                                big numerals keep --acc                  */

  /* ---- Typography ---- */
  --font-sans: 'Space Grotesk', system-ui, -apple-system, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', monospace;

  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;

  /* Type scale (px). Display sizes use clamp() in components. */
  --fs-eyebrow: 11px;   /* mono labels, letter-spaced */
  --fs-xs: 12px;
  --fs-sm: 13.5px;
  --fs-base: 16px;
  --fs-lg: 20px;
  --fs-h2: clamp(30px, 4.5vw, 46px);
  --fs-hero: clamp(54px, 9.5vw, 124px);
  --fs-contact: clamp(40px, 7vw, 84px);

  --lh-tight: 1.05;
  --lh-snug: 1.45;
  --lh-body: 1.6;
  --tracking-label: .18em;   /* uppercase mono eyebrows */
  --tracking-tight: -.02em;  /* large headings          */

  /* ---- Spacing scale (px) ---- */
  --sp-3xs: 4px;
  --sp-2xs: 8px;
  --sp-xs: 12px;
  --sp-sm: 16px;
  --sp-md: 24px;
  --sp-lg: 32px;
  --sp-xl: 48px;
  --sp-2xl: 68px;   /* section padding (mobile)  */
  --sp-3xl: 96px;   /* section padding (desktop) */

  /* ---- Layout ---- */
  --container: 1180px;
  --gutter: 24px;

  /* ---- Borders / radii / shadow ---- */
  --border: 1px solid var(--ln);
  --border-ink: 1px solid var(--ink);
  --radius-pill: 99px;          /* pills only; everything else is square */
  --shadow-block: 6px 6px 0 var(--ln);  /* offset "print" shadow (hero motif) */

  /* ---- Motion ---- */
  --dur: .6s;        /* reveal-on-scroll */
  --dur-fast: .2s;   /* hovers */
  --ease: ease;

  /* ---- Layers ---- */
  --z-nav: 50;
  --z-cursor-img: 200;   /* floating hover preview */
  --z-modal: 300;        /* project-preview overlay (above nav + cursor-img) */
}

/* ---- Color: dark theme ----
   Applied by adding class "dark" to <html> (the :root element). A tiny
   pre-paint script in each page's <head> sets it flash-free; main.js
   toggles it and persists the choice in localStorage. */
:root.dark {
  --bg:   #121210;
  --ink:  #F2F1EC;
  --mut:  #98978E;
  --ln:   rgba(242,241,236,.16);
  --card: #1A1A17;
  --soft: #1E1E1B;
  /* --acc stays the same across themes */
  --acc-text: #E9704F;   /* small-text accent lightens on dark ground */
}

/* ---- Root surface (the app wrapper) ---- */
.site {
  background: var(--bg);
  color: var(--ink);
  /* Clip horizontal overflow WITHOUT creating a scroll container.
     `overflow-x: hidden` would force overflow-y to compute to `auto`, which
     turns .site into a scroll container and breaks the nav's position:sticky.
     `overflow-x: clip` clips the same way but keeps sticky working. */
  overflow-x: clip;
}

/* ── assets/css/base.css ── */
/* ============================================================
   base.css — Reset + element defaults
   Foundation layer. Loaded after tokens.css, before components.css.
   Intentionally minimal: we do NOT reset heading/paragraph margins,
   because components set spacing explicitly (and use the .m0 utility).
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

img, svg, video { max-width: 100%; }

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

::selection {
  background: var(--acc);
  color: var(--acc-ink);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* ── assets/components/layout/layout.css ── */
/* layout — page scaffolding: container, section, section-head, heading.
   Edit here to change section rhythm site-wide. */
.wrap { max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter); box-sizing: border-box; }
.sec { padding: var(--sp-3xl) 0; border-top: var(--border); }
.sechead { margin-bottom: 44px; }
.h2 { font-size: var(--fs-h2); font-weight: var(--fw-semibold); letter-spacing: var(--tracking-tight); line-height: var(--lh-tight); margin: 16px 0 0; }

@media (max-width: 700px) {
  .sec { padding: var(--sp-2xl) 0; }
}

/* ── assets/components/eyebrow/eyebrow.css ── */
/* eyebrow — mono uppercase section label with an accent tick.
   Usage: <p class="eyebrow"><span class="tick"></span>01 / Section</p> */
.eyebrow { font-family: var(--font-mono); font-size: var(--fs-eyebrow); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--mut); display: flex; align-items: center; gap: 10px; }
.tick { width: 8px; height: 8px; background: var(--acc); flex: none; }

/* ── assets/components/button/button.css ── */
/* button — actions & links: primary (.btn), secondary (.btn2),
   quiet link (.rlink), ghost toggle (.tbtn). Theme toggle lives in
   theme-toggle/. Burger lives in nav/. */
.btn { display: inline-flex; align-items: center; gap: 8px; font-size: var(--fs-sm); font-weight: var(--fw-semibold); text-decoration: none; background: var(--ink); color: var(--bg); padding: 10px 18px; border: var(--border-ink); }
.btn:hover { background: var(--acc); border-color: var(--acc); color: #fff; }

.btn2 { display: inline-flex; align-items: center; gap: 8px; font-size: var(--fs-sm); font-weight: var(--fw-semibold); text-decoration: none; color: var(--ink); padding: 10px 18px; border: var(--border); }
.btn2:hover { border-color: var(--ink); }

.rlink { font-size: var(--fs-sm); font-weight: var(--fw-medium); text-decoration: none; color: var(--mut); }
.rlink:hover { color: var(--ink); }

.seemore { margin-top: 28px; }
.tbtn { background: none; border: var(--border); color: var(--mut); font-family: var(--font-mono); font-size: 11.5px; letter-spacing: .08em; text-transform: uppercase; padding: 9px 16px; cursor: pointer; }
.tbtn:hover { color: var(--ink); border-color: var(--ink); }

/* ── VISUAL REFRESH (20 Jul 2026) — micro-interactions.
   .ar nudges right inside any hovered card/button; quiet links grow an
   underline left→right. Reduced-motion opts out of both. ── */
.card.cs:hover .ar,
.btn:hover .ar,
.btn2:hover .ar,
.cmore .ar { transition: transform var(--dur-fast) var(--ease); }
.card.cs:hover .ar,
.btn:hover .ar,
.btn2:hover .ar { transform: translateX(4px); }
.rlink {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px; background-position: 0 100%; background-repeat: no-repeat;
  transition: background-size .18s var(--ease), color var(--dur-fast) var(--ease);
}
.rlink:hover { background-size: 100% 1px; }
@media (prefers-reduced-motion: reduce) {
  .card.cs, .ar, .rlink { transition: none; }
  .card.cs:hover { transform: none; }
}

/* ── assets/components/theme-toggle/theme-toggle.css ── */
/* theme-toggle — icon button that flips light/dark. Behavior in main.js;
   theme tokens in tokens.css (:root.dark). Sun shows in dark, moon in light. */
.themebtn { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; padding: 0; background: none; border: var(--border); color: var(--ink); cursor: pointer; flex: none; }
.themebtn:hover { border-color: var(--ink); color: var(--acc); }
.themebtn svg { width: 16px; height: 16px; display: block; }
.themebtn .t-sun { display: none; }
.themebtn .t-moon { display: inline-flex; }
:root.dark .themebtn .t-sun { display: inline-flex; }
:root.dark .themebtn .t-moon { display: none; }

/* ── assets/components/pill/pill.css ── */
/* pill — rounded status chip with a pulsing accent dot.
   Usage: <span class="pill"><span class="dot"></span>Open to work</span> */
.pill { display: inline-flex; align-items: center; gap: 7px; font-family: var(--font-mono); font-size: var(--fs-eyebrow); letter-spacing: .06em; text-transform: uppercase; border: var(--border); padding: 6px 11px; border-radius: var(--radius-pill); white-space: nowrap; }
.dot { width: 7px; height: 7px; border-radius: var(--radius-pill); background: var(--acc); animation: pulse 2.4s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* narrow screens: the long hero/contact status pill wraps instead of clipping */
@media (max-width: 700px) {
  .hpill .pill { white-space: normal; line-height: 1.5; gap: 13px; padding-left: 14px; }
}

/* ── assets/components/tag/tag.css ── */
/* tag — outlined mono label (with .hot accent variant) and its flex row.
   Usage: <div class="tags"><span class="tag hot">Agentic AI</span>…</div> */
.tags { display: flex; flex-wrap: wrap; gap: 8px; }
.tag { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; border: var(--border); padding: 4px 9px; color: var(--mut); }
.tag.hot { border-color: var(--acc); color: var(--acc-text); }

/* ── assets/components/chip/chip.css ── */
/* chip — soft-filled mono token (skills, stacks) and its flex row.
   Usage: <div class="chips"><span class="chip">LangChain</span>…</div> */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px; }
.chip { font-family: var(--font-mono); font-size: 11.5px; background: var(--soft); padding: 6px 11px; }

/* ── assets/components/placeholder/placeholder.css ── */
/* placeholder — visible "content to be added" markers. Inline (.ph) and
   block (.slot). Dashed border so gaps are obvious, never fabricated. */
.ph { font-family: var(--font-mono); font-size: 12px; color: var(--mut); background: var(--soft); border: 1px dashed var(--ln); padding: 10px 12px; margin-top: 16px; line-height: 1.5; }
.slot { border: 1px dashed var(--ln); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 40px; margin-top: 20px; text-align: center; }

/* ── assets/components/card/card.css ── */
/* card — base surface (bg + hairline border). Variants live in their own
   components (case-study, philosophy, testimonial, beyond, flagship) and
   compose with .card in markup, e.g. <div class="card cs">. */
.card { background: var(--card); border: var(--border); }

/* ── assets/components/nav/nav.css ── */
/* nav — sticky top bar + mobile menu. Includes .logo (also reused in footer).
   Responsive: links collapse to the burger menu below 700px. */
.nav { position: sticky; top: 0; z-index: var(--z-nav); background: color-mix(in srgb, var(--bg) 92%, transparent); backdrop-filter: blur(8px); border-bottom: var(--border); }
.navin { display: flex; align-items: center; justify-content: space-between; height: 60px; gap: 16px; }
.logo { font-family: var(--font-mono); font-weight: var(--fw-semibold); font-size: 14px; letter-spacing: .1em; text-decoration: none; border: var(--border-ink); padding: 5px 9px; }
.nlinks { display: flex; gap: 26px; }
.nlink { font-size: var(--fs-sm); font-weight: var(--fw-medium); text-decoration: none; color: var(--mut); }
.nlink:hover { color: var(--ink); }
.nright { display: flex; align-items: center; gap: 14px; }

.burger { display: none; background: none; border: var(--border); color: var(--ink); width: 38px; height: 38px; font-size: 16px; cursor: pointer; }
.mmenu { display: none; flex-direction: column; gap: 0; border-top: var(--border); background: var(--bg); }
.mlink { padding: 14px 24px; text-decoration: none; font-weight: var(--fw-medium); border-bottom: var(--border); }

/* mmenu contact footer — email primary, outline row, open-to-work pill */
.mfoot { display: flex; flex-direction: column; align-items: stretch; gap: 10px; padding: 16px 24px 18px; background: var(--soft); }
.mfoot .btn { justify-content: center; }
.mfoot-row { display: flex; gap: 8px; }
.mfoot-row .btn2 { flex: 1; justify-content: center; padding: 10px 0; background: var(--card); }
.mfoot .pill { white-space: normal; align-self: flex-start; margin-top: 2px; }

/* ≤960: links collapse to the burger (960–700 used to strand the reader
   with no section nav at all) */
@media (max-width: 960px) {
  .nlinks { display: none; }
  .pill.navpill { display: none; }
  .burger { display: flex; align-items: center; justify-content: center; }
  .mmenu.open { display: flex; }
}
@media (max-width: 700px) {
  .nright .btn { display: none; }
  /* résumé stays in the bar as a compact bordered button, revealed with the
     dock once the hero (which has its own résumé CTA) scrolls away */
  .nright .rlink { display: inline-flex; align-items: center; border: var(--border); border-color: transparent; color: var(--ink); font-weight: var(--fw-semibold); font-size: 13px; white-space: nowrap; max-width: 0; padding: 8px 0; margin: 0 -14px 0 0; opacity: 0; visibility: hidden; overflow: hidden; transition: max-width .3s ease, padding .3s ease, margin .3s ease, opacity .25s ease; }
  body.past-hero .nright .rlink { max-width: 120px; padding: 8px 11px; margin: 0; opacity: 1; visibility: visible; border-color: var(--ln); }
}
@media (prefers-reduced-motion: reduce) {
  .nright .rlink { transition: none; }
}

/* ---- burger state + scrim (audit V-7): the icon morphs ☰ → ✕ while the
   menu is open, and a scrim dims the page behind it (tap to close;
   main.js wires the click). ---- */
.burger svg { width: 16px; height: 16px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; }
.burger .x-icon { display: none; }
.nav:has(.mmenu.open) .burger .h-icon { display: none; }
.nav:has(.mmenu.open) .burger .x-icon { display: block; }
.mscrim { position: fixed; inset: 0; z-index: calc(var(--z-nav) - 5); background: rgba(20,20,18,.38);
          opacity: 0; pointer-events: none; transition: opacity var(--dur-fast) var(--ease); }
:root.dark .mscrim { background: rgba(0,0,0,.55); }
body:has(.mmenu.open) .mscrim { opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) { .mscrim { transition: none; } }

/* ── assets/components/hero/hero.css ── */
/* hero — name block: dot-grid background, display name, sub, CTAs, status pill.
   The ID-card motif is a separate component (id-card/). */
.hero { position: relative; padding: 110px 0 90px; background-image: radial-gradient(var(--ln) 1px, transparent 1px); background-size: 26px 26px; }
.hname { font-size: var(--fs-hero); font-weight: var(--fw-bold); letter-spacing: -.035em; line-height: .98; margin: 22px 0 0; text-wrap: balance; }
.hsub { font-size: clamp(16px, 1.8vw, 20px); line-height: var(--lh-snug); max-width: 680px; margin: 26px 0 0; color: var(--mut); }
.hsub .fw6 { color: var(--ink); }
.hctas { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 36px; align-items: center; }
.hpill { margin-top: 30px; }

@media (max-width: 700px) {
  .hero { padding: 80px 0 70px; }
}

/* ── assets/components/id-card/id-card.css ── */
/* id-card — the rotated "ID badge" motif in the hero (monogram, name, role,
   barcode). Decorative; hidden below 960px. */
.motif { position: absolute; right: 24px; top: 90px; width: 270px; background: var(--card); border: var(--border-ink); transform: rotate(3deg); box-shadow: var(--shadow-block); }
.idtop { display: flex; align-items: center; justify-content: space-between; border-bottom: var(--border); padding: 10px 14px; }
.idlab { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--mut); }
.idhole { width: 34px; height: 9px; border-radius: var(--radius-pill); background: var(--soft); border: var(--border); }
.idface { margin: 18px auto 0; width: 190px; height: 220px; border: 1.5px solid var(--ink); overflow: hidden; display: flex; align-items: center; justify-content: center; font-family: var(--font-mono); font-size: 38px; font-weight: var(--fw-semibold); letter-spacing: .05em; background: repeating-linear-gradient(45deg, transparent, transparent 6px, var(--soft) 6px, var(--soft) 7px); }
/* badge headshot — light-bg photo in light mode, charcoal-bg photo in dark mode */
.idface img { width: 100%; height: 100%; object-fit: cover; display: block; }
.idface .hs-dark { display: none; }
:root.dark .idface .hs-light { display: none; }
:root.dark .idface .hs-dark { display: block; }
.idbody { padding: 14px 16px 16px; text-align: center; }
.idname { font-weight: var(--fw-bold); font-size: 16px; letter-spacing: -.01em; }
.idrole { font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--mut); margin-top: 4px; }
.idbar { margin: 14px auto 0; height: 26px; width: 150px; background: repeating-linear-gradient(90deg, var(--ink) 0 2px, transparent 2px 5px, var(--ink) 5px 8px, transparent 8px 11px); }
.idfoot { display: flex; align-items: center; justify-content: center; gap: 7px; border-top: var(--border); padding: 8px; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--mut); }

@media (max-width: 960px) {
  .motif { display: none; }
}


/* ══════════════════════════════════════════════════════════════════
   VISUAL REFRESH (20 Jul 2026) — static ID card for the MOBILE hero
   Below 960px the draggable lanyard is hidden, so the phone hero had no
   image at all. This is the same badge, parked: no physics, no JS.
   ══════════════════════════════════════════════════════════════════ */
#top .hero-idcard { width: 228px; background: var(--card); border: var(--border-ink); box-shadow: var(--shadow-block); transform: rotate(-2deg); margin: 30px auto 12px; display: none; }
#top .hero-idcard .idface { width: 164px; height: 176px; margin-top: 14px; }
#top .hero-idcard .idbar { height: 22px; }
/* real rule: card below 960px (inverse of the site's .motif), chip at/above 960px */
@media (max-width: 959px) { #top .hero-idcard { display: block; }
}
@media (min-width: 960px) { }

/* ── assets/components/lanyard/lanyard.css ── */
/* lanyard — the hero's ID badge hanging from a physical band (replaces the
   static rotated motif). Band → flat clamp → card; 2D rope physics with
   gravity, stretch and bounce lives in lanyard.js. Decorative; hidden <960px.
   The card reuses the id-card component's .idtop/.idface/.idbody anatomy. */

/* Anchored so the rotation pivot — the band's origin — sits at the PAGE's very
   top edge (y≈0): wrap top = nav 60px + hero padding 110px = 170px down the
   page, hence top:-170px. The band passes behind the translucent sticky nav
   (nav z50 > rig z2) and visibly begins at the top edge — its cut edge lives
   exactly at y=0, where swing wobble (±22px·sinθ) stays hidden under the nav. */
.lan-rig { position: absolute; top: -170px; right: 24px; width: 270px; transform-origin: 135px 0; will-change: transform; z-index: 2; }

/* band: flat ink strap, hairline edges, repeating vertical wordmark.
   Height is driven by JS (rope length); text clips as it stretches. */
.lan-band { position: relative; width: 44px; height: 220px; margin: 0 auto; background: var(--ink); border-left: 1px solid var(--ln); border-right: 1px solid var(--ln); overflow: hidden; }
.lan-band span { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; writing-mode: vertical-rl; font-family: var(--font-mono); font-size: 9px; letter-spacing: .34em; text-transform: uppercase; color: var(--bg); opacity: .85; user-select: none; }
/* no dark override: the ink/bg tokens invert themselves — dark mode gets an
   off-white band with near-black text, mirroring light mode's figure-ground */

/* clip: flat print-language clamp — sharp corners, ink border, offset shadow,
   pill slot echoing the card's .idhole, red accent tick */
.lan-clip { position: relative; width: 68px; height: 24px; margin: -1px auto 0; background: var(--card); border: var(--border-ink); box-shadow: 3px 3px 0 var(--ln); z-index: 2; }
.lan-clip::before { content: ""; position: absolute; left: 50%; top: 50%; width: 30px; height: 7px; transform: translate(-50%, -50%); border-radius: var(--radius-pill); background: var(--soft); border: var(--border); }
.lan-clip::after { content: ""; position: absolute; left: 6px; top: 50%; width: 4px; height: 4px; margin-top: -2px; background: var(--acc); }

/* the card — same surface language as the old motif; slight lag rotation */
.lan-card { position: relative; margin-top: -4px; width: 270px; background: var(--card); border: var(--border-ink); box-shadow: var(--shadow-block); transform-origin: 50% -10px; will-change: transform; cursor: grab; }
.lan-card:active { cursor: grabbing; }
.lan-card, .lan-card * { touch-action: none; }

/* theme-swapped headshot */
.lan-card img.hs-light, .lan-card img.hs-dark { width: 100%; height: 100%; object-fit: cover; display: block; }
.lan-card img.hs-dark { display: none; }
:root.dark .lan-card img.hs-light { display: none; }
:root.dark .lan-card img.hs-dark { display: block; }

@media (max-width: 960px) {
  .lan-rig { display: none; }
}

/* ── assets/components/stats/stats.css ── */
/* stats — four self-labelled proof tiles.
   Each tile carries its own mono category kicker (.skick) above a hairline, so the
   four facts read as INDEPENDENT PEERS. This is deliberate: a headline number with
   smaller stats attached reads as parent → children, which implied the AI build
   caused the revenue figures (explored in test-stats-block-v2.html). Equal tiles
   plus per-tile category tags remove that reading entirely.
   The AI tile (.stat.ai) is accented — it's the positioning bet, presented as a
   parallel category rather than a result of the others.
   Numbers animate via the count-up in main.js (#statsCount + data-count attrs).
   4 → 2 → 1 columns as width shrinks. */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); border: var(--border); }
.stat { padding: 24px 22px 26px; border-right: var(--border); min-width: 0; }
.stat.last { border-right: none; }

/* category tag — tells the reader what KIND of proof this tile is */
.skick { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase; color: var(--mut); padding-bottom: 12px; margin-bottom: 16px; border-bottom: var(--border); }
.stat.ai .skick { color: var(--acc); border-bottom-color: var(--acc); }

.snum { font-size: clamp(26px, 3vw, 36px); font-weight: var(--fw-bold); letter-spacing: -.03em; line-height: 1; white-space: nowrap; }
.stat.ai .snum { color: var(--acc); }
/* folded-in units ("+", "/mo") — smaller, muted, mono so the figure stays dominant.
   Written into data-suffix as markup so the count-up re-renders them each frame. */
.snum .su, .snum .per { font-family: var(--font-mono); font-size: .46em; font-weight: var(--fw-medium); color: var(--mut); letter-spacing: 0; }

.slab { font-size: 12.5px; line-height: var(--lh-snug); color: var(--mut); margin-top: 12px; }

@media (max-width: 960px) {
  .stats { grid-template-columns: repeat(2, 1fr); }
  .stat { border-bottom: var(--border); }
  .stat:nth-child(2n) { border-right: none; }
  .stat:nth-last-child(-n+2) { border-bottom: none; }
}
@media (max-width: 700px) {
  .stats { grid-template-columns: 1fr; }
  .stat { border-right: none; border-bottom: var(--border); }
  .stat:last-child { border-bottom: none; }
}

/* ── assets/components/thread/thread.css ── */
/* thread — large editorial lead statement; .acc2 accents a key phrase. */
.thread { font-size: clamp(20px, 2.6vw, 30px); line-height: var(--lh-snug); font-weight: var(--fw-medium); letter-spacing: -.01em; max-width: 900px; text-wrap: pretty; }
.thread .acc2 { color: var(--acc); }

/* ── assets/components/flagship/flagship.css ── */
/* flagship — the big AI-build card: title/lead, metric grid (.pgrid/.pcell),
   failure list (.fails/.fail), external links, honest note, pull-quote.
   Composes with .card and reuses tag/ (.tags) + chip/ (.chips) in markup. */
.flag { padding: clamp(24px, 4vw, 48px); }
.ftitle { font-size: clamp(24px, 3vw, 34px); font-weight: var(--fw-semibold); letter-spacing: var(--tracking-tight); margin: 18px 0 0; }
.fline { font-size: var(--fs-base); line-height: var(--lh-body); color: var(--mut); max-width: 720px; margin: 14px 0 0; }

/* metric grid */
.pgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--ln); border: var(--border); margin-top: 30px; }
.pcell { background: var(--card); padding: 18px 16px; }
.pnum { font-family: var(--font-mono); font-size: 21px; font-weight: var(--fw-semibold); }
.plab { font-size: 12px; line-height: var(--lh-snug); color: var(--mut); margin-top: 6px; }

/* failure list */
.fails { margin-top: 36px; }
.fhead { font-family: var(--font-mono); font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--mut); margin-bottom: 14px; }
.fail { display: grid; grid-template-columns: 44px 1fr; gap: 16px; padding: 16px 0; border-top: var(--border); }
.fnum { font-family: var(--font-mono); font-size: 13px; color: var(--acc-text); padding-top: 2px; }
.fttl { font-weight: var(--fw-semibold); font-size: 15px; }
.fles { font-size: var(--fs-sm); line-height: var(--lh-snug); color: var(--mut); margin-top: 5px; }

.flinks { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 30px; }
.honest { font-size: 13px; line-height: var(--lh-snug); color: var(--mut); border-left: 2px solid var(--acc); padding: 2px 0 2px 14px; margin-top: 26px; max-width: 640px; font-style: italic; }
.pull { margin: 30px 0 0; padding: 24px; background: var(--soft); }
.pq { font-size: 17px; line-height: var(--lh-snug); font-weight: var(--fw-medium); margin: 0; }
.pa { font-family: var(--font-mono); font-size: 11.5px; color: var(--mut); margin-top: 12px; }

@media (max-width: 960px) {
  .pgrid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 700px) {
  .pgrid { grid-template-columns: 1fr; }
}

/* #ai-failures — receipt deep-link target; clear the sticky nav on arrival */
.fails { scroll-margin-top: 96px; }



/* ── VISUAL REFRESH (20 Jul 2026) — failure cases as incident tickets ── */
/* --- QW-6 incident tickets (merged with the arch cross-highlight rows) --- */
#ai .fails-tix { margin-top: 6px; }
#ai .fails-tix .fhead { font-family: var(--font-mono); font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--mut); margin-bottom: 14px; }
#ai .tix-row { display: grid; grid-template-columns: 52px 1fr; gap: 16px; padding: 16px 14px; border-top: var(--border); border-left: 2px solid var(--acc);
  transition: background var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease); cursor: default; }
#ai .tix-row:hover,
#ai .tix-row.hl { background: var(--soft); box-shadow: inset 3px 0 0 var(--acc); }
#ai .tix-row:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }
#ai .tix-idx { font-family: var(--font-mono); font-size: 13px; font-weight: var(--fw-semibold); color: var(--acc-text); padding-top: 2px; }
#ai .tix-ttl { font-weight: var(--fw-semibold); font-size: 15px; }
#ai .tix-les { font-size: var(--fs-sm); line-height: var(--lh-snug); color: var(--mut); margin-top: 5px; }
#ai .tix-chip { display: inline-block; margin-top: 10px; font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; padding: 3px 8px; background: var(--soft); color: var(--mut); border: var(--border); }

/* ── assets/components/arch/arch.css ── */
/* arch — the AI Support Resolution Agent request-path diagram.
   Wide drawing ≥760px; below that a vertical reflow of the SAME topology
   (lanes: markers left · column centre · escalate bypass right) so a phone
   never scrolls sideways. F-markers cross-highlight with the ticket rows. */
/* ══════════════════════════════════════════════════════════════════
   VISUAL REFRESH (20 Jul 2026) — failure cases as incident tickets + the architecture diagram
   ══════════════════════════════════════════════════════════════════ */
#ai .ai-off { display: none; }
/* --- TRUE architecture diagram (test-arch-v2, scoped) --- */
#ai .arch-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; margin-top: 28px; padding-bottom: 6px; }
#ai .arch-svg { display: block; width: 100%; min-width: 900px; max-width: 1040px; height: auto; margin: 0 auto; }
#ai .n-box { fill: var(--card); stroke: var(--ink); stroke-width: 1.6; }
#ai .flow { fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
#ai .eval-frame { fill: none; stroke: var(--mut); stroke-width: 1.5; stroke-dasharray: 5 4; }
#ai .fb { fill: none; stroke: var(--mut); stroke-width: 1.4; stroke-dasharray: 4 4; stroke-linecap: round; stroke-linejoin: round; }
#ai .n-l1 { font-family: var(--font-mono); font-size: 11px; font-weight: var(--fw-semibold); letter-spacing: .1em; fill: var(--ink); }
#ai .n-l2 { font-family: var(--font-mono); font-size: 8px; font-weight: var(--fw-regular); letter-spacing: .05em; fill: var(--mut); }
#ai .eval-lbl { font-family: var(--font-mono); font-size: 10.5px; font-weight: var(--fw-medium); letter-spacing: .12em; fill: var(--mut); }
#ai .eval-sub { font-family: var(--font-mono); font-size: 8px; letter-spacing: .05em; fill: var(--mut); }
#ai .eval-cap { font-family: var(--font-mono); font-size: 8.5px; letter-spacing: .05em; fill: var(--mut); }
#ai .bypass { fill: none; stroke: var(--acc); stroke-width: 1.6; stroke-dasharray: 5 4; stroke-linecap: round; stroke-linejoin: round; }
#ai .bypass-head { fill: none; stroke: var(--acc); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
#ai .acc-lbl { font-family: var(--font-mono); font-size: 8.5px; font-weight: var(--fw-medium); letter-spacing: .08em; fill: var(--acc); }
#ai .pii-lbl { font-family: var(--font-mono); font-size: 9px; font-weight: var(--fw-medium); letter-spacing: .08em; fill: var(--acc); }
#ai .marker { cursor: pointer; }
#ai .marker .m-dot { fill: var(--acc); transform-box: fill-box; transform-origin: center; transition: transform var(--dur-fast,.2s) var(--ease,ease); }
#ai .marker .m-lbl { font-family: var(--font-mono); font-size: 10px; font-weight: var(--fw-medium); letter-spacing: .1em; fill: var(--mut); transition: fill var(--dur-fast,.2s) var(--ease,ease); }
#ai .marker.hl .m-dot { transform: scale(1.55); }
#ai .marker.hl .m-lbl { fill: var(--acc); }
#ai .marker:focus-visible { outline: 2px solid var(--acc); outline-offset: 3px; border-radius: 2px; }
#ai .arch-svg:not(.arch-vert) .ln-draw { stroke-dasharray: 1; stroke-dashoffset: 1; }
#ai .arch-svg.drawn .ln-draw { stroke-dashoffset: 0; transition: stroke-dashoffset 1s var(--ease,ease); }
#ai .arch-svg:not(.arch-vert) .fade { opacity: 0; }
#ai .arch-svg.drawn .fade { opacity: 1; transition: opacity .5s var(--ease,ease); }
@media (prefers-reduced-motion: reduce) {
  #ai .arch-svg .ln-draw { stroke-dashoffset: 0 !important; }
#ai .arch-svg .fade { opacity: 1 !important; }
#ai .arch-svg .ln-draw, #ai .arch-svg .fade { transition: none !important; }
}
#ai .arch-legend { font-family: var(--font-mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--mut); margin: 22px 0 6px; display: flex; align-items: center; gap: 8px; }
#ai .arch-legend .lg-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--acc); display: inline-block; flex: 0 0 auto; }
#ai .arch-foot { margin-top: 22px; }
#ai .arch-foot p { font-family: var(--font-mono); font-size: 10.5px; line-height: 1.7; letter-spacing: .04em; color: var(--mut); margin: 0 0 6px; overflow-wrap: anywhere; }
/* ── A-2 · VERTICAL REFLOW (owner's pick for mobile, 19 Jul) ──
   ≥760px keeps the wide diagram exactly as approved; below that it is replaced
   by a top-to-bottom redraw of the same topology built on lanes (markers left,
   column centre, bypass right) so nothing crosses. */
#ai .arch-vert { display: none; }
@media (max-width: 760px) {
  #ai .arch-scroll { display: none; }
#ai .arch-vert {
    display: block; width: 100%; min-width: 0; max-width: 320px; height: auto; margin: 18px auto 0;
  }
}
/* the vertical draws its own arrowheads as separate paths — without these they
   default to fill:black and vanish on dark */
#ai .flow-head { fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
#ai .fb-head { fill: none; stroke: var(--mut); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
/* Staggered top-to-bottom reveal, as a PROGRESSIVE ENHANCEMENT.
   The diagram is visible by default; JS adds .anim only when it can guarantee it
   will also fire the reveal. Without that guard a failed observer left every node
   at opacity 0 — i.e. an invisible diagram — which is a far worse failure than
   simply not animating. */
#ai .arch-vert.anim .fade { opacity: 0; }
#ai .arch-vert.anim .ln-draw { stroke-dasharray: 1; stroke-dashoffset: 1; }
#ai .arch-vert.anim.drawn .fade { opacity: 1; transition: opacity .4s var(--ease) calc(var(--i, 0) * 45ms); }
#ai .arch-vert.anim.drawn .ln-draw { stroke-dashoffset: 0; transition: stroke-dashoffset .45s var(--ease) calc(var(--i, 0) * 45ms); }
@media (prefers-reduced-motion: reduce) {
  #ai .arch-vert.anim .fade { opacity: 1; }
#ai .arch-vert.anim .ln-draw { stroke-dashoffset: 0; }
}

/* ── assets/components/case-study/case-study.css ── */
/* case-study — 2-up grid of PM case cards: company/date meta, title,
   accented result line. Composes with .card and reuses chip/. */
.cgrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
.cs { padding: 28px; display: flex; flex-direction: column; gap: 0; }
.cmeta { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }
.cco { font-family: var(--font-mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--mut); }
/* the year token is atomic — the company label wraps instead (audit T-3) */
.cmeta .cco:last-child { white-space: nowrap; flex-shrink: 0; }
.cttl { font-size: 20px; font-weight: var(--fw-semibold); letter-spacing: -.01em; margin: 14px 0 0; }
/* result line in ink; only the numerals carry accent (audit V-4) */
.cres { font-size: 14.5px; margin-top: 8px; color: var(--ink); font-weight: var(--fw-regular); }
.cres .n { font-family: var(--font-mono); font-size: .95em; color: var(--acc-text); font-weight: var(--fw-semibold); }
/* cross-team collaborator microline — same voice as .cco meta; only on cards
   whose case study names the teams */
.cwith { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--mut); margin: 10px 0 0; }

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


/* ══════════════════════════════════════════════════════════════════
   VISUAL REFRESH (20 Jul 2026) — paper-clip photo (desktop) + 1:1 thumbnail (mobile), card lift, year in the footer
   ══════════════════════════════════════════════════════════════════ */
#work .cthumb, #work .polaroid { display: none; }
#work .cthumb { aspect-ratio: 1 / 1; overflow: hidden; border: var(--border); margin-bottom: 14px; background: var(--soft); }
#work .cthumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
#work .polaroid img { width: 100%; height: 117px; object-fit: cover; display: block; }
/* O4: year marker relocated bottom-right (desktop polaroid state only). .cfoot
   wraps the case-study link + a duplicate, aria-hidden .cyr year token — the
   real .cco year inside .cmeta stays the sole screen-reader-visible copy at
   all times (only visually clipped, never removed from the tree). Structural
   wrapper + hidden-by-default .cyr apply everywhere so OFF / mobile markup
   renders identically to before (.cyr display:none, .cfoot ≈ old .cmore spacing). */
#work .cfoot { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-top: 16px; }
#work .cmore { margin-top: 0; }
#work .cyr { display: none; }
/* owner: heading and impact line sat too close — open the gap (ON mode only;
   OFF keeps today's 8px from case-study.css) */
#work .cres { margin-top: 14px; }
/* card 1 only: owner-approved retitle "New-player growth & early VIPs" —
   matches the case study's own two-fold framing (grow the broad new-player
   base AND the early-VIP inflow) and never wraps 3-deep against the
   polaroid clearance. Shown in ON mode at every width for consistency;
   .cttl-full (today's wording) stays for the OFF state and screen readers.
   The modal keeps the full original title. */
#work .cttl-short { display: none; }
#work .cttl-full {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
#work .cttl-short { display: inline; }
/* ON, mobile / coarse pointer → Option-1 visible square thumbnail */
#work .cthumb { display: block; }
/* ON, desktop + fine hover pointer → PAPER-CLIPPED (owner's pick, 19 Jul):
   an accent wire clip straddles the card's top-right edge; the polaroid hangs
   from it at −5° and swings to vertical on hover around the clip's grip point.
   Wire sandwich: outer loop behind the photo (.wk-cb), inner leg in front
   (.wk-cf) — both <use> the shared defs paths and inherit the accent stroke. */
#work .wk-hang { display: none; }
@media (min-width: 960px) and (hover: hover) and (pointer: fine) {
  #work .cthumb { display: none; }
#work .cs { position: relative; overflow: visible; margin-top: 34px; padding-top: 28px; }
#work .wk-hang { display: block; position: absolute; top: -26px; right: 14px; z-index: 2; }
#work .polaroid {
    display: block; position: relative; z-index: 2; width: 124px; margin-top: 16px;
    background: var(--card); border: var(--border-ink); box-shadow: var(--shadow-block);
    padding: 4px; transform: rotate(-5deg); transform-origin: 78% 3%;
    transition: transform .2s var(--ease);
  }
#work .wk-clip {
    position: absolute; top: 0; right: 14px; width: 26px; height: 42px;
    transform: rotate(14deg); overflow: visible;
    fill: none; stroke: var(--acc); stroke-width: 2.6;
    stroke-linecap: round; stroke-linejoin: round;
  }
#work .wk-cb { z-index: 1; opacity: .72; }
#work .wk-cf { z-index: 3; }
/* text clearance for the hang's ~150px footprint (124px photo + inset + tilt) */
  #work .cmeta,
  #work .cttl,
  #work .cres { padding-right: 152px; }
/* year leaves the top meta row visually (kept for screen readers via the
     clip technique below) and reappears via the aria-hidden .cyr on the
     bottom link row, mono/muted like the original */
  #work .cmeta .cco:last-child {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
  }
#work .cyr { display: inline; }
/* hover/focus: the photo swings upright on the clip, in step with the card lift */
  #work .cs:hover .polaroid,
  #work .cs:focus-within .polaroid,
  #work .cs:focus-visible .polaroid { transform: rotate(0deg); }
}
/* reduced-motion: keep it tilted, no swing animation (chosen over instant-straight) */
@media (prefers-reduced-motion: reduce) and (min-width: 960px) and (hover: hover) and (pointer: fine) {
  #work .polaroid { transition: none; }
#work .cs:hover .polaroid,
  #work .cs:focus-within .polaroid,
  #work .cs:focus-visible .polaroid { transform: rotate(-5deg); }
}

/* ── VISUAL REFRESH (20 Jul 2026) — the card lifts on hover/focus.
   Offset print-shadow, matching the ID-card language; transform only. ── */
.card.cs { transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease); }
.card.cs:hover,
.card.cs:focus-visible {
  transform: translateY(-3px);
  border-color: var(--ink);
  box-shadow: 6px 6px 0 color-mix(in srgb, var(--ink) 14%, transparent);
}

/* ── assets/components/cursor-image/cursor-image.css ── */
/* cursor-image — a floating project preview whose bottom-left corner is pinned
   to the cursor while hovering a card. Add class "hover-card" to any element and
   (optionally) data-img="…" (real image) or data-title="…" (placeholder label).
   Behavior lives in cursor-image.js. Desktop / fine-pointer only (JS guards). */
.hover-card { cursor: pointer; }

.cursor-img { position: fixed; top: 0; left: 0; z-index: var(--z-cursor-img); pointer-events: none; opacity: 0; transition: opacity .16s ease; will-change: transform; }
.cursor-img.show { opacity: 1; }
.cursor-img img { display: block; width: 240px; height: 240px; object-fit: cover; border: var(--border-ink); box-shadow: var(--shadow-block); background: var(--card); }

/* ── assets/components/typetext/typetext.css ── */
/* typetext — type-once "typewriter" effect. Add [data-typetext] to any element;
   typetext.js types its text out char-by-char on load and stops (no delete/loop).
   The real text stays in the HTML (SEO / no-JS / reduced-motion all show it in full).
   Optional attrs: data-type-speed (ms/char), data-type-delay (ms), data-type-char,
   data-type-cursor="keep" (keep the cursor after typing). Behavior in typetext.js. */
.typetext-cursor {
  display: inline-block;
  margin-left: .04em;
  color: var(--acc);
  font-weight: var(--fw-regular);
  animation: typetext-blink 1s steps(1, end) infinite;
}
.typetext-cursor.done {
  animation: none;
  opacity: 0;
  transition: opacity .4s ease;
}
@keyframes typetext-blink {
  0%, 50% { opacity: 1; }
  50.001%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .typetext-cursor { animation: none; }
}

/* ── assets/components/timeline/timeline.css ── */
/* timeline — vertical work/education track. .titem.now marks the latest
   entry (accent dot); .ttag.edu marks education rows. */
.tl { position: relative; padding-left: 32px; border-left: var(--border); display: flex; flex-direction: column; gap: 34px; }
.titem { position: relative; }
.tdot { position: absolute; left: -37px; top: 5px; width: 9px; height: 9px; background: var(--bg); border: 2px solid var(--ink); border-radius: var(--radius-pill); }
.titem.now .tdot { background: var(--acc); border-color: var(--acc); }
.tyr { font-family: var(--font-mono); font-size: 12px; color: var(--mut); display: flex; gap: 10px; align-items: center; }
.ttag { font-size: 9.5px; letter-spacing: .12em; border: var(--border); padding: 2px 6px; }
.ttag.edu { color: var(--acc-text); border-color: var(--acc); }
.trole { font-size: 17px; font-weight: var(--fw-semibold); margin-top: 7px; }
.tdesc { font-size: var(--fs-sm); color: var(--mut); margin-top: 4px; line-height: var(--lh-snug); max-width: 620px; }

/* ---- zigzag layout (audit V-1a): center spine on desktop, entries
   alternate sides so the right rail carries content; collapses back to
   the classic single left rail ≤700px. Rows: .trow.L / .trow.R
   (+ .now for the latest entry). Reuses .tyr/.ttag/.trole/.tdesc. ---- */
.tl2 { position: relative; margin-top: 8px; }
.tl2::before { content: ""; position: absolute; left: 50%; top: 4px; bottom: 4px; width: 1px; background: var(--ln); }
.tl2 .trow { display: grid; grid-template-columns: 1fr 72px 1fr; margin: 0 0 34px; }
.tl2 .trow:last-child { margin-bottom: 0; }
.tl2 .titem2 { min-width: 0; }
.tl2 .trow.L .titem2 { grid-column: 1; text-align: right; }
.tl2 .trow.L .tyr { justify-content: flex-end; }
.tl2 .trow.L .tdesc { margin-left: auto; }
.tl2 .trow.R .titem2 { grid-column: 3; text-align: left; }
.tl2 .tdot2 { grid-column: 2; grid-row: 1; align-self: start; justify-self: center; margin-top: 5px;
              width: 9px; height: 9px; background: var(--bg); border: 2px solid var(--ink); border-radius: var(--radius-pill); }
.tl2 .trow.now .tdot2 { background: var(--acc); border-color: var(--acc); }
.tl2 .tdesc { max-width: 460px; }

@media (max-width: 700px) {
  .tl2::before { left: 4px; }
  .tl2 .trow { grid-template-columns: 32px 1fr; margin-bottom: 30px; }
  .tl2 .tdot2 { grid-column: 1; justify-self: start; }
  .tl2 .trow.L .titem2, .tl2 .trow.R .titem2 { grid-column: 2; text-align: left; }
  .tl2 .trow.L .tyr { justify-content: flex-start; }
  .tl2 .trow.L .tdesc { margin-left: 0; }
}


/* ══════════════════════════════════════════════════════════════════
   VISUAL REFRESH (20 Jul 2026) — company marks (desktop) · orgs wall (mobile) · accent ring on education milestones
   ══════════════════════════════════════════════════════════════════ */
#timeline .mk-line { display: flex; gap: 12px; margin: 12px 0 10px; }
#timeline .tl2 .trow.L .mk-line { justify-content: flex-end; }
#timeline .tl2 .trow.R .mk-line { justify-content: flex-start; }
#timeline .mk-logo { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; line-height: 0; }
/* OPTICAL SIZING (19 Jul): uniform 30px height made wide wordmarks enormous and
   square badges tiny. Heights are now tuned per logo so every mark carries
   similar visual weight; widths fall out of each file's aspect ratio. */
/* — shared tint rules: apply to BOTH the per-entry marks and the mobile wall — */
#timeline .mk-logo img,
#timeline .mw-cell img { display: block; width: auto; filter: brightness(0); opacity: .65; }
html.dark #timeline .mk-logo img,
html.dark #timeline .mw-cell img { filter: brightness(0) invert(1); opacity: .7; }
/* Opaque badges: brightness(0) would flatten them to blobs — keep grayscale. */
#timeline .mk-logo img[src$="narayana.webp"], #timeline .mw-cell img[src$="narayana.webp"],
#timeline .mk-logo img[src$="fastfilmz.webp"], #timeline .mw-cell img[src$="fastfilmz.webp"],
#timeline .mk-logo img[src$="a23.webp"], #timeline .mw-cell img[src$="a23.webp"],
html.dark #timeline .mk-logo img[src$="narayana.webp"], html.dark #timeline .mw-cell img[src$="narayana.webp"],
html.dark #timeline .mk-logo img[src$="fastfilmz.webp"], html.dark #timeline .mw-cell img[src$="fastfilmz.webp"],
html.dark #timeline .mk-logo img[src$="a23.webp"], html.dark #timeline .mw-cell img[src$="a23.webp"] { filter: grayscale(1); opacity: .9; }
/* Viacom18: the "18" is a WHITE KNOCKOUT inside a colored shape — brightness(0)
   turned shape and knockout both black, erasing the 18. Grayscale preserves the
   knockout; invert flips it correctly for dark. */
#timeline .mk-logo img[src$="viacom18.svg"],
#timeline .mw-cell img[src$="viacom18.svg"] { filter: grayscale(1); opacity: .8; }
html.dark #timeline .mk-logo img[src$="viacom18.svg"],
html.dark #timeline .mw-cell img[src$="viacom18.svg"] { filter: grayscale(1) invert(1); opacity: .8; }
/* desktop per-entry mark heights */
#timeline .mk-logo img[src$="viacom18.svg"]           { height: 15px; }
/* very wide wordmark */
#timeline .mk-logo img[src$="tarams.svg"]             { height: 18px; }
#timeline .mk-logo img[src$="interview-kickstart.svg"]{ height: 19px; }
#timeline .mk-logo img[src$="iiit-h.webp"]            { height: 34px; }
/* stacked lockup */
#timeline .mk-logo img[src$="hdw.webp"]               { height: 30px; }
/* stacked lockup */
#timeline .mk-logo img[src$="a23.webp"]               { height: 26px; }
/* square badge */
#timeline .mk-logo img[src$="isb.svg"]                { height: 22px; }
/* emblem + wordmark lockup */
#timeline .mk-logo img[src$="narayana.webp"],
#timeline .mk-logo img[src$="fastfilmz.webp"]         { height: 24px; }
/* square badges */
#timeline .mk-ph { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; border: 1px dashed var(--ln); padding: 0 10px; height: 24px; display: inline-flex; align-items: center; color: var(--mut); white-space: nowrap; }
/* ── E-3 · SPINE (owner's pick, 20 Jul) ──
   ISB and IIIT-H are marked in the timeline's OWN vocabulary — an accent ring on
   their dot — rather than by inflating the logos. Distinct from .trow.now, which
   is a SOLID accent dot ("you are here"); a ring reads as "milestone". Logos,
   colour count and layout are all untouched. Works on the mobile rail too. */
#timeline .tl2 .trow.edu-row .tdot2 {
  background: var(--bg);
  border-color: var(--acc);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 16%, transparent);
}
/* ── MOBILE (owner's pick, 19 Jul): M-3 "ORGS WALL" ──
   Per-entry marks crowded the single column, so below 760px they come off and
   the brands appear ONCE as a compact wall under the heading; entries stay
   plain. Desktop is unaffected. Wall is aria-hidden — every org is named in the
   timeline text right below it, so this is decorative duplication. */
#timeline .mwall { display: none; }
@media (max-width: 760px) {
  #timeline .mk-line { display: none; }
#timeline .mwall {
    display: flex; flex-wrap: wrap; align-items: center; gap: 14px 20px;
    border: var(--border); padding: 14px 16px; margin: 0 0 26px;
  }
}
#timeline .mw-cell { display: inline-flex; align-items: center; line-height: 0; }
#timeline .mw-cell img { height: 16px; }
#timeline .mw-cell img[src$="narayana.webp"],
#timeline .mw-cell img[src$="fastfilmz.webp"],
#timeline .mw-cell img[src$="iiit-h.webp"] { height: 20px; }
#timeline .mw-cell img[src$="viacom18.svg"] { height: 13px; }
#timeline .mw-cell img[src$="hdw.webp"] { height: 22px; }
#timeline .mw-cell img[src$="a23.webp"] { height: 20px; }
#timeline .mw-cell img[src$="isb.svg"] { height: 18px; }
#timeline .mw-cell img[src$="iiit-h.webp"] { height: 26px; }
/* lockup — taller than flat marks */
#timeline .mw-ph { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--mut); border: 1px dashed var(--ln); padding: 3px 8px; white-space: nowrap; }
/* IIIT-H's artwork carries the full institute name in micro-type — at wall size
   it reads as a smudge, so it runs as a typographic chip instead. SOLID hairline
   (a deliberate choice) vs the DASHED HDW chip (a genuinely missing file). */
#timeline .mw-txt { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--mut); border: var(--border); padding: 3px 8px; white-space: nowrap; }
/* ── assets/components/philosophy/philosophy.css ── */
/* philosophy → "How I think" — 6-step operating loop (.hloop/.hstep) grouped
   under subtle industry-phase labels (.hphase: Discover / Define / Experiment /
   Ship & scale). Each step closes with an evidence list (.hev): project-name
   links + the quote they anchor, deep-linking into the case-study modals.
   Closes with a compact principles strip (.hprin). The legacy 3-up principle
   grid (.pgrid2/.pcard) is kept below: reused by testimonial + styleguide. */

.hlnote { max-width: 640px; font-size: var(--fs-base); line-height: var(--lh-body); color: var(--mut); }
.hloop { margin-top: 36px; border-bottom: var(--border); }
.hphase { font-family: var(--font-mono); font-size: 11px; letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--mut); display: flex; align-items: center; gap: 10px; border-top: var(--border); padding: 26px 0 20px; }
.hphase .tick { width: 6px; height: 6px; }
.hstep { display: grid; grid-template-columns: 64px 1fr; gap: 20px; padding: 26px 0; border-top: var(--border); }
.hphase + .hstep { border-top: 0; padding-top: 0; }
.hnum { font-family: var(--font-mono); font-size: 13px; color: var(--acc-text); padding-top: 4px; }
.httl { font-size: 18px; font-weight: var(--fw-semibold); letter-spacing: -.01em; }
.hbody { font-size: var(--fs-sm); line-height: var(--lh-body); color: var(--mut); margin-top: 6px; max-width: 620px; }

/* evidence list — project-name link + quote, one line each, all equal weight.
   Left hairline rule keeps evidence distinct from body. */
.hev { margin-top: 14px; border-left: 2px solid var(--ln); padding-left: 15px; display: flex; flex-direction: column; gap: 10px; max-width: 620px; }
.hln { display: block; background: none; border: 0; padding: 0; text-align: left; cursor: pointer; font-family: inherit; color: inherit; text-decoration: none; font-size: var(--fs-sm); line-height: var(--lh-snug); }
.hpj { font-family: var(--font-mono); font-size: 12px; color: var(--acc-text); margin-right: 4px; white-space: nowrap; }
.hq { color: var(--ink); font-weight: var(--fw-medium); }
.hln:hover .hpj, .hln:focus-visible .hpj { text-decoration: underline; text-underline-offset: 3px; }

/* principles strip — the belief layer, demoted beneath the loop */
.hprin { margin-top: 36px; }
.hprin-head { font-family: var(--font-mono); font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--mut); margin-bottom: 14px; }
.hprin-list { display: flex; flex-wrap: wrap; gap: 10px 28px; }
.hpit { font-size: var(--fs-sm); color: var(--mut); }
.hpit .pidx { margin-right: 6px; }

@media (max-width: 700px) {
  .hstep { grid-template-columns: 40px 1fr; gap: 14px; padding: 22px 0; }
  .hprin-list { gap: 8px 20px; }
}

/* legacy principle-card grid (still used by testimonial + styleguide) */
.pgrid2 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.pcard { padding: 26px; display: flex; flex-direction: column; gap: 0; }
.pidx { font-family: var(--font-mono); font-size: 12px; color: var(--acc-text); }
.pttl { font-size: 18px; font-weight: var(--fw-semibold); margin: 12px 0 0; letter-spacing: -.01em; }
.pbody { font-size: var(--fs-sm); line-height: var(--lh-body); color: var(--mut); margin-top: 8px; }

@media (max-width: 960px) {
  .pgrid2 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 700px) {
  .pgrid2 { grid-template-columns: 1fr; }
}

/* touch devices — hover can't signal tappability, so the project link keeps a
   persistent underline and the row gives press feedback */
@media (hover: none), (pointer: coarse) {
  .hpj { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
  .hln:active { background: var(--soft); box-shadow: -8px 0 0 var(--soft), 8px 0 0 var(--soft); }
}

/* ---- sticky phase rail (audit V-1b): two-column grid on desktop — loop
   left, a sticky mini-map of the 4 phases + 7 moves right. philosophy.js
   highlights the current step and the rail doubles as jump nav. ---- */
.phi-grid { display: grid; grid-template-columns: minmax(0,1fr) 360px; gap: 64px; align-items: start; }
.phi-main { min-width: 0; }
.hstep { scroll-margin-top: 90px; }
.hrail { position: sticky; top: 96px; border-left: var(--border); padding-left: 20px; }
.hrail .rphase { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--mut); margin: 18px 0 8px; }
.hrail .rphase:first-child { margin-top: 0; }
.hrail a { display: block; font-size: 13.5px; color: var(--mut); text-decoration: none; padding: 3px 0; }
.hrail a:hover { color: var(--ink); }
.hrail a .rn { font-family: var(--font-mono); font-size: 11px; margin-right: 8px; }
.hrail a.on { color: var(--ink); font-weight: var(--fw-medium); }
.hrail a.on .rn { color: var(--acc-text); }
@media (max-width: 960px) {
  .phi-grid { grid-template-columns: 1fr; }
  .hrail { display: none; }
}

/* ---- mobile receipt collapse (audit V-8): ≤700px each step shows its
   curated first receipt; philosophy.js marks the rest .extra and injects
   a .hmore toggle. Desktop always shows all receipts. ---- */
.hmore { display: none; background: none; border: 0; padding: 0; cursor: pointer; font-family: var(--font-mono);
         font-size: 12px; letter-spacing: .06em; color: var(--mut); text-align: left; }
.hmore .pm { color: var(--acc-text); }
.hmore:hover, .hmore:focus-visible { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 700px) {
  .hev .hln.extra { display: none; }
  .hev.exp .hln.extra { display: block; }
  .hmore { display: block; }
}


/* ══════════════════════════════════════════════════════════════════
   VISUAL REFRESH (20 Jul 2026) — the loop drawn as a dial: sticky column (desktop) + sticky phase bar (mobile)
   ══════════════════════════════════════════════════════════════════ */
#philosophy .loopv2, #philosophy .loopv2 * { box-sizing: border-box; }
#philosophy .fl-svg { display: block; width: 100%; height: auto; margin: 0 auto; overflow: visible; }
#philosophy .fl-dcap { font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--mut); text-align: center; margin: var(--sp-sm) 0 0; }
#philosophy .arc { fill: none; stroke: var(--mut); stroke-width: 1.6; stroke-linecap: round; opacity: .55; transition: stroke var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease); }
#philosophy .arc.visited { stroke: var(--ink); opacity: .85; }
#philosophy .arc.active { stroke: var(--acc); opacity: 1; }
#philosophy .node-hit { fill: transparent; cursor: pointer; }
#philosophy .node-dot { fill: var(--bg); stroke: var(--mut); stroke-width: 1.6; transition: stroke var(--dur-fast) var(--ease), stroke-width var(--dur-fast) var(--ease); }
#philosophy .node-num { font-family: var(--font-mono); font-size: 10px; font-weight: var(--fw-medium); fill: var(--mut); text-anchor: middle; dominant-baseline: central; transition: fill var(--dur-fast) var(--ease); }
#philosophy .node.visited .node-dot { stroke: var(--ink); }
#philosophy .node.visited .node-num { fill: var(--ink); }
#philosophy .node.active .node-dot { stroke: var(--acc); stroke-width: 2; }
#philosophy .node.active .node-num { fill: var(--acc); }
#philosophy .node:focus-visible { outline: none; }
#philosophy .node:focus-visible .node-dot { stroke: var(--acc); stroke-width: 2.4; }
#philosophy .arc-label { font-family: var(--font-mono); font-size: 17.5px; font-weight: var(--fw-medium); letter-spacing: .04em; fill: var(--ink); opacity: 1; transition: fill var(--dur-fast) var(--ease); }
#philosophy .arc-label.active { fill: var(--acc); }
#philosophy .dir-chev { fill: none; stroke: var(--mut); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
#philosophy .loopback, #philosophy .loopback-head { fill: none; stroke: var(--acc); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
#philosophy .fl-center { font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; fill: var(--ink); text-anchor: middle; }
#philosophy .fl-center tspan.eq { fill: var(--acc); }
#philosophy .arc { stroke-dasharray: 1; stroke-dashoffset: 1; }
#philosophy .fl-svg.drawn .arc { stroke-dashoffset: 0; transition: stroke-dashoffset 1s var(--ease), stroke var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease); }
#philosophy .fl-fade { opacity: 0; transition: opacity .5s var(--ease) .35s; }
#philosophy .fl-svg.drawn .fl-fade { opacity: 1; }
#philosophy .loopback, #philosophy .loopback-head { opacity: 0; transition: opacity .4s var(--ease) .8s; }
#philosophy .fl-svg.drawn .loopback, #philosophy .fl-svg.drawn .loopback-head { opacity: 1; }
#philosophy .fl-svg.drawn .arc-label.active { opacity: 1; }
#philosophy .fl-head { margin-bottom: var(--sp-lg); }
#philosophy .fl-steps { display: flex; flex-direction: column; }
#philosophy .fl-step { display: grid; grid-template-columns: 40px 1fr; column-gap: var(--sp-md); padding: var(--sp-lg) 0; min-height: 140px; align-content: center; border-top: var(--border); transition: opacity var(--dur-fast) var(--ease); }
#philosophy .fl-step:first-child { border-top: none; }
#philosophy .fl-idx { font-family: var(--font-mono); font-size: 14px; font-weight: var(--fw-medium); color: var(--mut); padding-top: 3px; }
#philosophy .fl-phase { font-family: var(--font-mono); font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--acc-text); margin: 0 0 6px; }
#philosophy .fl-ttl { font-size: var(--fs-lg); font-weight: var(--fw-semibold); line-height: var(--lh-snug); margin: 0; }
#philosophy .fl-desc { font-size: var(--fs-sm); color: var(--mut); line-height: var(--lh-body); margin: 8px 0 0; max-width: 46ch; }
#philosophy .loopv2.spy-ready .fl-step { opacity: .5; }
#philosophy .loopv2.spy-ready .fl-step.active { opacity: 1; }
#philosophy .fl-step.active .fl-idx { color: var(--acc); }
@media (prefers-reduced-motion: reduce) {
  #philosophy .arc { stroke-dashoffset: 0 !important; transition: stroke var(--dur-fast) var(--ease); }
#philosophy .fl-fade, #philosophy .loopback, #philosophy .loopback-head { opacity: 1 !important; transition: none; }
}
/* desktop: sticky dial in the right column, 7 steps left */
#philosophy .loopv2-desk .fl-grid { display: grid; gap: var(--sp-xl); align-items: start; }
#philosophy .loopv2-desk .fl-dial { margin-bottom: var(--sp-lg); }
#philosophy .loopv2-desk .fl-svg { max-width: 360px; }
@media (min-width: 1000px) {
  #philosophy .loopv2-desk .fl-grid { grid-template-columns: 1fr 360px; gap: 64px; }
#philosophy .loopv2-desk .fl-dial { order: 2; margin-bottom: 0; position: sticky; top: 84px; align-self: start; }
}
/* mobile stacked instance */
#philosophy .loopv2-stack { max-width: 640px; margin: 0 auto; }
#philosophy .loopv2-stack .fl-eyebrow { display: flex; align-items: center; gap: 8px; font-family: var(--font-mono); font-size: 10px; letter-spacing: .14em; margin: 0 0 10px; }
#philosophy .loopv2-stack .fl-eyebrow .qw { color: var(--acc); }
#philosophy .loopv2-stack .fl-eyebrow .tk { width: 6px; height: 6px; background: var(--acc); flex: none; }
#philosophy .loopv2-stack .fl-h2 { font-size: 21px; line-height: 1.2; font-weight: var(--fw-semibold); letter-spacing: var(--tracking-tight); margin: 0; max-width: 20ch; }
#philosophy .loopv2-stack .fl-lead { font-size: 13.5px; line-height: 1.55; color: var(--mut); margin: 10px 0 0; }
#philosophy .loopv2-stack .fl-dial { margin: 6px 0 14px; }
#philosophy .loopv2-stack .fl-svg { max-width: 300px; }
#philosophy .loopv2-stack .fl-step { grid-template-columns: 34px 1fr; column-gap: 14px; padding: 20px 0; min-height: 128px; }
#philosophy .loopv2-stack .fl-idx { font-size: 13px; }
#philosophy .loopv2-stack .fl-ttl { font-size: 16px; }
#philosophy .loopv2-stack .fl-desc { font-size: 13px; }
/* H2 sticky hybrid bar: phase name left, 38px mini-dial right */
#philosophy .lbar { position: sticky; top: 60px; z-index: 20; display: flex; align-items: center; justify-content: space-between; gap: 12px; height: 48px; padding: 0 12px 0 4px; background: var(--bg); border-bottom: var(--border); margin: 0 0 6px; cursor: pointer; opacity: 0; transform: translateY(-6px); transition: opacity .28s var(--ease), transform .28s var(--ease); }
#philosophy .lbar.live { opacity: 1; transform: none; }
#philosophy .lbar:focus-visible { outline: 2px solid var(--acc); outline-offset: -2px; }
#philosophy .lbar-left { display: flex; flex-direction: column; justify-content: center; min-width: 0; padding-left: 10px; }
#philosophy .lbar-name { font-family: var(--font-mono); font-size: 14px; font-weight: var(--fw-semibold); letter-spacing: .04em; color: var(--ink); line-height: 1.15; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#philosophy .lbar-right { flex: none; display: flex; align-items: center; padding-right: 4px; }
#philosophy .lbar-right svg { display: block; overflow: visible; width: 36px; height: 36px; }
#philosophy .lbar-tickcircle { fill: none; stroke: var(--mut); stroke-width: 1.1; opacity: .3; }
#philosophy .lbar-tk { fill: var(--bg); stroke: var(--mut); stroke-width: 1.2; transition: fill var(--dur-fast) var(--ease), stroke var(--dur-fast) var(--ease); }
#philosophy .lbar-tk.visited { fill: var(--ink); stroke: var(--ink); }
#philosophy .lbar-tk.active { fill: var(--acc); stroke: var(--acc); }
@media (prefers-reduced-motion: reduce) {
  #philosophy .lbar { transition: none; }
#philosophy .lbar-tk { transition: none; }
}
/* which instance shows at which width (ON) */
@media (max-width: 999px) { #philosophy .loop-inst-desk { display: none; }
}
@media (min-width: 1000px) { #philosophy .loop-inst-mobile { display: none; }
}

/* the section already collapses to one column ≤900px — hide the dial there
   rather than stacking a 240px graphic above seven long steps */


/* the bar is desktop-irrelevant; it only appears once the rail is gone */
#philosophy .lbar { display: none; }
@media (max-width: 900px) {
  #philosophy .lbar { display: flex; opacity: 1; transform: none; }
  #philosophy .lbar.live { opacity: 1; transform: none; }
}
#philosophy .lbar-txt { display: flex; flex-direction: column; gap: 2px; line-height: 1.2; padding-left: 8px; }
#philosophy .lbar-phase { font-size: 13.5px; font-weight: var(--fw-semibold); color: var(--ink); }
#philosophy .lbar-step { font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em; color: var(--mut); }
#philosophy .lbar-ring { flex-shrink: 0; }

/* ══════════════════════════════════════════════════════════════════
   VISUAL REFRESH (21 Jul 2026) — How I Think dial, matched to test-final
   Desktop: a large (360px) sticky dial fills the grid's right column, in
   place of the old text index. Mobile (≤960px): the dial shows once at the
   top of the section and a sticky phase bar tracks progress below it.
   Two dial instances (desk + mobile) are driven by one spy in
   philosophy-loop.js; the seven real steps and their case-study receipts
   are untouched.
   ══════════════════════════════════════════════════════════════════ */
#philosophy .phi-dialcol { position: sticky; top: 84px; align-self: start; }
#philosophy .phi-dial .fl-svg { display: block; width: 100%; height: auto; overflow: visible; margin: 0 auto; }
#philosophy .phi-dial-desk .fl-svg { max-width: 360px; }
#philosophy .fl-dcap { font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--mut); text-align: center; margin: var(--sp-sm) 0 0; }
#philosophy .phi-dial .node { cursor: pointer; }
#philosophy .phi-dial .node:focus-visible { outline: none; }
#philosophy .phi-dial .node:focus-visible .node-dot { stroke: var(--acc); stroke-width: 2.4; }

#philosophy .phi-dial-mobile { display: none; }
@media (max-width: 960px) {
  #philosophy .phi-dialcol { display: none; }
  #philosophy .phi-dial-mobile { display: block; max-width: 320px; margin: 8px auto 4px; }
  #philosophy .phi-dial-mobile .fl-svg { max-width: 320px; }
}

/* ── VISUAL REFRESH (21 Jul 2026) — the step you're reading lights up ──
   The active step sits at full strength while the other six recede, so the
   dial and the prose agree on where you are. Gated on .spy-ready (added by
   philosophy-loop.js) so before JS — or if it never runs — every step is at
   full opacity and nothing is dimmed. Unlike the reference version, a step
   also un-dims on hover/focus-within: ours carry the quote-receipt buttons,
   and reaching for one should bring its step forward rather than leave you
   clicking into faded text. */
#philosophy .hloop.spy-ready .hstep { opacity: .5; transition: opacity .3s var(--ease); }
#philosophy .hloop.spy-ready .hstep.active,
#philosophy .hloop.spy-ready .hstep:hover,
#philosophy .hloop.spy-ready .hstep:focus-within { opacity: 1; }
#philosophy .hloop.spy-ready .hstep.active .hnum { color: var(--acc); }
@media (prefers-reduced-motion: reduce) {
  #philosophy .hloop.spy-ready .hstep { transition: none; }
}

/* ── assets/components/testimonial/testimonial.css ── */
/* testimonial — editorial recommendation cards ("What others say").
   Each card (.ec) = a big rotated double quote-mark WATERMARK behind the text
   (faint brand-orange fill + near-invisible gray outline), an ACCENT context bar
   naming what the person is vouching for, an italic quote body, a dashed rule,
   then name (brand accent) + role/relationship. Promoted from
   test-testimonials-editorial.html (user-approved iteration, 2026-07-12).
   Markup needs a one-time <svg><defs><path id="qpath">…</defs> in the page;
   each card carries an .ec-mark <svg> with four <use href="#qpath"> layers. */

:root { --ec-qfill: #F9E2DB; --ec-qline: #E7E5DF; }
/* Dark: the old #33221D was a warm brown that read as a muddy blob on the near-black
   card rather than a watermark. A watermark should be a faint LIFT of the surface it
   sits on, so these are neutral tints of --ink instead of a colour of their own. */
:root.dark { --ec-qfill: color-mix(in srgb, var(--ink) 6%, transparent);
             --ec-qline: color-mix(in srgb, var(--ink) 11%, transparent); }

/* items stretch (grid default) so both cards in a row share the taller card's
   height; the attribution block is pushed to the card bottom via .ec-rule's
   margin-top:auto, so stretched cards don't end in floating whitespace */
.ec-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 28px; }

.ec {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--ln);
  border-radius: 4px;
  padding: 40px 44px 34px;
  box-shadow: 0 2px 4px rgba(20,20,18,.03), 0 14px 34px rgba(20,20,18,.05);
}
:root.dark .ec { box-shadow: 0 14px 34px rgba(0,0,0,.28); }

/* two half-size glyphs side by side, rotated 180° so they read as an OPENING
   mark — a quiet watermark that never competes with the text */
.ec-mark { position: absolute; top: 10px; left: 20px; width: 150px; height: auto; z-index: 0; pointer-events: none; transform: rotate(180deg); }
.ec-mark .q-fill { fill: var(--ec-qfill); }
.ec-mark .q-line { fill: none; stroke: var(--ec-qline); stroke-width: 4; stroke-linejoin: miter; }

/* text sits over the glyph */
.ec-context, .ec-quote, .ec-rule, .ec-name, .ec-role { position: relative; z-index: 1; }

/* the bar was gold (#E0A63C) until 22 Jul. It was the only gold on the entire site —
   an orphan colour reads as an accident, and on the dark card it was the loudest thing
   present. In the accent it bookends the card with the (accent) name at the bottom and
   the pink quote watermark, which is already a tint of the same red. */
.ec-context { border-left: 3px solid var(--acc); padding-left: 18px; margin: 0 0 26px; font-style: italic; font-weight: var(--fw-semibold); font-size: 20px; line-height: 1.34; letter-spacing: -.01em; color: var(--ink); }

.ec-quote { margin-bottom: 28px; }
.ec-quote p { font-style: italic; font-size: 17px; line-height: 1.72; color: var(--ink); margin: 0 0 14px; }
.ec-quote p:last-child { margin-bottom: 0; }
.ec-quote ul { font-style: italic; font-size: 17px; line-height: 1.6; color: var(--ink); margin: 0 0 14px; padding-left: 22px; display: flex; flex-direction: column; gap: 6px; }

/* margin-top:auto pins the attribution block to the card bottom when the card
   is stretched taller than its content (the 28px minimum gap lives on .ec-quote) */
.ec-rule { border-top: 1px dashed var(--ln); margin: auto 0 20px; }

.ec-name { font-weight: var(--fw-bold); font-size: 15px; letter-spacing: -.01em; color: var(--acc-text); }
.ec-role { font-size: 13px; color: var(--mut); margin-top: 5px; line-height: 1.5; }

@media (max-width: 760px) {
  .ec-grid { grid-template-columns: 1fr; }
  .ec { padding: 32px 26px 28px; }
  .ec-context { font-size: 18px; }
}

/* ── VISUAL REFRESH (21 Jul 2026) — verify who said it ──
   Name + role on the left of the attribution row, a quiet LinkedIn button on the
   right linking to that person's profile, so a reader can check the person is real
   and that their stated title matches. Mono rather than LinkedIn blue (a second
   brand colour would be the loudest thing in the section, and the design system
   allows one accent) and rather than accent red (.ec-name is already accent — red
   buttons beside red names double up). Same language as .btn2 and the timeline marks. */
.ec-foot { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.ec-who { min-width: 0; }
.ec-li {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 32px; height: 32px; margin-top: 2px;                 /* 32px = comfortable tap target */
  border: var(--border); color: var(--mut); text-decoration: none;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}
.ec-li:hover { color: var(--ink); border-color: var(--ink); background: var(--soft); }
.ec-li:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }
.ec-li .li-mark { width: 15px; height: 15px; fill: currentColor; flex: none; }
@media (prefers-reduced-motion: reduce) { .ec-li { transition: none; } }

/* ── assets/components/beyond/beyond.css ── */
/* beyond — "beyond work" 2-up grid: mono heading, recommendation list,
   note text. Composes with .card. */
.beyond { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
.bcard { padding: 26px; }
.bhead { font-family: var(--font-mono); font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--mut); }
.blist { list-style: none; padding: 0; margin: 14px 0 0; display: flex; flex-direction: column; gap: 10px; }
.bnote { font-size: 14px; line-height: var(--lh-body); color: var(--mut); margin: 14px 0 0; }
.bnote .fw6 { color: var(--ink); }

/* decorative motif inside a bcard (e.g. the Family ID tag) — themes via tokens.
   .brow lays text + motif side by side (hero echo: words left, ID card right);
   stacks on very narrow cards. */
.brow { display: flex; gap: 18px; align-items: flex-start; }
.brow .bnote { flex: 1; }
.bmotif { display: block; flex: none; margin: 14px 0 0; max-width: 100%; }
@media (max-width: 480px) {
  .brow { flex-direction: column; }
  .brow .bmotif { margin-top: 4px; }
}

/* CTA that opens the "full list" modal (reuses the modal component via data-modal) */
.bmore { margin-top: 16px; background: none; border: none; padding: 0; cursor: pointer; font-family: var(--font-mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--mut); display: inline-flex; align-items: center; gap: 6px; }
.bmore:hover { color: var(--acc-text); }

@media (max-width: 700px) {
  /* single column; the two recommendation cards share a row height so the
     stack reads as a matched pair (family/NGO stay content-sized) */
  .beyond { grid-template-columns: minmax(0, 1fr); grid-template-rows: repeat(2, 1fr) auto auto; }
}

/* ── cover treatments (O6): anime as a poster rail, books as a library ledger.
   Covers are self-hosted WebP in assets/img/covers/ over a typographic
   fallback, so a missing file degrades to styled type rather than a gap.
   Full colour at rest everywhere — hover adds motion only. ── */

/* the cover primitive: type underneath, image on top */
.bc-cover { position: relative; overflow: hidden; background: var(--soft); border: var(--border-ink); aspect-ratio: 2 / 3; }
.bc-cover img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 2; }
.bc-cvt { position: absolute; inset: 0; padding: 9%; display: flex; flex-direction: column; justify-content: space-between; z-index: 1; }
.bc-cvt b { font-size: 11px; font-weight: var(--fw-bold); line-height: 1.15; }
.bc-cvt i { font-style: normal; font-family: var(--font-mono); font-size: 8px; text-transform: uppercase; letter-spacing: .06em; color: var(--mut); }
.bc-cover::after { content: ""; position: absolute; right: 7px; bottom: 7px; width: 5px; height: 5px; background: var(--acc); z-index: 3; }

/* anime — 3-up rail */
.bc-rail { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; margin-top: 16px; }
.bc-item { min-width: 0; }
.bc-item .bc-cover { transition: transform 200ms ease; }
.bc-item:hover .bc-cover { transform: translateY(-4px); }
.bc-ttl { font-size: 13px; font-weight: var(--fw-semibold); margin: 10px 0 0; line-height: 1.3; }
.bc-why { font-size: 12px; line-height: var(--lh-snug); color: var(--mut); margin: 4px 0 0; }

/* books — ledger rows */
.bc-ledger { margin-top: 14px; }
.bc-row { display: grid; grid-template-columns: 40px 52px minmax(0, 1fr); gap: 14px; align-items: start; padding: 14px 0; border-top: var(--border); }
.bc-ledger .bc-row:first-child { border-top: 0; padding-top: 4px; }
.bc-idx { font-family: var(--font-mono); font-size: 11.5px; color: var(--acc-text); padding-top: 2px; }
.bc-row .bc-cover { transition: transform 200ms ease; }
.bc-row:hover .bc-cover { transform: rotate(-3deg) scale(1.04); }
.bc-row:hover .bc-ttl { text-decoration: underline; text-decoration-color: var(--acc); text-underline-offset: 3px; }
.bc-row .bc-ttl { margin: 0; font-size: 14px; }
.bc-meta { font-family: var(--font-mono); font-size: 9.5px; text-transform: uppercase; letter-spacing: .12em; color: var(--mut); margin: 4px 0 0; }

/* modal: anime poster wall */
.bc-wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 12px; margin-top: 8px; }
.bc-poster { min-width: 0; }
.bc-poster .bc-cover { transition: transform 200ms ease; }
.bc-poster:hover .bc-cover { transform: scale(1.03); }
.bc-cap { font-family: var(--font-mono); font-size: 9px; line-height: 1.4; text-transform: uppercase; letter-spacing: .06em; color: var(--mut); margin: 6px 0 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* modal: books catalogue */
.bc-group { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: .14em; color: var(--acc-text); margin: 22px 0 2px; }
.bc-group:first-of-type { margin-top: 6px; }
.bc-cat .bc-row { grid-template-columns: 40px 40px minmax(0, 1fr); }
.bc-cat .bc-cvt { padding: 7%; }
.bc-cat .bc-cvt b { font-size: 7px; }
.bc-cat .bc-cvt i { display: none; }
.bc-auth { font-family: var(--font-mono); font-size: 9.5px; text-transform: uppercase; letter-spacing: .1em; color: var(--mut); margin: 3px 0 0; }

@media (prefers-reduced-motion: reduce) {
  .bc-item .bc-cover, .bc-row .bc-cover, .bc-poster .bc-cover { transition: none; }
}
@media (max-width: 700px) {
  /* rail becomes a swipeable snap track */
  /* proximity, not mandatory: mandatory yanks the rail to a poster edge on every
     swipe, which reads as sharp. proximity keeps the tidy alignment only when the
     thumb already stops near one, so momentum scrolling stays free. */
  .bc-rail { display: flex; overflow-x: auto; gap: 12px; scroll-snap-type: x proximity; scroll-padding-left: 0; overscroll-behavior-x: contain; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .bc-rail::-webkit-scrollbar { display: none; }
  /* 2.5 posters visible: 2 full + 2 gaps + a half, measured against the
     card (not the viewport) so it holds at any phone width */
  .bc-item { flex: 0 0 calc((100% - 24px) / 2.5); scroll-snap-align: start; }
  .bc-wall { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 10px; }
}

/* ── assets/components/contact/contact.css ── */
/* contact — closing section: oversized headline, CTA row, labelled detail list. */
.contact { padding: 110px 0; }
.cbig { font-size: var(--fs-contact); font-weight: var(--fw-bold); letter-spacing: -.03em; line-height: 1; margin: 18px 0 0; }
.crow { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 38px; align-items: center; }
.cdetails { display: flex; flex-wrap: wrap; gap: 28px; margin-top: 44px; }
.cd { min-width: 0; }
.cdl { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--mut); }
.cdv { font-size: 14.5px; font-weight: var(--fw-medium); margin-top: 6px; word-break: break-word; }
.cdv a { text-decoration: none; }
.cdv a:hover { color: var(--acc); }

/* ── assets/components/footer/footer.css ── */
/* footer — closing bar: logo (from nav/), quick links, mono signature. */
.foot { border-top: var(--border); padding: 28px 0; }
.footin { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; justify-content: space-between; }
.fsig { font-family: var(--font-mono); font-size: 11.5px; color: var(--mut); }
.flnks { display: flex; gap: 18px; }

/* ── assets/components/dock/dock.css ── */
/* dock — mobile quick-contact bar (Email · LinkedIn · Call), fixed to the
   bottom edge, mobile only (≤700px, same breakpoint where the nav CTA hides).
   dock.js adds .show once the hero scrolls out of view — the hero's own CTAs
   cover the top of the page, so the dock never competes with them. Hidden
   while the burger menu is open (body:has guard); the case-study modal covers
   it anyway (z-modal 300 > z-nav 50). */
.dock { position: fixed; bottom: 0; left: 0; right: 0; z-index: var(--z-nav); display: none; border-top: var(--border); background: color-mix(in srgb, var(--bg) 94%, transparent); backdrop-filter: blur(8px); padding-bottom: env(safe-area-inset-bottom); }
.dk { flex: 1; display: flex; align-items: center; justify-content: center; gap: 7px; padding: 13px 0; font-family: var(--font-mono); font-size: 12px; font-weight: var(--fw-medium); text-decoration: none; color: var(--ink); }
.dk + .dk { border-left: var(--border); }
.dk svg { width: 14px; height: 14px; }
.dk-mail { background: var(--ink); color: var(--bg); }

@media (max-width: 700px) {
  .dock { display: flex; transform: translateY(102%); transition: transform .28s ease; }
  .dock.show { transform: translateY(0); }
  body:has(.mmenu.open) .dock { display: none; }
  /* clear the dock at the end of the page so contact details stay readable */
  .foot { padding-bottom: 76px; }
}
@media (prefers-reduced-motion: reduce) {
  .dock { transition: none; }
}

/* ── assets/components/chapters/chapters.css ── */
/* chapters — section separation + wayfinding (from test-section-combo.html).
   Alternating sections (.sec-alt) sit on a deeper surface tint so chapter
   boundaries read as edges; a 2px accent progress bar under the nav and a
   "You are in <section> NN/NN" chip give orientation. The chip appears with
   body.past-hero (same signal as the dock), hides while the burger menu is
   open, and drops to bottom-right on mobile — clear of the contact dock. */

/* The band always moves AWAY from the card, never toward it, so a card is
   the lifted surface on every section — one rule the reader can learn.
   Light does that by darkening. Dark used to mix toward --soft, which is the
   same direction --card sits in: the band resolved to #1A1A17 and so does
   --card, contrast 1.000 — the card was held up by its border alone on
   #ai, #timeline, social proof and #contact. Mixing toward black instead
   restores the lift (1.136) without touching the card itself. */
:root { --tint: color-mix(in srgb, var(--ink) 3%, var(--soft)); }  /* light needs the deeper shade */
html.dark { --tint: color-mix(in srgb, #000 42%, var(--bg)); }

.sec-alt { background: var(--tint); }

.pbar { position: fixed; top: 60px; left: 0; height: 2px; width: 0; background: var(--acc); z-index: var(--z-nav); }

.seclabel { position: fixed; top: 74px; right: 24px; z-index: var(--z-nav); display: none; align-items: baseline; gap: 12px; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--mut); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(6px); border: var(--border); padding: 6px 10px; }
.seclabel b { color: var(--ink); font-weight: var(--fw-medium); }
.seclabel .num { color: var(--acc-text); font-weight: var(--fw-medium); margin-left: auto; }
body.past-hero .seclabel { display: inline-flex; }
body:has(.mmenu.open) .seclabel { display: none; }

@media (max-width: 700px) {
  .seclabel { top: auto; bottom: calc(58px + env(safe-area-inset-bottom)); right: 12px; }  /* above the dock */
}

/* ── assets/components/modal/modal.css ── */
/* modal — project-preview overlay. Clicking a [data-modal="key"] trigger opens a
   dialog whose reading column ("doc") is cloned from <template id="tmpl-key">.
   Same visual language as the site: card surface, ink hairline, sharp corners,
   print offset shadow. The window is intentionally WIDER than the doc column
   inside it, giving calm side gutters. Behavior lives in modal.js.
   Dims + scroll-locks the page behind. Sharp corners everywhere (brand). */

/* dimming layer — the page behind gets slightly darker */
.modal-overlay {
  position: fixed; inset: 0; z-index: var(--z-modal);
  display: flex; align-items: flex-start; justify-content: center;
  padding: clamp(16px, 5vh, 64px) var(--gutter);
  background: rgba(10, 10, 8, .46);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-fast) var(--ease), visibility var(--dur-fast) var(--ease);
}
:root.dark .modal-overlay { background: rgba(0, 0, 0, .62); }
.modal-overlay.open { opacity: 1; visibility: visible; }

/* the window — wider than the doc column it holds */
.modal-window {
  position: relative;
  width: 100%; max-width: 920px; max-height: 90vh;
  display: flex; flex-direction: column;
  background: var(--card); border: var(--border-ink);
  box-shadow: 10px 10px 0 var(--ln);
  transform: translateY(12px) scale(.985);
  transition: transform var(--dur-fast) var(--ease);
}
.modal-overlay.open .modal-window { transform: none; }

/* sticky header: eyebrow + title on the left, cross button on the right */
.modal-bar {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px 20px; background: var(--card); border-bottom: var(--border);
}
.modal-eyebrow { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--mut); }
.modal-title { font-size: 16px; font-weight: var(--fw-semibold); letter-spacing: -.01em; margin-top: 3px; }

/* the cross button */
.modal-close {
  flex: none; width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: var(--border); color: var(--ink); cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.modal-close:hover { background: var(--acc); border-color: var(--acc); color: #fff; }
.modal-close svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; }

/* scroll area */
.modal-scroll { overflow-y: auto; overscroll-behavior: contain; }

/* the doc — a narrower reading column, centered, so the window is
   visibly wider than the page it holds */
.modal-doc { max-width: 660px; margin: 0 auto; padding: 40px 32px 56px; }
.modal-doc .m-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.modal-doc h1 { font-size: clamp(26px, 4vw, 34px); font-weight: var(--fw-bold); letter-spacing: -.02em; line-height: 1.1; margin: 0 0 10px; }
/* .m-title = the case-study display title, an h2 so the page keeps exactly
   one h1 (the hero) while the modal is open — visually identical to h1 */
.modal-doc .m-title { font-size: clamp(26px, 4vw, 34px); font-weight: var(--fw-bold); letter-spacing: -.02em; line-height: 1.1; margin: 0 0 10px; }
.modal-doc .m-sub { font-size: 15px; color: var(--mut); line-height: 1.6; margin: 0 0 6px; }
.modal-doc h2 { font-size: 19px; font-weight: var(--fw-semibold); letter-spacing: -.01em; margin: 34px 0 10px; }
.modal-doc p { font-size: 15px; line-height: 1.65; margin: 0 0 14px; }
.modal-doc ul { margin: 0 0 14px; padding-left: 18px; }
.modal-doc li { font-size: 15px; line-height: 1.6; margin: 0 0 7px; }
.modal-doc strong { font-weight: var(--fw-semibold); }
.modal-doc .acc { color: var(--acc-text); font-weight: var(--fw-medium); }
.modal-doc code { font-family: var(--font-mono); font-size: 12.5px; background: var(--soft); padding: 1px 6px; border: 1px solid var(--ln); }
.modal-doc pre { background: var(--soft); border: var(--border); padding: 14px 16px; overflow-x: auto; margin: 0 0 16px; }
.modal-doc pre code { background: none; border: none; padding: 0; font-size: 12px; line-height: 1.7; white-space: pre; }
.modal-doc blockquote { margin: 0 0 16px; padding: 4px 0 4px 16px; border-left: 2px solid var(--acc); font-size: 15px; line-height: 1.6; }

/* metric grid (same look as the flagship performance cells) */
.modal-metrics { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--ln); border: var(--border); margin: 6px 0 20px; }
.modal-metric { background: var(--card); padding: 16px 18px; }
.modal-metric .m-num { font-family: var(--font-mono); font-size: 22px; font-weight: var(--fw-semibold); color: var(--acc); letter-spacing: -.01em; }
.modal-metric .m-lab { font-size: 12.5px; color: var(--mut); line-height: 1.45; margin-top: 6px; }

.modal-doc .m-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 26px; padding-top: 22px; border-top: var(--border); }

/* figure — a real product artifact (screenshot), framed like the site's cards */
.modal-doc .m-figure { margin: 8px 0 22px; }
.modal-doc .m-figure img { display: block; width: 100%; height: auto; border: var(--border-ink); box-shadow: 8px 8px 0 var(--ln); }
/* phone variant — tall mobile mock shown at phone width, centered */
.modal-doc .m-figure.phone { max-width: 290px; margin-left: auto; margin-right: auto; }
.modal-doc .m-cap { font-family: var(--font-mono); font-size: 11px; letter-spacing: .04em; color: var(--mut); line-height: 1.5; margin: 12px 2px 18px; }

/* doc-authorship / provenance note — signals "I wrote the strategy", not just "ran a test" */
.modal-doc .m-note { display: flex; gap: 12px; background: var(--soft); border: var(--border); border-left: 2px solid var(--acc); padding: 14px 16px; margin: 0 0 18px; }
.modal-doc .m-note .m-note-k { font-family: var(--font-mono); font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--acc-text); flex: none; padding-top: 2px; }
.modal-doc .m-note p { font-size: 13.5px; line-height: 1.55; margin: 0; color: var(--ink); }

/* comparison table — before/after channel shift, experiment phases, etc. */
.modal-doc .m-table { width: 100%; border-collapse: collapse; border: var(--border); margin: 4px 0 20px; font-size: 13px; }
.modal-doc .m-table th, .modal-doc .m-table td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--ln); line-height: 1.4; }
.modal-doc .m-table thead th { font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--mut); font-weight: var(--fw-medium); }
.modal-doc .m-table td + td, .modal-doc .m-table th + th { border-left: 1px solid var(--ln); }
.modal-doc .m-table tbody tr:last-child td { border-bottom: none; }
.modal-doc .m-table .num { font-family: var(--font-mono); }
.modal-doc .m-table .up { color: var(--acc-text); }

/* horizontal bar chart — recovery-decile churn (real data, redrawn clean) */
.modal-doc .m-chart { border: var(--border); padding: 16px 18px; margin: 4px 0 8px; display: grid; gap: 9px; }
.modal-doc .m-row { display: grid; grid-template-columns: 76px 1fr 40px; align-items: center; gap: 10px; }
.modal-doc .m-row .m-k { font-family: var(--font-mono); font-size: 11px; color: var(--mut); }
.modal-doc .m-bar { height: 14px; background: var(--soft); border: 1px solid var(--ln); position: relative; }
.modal-doc .m-bar span { display: block; height: 100%; background: var(--mut); }
.modal-doc .m-row.hot .m-bar span { background: var(--acc); }
.modal-doc .m-row .m-v { font-family: var(--font-mono); font-size: 11.5px; text-align: right; }
.modal-doc .m-row.hot .m-v { color: var(--acc-text); font-weight: var(--fw-semibold); }

/* phased journey — a compact vertical timeline inside the doc */
.modal-doc .m-phases { list-style: none; padding: 0; margin: 6px 0 20px; }
.modal-doc .m-phases li { position: relative; padding: 0 0 20px 22px; margin: 0; border-left: 1px solid var(--ln); }
.modal-doc .m-phases li:last-child { padding-bottom: 0; }
.modal-doc .m-phases li::before { content: ""; position: absolute; left: -5px; top: 3px; width: 9px; height: 9px; background: var(--acc); border: 2px solid var(--card); }
.modal-doc .m-ph-k { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--mut); }
.modal-doc .m-ph-t { font-size: 14.5px; font-weight: var(--fw-semibold); margin: 2px 0 4px; letter-spacing: -.01em; }
.modal-doc .m-ph-t .m-when { font-family: var(--font-mono); font-weight: var(--fw-regular); font-size: 11px; color: var(--mut); margin-left: 8px; letter-spacing: 0; }
.modal-doc .m-phases p { font-size: 13.5px; line-height: 1.55; margin: 0; color: var(--mut); }
.modal-doc .m-phases p .acc { color: var(--acc-text); }

/* affordance on a clickable card */
.card[data-modal] { cursor: pointer; }
.cmore { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--acc-text); margin-top: 16px; display: inline-flex; align-items: center; gap: 6px; }
.card[data-modal]:hover .cmore { text-decoration: underline; text-underline-offset: 3px; }

/* mobile: window fills the screen, gutters collapse */
@media (max-width: 640px) {
  .modal-overlay { padding: 0; }
  .modal-window { max-width: 100%; max-height: 100vh; height: 100%; box-shadow: none; border: none; }
  .modal-doc { padding: 28px 20px 44px; }
  .modal-metrics { grid-template-columns: 1fr; }
  .modal-doc .m-figure img, .modal-doc .m-figure { box-shadow: 5px 5px 0 var(--ln); }
  .modal-doc .m-table { font-size: 12px; }
  .modal-doc .m-table th, .modal-doc .m-table td { padding: 8px 9px; }
  .modal-doc .m-row { grid-template-columns: 64px 1fr 36px; gap: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .modal-overlay, .modal-window { transition: none; }
  .modal-window { transform: none; }
}

/* anchor highlight — receipt deep-links flash the exact proof line.
   Two pulses of a faint accent wash behind a 3px accent bar: the bar says
   WHERE, the wash says WHAT, in one colour rather than two. (Until 22 Jul
   this was a single 1.4s hold of a grey wash, which read as static.)

   The wash is the accent mixed into --card — the modal surface — and NOT
   into `transparent`. It has to be opaque: the -12px wash shadow paints on
   top of the -15px accent shadow, so a translucent wash lets the bar bleed
   through for the full 15px and it reads as a slab instead of a hairline.
   Opaque masks it back to the intended 3px sliver. (The three anchors that
   sit on a --soft element differ by 4/255 — not perceptible.)

   Colour only, no motion, ~1 pulse/sec — an order of magnitude under the
   3-flashes/sec threshold, so it stays acceptable under reduced-motion. */
:root      { --hit-wash: color-mix(in srgb, var(--acc) 10%, var(--card)); }
:root.dark { --hit-wash: color-mix(in srgb, var(--acc) 14%, var(--card)); }

.modal-doc .m-hit { animation: m-hit 2.1s ease-out 1; }
@keyframes m-hit {
  0%   { background: var(--hit-wash); box-shadow: -12px 0 0 var(--hit-wash), 12px 0 0 var(--hit-wash), -15px 0 0 var(--acc); }
  22%  { background: transparent;     box-shadow: -12px 0 0 transparent, 12px 0 0 transparent, -15px 0 0 var(--acc); }
  40%  { background: var(--hit-wash); box-shadow: -12px 0 0 var(--hit-wash), 12px 0 0 var(--hit-wash), -15px 0 0 var(--acc); }
  72%  { background: var(--hit-wash); box-shadow: -12px 0 0 var(--hit-wash), 12px 0 0 var(--hit-wash), -15px 0 0 var(--acc); }
  100% { background: transparent;     box-shadow: -12px 0 0 transparent, 12px 0 0 transparent, -15px 0 0 transparent; }
}


/* ══════════════════════════════════════════════════════════════════
   VISUAL REFRESH (20 Jul 2026) — line-art figures that replaced the ASCII diagrams
   ══════════════════════════════════════════════════════════════════ */
.modal pre { background: var(--soft); border: var(--border); padding: 14px 16px; overflow-x: auto; margin: 0; max-width: 800px; }
.modal pre code { background: none; border: none; padding: 0; font-family: var(--font-mono); font-size: 12px; line-height: 1.7; white-space: pre; color: var(--ink); }
.dg-svgwrap { max-width: 800px; width: 100%; overflow-x: auto; }
.dg-svgwrap svg { display: block; width: 100%; height: auto; }
.modal svg text { font-family: var(--font-mono); }
.dg-svgwrap .bxln, .dg-svgwrap .bxink, .dg-svgwrap .bxacc { fill: var(--card); }
.dg-svgwrap .bxln  { stroke: var(--ln);  stroke-width: 1.6; }
.dg-svgwrap .bxink { stroke: var(--ink); stroke-width: 1.6; }
.dg-svgwrap .bxacc { stroke: var(--acc); stroke-width: 1.8; }
.dg-svgwrap .eln  { stroke: var(--ln);  stroke-width: 1.6; fill: none; stroke-linecap: round; }
.dg-svgwrap .eink { stroke: var(--ink); stroke-width: 1.6; fill: none; stroke-linecap: round; }
.dg-svgwrap .eacc { stroke: var(--acc); stroke-width: 1.6; fill: none; stroke-linecap: round; }
.dg-svgwrap .eaccd{ stroke: var(--acc); stroke-width: 1.4; fill: none; stroke-linecap: round; stroke-dasharray: 3 3; }
.dg-svgwrap .zln  { stroke: var(--ln); stroke-width: 1; }
.dg-svgwrap .dot-acc { fill: var(--acc); }
.dg-svgwrap .dnode   { font-size: 10px;   letter-spacing: .02em; fill: var(--ink); }
.dg-svgwrap .dsub    { font-size: 8.5px;  letter-spacing: .05em; fill: var(--mut); }
.dg-svgwrap .dchip   { font-size: 9.5px;  letter-spacing: .08em; fill: var(--mut); }
.dg-svgwrap .dchip-em{ font-size: 9.5px;  letter-spacing: .08em; fill: var(--ink); }
.dg-svgwrap .dnote   { font-size: 9.5px;  letter-spacing: .02em; fill: var(--mut); }
.dg-svgwrap .dnoteacc{ font-size: 9.5px;  letter-spacing: .02em; fill: var(--acc-text); }
.dg-svgwrap .dzone   { font-size: 9.5px;  letter-spacing: .12em; fill: var(--mut); }
.dg-svgwrap .dacclab { font-size: 9.5px;  letter-spacing: .04em; fill: var(--acc-text); font-weight: var(--fw-medium); }
/* A/B: base = ON (svg shown, pre hidden) */
.dg-svgwrap { display: block; }
.modal pre { display: none; }
@media (max-width: 480px) {
  .dg-svgwrap svg { min-width: 600px; }
}
/* ── assets/css/utilities.css ── */
/* ============================================================
   utilities.css — Small single-purpose helpers
   Loaded LAST so utilities can win specificity ties with components.
   Keep these atomic and rare; prefer component classes for anything
   with more than one declaration.
   ============================================================ */

/* Text */
.mono { font-family: var(--font-mono); }
.mut  { color: var(--mut); }
.acc  { color: var(--acc-text); }
.fw6  { font-weight: var(--fw-semibold); }
.fs11 { font-size: 11px; }
.fs12 { font-size: 12px; }
.fs13 { font-size: 13px; }

/* Layout */
.m0     { margin: 0; }
.posrel { position: relative; }

/* Reveal-on-scroll (JS adds .in when the element enters the viewport).
   Under reduced-motion we never hide content — no opacity:0 rule applies. */
@media (prefers-reduced-motion: no-preference) {
  .rv {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
  }
  .rv.in { opacity: 1; transform: none; }
}
