/* Christopher Miller — portfolio v2
   Tokens and shared type scale.

   Colour rule: Friction and Resolved encode state, never decorate.
   Friction marks the situation on arrival. Resolved marks what changed.
   If either appears anywhere else, it is a bug. */

:root {
  /* Ground was #F5F5F5 (pixel-sampled from the Menowo references) — read as
     flat grey rather than paper. Lifted to an off-white so the page reads
     as a warm neutral instead, and to widen the margin against Ink for
     body text (this only ever raises contrast versus the darker value
     below, never lowers it). Card surface stays pure white, near-black
     text, no visible card border — cards separate from the ground with a
     soft shadow instead. Friction and Resolved are the only hues layered
     on top of this neutral scaffold. */
  --paper: #FFFFFF;
  --ink: #141414;
  --friction: #9E4A2E;
  --resolved: #1F4E46;

  /* Personal brand accent, requested by name (Green Lantern), sampled from
     the "Get in touch" CTA on the live mmmiller.ca site. Deliberate
     exception to the Friction/Resolved-only rule above: scoped to the CTA
     alone (.btn--brand), never used for evidence encoding. */
  --brand: #047857;
  --brand-hover: #035f45;

  /* Deep green ground for the closing footer band, sampled from mmmiller.ca. */
  --footer-green: #0f1f0f;

  /* Derived, not new colours: opacity steps of ink for structure only.
     ink-70 and ink-60 both clear 4.5:1 (WCAG AA, normal text) against Paper
     and Surface. ink-70 (the workhorse secondary body colour) was raised
     from 0.70 to 0.80 alpha to also clear the 7:1 AAA-enhanced threshold;
     ink-60 stays at AA (labels, captions, de-emphasized meta). */
  --ink-70: rgba(20, 20, 20, 0.80);
  --ink-60: rgba(20, 20, 20, 0.62);
  --rule: rgba(20, 20, 20, 0.10);
  --rule-soft: rgba(20, 20, 20, 0.05);

  /* Neutral surface tone, not a fifth meaning-bearing colour. Cards sit on
     this instead of Paper to separate structure from ground, the way the
     Menowo reference uses white panels on a grey page. */
  --surface: #FFFFFF;
  --radius: 10px;
  --radius-sm: 6px;
  --shadow-card: 0 1px 2px rgba(20, 20, 20, 0.04), 0 16px 32px -20px rgba(20, 20, 20, 0.14);

  --serif: "Source Serif 4", Georgia, serif;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --step--2: 0.75rem;
  --step--1: 0.8125rem;
  --step-0: 1.0625rem;
  --step-1: 1.1875rem;
  --step-2: 1.375rem;
  --step-3: clamp(1.5rem, 1.2rem + 1.2vw, 2rem);
  --step-4: clamp(2rem, 1.5rem + 2.4vw, 3.25rem);

  --measure: 33rem;
  --page: 80rem;

  --s1: 0.5rem;
  --s2: 1rem;
  --s3: 1.5rem;
  --s4: 2.5rem;
  --s5: 4rem;
  --s6: 6rem;
  --s7: 9rem;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--step-0);
  line-height: 1.65;
  font-feature-settings: "kern" 1;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.011em;
  margin: 0;
  text-wrap: balance;
}

p { margin: 0 0 var(--s2); max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

a { color: inherit; }

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

/* All figures align in columns. This is why there is no third typeface. */
.num, .stat__figure, table td, time {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.wrap {
  width: 100%;
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: var(--s3);
}

.section { padding-block: var(--s6); }
.section + .section { border-top: 1px solid var(--rule-soft); }

/* The case study header already closes with the meta block's own rule.
   A second divider immediately under it reads as a printing error. */
.section--head + .section { border-top: 0; }

/* A .secline draws its own top rule directly above the eyebrow text. When a
   section opens with one, the outer section-to-section divider above it is
   a second, redundant line with dead space between the two — drop the
   outer line and tighten the top padding now that only one line remains. */
.section:has(> .secline) {
  border-top: 0;
  padding-top: var(--s5);
}

/* Verification, testimony, and the About teaser read as one continuous
   "closing" movement rather than three separate acts — tighter than the
   6rem default so the rhythm quickens toward the footer. */
.section--tight { padding-block: var(--s5); }

/* Section label. Small caps, ink only — labels orient, they do not encode. */
.label {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-60);
  margin: 0 0 var(--s3);
}

.lead {
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--ink-70);
  max-width: var(--measure);
}

/* ---- Menowo-derived layout language ----
   Slash eyebrow, numbered section rule, pill CTA, arrow link, white panel.
   Adopted for shape and rhythm only — colour still obeys the Friction/
   Resolved encoding rule below; none of this introduces a new hue. */

.eyebrow {
  font-size: var(--step--1);
  color: var(--ink-70);
  margin: 0 0 var(--s2);
}

.secline {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  padding-top: var(--s2);
  border-top: 1px solid var(--rule);
  margin-bottom: var(--s3);
}

.secline__idx {
  font-size: var(--step--2);
  color: var(--ink-60);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.secline__cat {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-60);
}

.sech2 {
  font-family: var(--serif);
  font-size: var(--step-3);
  font-weight: 600;
  max-width: 24ch;
  margin: 0 0 var(--s3);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: var(--step--1);
  font-weight: 500;
  padding: 0.6rem 1.05rem;
  border-radius: 999px;
  border: 1px solid var(--ink);
  text-decoration: none;
  line-height: 1;
}

.btn:hover { background: var(--paper); color: var(--ink); }

.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--rule);
}

.btn--ghost:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--step--1);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 2px;
}

.link-arrow:hover { border-bottom-color: var(--ink); }

.panel {
  background: var(--surface);
  border: 1px solid rgba(20, 20, 20, 0.05);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: var(--s4);
}

/* Typography-led hero, now paired with a portrait. The copy column keeps its
   own max-width so line length stays sane; the photo sits beside it from
   tablet up and stacks below on narrow. */
.hero--type .hero__copy { max-width: 46rem; }

@media (min-width: 56rem) {
  .hero--type .hero__copy h1 { font-size: clamp(2.75rem, 2rem + 3vw, 4.25rem); }
}

.hero__grid {
  display: grid;
  gap: var(--s5);
  align-items: stretch;
}

.hero__photo {
  margin: 0;
  max-width: 22rem;
  position: relative;
}

.hero__photo img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  /* Y pulled down off the ceiling light; X shifted so the gap from each arm
     to the frame edge reads even. */
  object-position: 46% 34%;
  border-radius: var(--radius);
}

@media (min-width: 56rem) {
  .hero__grid {
    grid-template-columns: minmax(0, 1fr) minmax(14rem, 21rem);
    gap: var(--s6);
  }
  /* The copy column sets the row height; the photo stretches to match it, so
     the frame's top lines up with the eyebrow and its bottom reaches the
     "See the work" link. */
  .hero__photo {
    max-width: none;
    align-self: stretch;
    overflow: hidden;
    border-radius: var(--radius);
  }
  /* Rendered ~24px wider than the frame so the subject can be shifted
     horizontally; left:-2px (vs. -12px centred) nudges him ~10px right. */
  .hero__photo img {
    position: absolute;
    top: 0;
    left: -2px;
    width: calc(100% + 24px);
    height: 100%;
    aspect-ratio: auto;
    border-radius: 0;
  }
}

/* ---- State encoding ---- */

.friction { color: var(--friction); }
.resolved { color: var(--resolved); }

.figure {
  font-family: var(--serif);
  font-size: var(--step-3);
  font-weight: 600;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  display: block;
}

.figure__caption {
  font-size: var(--step--1);
  line-height: 1.45;
  color: var(--ink-70);
  margin-top: var(--s1);
  max-width: 22rem;
}

/* ---- Nav ---- */

.nav {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s3);
  row-gap: var(--s2);
  flex-wrap: wrap;
  padding-block: var(--s3);
  border-bottom: 1px solid var(--rule);
}

.nav__name {
  font-family: var(--serif);
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
}

.nav__role {
  display: block;
  font-family: var(--sans);
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--ink-60);
  letter-spacing: 0.01em;
}

.nav__links {
  display: flex;
  gap: var(--s3);
  font-size: var(--step--1);
  padding-top: 0.35rem;
}

.nav__links a {
  text-decoration: none;
  color: var(--ink-70);
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
}

.nav__links a:hover,
.nav__links a[aria-current="page"] {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

/* ---- Evidence table: what was already true / what changed ----
   Mobile-first: stacks into labelled blocks. Becomes a true two-column
   comparison at 48rem, where the side-by-side reading is what makes the
   arrival-to-outcome sequence legible at a glance. */

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

.ledger { width: 100%; border-collapse: collapse; }

.ledger caption { text-align: left; }

.ledger thead { display: none; }

.ledger tr {
  display: block;
  padding-block: var(--s3);
  border-bottom: 1px solid var(--rule-soft);
}

.ledger th, .ledger td { display: block; text-align: left; }

.ledger td {
  font-size: var(--step--1);
  line-height: 1.55;
  padding: 0;
  margin-top: var(--s2);
}

.ledger td::before {
  content: attr(data-k);
  display: block;
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-60);
  margin-bottom: 0.35rem;
}

