/* ══════════════════════════════════════════════════════════════════
   Version C — editorial two-column case study
   Informed by the Cornershop case study layout: small tight type,
   a narrow reading column, and a sticky credits rail on the left.

   Structure per page:
     .cover      title + discipline, description opposite
     .plate      full-bleed hero on a tinted panel
     .article    grid: sticky .credits | .content
   Collapses to one column at 850px, where .credits goes static.
   ══════════════════════════════════════════════════════════════════ */

@font-face{font-family:"Neue Montreal";src:url("../fonts/neue-montreal-book.woff2") format("woff2");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Neue Montreal";src:url("../fonts/neue-montreal-medium.woff2") format("woff2");font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:"Neue Montreal";src:url("../fonts/neue-montreal-bold.otf") format("opentype");font-weight:700;font-style:normal;font-display:swap}   /* only loads where something is set bold */

:root{
  --ink:#000;
  --body:#3f3f44;
  --muted:#6f7278;   /* 5.0:1 — was #888a90 at 3.45:1, under AA */
  --hair:#e7e8e9;
  --panel:#f2f2f3;
  --bg:#fff;
  --accent:#101050;

  --sans:"Neue Montreal",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;

  /* small, tight scale: hierarchy comes from weight and space, not size */
  /* one multiplier drives the whole scale, so the size control keeps the
     hierarchy intact instead of resizing body copy on its own */
  --type:1;
  /* Same four sizes as the live pages, matched to the Laura Sandoval
     reference: 32 / 22 / 16 / 14. --type still scales all of them. */
  --fs-title:clamp(calc(26px * var(--type)),2.4vw,calc(32px * var(--type)));
  --fs-h2:calc(22px * var(--type));
  --fs-body:calc(16px * var(--type));
  --fs-meta:calc(14px * var(--type));
  --fs-cap:calc(14px * var(--type));

  /* ── measure & grid ────────────────────────────────────────────────
     Frame is a 1440 desktop artboard; nothing spans edge to edge except
     the hero plate. Inside it the article sits in its own narrower band,
     so the page reads as a column of text rather than a full-width sheet.

       rail    outer page padding — 24px on a phone up to 112px on desktop
       band    the article's own width:320 + 72 + 560 = 952
       ratio   sidebar : gutter : text = 1 : 0.22 : 1.75, i.e. close to the
               classic 1:2 editorial split (and to 4:7 of a 12-column grid)
       measure 560px ≈ 68 characters at 16px — inside the 45–75ch range
               that typography research treats as comfortable
     ------------------------------------------------------------------ */
  /* One content band for the whole page. The masthead, cover, hero plate,
     article, next-project block and footer all resolve to the same width, so
     the body starts and ends exactly where the header does — rather than the
     header running wide while a narrower article sat centred inside it. */
  --rail:clamp(24px,6vw,112px);
  --band:952px;
  --maxw:calc(var(--band) + var(--rail) * 2);
  --measure:560px;
  --credits-w:320px;
  --credits-gap:24px;    /* between the rail's two text columns */
  --gap:72px;
  --sticky-top:clamp(20px,3vw,40px);
  --secnav-h:0px;        /* set by c.js while the top bar is showing */
}

/* ── layout presets, switched from the panel ─────────────
   Each keeps sidebar + gutter + text adding up to the band, so the two
   columns always meet the edges instead of leaving a dead strip.
   ------------------------------------------------------ */
:root[data-pad="compact"]{--rail:clamp(20px,4vw,72px);--band:880px;--credits-w:300px;--gap:60px;--measure:520px}
:root[data-pad="roomy"]{--rail:clamp(28px,8vw,160px);--band:1024px;--credits-w:340px;--gap:84px;--measure:600px}

:root[data-type="s"]{--type:.875}   /* 14px body */
:root[data-type="l"]{--type:1.125}  /* 18px */
:root[data-type="xl"]{--type:1.25}  /* 20px */

/* one column of pairs in the rail, for comparison against the default two */
:root[data-rail="one"] .credits{columns:1}
:root[data-rail="one"] .credits__group:nth-child(4){break-before:auto}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font-family:var(--sans);font-size:var(--fs-body);font-weight:400;line-height:1.6;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  overflow-x:hidden;
}
img{max-width:100%;height:auto;display:block}
h1,h2,h3{margin:0;font-weight:500;line-height:1.2}
p{margin:0}
ul,ol,dl{margin:0;padding:0}
figure{margin:0}
a{color:inherit}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

.wrap{width:100%;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--rail)}
.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ── top bar ─────────────────────────────────────────── */
.topbar{padding-block:clamp(20px,2.6vw,32px);display:flex;align-items:center;justify-content:space-between;gap:20px}
.topbar img{width:clamp(96px,8vw,116px)}
.topbar nav{display:flex;gap:20px;font-size:var(--fs-meta);color:var(--muted)}
.topbar nav a{text-decoration:none;transition:color .15s ease}
.topbar nav a:hover{color:var(--ink)}

/* ── cover ───────────────────────────────────────────── */
.cover{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:var(--gap);align-items:start;
  padding-block:clamp(28px,4vw,52px) clamp(28px,3.4vw,44px);
}
.cover__title{font-size:var(--fs-title);letter-spacing:-.02em;line-height:1.2;max-width:18ch}
.cover__discipline{font-size:var(--fs-meta);color:var(--muted);margin-top:12px}
.cover__desc{font-size:var(--fs-body);line-height:1.5;max-width:34ch;color:var(--ink)}

/* ── full-bleed hero plate ───────────────────────────── */
.plate{background:var(--panel);padding:clamp(24px,4vw,56px) 0}
.plate__inner{width:100%;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--rail)}
.plate img{width:100%;max-width:var(--w,960px);margin-inline:auto}
.plate__cap{max-width:var(--w,960px);margin:12px auto 0;font-size:var(--fs-cap);line-height:1.6;color:var(--muted);text-align:center}

