/* ===========================================================================
   Portfolio site -- no framework, no build step.

   ONE GROUND, ONE THEME (2026-09-03). The page used to open and close on dark
   bands with a light reading column between them, and it used to ship a light
   and a dark palette with a toggle. Both are gone: the whole page is now a
   single uninterrupted slate ground, and there is only this palette. The old
   two-theme stylesheet is parked at _prev/style-dual-theme.css.

   Colour and contrast (WCAG 2.1), computed against the ground #0F172A:

     --ink    #E2E8F0 on --paper   14.48 : 1   AAA   headings
     --body   #AEB9C9 on --paper    9.00 : 1   AAA   body copy
     --body   #AEB9C9 on --sunk     7.83 : 1   AAA   body copy on tinted blocks
     --muted  #94A3B8 on --paper    6.96 : 1   AA+   captions, meta, kickers
     --muted  #94A3B8 on --sunk     6.06 : 1   AA+   same, on tinted blocks
     --accent #4EC1C5 on --paper    8.30 : 1   AAA   links, rules, marks
     --accent #4EC1C5 on --sunk     7.22 : 1   AAA   same
     --stop   #E8836A on --paper    6.70 : 1   AA+   the blocked branch, Cancelled
     --stop   #E8836A on --sunk     5.83 : 1   AA+   same

   Every text pair clears 4.5:1, and the smallest type on the page (captions and
   meta labels at 16px) sits at 6.06:1 or better. --rule #23304A is a hairline
   border only and carries no text; --sunk is a 1.15:1 step off the ground,
   which reads as a surface without becoming a band.
   =========================================================================== */

:root {
  --paper:  #0F172A;   /* the ground -- the whole page, top to bottom */
  --sunk:   #1B2436;   /* tinted blocks and callouts: a surface, not a band */
  --card:   #182032;
  --ink:    #E2E8F0;
  --body:   #AEB9C9;
  --muted:  #94A3B8;
  --rule:   #23304A;
  --accent: #4EC1C5;   /* the one colour that is hers */
  --stop:   #E8836A;   /* the Cancelled chip */

  --serif: "Source Serif 4", Georgia, "Times New Roman", serif;
  --sans:  "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;

  --gutter:  1.5rem;  /* the page edge -- body padding AND the floor on every
                         full-bleed element, so they land on the same line */
  --measure: 52rem;   /* 832px -- the one text width; the hero lede's first line (830px) just fits */
  --column:  46rem;   /* the case study's reading column; figures run wider than the text */
  --gap:     2.75rem;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; background: var(--paper); color-scheme: dark; }

body {
  margin: 0;
  padding: 0 var(--gutter);
  background: var(--paper);
  color: var(--body);
  font-family: var(--serif);
  font-size: 1.125rem;          /* 18px */
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: var(--gutter);
  top: 1rem;
  z-index: 10;
  background: var(--paper);
  color: var(--accent);
  font-family: var(--sans);
  font-size: 1rem;
  padding: .6rem 1rem;
  border: 2px solid var(--accent);
  border-radius: 3px;
}

/* ------------------------------------------------------------------ masthead */

.masthead {
  position: relative;
  max-width: var(--column);
  margin: 0 auto;
  padding: 5.5rem 0 0;
}

.kicker {
  margin: 0 0 1.5rem;
  font-family: var(--sans);
  font-size: .8125rem;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}

h1 {
  margin: 0;
  max-width: 18ch;
  font-family: var(--sans);
  font-size: clamp(2.1rem, 6vw, 3.1rem);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -.022em;
  color: var(--ink);
  text-wrap: balance;
}

.subtitle {
  margin: 1rem 0 0;
  max-width: 42ch;
  font-size: clamp(1.2rem, 3vw, 1.45rem);
  line-height: 1.4;
  color: var(--muted);
  text-wrap: balance;
}

.meta {
  margin: 3rem 0 0;
  padding: 1.5rem 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  display: grid;
  gap: 1rem 2.5rem;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  font-family: var(--sans);
  font-size: 1rem;
}
.meta > div { display: flex; flex-direction: column; gap: .25rem; }
.meta dt {
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.meta dd { margin: 0; color: var(--ink); line-height: 1.45; }

/* ------------------------------------------------------------------ sections */

main {
  max-width: var(--column);
  margin: 0 auto;
}

section {
  margin-top: 4.5rem;
}

h2 {
  margin: 0 0 1.25rem;
  max-width: 30ch;
  font-family: var(--sans);
  font-size: 1.6rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -.014em;
  color: var(--ink);
  text-wrap: balance;
}
h2::before {
  content: "";
  display: block;
  width: 1.75rem;
  height: 2px;
  margin-bottom: 1.1rem;
  background: var(--accent);
}

h3 {
  margin: 2.75rem 0 .75rem;
  max-width: 34ch;
  font-family: var(--sans);
  font-size: 1.075rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink);
  text-wrap: balance;
}

p {
  margin: 0 0 1.15rem;
  max-width: var(--measure);
}
p:last-child { margin-bottom: 0; }

.questions {
  max-width: var(--measure);
  margin: 0 0 1.15rem;
  padding: 0 0 0 1.6rem;
}
.questions li {
  margin-bottom: .5rem;
  padding-left: .35rem;
}
.questions li::marker {
  font-family: var(--sans);
  font-size: .9rem;
  color: var(--accent);
}

.admission {
  max-width: var(--measure);
  margin: 1.5rem 0 0;
  padding: 1.15rem 1.35rem;
  background: var(--sunk);
  border-left: 2px solid var(--accent);
  color: var(--body);
}
.admission strong { color: var(--ink); font-weight: 600; }
/* the same block holding several paragraphs and a list: what I would do now,
   marked as reflection so it cannot be read as delivered work */
.admission.reflection-block { margin-top: 2rem; }
.admission.reflection-block > * { margin: .8rem 0 0; max-width: none; }
.admission.reflection-block > :first-child { margin-top: 0; }
.admission.reflection-block ul { padding-left: 1.2rem; }
.admission.reflection-block li + li { margin-top: .45rem; }
.admission-label {
  font-family: var(--sans);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--accent);
}

/* ------------------------------------------------------------------- figures */

figure {
  margin: 2.5rem 0 0;
  width: min(58rem, calc(100vw - 3rem));
  margin-left: 50%;
  transform: translateX(-50%);
}

figure img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: var(--sunk);
}

