/* ═══════════════════════════════════════════════════════════════════════
   SCROLL STORY — the shared case format.

   What travels between cases is the rhythm and the components, not a beat
   list. Each case has however many beats its own story needs.

   The body runs wide with an asymmetric column. The nav, breadcrumb, pager
   and footer are left exactly as portfolio.css sets them — 720px centred,
   identical on every case page. Chrome is site furniture; it does not change
   shape because the page underneath it is wider.
   ═══════════════════════════════════════════════════════════════════════ */

:root{ --story: 1440px; --gutter: 30%; --measure: 620px; }

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

/* The column sets the offset only. The measure lives on the text, so a display
   line can run wider than the paragraph beneath it. Container-relative, not
   viewport-relative: a vw gutter keeps growing past the capped wrap and
   squeezes the column to 56% on a 2560 display. */
.col{ margin-left: var(--gutter); }
.lede,.cap,.quote,.states,.st,.note{ max-width: var(--measure); }

.s-h1,.s-h2{ font-family:'Fraunces',serif; font-weight:600; letter-spacing:-0.03em;
  line-height:1.03; margin:0; color:var(--ink); }
.s-h1{ font-size:clamp(40px,5.6vw,82px); max-width:14ch; }
.s-h2{ font-size:clamp(30px,4vw,54px); max-width:15ch; }
.s-h1 em,.s-h2 em{ font-style:italic; color:var(--green-deep); }
/* ── Chapter marker. It used to be a "// " comment in 11px grey, which read
   as debris rather than orientation. It is now numbered, so in a page this
   long you always know which beat you are on, and it is big enough to be a
   label rather than a whisper. ── */
main{ counter-reset:beat; }
.brow{ display:flex; align-items:center; gap:11px; margin:0 0 22px;
  font-family:'IBM Plex Mono',monospace; font-size:12.5px; letter-spacing:.1em;
  text-transform:uppercase; font-weight:500; color:var(--ink-2); }
.b .brow, .pin .brow{ counter-increment:beat; }
.b .brow::before, .pin .brow::before{
  content:counter(beat,decimal-leading-zero);
  font-weight:600; color:var(--green-deep);
  padding:3px 8px; border-radius:5px; background:var(--green-soft);
  letter-spacing:.04em; font-size:11.5px; }
.b .brow::after, .pin .brow::after{
  content:""; flex:1; height:1px; max-width:56px;
  background:var(--line); }

/* the wordmark line and the summary are not beats — no number, no rule */
.s-kicker .brow, .tldr .brow{ counter-increment:none; }
.s-kicker .brow::before, .tldr .brow::before,
.s-kicker .brow::after,  .tldr .brow::after{ content:none; }
.s-kicker .brow{ color:var(--ink-low); font-weight:400; }
.lede{ font-family:'Newsreader',Georgia,serif; font-size:clamp(19px,2.1vw,25px);
  line-height:1.45; color:var(--ink-2); margin:24px 0 0; }
.quote{ font-family:'Newsreader',Georgia,serif; font-style:italic;
  font-size:clamp(25px,3.4vw,44px); line-height:1.22; color:var(--ink); margin:0; }

/* Block rhythm — section height varies on purpose. Equal sections march;
   uneven ones breathe. That variation is the whole "scroll effect". */
.b{ padding-block:clamp(64px,11vh,140px); }
.b--tall{ padding-block:clamp(104px,18vh,230px); }
.b--tight{ padding-block:clamp(36px,5vh,70px); }

/* Reveal. Resting state is visible; the script arms and disarms, so nothing
   can strand blank if the animation never ticks. */
.r{ --d:0ms; }
.armed .r{ opacity:0; transform:translateY(14px); }
.armed .r.in{ animation:s-rise 700ms cubic-bezier(.2,.6,.2,1) var(--d) forwards; }
@keyframes s-rise{ to{ opacity:1; transform:none; } }

