/*
  zyltech.ca shared styles. Direction D, "Exploded view", merged from the approved samples
  _directions/d.html and _directions/d-claude.html. Every page links this file and assets/js/site.js.

  Palette (one light theme, one accent):
    --ground   #EEF1F4  page
    --ground-2 #E3E8ED  tinted band, hover fills
    --surface  #F7F9FB  panel cores, tiles
    --ink      #111418  text, 16.3:1 on ground
    --ink-2    #3E4651  secondary text, 8.4:1 on ground
    --ink-3    #59616C  mono labels, 5.5:1 on ground, 5.1:1 on ground-2
    --line     #C6CDD5  hairlines (never text)
    --yellow   #F5C400  the only accent, always a fill under ink text (11.2:1). Never yellow text.
  Radius: 999px for anything you press, 16px outer bezel, 10px inner core and tiles.
  Type: Geist 400/500/600, Geist Mono 400/500 for numbers, specs and labels.
    Fixed sizes come from five steps: --fs-label 12.5, --fs-meta 14, --fs-small 15.5, --fs-body 17, --fs-lead 19.
  Motion: transform, opacity and clip-path only. No scroll listeners. Reduced motion shows everything at once.
*/

:root{
  --ground:#EEF1F4; --ground-2:#E3E8ED; --surface:#F7F9FB;
  --ink:#111418; --ink-2:#3E4651; --ink-3:#59616C;
  --line:#C6CDD5;
  --yellow:#F5C400; --yellow-2:#E2B400;
  --ink-hover:#2A3038; --media-well:#0F1215;
  --r-lg:16px; --r-md:10px; --r-pill:999px;
  --fs-label:12.5px; --fs-meta:14px; --fs-small:15.5px; --fs-body:17px; --fs-lead:19px;
  --e-out:cubic-bezier(.16,1,.3,1);
  --gut:clamp(16px,4vw,48px);
  --max:1344px;
  --sans:"Geist",ui-sans-serif,system-ui,sans-serif;
  --mono:"Geist Mono",ui-monospace,"Cascadia Mono",Consolas,monospace;
  color-scheme:light;
}

/* ---------- base ---------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-padding-top:88px;overflow-x:clip;scrollbar-color:#7A838E var(--ground)}
body{margin:0;background:var(--ground);color:var(--ink);font:400 var(--fs-body)/1.55 var(--sans);-webkit-font-smoothing:antialiased;overflow-x:clip}
h1,h2,h3,p,dl,dd,figure,ul,ol,blockquote{margin:0}
ul,ol{padding:0;list-style:none}
img,svg{display:block;max-width:100%}
a{color:inherit}
p{text-wrap:pretty}
::selection{background:var(--yellow);color:var(--ink)}
:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
.wrap{width:100%;max-width:calc(var(--max) + 2 * var(--gut));margin-left:auto;margin-right:auto;padding-left:var(--gut);padding-right:var(--gut)}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.skip{position:absolute;left:var(--gut);top:-80px;z-index:60;padding:12px 16px;border-radius:var(--r-pill);background:var(--ink);color:var(--surface);text-decoration:none}
.skip:focus{top:12px}
.ic{width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;gap:10px;min-height:48px;padding:0 22px;border:1.5px solid transparent;border-radius:var(--r-pill);font:500 16px/1 var(--sans);text-decoration:none;white-space:nowrap;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:transform .25s var(--e-out)}
.btn:active{transform:translateY(1px) scale(.98)}
.btn--y{background:var(--yellow);color:var(--ink);box-shadow:inset 0 1px 0 rgba(255,255,255,.55),0 8px 18px -10px rgba(28,38,52,.45)}
.btn--y:hover{background:var(--yellow-2);box-shadow:inset 0 1px 0 rgba(255,255,255,.5),0 12px 22px -10px rgba(28,38,52,.5)}
.btn--ink{background:var(--ink);color:var(--surface)}
.btn--ink:hover{background:var(--ink-hover)}
.btn--line{background:transparent;color:var(--ink);border-color:var(--ink)}
.btn--line:hover{background:var(--ground-2)}
.btn--sm{min-height:40px;padding:0 16px;font-size:15px}
.btn--lg{min-height:60px;padding:0 28px;font-size:18px}
.btn__i{display:grid;place-items:center;width:30px;height:30px;margin-right:-12px;border-radius:var(--r-pill);background:rgba(17,20,24,.10);transition:transform .35s var(--e-out)}
.btn:hover .btn__i{transform:translateX(2px)}
.btn:hover .btn__i--down{transform:translateY(2px)}

/* ---------- top: logo pill, chapter capsule, chapter readout ---------- */
/* glass only on fixed layers */
.glass{background:rgba(247,249,251,.74);-webkit-backdrop-filter:blur(16px) saturate(140%);backdrop-filter:blur(16px) saturate(140%);border:1px solid rgba(17,20,24,.10);box-shadow:inset 0 1px 0 rgba(255,255,255,.9),0 10px 28px -16px rgba(28,38,52,.38)}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){.glass{background:rgba(247,249,251,.96)}}
@media (prefers-reduced-transparency:reduce){.glass{background:var(--surface);-webkit-backdrop-filter:none;backdrop-filter:none}}
.top{position:fixed;z-index:40;top:12px;left:var(--gut);right:var(--gut);display:flex;align-items:center;justify-content:space-between;gap:16px;pointer-events:none}
.top>*{pointer-events:auto}
.brand{display:flex;align-items:center;height:52px;padding:0 18px;border-radius:var(--r-pill)}
.brand img{width:92px;height:22px}
.cap{position:relative;display:flex;align-items:center;min-height:52px;padding:5px;border-radius:var(--r-pill)}
.cap__panel{display:flex;align-items:center;gap:6px}
.chapters ol{display:flex;gap:2px}
.chapters a{display:flex;align-items:center;gap:7px;height:40px;padding:0 12px;border-radius:var(--r-pill);font:500 var(--fs-meta)/1 var(--sans);text-decoration:none}
.chapters .n{font:500 var(--fs-label)/1 var(--mono);color:var(--ink-2)}
.chapters a:hover{background:var(--ground-2)}
/* the current chapter: yellow fill plus an ink ring, so it does not rest on hue or text colour alone */
.chapters a[aria-current]{background:var(--yellow);box-shadow:inset 0 0 0 1.5px var(--ink)}
.chapters a[aria-current] .n{color:var(--ink)}
.menu-btn{display:none}
.where{position:fixed;z-index:40;left:var(--gut);bottom:16px;display:flex;align-items:center;gap:8px;height:38px;padding:0 14px 0 6px;border-radius:var(--r-pill);font:500 var(--fs-label)/1 var(--mono);color:var(--ink-2);pointer-events:none}
.where b{font-weight:500;color:var(--ink);background:var(--yellow);border-radius:var(--r-pill);padding:6px 8px}
.where__t{color:var(--ink);font-family:var(--sans);font-size:var(--fs-meta)}
/* desktop: the capsule already marks the chapter, and a fixed readout would sit on top of content */
@media (min-width:1000px){.where{display:none}}
@media (max-width:999px){
  .menu-btn{display:inline-flex}
  .cap__panel{display:none;position:absolute;top:calc(100% + 10px);right:0;width:min(420px,calc(100vw - 2 * var(--gut)));max-height:calc(100dvh - 86px);overflow-y:auto;overscroll-behavior:contain;flex-direction:column;align-items:stretch;gap:12px;padding:10px;border-radius:var(--r-lg);background:var(--surface);border:1px solid rgba(17,20,24,.10);box-shadow:0 18px 40px -18px rgba(28,38,52,.45)}
  .top[data-open] .cap__panel{display:flex}
  .chapters ol{flex-direction:column}
  .chapters a{height:48px;padding:0 14px;font-size:var(--fs-body)}
  .cap__panel .btn{justify-content:center}
  /* 8px right of the 130px brand pill, so it never covers the wordmark or its focus ring */
  .where{top:19px;bottom:auto;left:calc(var(--gut) + 138px);padding-right:12px}
  .where__t{display:none}
  /* the menu only opens with site.js; without it the capsule is hidden rather than left as a dead button.
     The brand pill still goes home, and each page links its work and Email me in the body */
  html:not(.js) .cap{display:none}
}
/* under 360px the brand, the readout and the menu button do not fit on one row */
@media (max-width:359px){.where{display:none}}