.pair, .trio {
  display: grid;
  gap: 1rem;
}
.pair { grid-template-columns: repeat(2, 1fr); }
.trio { grid-template-columns: repeat(3, 1fr); }
.pair img, .trio img { border-radius: 10px; }

figcaption {
  margin-top: .9rem;
  margin-inline: auto;
  width: 100%;
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--muted);
}

.glance {
  margin-top: 3.25rem;
  padding-top: 1.75rem;
  border-top: 2px solid var(--ink);
}
.glance h2 { font-size: 1.05rem; margin-bottom: 1.25rem; max-width: none; }
.glance h2::before { display: none; }
/* the line above the pairs saying where they came from -- this page has no
   research section, so it has to be explicit that these are not test findings */
.sourcenote {
  margin: 0 0 1.6rem;
  font-family: var(--sans);
  font-size: .92rem;
  line-height: 1.5;
  color: var(--muted);
  max-width: var(--measure);
}

/* what I saw / what I did: two columns on desktop so the pair reads across,
   stacked on narrow screens with the action marked by an accent rule so the
   two halves never merge into one paragraph */
.insight {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: none;
}
.insight > li {
  display: grid;
  /* minmax(0, ...) so a full-width figure inside a row (dashboard page) breaks out
     of the column as figures do elsewhere, instead of stretching the tracks */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0 2.75rem;
  padding: 1.2rem 0;
  border-top: 1px solid var(--rule);
}
.insight > li:last-child { border-bottom: 1px solid var(--rule); }
.ins-a, .ins-b { display: grid; gap: .35rem; align-content: start; }
.insight span {
  font-family: var(--sans);
  font-size: .7rem;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}
.ins-b span { color: var(--accent); }
.insight p {
  margin: 0;
  max-width: none;
  font-size: 1.02rem;
  line-height: 1.5;
}
.ins-b p { color: var(--ink); }

@media (max-width: 40rem) {
  .insight > li { grid-template-columns: 1fr; gap: .9rem; }
  .ins-b { padding-left: .95rem; border-left: 2px solid var(--accent); }
}

/* dashboard case study (2026-10-06): a screen sits under the pair it
   illustrates, spanning both columns, so each image stays next to its row.
   No new colours -- every text pair here is one already documented at the top
   of this file: --ink 14.48, --body 9.00, --muted 6.96 and --accent 8.30 : 1
   on --paper; captions are --muted at 16px.                                  */
.insight > li > figure { grid-column: 1 / -1; margin-top: 1.5rem; }
figure img.stacked { margin-top: 1rem; }
figure video {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid #2A3550;
  border-radius: 4px;
  background: var(--sunk);
}

/* iterations: one rule down the left so the list reads as a sequence of
   versions rather than as more prose */
.iters {
  list-style: none;
  margin: 2rem 0 0;
  padding: 0;
  display: grid;
  gap: 1.6rem;
  max-width: var(--measure);
}
.iters > li { padding-left: 1.15rem; border-left: 2px solid var(--rule); }
.iter-tag {
  margin: 0 0 .45rem;
  font-family: var(--sans);
  font-size: .74rem;
  font-weight: 500;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--accent);
}
.iters p { margin: 0; max-width: none; }

/* the three learnings, numbered in the accent to match the section kickers */
.learned {
  list-style: none;
  counter-reset: learned;
  margin: 2rem 0 0;
  padding: 0;
  display: grid;
  gap: 1.9rem;
  max-width: var(--measure);
}
.learned > li {
  counter-increment: learned;
  display: grid;
  grid-template-columns: 2.1rem minmax(0, 1fr);
  gap: 0 .9rem;
}
/* all three children are placed explicitly: left to itself, the number would
   take cell 1, the heading cell 2, and the paragraph would wrap onto row 2 in
   the 2.1rem number column and set one word per line */
.learned > li::before {
  content: counter(learned, decimal-leading-zero);
  grid-column: 1;
  grid-row: 1 / span 2;
  font-family: var(--sans);
  font-size: .85rem;
  font-weight: 600;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  padding-top: .3rem;
}
/* h3 is capped at 34ch globally; here it titles a full-measure paragraph */
.learned h3 { grid-column: 2; grid-row: 1; margin: 0 0 .45rem; max-width: none; }
.learned p  { grid-column: 2; grid-row: 2; margin: 0; max-width: none; }

@media (max-width: 30rem) {
  .learned > li { grid-template-columns: 1fr; gap: .35rem; }
  .learned > li::before { grid-column: 1; grid-row: 1; }
  .learned h3 { grid-column: 1; grid-row: auto; }
  .learned p  { grid-column: 1; grid-row: auto; }
}

/* the way out of the page */
.nextcase { margin-top: 3.5rem; border-top: 1px solid var(--rule); }
.nextcase a {
  display: grid;
  gap: .4rem;
  padding: 1.8rem 0 .25rem;
  text-decoration: none;
  color: inherit;
}
.next-label {
  font-family: var(--sans);
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}
.next-title {
  font-family: var(--sans);
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: -.015em;
  color: var(--ink);
  transition: color .18s ease;
}
.next-desc { font-size: 1rem; line-height: 1.5; color: var(--body); max-width: var(--measure); }
.nextcase a:hover .next-title { color: var(--accent); }
.nextcase a:hover .go { transform: translateX(.3rem); }
.nextcase a:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* ================================================== case study architecture ==
   2026-10-06, payment-links first. Headline + italic turn, an opening
   paragraph, a fact strip, numbered figures with a full-screen link, a
   "numbers to add" box and a next-case card with a preview. Same tokens as
   everywhere else; computed on --paper #0F172A: --ink 14.48, --body 9.00,
   --muted 6.96, --accent 8.30 : 1; on --card #182032: --body 8.19,
   --muted 6.34, --accent 7.56 : 1.                                          */

/* the italic turn -- the same serif italic as the home hero */
h1 em, h2 em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -.01em;
}
h1 em { color: var(--accent); }
/* prose straight after a figure needs the same air a heading would have given it */
figure + p { margin-top: 2.25rem; }
h2 em { color: var(--body); }
body.case .masthead h1 { max-width: 22ch; }
/* a headline that reads as two sentences: one per line where the column allows it */
body.case .masthead h1 { font-size: clamp(2.1rem, 6vw, 3rem); }
/* balance would re-wrap the first sentence to even out the lines, ignoring the break */
body.case .masthead h1.h1-wide { max-width: none; text-wrap: wrap; }
/* the longest headline (dashboard): a size step down so its first sentence holds one line */
body.case .masthead h1.h1-long { font-size: clamp(2.1rem, 5.4vw, 2.7rem); }
@media (max-width: 47.99rem) { .br-wide { display: none; } }
/* a two-part headline needs more room than the 30ch cap, or the turn breaks after its first word */
h2:has(em) { max-width: 40ch; }
/* the arrow inside a filled button takes the button's text colour, not the accent it sits on */
.protolink .btn span { color: inherit; }