.s-hero{ min-height:82vh; display:flex; flex-direction:column; justify-content:flex-end;
  padding-bottom:clamp(36px,7vh,80px); }
.s-kicker{ display:flex; gap:14px; align-items:baseline; margin-bottom:20px; flex-wrap:wrap; }

.plate{ background:var(--bg-2); aspect-ratio:16/7; overflow:hidden; }
.plate img{ width:100%; height:100%; object-fit:cover; display:block; }
.cap{ font-family:'IBM Plex Mono',monospace; font-size:11px; color:var(--ink-low); padding-top:12px; }

/* ── One pinned moment per case, spent on whatever that case is actually
   about. The stage holds; the steps resolve as you scroll past. ── */
.pin{ position:relative; height:320vh; }
.pin__stage{ position:sticky; top:0; height:100vh; display:flex; align-items:center; }

/* ── A beat is a hook you react to, not a paragraph you wade through.
   The headline carries the whole point; the explanation sits behind a native
   disclosure for the reader who wants it. Two lines, then a choice. ── */
.say{ margin:0; }
.say > summary{ list-style:none; cursor:pointer; display:block;
  padding-bottom:14px; border-bottom:1px dashed var(--line); }
.say > summary::-webkit-details-marker{ display:none; }
.say > summary::marker{ content:""; }
.say > summary:focus-visible{ outline:2px solid var(--green-deep); outline-offset:6px; }

/* A control has to look like one. This was 11px grey and a dashed hairline
   circle — technically an affordance, practically invisible. It is now a pill,
   which is the one shape nobody has to be taught. */
.say__cue{ display:inline-flex; align-items:center; gap:10px; margin-top:24px;
  min-height:44px; padding:0 20px 0 7px;
  border:1px solid var(--line); border-radius:999px; background:var(--bg);
  font-family:'IBM Plex Mono',monospace; font-size:12.5px; letter-spacing:.08em;
  text-transform:uppercase; font-weight:500; color:var(--green-deep);
  transition:border-color .2s ease, background .2s ease; }
.say__cue .sign{ display:grid; place-items:center; width:30px; height:30px;
  border-radius:50%; background:var(--green-soft); color:var(--green-deep);
  font-size:16px; line-height:1;
  transition:transform .3s cubic-bezier(.2,.6,.2,1), background .2s ease, color .2s ease; }
.say > summary:hover .say__cue{ border-color:var(--green-deep); background:var(--green-soft); }
.say > summary:hover .sign{ background:var(--green-deep); color:#fff; }
.say[open] .sign{ transform:rotate(45deg); }
.say[open] > summary{ border-bottom-color:transparent; }

/* the label says which way the control goes */
.say__lab--on{ display:none; }
.say[open] .say__lab--off{ display:none; }
.say[open] .say__lab--on{ display:inline; }

.say__detail{ margin:18px 0 0; max-width:var(--measure);
  font-family:'IBM Plex Sans',system-ui,sans-serif; font-size:16.5px; line-height:1.6;
  color:var(--ink-2); }
.say__detail em{ font-style:italic; color:var(--green-deep); }
.say[open] .say__detail{ animation:s-open 420ms cubic-bezier(.2,.6,.2,1) both; }
@keyframes s-open{ from{ opacity:0; transform:translateY(-6px); } to{ opacity:1; transform:none; } }

@media (prefers-reduced-motion:reduce){
  .say[open] .say__detail{ animation:none; }
  .say[open] .sign{ transform:none; }
}

/* ── Pinned steps. One short line each; the detail sits behind a native
   disclosure so the sequence stays scannable and the curious can still get
   everything without leaving the page. Shared, because every case uses it. ── */
.steps{ width:100%; margin-top:26px; border-top:1px dashed var(--line); }
.step{ border-bottom:1px dashed var(--line); opacity:.18; transition:opacity .5s ease; }
.step.on{ opacity:1; }
.step summary{ display:grid; grid-template-columns:104px 1fr auto; gap:18px;
  align-items:baseline; padding:17px 0; cursor:pointer; list-style:none; }
.step summary::-webkit-details-marker{ display:none; }
.step summary::marker{ content:""; }
.step .when{ font-family:'IBM Plex Mono',monospace; font-size:10.5px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--ink-low); transition:color .3s ease; }
.step.on .when{ color:var(--green-deep); }
.step .line{ font-family:'Fraunces',serif; font-weight:500; font-size:clamp(19px,2.2vw,26px);
  line-height:1.22; letter-spacing:-0.02em; color:var(--ink); }