.ledger .ledger__co {
  font-family: var(--serif);
  font-size: var(--step-1);
  font-weight: 600;
  padding: 0;
}

.ledger .ledger__was { color: var(--friction); }
.ledger .ledger__now { color: var(--resolved); }

@media (min-width: 48rem) {
  .ledger thead { display: table-header-group; }
  .ledger tr { display: table-row; padding-block: 0; border-bottom: 0; }
  .ledger th, .ledger td { display: table-cell; }
  .ledger td::before { display: none; }

  .ledger thead th {
    font-family: var(--sans);
    font-size: var(--step--2);
    font-weight: 600;
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: var(--ink-60);
    padding: 0 var(--s3) var(--s2) 0;
    border-bottom: 1px solid var(--rule);
    vertical-align: bottom;
  }

  .ledger td {
    padding: var(--s3) var(--s3) var(--s3) 0;
    border-bottom: 1px solid var(--rule-soft);
    vertical-align: top;
    margin-top: 0;
  }

  .ledger td:last-child { padding-right: 0; }

  .ledger .ledger__co {
    white-space: nowrap;
    width: 1%;
    padding: var(--s3) var(--s3) var(--s3) 0;
    border-bottom: 1px solid var(--rule-soft);
    vertical-align: top;
  }
}

/* ---- Selected work: white cards, Menowo service-card shape ----
   Number + title + outcome header, full-width artifact, situation, link.
   A thumbnail is still a pointer to evidence, not the evidence itself —
   it is capped to a wide banner ratio rather than left to dominate. */

.work { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s4); }

.work__row {
  position: relative;
  background: var(--surface);
  border: 1px solid rgba(20, 20, 20, 0.05);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: var(--s4);
}

/* Whole-card click target: an invisible anchor stretched over the entire
   .work__row, so visitors can click anywhere on the card instead of
   hunting for "More details." Sits above the card's own content (z-index)
   but below nothing interactive, since headline/media/CTA are now inert
   text rather than nested links (an <a> can't contain another <a>). */
.work__row-link {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: var(--radius);
}

.work__row-link:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 3px;
}

.work__top {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s1) var(--s3);
  margin-bottom: var(--s3);
}

.work__num {
  font-family: var(--sans);
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  color: var(--ink-60);
}

.work__co {
  font-family: var(--serif);
  font-size: var(--step-2);
  font-weight: 600;
  margin: 0;
  flex: 1 1 12rem;
  min-width: 0;
}

.work__co a { text-decoration: none; }
.work__co a:hover { text-decoration: underline; text-underline-offset: 4px; }

.work__outcome {
  font-size: var(--step--1);
  color: var(--resolved);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  margin: 0;
}

.work__artifact {
  margin: 0 0 var(--s3);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

/* Wide banner, deliberately shallow: a placeholder this size still reads as
   a pointer to the artifact, not a stand-in for it. Swap in real artifacts
   and this ratio can loosen. */
.work__artifact .ph { aspect-ratio: 3 / 1; }

.work__artifact img {
  aspect-ratio: 3 / 1;
  width: 100%;
  object-fit: cover;
  display: block;
}

@media (min-width: 48rem) {
  .work__artifact .ph { aspect-ratio: 4 / 1; }
  .work__artifact img { aspect-ratio: 4 / 1; }
}

/* Muted thumbnail for the reserved EA/Frostbite slot: present, but visually
   subordinate to the four live case studies above it. */
.work__artifact--muted img {
  filter: grayscale(1);
  opacity: 0.55;
}

.work__artifact figcaption {
  font-size: var(--step--2);
  color: var(--ink-60);
  padding: var(--s1) 0.1rem 0;
  background: none;
  border-top: 0;
}

.work__situation {
  color: var(--ink-70);
  font-size: var(--step--1);
  max-width: 42rem;
  margin: 0 0 var(--s3);
}

/* ---- Selected work v2 (index-alt3.html) ----
   Studied from mmmiller.ca's own live homepage card: eyebrow (company ·
   category), a real headline distinct from the company name, a hook
   paragraph, tag pills, and a solid CTA button — with the artifact moved
   to a side column instead of a full-width banner on top. Additive on top
   of .work__row, not a replacement — .work__row2-grid only changes layout
   at the desktop breakpoint; mobile still stacks num → content → media in
   natural document order. */

.work__filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-bottom: var(--s4);
}

.work__filter {
  font-family: var(--sans);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--ink-70);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.45rem 1rem;
  cursor: pointer;
}

.work__filter:hover { border-color: var(--ink-60); }

.work__filter.is-active {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.work__row--hidden { display: none; }

/* Category filter row: smaller, quieter than the type tabs above it
   (secondary refinement, not a primary switch), and multi-select — active
   state uses the brand green tint already established for tag pills
   elsewhere, so an active category filter visually echoes the tags it's
   matching against. */
.work__cat-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: var(--step--2);
  color: var(--ink-60);
  margin: 0 0 var(--s2);
}

.work__cat-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s1);
  margin-bottom: var(--s4);
}

.work__cat-divider {
  width: 1px;
  align-self: stretch;
  background: var(--rule);
  margin-inline: var(--s1);
}

.work__cat {
  font-family: var(--sans);
  font-size: var(--step--2);
  font-weight: 500;
  color: var(--ink-70);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.35rem 0.8rem;
  cursor: pointer;
}

.work__cat:hover { border-color: var(--brand); }

.work__cat.is-active {
  background: rgba(4, 120, 87, 0.1);
  color: var(--brand);
  border-color: var(--brand);
  font-weight: 600;
}

/* "All work" is the reset chip at the head of the row: when it is the active
   state (nothing narrowed) it reads as a solid dark pill, distinct from the
   green "narrowed" state of the category chips. */
.work__cat--all { font-weight: 600; }
.work__cat--all.is-active {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.work__row2-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s3);
}

@media (min-width: 64rem) {
  .work__row2-grid {
    grid-template-columns: 1fr 24rem;
    gap: var(--s4);
    align-items: start;
  }
}

.work__eyebrow2 {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-60);
  margin: 0 0 var(--s2);
}

/* Two-tone headline: the plain-language name of the work in full-strength
   ink, then a muted continuation clause that completes the sentence with
   what it actually did — one visual unit instead of a title plus a
   separate prose paragraph underneath it. */
.work__headline2 {
  font-family: var(--serif);
  font-size: var(--step-3);
  font-weight: 700;
  line-height: 1.2;
  margin: 0 0 var(--s3);
}

.work__headline2 a { text-decoration: none; color: inherit; }
.work__headline2 a:hover { text-decoration: underline; text-underline-offset: 5px; }

.work__headline2 .muted { color: var(--ink-60); font-weight: 500; }

/* User impact stays neutral ink — what changed for the person using it.
   Biz impact is set in --brand — what changed for the business, the one
   line meant to catch a scanning eye. */
.work__impact {
  font-size: var(--step--1);
  line-height: 1.55;
  margin: 0 0 var(--s1);
  color: var(--ink-70);
}

.work__impact b { color: var(--ink); font-weight: 600; }

.work__impact--biz {
  color: var(--brand);
  font-weight: 600;
  margin-bottom: var(--s3);
}

.work__row2-media {
  margin: 0;
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.work__row2-media img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
}

@media (min-width: 64rem) {
  .work__row2-media img { aspect-ratio: 5 / 4; }
}

/* EA Frostbite homepage thumbnail (2026-09-11): the source photo is a wide
   1200x630 banner (the EA roundel sits hard against its left edge, at
   roughly x=150-300 of 1200), but this card's crop box is much closer to
   square (4/3 phone, 5/4 desktop). object-fit:cover was cropping ~35-40% of
   the source's width symmetrically from centre, slicing off the left half
   of the EA mark. Scoped to this one card only: the image renders at 85% of
   the box's height (a 15% "zoom out"), centred, revealing ~77% of the
   source width instead of ~66% -- enough to clear both the full roundel and
   the full "Electronic Arts" wordmark -- with the freed vertical space
   filled by a colour sampled from the photo's own dark edges so the letterbox
   reads as a frame, not a bug. */
#cs-eafrostbitebridge .work__row2-media {
  position: relative;
  aspect-ratio: 4 / 3;
  background: #0b0e0e;
}
@media (min-width: 64rem) {
  #cs-eafrostbitebridge .work__row2-media { aspect-ratio: 5 / 4; }
}
#cs-eafrostbitebridge .work__row2-media figure { height: 100%; }
#cs-eafrostbitebridge .work__row2-media img {
  position: absolute;
  top: 50%;
  left: 0;
  transform: translateY(-50%);
  width: 100%;
  height: 85%;
  aspect-ratio: auto;
}