.lede {
  margin: 1.5rem 0 0;
  font-size: clamp(1.12rem, 2vw, 1.3rem);
  line-height: 1.55;
  color: var(--body);
}
.lede b { color: var(--ink); font-weight: 600; }

/* the case study in four lines, for a reader with three minutes -- a tinted panel
   between the opening paragraph and the metadata (2026-10-07). On --sunk #1B2436:
   labels --accent 7.22, sentences --ink 12.60 : 1.                            */
.summary {
  margin: 1.75rem 0 0;
  padding: .35rem 1.5rem;
  background: var(--sunk);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  border-radius: 10px;
  font-family: var(--sans);
}
.summary > div {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: 1.25rem;
  padding: .85rem 0;
}
.summary > div + div { border-top: 1px solid var(--rule); }
.summary dt {
  padding-top: .15rem;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--accent);
}
.summary dd { margin: 0; font-size: 1rem; line-height: 1.5; color: var(--ink); }
@media (max-width: 36rem) {
  .summary { padding: .25rem 1.1rem; }
  .summary > div { grid-template-columns: 1fr; gap: .3rem; }
}

/* four facts that make the problem concrete -- not achievements -- each tagged
   with the section it belongs to */
.factstrip {
  margin: 3.25rem 0 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.5rem;
  font-family: var(--sans);
}
.factstrip > div {
  display: flex;
  flex-direction: column;
  gap: .45rem;
  padding-top: 1rem;
  border-top: 2px solid var(--accent);
}
.factstrip dt {
  font-size: clamp(2.2rem, 4vw, 2.9rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -.03em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.factstrip dt small { margin-left: .2rem; font-size: .38em; font-weight: 500; letter-spacing: .04em; color: var(--muted); }
.factstrip dd { margin: 0; font-size: .95rem; line-height: 1.45; color: var(--body); }
.factstrip a {
  margin-top: auto;
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
}
.factstrip a::before { content: "\2192\00a0"; color: var(--accent); }
.factstrip a:hover { color: var(--accent); }
@media (max-width: 48rem) { .factstrip { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.75rem 1.25rem; } }

.fignum { font-weight: 600; color: var(--ink); }
.fullscreen { margin-left: .2rem; }
/* a list of links may wrap between them, never inside one */
a.fullscreen, .fullscreen a { white-space: nowrap; }
.fullscreen, .fullscreen a { color: var(--accent); }
figcaption a { text-underline-offset: 3px; }

.numbersbox {
  margin: 2rem 0 0;
  padding: 1.35rem 1.5rem 1.4rem;
  background: var(--card);
  border: 1px dashed #3A4A6B;
  border-radius: 12px;
  font-family: var(--sans);
}
.numbersbox p { margin: 0; font-size: 1rem; color: var(--body); }
.numbersbox-kicker {
  margin-bottom: .6rem !important;
  font-size: .75rem !important;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent) !important;
}
.numbersbox ul { margin: .75rem 0 .9rem; padding-left: 1.2rem; color: var(--ink); line-height: 1.6; }
.numbersbox li::marker { color: var(--accent); content: "\25A1\00a0\00a0"; }

/* next case study, with its preview */
.nextcase a.has-preview {
  grid-template-columns: 13rem minmax(0, 1fr);
  align-items: center;
  gap: 1.5rem;
}
.next-text { display: grid; gap: .4rem; }
.next-shot {
  display: block;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border: 1px solid #2A3550;
  border-radius: 10px;
  background: #F4F5F7;
}
.next-shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top left; transition: transform .5s ease; }
.next-shot.centre img { object-position: center; }
.nextcase a:hover .next-shot img { transform: scale(1.04); }
@media (max-width: 36rem) { .nextcase a.has-preview { grid-template-columns: 1fr; } }
@media (prefers-reduced-motion: reduce) { .next-shot img { transition: none; } .nextcase a:hover .next-shot img { transform: none; } }

/* ------------------------------------------------------- routing decision flow */

.flow {
  list-style: none;
  margin: 2.25rem 0 0;
  padding: 0 0 0 1.75rem;
  border-left: 2px solid var(--rule);
  display: grid;
  gap: 1.6rem;
  max-width: var(--measure);
}
.flow > li { position: relative; }
.flow > li::before {
  content: "";
  position: absolute;
  left: -2.2rem;
  top: .5rem;
  width: .6rem;
  height: .6rem;
  border-radius: 50%;
  background: var(--muted);
  outline: 3px solid var(--paper);
}
.flow > li.is-end::before { background: var(--accent); }

.flow-q {
  display: block;
  font-family: var(--sans);
  font-size: 1.02rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
}
.flow-no {
  margin: .6rem 0 0;
  padding: .7rem .95rem;
  background: var(--sunk);
  border-left: 2px solid var(--stop);
  font-size: .97rem;
  line-height: 1.5;
  max-width: none;
}
.flow-no b { color: var(--stop); font-weight: 600; }
.flow-end {
  display: block;
  font-family: var(--sans);
  font-size: 1.02rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--accent);
}
.flow-note {
  margin-top: 1.5rem;
  max-width: var(--measure);
  font-size: .97rem;
  color: var(--muted);
}

/* -------------------------------------------------------------------- footer */

footer {
  max-width: var(--column);
  margin: 5rem auto 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
  font-family: var(--sans);
  font-size: .9375rem;
  color: var(--muted);
}
footer p { margin: 0; max-width: none; }

a { color: var(--accent); }
a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* --------------------------------------------------------------- responsive */

@media (max-width: 40rem) {
  /* body copy stays at 18px on every width; only the page edge tightens */
  :root { --gutter: 1.25rem; }
  .masthead { padding-top: 3.5rem; }
  .meta { grid-template-columns: 1fr; gap: 1.15rem; }
  section { margin-top: 3.5rem; }
  h2 { font-size: 1.4rem; }
  figure { margin-top: 2rem; }
}