.step .line em{ font-style:italic; color:var(--green-deep); }
.step .more{ font-family:'IBM Plex Mono',monospace; font-size:16px; color:var(--line);
  transition:color .2s ease, transform .25s cubic-bezier(.2,.6,.2,1); }
.step[open] .more{ transform:rotate(45deg); color:var(--ink-low); }
.step summary:hover .more{ color:var(--green-deep); }
.step summary:focus-visible{ outline:2px solid var(--green-deep); outline-offset:3px; }
.step .detail{ margin:0 0 18px; padding-left:122px; max-width:540px;
  font-family:'IBM Plex Sans',system-ui,sans-serif; font-size:15.5px; line-height:1.55;
  color:var(--ink-2); }

/* The count is the drama: the one colour reserved for something being wrong,
   and the only red on the page. */
.wrongcount{ color:var(--red); font-style:normal; font-weight:600; display:inline-block; }
.step.on .wrongcount{ animation:s-slam 620ms cubic-bezier(.2,.9,.3,1.2) 220ms both; }
@keyframes s-slam{
  0%  { transform:scale(1.9); opacity:0; letter-spacing:.06em; }
  60% { transform:scale(.97); opacity:1; letter-spacing:normal; }
  100%{ transform:scale(1);   opacity:1; }
}

/* ── The stack. What the thing is actually made of, stated once and quietly.
   Hairline row, mono, no chrome — it answers "what am I looking at" without
   competing with the story. ── */
.stack{ display:flex; flex-wrap:wrap; align-items:center; gap:6px 11px;
  margin:28px 0 0; padding-top:14px;
  border-top:1px dashed var(--line); max-width:var(--measure);
  font-family:'IBM Plex Mono',monospace; font-size:12px; letter-spacing:.06em;
  color:var(--ink-low); }
.stack dt{ color:var(--ink-dim); margin-right:2px; }
.stack dd{ margin:0; color:var(--ink-2); display:inline-flex; align-items:center; }
.stack dd:not(:last-child)::after{ content:"·"; color:var(--line); margin-left:5px; }

/* ── Marks, not a sponsor wall. Each is a single monochrome path masked in
   currentColor, so it sits in the type rather than beside it, and a name
   only earns a glyph if the mark still reads at 15px — Bun's does not, so
   Bun is a word. Inline data URIs because the CSP allows no third party. ── */
.stack dd[data-i]::before{ content:""; width:15px; height:15px; flex:none;
  margin-right:7px; background-color:currentColor; opacity:.85;
  -webkit-mask:var(--g) center/contain no-repeat;
          mask:var(--g) center/contain no-repeat; }