/* Reserved slot: structurally present, deliberately unpopulated. No shadow
   or shine here — a dashed line reads as "not yet," not as another card. */
.work__row--reserved {
  background: transparent;
  border: 1px dashed var(--rule);
  box-shadow: none;
  opacity: 0.7;
}
.work__row--reserved .work__co { font-style: italic; font-weight: 400; }

/* ---- Evidence strip: real companies ---- */

.orgs {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border: 1px solid var(--rule);
  border-bottom: 0;
  border-right: 0;
  border-radius: var(--radius);
  overflow: hidden;
  list-style: none;
  margin: 0;
  padding: 0;
}

.orgs li {
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: var(--s3) var(--s2);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  text-align: center;
  min-height: 7rem;
}

/* Real marks, not typeset names — a logo is a claim readers can verify at a
   glance against a company they already recognize. Karumi's "Trusted by"
   grid is the reference: full-colour marks, no name repeated underneath.

   Every mark sits in the same fixed-height box regardless of its native
   aspect ratio, so a compact icon+wordmark stack (Frostbite) and a wide
   wordmark (Intelex) read at the same optical weight instead of whichever
   shape happens to fill more of its own bounding box. brightness(0) forces
   every logo to Ink, including the one asset that ships in brand colour —
   concept.md caps the site at four colours and a client's brand blue would
   be a fifth, decorative one. */
.orgs__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 34px;
  width: 100%;
}

/* max-width used to be 9rem (144px), which was the actual binding
   constraint for every wide wordmark — OneTrust needs ~209px to reach the
   full 34px mark height (its aspect ratio is ~6:1), Intelex ~160px, EA/
   Frostbite ~176px. Only Unity's compact icon+wordmark (~98px) cleared the
   old cap and rendered at full height, which is why it read larger than
   the rest. Raised past OneTrust's requirement so height is the only
   constraint that binds at this row's normal width. */
.orgs__logo {
  max-height: 100%;
  max-width: 100%;
  width: auto;
  height: auto;
  filter: brightness(0);
  opacity: 0.86;
}

/* The 14rem cap lets wide wordmarks (OneTrust ~6:1, Intelex, EA/Frostbite)
   reach the full mark height at tablet width and up. Below 30rem the grid
   cell is narrower than 14rem, so the cap has to yield to the cell or the
   logo punches out of a 2-column phone grid. */
@media (min-width: 30rem) {
  .orgs__logo { max-width: 14rem; }
}

/* A logo that must keep its brand colour (e.g. George Brown's boxed mark, which
   turns into a solid black rectangle under the default brightness(0) treatment). */
.orgs__logo--color {
  filter: none;
  opacity: 1;
}

/* Text stand-in for a logo not yet sourced. Reads as a quiet wordmark in the
   same ink family as the real marks, paired with a "Placeholder" badge so the
   cell is unmistakably provisional. */
.orgs__logo--placeholder {
  filter: none;
  font-family: var(--sans);
  font-weight: 700;
  font-size: var(--step--1);
  line-height: 1.15;
  letter-spacing: 0.01em;
  text-align: center;
  color: var(--ink-50);
  max-width: 12rem;
}

.orgs__badge {
  font-family: var(--sans);
  font-size: var(--step--2);
  letter-spacing: 0.04em;
  color: var(--resolved);
  border: 1px solid currentColor;
  border-radius: 999px;
  padding: 0.1rem 0.55rem;
}

/* Resolved is reserved for outcomes that have shipped — a promise of future
   work is not one, so this stays neutral Ink rather than borrowing the
   colour that elsewhere means "what changed." */
.orgs__badge--soon {
  color: var(--ink-60);
}

/* A cell with more than one case study lists its badges side by side. */
.orgs__badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  justify-content: center;
}

/* Badge rendered as a link — scrolls to the matching row in Selected work. */
a.orgs__badge {
  text-decoration: none;
  transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

a.orgs__badge:hover,
a.orgs__badge:focus-visible {
  color: var(--paper);
  background: var(--resolved);
  border-color: var(--resolved);
}

/* Anchored case-study rows: keep a little breathing room above the row when
   a homepage badge jumps to it. */
.work__row[id] { scroll-margin-top: var(--s5); }

/* ---- Tabbed work-card (index.html) ----
   One card holding 2+ password-gated case studies for the same org, so EA
   and OneTrust don't each spread across two homepage rows. No whole-card
   link: every tab panel carries its own "Read this case study" link. The
   password state is stated once, top-right. Pattern approved 2026-09-07
   (portfolio-audit-dashboard/council-2026-09-07/tab-card-prototype.html). */
.work__row--tabbed { padding-top: var(--s3); }

.work__tabhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
  margin-bottom: var(--s2);
}

.work__tabcount {
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-60);
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  column-gap: 0.5rem;
  row-gap: 0.1rem;
}

.work__tabcount-n { white-space: nowrap; }
.work__tabcount .lock {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  white-space: nowrap;
}
.work__tabcount .lock svg { display: block; }
.work__tabcount .lock::before { content: "\00b7"; margin-right: 0.4rem; }

@media (max-width: 32rem) {
  .work__tabcount .lock::before { content: none; }
}

/* Single-case-study cards (no tab count) reuse .work__tabhead's top-right
   badge slot, minus the "N case studies ·" text, so the "·" separator the
   tabbed cards rely on has nothing to follow — suppressed here. */
.work__tabcount--lockonly .lock::before { content: none; }

.work__tabs {
  display: flex;
  gap: var(--s1);
  flex-wrap: wrap;
  border-bottom: 1px solid var(--rule);
  margin-bottom: var(--s4);
  position: relative;
  z-index: 2;
}

.work__tab {
  appearance: none;
  background: none;
  border: 0;
  font-family: var(--sans);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--ink-60);
  padding: 0.55rem 0.9rem;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.work__tab:hover { color: var(--ink); }
.work__tab.is-active { color: var(--ink); font-weight: 600; border-bottom-color: var(--ink); }
.work__tab:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 3px; }

.work__tab .work__tab-idx {
  font-size: var(--step--2);
  font-variant-numeric: tabular-nums;
  color: var(--ink-60);
  font-weight: 600;
}

.work__tab.is-active .work__tab-idx { color: var(--brand); }

.work__tabpanel[hidden] { display: none; }
.work__row--tabbed [role="tab"][id] { scroll-margin-top: var(--s6); }
.work__tab-cta { margin-top: var(--s3); }

/* Tabbed cards have no whole-card link (each panel is a different case
   study), so the panel thumbnail carries its own link to that panel's
   case study — plan §2, homepage thumbnails must be clickable. */
.work__thumblink {
  display: block;
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.work__thumblink img { transition: transform 0.3s ease; }
.work__thumblink:hover img,
.work__thumblink:focus-visible img { transform: scale(1.02); }
.work__thumblink:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .work__thumblink img { transition: none; }
  .work__thumblink:hover img { transform: none; }
}

/* ---- Evidence strip, merged variant (index-alt3.html) ----
   12 cells instead of 4: the four case-study marks plus eight more logos
   folded in from the former "Companies I've worked with" image, now shown
   as real bordered cells instead of a flat picture. Denser column count at
   each breakpoint is the "compressed" grid the merge was meant to produce.
   Cells with no case study simply have no .orgs__badge child — .orgs li's
   centered flex column already handles that with no extra rule needed. */
@media (min-width: 30rem) {
  .orgs--wide { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 48rem) {
  .orgs--wide { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}


/* Rows 2–3 of the merged grid (the 8 logos folded in from the former
   "Companies I've worked with" image), sized up 25% over the row-1 case
   study marks, then up another 25% on top of that (34px → 42.5px → 53px)
   — requested specifically for those two rows, row 1 stays at the shared
   --orgs mark height. */
.orgs--wide li:nth-child(n+5) .orgs__mark {
  height: 53px;
}

/* EA/Frostbite reserved slot: password-protected indicator, index-alt3.html
   only. Lock sits inline with the outcome text rather than as a badge, so
   it reads as metadata (like the "Available on request" text it replaces)
   rather than a decorative flourish. */
.work__outcome--locked {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  color: var(--ink-60);
}

/* Locked-card hover cue: the thumbnail becomes a link to the password
   lobby, and hovering it darkens the artifact and fades in a centered
   lock + "Password required" cue — the same signal a disabled-looking
   affordance would give, but confirming the card is still clickable
   rather than dead. */
.work__lockable {
  position: relative;
  display: block;
}

.work__lockable .work__artifact { margin-bottom: var(--s3); }

.work__lock-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  border-radius: var(--radius-sm);
  background: rgba(20, 20, 20, 0.55);
  color: #fff;
  font-size: var(--step--1);
  font-weight: 500;
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none;
}

.work__row:active .work__lock-overlay,
.work__row:focus-within .work__lock-overlay {
  opacity: 1;
}

/* Permanent frosted-glass treatment (Fenix Forge Platform thumbnail): the
   screenshot itself is real client-visible product UI, so unlike the other
   locked cards this one stays obscured at rest, not just on hover — a
   heavy blur plus a translucent wash, with a permanently visible padlock
   badge rather than one that only appears on interaction. */
.work__frost-wrap {
  position: relative;
  overflow: hidden;
}

.work__frost-img {
  filter: blur(18px) saturate(1.1);
  transform: scale(1.12);
}

.work__frost-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.45);
}