@media (max-width: 26rem) {
  /* at 360px the phone screens stop being legible side by side */
  .pair, .trio { grid-template-columns: 1fr; gap: 1.25rem; }
  .pair img, .trio img { max-width: 20rem; margin: 0 auto; }
}


/* =================================================================== bands ==
   These used to be painted dark against a light reading column. They are not
   painted at all any more -- the page is one ground -- but the CLASS IS KEPT
   because the masthead and the closing section still want its geometry: full
   bleed out of the centred column, and a wider inline padding than the prose.
   The --b-* tokens now resolve to the page tokens, so every rule further down
   that colours band content keeps working and lands on the same values as the
   rest of the page. Nothing here paints a background.                         */

.band {
  --b-bg:     var(--paper);
  --b-ink:    var(--ink);
  --b-body:   var(--body);
  --b-muted:  var(--muted);
  --b-rule:   var(--rule);
  --b-accent: var(--accent);
  color: var(--b-body);
  /* full bleed out of the centred column; max-width must go or the band
     stays capped at the reading measure and leaves a strip of paper */
  max-width: none;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  padding-inline: max(var(--gutter), calc(50vw - var(--column) / 2));
}

.masthead.band { padding-top: 5rem; padding-bottom: 3.25rem; margin-bottom: 0; }
.masthead.band .kicker { color: var(--b-muted); }
.masthead.band h1 { color: var(--b-ink); }
.masthead.band .subtitle { color: var(--b-body); }
.masthead.band .meta { border-color: var(--b-rule); }
.masthead.band .meta dt { color: var(--b-muted); }
.masthead.band .meta dd { color: var(--b-ink); }

.endband {
  max-width: none;
  background: #14161A;
  color: #C2C8D2;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  padding-inline: max(var(--gutter), calc(50vw - var(--column) / 2));
  padding-block: .5rem 3rem;
  margin-top: 4.5rem;
}
.endband section { margin-top: 3.5rem; }
.endband { padding-bottom: 4rem; }
.endband h2 { color: var(--ink); }
.endband h2::before { background: var(--accent); }
.endband p { color: var(--body); }
.endband .seckick { color: var(--muted); }
.endband .seckick span { color: var(--accent); }
.endband footer {
  margin-top: 3.25rem;
  border-color: #2C313A;
  color: #939AA6;
}

/* ------------------------------------------------------- section kickers -- */

.seckick {
  margin: 0 0 .85rem;
  font-family: var(--sans);
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--muted);
}
.seckick span {
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  margin-right: .55rem;
}
/* the kicker replaces the accent dash that used to sit above each h2 */
main > section > .seckick + h2::before,
.endband section > .seckick + h2::before { display: none; }
.glance h2 { margin-top: 0; }

@media (max-width: 40rem) {
  .masthead.band { padding-top: 3.25rem; padding-bottom: 2.5rem; }
  .endband { padding-block: .5rem 2.25rem; margin-top: 3.5rem; }
}


/* ========================================================= portfolio home ==
   The home page reuses the case-study system exactly -- same bands, same
   kickers, same measure -- so moving between them feels like one site.        */