.stack dd[data-i="anthropic"]{ --g:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z'/%3E%3C/svg%3E"); }
.stack dd[data-i="discord"]{ --g:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z'/%3E%3C/svg%3E"); }
.stack dd[data-i="github"]{ --g:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E"); }
.stack dd[data-i="nodedotjs"]{ --g:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M11.998,24c-0.321,0-0.641-0.084-0.922-0.247l-2.936-1.737c-0.438-0.245-0.224-0.332-0.08-0.383 c0.585-0.203,0.703-0.25,1.328-0.604c0.065-0.037,0.151-0.023,0.218,0.017l2.256,1.339c0.082,0.045,0.197,0.045,0.272,0l8.795-5.076 c0.082-0.047,0.134-0.141,0.134-0.238V6.921c0-0.099-0.053-0.192-0.137-0.242l-8.791-5.072c-0.081-0.047-0.189-0.047-0.271,0 L3.075,6.68C2.99,6.729,2.936,6.825,2.936,6.921v10.15c0,0.097,0.054,0.189,0.139,0.235l2.409,1.392 c1.307,0.654,2.108-0.116,2.108-0.89V7.787c0-0.142,0.114-0.253,0.256-0.253h1.115c0.139,0,0.255,0.112,0.255,0.253v10.021 c0,1.745-0.95,2.745-2.604,2.745c-0.508,0-0.909,0-2.026-0.551L2.28,18.675c-0.57-0.329-0.922-0.945-0.922-1.604V6.921 c0-0.659,0.353-1.275,0.922-1.603l8.795-5.082c0.557-0.315,1.296-0.315,1.848,0l8.794,5.082c0.57,0.329,0.924,0.944,0.924,1.603 v10.15c0,0.659-0.354,1.273-0.924,1.604l-8.794,5.078C12.643,23.916,12.324,24,11.998,24z M19.099,13.993 c0-1.9-1.284-2.406-3.987-2.763c-2.731-0.361-3.009-0.548-3.009-1.187c0-0.528,0.235-1.233,2.258-1.233 c1.807,0,2.473,0.389,2.747,1.607c0.024,0.115,0.129,0.199,0.247,0.199h1.141c0.071,0,0.138-0.031,0.186-0.081 c0.048-0.054,0.074-0.123,0.067-0.196c-0.177-2.098-1.571-3.076-4.388-3.076c-2.508,0-4.004,1.058-4.004,2.833 c0,1.925,1.488,2.457,3.895,2.695c2.88,0.282,3.103,0.703,3.103,1.269c0,0.983-0.789,1.402-2.642,1.402 c-2.327,0-2.839-0.584-3.011-1.742c-0.02-0.124-0.126-0.215-0.253-0.215h-1.137c-0.141,0-0.254,0.112-0.254,0.253 c0,1.482,0.806,3.248,4.655,3.248C17.501,17.007,19.099,15.91,19.099,13.993z'/%3E%3C/svg%3E"); }
.stack dd[data-i="python"]{ --g:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01h8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z'/%3E%3C/svg%3E"); }
.stack dd[data-i="railway"]{ --g:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M.113 10.27A13.026 13.026 0 000 11.48h18.23c-.064-.125-.15-.237-.235-.347-3.117-4.027-4.793-3.677-7.19-3.78-.8-.034-1.34-.048-4.524-.048-1.704 0-3.555.005-5.358.01-.234.63-.459 1.24-.567 1.737h9.342v1.216H.113v.002zm18.26 2.426H.009c.02.326.05.645.094.961h16.955c.754 0 1.179-.429 1.315-.96zm-17.318 4.28s2.81 6.902 10.93 7.024c4.855 0 9.027-2.883 10.92-7.024H1.056zM11.988 0C7.5 0 3.593 2.466 1.531 6.108l4.75-.005v-.002c3.71 0 3.849.016 4.573.047l.448.016c1.563.052 3.485.22 4.996 1.364.82.621 2.007 1.99 2.712 2.965.654.902.842 1.94.396 2.934-.408.914-1.289 1.458-2.353 1.458H.391s.099.42.249.886h22.748A12.026 12.026 0 0024 12.005C24 5.377 18.621 0 11.988 0z'/%3E%3C/svg%3E"); }
.stack dd[data-i="typescript"]{ --g:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75c.612 0 1.154.037 1.627.111a6.38 6.38 0 0 1 1.306.34v2.458a3.95 3.95 0 0 0-.643-.361 5.093 5.093 0 0 0-.717-.26 5.453 5.453 0 0 0-1.426-.2c-.3 0-.573.028-.819.086a2.1 2.1 0 0 0-.623.242c-.17.104-.3.229-.393.374a.888.888 0 0 0-.14.49c0 .196.053.373.156.529.104.156.252.304.443.444s.423.276.696.41c.273.135.582.274.926.416.47.197.892.407 1.266.628.374.222.695.473.963.753.268.279.472.598.614.957.142.359.214.776.214 1.253 0 .657-.125 1.21-.373 1.656a3.033 3.033 0 0 1-1.012 1.085 4.38 4.38 0 0 1-1.487.596c-.566.12-1.163.18-1.79.18a9.916 9.916 0 0 1-1.84-.164 5.544 5.544 0 0 1-1.512-.493v-2.63a5.033 5.033 0 0 0 3.237 1.2c.333 0 .624-.03.872-.09.249-.06.456-.144.623-.25.166-.108.29-.234.373-.38a1.023 1.023 0 0 0-.074-1.089 2.12 2.12 0 0 0-.537-.5 5.597 5.597 0 0 0-.807-.444 27.72 27.72 0 0 0-1.007-.436c-.918-.383-1.602-.852-2.053-1.405-.45-.553-.676-1.222-.676-2.005 0-.614.123-1.141.369-1.582.246-.441.58-.804 1.004-1.089a4.494 4.494 0 0 1 1.47-.629 7.536 7.536 0 0 1 1.77-.201zm-15.113.188h9.563v2.166H9.506v9.646H6.789v-9.646H3.375z'/%3E%3C/svg%3E"); }
.stack dd[data-i="vercel"]{ --g:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m12 1.608 12 20.784H0Z'/%3E%3C/svg%3E"); }


/* ── The numbers. No boxes: hairlines and scale do the separating, so the
   figures read as facts rather than as four more cards. ── */
.figs{ display:grid; grid-template-columns:repeat(4,1fr); margin-top:34px;
  border-top:1px solid var(--ink); }
.fig{ padding:22px 20px 22px 0; border-right:1px dashed var(--line); }
.fig:last-child{ border-right:0; padding-right:0; }
.fig .n{ display:block; font-family:'Fraunces',serif; font-weight:600;
  font-size:clamp(38px,4.4vw,60px); line-height:.95; letter-spacing:-0.04em; color:var(--ink); }
.fig .n em{ font-style:italic; color:var(--green-deep); }
.fig .l{ display:block; margin-top:10px; font-family:'IBM Plex Mono',monospace;
  font-size:11px; letter-spacing:.06em; line-height:1.5; color:var(--ink-low); }

/* ── The close. Deliberately static: you have scrolled the story, here it is
   whole, and here is a version you can take with you. ── */
.tldr{ background:var(--ink); color:#f3f3f3; border-radius:16px;
  padding:clamp(26px,4vw,44px); max-width:760px; }
.tldr .brow{ color:#8fbf8f; }
.tldr .s-h2{ color:#fff; font-size:clamp(26px,3vw,38px); max-width:18ch; }
.tldr .s-h2 em{ color:#a8d98a; }

/* ── The turn. The summary is not a list, it is a pivot: one line of problem,
   then the answer. A spine runs down the rows and changes colour exactly where
   the story turns, so you see the shape before you read a word. ── */
.turn{ list-style:none; margin:30px 0 0; padding:0; }
.turn__row{ position:relative; display:grid; grid-template-columns:34px 92px 1fr;
  gap:0 16px; align-items:baseline; padding:15px 0 15px 0;
  border-top:1px dashed #333; }
.turn__row:first-child{ border-top:0; }

/* the spine, drawn per row so it stays continuous and takes each row's tone */
.turn__row::before{ content:""; position:absolute; left:7px; top:0; bottom:0;
  width:1px; background:var(--tone,#3a3a3a); opacity:.55; }
.turn__row:first-child::before{ top:50%; }
.turn__row:last-of-type::before{ bottom:50%; }

.turn__dot{ position:relative; z-index:1; width:15px; height:15px; border-radius:50%;
  border:1px solid var(--tone,#4a4a4a); background:var(--ink);
  display:grid; place-items:center; justify-self:start; align-self:center;
  transition:transform .28s cubic-bezier(.2,.9,.3,1.3), box-shadow .28s ease; }
.turn__dot::after{ content:""; width:5px; height:5px; border-radius:50%;
  background:var(--tone,#4a4a4a); transition:transform .28s ease; }

.turn__k{ font-family:'IBM Plex Mono',monospace; font-size:10px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--tone,#8a8a8a); align-self:center;
  transition:color .25s ease; }
.turn__v{ font-size:16px; line-height:1.5; color:#d0d0d0; }
.turn__v b{ color:#fff; font-weight:600; }

/* problem reads wrong; everything after it reads resolved */
.turn__row--problem{ --tone:#e0705c; }
.turn__row--answer{ --tone:#a8d98a; }

/* micro-interaction: the row you are on lifts out of the dark */
.turn__row{ transition:background .22s ease; border-radius:8px; }
.turn__row:hover{ background:rgba(255,255,255,.035); }
.turn__row:hover .turn__dot{ transform:scale(1.28); box-shadow:0 0 0 4px rgba(255,255,255,.05); }
.turn__row:hover .turn__k{ color:#fff; }

/* the dots arrive in sequence when the card reveals — red first, then the
   answer cascading down, which is the story in one gesture */
.armed .tldr:not(.in) .turn__dot{ transform:scale(0); }
/* backwards, not both: a filled animation keeps ownership of transform after
   it ends, which would leave the hover scale permanently dead. */
.armed .tldr.in .turn__dot{ animation:s-pop 460ms cubic-bezier(.2,.9,.3,1.4) backwards;
  animation-delay:calc(340ms + var(--i,0) * 110ms); }
@keyframes s-pop{ from{ transform:scale(0); } to{ transform:scale(1); } }

/* ── The way out. Reads as the last row, behaves as a link. ── */
.turn__cta{ display:grid; grid-template-columns:34px 1fr auto; gap:0 16px;
  align-items:baseline; margin-top:6px; padding:16px 10px 16px 0;
  border-top:1px dashed #333; border-radius:8px; text-decoration:none;
  transition:background .22s ease, padding-left .22s ease; }
.turn__cta:hover, .turn__cta:focus-visible{ background:rgba(168,217,138,.07); }
.turn__cta:focus-visible{ outline:2px solid var(--green-soft); outline-offset:2px; }
.turn__cta .turn__k{ color:#a8d98a; grid-column:2; }
.turn__cta .turn__v{ grid-column:2; margin-top:3px; color:#eaeaea;
  font-family:'Newsreader',Georgia,serif; font-size:17px; font-style:italic; }
.turn__cta .arr{ grid-column:3; align-self:center; color:#8fbf8f; font-size:15px;
  transition:transform .25s cubic-bezier(.2,.6,.2,1); }
.turn__cta:hover .arr{ transform:translate(3px,-3px); }

.actions{ display:flex; gap:10px; flex-wrap:wrap; margin-top:28px; }
/* min-height keeps every control on the 44px touch floor from DESIGN.md; the
   mono label is small, so padding alone lands at 39px. */
.sbtn{ font-family:'IBM Plex Mono',monospace; font-size:11.5px; letter-spacing:.1em;
  text-transform:uppercase; padding:13px 18px; min-height:44px; border-radius:999px;
  cursor:pointer; border:1px solid #4a4a4a; background:transparent; color:#e8e8e8;
  display:inline-flex; align-items:center; justify-content:center; gap:8px; text-decoration:none;
  transition:background .18s ease, color .18s ease, border-color .18s ease; }
.sbtn:hover{ border-color:#fff; color:#fff; }
.sbtn--go{ background:var(--green-soft); border-color:var(--green-soft); color:var(--green-deep); }
.sbtn--go:hover{ background:#fff; border-color:#fff; color:var(--ink); }
.sbtn:focus-visible{ outline:2px solid var(--green-soft); outline-offset:3px; }
.said{ font-family:'IBM Plex Mono',monospace; font-size:11px; color:#8fbf8f;
  margin:14px 0 0; min-height:1.2em; }
/* Last-resort fallback: if neither clipboard path is allowed, put the text on
   screen, selected, so "copy" is still one keystroke away. */
.fallback{ display:none; width:100%; margin-top:16px; padding:14px; border-radius:10px;
  border:1px solid #4a4a4a; background:#141414; color:#d8d8d8; resize:vertical;
  font-family:'IBM Plex Mono',monospace; font-size:12px; line-height:1.6; min-height:150px; }
.fallback.on{ display:block; }

@media (max-width:1000px){
  .figs{ grid-template-columns:repeat(2,1fr); }
  .fig{ padding-right:20px; }
  .fig:nth-child(2n){ border-right:0; padding-right:0; }
  .fig:nth-child(-n+2){ border-bottom:1px dashed var(--line); }
}
@media (max-width:900px){
  :root{ --gutter:0; }
  .wrap{ padding:0 20px; }
  .pin{ height:280vh; }
  .turn__row{ grid-template-columns:26px 1fr; gap:2px 12px; }
  .turn__k{ grid-column:2; }
  .turn__v{ grid-column:2; }
  .turn__row::before{ left:6px; }
  .turn__cta{ grid-template-columns:26px 1fr auto; }

  /* On a phone the blocks collapsed to a third of a screen each — four beats
     went past in one thumb flick and the story stopped reading one-at-a-time.
     A floor gives each beat roughly a screen, with the content centred in it,
     which is what makes a scroll story feel paced on mobile. */
  .b{ min-height:62vh; display:flex; flex-direction:column; justify-content:center; }
  .b--tall{ min-height:78vh; }
  .b--tight{ min-height:0; display:block; }
  .b > .col, .b > .wrap{ width:100%; }

  /* The label column ate 104 of 375px and squeezed the line. Stack it. */
  .step summary{ grid-template-columns:1fr auto; gap:6px 12px; padding:15px 0; }
  .step .when{ grid-column:1 / -1; }
  .step .detail{ padding-left:0; }
  .stack{ gap:0 8px; }
}
@media (max-width:900px) and (max-height:640px){
  /* Short phones in landscape: a 62vh floor would leave almost no room to
     read, so let content decide again. */
  .b, .b--tall{ min-height:0; display:block; }
}
@media (max-width:520px){
  .figs{ grid-template-columns:1fr; }
  .fig{ border-right:0; padding:18px 0; border-bottom:1px dashed var(--line); }
  .fig:last-child{ border-bottom:0; }
}
@media (prefers-reduced-motion:reduce){
  .armed .r{ opacity:1; transform:none; animation:none; }
  .armed .tldr:not(.in) .turn__dot,
  .armed .tldr.in .turn__dot{ transform:none; animation:none; }
  .turn__row:hover .turn__dot{ transform:none; }
  .pin{ height:auto; }
  .pin__stage{ position:static; height:auto; padding-block:60px; }
}

/* ─── Keyart ───────────────────────────────────────────────────────────────
   The case pages are otherwise pure typography, so an image has to arrive
   deliberately rather than float in as decoration: full measure, a hard edge,
   and a caption that carries an idea instead of naming the file. */
.keyart{ margin:44px 0 0; max-width:min(760px,100%); }
.keyart img{
  display:block; width:100%; height:auto;
  border:1px solid var(--ink); border-radius:4px; background:var(--ink);
}
.keyart figcaption{
  margin-top:12px; font-family:'IBM Plex Mono',monospace; font-size:12px;
  letter-spacing:.02em; color:var(--ink-low);
}
.keyart figcaption em{ font-style:normal; color:var(--ink); }
@media (max-width:700px){ .keyart{ margin-top:32px; } }