.work__frost-lock {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 3rem;
  height: 3rem;
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-card);
}

.orgs__note {
  font-size: var(--step--1);
  color: var(--ink-60);
  margin-top: var(--s2);
}

/* ---- Named testimony ---- */

.quote {
  border-left: 2px solid var(--rule);
  padding-left: var(--s3);
  margin: 0 0 var(--s3);
  max-width: 38rem;
}

.quote blockquote {
  font-family: var(--serif);
  font-size: var(--step-2);
  line-height: 1.4;
  margin: 0 0 var(--s2);
}

.quote figcaption {
  font-size: var(--step--1);
  color: var(--ink-70);
}

.quote figcaption b { font-weight: 600; color: var(--ink); }

/* Testimonial cards: full quote always visible, no accordion/expand. Avatar
   kept small and secondary so the text (the actual content) reads first. */
.testimonial-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s4);
}

@media (min-width: 56rem) {
  .testimonial-grid { grid-template-columns: repeat(2, 1fr); }
}

.testimonial-card {
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--s4);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}

.testimonial-card__person {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.testimonial-card__avatar {
  width: 44px;
  height: 44px;
  border-radius: 999px;
  object-fit: cover;
  flex-shrink: 0;
}

.testimonial-card__name { font-weight: 600; color: var(--ink); font-size: var(--step--1); }
.testimonial-card__role { color: var(--ink-60); font-size: var(--step--2); }

.testimonial-card__logo {
  height: 18px;
  width: auto;
  margin-top: var(--s1);
  opacity: 0.7;
}

.testimonial-card__quote {
  font-size: var(--step--1);
  line-height: 1.65;
  color: var(--ink-70);
  margin: 0;
}

/* ---- Named testimony carousel (index-alt2.html) ----
   Horizontal scroll-snap track instead of a vertical stack of .quote
   figures, so a larger roster of testimonials (7, up from 2) stays
   scannable rather than turning the section into a wall of text. */

.testimony__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  margin-bottom: var(--s3);
}

.testimony__nav { display: flex; gap: var(--s1); }

.testimony__btn {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 999px;
  border: 1px solid var(--rule);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  font-size: var(--step--1);
  line-height: 1;
}

.testimony__btn:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }

.testimony__track {
  display: flex;
  gap: var(--s3);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scroll-padding-left: 1px;
  padding-bottom: var(--s1);
  margin: 0 calc(var(--s3) * -1);
  padding-inline: var(--s3);
  -webkit-overflow-scrolling: touch;
}

.testimony__track::-webkit-scrollbar { height: 6px; }
.testimony__track::-webkit-scrollbar-thumb { background: var(--rule); border-radius: 999px; }

.quote--card {
  flex: 0 0 auto;
  width: min(85vw, 20rem);
  scroll-snap-align: start;
  border-left: 2px solid var(--rule);
  padding-left: var(--s3);
  margin: 0;
  max-width: none;
}

.quote--card blockquote {
  font-size: var(--step-1);
  line-height: 1.45;
}

@media (min-width: 40rem) {
  .quote--card { width: 22rem; }
}

/* ---- Named testimony: two-column grid (index.html) ----
   Was a Relume "Testimonial 30" scroll-snap carousel; replaced with a
   static multi-row grid so every testimonial is visible while scrolling
   instead of gated behind arrow/dot interaction. Company logo, full quote,
   avatar-and-role line, case-study link. Square corners and a plain 1px
   border, not the card/shadow language used elsewhere on this page —
   deliberately matching a flatter, more literal card. */

.testi30__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s4);
}

@media (min-width: 40rem) {
  .testi30__grid { grid-template-columns: repeat(2, 1fr); }
}

.testi30__card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--rule);
  border-radius: 16px;
  padding: var(--s4);
  margin: 0;
  max-width: none;
}

.testi30__logo {
  align-self: flex-start;
  height: 22px;
  width: auto;
  filter: brightness(0);
  opacity: 0.86;
  margin-bottom: var(--s4);
}

.testi30__card blockquote {
  font-size: var(--step-0);
  line-height: 1.6;
  color: var(--ink);
  margin: 0 0 var(--s4);
  flex: 1;
}

.testi30__person {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-bottom: var(--s3);
}

.testi30__avatar {
  flex-shrink: 0;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  background: var(--rule-soft);
  color: var(--ink-70);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.testi30__avatar--photo {
  object-fit: cover;
}

.testi30__name {
  display: block;
  font-weight: 600;
  font-size: var(--step--1);
}

.testi30__role {
  display: block;
  font-size: var(--step--2);
  color: var(--ink-60);
  margin-top: 0.15rem;
}

.testi30__soon {
  font-size: var(--step--1);
  color: var(--ink-60);
  font-style: italic;
}

/* ---- Password lobby (work/*-lobby.html) ----
   Structure and language borrowed from a reference password-gate pattern:
   centered card, lock glyph, one-line explanation, password field, dark
   primary action, quiet secondary "Go back." Built from this site's own
   .panel/.btn primitives rather than copied markup, so it stays visually
   native instead of reading as a foreign page dropped into the site. */

.lobby {
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-block: var(--s6);
}

.lobby__card {
  width: 100%;
  max-width: 22rem;
  text-align: center;
}

.lobby__lock {
  width: 2.75rem;
  height: 2.75rem;
  margin: 0 auto var(--s3);
  border-radius: 999px;
  background: rgba(4, 120, 87, 0.08);
  color: var(--brand);
  display: flex;
  align-items: center;
  justify-content: center;
}

.lobby__title {
  font-family: var(--serif);
  font-size: var(--step-1);
  font-weight: 600;
  margin-bottom: var(--s2);
}

.lobby__desc {
  font-size: var(--step--1);
  color: var(--ink-70);
  margin-bottom: var(--s4);
}

.lobby__form { display: flex; flex-direction: column; gap: var(--s2); }

.lobby__form input {
  font-family: var(--sans);
  font-size: var(--step--1);
  padding: 0.65rem 0.9rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
}

.lobby__form input:focus-visible { outline: 2px solid var(--resolved); outline-offset: 1px; }

.lobby__error {
  font-size: var(--step--2);
  color: var(--friction);
  min-height: 1.2em;
  margin: 0;
}

.lobby__btn { width: 100%; justify-content: center; }

/* Fixed-overlay variant of the lobby card, for gating a case study page
   in place (eafrostbitebridge.html, fenix-forge-platform.html) rather than
   redirecting to a separate lobby route. Same .lobby__card visuals. */
.pwgate {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s3);
}

.pwgate[hidden] { display: none; }

/* ---- Numbered list + live preview (work/eafrostbitebridge.html) ----
   Studied from a Relume portfolio pattern: image preview on one side, a
   numbered row list on the other. Active row is bold with an underline;
   hovering or focusing a row swaps the preview and its number badge.
   Built on this site's own .ph placeholder rather than real screenshots. */

.numlist {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s4);
}

@media (min-width: 48rem) {
  .numlist { grid-template-columns: 1fr 1fr; align-items: start; }
  .numlist__preview { position: sticky; top: var(--s4); }
}

.numlist__img {
  aspect-ratio: 4 / 3;
  position: relative;
  font-size: var(--step--1);
}

.numlist__badge {
  position: absolute;
  top: var(--s2);
  left: var(--s2);
  font-family: "JetBrains Mono", monospace;
  font-size: var(--step--2);
  color: var(--ink-60);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.15rem 0.55rem;
}

.numlist__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.numlist__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--rule-soft);
  color: var(--ink-60);
  cursor: pointer;
}

.numlist__item:last-child { border-bottom: 0; }

.numlist__row {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
}

.numlist__num {
  font-family: "JetBrains Mono", monospace;
  font-size: var(--step--2);
  font-variant-numeric: tabular-nums;
  color: var(--ink-60);
}

.numlist__name {
  font-family: var(--serif);
  font-size: var(--step-0);
}

.numlist__tag {
  flex-shrink: 0;
  font-size: var(--step--2);
  color: var(--ink-60);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.1rem 0.6rem;
}