.lang {
  position: absolute;
  top: 2.25rem;
  right: max(var(--gutter), calc(50vw - var(--column) / 2));
  margin: 0;
  font-family: var(--sans);
  font-size: .8rem;
  font-weight: 500;
  letter-spacing: .12em;
  display: flex;
  gap: .65rem;
}
.lang span { color: #F7F8FA; }
.lang a { color: #939AA6; text-decoration: none; }
.lang a:hover { color: #F7F8FA; }

/* ============================================================== work rail ==
   2026-10-06, replacing three stacked full-width cards (960x540 covers -- "the
   images are very big"). After the reference she pointed to: compact cards in a
   row that runs off the right edge of the page, so the next one always peeks in.
   Native horizontal scroll with snap -- NOT the reference's scroll-jacking, which
   turns a three-card shelf into two screens of forced scrolling -- plus arrow
   buttons, a 01 / 03 counter and a progress line for anyone without a trackpad.
   Contrast on --card #182032: --ink 13.19, --body 8.19, --muted 6.34,
   --accent 7.56 : 1.                                                          */

/* the rail is full-bleed to the right; clip, not hidden, so nothing becomes a
   scroll container and the sticky nav keeps working */
html { overflow-x: clip; }

.workhead {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
}
.railcount {
  margin: 0 0 1.4rem;
  font-family: var(--sans);
  font-size: .85rem;
  letter-spacing: .12em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.railcount b { color: var(--accent); font-weight: 600; }

.rail {
  margin-top: 1.25rem;
  /* from the column's left edge to the window's right edge */
  margin-right: calc((100% - 100vw) / 2);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-left: 0;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  padding: .75rem 0 3.5rem;      /* room for the lifted card and its whole glow -- a scroll box clips
                                    shadows, and less than this left a hard edge under the card */
}
.rail::-webkit-scrollbar { display: none; }
.rail:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 8px; }

.rail-track {
  list-style: none;
  margin: 0;
  padding: 0 var(--gutter) 0 0;
  display: flex;
  gap: 1.5rem;
  width: max-content;
  max-width: none;
}
.rail-track > li { scroll-snap-align: start; }

.casecard {
  display: flex;
  flex-direction: column;
  width: min(26rem, 82vw);
  height: 100%;
  color: inherit;
  text-decoration: none;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 14px;
  overflow: hidden;
  transition: border-color .2s ease, box-shadow .25s ease, transform .2s ease;
}
.casecard:hover, .casecard.is-current {
  border-color: rgba(78, 193, 197, .7);
  box-shadow: 0 0 0 1px rgba(78, 193, 197, .25), 0 18px 48px -12px rgba(78, 193, 197, .28);
}
.casecard:hover { transform: translateY(-3px); }
.casecard:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* the screenshot is a strip, not a poster: 16:10, cropped from the top of the
   interface where the identifying chrome is */
.casecard-shot {
  display: block;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: #F4F5F7;
  border-bottom: 1px solid #2A3550;
}
.casecard-shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top left;
  border: 0;
  transition: transform .5s cubic-bezier(.2, .7, .3, 1);
}
.casecard-shot.centre img { object-position: center; }
.casecard:hover .casecard-shot img { transform: scale(1.03); }

.casecard-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 1.3rem 1.4rem 1.2rem;
}
.casecard-num {
  font-family: var(--sans);
  font-size: .8rem;
  font-weight: 500;
  letter-spacing: .12em;
  color: var(--accent);
}
.casecard h3 {
  margin: .55rem 0 .5rem;
  max-width: none;
  font-size: 1.22rem;
  line-height: 1.25;
  letter-spacing: -.015em;
  text-wrap: balance;
  transition: color .18s ease;
}
.casecard:hover h3 { color: #FFFFFF; }
.casecard-desc {
  margin: 0;
  max-width: none;
  font-size: .98rem;
  line-height: 1.5;
  color: var(--body);
}
.casecard-meta {
  margin: auto 0 0;               /* pinned to the foot, so the three footers align */
  padding-top: 1.1rem;
  display: flex;
  align-items: center;
  gap: .6rem;
  max-width: none;
  font-family: var(--sans);
  font-size: .76rem;
  font-weight: 500;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--muted);
}
.casecard-go {
  display: inline-grid;
  place-items: center;
  width: 1.7rem;
  height: 1.7rem;
  border: 1px solid var(--accent);
  border-radius: 50%;
  color: var(--accent);
  font-size: .85rem;
  letter-spacing: 0;
  transition: background .2s ease, color .2s ease, transform .2s ease;
}
.casecard:hover .casecard-go { background: var(--accent); color: var(--paper); transform: translateX(2px); }

.railbar {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin-top: -2.25rem;           /* back up into the rail's glow padding */
}
.railprogress {
  position: relative;
  flex: 1;
  height: 2px;
  background: var(--rule);
  border-radius: 2px;
  overflow: hidden;
}
.railprogress span {
  position: absolute;
  inset: 0 auto 0 0;
  width: 33.4%;
  background: var(--accent);
  border-radius: 2px;
  transition: transform .2s ease, width .2s ease;
}
.railbtns { display: flex; gap: .5rem; }
.railbtn {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: transparent;
  color: var(--ink);
  font: 500 1rem var(--sans);
  cursor: pointer;
  transition: border-color .18s ease, color .18s ease;
}
.railbtn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.railbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.railbtn:disabled { opacity: .35; cursor: default; }
/* when every card fits, there is nothing to page through; the counter shows
   only while the rail genuinely pages one card at a time (set by the script) */
.rail-fits .railbtns, .rail-fits .railprogress { visibility: hidden; }
.railcount { visibility: hidden; }
.rail-pages .railcount { visibility: visible; }

@media (prefers-reduced-motion: reduce) {
  .casecard, .casecard-shot img, .casecard-go, .railprogress span { transition: none; }
  .casecard:hover { transform: none; }
  .casecard:hover .casecard-shot img { transform: none; }
}

.contact { list-style: none; margin: 1.75rem 0 0; padding: 0; display: grid; gap: .9rem; }
.contact li {
  display: grid;
  grid-template-columns: 7rem minmax(0, 1fr);
  gap: 1rem;
  align-items: baseline;
  font-family: var(--sans);
  font-size: 1.02rem;
}
.contact span {
  font-size: .75rem;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: #939AA6;
}
.contact a {
  justify-self: start;
  color: #F7F8FA;
  text-decoration: none;
  border-bottom: 1px solid #3A4048;
}
.contact a:hover { border-color: var(--accent); color: var(--accent); }

@media (max-width: 40rem) {
  .lang { top: 1.5rem; }
  .contact li { grid-template-columns: 1fr; gap: .2rem; }
}


/* ---------------------------------------------------------- experience list --
   Year in a muted gutter, role and organisation as the heading, one or two
   sentences, then the domains. Collapses to a single column on narrow screens. */

.cv { list-style: none; margin: 2.5rem 0 0; padding: 0; display: grid; gap: 0; }
.cv > li {
  display: grid;
  grid-template-columns: 9.5rem minmax(0, 1fr);
  gap: 0 1.75rem;
  padding: 1.6rem 0;
  border-top: 1px solid var(--rule);
}
.cv > li:last-child { border-bottom: 1px solid var(--rule); }

/* a grouping label above several roles at one employer: quieter than a role title,
   and the roles under it keep their own dates and rules */
.cv > li.cv-group { padding-bottom: .4rem; }
.cv-group-name {
  font-family: var(--sans);
  font-size: .8rem !important;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink);
  padding-top: .3rem;
  margin-bottom: .35rem !important;
}
.cv > li.cv-group p:not(.cv-group-name):not(.cv-years) { color: var(--muted); font-style: italic; }

.cv-years {
  margin: 0;
  padding-top: .3rem;
  font-family: var(--sans);
  font-size: .8rem;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.cv h3 {
  margin: 0 0 .5rem;
  max-width: none;
  font-size: 1.08rem;
  line-height: 1.35;
}
/* muted, not accent: nothing here is a link and accent would imply one */
.cv-org { color: var(--muted); font-weight: 400; }
.cv-org::before { content: "· "; color: var(--muted); }
/* indented by the years column + gap, so it ends on the same edge as the text above */
.cv > li p { margin: 0; max-width: calc(var(--measure) - 11.25rem); font-size: 1rem; }

/* outcomes under a role, each linking to its case study (2026-10-06).
   --body on --paper 9.00 : 1, the accent links and markers 8.30 : 1 */
.cv-wins {
  margin: .75rem 0 0;
  padding-left: 1.1rem;
  list-style: disc;          /* nested inside .cv's list, it would default to hollow circles */
  max-width: calc(var(--measure) - 11.25rem);
  font-size: 1rem;
  line-height: 1.55;
}
.cv-wins li + li { margin-top: .35rem; }
.cv-wins li::marker { color: var(--accent); }
.cv-wins a { color: var(--ink); text-decoration-color: var(--accent); text-underline-offset: 3px; }
.cv-wins a:hover { color: var(--accent); }

.cv-tags { margin-top: .9rem !important; display: flex; flex-wrap: wrap; gap: .45rem; }
.cv-tags span {
  font-family: var(--sans);
  font-size: .78rem;
  letter-spacing: .03em;
  color: var(--muted);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: .24rem .65rem;
}

@media (max-width: 40rem) {
  .cv > li { grid-template-columns: 1fr; gap: .5rem; }
  .cv-years { padding-top: 0; }
}


/* ================================================================== hero ===
   Home page only. The case study keeps .masthead.band untouched.             */

.hero {
  position: relative;
  padding-inline: max(var(--gutter), calc(50vw - var(--column) / 2));
  min-height: 90vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: 5rem;
  padding-bottom: 3rem;
}

.hero h1 {
  margin: 0;
  max-width: 24ch;
  font-size: clamp(2.6rem, 7.4vw, 5.4rem);
  font-weight: 600;
  line-height: 1.0;
  letter-spacing: -.042em;
  color: #F1F5F9;
}
.hero h1 em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -.02em;
  color: var(--accent);
}