/* ---------- section scaffolding ---------- */
.sec{padding-top:clamp(96px,11vw,168px)}
/* section heads stay a step under the hero h1 so the page keeps one top voice */
.sec h2,.band h2{font:600 clamp(1.6rem,2.4vw,2.25rem)/1.08 var(--sans);letter-spacing:-.03em;text-wrap:balance;max-width:24em}
.sec__lede{margin-top:20px;font-size:clamp(18px,1.5vw,20px);line-height:1.55;max-width:60ch;color:var(--ink-2)}
.band{margin-top:clamp(96px,11vw,168px);padding-top:80px;padding-bottom:80px;background:var(--ground-2)}
@media (min-width:1000px){.band{padding-top:112px;padding-bottom:112px}}
.bezel{border-radius:var(--r-lg);padding:6px;background:linear-gradient(180deg,#FAFBFC,#D6DCE3);box-shadow:0 0 0 1px rgba(17,20,24,.08),0 18px 36px -26px rgba(28,38,52,.5)}
.bezel__in{border-radius:calc(var(--r-lg) - 6px);background:var(--surface);box-shadow:inset 0 1px 0 #FDFDFE}
.tags{display:flex;flex-wrap:wrap;gap:6px}
.tag{display:inline-flex;align-items:center;height:26px;padding:0 10px;border:1px solid var(--ink-3);border-radius:var(--r-pill);font:400 var(--fs-label)/1 var(--mono);color:var(--ink);white-space:nowrap}
.tag--ink{background:var(--ink);border-color:var(--ink);color:var(--surface)}
.who{font:600 20px/1.3 var(--sans);letter-spacing:-.01em}
.who span{font-weight:400;color:var(--ink-3)}
.idline{margin-top:8px;font:400 var(--fs-small)/1.5 var(--sans);color:var(--ink-2)}
.idline span{display:block}
.prose a,.end__line a,.fig__cap a{text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:4px}
.prose a:hover,.end__line a:hover,.fig__cap a:hover{text-decoration-thickness:2px}
.prose{margin-top:28px;max-width:72ch;font-size:var(--fs-lead);line-height:1.6}
.prose p+p{margin-top:1em}
/* captions are prose, so sans; mono stays for numbers, dates and short labels */
.fig__cap{margin-top:10px;font:400 var(--fs-meta)/1.5 var(--sans);color:var(--ink-2)}
.shot .bezel__in{overflow:hidden;background:var(--media-well)}
.shot--light .bezel__in{background:var(--surface)}
.shot img{width:100%;height:auto}

/* ---------- home: hero ---------- */
.hero{min-height:100dvh;display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);align-items:center;gap:clamp(32px,4.5vw,72px);padding-top:96px;padding-bottom:48px}
.hero h1{margin-top:28px;font:600 clamp(2.1rem,3.25vw,3.1rem)/1.06 var(--sans);letter-spacing:-.035em;text-wrap:balance;max-width:21em}
.acts{margin-top:36px;display:flex;flex-wrap:wrap;gap:12px}

/* ---------- home: the work stack (hero, right column) ----------
   Adapted from React Bits CardSwap (https://github.com/DavidHDev/react-bits/blob/main/src/content/Components/CardSwap/CardSwap.css).
   MIT + Commons Clause: used on zyltech.ca only; this port is not redistributed or sold.
   --p is a card's layer (0 = front). These rules alone fan the stack out, so it is complete with JS off;
   site.js then writes each card's transform, z-index and shade inline. Sizes come from container units. */
.swapfig{display:grid;gap:14px;min-width:0}
/* clip: a card thrown off the front is cut at the column edge and never covers the console or the caption */
.swap{position:relative;height:clamp(320px,54svh,520px);container-type:size;perspective:900px;perspective-origin:30% 62%;overflow:clip}
/* pan-y pinch-zoom: a sideways swipe moves the card, an upright one still scrolls the page */
.swap__slot{position:absolute;left:3%;bottom:8%;width:min(72cqw,98cqh);aspect-ratio:16/10;
  transform:translate3d(calc(var(--p)*9%),calc(var(--p)*-13%),calc(var(--p)*-80px)) skewY(4deg);
  z-index:calc(10 - var(--p));touch-action:pan-y pinch-zoom;user-select:none;-webkit-user-select:none;will-change:transform}
.swap__card{display:flex;flex-direction:column;height:100%;text-decoration:none;-webkit-user-drag:none}
.swap__head{display:flex;align-items:center;gap:8px;height:30px;padding:0 8px;font:500 var(--fs-label)/1 var(--mono)}
.swap__head .n{color:var(--ink-2)}
.swap__head .ic{margin-left:auto}
.swap__media{position:relative;flex:1;min-height:0;overflow:hidden;background:var(--media-well)}
.swap__media:not(.media--sm)>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:top left}
.swap__media img{-webkit-user-drag:none}
.swap__shade{position:absolute;inset:0;border-radius:var(--r-lg);background:var(--ground);opacity:calc(var(--p)*.155);pointer-events:none}
.swap__card:focus-visible{outline:2px solid var(--ink);outline-offset:3px;border-radius:var(--r-lg)}
/* system cursors only: grab on the front card, the link pointer on the cards behind it */
@media (pointer:fine){.js .swap__slot.is-front .swap__card{cursor:grab}.swap.is-drag .swap__card{cursor:grabbing}}
.console__in{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px 20px;padding:14px 14px 14px 18px}
.readout{flex:1 1 100%;display:grid;gap:4px;min-width:0}
.readout__k{font:500 var(--fs-label)/1.3 var(--mono);color:var(--ink-3)}
/* room for the longest line of the three, so the console does not change height between cards */
.readout__v{font:600 18px/1.3 var(--sans);letter-spacing:-.01em;min-height:2.6em}
@media (max-width:599px){.readout__v{min-height:3.9em}}
/* the controls only mean something once site.js runs; they hold their space so the hero does not jump */
.ctrl{display:flex;flex-wrap:wrap;gap:8px;visibility:hidden}
.js .ctrl{visibility:visible}
@media (max-width:899px){
  .hero{grid-template-columns:minmax(0,1fr);align-items:start;gap:40px;padding-top:100px}
  .swap{height:clamp(260px,62vw,400px)}
}

/* ---------- home: the work shelf (7 + 5, 7 + 5, then three short entries) ---------- */
.shelf{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:20px;margin-top:clamp(40px,5vw,64px)}
.bay{position:relative;display:flex;min-width:0}
.bay--ord{grid-column:span 7}.bay--cc{grid-column:span 5}
.bay--sm{grid-column:span 7}.bay--tf{grid-column:span 5}
.bay--cs,.bay--tm,.bay--br{grid-column:span 4}
.bay>.bezel__in{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}
.bay__media{--mh:clamp(220px,20vw,300px);position:relative;height:var(--mh);overflow:hidden;background:var(--media-well)}
.bay--ord .bay__media,.bay--cc .bay__media{--mh:clamp(280px,30vw,440px)}
.bay__media>img{width:100%;height:100%;object-fit:cover;object-position:top left;transition:transform .9s var(--e-out)}
.phone{position:absolute;right:5%;bottom:6%;width:15%;padding:4px;border-radius:var(--r-lg);background:linear-gradient(180deg,#FAFBFC,#B9C2CC);box-shadow:0 16px 30px -12px rgba(8,11,15,.6);transition:transform .9s var(--e-out)}
.phone img{width:100%;height:auto;border-radius:calc(var(--r-lg) - 4px)}
.media--sm{display:flex;justify-content:center;align-items:flex-start;gap:4%;padding-top:22px;background:linear-gradient(180deg,#E4E9EE,#C3CBD4)}
.media--sm img{width:26%;height:auto;border-radius:var(--r-md);box-shadow:0 14px 28px -14px rgba(28,38,52,.6);transition:transform .9s var(--e-out)}
.bay__body{display:grid;gap:10px;padding:20px 22px 0;align-content:start}
.bay h3{font:600 26px/1.1 var(--sans);letter-spacing:-.025em}
.bay__line{color:var(--ink-2);max-width:52ch}
.bay__fact{font:400 var(--fs-meta)/1.55 var(--sans);color:var(--ink-2)}
.bay__links{display:flex;flex-wrap:wrap;gap:8px 22px;padding-top:2px}
.bay__links a{position:relative;z-index:2;display:inline-flex;align-items:center;gap:6px;min-height:32px;font-weight:500;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:5px}
.bay__links a:hover{text-decoration-thickness:2px}
/* the main link stretches over the whole bay; any second link sits above it */
.bay__links a.main{position:static}
.bay__links a.main::after{content:"";position:absolute;inset:0;z-index:1;border-radius:var(--r-lg)}
.bay__meta{display:flex;flex-wrap:wrap;gap:8px 28px;margin-top:auto;padding:18px 22px 20px}
.bay__meta>div{min-width:0}
.bay__meta dt{font:400 var(--fs-label)/1.4 var(--mono);color:var(--ink-3)}
.bay__meta dd{font-size:var(--fs-meta);line-height:1.4}
.bay--link:hover .bay__media>img,.bay--link:hover .media--sm img{transform:scale(1.03)}
.bay--link:hover .phone{transform:translateY(-6px)}
.bay:has(a.main:focus-visible){outline:2px solid var(--ink);outline-offset:4px;border-radius:var(--r-lg)}
.bay:has(a.main:focus-visible) a.main:focus-visible{outline:none}
@media (max-width:1099px){
  .shelf{grid-template-columns:repeat(6,minmax(0,1fr))}
  .bay--ord,.bay--cc,.bay--sm,.bay--tf,.bay--cs,.bay--tm,.bay--br{grid-column:span 6}
  .bay__media{--mh:34vw}
  .bay--ord .bay__media,.bay--cc .bay__media{--mh:46vw}
}
@media (max-width:699px){
  .shelf{grid-template-columns:minmax(0,1fr);gap:16px}
  .bay--ord,.bay--cc,.bay--sm,.bay--tf,.bay--cs,.bay--tm,.bay--br{grid-column:auto}
  .bay__media{--mh:54vw}
  .bay--ord .bay__media,.bay--cc .bay__media{--mh:68vw}
  .phone{width:22%}
  .bay__body{padding:18px 16px 0}
  .bay__meta{padding:16px}
}

/* ---------- ledger: big mono numbers with a line of text (home band, how-i-build, case Checks) ---------- */
.ledger{margin-top:clamp(40px,5vw,64px)}
.ledger li{display:grid;grid-template-columns:minmax(0,4fr) minmax(0,8fr);gap:12px 32px;align-items:baseline;padding-top:24px;padding-bottom:24px;border-top:1px solid var(--line)}
.ledger__n{font:500 clamp(2rem,3.6vw,3.25rem)/1 var(--mono);letter-spacing:-.04em;font-variant-numeric:tabular-nums}
.ledger__t{font-size:18px;line-height:1.5;max-width:60ch}
.ledger__t span{display:block;margin-top:6px;color:var(--ink-2);font-size:var(--fs-small)}
@media (max-width:699px){.ledger li{grid-template-columns:minmax(0,1fr)}}
/* grouped by project: a ruled group label beside its rows */
.grp{display:grid;grid-template-columns:minmax(0,3fr) minmax(0,9fr);gap:0 32px;margin-top:clamp(40px,5vw,56px);border-top:1.5px solid var(--ink)}
.grp h3{padding-top:26px;font:500 var(--fs-meta)/1.3 var(--mono);color:var(--ink-2)}
.grp .ledger{margin-top:0}
.grp .ledger li{grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:10px 32px;border-top:0}
.grp .ledger li+li{border-top:1px solid var(--line)}
.grp .ledger__n{font-size:clamp(2.2rem,4.4vw,3.25rem)}
.grp .ledger__t{max-width:44ch}
/* a group told in a sentence instead of big numbers (self-reported figures) */
.grp>p{padding-top:24px;padding-bottom:24px;font-size:18px;line-height:1.5;max-width:60ch}
@media (max-width:999px){.grp{grid-template-columns:minmax(0,1fr)}.grp h3{padding-top:18px}}
@media (max-width:599px){.grp .ledger li{grid-template-columns:minmax(0,1fr)}}

/* ---------- home: how I build band ---------- */
.how__lead{margin-top:28px;font:500 clamp(1.3rem,2.1vw,1.85rem)/1.35 var(--sans);letter-spacing:-.02em;max-width:46ch}
.how__go{margin-top:40px}

/* ---------- home: about ---------- */
.about__grid{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);gap:40px clamp(32px,5vw,80px);margin-top:40px;align-items:start}
.about__text p{font-size:var(--fs-lead);line-height:1.6;max-width:62ch}
.about__text p+p{margin-top:1em}
.about__text .now{margin-top:1.6em;padding-top:1em;border-top:1px solid rgba(17,20,24,.12);font-size:var(--fs-small);color:var(--ink-2)}
.creds{padding:22px 22px 24px}
.creds h3{font:500 var(--fs-label)/1.3 var(--mono);color:var(--ink-3)}
.creds h3+*{margin-top:12px}
.creds h3:not(:first-child){margin-top:28px}
.edu li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px 16px;padding-top:10px;padding-bottom:10px;border-top:1px solid var(--line)}
.edu b{font-weight:600}
.edu span{grid-column:1;color:var(--ink-2);font-size:var(--fs-small)}
.edu .yr{grid-column:2;grid-row:1;font:400 var(--fs-label)/1.6 var(--mono);color:var(--ink-2);white-space:nowrap}
.certs div{display:grid;grid-template-columns:minmax(0,9.5em) minmax(0,1fr);gap:4px 16px;padding-top:9px;padding-bottom:9px;border-top:1px solid var(--line);font-size:var(--fs-small)}
.certs dt{font-weight:600}
.certs dd{color:var(--ink-2)}
@media (max-width:899px){.about__grid{grid-template-columns:minmax(0,1fr)}}
@media (max-width:479px){.certs div{grid-template-columns:minmax(0,1fr)}}

/* ---------- page intro (how-i-build) ---------- */
.intro{min-height:100dvh;display:grid;align-content:center;gap:clamp(36px,5vw,64px);padding-top:110px;padding-bottom:56px}
.intro h1{margin-top:28px;font:600 clamp(3rem,7vw,6rem)/.96 var(--sans);letter-spacing:-.04em;text-wrap:balance}
.intro__grid{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);gap:32px clamp(32px,5vw,80px);align-items:start}
.intro__lead{font:500 clamp(1.3rem,2.1vw,1.85rem)/1.35 var(--sans);letter-spacing:-.02em;max-width:40ch}
.intro__body{margin-top:20px;font-size:var(--fs-lead);line-height:1.6;max-width:56ch;color:var(--ink-2)}
.toc .bezel__in{padding:16px 18px 8px}
.toc__k{font:500 var(--fs-label)/1.3 var(--mono);color:var(--ink-3)}
.toc ol{margin-top:10px}
.toc li a{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:48px;border-top:1px solid var(--line);font-weight:500;text-decoration:none}
.toc li a .ic{transition:transform .3s var(--e-out)}
.toc li a:hover .lbl{text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:4px}
.toc li a:hover .ic{transform:translateY(2px)}
@media (max-width:899px){.intro__grid{grid-template-columns:minmax(0,1fr)}}

/* ---------- how-i-build: the split, one table with a hinge down the middle ---------- */
.split{margin-top:clamp(40px,5vw,64px)}
.split table{display:block;width:100%;border-collapse:collapse}
.split thead,.split tbody{display:block}
.split tr{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
.split th,.split td{display:block;padding:0;text-align:left;font-weight:inherit}
.split thead th{padding-bottom:18px;font:600 clamp(1.3rem,2.2vw,1.9rem)/1.15 var(--sans);letter-spacing:-.02em}
.split thead .mine{text-align:right;padding-right:44px}
.split thead .ai{padding-left:44px;border-left:1px solid var(--ink)}
.split tbody tr{border-top:1px solid var(--line)}
.split tbody .mine{padding:24px 44px 26px 0;text-align:right}
.split tbody .ai{position:relative;padding:24px 0 26px 44px;border-left:1px solid var(--ink)}
.term{display:block;font:600 var(--fs-lead)/1.3 var(--sans);letter-spacing:-.01em}
.eg{display:block;max-width:46ch;margin-top:6px;color:var(--ink-2);font-size:var(--fs-small);line-height:1.5}
/* the block hugs the hinge, but its lines start flush left so they read evenly */
.mine .eg{margin-left:auto;text-align:left}
.cl{display:none}
.knob{position:absolute;left:-15px;top:24px;display:grid;place-items:center;width:28px;height:28px;border-radius:var(--r-pill);background:linear-gradient(180deg,#FAFBFC,#C9D1D9);box-shadow:0 0 0 1px rgba(17,20,24,.2),0 4px 10px -6px rgba(28,38,52,.5)}
.knob .ic{width:14px;height:14px}
@media (max-width:699px){
  .split thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .split tr{grid-template-columns:minmax(0,1fr)}
  .split tbody .mine{padding:20px 0 14px;text-align:left}
  .mine .eg{margin-left:0}
  .split tbody .ai{padding:14px 0 22px;border-left:0;border-top:1px dashed var(--line)}
  .knob{display:none}
  .cl{display:block;margin-bottom:6px;font:500 var(--fs-label)/1.3 var(--mono);color:var(--ink-3)}
}

/* ---------- how-i-build: the loop, a five-step rail with a return bracket ---------- */
.steps{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));margin-top:clamp(48px,5vw,64px)}
.steps li{position:relative;padding:30px 24px 0 0;border-top:1.5px solid var(--ink)}
.steps .tag{position:absolute;top:-14px;left:0;background:var(--ground)}
.steps .tag--ink{background:var(--ink)}
.steps h3{font:600 var(--fs-lead)/1.3 var(--sans);letter-spacing:-.01em}
.steps p{margin-top:6px;color:var(--ink-2);font-size:var(--fs-small);line-height:1.5}
.back{display:flex;align-items:flex-end;justify-content:center;gap:8px;height:52px;margin:22px 0 0 40%;width:40%;padding-bottom:10px;border:1.5px solid var(--ink);border-top:0;border-radius:0 0 var(--r-md) var(--r-md);font:400 var(--fs-label)/1.3 var(--mono);color:var(--ink-2)}
.back .ic{width:14px;height:14px}
@media (max-width:899px){
  .steps{grid-template-columns:minmax(0,1fr);gap:34px}
  .steps li{padding-right:0}
  .back{justify-content:flex-start;align-items:center;height:auto;width:auto;margin:28px 0 0;padding:0;border:0}
}

/* ---------- worked examples (how-i-build) and decision sections (case pages): text 5, figure 7 ---------- */
.exs{display:grid;gap:clamp(56px,6vw,96px);margin-top:clamp(64px,7vw,104px)}
.ex,.dec{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:28px clamp(32px,4vw,64px);align-items:start}
.ex{padding-top:28px;border-top:1px solid var(--line)}
.dec{margin-top:clamp(40px,5vw,56px)}
/* rhythm on the case pages: every other decision flips, a wide board runs full width under its text,
   and a single phone takes the narrow column */
.dec--flip{grid-template-columns:minmax(0,7fr) minmax(0,5fr)}
.dec--flip>figure{order:-1}
.dec--wide{grid-template-columns:minmax(0,1fr)}
.dec--phone{grid-template-columns:minmax(0,7fr) minmax(0,5fr)}
.dec--phone .phones{grid-template-columns:minmax(0,1fr);max-width:340px}
.ex__meta{display:flex;flex-wrap:wrap;gap:8px 28px;margin-bottom:16px}
.ex__meta dt{font:400 var(--fs-label)/1.4 var(--mono);color:var(--ink-3)}
.ex__meta dd{font-size:var(--fs-meta);line-height:1.4}
.ex h3{font:600 clamp(1.4rem,2.2vw,1.9rem)/1.15 var(--sans);letter-spacing:-.025em;text-wrap:balance}
.ex__text p,.dec__text p{margin-top:14px;max-width:52ch}
.dec__text p:first-child{margin-top:0}
/* an example with no figure: the paragraph takes the figure column at lead size */
.ex__body p{font-size:clamp(19px,1.6vw,21px);line-height:1.55;max-width:52ch}
.ex__body p+p{margin-top:14px}
@media (max-width:899px){.ex,.dec{grid-template-columns:minmax(0,1fr)}.dec--flip>figure{order:0}}

/* ---------- case pages: header definition list (Role / Team / Timeline / Context / Status / Testing / Scope / How it was made) ---------- */
.casehead{display:grid;align-content:center;gap:clamp(28px,4vw,48px);padding-top:110px;padding-bottom:56px}
.casehead h1{margin-top:20px;font:600 clamp(2.4rem,5vw,4.5rem)/1.02 var(--sans);letter-spacing:-.035em;text-wrap:balance;max-width:18em}
.casehead__lede{margin-top:20px;font:500 clamp(1.2rem,1.8vw,1.55rem)/1.4 var(--sans);letter-spacing:-.015em;max-width:48ch}
.casehead__acts{display:flex;flex-wrap:wrap;gap:12px;margin-top:28px}
.ovw{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr));gap:0 28px;border-top:1.5px solid var(--ink)}
.ovw>div{padding:16px 0 18px;border-bottom:1px solid var(--line)}
.ovw dt{font:400 var(--fs-label)/1.4 var(--mono);color:var(--ink-3)}
.ovw dd{margin-top:6px;font-size:var(--fs-small);line-height:1.45}

/* ---------- case pages: before and after, side by side ---------- */
.ba{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px clamp(16px,2vw,28px);margin-top:clamp(32px,4vw,48px)}
.ba figure{min-width:0}
.ba .fig__cap{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px}
@media (max-width:699px){.ba{grid-template-columns:minmax(0,1fr)}}

/* ---------- rules (how-i-build) and gaps (case pages): ruled lists ---------- */
.rules{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:clamp(32px,5vw,80px);margin-top:clamp(40px,5vw,56px)}
.gaps{margin-top:clamp(32px,4vw,48px);max-width:880px}
.rules li,.gaps li{padding:22px 0 26px;border-top:1px solid var(--line)}
.rules h3,.gaps h3{font:600 clamp(1.25rem,1.8vw,1.5rem)/1.25 var(--sans);letter-spacing:-.015em}
.rules p,.gaps p{margin-top:8px;color:var(--ink-2);font-size:var(--fs-body);line-height:1.55;max-width:56ch}
@media (max-width:699px){.rules{grid-template-columns:minmax(0,1fr)}}

/* ---------- quote on the tinted band ---------- */
.quote{margin-top:clamp(32px,4vw,48px)}
.quote blockquote p{font:500 clamp(1.6rem,3.2vw,2.75rem)/1.25 var(--sans);letter-spacing:-.025em;max-width:32ch}
.quote figcaption{margin-top:20px;font:400 var(--fs-meta)/1.5 var(--mono);color:var(--ink-3)}

/* ---------- case pages: next case study ---------- */
.next{display:block;margin-top:clamp(96px,11vw,168px);text-decoration:none;color:var(--ink)}
.next__in{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);align-items:center;gap:24px clamp(24px,4vw,56px);padding:24px;overflow:hidden}
@media (min-width:900px){.next__in{padding:32px}}
.next__k{display:block;font:500 var(--fs-label)/1.3 var(--mono);color:var(--ink-3)}
.next__t{display:flex;align-items:center;gap:16px;margin-top:10px;font:600 clamp(2rem,4vw,3.5rem)/1 var(--sans);letter-spacing:-.035em}
.next__t .btn__i{width:44px;height:44px;margin:0;background:var(--yellow)}
.next__line{display:block;margin-top:14px;color:var(--ink-2);max-width:44ch}
.next__media{display:block;aspect-ratio:16/10;border-radius:var(--r-md);overflow:hidden;background:var(--media-well)}
.next__media img{width:100%;height:100%;object-fit:cover;object-position:top left;transition:transform .9s var(--e-out)}
.next:hover .next__lbl{text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:6px}
.next:hover .btn__i{transform:translateX(3px)}
.next:hover .next__media img{transform:scale(1.03)}
@media (max-width:699px){.next__in{grid-template-columns:minmax(0,1fr)}}