.numlist__item.is-active {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

.numlist__item.is-active .numlist__num,
.numlist__item.is-active .numlist__name { color: var(--ink); font-weight: 600; }

.numlist__item:focus-visible { outline: 2px solid var(--resolved); outline-offset: 2px; }

/* Card-nested variant (index-alt3.html Selected Work): same pattern, sized
   down to sit inside a work card instead of a full page section — no
   sticky preview (the card isn't tall enough for it to matter), tighter
   image aspect so it doesn't dominate the card. */
.numlist--card { margin: 0 0 var(--s3); }
.numlist--card .numlist__preview { position: static; }
.numlist--card .numlist__img { aspect-ratio: 16 / 10; }
.numlist--card .numlist__item { padding: var(--s2) 0; }

/* ---- Case study ---- */

.cs__meta {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s3);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding-block: var(--s3);
  margin-top: var(--s4);
  list-style: none;
  padding-inline: 0;
}

.cs__meta dt, .cs__meta .k {
  font-size: var(--step--2);
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-60);
  margin-bottom: 0.35rem;
}

.cs__meta dd, .cs__meta .v {
  margin: 0;
  font-size: var(--step--1);
  line-height: 1.5;
}

.strip {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s4);
}

.beat { max-width: var(--measure); }

/* A .beat that is the only content in its .section (no diagram, quote,
   or strip alongside it) reads as a left-aligned column with a large
   empty hole beside it on wide screens. Centering it instead splits
   that space evenly. Applied only to sections confirmed text-only by
   audit — most .beat usage sits beside other content and stays
   left-aligned as-is. */
.beat--solo { margin-inline: auto; }

.beat h2 { font-size: var(--step-3); margin-bottom: var(--s3); }

.cs__takeaways {
  padding-left: 1.25em;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  font-size: var(--step-1);
  line-height: 1.6;
}

/* Document scale, but capped. A full-bleed empty placeholder outweighs the
   copy that carries the argument; swap in real artifacts before raising this. */
.artifact {
  margin: 0 0 var(--s4);
  max-width: 44rem;
  border: 1px solid var(--rule);
  background: rgba(255, 255, 255, 0.55);
}

.artifact figcaption {
  font-size: var(--step--1);
  color: var(--ink-70);
  padding: var(--s2);
  border-top: 1px solid var(--rule-soft);
}

/* Placeholder for artifacts not yet dropped in. */
.ph {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--s3);
  aspect-ratio: 16 / 9;
  color: var(--ink-60);
  font-size: var(--step--1);
  background: repeating-linear-gradient(
    -45deg,
    transparent 0 10px,
    rgba(20, 20, 20, 0.035) 10px 20px
  );
}

.pager {
  display: flex;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
  font-size: var(--step--1);
  padding-top: var(--s4);
  border-top: 1px solid var(--rule);
}

/* ---- Related work: end-of-case-study card grid, Menowo "More Resources" ----
   Shows the other case studies. Thumbnails are tinted dark, matching the
   photography treatment in the reference, since our artifacts are line
   diagrams and blueprints rather than colour photography. */

.related__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
  margin-bottom: var(--s4);
}

.related__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s4);
}

.related__card a { text-decoration: none; color: inherit; display: block; }

.related__thumb {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  background: var(--ink);
  color: rgba(244, 242, 237, 0.55);
  margin-bottom: var(--s2);
  object-fit: cover;
  transition: transform 0.3s ease;
}

.related__card a:hover .related__thumb { transform: scale(1.02); }

@media (prefers-reduced-motion: reduce) {
  .related__thumb { transition: none; }
  .related__card a:hover .related__thumb { transform: none; }
}

.related__meta {
  font-size: var(--step--2);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-60);
  margin: 0 0 0.35rem;
}

.related__title {
  font-family: var(--serif);
  font-size: var(--step-1);
  font-weight: 600;
  margin: 0 0 0.35rem;
}

.related__desc {
  font-size: var(--step--1);
  color: var(--ink-70);
  margin: 0;
}

@media (min-width: 48rem) {
  .related__grid { grid-template-columns: repeat(3, 1fr); }
}

/* Grid items default to min-width:auto, so a .diagram child with a
   min-content wider than its track (the diagram's 640px-min img) would
   otherwise force the whole grid — and the page — to overflow
   horizontally instead of the image simply scaling down to the column.
   Same fix as .chapter-split above, applied to this grid too: pre-existing
   on work/unity.html's Activities & Outputs image pairs, caught in a
   responsive pass. */
.related__grid > * { min-width: 0; }

.related__grid .diagram .diagram__frame {
  display: block;
  width: 100%;
}

.related__grid .diagram .diagram__frame img {
  width: 100%;
  min-width: 0;
  height: auto;
}

/* Same fix, for a plain single-column stack of full-width diagrams
   (work/onetrust-speakup.html) rather than a .related__grid. */
.diagram-stack > * { min-width: 0; }

.diagram-stack .diagram .diagram__frame {
  display: block;
  width: 100%;
}

.diagram-stack .diagram .diagram__frame img {
  width: 100%;
  min-width: 0;
  height: auto;
}

/* ---- Related work, text-only pattern ----
   Promoted from the prototype tried on work/ea-gameview-bootstrap.html:
   the outcome hook leads as the headline, the case-study name drops to a
   byline underneath. No thumbnails — a thin top rule per column instead,
   so it scales to a full cross-linked network of case studies without
   needing an image for every one. Sitewide component; item order is
   shuffled client-side (see the shared script each page includes) so a
   repeat visit doesn't always show the same neighbors first. */
.related-text {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s4);
}

@media (min-width: 40rem) {
  .related-text { grid-template-columns: repeat(3, 1fr); }
}

@media (min-width: 64rem) {
  /* 5 columns (not 6) so 9 related cards split 5/4 across two rows
     instead of a dense 6/3 — also puts whichever card sits 6th in
     document order at the start of row two. */
  .related-text { grid-template-columns: repeat(5, 1fr); }
}

.related-text__item {
  border-top: 1px solid var(--rule);
  padding-top: var(--s3);
}

.related-text__item a { text-decoration: none; color: inherit; display: block; }

.related-text__meta {
  font-size: var(--step--2);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-60);
  margin: 0 0 var(--s1);
}

.related-text__hook {
  font-size: var(--step-0);
  font-weight: 600;
  color: var(--brand);
  margin: 0 0 var(--s1);
  line-height: 1.3;
}

.related-text__hook::after {
  content: " \2197";
  display: inline;
}

.related-text__company {
  font-size: var(--step--1);
  color: var(--ink-60);
  margin: 0;
}

.related-text__item a:hover .related-text__hook { text-decoration: underline; }

/* ---- Footer: dark-green closing band ----
   The one deliberate departure from Paper as ground: a full-surface dark
   green, carrying a "let's work together" call and the colophon. Modeled
   on the mmmiller.ca footer treatment. */

.footband {
  background: var(--footer-green);
  color: #fff;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

/* -- the call-to-action block --
   2026-09-11: dropped the middle "Great design emerges..." line (redundant
   with About) and let the headline claim that freed width instead of
   sharing it. Two columns on wide viewports: the headline fills everything
   up to the nav, the nav stays a fixed-width column on the right, so the
   headline visibly runs from the left edge to just short of "Work" -- the
   effect asked for. Gradient runs yellow-green to emerald; colours clear
   WCAG AA (large text 3:1) on the near-black --footer-green ground. */
.footband__cta {
  padding-block: var(--s6) var(--s5);
  display: grid;
  gap: var(--s5);
  grid-template-columns: 1fr;
}

@media (min-width: 64rem) {
  .footband__cta {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--s6);
  }
}

/* Two lines instead of three ("Let's create" / "& collaborate together"):
   fewer, longer lines read as one confident block rather than a stack of
   short ones, and fill the wider column better. "Let's" stays plain white
   as a quiet lead-in; the gradient carries the rest. Size raised and made
   more fluid (5.6vw) now that the headline owns the row instead of sharing
   it with the blurb -- it should feel like it is claiming the space. */
.footband__headline {
  position: relative;
  font-family: var(--sans);
  font-weight: 700;
  font-size: clamp(2.5rem, 1.1rem + 5.6vw, 5.75rem);
  line-height: 0.98;
  letter-spacing: -0.035em;
  color: rgba(255, 255, 255, 0.92);
  margin: 0;
  text-wrap: balance;
}

/* the site mark (same glyph as the top-nav wordmark), hanging above the
   top-left of "Let's". bottom:100% keeps it out of the text box so the
   headline's first line still baselines with the nav. */
.footband__logomark {
  position: absolute;
  left: 0;
  bottom: calc(100% + 0.4rem);
  width: 2.4rem;
  height: auto;
  opacity: 0.9;
}