.hero-lede {
  margin: 2rem 0 0;
  max-width: var(--measure);
  font-size: clamp(1.05rem, 1.7vw, 1.32rem);
  line-height: 1.5;
  color: var(--body);
}
/* the break after "checkout" only holds while line one fits whole */
@media (max-width: 47.99rem) { .hero-lede br { display: none; } }

.cta { display: flex; flex-wrap: wrap; gap: .75rem; margin: 2.5rem 0 0; max-width: none; }
.btn {
  display: inline-block;
  padding: .85rem 1.6rem;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: var(--accent);
  color: #0E1116;
  font-family: var(--sans);
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  transition: background .18s ease, color .18s ease, transform .18s ease;
}
.btn:hover { background: #63CDD1; border-color: #63CDD1; transform: translateY(-1px); }
.btn-ghost { background: transparent; color: var(--ink); border-color: #33415B; }
.btn-ghost:hover { background: transparent; border-color: var(--ink); color: #F1F5F9; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.stats {
  margin: 4rem 0 0;
  padding-top: 2rem;
  border-top: 1px solid var(--rule);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem 2.5rem;
}
.stats dt {
  font-family: var(--sans);
  font-size: clamp(1.7rem, 3.6vw, 2.6rem);
  font-weight: 600;
  letter-spacing: -.03em;
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.stats dt span { color: var(--accent); }
.stats dd {
  margin: .7rem 0 0;
  font-size: .95rem;
  line-height: 1.45;
  color: var(--muted);
  max-width: 26ch;
}

.whoami {
  margin: 3rem 0 0;
  font-family: var(--sans);
  font-size: .82rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);   /* #6B7280 was only 3.4:1 on the new ground */
}

.go {
  display: inline-block;
  margin-left: .35rem;
  color: var(--accent);
  transition: transform .22s ease;
}

@media (prefers-reduced-motion: reduce) {
  .go, .btn { transition: none; }
}

@media (max-width: 46rem) {
  .hero { min-height: 0; padding-top: 4rem; padding-bottom: 2.5rem; }
  .stats { grid-template-columns: 1fr; gap: 1.6rem; margin-top: 3rem; }
  .stats dd { max-width: 40ch; }
  .whoami { margin-top: 2.25rem; }
}


/* ============================================================== one ground ==
   Nothing paints its own background any more. .band, .endband and .hero are
   kept for their geometry only (see above); the ground comes from html/body and
   runs unbroken from the top of the masthead to the foot of the page.         */

.band, .endband, .hero { background: transparent; }

figure img { border-color: #2A3550; }


/* The spotlight: one soft wash that follows the cursor. A single fixed layer,
   painted under the content, never interactive. Deliberately faint -- it should
   register as depth rather than as an effect. Delete the element from the two
   HTML pages and this block to remove it. */
.spotlight {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(640px at var(--mx, 78%) var(--my, -10%),
              rgba(78, 193, 197, .075), transparent 76%);
  transition: background .2s ease-out;
}
header, main { position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  .spotlight { transition: none; }
}


/* ================================================================= site nav ==
   Added 2026-09-03. Transparent while the page is at the top -- there is one
   ground now and nothing to divide -- then frosted translucent once anything
   scrolls under it. Sticky because the case study runs to ~12,000px and the
   section links are worth keeping within reach that whole way down.           */

.sitenav {
  --b-ink:    var(--ink);
  --b-body:   var(--body);
  --b-muted:  var(--muted);
  --b-rule:   var(--rule);
  --b-accent: var(--accent);

  position: sticky;
  top: 0;
  z-index: 30;

  /* same full-bleed trick as .band, and the same inline padding as .hero so the
     wordmark sits on the same optical line as the h1 below it */
  max-width: none;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  padding-inline: max(var(--gutter), calc(50vw - var(--column) / 2));

  /* clear at rest: the nav sits on the same ground as everything else, so at
     the top of the page there is nothing for it to separate */
  background: transparent;
  border-bottom: 1px solid transparent;

  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
  min-height: 4.25rem;
  font-family: var(--sans);
}

/* once the page has moved under it, the nav has to hold itself apart from the
   content scrolling past: the ground at 72% with a blur behind it, and a
   hairline. .is-stuck is set from script as soon as the page leaves the top. */
.sitenav.is-stuck {
  background: rgba(15, 23, 42, .72);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  backdrop-filter: blur(14px) saturate(140%);
  border-bottom-color: var(--rule);
}
/* without backdrop-filter, translucency would let text collide with text */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .sitenav.is-stuck { background: rgba(15, 23, 42, .96); }
}

.wordmark {
  justify-self: start;
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: -.012em;
  line-height: 1;
  color: var(--b-ink);
  text-decoration: none;
  white-space: nowrap;
}
.wordmark:hover { color: #FFFFFF; }
.wordmark:focus-visible { outline: 2px solid var(--b-accent); outline-offset: 4px; border-radius: 2px; }

.navlinks {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: none;
  display: flex;
  gap: 1.9rem;
}
.navlinks a {
  position: relative;
  display: block;
  padding: .4rem 0;
  font-size: .8rem;
  font-weight: 500;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--b-muted);
  text-decoration: none;
  transition: color .18s ease;
}
.navlinks a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--b-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .22s ease;
}
.navlinks a:hover { color: var(--b-ink); }
.navlinks a[aria-current] { color: var(--b-ink); }
.navlinks a[aria-current]::after { transform: scaleX(1); }
.navlinks a:focus-visible { outline: 2px solid var(--b-accent); outline-offset: 3px; }

.navend {
  justify-self: end;
  display: flex;
  align-items: center;
  gap: .7rem;
}

.btn-sm { padding: .58rem 1.15rem; font-size: .9rem; }

/* the skip link has to clear the nav when it is focused */
.skip:focus { z-index: 40; }

/* anchored sections must not land underneath the bar */
#top, #work, #experience, #contact { scroll-margin-top: 5rem; }

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
@media (prefers-reduced-motion: reduce) {
  .navlinks a, .navlinks a::after { transition: none; }
}

/* ------------------------------------------------------------- page width --
   The home page runs wider than the case study. It has no 12,000px of prose to
   protect -- it has display type, a work card and a three-column stat row, all
   of which want the room -- so it moves --column, and the masthead, the nav,
   main and the closing band all follow it to the same edge. Text stops at
   --measure (832px), short of that edge.                                    */

body.home { --column: 60rem; }

/* case studies take the home page's width too (2026-10-06, her call: the 46rem
   column left too much empty space). Text still stops at --measure (832px), as
   on the home page; figures now fill the column instead of breaking out of it. */
/* and the prose runs the full column, the same width as the captions under the
   figures (her call, same day) -- so --measure is lifted on these pages only */
body.case { --column: 60rem; --measure: none; }
body.case figure { width: min(var(--column), calc(100vw - 3rem)); }


/* the hero asked for 90vh of a viewport that no longer starts at the top */
@media (min-width: 46.01rem) {
  .sitenav + .hero { min-height: calc(90vh - 4.25rem); padding-top: 3.25rem; }
}

/* two rows below the point where three columns stop fitting: identity and
   controls on top, the links as a full-width strip under them */
@media (max-width: 62rem) {
  .sitenav {
    grid-template-columns: 1fr auto;
    row-gap: .1rem;
    padding-top: .65rem;
    padding-bottom: .55rem;
    min-height: 0;
  }
  .wordmark { grid-column: 1; grid-row: 1; }
  .navend   { grid-column: 2; grid-row: 1; }
  .navlinks { grid-column: 1 / -1; grid-row: 2; gap: 1.5rem; }
  #top, #work, #experience, #contact { scroll-margin-top: 6.5rem; }
}

@media (max-width: 26rem) {
  .wordmark { font-size: .95rem; }
  .navlinks { gap: 1.15rem; }
  .navlinks a { font-size: .74rem; letter-spacing: .1em; }
  .btn-sm { padding: .5rem .85rem; font-size: .84rem; }
}


/* ========================================================== case study nav ==
   Added 2026-10-06. The home page's sticky bar, reused on every case study:
   wordmark home on the left, Get in touch on the right, and in the middle a
   sections menu in place of the home page's three anchor links -- a case study
   has seven to fourteen sections, too many to lay out in a row. It is a plain
   <details>, so it opens without script; case-nav.js names the current section
   in its label, marks it in the list and draws the progress line.
   Contrast, computed (WCAG 2.1). Menu panel --card #182032: --ink 13.19,
   --body 8.19, --accent 7.56 : 1. Hovered / current row --sunk #1B2436:
   --ink 12.60, --body 7.83, --accent 7.22 : 1. Summary on the ground
   (--paper): --ink 14.48, --muted 6.96 : 1. All clear AA; most clear AAA.   */

.toc { position: relative; justify-self: center; }
.toc summary {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .45rem .9rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  list-style: none;
  cursor: pointer;
  font-size: .8rem;
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
  transition: border-color .18s ease;
}
.toc summary::-webkit-details-marker { display: none; }
.toc summary:hover, .toc[open] summary { border-color: var(--accent); }
.toc summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.toc-label {
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--muted);
}
.toc-current:empty { display: none; }
.toc summary::after {          /* the chevron, turned when the list is open */
  content: "";
  width: .42rem;
  height: .42rem;
  margin-top: -.2rem;
  border-right: 1.5px solid var(--muted);
  border-bottom: 1.5px solid var(--muted);
  transform: rotate(45deg);
  transition: transform .18s ease;
}
.toc[open] summary::after { transform: rotate(-135deg); margin-top: .15rem; }