/* ── article: sticky credits + reading column ────────── */
.article{
  display:grid;
  grid-template-columns:var(--credits-w) minmax(0,var(--measure));
  gap:var(--gap);
  align-items:start;
  width:100%;max-width:var(--band);margin-inline:auto;
  padding-block:clamp(36px,5vw,72px) clamp(48px,7vw,104px);
}
/* c.js wraps the credits and the section list in .rail and moves the
   stickiness up to it; the rule on .credits is the no-script fallback. */
.rail{position:sticky;top:calc(var(--secnav-h) + var(--sticky-top));display:flex;flex-direction:column}
.article > .credits{position:sticky;top:calc(var(--secnav-h) + var(--sticky-top))}
/* Column one is the short metadata, column two the lists. Multi-column
   rather than grid, so each column packs to its own content instead of
   sharing row heights — a tall Discipline would otherwise push Sector
   down to meet it. The break is forced so the split never re-balances. */
.credits{
  columns:2;column-gap:var(--credits-gap);
  orphans:1;widows:1;
}
.credits__group{margin-bottom:clamp(16px,2vw,24px)}
.credits__group:nth-child(4){break-before:column}
.credits__group{display:flex;flex-direction:column;gap:3px;break-inside:avoid}
/* the two long lists earn a full row of the rail */
.credits__group--wide{grid-column:1 / -1}
.credits dt{font-size:var(--fs-meta);color:var(--muted);text-transform:uppercase;letter-spacing:.06em}   /* one label voice site-wide: uppercase, spaced */
.credits dd{margin:0;font-size:var(--fs-meta);color:var(--ink);line-height:1.5}
.credits ul{list-style:none}
/* kept for any group that still wants the full width of the rail */
.credits__group--wide{grid-column:1 / -1}
.credits li{font-size:var(--fs-meta);color:var(--ink);line-height:1.5}
/* a value still to be filled in */
/* a placeholder credit ("add dates") looks like the rest; build.py leaves it out of production */

.content{max-width:var(--measure);display:flex;flex-direction:column;gap:clamp(24px,2.6vw,32px)}
.content h2{font-size:var(--fs-h2);font-weight:500;line-height:1.4;letter-spacing:-.03em;margin-top:clamp(20px,2.8vw,32px);
  /* clear the sticky bar when a section link jumps here */
  scroll-margin-top:calc(var(--secnav-h) + 28px)}
.content h2:first-child{margin-top:0}
.content p{color:var(--ink);line-height:1.6}
.content strong{font-weight:500;color:var(--ink)}
.content a{color:var(--ink);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px}
.content ul{padding-left:1.1em;display:flex;flex-direction:column;gap:.45em;color:var(--body)}
.content li{list-style:disc}
.content li strong{color:var(--ink)}
/* tables (written by content/sync.py from Markdown tables): the first column
   names each row; on phones each row stacks into a small card, each cell
   introduced by its column's name (data-label) */
.cs-table{overflow-x:auto}
/* [[Out of scope]] in the Markdown: a small chip beside a label */
.cs-chip{display:inline-flex;align-items:center;margin-left:8px;padding:3px 8px;border-radius:999px;border:1px solid var(--hair);
  background:var(--panel);font:400 12px/1.2 var(--sans);letter-spacing:.01em;color:var(--body);white-space:nowrap;vertical-align:middle}
.cs-table table{width:100%;border-collapse:collapse;font-size:var(--fs-meta);line-height:1.5;color:var(--body)}
.cs-table th,.cs-table td{text-align:left;vertical-align:top;padding:12px 16px 12px 0;border-top:1px solid var(--hair)}
.cs-table thead th{border-top:0;padding-top:0;font-weight:400;color:var(--muted);font-size:var(--fs-cap)}
.cs-table tbody th{font-weight:500;color:var(--ink);width:24%}
/* Inner Bloom "What could go wrong": three equal columns, so the risk isn't squeezed by a long response */
[data-sp="risks"] .cs-table table{table-layout:fixed}
[data-sp="risks"] .cs-table tbody th{width:auto}
.cs-table tr > :last-child{padding-right:0}
/* a chip in a narrow first column drops under its term: start it flush left, not indented */
.cs-table tbody th .cs-chip{display:flex;width:fit-content;margin:6px 0 0}
@media (max-width:560px){
  .cs-table thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .cs-table table,.cs-table tbody,.cs-table tr,.cs-table th,.cs-table td{display:block;width:auto}
  .cs-table tr{padding:14px 0;border-top:1px solid var(--hair)}
  .cs-table tbody th,.cs-table td{border:0;padding:0}
  .cs-table tbody th{margin-bottom:6px;width:auto}
  .cs-table tbody th .cs-chip{display:inline-flex;margin:0 0 0 8px}
  .cs-table td + td,.cs-table th + td{margin-top:6px}
  .cs-table td::before{content:attr(data-label);display:block;color:var(--muted);font-size:var(--fs-cap)}
}

/* figures inside the reading column */
.fig{display:flex;flex-direction:column;gap:12px}
.fig img{width:100%;background:var(--panel)}
.fig figcaption{font-size:var(--fs-cap);color:var(--muted);line-height:1.5}
.fig--tint img{background:var(--tint,var(--panel));padding:var(--pad,0)}
/* a device screenshot (transparent around the laptop) floated in its panel, with more room above and below than at the sides */
.fig--device img{padding:clamp(32px,6vw,72px) clamp(16px,3vw,40px);border-radius:8px}
/* wide, short images want the whole column, not half of it */
.fig-stack{display:flex;flex-direction:column;gap:clamp(12px,1.6vw,20px)}
.fig-stack img{width:100%}