/* yellow-green -> emerald, on the same diagonal as before */
.footband__grad {
  background: linear-gradient(100deg, #bef264 0%, #34d399 55%, #10b981 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.footband__nav {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  font-family: var(--sans);
  font-size: var(--step--1);
  margin: 0;
}

.footband__nav a {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: rgba(255, 255, 255, 0.75);
  text-decoration: none;
  width: max-content;
}
.footband__nav a:hover { color: #fff; }

.footband__icons {
  display: flex;
  gap: var(--s2);
  margin-top: var(--s3);
}
.footband__icons a {
  width: 2.25rem;
  height: 2.25rem;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 999px;
  color: rgba(255, 255, 255, 0.72);
}
.footband__icons a:hover {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.4);
}
.footband__icons svg { width: 1.05rem; height: 1.05rem; display: block; }

/* -- the colophon bar -- */
.footband__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem var(--s3);
  padding-block: var(--s4);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  font-size: var(--step--2);
}

.footband__copy { color: rgba(255, 255, 255, 0.62); margin: 0; }

.footband__bar a {
  color: rgba(255, 255, 255, 0.65);
  text-decoration: none;
}

.footband__bar a:hover { color: #fff; }

/* -- legacy .footcard markup (still on the case-study pages): make it read
   on the dark-green ground until those footers are unified -- */
.footcard { background: transparent; padding: var(--s5) 0 0; }
.footcard__top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--s3);
  flex-wrap: wrap;
  padding-bottom: var(--s4);
  margin-bottom: var(--s4);
  border-bottom: 1px solid rgba(255, 255, 255, 0.10);
}
.footcard__name { font-family: var(--serif); font-size: var(--step-2); font-weight: 600; margin: 0; color: #fff; }
.footcard__role { font-size: var(--step--1); color: rgba(255, 255, 255, 0.60); margin: 0.2rem 0 0; }
.footcard__cols { display: grid; grid-template-columns: 1fr; gap: var(--s3); font-size: var(--step--1); }
.footcard__cols a { display: block; text-decoration: none; color: rgba(255, 255, 255, 0.65); padding-block: 0.15rem; }
.footcard__cols a:hover { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
.footcard__h {
  font-size: var(--step--2);
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.45);
  margin: 0 0 0.5rem;
}
.footband .footcard + .footband__copy,
.footband__copy--legacy {
  color: rgba(255, 255, 255, 0.5);
  font-size: var(--step--2);
  margin-top: var(--s3);
  padding-block: var(--s4) var(--s5);
  text-align: center;
}

/* ---- Responsive ---- */

@media (min-width: 48rem) {
  /* :not(.orgs--wide) — the merged evidence-strip grid (index.html) sets
     its own 3/4/6-column progression above; this older rule predates that
     merge and was silently overriding it back to 4 columns at every width
     from tablet up, since equal-specificity + later source order wins. */
  .orgs:not(.orgs--wide) { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .cs__meta { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .strip { grid-template-columns: repeat(3, 1fr); }
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

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

/* ---- Alt homepage: work-first structure (index-alt.html) ----
   Additive only — nothing here changes the default index.html. */

/* Hero proof strip: three headline figures directly under the fold,
   standing in for the evidence strip's old position at the very top. */
.hero__proof {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s5) var(--s5);
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--rule);
}

/* index-alt3.html only (body.page-tagged): the rule directly under "See the
   work" read as a redundant divider stacked right on top of the .companies
   section's own border-top just below it — dropped so only one line
   separates the hero from what follows. Scoped to this page's body class so
   index-alt2.html, which shares .hero--confident, keeps its own rule. */
body.page-tagged .hero__proof { border-top: 0; padding-top: 0; }

.hero__proof > div { min-width: 9rem; }

/* Two-up card grid for Selected Work when it leads the page, instead of
   the single-column stacked rows used lower down in the default layout. */
.work--grid {
  grid-template-columns: 1fr;
}

@media (min-width: 48rem) {
  .work--grid { grid-template-columns: repeat(2, 1fr); }
  .work--grid .work__row--reserved { grid-column: 1 / -1; }
}

/* ---- Confident hero (index-alt2.html) ----
   Studied from alexmchong.ca: oversized mixed-weight headline with an
   italic serif accent phrase, muted-grey (not colour) emphasis on select
   body words, small bordered monospace pill nav. The site's tiled grain
   background is deliberately not carried over — flagged as too heavy.
   Still obeys this system's own rule: Friction/Resolved stay reserved for
   state, so emphasis here uses ink weight and opacity only. */

.hero--confident .hero__copy { max-width: 50rem; }

.hero--confident h1 {
  font-size: clamp(3rem, 2rem + 4vw, 5rem);
  font-weight: 600;
  letter-spacing: -0.015em;
}

.hero--confident .accent {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
}

.hero--confident .lead {
  font-size: var(--step-1);
  max-width: 42rem;
}

.hero--confident .lead b {
  color: var(--ink);
  font-weight: 600;
}

.eyebrow--mono {
  font-family: "JetBrains Mono", monospace;
  font-size: var(--step--2);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-60);
}

/* Nav wordmark with no role line underneath — the self-intro now lives in
   the hero copy instead, so the nav doesn't need to carry the job title
   too and no longer reads as two mismatched lines stacked as an
   afterthought. */
.nav--mark .nav__name {
  font-size: var(--step-0);
  line-height: 1;
}

/* Self-intro line, set apart from the lead paragraph above it rather than
   folded into the same block — a distinct, quieter beat right before the
   CTA, not a continuation of the pitch. */
.intro-line {
  margin-top: var(--s3);
  font-size: var(--step--1);
  color: var(--ink-60);
}

/* Bordered monospace pill nav links, replacing the underline-on-hover
   nav links used elsewhere on the site. */
.nav--pill .nav__links { gap: var(--s2); }

.nav--pill .nav__links a {
  font-family: "JetBrains Mono", monospace;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  border: 1px solid var(--ink);
  border-radius: 4px;
  padding: 0.375rem 0.75rem;
  color: var(--ink);
}

.nav--pill .nav__links a:hover,
.nav--pill .nav__links a[aria-current="page"] {
  background: var(--ink);
  color: var(--paper);
  border-bottom-color: var(--ink);
}

/* ---- Brand nav (all pages) ----
   Flowed in from mmmiller.ca's live header: wordmark logo left, plain-text
   links, solid brand-green CTA right. Replaces the .nav--pill treatment
   above site-wide; those rules are left in place unused rather than
   deleted, in case a pill variant is wanted again later. */

.nav--brand { align-items: center; }

.nav__logo { display: flex; align-items: center; flex: none; }
.nav__logo img { height: 28px; width: auto; max-width: none; display: block; }

.nav--brand .nav__links {
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-size: var(--step--1);
  padding-top: 0;
}

.nav--brand .nav__links a {
  text-decoration: none;
  color: var(--ink-70);
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
}

.nav--brand .nav__links a:hover,
.nav--brand .nav__links a[aria-current="page"] {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

.btn--brand {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}

.btn--brand:hover {
  background: var(--brand-hover);
  border-color: var(--brand-hover);
  color: #fff;
}

/* ---- Companies I've worked with (index-alt3.html) ----
   Reused verbatim from mmmiller.ca's homepage: one composite logo image
   under a small uppercase label, not rebuilt as individual marks. Spacing
   mirrors the source (border-top rule, generous top padding, label-to-image
   gap) rather than this site's usual .secline/.sech2 pattern, since this
   section is a direct lift rather than a page-native block. */

.companies {
  padding-top: var(--s4);
  border-top: 1px solid var(--rule);
}

.companies__label {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-60);
  margin: 0 0 var(--s4);
}

.companies__img {
  max-width: 100%;
  height: auto;
}

/* ---- Tagged work card (index-alt3.html) ----
   Adopted from the Menowo service-list reference: an "Includes" column of
   pill tags sits to the right of the artifact and copy, rather than below
   it. Additive only — .work__row keeps its index-alt2.html shape (stacked)
   until this variant's own modifier, .work__row--tagged, opens a second
   grid column at desktop width. */

.work__row--tagged {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s3);
}

.work__row--tagged .work__main { min-width: 0; }

.work__tags {
  padding-top: 0.2rem;
}

.work__tags-label {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-60);
  margin: 0 0 var(--s2);
}

.work__tags-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1);
}

/* Tinted with --brand (the nav CTA's green) rather than neutral ink, so the
   Includes tags read as a distinct, branded chip instead of a plain
   outline pill — a light wash of the same green, not the full-strength
   button colour. */
.work__tags-list li {
  font-size: var(--step--2);
  font-weight: 600;
  color: var(--brand);
  background: rgba(4, 120, 87, 0.1);
  border: 0;
  border-radius: 999px;
  padding: 0.35rem 0.8rem;
  white-space: nowrap;
}

.work__row--reserved .work__tags-list li { opacity: 0.7; }

/* Thumbnails scoped to the tagged variant only, taller than the 3/1–4/1
   banner used elsewhere on the site: the Fenix Career Forge photo was
   cropping the subject's face at that shallow ratio. Height raised 50%
   (ratio divided by 1.5) rather than widened, so a portrait-oriented
   photo has room to keep a face in frame. */
.work__row--tagged .work__artifact .ph,
.work__row--tagged .work__artifact img {
  aspect-ratio: 2 / 1;
}