.toc-list {
  position: absolute;
  top: calc(100% + .6rem);
  left: 50%;
  transform: translateX(-50%);
  width: min(27rem, calc(100vw - 2rem));
  max-height: calc(100vh - 7rem);
  overflow-y: auto;
  margin: 0;
  padding: .45rem;
  list-style: none;
  background: var(--card);
  border: 1px solid #2A3550;
  border-radius: 10px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, .45);
}
.toc-list a {
  display: grid;
  grid-template-columns: 1.9rem 1fr;
  align-items: baseline;
  padding: .55rem .7rem;
  border-radius: 6px;
  font-size: .92rem;
  line-height: 1.35;
  color: var(--body);
  text-decoration: none;
}
.toc-list a span { font-size: .75rem; font-weight: 500; letter-spacing: .08em; color: var(--accent); }
.toc-list a:hover { background: var(--sunk); color: var(--ink); }
.toc-list a[aria-current] { background: var(--sunk); color: var(--ink); box-shadow: inset 2px 0 0 var(--accent); }
.toc-list a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* the reading progress line along the bar's bottom edge */
.readbar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  pointer-events: none;
}
.readbar span {
  display: block;
  height: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
}

body.case section[id] { scroll-margin-top: 5.5rem; }

/* rule-engine case study (2026-10-06) -------------------------------------
   Contrast, computed against --paper #0F172A: --muted 6.96, --ink 14.48,
   --accent 8.30 : 1; the button is the existing .btn (ink on accent).       */
.protolink { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1.1rem; margin: 2rem 0 0; }
.protolink span { font-family: var(--sans); font-size: .92rem; color: var(--muted); }
.meta dd a { color: var(--accent); text-underline-offset: 3px; }
/* the outcome line under each position in "Where I disagreed" */
.decision { font-family: var(--sans); font-size: .98rem; color: var(--muted); }
.decision b { color: var(--accent); font-weight: 600; margin-right: .3em; }
/* machine ids quoted in prose */
main code {
  font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
  font-size: .86em;
  padding: .08em .35em;
  border-radius: 4px;
  background: var(--sunk);
  color: var(--ink);
}
/* a single dropdown capture is small and tall -- at full column width it would
   be blown up to 2.5x, so it keeps roughly its natural size */
figure img.narrow { max-width: 25rem; }
/* two narrow captures compared side by side (the audit's two menus): near natural size,
   so the pair reads as a comparison rather than two blown-up strips */
.pair.sidepair { max-width: 34rem; }

/* reading time rides on the kicker, quieter than the label it follows; worked
   out from the page's word count at 230 words a minute -- recount on edits */
.readtime { margin-left: .35em; color: var(--muted); }

@media (max-width: 62rem) {
  .casenav .toc { grid-column: 1 / -1; grid-row: 2; justify-self: start; margin-top: .35rem; }
  .casenav .toc-list { left: 0; transform: none; }
  body.case section[id] { scroll-margin-top: 8rem; }
}
@media (max-width: 26rem) {
  .toc summary { font-size: .76rem; padding: .4rem .8rem; }
}
@media (prefers-reduced-motion: reduce) {
  .toc summary, .toc summary::after { transition: none; }
}