/* ---------- page end: contact, how-i-build close, 404 ---------- */
.end{min-height:60vh}
/* the closing head sits under each page's h1; only the 404 title, the page's one heading, keeps display size */
.end h2{font:600 clamp(2.2rem,4vw,3.5rem)/1 var(--sans);letter-spacing:-.04em;max-width:none;text-wrap:balance}
.nf h1{font:600 clamp(3rem,8vw,6rem)/.95 var(--sans);letter-spacing:-.04em;max-width:none;text-wrap:balance}
.end__line{margin-top:28px;font-size:clamp(19px,1.8vw,24px);line-height:1.45;max-width:46ch}
.contact__acts{margin-top:36px;display:flex;flex-wrap:wrap;align-items:center;gap:16px 32px}
.contact__acts li{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.contact__acts li:has(.btn){align-items:center}
.contact__acts .ln{font:500 var(--fs-lead)/1.3 var(--sans);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:5px}
.contact__acts .ln:hover{text-decoration-thickness:2px}
.contact__acts small{font:400 var(--fs-meta)/1.4 var(--mono);color:var(--ink-2)}
.nf{min-height:100dvh;display:grid;align-content:center;padding-top:110px;padding-bottom:56px}
.foot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px 32px;margin-top:clamp(80px,9vw,128px);padding-top:24px;padding-bottom:72px;border-top:1px solid var(--line)}
.foot img{width:92px;height:22px}
.foot p{font:400 var(--fs-label)/1.5 var(--mono);color:var(--ink-3)}