/* two columns, the second dropped so the pair reads as a masonry stagger
   rather than as a matched set; each figure keeps its own height */
.fig-pair{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(16px,1.8vw,20px);align-items:start;
}
.fig-pair > :nth-child(even){margin-top:clamp(28px,5vw,64px)}
/* innerbloom-pet-2 is a 263×3000 screenshot; shown whole it runs three
   thousand pixels down one column, so it gets a portrait window like the
   other variants give it */
/* a tall screenshot cropped to its top 3:5; only the image is cropped, so the caption shows below it */
.fig.crop img{width:100%;height:auto;aspect-ratio:3/5;object-fit:cover;object-position:top}
/* every case study image gets 8px rounded corners (the cropped frame rounds
   with it, and the focus ring follows the curve) */
.fig img,.fig.crop{border-radius:8px}
.fig .zoomable{border-radius:8px}
@media (max-width:560px){
  .fig-pair{grid-template-columns:minmax(0,1fr)}
  .fig-pair > :nth-child(even){margin-top:0}
}

/* a quiet statistic row */
.facts{display:flex;flex-direction:column;gap:12px}
.facts div{display:grid;grid-template-columns:minmax(90px,auto) minmax(0,1fr);gap:16px;font-size:var(--fs-meta)}
.facts dt{color:var(--ink);font-weight:500}
.facts dd{margin:0;color:var(--body)}

/* quotes */
.quote{border-left:2px solid var(--hair);padding-left:16px;display:flex;flex-direction:column;gap:8px}
.quote p{color:var(--ink)}
.quote cite{font-style:normal;font-size:var(--fs-cap);color:var(--muted)}
/* Inner Bloom, "What I learned": what testers said, as equal square tiles, two
   to a row. The section's layout becomes a two-column grid; everything but the
   quotes spans both columns, so only the quotes pair up. Rows are 16px apart,
   like the columns, and the text around the tiles keeps its usual distance. */
[data-sp="what-i-learned"] > .sp__pane:not([hidden]){display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;container:wil / inline-size}
[data-sp="what-i-learned"] > .sp__pane > :not(.quote){grid-column:1/-1}
[data-sp="what-i-learned"] > .sp__pane > * + :not(.quote),
[data-sp="what-i-learned"] > .sp__pane > :not(.quote) + .quote,
[data-sp="what-i-learned"] > .sp__pane > :not(.quote) + .quote + .quote{margin-top:clamp(8px,1vw,16px)}
[data-sp="what-i-learned"] .quote{margin:0;aspect-ratio:1;border-left:0;padding:clamp(16px,2.2vw,24px);border-radius:12px;background:var(--panel);
  display:flex;flex-direction:column;justify-content:space-between;gap:12px;overflow-wrap:break-word}
@media (max-width:560px){[data-sp="what-i-learned"] > .sp__pane:not([hidden]){grid-template-columns:minmax(0,1fr)}}   /* phones: one square per row */
/* two narrow squares (the text column is slimmest around 860–900px wide): the words step down a size so the longest quote still fits */
@container wil (max-width:460px){[data-sp="what-i-learned"] .quote p{font-size:calc(var(--fs-body) * .875)}}
/* phones, one square per row: the words grow with the square so each quote fills its tile */
@media (max-width:560px){[data-sp="what-i-learned"] .quote p{font-size:clamp(18px,9.4cqi,38px);line-height:1.2;letter-spacing:-.015em}}
/* feedback tiles by tone (Markdown '> Tone:'): a green or red-pink border and fill.
   With a back ('> Back:', assets/js/quote-flip.js) each tile is a small stack: the
   coloured quote on top, a white card with a black border peeking out behind it;
   clicking swaps the two */