/* --------------------------------------------------------------- CV button --
   The little PDF tag rides inside the ghost button so the label stays a verb
   and the format is still stated -- people decide differently about a download
   than about a link.                                                          */

.btn-ext {
  display: inline-block;
  margin-left: .5rem;
  padding: .1rem .32rem;
  border: 1px solid currentColor;
  border-radius: 3px;
  font-size: .62em;
  font-weight: 600;
  letter-spacing: .09em;
  vertical-align: .12em;
  opacity: .72;
}

/* text for screen readers only, e.g. that a link opens in a new tab */
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* the hidden text is placed against its card, not the page -- inside the scrolling rail it would
   otherwise widen the whole document */
.casecard { position: relative; }

/* ===========================================================================
   Light pages: the two Amazon case studies, whose Behance boards are white.
   On a white ground the boards sit flush, so they lose their frame. The cyan
   accent is only 2.15 : 1 on white, so the accent becomes the print teal.
   Contrast on #FFFFFF: --ink 17.85, --body 10.35, --muted 5.66, --accent 5.33 : 1.
   =========================================================================== */
html:has(body.light) { background: #FFFFFF; color-scheme: light; }
body.light {
  --paper:  #FFFFFF;
  --sunk:   #F1F5F9;
  --card:   #FFFFFF;
  --ink:    #0F172A;
  --body:   #334155;
  --muted:  #5B6779;
  --rule:   #E2E8F0;
  --accent: #107977;
}
body.light .spotlight { display: none; }
body.light figure img { border: 0; border-radius: 0; background: transparent; }
body.light .next-shot,
body.light .toc-list { border-color: var(--rule); }
body.light .toc-list { box-shadow: 0 18px 40px rgba(15, 23, 42, .12); }
body.light .sitenav.is-stuck { background: rgba(255, 255, 255, .8); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  body.light .sitenav.is-stuck { background: rgba(255, 255, 255, .96); }
}
body.light .wordmark:hover { color: var(--accent); }
body.light .btn { color: #FFFFFF; }
body.light .btn:hover { background: #0C625F; border-color: #0C625F; }

/* ------------------------------------------------ masthead: two cards
   The summary panel and the project details sit side by side as two cards
   of the same height. Below 52rem the details card drops under the summary. */
.mh-pair {
  margin: 1.75rem 0 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 15.5rem;
  gap: 1.25rem;
  align-items: stretch;
}
.mh-pair .summary { margin: 0; }
.meta.meta-card {
  margin: 0;
  padding: 1.15rem 1.5rem;
  grid-template-columns: 1fr;
  align-content: start;
  gap: 0;
  background: var(--sunk);
  border: 1px solid var(--rule);
  border-radius: 10px;
}
.meta.meta-card > div { padding: .9rem 0; }
.meta.meta-card > div:first-child { padding-top: .2rem; }
.meta.meta-card > div + div { border-top: 1px solid var(--rule); }
@media (max-width: 52rem) {
  .mh-pair { grid-template-columns: 1fr; }
}
@media (max-width: 36rem) {
  .meta.meta-card { padding: .9rem 1.1rem; }
}

/* the anonymisation note sits under "Selected work", where a reader arrives,
   rather than as the last line before the end of the page                    */
.worknote {
  margin: .6rem 0 0;
  font-family: var(--sans);
  font-size: .95rem;
  line-height: 1.5;
  color: var(--muted);
}

/* ===========================================================================
   Case-study figures, smaller (2026-10-13). An image is never wider than
   44rem, and never taller than 32rem -- a portrait screenshot used to fill the
   whole column and run past a screen and a half. Every image opens full size
   in an overlay (lightbox.js), so nothing is lost by showing it smaller.
   The Amazon boards are long pages of their own, so they keep the width cap
   but not the height cap.
   =========================================================================== */
/* --ar (width / height) is written on each figure image, so the caps can be
   worked out without distorting it: 44rem wide at most, and only as wide as
   keeps it under 32rem tall. */
body.case figure img {
  width: 100%;
  max-width: min(100%, 44rem, calc(32rem * var(--ar, 10)));
  margin-inline: auto;
  cursor: zoom-in;
}
body.case.light figure img { max-width: min(100%, 44rem); }
body.case figure video { max-width: min(100%, 44rem); margin-inline: auto; }
body.case .pair, body.case .trio { max-width: 44rem; margin-inline: auto; align-items: start; }
body.case figcaption { max-width: 44rem; }
/* the two narrower cases already set on some pages keep winning */
body.case figure img.narrow { max-width: min(100%, 25rem, calc(32rem * var(--ar, 10))); }
body.case .pair.sidepair { max-width: 34rem; }

/* the overlay */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 3.5rem 1.25rem 1.25rem;
  overflow: auto;
  background: rgba(8, 12, 22, .9);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  cursor: zoom-out;
}
.lightbox[hidden] { display: none; }
.lightbox img {
  display: block;
  max-width: min(94vw, 100%);
  max-height: calc(100vh - 5rem);
  width: auto;
  height: auto;
  border-radius: 6px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .5);
  background: #FFFFFF;
}
/* a long board (the Amazon pages) scrolls inside the overlay at a readable width */
.lightbox.is-tall { place-items: start center; }
.lightbox.is-tall img { max-height: none; width: min(94vw, 70rem); }
.lightbox-close {
  position: fixed;
  top: .9rem;
  right: 1rem;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid rgba(255, 255, 255, .25);
  border-radius: 999px;
  background: rgba(15, 23, 42, .85);
  color: #FFFFFF;
  font: 400 1.4rem/1 var(--sans);
  cursor: pointer;
}
.lightbox-close:hover { border-color: #FFFFFF; }
.lightbox-close:focus-visible { outline: 2px solid #4EC1C5; outline-offset: 3px; }
html.lightbox-open { overflow: hidden; }

/* a figure whose screen has not been supplied yet: a dashed slot naming the
   screen that goes there. Remove the class once the image is in. */
.fig-todo .fig-slot {
  max-width: 44rem;
  margin-inline: auto;
  aspect-ratio: 16 / 9;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  border: 1px dashed #3A4A6B;
  border-radius: 6px;
  background: var(--sunk);
  font-family: var(--sans);
  font-size: .95rem;
  line-height: 1.5;
  text-align: center;
  color: var(--muted);
}