/* ---------- touch: 44px targets ---------- */
@media (pointer:coarse){
  .btn--sm{min-height:44px}
  .bay__links a,.contact__acts .ln{min-height:44px;display:inline-flex;align-items:center}
}

/* ---------- forced colours: background fills vanish, so selected states use system highlight ---------- */
@media (forced-colors:active){
  .chapters a[aria-current]{forced-color-adjust:none;background:Highlight;color:HighlightText}
  .chapters a[aria-current] .n{color:HighlightText}
}

/* ---------- motion, only on screen and only when the viewer has not asked for less ---------- */
/* screen only: printing or saving as PDF shows every hidden-until-revealed item in its final state */
@media screen and (prefers-reduced-motion:no-preference){
  html{scroll-behavior:smooth}
  /* cross-document View Transition: the work image clicked on the home page or in Next case becomes the case cover.
     Native API, no library; site.js names the clicked image "cover" for that one navigation */
  @view-transition{navigation:auto}
  .top[data-open] .cap__panel{transition:opacity .25s var(--e-out),transform .4s var(--e-out)}
  @starting-style{.top[data-open] .cap__panel{opacity:0;transform:translateY(-8px)}}
  .js .bay[data-reveal]{opacity:0;transform:translateY(32px)}
  .js .bay[data-reveal].is-in{opacity:1;transform:none;transition:opacity .7s var(--e-out) calc(var(--i,0) * 80ms),transform 1s var(--e-out) calc(var(--i,0) * 80ms)}
  .js .ledger [data-reveal] .ledger__n{clip-path:inset(0 0 100% 0);transform:translateY(.3em)}
  .js .ledger [data-reveal].is-in .ledger__n{clip-path:inset(0);transform:none;transition:clip-path .9s var(--e-out),transform .9s var(--e-out)}
  .js .split tr[data-reveal] .ai>*{opacity:0;transform:translateX(-14px)}
  .js .split tr[data-reveal].is-in .ai>*{opacity:1;transform:none;transition:opacity .6s var(--e-out) .12s,transform .8s var(--e-out) .12s}
  .js .steps li[data-reveal]{opacity:0;transform:translateY(18px)}
  .js .steps li[data-reveal].is-in{opacity:1;transform:none;transition:opacity .6s var(--e-out) calc(var(--i,0) * 90ms),transform .8s var(--e-out) calc(var(--i,0) * 90ms)}
  .js [data-reveal]:is(.ex,.dec,.ba) figure{opacity:0;transform:translateY(28px)}
  .js [data-reveal].is-in:is(.ex,.dec,.ba) figure{opacity:1;transform:none;transition:opacity .7s var(--e-out),transform 1s var(--e-out)}
  .js .ba[data-reveal].is-in figure+figure{transition-delay:.12s}
}
.casehead .shot .bezel__in{view-transition-name:cover}
::view-transition-group(cover){animation-duration:.45s;animation-timing-function:var(--e-out)}
::view-transition-old(root),::view-transition-new(root){animation-duration:.2s}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;animation-delay:0s!important;transition:none!important;transition-delay:0s!important;scroll-behavior:auto!important}
}