.quote{--tone-line:transparent;--tone-fill:var(--panel)}
.quote[data-tone="positive"]{--tone-line:#2f9e5b;--tone-fill:#eaf7ef}
.quote[data-tone="negative"]{--tone-line:#e2557f;--tone-fill:#fdedf2}
[data-sp="what-i-learned"] .quote[data-tone]:not(.quote--stack){background:var(--tone-fill);box-shadow:inset 0 0 0 1px var(--tone-line)}
[data-sp="what-i-learned"] .quote--stack{position:relative;background:none;padding:0;overflow:visible;cursor:pointer;isolation:isolate}
.quote--stack > .quote__front,.quote--stack > .quote__backcard{position:absolute;inset:0;border-radius:12px;
  padding:clamp(16px,2.2vw,24px);display:flex;flex-direction:column;justify-content:space-between;gap:12px;
  transition:transform .5s cubic-bezier(.3,.7,.2,1),z-index 0s .2s}
.quote--stack > .quote__front{z-index:2;background:var(--tone-fill);box-shadow:inset 0 0 0 1px var(--tone-line)}
/* the white card: the same 12px rounded corners as the tiles, a 1px black line, signed "My response" like the quote's cite */
.quote--stack > .quote__backcard{z-index:1;background:#fff;box-shadow:inset 0 0 0 1px #000;transform:translate(6px,7px) rotate(1.6deg)}
.quote--stack .quote__back{margin:0;min-height:0;overflow:auto;font-size:var(--fs-body);line-height:1.5;letter-spacing:normal;color:var(--ink)}
.quote__by{font-size:var(--fs-cap);color:var(--muted);padding-right:40px}
.quote--stack.is-flipped > .quote__backcard{z-index:2;transform:none}
.quote--stack.is-flipped > .quote__front{z-index:1;transform:translate(6px,7px) rotate(1.6deg)}
/* whichever card is underneath shows only its edge */
.quote--stack.is-flipped > .quote__front > *,
.quote--stack:not(.is-flipped) > .quote__backcard > *{visibility:hidden;transition:visibility 0s .2s}
.quote__flip{position:absolute;right:10px;bottom:10px;z-index:3;width:32px;height:32px;border-radius:50%;border:0;
  display:grid;place-items:center;background:rgb(255 255 255 / .85);color:var(--ink);cursor:pointer;
  box-shadow:0 0 0 1px rgb(0 0 0 / .12);transition:transform .2s ease}
.quote__flip:hover{transform:rotate(-30deg)}
.quote__flip svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
.quote--stack .quote__front cite{padding-right:40px}   /* clear of the flip button */
@media (prefers-reduced-motion:reduce){.quote--stack > .quote__front,.quote--stack > .quote__backcard{transition:none}}

/* ── section navigation ──────────────────────────────────
   Two options, switched from the control in the masthead:
     [data-case-nav="top"]   a slim bar that pins once the hero has passed
     [data-case-nav="rail"]  the same list inside the sticky left column
   ------------------------------------------------------ */
/* shared list */
.secnav ol{list-style:none;margin:0;padding:0}
.secnav a{
  display:flex;gap:8px;text-decoration:none;color:var(--muted);
  font-size:var(--fs-meta);line-height:1.4;transition:color .15s ease;
}
.secnav a .n{font-variant-numeric:tabular-nums;color:var(--muted)}  /* --hair was 1.2:1 — invisible, and these carry the section number */
.secnav a:hover{color:var(--ink)}
.secnav a.is-current{color:var(--ink)}
.secnav a.is-current .n{color:var(--muted)}

/* option 1 — the bar */
.secnav--top{
  position:sticky;top:0;z-index:20;
  background:color-mix(in srgb,var(--bg) 92%,transparent);
  backdrop-filter:blur(8px);
  border-bottom:1px solid var(--hair);
}
.secnav--top .secnav__inner{padding-block:12px}
.secnav--top ol{
  display:flex;gap:clamp(16px,2.2vw,28px);
  overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none;
}
.secnav--top ol::-webkit-scrollbar{display:none}
.secnav--top li{flex:none}
.secnav--top a{white-space:nowrap;padding-block:2px}
.secnav--top a.is-current{box-shadow:inset 0 -1px 0 var(--ink)}
.secnav__progress{height:1px;background:var(--hair)}
.secnav__progress i{display:block;height:100%;background:var(--ink);transform:scaleX(0);transform-origin:0 50%}

/* How far the section list sits from the credits above it.
     spaced  a wider gap plus the hairline — separation by proximity, which
             is the conventional and lower-risk move
     bottom  the rail spans the viewport and the list sits at its foot, so
             the gap reads as structure rather than as a loose margin
   ------------------------------------------------------ */
:root[data-sep="spaced"] .secnav--rail{margin-top:clamp(28px,3.4vw,44px);padding-top:clamp(16px,1.8vw,24px)}
:root[data-sep="bottom"] .rail{min-height:calc(100svh - var(--secnav-h) - var(--sticky-top) - clamp(24px,3vw,40px))}
:root[data-sep="bottom"] .secnav--rail{margin-top:auto;padding-top:clamp(16px,1.8vw,24px)}
/* a viewport too short to hold both: fall back to the spaced treatment */
@media (max-height:640px){
  :root[data-sep="bottom"] .rail{min-height:0}
  :root[data-sep="bottom"] .secnav--rail{margin-top:clamp(28px,3.4vw,44px)}
}

/* option 2 — inside the rail.
   The numbers are dropped here: in a vertical list they add nothing that
   position doesn't already say, and they pushed every label off the left
   edge the credits above are aligned to. The count moves to the heading,
   and the current-section marker hangs in the margin so the text itself
   stays flush. */
.secnav--rail{border-top:1px solid var(--hair)}
.secnav--rail .secnav__head{display:flex;justify-content:space-between;gap:12px;margin-bottom:8px;color:var(--muted);font-size:var(--fs-cap)}
.secnav--rail .secnav__head b{font-weight:400;color:var(--body);font-variant-numeric:tabular-nums}
.secnav--rail ol{display:flex;flex-direction:column;gap:8px}
.secnav--rail a{position:relative;padding-left:0}
.secnav--rail a .n{display:none}
.secnav--rail a.is-current::before{
  content:"";position:absolute;left:-12px;top:.35em;bottom:.35em;width:1px;background:var(--ink);
}

/* the switch itself — prototype furniture, kept quiet */
.navpick{display:flex;align-items:center;gap:8px;font-size:var(--fs-cap);color:var(--muted)}
.navpick__label{margin-right:2px}
.navpick button{
  font:inherit;color:var(--muted);background:none;
  border:1px solid var(--hair);border-radius:2px;
  padding:3px 8px;cursor:pointer;transition:all .15s ease;
}
.navpick button:hover{color:var(--ink);border-color:var(--muted)}
.navpick button[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:var(--bg)}

/* ── layout panel ────────────────────────────────────────
   Prototype furniture, not part of the design: a docked panel for trying
   the variants side by side, plus a checker that measures the result.
   ------------------------------------------------------ */
.lab{
  /* docked right:the left gutter is where the sticky rail sits, and the
     rail is one of the things being compared */
  position:fixed;right:16px;bottom:16px;z-index:40;
  width:min(292px,calc(100vw - 32px));
  background:var(--bg);border:1px solid var(--hair);border-radius:3px;
  box-shadow:0 8px 28px rgb(0 0 0 / .10);
  font-size:var(--fs-cap);color:var(--body);
}
/* narrow screens: above the review switcher pill instead of under it */
@media (max-width:1100px){body:has(.rsw) .lab{bottom:72px}}
.lab__toggle{
  display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  font:inherit;font-size:var(--fs-cap);color:var(--ink);background:none;border:0;
  padding:8px 12px;cursor:pointer;text-align:left;
}
.lab__toggle .chev{color:var(--muted);transition:transform .18s ease}
.lab[data-open="true"] .chev{transform:rotate(180deg)}
.lab__body{display:none;padding:2px 12px 12px;border-top:1px solid var(--hair)}
.lab[data-open="true"] .lab__body{display:block}
.labrow{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-block:8px}
.labrow + .labrow{border-top:1px solid color-mix(in srgb,var(--hair) 60%,transparent)}
/* type sizes (case.js): a slider per size, with Auto */
.labsizes{display:grid;gap:8px;padding-block:8px;border-top:1px solid color-mix(in srgb,var(--hair) 60%,transparent)}
.labsize{display:grid;grid-template-columns:minmax(0,1fr) 120px 44px auto;align-items:center;gap:8px;font-size:12px}
.labsize input{width:100%;accent-color:var(--ink)}
.labsize output{font-variant-numeric:tabular-nums;color:var(--muted);text-align:right}
.labsize.is-auto input{opacity:.4}
.labsize button{font:inherit;font-size:12px;padding:4px 8px;border-radius:999px;border:1px solid var(--hair);background:none;color:var(--muted);cursor:pointer}
.labsize button[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:var(--bg)}
.labrow__label{flex:1 0 82px;color:var(--muted)}
.labrow button,.lab__run{
  font:inherit;color:var(--muted);background:var(--bg);
  border:1px solid var(--hair);border-radius:2px;
  padding:3px 8px;cursor:pointer;transition:all .15s ease;
}
.labrow button:hover,.lab__run:hover{color:var(--ink);border-color:var(--muted)}
.labrow button[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:var(--bg)}
.lab__run{width:100%;margin-top:12px;padding-block:8px;color:var(--ink)}
.lab__out{margin-top:8px;display:none;max-height:38vh;overflow:auto;overscroll-behavior:contain}
.lab__out[data-shown="true"]{display:block}
.lab__sum{display:flex;gap:8px;align-items:baseline;margin-bottom:8px;color:var(--ink)}
.lab__sum b{font-weight:500}
.check{display:grid;grid-template-columns:14px minmax(0,1fr);gap:4px 8px;padding:4px 0;border-top:1px solid color-mix(in srgb,var(--hair) 60%,transparent)}
.check__mark{font-weight:500;line-height:1.35}
.check--pass .check__mark{color:#1d7a3d}
.check--warn .check__mark{color:#9a6400}
.check--fail .check__mark{color:#b3261e}
.check__name{color:var(--ink);line-height:1.35}
.check__note{grid-column:2;color:var(--muted);line-height:1.45}
.check__note code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.95em}
@media (max-width:1240px){
  .lab{right:8px;bottom:8px}
}
@media (max-width:850px){
  .lab{left:8px;right:8px;bottom:8px;width:auto}
}
@media print{.lab,.secnav--top{display:none}}

/* ── next + footer ───────────────────────────────────── */
/* The next project is set exactly like the cover at the top:big title and
   discipline on the left, description opposite. Same grid, same sizes, so
   the page opens and closes on the same note. */
.next{border-top:1px solid var(--hair);padding-block:clamp(28px,4vw,52px)}
.next a{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  grid-template-rows:auto auto auto 1fr;   /* the last row takes a tall description, so the discipline stays under the title */
  gap:0 var(--gap);align-items:start;text-decoration:none;
}
.next__label{grid-column:1 / -1;font-size:var(--fs-meta);color:var(--muted);margin-bottom:clamp(12px,1.4vw,16px)}
.next__title{
  font-size:var(--fs-title);letter-spacing:-.02em;line-height:1.2;max-width:18ch;
  text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:6px;
  text-decoration-color:var(--hair);transition:text-decoration-color .15s ease;
}
.next__discipline{grid-row:3;grid-column:1;font-size:var(--fs-meta);color:var(--muted);margin-top:12px}
.next__desc{grid-row:2 / -1;grid-column:2;font-size:var(--fs-body);line-height:1.5;max-width:34ch;color:var(--ink)}
.next a:hover .next__title{text-decoration-color:var(--ink)}
/* phones: the description sits under the title and its discipline line, so a
   full sentence doesn't squeeze the title into one word per line */
@media (max-width:600px){
  .next a{grid-template-columns:1fr;grid-template-rows:none}
  .next__discipline{grid-row:3}
  .next__desc{grid-row:4;grid-column:1;margin-top:16px;max-width:none}
}

.foot{border-top:1px solid var(--hair);padding-block:clamp(24px,2.6vw,32px);display:flex;gap:20px;align-items:center;flex-wrap:wrap;font-size:var(--fs-cap);color:var(--muted)}
.foot a{text-decoration:none;transition:color .15s ease}
.foot a:hover{color:var(--ink)}
.foot .end{margin-left:auto}

/* ── responsive ──────────────────────────────────────── */
/* the rail is too narrow to carry a list below about 1100px, so the
   section nav falls back to the bar there whatever the setting says */
@media (max-width:1100px){
  :root{--credits-w:280px;--gap:56px;--band:896px}
  .secnav--rail{display:none}
}
@media (max-width:850px){
  .article{grid-template-columns:1fr;gap:clamp(28px,4vw,36px);max-width:var(--measure)}
  .rail,.article > .credits{position:static}
  :root[data-sep="bottom"] .rail{min-height:0}
  .credits{display:grid;columns:auto;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:20px}
  .credits__group{margin-bottom:0}
  .credits__group:nth-child(4){break-before:auto}
  .credits__group--wide{grid-column:auto}
  .navpick__label{display:none}
  .cover{grid-template-columns:1fr;gap:20px}
  .cover__title{max-width:none}
  .content{max-width:none}
  .foot .end{margin-left:0;width:100%}
}
@media (prefers-reduced-motion:reduce){*{transition:none!important;scroll-behavior:auto}}

/* hero plate variants: --ink is a black field (Complex Community, whose phone
   UI is dark); --match sizes a tall image to the height the standard 1280×780
   laptop hero gets (Inner Bloom), so the case studies open at one height */
.plate--ink{background:#000}
.plate--match .plate__inner{container-type:inline-size}
.plate--match img{width:auto;max-width:100%;height:calc(min(960px, 100cqi) * 780 / 1280);object-fit:contain}

/* phone screenshots shown in a black square, centred (Complex Community) */
.fig--square :is(img,video){aspect-ratio:1;width:100%;height:auto;object-fit:contain;background:#000;padding:24px;display:block}
.fig-pair:has(.fig--square) > :nth-child(even){margin-top:0}   /* squares sit level, side by side */

/* top section bar: no underline on the current section; its number gives way
   to the brand star, which wipes up from the baseline (a clip, not a move) */
.secnav--top a.is-current{box-shadow:none}
.secnav .n{position:relative;display:inline-block}
.secnav .n__num{transition:opacity .15s ease}
.n__star{position:absolute;left:50%;top:50%;width:.72em;height:1.02em;translate:-50% -52%;fill:var(--ink);
  clip-path:inset(100% 0 0 0);transition:clip-path .35s cubic-bezier(.3,.7,.2,1)}
.secnav--top a.is-current .n__num{opacity:0}
.secnav--top a.is-current .n__star{clip-path:inset(0 0 0 0)}
@media (prefers-reduced-motion:reduce){.n__star,.secnav .n__num{transition:none}}

/* no cross-page view transition into or out of a case study: opening one is
   the tile's colour filling the screen, then the page fading in under it
   (tile-transition.js + the arrival fade in each case page's <head>); the
   browser's own crossfade and wordmark glide fought with that */
/* Washington Healthcare: the hero field in the project's green */
.plate--green{background:#4db72e}


/* --- header: the same as the homepage / Field Notes (wordmark left; Resume,
   LinkedIn, Playground with their icons, right), in light mode ----------- */
.cmast{padding-block:24px clamp(16px,2vw,24px);display:flex;align-items:center;justify-content:space-between;gap:20px}
@media (min-width:768px){.cmast{padding-top:40px}}   /* the homepage logo's top: 24px on phones, 40px from 768px up */
.cmast__mark{flex:none;align-self:flex-start}   /* never squeezed by the nav, and its top stays put if the nav wraps */
.cmast__mark img{display:block;width:var(--hwm, clamp(104px,9vw,128px));max-width:100%;height:auto}   /* --hwm: the homepage's Header wordmark size, copied by case-head.js, so both logos match */
.cmast__nav{display:flex;align-items:center;gap:clamp(16px,2vw,28px)}
.cmast__link{display:inline-flex;align-items:center;gap:8px;font-size:16px;line-height:1.4;color:#555;text-decoration:none;transition:color .15s ease}
.cmast__link:hover{color:var(--ink)}
.cmast .nav-ico{width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;display:none}
.cmast .nav-ico--lucide{display:inline-block}
.cmast .nav-ico--custom{stroke-width:1.25}
:root[data-navicons="custom"] .cmast .nav-ico--lucide{display:none}
:root[data-navicons="custom"] .cmast .nav-ico--custom{display:inline-block}
:root[data-navicons="none"] .cmast .nav-ico{display:none !important}
:root[data-navicons="custom"] .cmast .nav-ico--alt{stroke-width:1.875}
:root:is([data-pgicon="telescope"],[data-pgicon="orbit"]) .cmast [data-nav="playground"] :is(.nav-ico--lucide,.nav-ico--custom){display:none}
:root[data-pgicon="telescope"] .cmast .nav-ico--alt[data-icon="telescope"],
:root[data-pgicon="orbit"] .cmast .nav-ico--alt[data-icon="orbit"]{display:inline-block}
.cmast__link:focus-visible,.cmast__mark:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:2px}
@media (max-width:560px){.cmast__nav{gap:14px}.cmast__link{font-size:15px}}
/* phones: the full-size logo leaves no room beside it, so the links drop to a row of their own */
@media (max-width:560px){.cmast{flex-wrap:wrap;row-gap:16px}.cmast__nav{flex-basis:100%}}

/* --- the section bar as a floating pill (everything inside unchanged: the
   numbered sections, the star on the current one, the scroll progress) ---- */
.secnav--top{top:16px;padding-top:16px;background:none;border-bottom:0;-webkit-backdrop-filter:none;backdrop-filter:none;
  pointer-events:none}
.secnav--top .secnav__inner{position:relative;pointer-events:auto;width:fit-content;max-width:calc(100% - 32px);margin-inline:auto;
  padding:10px 24px;border-radius:999px;overflow:hidden;
  /* iOS glass: a thin white tint over a strong blur that also lifts the
     colour behind it (saturate), a bright rim of light along the top edge,
     a softer one along the bottom, a hairline edge and a soft float shadow */
  background:rgb(255 255 255 / .72);
  -webkit-backdrop-filter:blur(16px) saturate(180%);backdrop-filter:blur(16px) saturate(180%);
  border:0;
  box-shadow:inset 0 1px 0 rgb(255 255 255 / .9),inset 0 -1px 0 rgb(255 255 255 / .35),
    inset 0 0 0 .5px rgb(255 255 255 / .6),0 0 0 .5px rgb(0 0 0 / .08),
    0 8px 32px rgb(0 0 0 / .10),0 1px 3px rgb(0 0 0 / .06)}
/* the sheen: light catching the upper half of the glass */
.secnav--top .secnav__inner::before{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:linear-gradient(180deg,rgb(255 255 255 / .45),rgb(255 255 255 / 0) 55%)}
.secnav--top .secnav__inner > ol{position:relative}
/* Text on the glass. One light tint everywhere (it never switches colour):
   72% white means even over pure black the glass is no darker than ~184/255,
   and the labels use --body (#3f3f44), not --muted, so they stay at 5.3:1
   or better over anything that scrolls behind. */
.secnav--top .secnav__inner a,.secnav--top .secnav__inner a .n{color:var(--body)}
.secnav--top .secnav__inner a.is-current{color:var(--ink)}
/* Reduce Transparency or Increase Contrast (macOS / iOS accessibility):
   a solid bar instead, white with black type */
@media (prefers-reduced-transparency:reduce),(prefers-contrast:more){
  .secnav--top .secnav__inner{background:var(--bg);-webkit-backdrop-filter:none;backdrop-filter:none}
  .secnav--top .secnav__inner::before{display:none}}
.secnav--top .secnav__progress{position:absolute;left:0;right:0;bottom:0;height:2px;background:transparent}
.secnav--top .secnav__progress i{background:var(--ink);opacity:.85}
@media (max-width:560px){.secnav--top .secnav__inner{padding:8px 16px}}

/* --- enlargeable figures (lightbox.js wraps each image in .zoomable) -----
   A small ⤢ in the corner says "you can open this" at a glance; on hover it
   widens into "Enlarge". */
.zoomable{display:block;width:100%;padding:0;border:0;background:none;cursor:zoom-in;position:relative;line-height:0;color:inherit}
.zoomable img{width:100%;height:auto;display:block}
.zoomable::after{content:"";position:absolute;right:12px;bottom:12px;width:32px;height:32px;border-radius:999px;
  background:rgb(255 255 255 / .92) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6'/%3E%3Cpath d='M9 21H3v-6'/%3E%3Cpath d='M21 3l-7 7'/%3E%3Cpath d='M3 21l7-7'/%3E%3C/svg%3E") center/14px no-repeat;
  box-shadow:0 1px 4px rgb(0 0 0 / .18);opacity:.85;transition:opacity .15s ease, transform .15s ease;pointer-events:none}
.zoomable:hover::after,.zoomable:focus-visible::after{opacity:1;transform:scale(1.08)}
.zoomable:focus-visible{outline:2px solid var(--accent);outline-offset:4px}
dialog.zoom{max-width:100vw;max-height:100vh;width:100%;height:100%;margin:0;padding:0;border:0;background:transparent;overflow:hidden;display:none;place-items:center}
dialog.zoom[open]{display:grid}
dialog.zoom.is-actual{overflow:auto;place-items:start center}
dialog.zoom.is-actual .zoom__figure{max-width:none;max-height:none;padding:clamp(12px,2vw,28px) 0}
dialog.zoom.is-actual .zoom__img{max-width:none;max-height:none;width:var(--nw);cursor:zoom-out}
dialog.zoom::backdrop{background:rgb(255 255 255 / .94);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.zoom__figure{margin:0;display:flex;flex-direction:column;gap:12px;align-items:center;max-width:92vw;max-height:90vh}
.zoom__img{max-width:min(92vw, var(--nw, 92vw));max-height:min(90vh, var(--nh, 90vh));width:auto;height:auto;display:block;cursor:zoom-out;
  background:var(--panel);box-shadow:0 18px 60px rgb(0 0 0 / .18);animation:zoom-in .22s cubic-bezier(.2,.7,.2,1)}
.zoom__cap{font-size:var(--fs-cap);line-height:1.5;color:var(--muted);max-width:70ch;text-align:center}
.zoom__bar{position:fixed;top:clamp(12px,2vw,24px);right:clamp(12px,2vw,24px);z-index:1;display:flex;gap:8px}
.zoom__bar button{display:flex;align-items:center;gap:8px;cursor:pointer;font:inherit;font-size:var(--fs-cap);letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink);background:var(--bg);border:1px solid var(--ink);padding:8px 12px}
.zoom__bar button:hover{background:var(--ink);color:var(--bg)}
@keyframes zoom-in{from{opacity:0;transform:scale(.97)}}
.zoom--instant .zoom__img{animation:none}
@media (prefers-reduced-motion:reduce){.zoom__img{animation:none}}
/* phone screenshots in black squares (Complex): the image is padded, so the ⤢ sits inside the square */
.fig--square .zoomable::after{right:16px;bottom:16px}

.secnav--top .secnav__inner{transition:opacity .25s ease, translate .25s ease}
.secnav--top.is-past .secnav__inner{opacity:0;translate:0 -8px;pointer-events:none}

/* under the title: the project's chips from its homepage tile (Layout options
   → Under the title), styled as there: the size and padding sliders and the
   filled / outlined choice are copied from the homepage settings */
.cover__chips{display:flex;flex-wrap:wrap;gap:var(--chipgap,8px);margin:12px 0 0}
:root[data-sub="text"] .cover__chips, :root:not([data-sub="text"]) .cover:has(.cover__chips) .cover__discipline{display:none}
:is(.cover__chips,.cover__skills) .chip{display:inline-flex;align-items:center;gap:4px;border-radius:999px;white-space:nowrap;
  font:400 var(--chipfs,12px)/1 var(--sans);padding:var(--chippy,4px) var(--chippx,8px);letter-spacing:.01em;
  border:1px solid transparent;background:#f5f5f5;color:#2b2b2b}
:is(.cover__chips,.cover__skills) .chip--industry{background:#ebebeb}
:root[data-eyebrow="pills"] :is(.cover__chips,.cover__skills) .chip{background:none;border-color:#d4d4d4;color:#555}
:root[data-eyebrow="pills"] :is(.cover__chips,.cover__skills) .chip--industry{border-color:#bdbdbd;color:#2b2b2b}
.cover__chips .live-dot{position:relative;display:inline-block;width:8px;height:8px;border-radius:50%;background:#4db72e;
  box-shadow:0 0 0 3px color-mix(in srgb,#4db72e 22%,transparent);flex:none}
.cover__chips .live-dot::after{content:"";position:absolute;inset:0;border-radius:50%;background:#4db72e;
  animation:cover-live 2.4s cubic-bezier(.2,.6,.3,1) infinite}
@keyframes cover-live{0%{transform:scale(1);opacity:.45}70%,100%{transform:scale(2.6);opacity:0}}
@media (prefers-reduced-motion:reduce){.cover__chips .live-dot::after{animation:none;opacity:0}}

/* the skills under the title (Layout options → Under the title): as chips in
   place of the industry chips, or as grey body text above them */
.cover__skills{display:none;margin:12px 0 0}
:root[data-sub="skills"] .cover__skills{display:flex;flex-wrap:wrap;gap:var(--chipgap,8px)}
:root[data-sub="skills"] .cover__chips{display:none}
:root[data-sub="both"] .cover__skills{display:block;font-size:var(--fs-meta);color:var(--muted);text-transform:capitalize}   /* as the Next project block's .next__discipline */
:root[data-sub="both"] .cover__skills .chip{all:unset}
:root[data-sub="both"] .cover__skills .chip + .chip::before{content:", "}

/* the Live chip under a case study title: hidden unless shown (Layout options → Live chip) */
:root:not([data-clive="show"]) .cover__chips .chip--live{display:none}
/* the chip words as secondary text, comma-separated (Under the title → Chip words as text) */
.cover__chiptext{display:none;font-size:var(--fs-meta);color:var(--muted);margin-top:12px}
:root[data-sub="list"] .cover__chiptext{display:block}
:root[data-sub="list"] .cover__chips{display:none}


/* --- the footer on a case study ----------------------------------------------
   footer.css is the one footer stylesheet (the homepage loads it too). What the
   homepage gets from site.css, a case study gets here: the footer's fonts and
   tokens, set on the footer itself so they never reach the article above. */
/* the footer's typefaces (declared in site.css for the homepage) */
@font-face{font-family:"That That New Pixel";src:url("../fonts/that-that-new-pixel-square.otf") format("opentype");
  font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Editorial New";src:url("../fonts/editorial-new.woff2") format("woff2");
  font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Editorial New";src:url("../fonts/editorial-new-italic.woff2") format("woff2");
  font-weight:400;font-style:italic;font-display:swap}
@font-face{font-family:"That That New Pixel";src:url("../fonts/that-that-new-pixel-italic-square.otf") format("opentype");
  font-weight:400;font-style:italic;font-display:swap}
@font-face{font-family:"PP Editorial New";src:url("../fonts/pp-editorial-new-ultralight.otf") format("opentype");font-weight:200;font-style:normal;font-display:swap}
@font-face{font-family:"PP Editorial New";src:url("../fonts/pp-editorial-new-regular.otf") format("opentype");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"PP Editorial New";src:url("../fonts/pp-editorial-new-ultrabold.otf") format("opentype");font-weight:800;font-style:normal;font-display:swap}
@font-face{font-family:"PP Editorial New";src:url("../fonts/pp-editorial-new-ultralightitalic.otf") format("opentype");font-weight:200;font-style:italic;font-display:swap}
@font-face{font-family:"PP Editorial New";src:url("../fonts/pp-editorial-new-italic.otf") format("opentype");font-weight:400;font-style:italic;font-display:swap}
@font-face{font-family:"PP Editorial New";src:url("../fonts/pp-editorial-new-ultrabolditalic.otf") format("opentype");font-weight:800;font-style:italic;font-display:swap}
.site-footer{--maxw:1080px;--rail:clamp(20px,5.2vw,72px);--gap-col:clamp(28px,4.4vw,64px);
  --serif:"Editorial New",Georgia,"Times New Roman",serif;--fs-lead:clamp(19px,1.6vw,22px);--fs-body:16px;--fs-cap:14px;
  --ink:#000;--bg:#fff;--muted:#6b6b6b}
.site-footer{font-weight:500}   /* the homepage body is Medium, and its footer inherits it */
.site-footer .container{width:100%;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--rail)}
.site-footer .visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
@media print{
.social{display:none}
}
/* pull-to-peek (peekaboo.js), as on the homepage */
.has-peekaboo{overscroll-behavior-y:none}
.peekaboo{position:fixed;top:0;left:0;width:100%;height:170px;z-index:0;
  visibility:hidden;pointer-events:none;image-rendering:pixelated}
/* matching the homepage's column layouts: the footer runs full width */
@media (min-width:768px){:root[data-footwide] .site-footer{--maxw:100%;--rail:var(--colpad,48px)}}
/* case studies: more room at the end of the white page, under "Next project" */
.layered .sheet{padding-bottom:clamp(64px,8vw,128px)}


/* a phone in the black container: taller than square, so the phone fills it */
.fig--square.fig--phone :is(img,video){aspect-ratio:3/4;padding:20px}
.fig video{border-radius:8px}   /* videos get the same rounded corners as images */

/* Complex, Internet points: the 2nd screenshot can be left out (Layout options, or the toggle on the image in staging) */
:root[data-gift2="hide"]:not(.case-staging) .content figure:has(img[src*="complex-gift-2"]){display:none}
:root[data-gift2="hide"].case-staging .content figure:has(img[src*="complex-gift-2"]) > :not(.gift2-toggle){opacity:.25}
.content figure:has(> .gift2-toggle){position:relative}
.gift2-toggle{position:absolute;top:12px;left:12px;z-index:3;padding:6px 12px;border:0;border-radius:999px;background:#fff;color:#111;
  font:500 12px/1.2 var(--sans);cursor:pointer;box-shadow:0 1px 4px rgb(0 0 0/.25)}