@media (min-width: 48rem) {
  .work__row--tagged .work__artifact .ph,
  .work__row--tagged .work__artifact img {
    aspect-ratio: 8 / 3;
  }
}

/* Thumbnail corners, index-alt3.html only: 8px flat, rather than the
   6px --radius-sm shared by every other page's .work__artifact. */
.work__row--tagged .work__artifact {
  border-radius: 8px;
}

/* ---- Problem cards (work/migraine-canada.html) ----
   Numbered problem statement + a "Therefore" consequence line set apart
   with a left-border accent, so cause and downstream effect stay visually
   distinct without a second card shape. */

.problems {
  display: grid;
  gap: var(--s3);
  margin-top: var(--s4);
}

.problem {
  background: var(--surface);
  border: 1px solid rgba(20, 20, 20, 0.05);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: var(--s4);
}

.problem__title {
  font-family: var(--serif);
  font-size: var(--step-1);
  font-weight: 600;
  margin: 0 0 var(--s2);
}

.problem p { margin-bottom: 0; }

@media (min-width: 60rem) {
  .problems--3up { grid-template-columns: repeat(3, 1fr); align-items: start; }
  .two-col.mc-act1-head { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
}

/* Migraine Canada, Act 2: numbered approach list beside the intro + diagram */
.mc-approach { margin: 0; padding-left: 1.5em; display: grid; gap: var(--s3); }
.mc-approach li { max-width: 34rem; }
.mc-approach li::marker { color: var(--ink-70); font-variant-numeric: tabular-nums; }
.mc-approach-media .diagram__frame { display: block; width: 100%; overflow-x: auto; }
.mc-approach-media .diagram__frame img { width: 100%; min-width: 34rem; height: auto; }

.problem__therefore {
  margin-top: var(--s3);
  padding-left: var(--s3);
  border-left: 2px solid var(--friction);
  font-style: italic;
  color: var(--ink-70);
  font-size: var(--step--1);
}

/* ---- Diagram figure (case-study SVG artifacts) ----
   Caps native viewBox scale on very wide screens per the build spec, and
   gives wide diagrams (Figures 2 and 4) their own horizontal-scroll lane
   instead of letting the swimlane logic reflow. */

.diagram {
  margin: var(--s4) 0 0;
  max-width: 1200px;
}

/* fit-content so the bordered frame hugs the SVG's actual rendered width
   instead of stretching to the section's full column width and leaving a
   dead gap on the right. max-width caps it back to 100% of .diagram so it
   never causes page-level horizontal overflow on narrow viewports — the
   overflow-x:auto below still handles the case where even the viewport
   width can't fit the image's min-width. */
.diagram__frame {
  display: inline-block;
  width: fit-content;
  max-width: 100%;
  /* Reinforced stroke + subtle lift (plan §2): the 1px rule-opacity border
     was disappearing against the page ground at the corners on several
     pages. A denser border colour plus a 2px shadow gives the container a
     defined edge all the way around. */
  border: 1px solid rgba(20, 20, 20, 0.16);
  border-radius: var(--radius-sm);
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(20, 20, 20, 0.05), 0 2px 8px -2px rgba(20, 20, 20, 0.12);
  overflow: auto;
}

.diagram__frame img { display: block; min-width: 640px; border-radius: inherit; }

.diagram--wide .diagram__frame { display: block; width: auto; }
.diagram--wide .diagram__frame img { min-width: 900px; }

.diagram--hero { max-width: none; }
.diagram--hero .diagram__frame { display: block; width: auto; }

.video-embed {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--ink);
}

.video-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

figcaption.diagram__caption {
  font-size: var(--step--1);
  color: var(--ink-70);
  margin-top: var(--s2);
}

/* Stage / step label inside a diagram caption, given real weight so it
   reads as a heading for the figure rather than part of the caption
   sentence (plan §5, OneTrust stage diagrams). */
.diagram__stage {
  display: block;
  font-family: var(--sans);
  font-weight: 700;
  font-size: var(--step--1);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 0.25rem;
}

/* Act 1's before/after diagram is deliberately half-revealed — the "after"
   panel gets its full reveal later, in Act 3 (Figure 6). Crops the SVG's
   viewBox to its left half via a wrapper with matched aspect-ratio and a
   scaled, left-anchored image. */
.diagram--crop-left .diagram__frame {
  display: block;
  width: auto;
  aspect-ratio: 700 / 850;
  overflow: hidden;
}

.diagram--crop-left .diagram__frame img {
  width: 200%;
  max-width: none;
  min-width: 0;
  height: auto;
}

/* ---- Chapter split (text + diagram side by side) ----
   Stacks single-column below 64rem so the diagram keeps its legible
   min-width/scroll behavior on phones and small tablets, where shrinking
   an SVG's fixed-size text to fit a column would make it unreadable.
   At 64rem+, the text track is capped to --measure so line length doesn't
   balloon back out, and the diagram track fills what's left, scaling the
   SVG down to fit rather than cropping or scrolling it. */
.chapter-split {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s4);
}

/* Grid items default to min-width:auto, so a child with min-content wider
   than the track (the diagram's 640px-min img) would otherwise force the
   whole grid — and the page — to overflow instead of scrolling internally
   or wrapping. min-width:0 lets the track actually shrink to the column. */
.chapter-split > * { min-width: 0; }

.chapter-split > .diagram,
.chapter-split > .chapter-split__ph { margin-top: 0; }

/* Generic image-placeholder pairing (screenshots/wireframes not yet
   supplied) — same grid treatment as a diagram, without the SVG-specific
   frame/scroll rules, since a .ph placeholder is already fluid. */
.chapter-split__ph .ph {
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--surface);
}

@media (min-width: 64rem) {
  .chapter-split {
    grid-template-columns: minmax(0, var(--measure)) minmax(260px, 1fr);
    gap: var(--s5);
    align-items: start;
  }

  .chapter-split > .diagram,
  .chapter-split > .chapter-split__ph {
    max-width: 100%;
    align-self: start;
  }

  .chapter-split > .diagram .diagram__frame {
    display: block;
    width: 100%;
    overflow-x: visible;
  }

  .chapter-split > .diagram .diagram__frame img {
    width: 100%;
    min-width: 0;
    height: auto;
  }
}

/* ---- Two-column layout (sitewide, plan §2) ----
   The dominant restructuring request across the case studies: content on one
   side, an image / diagram / quote / stat block on the other. Built once
   here so per-page CSS doesn't drift.

   Stacks in natural document order below 60rem. At 60rem+:
     .two-col            → reading column capped to --measure, media fills rest
     .two-col--even      → 1fr / 1fr
     .two-col--aside     → fluid main + fixed-ish 20rem aside (stats, quote)
     .two-col--aside-left→ aside first, then main
   Add .two-col__span to any child that should break out to full width in the
   grid (e.g. a shared sub-heading above both columns). */
.two-col {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s4);
  align-items: start;
}

.two-col > * { min-width: 0; }
.two-col__span { grid-column: 1 / -1; }

@media (min-width: 60rem) {
  .two-col { grid-template-columns: minmax(0, var(--measure)) minmax(0, 1fr); gap: var(--s5); }
  .two-col--even { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .two-col--aside { grid-template-columns: minmax(0, 1fr) minmax(0, 20rem); }
  .two-col--aside-left { grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); }
}

/* A diagram or figure sitting directly in a .two-col track should fill it
   rather than keep its own fit-content / max-width cap. */
.two-col > .diagram,
.two-col > figure { margin-top: 0; max-width: 100%; }

@media (min-width: 60rem) {
  .two-col > .diagram .diagram__frame {
    display: block;
    width: 100%;
    overflow-x: auto;
  }
  .two-col > .diagram .diagram__frame img { width: 100%; min-width: 0; height: auto; }
}

/* ---- Artifact thumbnails (see UNITY_MIGRATION_PROTOCOL_v1.md §4) ----
   The container fits the image at its native aspect ratio, shown small in a
   grid; click opens the full size via lightbox.js. No fixed height, no
   object-fit letterboxing. Give each .thumb a data-lb-src + data-lb-alt. */
.thumbs { display: grid; gap: var(--s3); align-items: start; margin-top: var(--s3); }
.thumbs--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.thumbs--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.thumbs--1 { grid-template-columns: minmax(0, 34rem); }
.thumb { display: block; width: 100%; padding: 0; border: 1px solid var(--rule);
         border-radius: var(--radius-sm); overflow: hidden; background: var(--paper);
         cursor: zoom-in; }
.thumb img { display: block; width: 100%; height: auto; }
.thumb:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
@media (max-width: 40rem) { .thumbs--2, .thumbs--3 { grid-template-columns: 1fr; } }

/* ---- Pull-quote (sitewide, plan §2) ----
   One treatment everywhere a quote is called out of body text. Modeled on
   the About page's .quote: serif, larger, generous line-height, a hairline
   left rule. .pullquote is the more prominent variant used when a quote is
   the visual anchor of its section (no attribution needed inline — add a
   <cite> or trailing line if there is one). */