/* ---------- case: additions for case-jd-health.html ---------- */
/* case: phone screenshots at a readable size, two to a row; a single phone takes one column */
.phones{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(12px,2vw,20px);max-width:600px}
/* case: what changed along the way, a dated list */
.turns{margin-top:clamp(32px,4vw,48px);max-width:880px}
.turns li{display:grid;grid-template-columns:minmax(0,8.5em) minmax(0,1fr);gap:4px 28px;padding:18px 0 20px;border-top:1px solid var(--line)}
.turns time{font:500 var(--fs-meta)/1.75 var(--mono);color:var(--ink-2);font-variant-numeric:tabular-nums}
.turns p{max-width:60ch}
@media (max-width:599px){.turns li{grid-template-columns:minmax(0,1fr)}}

/* ---------- case: additions for case-ordinal.html ---------- */
/* case: detail crop, part of a screenshot at reading size. Inline custom properties give the image width (--iw)
   and the crop box (--cx, --cy, --cw, --ch), all in the image's own pixels */
.crop{position:relative;overflow:hidden;aspect-ratio:var(--cw)/var(--ch)}
.crop img{position:absolute;left:calc(var(--cx) / var(--cw) * -100%);top:calc(var(--cy) / var(--ch) * -100%);width:calc(var(--iw) / var(--cw) * 100%);max-width:none;height:auto}
/* with the crop box set on the bezel instead, the frame stops at the crop's own width (plus the 6px frame each side),
   so a narrow crop is never shown larger than 1x */
.bezel:has(>.bezel__in>.crop){max-width:calc(var(--cw) * 1px + 12px)}
/* case: before and after under one divider (#threshold). Mechanism borrowed from Aceternity Compare
   (https://ui.aceternity.com/components/compare), used under the item licence at https://ui.aceternity.com/licence;
   the code is this site's own. --pos (0-100) is the share of the frame, from the left, that shows Before.
   The divider moves with transform; a native range input does the work, so keys and screen readers come free */
.ba--cmp{grid-template-columns:minmax(0,1fr)}
.ba--cmp .fig__cap{display:grid;gap:8px}
.ba--cmp .fig__cap .tag{margin-right:6px}
.cmp{position:relative;aspect-ratio:1440/900;container-type:inline-size}
.cmp img,.cmp__after{position:absolute;inset:0;width:100%;height:100%}
/* the After image and the After tag are clipped together, so each tag only ever sits on its own image */
.cmp__after{clip-path:inset(0 0 0 calc(var(--pos)*1%))}
/* a light line with an ink edge, so it shows on the dark screenshots and on light ones */
.cmp__bar{position:absolute;top:0;bottom:0;left:-1px;width:2px;background:var(--surface);box-shadow:0 0 0 1px var(--ink);transform:translateX(calc(var(--pos)*1cqw));pointer-events:none}
/* yellow only as the handle's fill, under an ink border and ink arrows */
.cmp__knob{position:absolute;top:50%;left:50%;translate:-50% -50%;display:flex;align-items:center;justify-content:center;width:30px;height:44px;border-radius:var(--r-pill);background:var(--yellow);border:1.5px solid var(--ink)}
.cmp__knob .ic{width:12px;height:12px}
.cmp__l{transform:scaleX(-1)}
.cmp__tag{position:absolute;top:12px;left:12px}
.cmp__tag:not(.tag--ink){background:var(--surface)}
.cmp__tag--r{left:auto;right:12px}
.cmp__range{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:ew-resize}
/* focus: a surface ring inside an ink ring, so it shows on dark and light screenshots alike */
.cmp:has(.cmp__range:focus-visible) .cmp__knob{outline:none;box-shadow:0 0 0 3px var(--surface),0 0 0 5px var(--ink)}
/* touch: the range is a visible 44px slider on a light pill along the bottom, so the rest of the image still scrolls
   the page. The line stays to mark the seam; the handle goes, since the image itself is not dragged */
@media (pointer:coarse){
  .cmp__range{inset:auto 12px 12px;width:auto;height:44px;padding:0 10px;border-radius:var(--r-pill);background:var(--surface);opacity:1;accent-color:var(--ink)}
  .cmp__range:focus-visible{outline:2px solid var(--surface);outline-offset:2px;box-shadow:0 0 0 4px var(--ink)}
  .cmp__knob{display:none}
}
/* without JS the divider cannot move, so the control is not shown; the frame stays at 50/50 */
.cmp__range,.cmp__bar{visibility:hidden}
.js .cmp__range,.js .cmp__bar{visibility:visible}