.pullquote {
  border-left: 3px solid var(--rule);
  padding-left: var(--s4);
  margin: var(--s4) 0;
  max-width: 40rem;
}

.pullquote p,
.pullquote blockquote {
  font-family: var(--serif);
  font-size: var(--step-2);
  line-height: 1.4;
  color: var(--ink);
  margin: 0 0 var(--s2);
  max-width: none;
}

.pullquote p:last-child,
.pullquote blockquote:last-child { margin-bottom: 0; }

.pullquote cite,
.pullquote .pullquote__by {
  display: block;
  font-family: var(--sans);
  font-style: normal;
  font-size: var(--step--1);
  color: var(--ink-70);
  margin-top: var(--s2);
}

.pullquote .pullquote__by b { color: var(--ink); font-weight: 600; }

/* Emphasis variant: brand-tinted rule, for the one quote per page that
   carries the argument (used where the plan says "give more visual
   emphasis" / "more visual impact"). */
.pullquote--accent { border-left-color: var(--brand); }

/* ---- Diagram lightbox ----
   Any .diagram__frame img is click-to-enlarge; the frame gets a zoom
   cursor and a focus-visible ring since it's made keyboard-operable via
   role="button"/tabindex in markup. */
.diagram__frame {
  cursor: zoom-in;
}

.diagram__frame:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s4);
  background: rgba(20, 20, 20, 0.86);
}

.lightbox[hidden] { display: none; }

.lightbox__img {
  max-width: min(92vw, 1100px);
  max-height: 88vh;
  width: auto;
  height: auto;
  background: var(--surface);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-card);
  cursor: zoom-out;
}

.lightbox__close {
  position: absolute;
  top: var(--s3);
  right: var(--s3);
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: rgba(20, 20, 20, 0.6);
  color: #fff;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}

.lightbox__close:hover { background: rgba(20, 20, 20, 0.8); }

/* Prev/next controls, shown only when a page has more than one enlargeable
   image (the shared lightbox.js hides them otherwise). */
.lightbox__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: rgba(20, 20, 20, 0.6);
  color: #fff;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}
.lightbox__nav:hover { background: rgba(20, 20, 20, 0.85); }
.lightbox__nav--prev { left: var(--s3); }
.lightbox__nav--next { right: var(--s3); }
.lightbox__nav[hidden] { display: none; }

/* ---- Enlarge affordance: two-arrow icon, top-right of any enlargeable
   diagram or screenshot (shared lightbox.js injects the markup) ---- */
.has-enlarge { position: relative; }

.enlarge-host { display: block; }

.enlarge-icon {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 1.75rem;
  height: 1.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.92);
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(20, 20, 20, 0.18);
  opacity: 0.75;
  transition: opacity 0.15s ease;
  pointer-events: none;
}

.has-enlarge:hover .enlarge-icon,
.has-enlarge:focus-visible .enlarge-icon { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .enlarge-icon { transition: none; }
}

/* ---- Key Insight callout (work/migraine-canada.html) ---- */

.callout {
  background: rgba(4, 120, 87, 0.06);
  border: 1px solid rgba(4, 120, 87, 0.18);
  border-radius: var(--radius);
  padding: var(--s4);
  margin-top: var(--s4);
  max-width: var(--measure);
}

.callout__label {
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--brand);
  margin: 0 0 var(--s2);
}

.callout p:last-child { margin-bottom: 0; }

/* ---- Checklist groups (work/migraine-canada.html Act 3) ---- */

.checklists {
  display: grid;
  gap: var(--s4);
  margin-top: var(--s4);
}

.checklist__h {
  font-size: var(--step--1);
  font-weight: 600;
  margin: 0 0 var(--s2);
}

.checklist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}

.checklist li {
  display: flex;
  gap: 0.6rem;
  font-size: var(--step--1);
  color: var(--ink-70);
  line-height: 1.5;
}

.checklist li::before {
  content: "\2713";
  color: var(--resolved);
  font-weight: 600;
  flex-shrink: 0;
}

@media (min-width: 48rem) {
  .checklists { grid-template-columns: repeat(3, 1fr); }
}

/* ---- Draft/TODO placeholder block ----
   Marks a section still pending the author's own voice or confirmed data,
   per the build spec's "do not fabricate" rule — dashed border, no shadow,
   reads as structurally present but deliberately unfinished. */

.todo-block {
  border: 1px dashed var(--rule);
  border-radius: var(--radius);
  padding: var(--s4);
  margin-top: var(--s3);
  max-width: var(--measure);
}

.todo-block__tag {
  display: inline-block;
  font-size: var(--step--2);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--friction);
  border: 1px solid currentColor;
  border-radius: 2px;
  padding: 0.1rem 0.5rem;
  margin-bottom: var(--s2);
}

.todo-block p {
  color: var(--ink-60);
  font-style: italic;
  font-size: var(--step--1);
  margin-bottom: 0;
}

/* ---- "How I work" photo (homepage About teaser), 2026-09-11 ----
   A group photo sits to the right of the text block, roughly matched to the
   text's own height rather than a fixed height, so it never forces the
   section taller than the copy needs. On wide viewports the photo is
   right-anchored and allowed to run under the text -- its own left portion
   fades to the page's paper colour via a gradient overlay so the copy stays
   fully legible regardless of how much the two overlap. Stacks to a plain
   contained image above the text on narrower viewports, where there's no
   width to spare for an overlap. */
.howiwork { position: relative; }

.howiwork__text { position: relative; z-index: 1; max-width: var(--measure); }

.howiwork__photo {
  margin: var(--s4) 0 0;
  border-radius: 32px;
  overflow: hidden;
}

.howiwork__photo img {
  width: 100%;
  height: auto;
  display: block;
}

@media (min-width: 64rem) {
  .howiwork__photo {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    margin: 0;
    z-index: 0;
  }

  .howiwork__photo img {
    width: auto;
    height: 100%;
    max-width: none;
  }

  /* the fade: opaque paper through the zone the photo can overlap the text
     column, soft through its middle, clear by its own right half */
  .howiwork__photo::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(
      to right,
      var(--paper) 0%,
      var(--paper) 34%,
      rgba(255, 255, 255, 0.7) 48%,
      transparent 68%
    );
  }
}

/* ---- Podcast features (homepage section 6) ------------------------------
   One row per episode: write-up on the left, click-to-play thumbnail on
   the right. Thumbnail opens a lightweight video lightbox (own JS below,
   distinct from js/lightbox.js which is image-only). */
.podcasts { display: flex; flex-direction: column; gap: var(--s5); margin-top: var(--s4); }

.podcast-card {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s3);
}
@media (min-width: 56rem) {
  .podcast-card { grid-template-columns: minmax(0, 1fr) minmax(0, 22rem); gap: var(--s5); align-items: center; }
}

.podcast-card__desc { display: flex; flex-direction: column; gap: var(--s2); }

.podcast-card__show {
  font-family: "JetBrains Mono", monospace;
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-60);
}
.podcast-card__show b { color: var(--ink); font-weight: 600; }

.podcast-card__title {
  font-family: var(--serif);
  font-size: var(--step-2);
  line-height: 1.25;
  margin: 0;
}

.podcast-card__desc p { font-size: var(--step--1); line-height: 1.65; color: var(--ink-70); margin: 0; }

.podcast-card__media {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 16px;
  overflow: hidden;
  border: 0;
  padding: 0;
  cursor: pointer;
  background: var(--ink);
}
.podcast-card__media img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: 0.88; transition: opacity 0.2s; }
.podcast-card__media:hover img { opacity: 1; }
.podcast-card__media:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.podcast-card__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.92);
  display: flex;
  align-items: center;
  justify-content: center;
}
.podcast-card__play::after {
  content: "";
  border-style: solid;
  border-width: 9px 0 9px 15px;
  border-color: transparent transparent transparent var(--ink);
  margin-left: 3px;
}

.podcast-card__year {
  position: absolute;
  top: var(--s2);
  right: var(--s2);
  font-family: "JetBrains Mono", monospace;
  font-size: var(--step--2);
  letter-spacing: 0.04em;
  color: #fff;
  background: rgba(20, 20, 20, 0.55);
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
}

/* Video lightbox */
.video-lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s4);
  background: rgba(20, 20, 20, 0.9);
}
.video-lightbox[hidden] { display: none; }
.video-lightbox__frame {
  width: min(92vw, 1100px);
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: #000;
}
.video-lightbox__frame iframe { width: 100%; height: 100%; border: 0; display: block; }
.video-lightbox__close {
  position: absolute;
  top: var(--s3);
  right: var(--s3);
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: rgba(20, 20, 20, 0.6);
  color: #fff;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}
.video-lightbox__close:hover { background: rgba(20, 20, 20, 0.8); }
