/* ============================================================
   TOKENS
============================================================ */
:root{
  --bg:#FAFAF8;
  --ink:#111111;
  --sub:#6A6A66;
  --line:rgba(17,17,17,.10);
  --hair:rgba(17,17,17,.07);
  --word:rgba(17,17,17,.055);
  --card:#FFFFFF;
  --accent:#9B948A;              /* barely-there warm grey, used sparingly */
  --display:"Satoshi",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --body:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --maxw:1280px;
  --gutter:40px;
  --ease:cubic-bezier(.22,1,.36,1);
}
*{margin:0;padding:0;box-sizing:border-box}
/* Chrome's scroll anchoring watches for content changes above the viewport
   and "helpfully" corrects scroll position on its own — which fights the
   explicit scrollTo(0,0) in showView() and is the actual cause of the
   scroll-below-then-jump-to-top flash when a case study opens. Disabled
   outright rather than raced against. */
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;scroll-padding-top:92px;overflow-anchor:none;scrollbar-width:none;-ms-overflow-style:none}
/* the page still scrolls, the bar just isn't painted — the reading-progress
   line under the header already reports position, so the native bar is
   redundant chrome. Same pattern the phone mockups already use. */
html::-webkit-scrollbar,body::-webkit-scrollbar{width:0;height:0;display:none}
body{
  background:var(--bg);color:var(--ink);font-family:var(--body);
  line-height:1.5;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
a{color:inherit;text-decoration:none}
::selection{background:var(--ink);color:var(--bg)}
img{max-width:100%;display:block}

.shell{max-width:var(--maxw);margin:0 auto;padding:0 var(--gutter)}
@media (max-width:640px){:root{--gutter:22px}}

/* editorial eyebrow */
.eyebrow{
  font-family:var(--body);font-size:12px;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--sub);display:flex;align-items:center;gap:12px;
}
.eyebrow::before{content:"";width:26px;height:1px;background:var(--ink);opacity:.4}
@media (max-width:640px){.eyebrow::before,.contact .eyebrow::after{display:none}}

/* animated underline link */
.ul{position:relative;display:inline-block}
.ul::after{
  content:"";position:absolute;left:0;bottom:-2px;width:100%;height:1px;background:currentColor;
  transform:scaleX(0);transform-origin:left;transition:transform .5s var(--ease);
}
.ul:hover::after,.ul:focus-visible::after{transform:scaleX(1)}

a:focus-visible,button:focus-visible{outline:2px solid var(--ink);outline-offset:4px;border-radius:2px}

/* stat numbers that count up on scroll (see script.js): fixed-width digits
   so the badge doesn't jitter/reflow as the value climbs */
.ev .n,.fs .n,.oc .n,.pp,.cst-pill{font-variant-numeric:tabular-nums}

/* ============================================================
   NAV
============================================================ */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:100;
  transition:background .4s ease,box-shadow .4s ease;
}
/* the scrolled blur lives on ::before, not .nav itself — backdrop-filter on .nav
   would create a containing block for its position:fixed descendants (the mobile
   .nav-links drawer included), making them clip to .nav's own box instead of the
   viewport once scrolled. Isolating it to a pseudo-element avoids that entirely. */
.nav::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  transition:backdrop-filter .4s ease,-webkit-backdrop-filter .4s ease;
}
body:not(.reading) .nav.scrolled{
  background:rgba(250,250,248,.72);box-shadow:0 1px 0 var(--hair);
}
body:not(.reading) .nav.scrolled::before{
  backdrop-filter:blur(16px) saturate(1.2);-webkit-backdrop-filter:blur(16px) saturate(1.2);
}
.nav-inner{display:flex;align-items:center;justify-content:space-between;height:74px;max-width:var(--maxw);margin:0 auto;padding:0 var(--gutter)}
.nav-inner[hidden]{display:none}
.nav-back{display:inline-flex;align-items:center;gap:9px;font-size:14px;font-weight:500;color:var(--ink);transition:gap .3s var(--ease),color .3s ease}
.nav-back svg{flex:0 0 auto;transition:transform .3s var(--ease)}
.nav-back:hover{gap:12px;color:var(--accent)}
.nav-back:hover svg{transform:translateX(-2px)}

/* case-study view: no full header bar — just a floating "All work" pill,
   so the reader gets the full viewport height for the case study itself */
/* the bar spans the full viewport width but is visually empty between its two
   controls, so it must not swallow clicks meant for whatever is pinned beneath
   it. Only the controls themselves take pointer events. */
#navCaseInner{position:fixed;top:20px;left:0;right:0;height:auto;justify-content:flex-start;pointer-events:none}
#navCaseInner .nav-back,#navCaseInner .nav-toc{pointer-events:auto}
#navCaseInner .nav-back{
  background:rgba(250,250,248,.78);backdrop-filter:blur(16px) saturate(1.2);-webkit-backdrop-filter:blur(16px) saturate(1.2);
  border:1px solid var(--line);border-radius:100px;padding:11px 17px;box-shadow:0 8px 20px -14px rgba(17,17,17,.28);
}
#navCaseInner .nav-cta{display:none}

/* table of contents: same floating-pill language as the back button, parked on
   the opposite side. Opens on hover for pointers, on click for touch. */
.nav-toc{margin-left:auto;position:relative}
.nav-inner:not(#navCaseInner) .nav-toc{display:none}
.toc-btn{
  display:inline-flex;align-items:center;gap:9px;cursor:pointer;
  font-family:var(--body);font-size:14px;font-weight:500;color:var(--ink);
  background:rgba(250,250,248,.78);backdrop-filter:blur(16px) saturate(1.2);-webkit-backdrop-filter:blur(16px) saturate(1.2);
  border:1px solid var(--line);border-radius:100px;padding:11px 20px 11px 16px;
  box-shadow:0 8px 20px -14px rgba(17,17,17,.28);
  transition:color .3s ease,border-color .3s ease,box-shadow .3s var(--ease);
}
.toc-btn:hover,.toc-btn[aria-expanded="true"]{border-color:rgba(17,17,17,.28);box-shadow:0 10px 26px -14px rgba(17,17,17,.4)}
.toc-btn svg{flex:0 0 auto}
/* icon only until a section is under the reading line, then its name slides in.
   The label is still a flex item at zero width, so the gap has to go too or it
   reads as extra padding on the right */
.toc-btn{padding:11px 17px}
.toc-btn:not(.has-here){gap:0}
.toc-btn-here{
  max-width:0;opacity:0;overflow:hidden;white-space:nowrap;
  font-size:14px;font-weight:500;color:var(--ink);
  transition:max-width .45s var(--ease),opacity .3s ease,margin-left .45s var(--ease);
}
.toc-btn.has-here .toc-btn-here{max-width:230px;opacity:1}
@media (max-width:720px){.toc-btn.has-here .toc-btn-here{max-width:130px}}

/* the panel sits 10px below the button; without a bridge across that gap the
   pointer leaves .nav-toc on the way down and the panel closes before a click
   can land */
.nav-toc.open::after{content:"";position:absolute;top:100%;right:0;width:100%;height:12px}

.toc-panel{
  position:absolute;top:calc(100% + 10px);right:0;min-width:300px;max-width:340px;
  max-height:min(70vh,560px);overflow-y:auto;
  background:rgba(250,250,248,.94);backdrop-filter:blur(20px) saturate(1.2);-webkit-backdrop-filter:blur(20px) saturate(1.2);
  border:1px solid var(--line);border-radius:16px;padding:8px;
  box-shadow:0 26px 60px -30px rgba(17,17,17,.55);
  opacity:0;transform:translateY(-6px);pointer-events:none;
  transition:opacity .22s ease,transform .28s var(--ease);
}
.toc-panel[hidden]{display:none}
/* thin, minimal scrollbar matching the site's own muted-ink treatment,
   instead of the browser default */
.toc-panel{scrollbar-width:thin;scrollbar-color:rgba(17,17,17,.18) transparent}
.toc-panel::-webkit-scrollbar{width:6px}
.toc-panel::-webkit-scrollbar-track{background:transparent}
.toc-panel::-webkit-scrollbar-thumb{background:rgba(17,17,17,.18);border-radius:100px}
.toc-panel::-webkit-scrollbar-thumb:hover{background:rgba(17,17,17,.32)}
.nav-toc.open .toc-panel{opacity:1;transform:none;pointer-events:auto}
.toc-link{
  display:flex;gap:12px;align-items:baseline;padding:9px 12px;border-radius:10px;
  font-size:14px;line-height:1.35;color:var(--sub);cursor:pointer;
  transition:background .2s ease,color .2s ease;
}
.toc-link .toc-n{font-family:var(--plex);font-size:11px;color:var(--sub);flex:0 0 auto;padding-top:1px;width:18px;transition:color .2s ease}
.toc-link:hover{background:rgba(17,17,17,.05);color:var(--ink)}
/* current section: a quiet tint and a weight change, not a solid block.
   A filled row in a list of twelve reads as a button someone pressed
   rather than as "you are here" */
.toc-link.is-current{background:rgba(17,17,17,.055);color:var(--ink);font-weight:600}
.toc-link.is-current .toc-n{color:var(--ink)}
/* sub-entries: indented under their section, and quieter */
.toc-sub{padding-top:6px;padding-bottom:6px;font-size:13px}
.toc-sub > span:last-child{padding-left:2px;border-left:0}
.toc-sub .toc-n::before{content:"";display:block;width:9px;height:1px;background:var(--line);margin-top:8px;margin-left:5px}
.toc-sub.is-current .toc-n::before{background:var(--ink)}
@media (max-width:520px){.toc-panel{min-width:0;width:min(84vw,320px)}}
.nav-logo{font-family:var(--display);font-weight:700;font-size:17px;letter-spacing:-.01em}
.nav-links{display:flex;gap:34px;align-items:center}
.nav-links a{font-size:14px;color:var(--sub);transition:color .3s ease;position:relative;padding:4px 0}
.nav-links a:hover{color:var(--ink)}
.nav-links a.active{color:var(--ink)}
.nav-links a.active::after{content:"";position:absolute;left:0;right:0;bottom:-2px;height:1px;background:var(--ink)}
.nav-links a.nav-cta, #navCaseInner a.nav-cta{font-size:14px;border:1px solid var(--line);padding:10px 28px;border-radius:100px;color:var(--ink);transition:transform .3s var(--ease),box-shadow .3s var(--ease),background .3s ease,color .3s ease}
.nav-links a.nav-cta:hover, #navCaseInner a.nav-cta:hover{transform:translateY(-2px);box-shadow:0 8px 22px -12px rgba(17,17,17,.35);background:var(--ink);color:var(--bg)}
.nav-toggle{display:none;background:none;border:none;cursor:pointer;flex-direction:column;align-items:center;justify-content:center;gap:5px;padding:6px;min-width:44px;min-height:44px}
.nav-toggle span{width:22px;height:1.5px;background:var(--ink);transition:transform .3s var(--ease),opacity .3s ease}
@media (max-width:860px){
  .nav-links{position:fixed;inset:0 0 0 auto;width:min(78vw,340px);background:var(--bg);flex-direction:column;justify-content:center;align-items:flex-start;padding:0 40px;gap:26px;transform:translateX(100%);transition:transform .5s var(--ease);box-shadow:-20px 0 60px -30px rgba(0,0,0,.3)}
  .nav-links.open{transform:none}
  .nav-links a{font-size:22px;font-family:var(--display);font-weight:500}
  .nav-links a.nav-cta{font-size:18px;padding:14px 32px;margin-top:6px}
  .nav-toggle{display:flex;z-index:120}
  .nav-toggle.open span:nth-child(1){transform:translateY(6.5px) rotate(45deg)}
  .nav-toggle.open span:nth-child(2){opacity:0}
  .nav-toggle.open span:nth-child(3){transform:translateY(-6.5px) rotate(-45deg)}
}

/* ============================================================
   HERO + WORD FIELD
============================================================ */
.hero{position:relative;min-height:100vh;min-height:100svh;display:flex;flex-direction:column;overflow:hidden}
.wordfield{position:absolute;inset:0;z-index:0;pointer-events:none}
.wordfield .w{
  position:absolute;white-space:nowrap;font-family:var(--display);font-weight:500;
  font-size:13px;letter-spacing:.08em;text-transform:uppercase;color:var(--word);
  user-select:none;pointer-events:none;will-change:transform;
}
/* soft vignette keeps hero copy crisp over the texture */
.hero::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:radial-gradient(115% 85% at 50% 46%, rgba(250,250,248,.75) 0%, rgba(250,250,248,0) 60%)}
.hero-inner{position:relative;z-index:2;flex:1 1 auto;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;max-width:var(--maxw);margin:0 auto;padding:110px var(--gutter) 40px;width:100%}
.hero-tag{margin-bottom:34px;justify-content:center}
.hero-tag::after{content:"";width:26px;height:1px;background:var(--ink);opacity:.4}
@media (max-width:640px){.hero-tag::after{display:none}}
.hero h1{
  font-family:var(--display);font-weight:700;line-height:1.03;letter-spacing:-.03em;
  font-size:clamp(40px,7vw,92px);margin:0;max-width:16ch;
}
.hero-lede{max-width:44ch;margin-top:38px;font-size:clamp(17px,2vw,21px);line-height:1.55;color:var(--sub)}
.hero-lede b{color:var(--ink);font-weight:500}
.hero-actions{margin-top:46px;display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap}
.btn{
  display:inline-flex;align-items:center;gap:10px;font-size:15px;font-weight:500;
  padding:15px 26px;border-radius:100px;background:var(--ink);color:var(--bg);
  transition:transform .35s var(--ease),box-shadow .35s var(--ease);
}
.btn:hover{transform:translateY(-3px) scale(1.015);box-shadow:0 16px 34px -16px rgba(17,17,17,.5)}
.btn .arrow{transition:transform .35s var(--ease)}
.btn:hover .arrow{transform:translateY(2px)}
.hero-scroll{font-size:14px;color:var(--sub)}
/* corner meta */
.hero-meta{position:relative;z-index:2;flex:0 0 auto;max-width:var(--maxw);width:100%;margin:0 auto;padding:0 var(--gutter) 32px;display:flex;justify-content:space-between;gap:20px;font-size:12.5px;letter-spacing:.04em;color:var(--sub);text-transform:uppercase}
@media (max-width:640px){.hero-meta{display:none}.hero::after{background:radial-gradient(150% 100% at 50% 44%,rgba(250,250,248,.92) 0%,rgba(250,250,248,.3) 58%,rgba(250,250,248,0) 100%)}}

/* ============================================================
   SECTION SCAFFOLD
============================================================ */
.section{padding:130px 0}
#view-home .section{padding:104px 0}
.section-head{display:flex;flex-direction:column;align-items:flex-start;gap:16px;margin-bottom:60px;scroll-margin-top:106px}
.section-head h2{font-family:var(--display);font-weight:700;font-size:clamp(28px,4vw,54px);letter-spacing:-.03em;line-height:1;white-space:nowrap}
.section-index{order:-1;font-family:var(--body);font-size:12px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--sub);white-space:nowrap;display:inline-flex;align-items:center;gap:12px}
.section-index::before{content:"";width:26px;height:1px;background:var(--ink);opacity:.4}
@media (max-width:640px){.section-index::before{display:none}}
@media (max-width:640px){.section{padding:88px 0}#view-home .section{padding:70px 0}.section-head{margin-bottom:44px;gap:14px}.section-head h2{white-space:normal;font-size:clamp(26px,7.5vw,36px)}}

/* ============================================================
   SELECTED WORK
============================================================ */
.work-grid{display:grid;grid-template-columns:1fr 1fr;gap:56px 44px}
@media (max-width:820px){.work-grid{grid-template-columns:1fr;gap:52px}}
.project{cursor:pointer;display:block}
.project-media{position:relative;overflow:hidden;border-radius:14px;aspect-ratio:4/3;background:#EEEDE8;
  transition:transform .6s var(--ease),box-shadow .6s var(--ease)}
@media (max-width:640px){.project-media{border:1px solid var(--line)}}
.project-media .ph{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  background:linear-gradient(135deg,#F1F0EB 0%,#E7E6E0 100%);
  background-image:radial-gradient(circle,rgba(17,17,17,.05) 1px,transparent 1px);background-size:16px 16px;
  transition:transform .7s var(--ease)}
.project-media .ph .ph-in{background:rgba(255,255,255,.8);border:1px solid var(--line);border-radius:10px;padding:12px 16px;font-size:12.5px;color:var(--sub);letter-spacing:.04em;text-align:center}
.project-media .ph img{width:100%;height:100%;object-fit:cover;display:block}
/* the helix cover is 3.5:1 on a 4:3 card — cover would crop it down to a
   sliver of the middle family alone, so this one letterboxes instead, on a
   white ground matching the image's own background */
.project-media .ph.ph-contain{background:#FFFFFF}
.project-media .ph.ph-contain img{object-fit:contain}
.project-media .idx{position:absolute;top:18px;left:18px;font-family:var(--display);font-weight:700;font-size:14px;color:var(--sub);z-index:2}
.project-media .metric{position:absolute;top:16px;right:16px;z-index:2;font-size:12px;font-weight:500;letter-spacing:.02em;background:var(--ink);color:var(--bg);padding:6px 12px;border-radius:100px}
.project-media .ptag{position:absolute;top:16px;left:16px;z-index:2;font-family:var(--body);font-size:11.5px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--ink);background:rgba(255,255,255,.78);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid var(--line);border-radius:100px;padding:7px 14px}
.project:hover .project-media{transform:translateY(-8px);box-shadow:0 40px 60px -34px rgba(17,17,17,.4)}
.project:hover .project-media .ph{transform:scale(1.05)}
.project--soon{cursor:default}
.project--soon .project-media .ph img{filter:grayscale(80%);opacity:.82}
.project--soon:hover .project-media{transform:none;box-shadow:none}
.project--soon:hover .project-media .ph{transform:none}
.project-body{padding-top:26px}
.project-body h3{font-family:var(--display);font-weight:600;font-size:clamp(21px,2.5vw,28px);letter-spacing:-.02em;line-height:1.14;margin-bottom:12px;transition:color .3s ease}
.project-desc{color:var(--sub);font-size:16px;line-height:1.55;margin-bottom:22px}
.project-pills{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:14px}
.project-pills .pp{font-family:var(--display);font-weight:600;font-size:12.5px;letter-spacing:-.005em;color:var(--bg);background:var(--ink);border-radius:100px;padding:8px 16px;white-space:nowrap}
.project-pills--dummy .pp{color:var(--sub);background:transparent;border:1px dashed var(--line);font-weight:500}
.project-foot{display:flex;justify-content:space-between;align-items:center;gap:16px;padding-top:18px;border-top:1px solid var(--hair)}
.project-foot .roles{display:flex;gap:28px}
.project-foot .roles > div{flex:0 0 auto}
.project-foot .roles > div:first-child{width:172px}
.project-foot .roles > div:last-child{width:60px}
.project-foot .roles div .k{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--sub);margin-bottom:5px}
.project-foot .roles div .v{font-size:14px;font-weight:500}
.project-foot .go{font-size:14px;font-weight:500;display:inline-flex;align-items:center;gap:7px;color:var(--ink)}
.project-foot .go .arrow{transition:transform .35s var(--ease)}
.project:hover .project-foot .go .arrow{transform:translate(4px,0)}
@media (max-width:520px){.project-foot{flex-direction:column;align-items:flex-start}.project-foot .roles{gap:22px}}

/* ============================================================
   ABOUT
============================================================ */
.about-grid{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.6fr);gap:60px}
/* solved so the rendered column width itself drops 25% (fr is relative to
   the sum of both columns, so shrinking .9 by a flat 25% only shrank the
   render ~17.5% — .592fr is what actually yields a 25% narrower column at
   a fixed 1.6fr partner). gap stays 60px untouched either way. */
@media (min-width:821px){.about-grid{grid-template-columns:minmax(0,.592fr) minmax(0,1.6fr)}}
@media (max-width:820px){.about-grid{grid-template-columns:1fr;gap:34px}}
.about-left .k{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--sub);margin-bottom:14px}
.about-left .portrait{aspect-ratio:1/1;border-radius:14px;background:linear-gradient(135deg,#F1F0EB,#E7E6E0);display:flex;align-items:center;justify-content:center;color:var(--sub);font-size:12.5px;letter-spacing:.04em;border:1px solid var(--hair);width:100%;height:auto;object-fit:cover;filter:grayscale(1)}
.about-right p{font-family:var(--display);font-weight:500;font-size:clamp(21px,2.7vw,32px);line-height:1.32;letter-spacing:-.02em}
.about-right p+p{margin-top:24px}
.about-right .small{font-family:var(--body);font-weight:400;font-size:17px;line-height:1.6;color:var(--sub);letter-spacing:0;margin-top:30px;max-width:56ch}

/* the "I write" line types itself in on scroll, then leaves a cursor blinking,
   since it's the one place on the page actually claiming to write something */
/* the "I write" line's font and typing motion, no sheet/card around it —
   it just sits on the page's own ground like the rest of the section */
.doc-sheet{max-width:620px}
.doc-page{font-family:Arial,Helvetica,sans-serif;font-size:16.67px;line-height:1.5;color:var(--ink);
  white-space:pre-wrap;word-break:break-word;min-height:6em}
.doc-caret{display:inline-block;width:1.6px;height:1.05em;background:var(--ink);vertical-align:-.19em;margin:0 -.8px;animation:docBlink 1.06s step-end infinite}
.doc-page.is-busy .doc-caret{animation:none}
@keyframes docBlink{0%,49%{opacity:1}50%,100%{opacity:0}}

/* ============================================================
   SKILLS
============================================================ */
.skills-wrap{display:flex;flex-wrap:wrap;gap:12px}
.skills-note{font-size:16px;color:var(--sub);margin-bottom:32px;max-width:52ch}

.tools{margin-top:56px;padding-top:40px;border-top:1px solid var(--hair)}
.tools-label{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--sub);margin-bottom:22px}
.tools-row{display:flex;flex-wrap:wrap;gap:14px}
.tool{
  display:flex;align-items:center;gap:11px;padding:12px 18px 12px 14px;
  border:1px solid var(--line);border-radius:100px;background:var(--card);
  color:var(--sub);transition:transform .3s var(--ease),color .3s ease,border-color .3s ease,box-shadow .3s var(--ease);
}
.tool:hover{transform:translateY(-3px);color:var(--ink);border-color:var(--ink);box-shadow:0 14px 26px -16px rgba(17,17,17,.28)}
.tool-icon{width:22px;height:22px;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.tool-icon svg{width:100%;height:100%}
.tool-icon svg path{fill:currentColor}
.tool-icon--text{
  font-family:var(--display);font-weight:700;font-size:13px;color:currentColor;
  width:22px;height:22px;border-radius:50%;border:1.4px solid currentColor;
  display:flex;align-items:center;justify-content:center;line-height:1;
}
.tool-name{font-size:14.5px;font-weight:500;white-space:nowrap}
@media (max-width:520px){.tool{padding:10px 15px 10px 12px}.tool-name{font-size:13.5px}}
.pill{font-size:15px;padding:12px 22px;border:1px solid var(--line);border-radius:100px;color:var(--ink);
  transition:transform .3s var(--ease),background .3s ease,color .3s ease,border-color .3s ease}
.pill:hover{transform:translateY(-3px);background:var(--ink);color:var(--bg);border-color:var(--ink)}

/* ============================================================
   EXPERIENCE
============================================================ */
.timeline{border-top:1px solid var(--line)}
.tl-row{display:grid;grid-template-columns:.4fr 1.6fr .5fr;gap:30px;padding:34px 0;border-bottom:1px solid var(--hair);align-items:baseline;transition:padding-left .4s var(--ease)}
.tl-row:hover{padding-left:12px}
.tl-year{font-size:14px;color:var(--sub);letter-spacing:.04em}
.tl-role h3{font-family:var(--display);font-weight:600;font-size:clamp(19px,2.2vw,24px);letter-spacing:-.02em;margin-bottom:8px}
.tl-role p{font-size:15px;color:var(--sub);line-height:1.5;max-width:52ch}
.tl-co{font-size:14px;color:var(--sub);text-align:right}
@media (max-width:720px){.tl-row{grid-template-columns:1fr;gap:8px}.tl-co{text-align:left}}

/* ============================================================
   CONTACT
============================================================ */
.contact{padding:150px 0 90px;text-align:center}
.contact .shell{display:flex;flex-direction:column;align-items:center}
.contact .eyebrow{margin-bottom:40px;justify-content:center}
.contact .eyebrow::after{content:"";width:26px;height:1px;background:var(--ink);opacity:.4}
.contact-head{font-family:var(--display);font-weight:700;letter-spacing:-.04em;line-height:.95;
  font-size:clamp(40px,8.5vw,104px);margin-bottom:30px}
.contact-mail{font-family:var(--display);font-weight:500;letter-spacing:-.02em;line-height:1.1;
  font-size:clamp(22px,4.6vw,44px);display:inline-block;max-width:100%;overflow-wrap:anywhere}
.contact-sub{margin-top:40px;font-size:18px;color:var(--sub);max-width:48ch;line-height:1.6}
.contact-links{margin-top:46px;display:flex;gap:16px;flex-wrap:wrap;align-items:center;justify-content:center}
.contact-links .btn.ghost{background:transparent;color:var(--ink);border:1px solid var(--line)}
.contact-links .btn.ghost:hover{background:var(--ink);color:var(--bg)}

footer{border-top:1px solid var(--hair);padding:30px 0}
.footer-inner{display:flex;justify-content:center;flex-wrap:wrap;gap:14px;font-size:13px;color:var(--sub);letter-spacing:.03em}

/* ============================================================
   SCROLL REVEAL
============================================================ */
.reveal{opacity:0;transform:translateY(26px);transition:opacity .9s var(--ease),transform .9s var(--ease)}
.reveal.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
  .reveal{opacity:1!important;transform:none!important;transition:none!important}
  html{scroll-behavior:auto}
}

/* ================= CASE STUDY STYLES (scoped to .view-case) ================= */

/* ============================================================
   TOKENS: identical to the home page
============================================================ */
.view-case{
  --bg:#FAFAF8;--ink:#111111;--sub:#6A6A66;
  --line:rgba(17,17,17,.10);--hair:rgba(17,17,17,.07);--word:rgba(17,17,17,.055);
  --card:#FFFFFF;--accent:#9B948A;
  --display:"Satoshi",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --body:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --maxw:1180px;--gutter:40px;--read:70ch;
  --ease:cubic-bezier(.22,1,.36,1);
  /* the Denefits system's own typeface, used only inside exhibit content —
     never for portfolio chrome (headings/labels stay on --display/--body) */
  --nunito:"Nunito Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --plex:"IBM Plex Mono","SF Mono",Consolas,monospace;
}
/* the floating "All work" pill lives in <header>, outside .view-case, so it
   can't see .view-case's --maxw override above — without this it centers
   against the site-wide 1280px instead of the case study's 1180px and drifts
   out of alignment with the .shell content beneath it on wide viewports */
body.reading{--maxw:1180px}
*{margin:0;padding:0;box-sizing:border-box}
/* Chrome's scroll anchoring watches for content changes above the viewport
   and "helpfully" corrects scroll position on its own — which fights the
   explicit scrollTo(0,0) in showView() and is the actual cause of the
   scroll-below-then-jump-to-top flash when a case study opens. Disabled
   outright rather than raced against. */
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;scroll-padding-top:92px;overflow-anchor:none;scrollbar-width:none;-ms-overflow-style:none}
/* the page still scrolls, the bar just isn't painted — the reading-progress
   line under the header already reports position, so the native bar is
   redundant chrome. Same pattern the phone mockups already use. */
html::-webkit-scrollbar,body::-webkit-scrollbar{width:0;height:0;display:none}
body{background:var(--bg);color:var(--ink);font-family:var(--body);line-height:1.5;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;overflow-x:hidden}
a{color:inherit;text-decoration:none}
::selection{background:var(--ink);color:var(--bg)}
img{max-width:100%;display:block}
.shell{max-width:var(--maxw);margin:0 auto;padding:0 var(--gutter)}
.prose{max-width:var(--read)}
@media (max-width:640px){.view-case{--gutter:22px}}

.eyebrow{font-family:var(--body);font-size:12px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--sub);display:flex;align-items:center;gap:12px}
.eyebrow::before{content:"";width:26px;height:1px;background:var(--ink);opacity:.4}
.ul{position:relative;display:inline-block}
.ul::after{content:"";position:absolute;left:0;bottom:-2px;width:100%;height:1px;background:currentColor;transform:scaleX(0);transform-origin:left;transition:transform .5s var(--ease)}
.ul:hover::after,.ul:focus-visible::after{transform:scaleX(1)}
a:focus-visible,button:focus-visible{outline:2px solid var(--ink);outline-offset:4px;border-radius:2px}

/* reading progress */
.progress{position:fixed;top:0;left:0;height:2px;width:0;background:var(--ink);z-index:120;transition:width .1s linear}

/* the moment between clicking a case study and the hero landing: a plain field
   with one line sweeping once, nothing else. Opacity-toggled rather than
   removed, so it's always ready and never causes a layout jump. Duration here
   (1.3s) is the "NORMAL" pace and is matched exactly by CASE_LOADER_MS in
   script.js, so the JS-driven view swap lands right as the sweep finishes. */
.case-loader{position:fixed;inset:0;z-index:200;background:var(--bg);display:flex;align-items:center;justify-content:center;
  opacity:0;visibility:hidden;transition:opacity .18s ease;pointer-events:none}
.case-loader.is-active{opacity:1;visibility:visible;pointer-events:auto}
.case-loader-sweep{width:320px;height:2px;background:var(--hair);position:relative;overflow:hidden}
.case-loader-sweep::after{content:"";position:absolute;inset:0;background:var(--ink)}
.case-loader.is-active .case-loader-sweep::after{animation:caseSweep 1.3s cubic-bezier(.65,0,.35,1) 1}
@keyframes caseSweep{
  0%{transform:translateX(-100%) scaleX(.4)}
  50%{transform:translateX(0) scaleX(.7)}
  100%{transform:translateX(100%) scaleX(.4)}
}
@media (prefers-reduced-motion: reduce){
  .case-loader.is-active .case-loader-sweep::after{animation:none;transform:translateX(0) scaleX(.35)}
}

/* ============================================================
   NAV: same language as home (shared with home; see single .nav rules above)
============================================================ */
@media (max-width:520px){.nav-right{gap:16px}.nav-right a.back span{display:none}}

/* ============================================================
   CASE HERO (with word-field texture, left-aligned editorial)
============================================================ */
.chero{position:relative;min-height:60vh;display:flex;align-items:center;overflow:hidden;padding:104px 0 72px}
.wordfield{position:absolute;inset:0;z-index:0;pointer-events:none}
.wordfield .w{position:absolute;white-space:nowrap;font-family:var(--display);font-weight:500;font-size:13px;letter-spacing:.08em;text-transform:uppercase;color:var(--word);user-select:none;pointer-events:none;will-change:transform}
.chero::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;background:radial-gradient(120% 90% at 26% 44%,rgba(250,250,248,.82) 0%,rgba(250,250,248,0) 60%)}
.chero-inner{position:relative;z-index:2;width:100%;display:flex;gap:56px;align-items:center;justify-content:space-between;flex-wrap:wrap}
.chero-text{flex:1 1 520px;min-width:0}
.chero .eyebrow{margin-bottom:28px}
/* max-width is in ch, which scales with the element's own font-size: dropping
   the desktop cap from 84 to 64px shrinks the box unless ch grows to match,
   so 19.7ch here reproduces the same pixel width the old 15ch/84px pair gave */
.chero h1{font-family:var(--display);font-weight:700;line-height:1.02;letter-spacing:-.035em;font-size:clamp(38px,5vw,64px);max-width:19.7ch}
.chero-dek{margin-top:28px;max-width:58ch;font-size:clamp(17px,2vw,21px);line-height:1.55;color:var(--sub)}
.cmeta{width:100%;margin-top:46px;display:flex;flex-wrap:wrap;gap:26px 46px;padding-top:26px;border-top:1px solid var(--line)}
.cmeta .k{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--sub);margin-bottom:7px}
.cmeta .v{font-size:15px;font-weight:500;max-width:26ch;line-height:1.4}
@media (min-width:1000px){
  .cmeta{display:grid;grid-template-columns:repeat(4,1fr);gap:26px 32px}
  .cmeta .v{max-width:none}
}
@media (max-width:640px){
  .cmeta{gap:22px 26px}
  .cmeta .v{max-width:none}
}

/* the case study's star metrics, standing in for the old text eyebrow */
.chero-stats{display:flex;flex-direction:row;flex-wrap:wrap;gap:10px;margin-bottom:28px}
.chero-stats .cst-pill{display:inline-flex;align-items:center;font-family:var(--display);font-weight:600;font-size:14px;letter-spacing:-.005em;color:var(--ink);background:#fff;border:1px solid var(--line);border-radius:100px;padding:10px 20px;white-space:nowrap;box-shadow:0 16px 28px -20px rgba(17,17,17,.16)}

/* ============================================================
   COVER + DEVICE FRAMES
============================================================ */
.cover{padding-top:24px}
.cover-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,402px));justify-content:center;gap:24px}
.device{position:relative;background:#141414;border-radius:54px;padding:11px;box-shadow:0 34px 60px -34px rgba(17,17,17,.4);max-width:402px;margin:0 auto;width:100%}
.device .screen{position:relative;border-radius:44px;overflow:hidden;background-color:#EFEEEA;background-image:radial-gradient(circle,rgba(17,17,17,.06) 1px,transparent 1px);background-size:15px 15px;aspect-ratio:1206/2622;display:flex;align-items:center;justify-content:center}
.device .screen img{width:100%;display:block}

/* interactive device: real, scrollable screen content with a fixed nav overlay */
.device--live .screen{display:block;background-color:#fff;background-image:none}
.screen-scroll{position:absolute;inset:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;scrollbar-width:none}
.screen-scroll::-webkit-scrollbar{display:none}
.screen-scroll img{width:100%;height:auto;display:block}
.screen-scroll img.img-overlap{margin-top:-1%;position:relative}
.screen-header-spacer{width:100%}
.screen-navbar-spacer{width:100%;aspect-ratio:375/81}
/* content below the header spacer shifts up via transform when the header
   hides, instead of the spacer changing height — resizing the spacer moved
   the real scrollHeight around mid-scroll, which fought the browser's own
   momentum/scroll-position math and produced a visible shake/stutter. A
   transform never touches scroll geometry, so there's nothing left to fight */
.screen-content{transition:transform .28s var(--ease)}
.screen-header{position:absolute;left:0;right:0;top:0;z-index:4;pointer-events:none}
.screen-header img{width:100%;height:auto;display:block}
.screen-header .chrome-top--scrolled{position:absolute;inset:0;opacity:0;transition:opacity .3s ease}
.screen-header.is-scrolled .chrome-top--scrolled{opacity:1}
.screen-navbar{position:absolute;left:0;right:0;bottom:0;z-index:4;pointer-events:none}
.screen-navbar img{width:100%;height:auto;display:block}

/* in-device tabs: two full panels (own header/filtration/list/navbar), toggled by
   invisible buttons overlaid on the header image's own tab control */
.tab-panel{position:absolute;inset:0}
.tab-panel[hidden]{display:none}
.tab-hit{position:absolute;background:transparent;border:0;padding:0;margin:0;cursor:pointer;pointer-events:auto;-webkit-tap-highlight-color:transparent}
.tab-hit--left{left:4%;width:44%}
.tab-hit--right{right:4%;width:44%}
.screen-header .tab-hit{top:74%;height:19%}
.ph-subnav .tab-hit{top:15.6%;height:31.2%}

/* staged header: back arrow/overdue badge + title/description, one combined
   image that hides on scroll-down and reappears on scroll-up as one overlay
   (screen-header--staged, same mechanism as any other auto-hide header). The
   subnav (switcher + filter chips, one combined image) is NOT part of that
   overlay — it's normal-flow content whose position is recomputed via JS on
   every scroll (initPaymentHeaderCascade in script.js) so it always stays
   pinned at the top, sliding to sit flush against the header whether the
   header is shown or hidden. */
.ph-subnav{position:relative;z-index:2;background:#fff;transition:transform .28s var(--ease)}
.ph-subnav img{width:100%;height:auto;display:block}

/* auto-hide chrome: header/navbar slide out on scroll-down, snap back on scroll-up */
.device--autohide .screen-header,
.device--autohide .screen-navbar{transition:transform .28s var(--ease)}
.device--autohide .screen-header.is-hidden{transform:translateY(-100%)}
.device--autohide .screen-navbar.is-hidden{transform:translateY(100%)}

.chip{background:#fff;border:1px solid var(--line);border-radius:12px;padding:13px 15px;max-width:82%;text-align:center}
.chip .t{font-size:12.5px;color:var(--ink);font-weight:500}
.chip .d{font-size:11.5px;color:var(--sub);margin-top:5px;line-height:1.4}
.chip .drop{font-size:10.5px;color:var(--accent);margin-top:8px;letter-spacing:.03em}

/* ============================================================
   SECTIONS
============================================================ */
.view-case .section{padding-bottom:65px}
.view-case .section.divide{padding-top:65px}
@media (max-width:640px){.view-case .section{padding-bottom:44px}.view-case .section.divide{padding-top:44px}}

/* unified section header — same visual system as the home page */
.cs-head{display:flex;flex-direction:column;align-items:flex-start;gap:16px;margin-bottom:44px}
.cs-head h2{font-family:var(--display);font-weight:700;font-size:clamp(26px,3.8vw,44px);letter-spacing:-.025em;line-height:1.05;margin:0;white-space:nowrap}
@media (max-width:640px){.cs-head h2{white-space:normal}}
.cs-index{order:-1;font-family:var(--body);font-size:12px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--sub);white-space:nowrap;display:inline-flex;align-items:center;gap:12px}
.cs-index::before{content:"";width:26px;height:1px;background:var(--ink);opacity:.4}
@media (max-width:640px){.cs-index::before{display:none}}
@media (max-width:640px){.cs-head{gap:14px;margin-bottom:36px}}
/* a section whose content carries its own heading: the index still shows, so
   the numbering does not skip, but it sits closer to what follows */
.cs-head--indexonly{margin-bottom:26px}

.view-case .section p{font-size:17.5px;line-height:1.68;color:#333;margin-bottom:18px}
.view-case .section p:last-child{margin-bottom:0}
.view-case .section p b{font-weight:600;color:var(--ink)}
.q{font-style:italic;color:#45454a}
/* Lendee: quoted product copy is already marked by its quote marks, so italic
   double-marks it. Weight + ink carry the distinction instead. */
#view-case .q{font-style:normal;color:var(--ink);font-weight:500}
/* the quoted line inside the dark "New Capability" box inherited the light-card
   .q color (near-black), which is invisible against .feature's own near-black
   background — same text color as its background, effectively erased */
#view-case .feature .q{color:rgba(255,255,255,.92)}
.lead{font-size:clamp(18px,2vw,20px);line-height:1.6;color:#333;margin-bottom:8px}

/* at a glance */
.glance-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
@media (max-width:720px){.glance-grid{grid-template-columns:1fr}}
.gc{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:28px}
.gc .n{font-family:var(--display);font-weight:700;font-size:clamp(30px,3.4vw,40px);letter-spacing:-.03em;line-height:1}
.gc .c{font-size:14px;color:var(--sub);margin-top:14px;line-height:1.45}

/* evidence stats */
.evidence{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:24px;margin:32px 0 40px}
@media (max-width:720px){.evidence{grid-template-columns:1fr;gap:26px}}
.ev{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:24px 26px}
.ev .n{font-family:var(--display);font-weight:700;font-size:clamp(28px,3.2vw,38px);letter-spacing:-.03em;line-height:1}
.ev .c{font-size:13.5px;color:var(--sub);margin-top:12px;line-height:1.45}

/* the two surfaces each side of the product ships on */
.surface-row{display:flex;gap:8px;flex-wrap:wrap;padding-top:12px;border-top:1px solid var(--hair)}
.surface-row span{font-size:11.5px;font-weight:500;color:var(--sub);background:var(--bg);border:1px solid var(--hair);border-radius:100px;padding:5px 12px}

/* the brief, in the PM's words */
.pullquote{margin:34px 0;padding:28px 32px;background:var(--ink);border-radius:16px}
#view-case-design-system .section .pullquote p{font-family:var(--display);font-weight:600;font-size:clamp(17px,2.1vw,22px);line-height:1.42;letter-spacing:-.015em;color:var(--bg);margin:0;max-width:none}
.pullquote-a{display:block;margin-top:16px;font-size:12px;letter-spacing:.06em;color:rgba(255,255,255,.5)}
@media (max-width:640px){.pullquote{padding:22px 22px}}

/* the two fixed inputs */
.constraints{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media (max-width:720px){.constraints{grid-template-columns:1fr}}
.constraint{display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:center;background:var(--card);border:1px solid var(--line);border-radius:14px;padding:20px 24px}
.constraint-n{font-family:var(--plex);font-size:11px;color:var(--accent)}
.constraint h5{font-family:var(--display);font-weight:600;font-size:16px;letter-spacing:-.01em;line-height:1.35;margin:0}

/* the requirement: a highlighted callout, not just another paragraph */
.requirement{margin-top:22px;padding:20px 26px;border-left:3px solid var(--ink);background:var(--card);border-radius:0 12px 12px 0;display:inline-block}
.view-case .section .requirement p{margin:0;font-size:18px;font-weight:600;color:var(--ink);line-height:1.4;max-width:none}

/* the same ink-filled, tag-topped block used for the design system case
   study's "Key result" card, reused here so both projects reach for the same
   device when something outranks the paragraph around it */
.requirement.requirement--key{border-left:0;border-radius:14px;background:var(--ink);padding:26px 30px;display:block}
.requirement.requirement--key .oc-key-tag{margin-bottom:12px}
.view-case .section .requirement--key p{color:var(--bg)}

/* problem / discovery — two-column split for first-time vs returning */
.dual-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;margin:28px 0}
@media (max-width:720px){.dual-grid{grid-template-columns:1fr}}
.dual-card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:26px 28px}
.dual-tag{display:inline-block;font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--bg);background:var(--ink);padding:5px 12px;border-radius:100px;margin-bottom:16px}
.dual-card h4{font-family:var(--display);font-weight:600;font-size:17px;letter-spacing:-.01em;line-height:1.35;margin-bottom:18px}
.dual-card .ev{border-top:1px solid var(--ink);padding-top:16px}
.findings{margin:0 0 16px;padding:0;list-style:none}
.findings li{position:relative;padding-left:18px;font-size:14.5px;line-height:1.6;color:#333;margin-bottom:10px}
/* this note is a deliberately de-emphasized aside ("byproduct, not the goal"),
   so its bullets stay muted rather than reading heavier than the framing text
   around them */
.craft-note .findings li{color:var(--sub)}
/* bumped from the original muted treatment — read as too washed out at 15px/--sub */
.view-case .section .craft-note.craft-note--strip p{font-size:16.5px;color:var(--ink)}
.craft-note.craft-note--strip .findings li{font-size:15.5px;color:var(--ink)}
.findings li:last-child{margin-bottom:0}
.findings li::before{content:"";position:absolute;left:0;top:9px;width:5px;height:5px;border-radius:50%;background:var(--sub)}
.finding-conclusion{font-size:14px;line-height:1.55;color:var(--sub);margin:0;padding-top:14px;border-top:1px solid var(--line)}
.finding-conclusion b{color:var(--ink);font-weight:600}
.finding-conclusion + .ev{margin-top:20px}

/* shared whole-app stats, separate from the per-home findings above them */
.pair-evidence{display:grid;grid-template-columns:repeat(2,1fr);gap:24px;margin:20px 0 0}
@media (max-width:720px){.pair-evidence{grid-template-columns:1fr;gap:26px}}

/* principles */
.principles{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-top:8px}
@media (max-width:860px){.principles{grid-template-columns:1fr}}
.pr{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:26px}
.pr .pn{font-family:var(--display);font-weight:700;font-size:14px;color:var(--accent)}
.pr h3{font-family:var(--display);font-weight:600;font-size:19px;margin:12px 0 10px;letter-spacing:-.01em}
.pr p{font-size:14.5px;color:var(--sub);line-height:1.55;margin:0;max-width:none}

/* Each act is now its own numbered section with a standard cs-head, so the
   act label chip, its sticky slide and the act-scoped heading styles are
   gone. `.act` itself stays: it is the scope the phone-scroll sync resolves
   a decision card's paired devices against (script.js `closest('.act')`). */
.act{margin-top:0}
.decisions{margin-top:36px}

/* decisions */
.decision{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:26px 28px;margin:0 0 16px;transition:transform .22s var(--ease),box-shadow .22s var(--ease)}
@media (hover:hover) and (pointer:fine){
  .decision{cursor:pointer}
  .decision:hover{transform:translateY(-4px);box-shadow:0 16px 30px -18px rgba(17,17,17,.25)}
}
.decision:last-child{margin-bottom:0}
.decision h4{font-family:var(--display);font-weight:600;font-size:18px;margin-bottom:10px;line-height:1.35}
.decision .alt{font-size:13.5px;color:var(--sub);margin-bottom:12px}
.decision .alt b{color:var(--ink);font-weight:600}
.view-case .section .decision p{font-size:16.5px;line-height:1.62;margin:0;color:#333;max-width:none}
.proof{display:inline-block;margin-top:14px;font-size:12.5px;font-weight:500;color:var(--ink);background:#EFEEEA;border:1px solid var(--line);border-radius:100px;padding:6px 14px}

/* explored & rejected: a direction that didn't survive testing, shown as its
   own moment once the shipped before/after has already made its case */
.tag.explored{display:inline-block;background:#EDECE8;color:var(--sub);border:1px solid var(--line)}
.detour{margin-top:20px}
.detour-head h3{font-family:var(--display);font-weight:600;font-size:clamp(20px,2.4vw,26px);margin:14px 0 0;letter-spacing:-.01em;line-height:1.25;max-width:34ch}
.detour-body{display:grid;grid-template-columns:auto 1fr;gap:32px;align-items:start;margin-top:26px}
.detour-img{width:220px;height:auto;border-radius:14px;border:1px solid var(--line);filter:grayscale(35%);opacity:.92;flex-shrink:0}
.view-case .section .detour-text p{font-size:16.5px;line-height:1.62;color:#333;margin:0;max-width:none}
.detour-text .detour-label{margin-top:22px;margin-bottom:10px}
.detour-text .findings{max-width:none}
.detour-text .proof{margin-top:6px}
@media (max-width:720px){.detour-body{grid-template-columns:1fr}.detour-img{width:200px}}

/* before/after */
.compare{margin:44px 0 0}
.frames{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,402px));justify-content:center;gap:24px}
.fw .cap{display:flex;align-items:center;gap:9px;margin-bottom:12px}
.tag{font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:4px 11px;border-radius:100px;font-weight:500}
.tag.before{background:#EDECE8;color:var(--sub);border:1px solid var(--line)}
.tag.after{background:var(--ink);color:var(--bg)}
.fw .capt{font-size:12.5px;color:var(--sub)}
.changes-label{margin-top:40px;margin-bottom:10px;display:inline-block;font-size:11px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--ink)}
.view-case .section p.panel-note{display:block;margin:0 0 20px;font-size:12px;color:var(--sub);line-height:1.6}
.changes{margin-top:0}
.change{display:grid;grid-template-columns:26px 1fr;gap:16px;padding:18px 0;border-bottom:1px solid var(--hair)}
.change:last-child{border-bottom:0}
.change .num{font-family:var(--display);font-weight:700;font-size:14px;color:var(--accent);padding-top:2px}
.change .ct{font-family:var(--display);font-weight:600;font-size:16.5px;margin-bottom:6px;line-height:1.3}
.change .cd{font-size:14.5px;color:var(--sub);line-height:1.55}

/* wide screens: before/after sits beside the decisions, pinned in place while
   you scroll through them. .split is its own container (decisions + compare
   only) so .compare's sticky range is bounded to it and can never visually
   bleed into .feature — a separate, always-full-width section that only
   starts once .split has fully scrolled past, same as it looks on mobile */
@media (min-width:1000px){
  .split{display:grid;grid-template-columns:minmax(0,35fr) minmax(0,65fr);column-gap:56px;align-items:start}
  .decisions-top{grid-column:1;grid-row:1}
  .decisions-bottom{grid-column:1;grid-row:2}
  .compare{grid-column:2;grid-row:1 / span 2;margin:36px 0 0;position:sticky;top:10px;align-self:start}
  .compare .frames{grid-template-columns:1fr 1fr}
}
.change .cd b{color:var(--ink);font-weight:600}

/* feature (dark panel) */
.feature{background:var(--ink);color:var(--bg);border-radius:18px;padding:40px;margin:44px 0 0;display:grid;grid-template-columns:1.3fr 1fr;gap:36px;align-items:center}
.feature .lab{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.55)}
.feature h3{font-family:var(--display);font-weight:600;font-size:clamp(22px,2.6vw,28px);margin:14px 0 14px;letter-spacing:-.01em;line-height:1.15}
.view-case .section .feature p{font-size:16.5px;color:rgba(255,255,255,.9);line-height:1.7;margin:0;max-width:46ch}
.feature-stats{display:flex;flex-direction:column;gap:20px;margin-top:24px}
.feature-stats .fs{border-top:1px solid rgba(255,255,255,.2);padding-top:14px}
.feature-stats .fs .n{font-family:var(--display);font-weight:700;font-size:clamp(22px,2.4vw,28px);letter-spacing:-.02em;color:#fff;line-height:1}
.feature-stats .fs .c{font-size:12.5px;color:rgba(255,255,255,.62);margin-top:7px;line-height:1.4}
@media (max-width:760px){.feature-stats{flex-wrap:wrap;gap:22px}}
.feature .device{box-shadow:none;background:#B9BABE}
.feature .device .screen{background-color:#fff;background-image:none}
.feature .chip{background:#1c1c20;border-color:rgba(255,255,255,.12)}
.feature .chip .t{color:#eee}
.feature .chip .d{color:rgba(255,255,255,.5)}
@media (max-width:760px){.feature{grid-template-columns:1fr;gap:24px;padding:28px}.feature .device{max-width:270px}}

/* outcome */
.headline-metric{margin:28px 0 36px}
.headline-metric .hm-n{font-family:var(--display);font-weight:700;font-size:clamp(30px,4vw,46px);letter-spacing:-.03em;line-height:1;color:var(--bg)}
.headline-metric .hm-c{font-size:15px;color:rgba(250,250,248,.72);margin-top:14px;line-height:1.5}
.hm-lead{font-family:var(--display);font-weight:600;font-size:clamp(17px,1.9vw,20px);letter-spacing:-.01em;line-height:1.35;margin-bottom:26px}
.hm-split{display:grid;grid-template-columns:1fr 1fr;gap:28px;margin-top:0}
.hm-half{background:var(--ink);border-radius:14px;padding:24px 22px}
.hm-half-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}
.hm-title{font-family:var(--display);font-weight:600;font-size:15px;letter-spacing:-.01em;color:var(--bg)}
/* inverted pill: the black-on-white .dual-tag would disappear on the now-black card */
.hm-half .dual-tag{background:rgba(255,255,255,.18);color:var(--bg)}
.hm-half .dual-tag{margin-bottom:0;flex-shrink:0}
@media (max-width:640px){.hm-split{grid-template-columns:1fr;gap:26px}}
.outcome-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:18px}
/* seven outcomes, all the same size: no card is the headline, so none gets
   the bigger hero treatment the two-metric version used to reserve for the
   first two numbers */
.outcome-grid--wide{grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
@media (max-width:720px){.outcome-grid{grid-template-columns:1fr}}
.oc{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:24px}
/* the label leads now, not the number: what the outcome IS, read before what it
   measures. The number becomes a supporting readout, not the headline. */
.oc-t{font-family:var(--display);font-weight:600;font-size:15px;letter-spacing:-.005em;color:var(--ink);line-height:1.3}
.oc .n{font-family:var(--plex);font-weight:600;font-size:15px;letter-spacing:-.005em;color:var(--sub);
  margin-top:8px;font-variant-numeric:tabular-nums}
.oc .c{font-size:13px;color:var(--sub);margin-top:10px;line-height:1.5}

/* the one outcome that outranks the other six: this is the actual thesis of
   the project (a scalable, adjustable system), not just another metric, so it
   sits in its own block between the lead paragraph and the grid, rather than
   competing with six other cards for attention inside it */
.oc.oc--key{background:var(--ink);border-color:var(--ink);padding:28px 30px}
.oc.oc--key.oc--standalone{margin:28px 0 32px;max-width:none}
.oc.oc--key .oc-t{color:var(--bg);font-size:17px}
.oc.oc--key .n{color:rgba(250,250,248,.55)}
.oc.oc--key .c{color:rgba(250,250,248,.82);font-size:14px;max-width:none}
.oc-key-tag{display:inline-block;font-family:var(--body);font-size:10.5px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--ink);background:var(--bg);border-radius:100px;padding:5px 12px;margin-bottom:14px}
.oc:not(:has(.oc-t)) .n{font-family:var(--display);font-weight:700;font-size:clamp(24px,2.8vw,32px);letter-spacing:-.03em;line-height:1;color:var(--ink);margin-top:0}
.oc:not(:has(.oc-t)) .c{margin-top:12px}
.note{margin-top:24px;font-size:13px;color:var(--sub)}

/* craft note (closing aside on section 04) */
.craft-note{margin-top:56px;padding:22px 28px;border-left:3px solid var(--ink);background:var(--card);border-radius:0 12px 12px 0}
.view-case .section .craft-note p{font-size:15px;font-weight:500;color:var(--sub);line-height:1.65;margin:0;max-width:none}

/* a white band that runs edge to edge while its text stays on the page's own
   margins. The box stays exactly where it is inside .shell — so the text needs
   no realignment — and only the paint escapes, via a very wide box-shadow.
   clip-path trims that shadow vertically so it can't bleed into neighbours.
   Deliberately not 100vw + negative margins: that measures the scrollbar into
   the width and leaves the page a few pixels wider than the viewport. */
.craft-note.craft-note--strip{
  border-left:0;border-radius:0;background:#FFFFFF;padding:38px 0;margin:64px 0 0;
  box-shadow:0 0 0 100vmax #FFFFFF;
  clip-path:inset(0 -100vmax);
}

/* case nav */
.casenav{padding-top:90px;padding-bottom:90px}
.casenav-inner{border-top:1px solid var(--line);display:flex;padding-top:34px}
.casenav a .t{font-family:var(--display);font-weight:600;font-size:clamp(18px,2.2vw,24px);letter-spacing:-.01em}
.next-case{display:grid;grid-template-columns:220px 1fr auto;gap:32px;align-items:center;width:100%}
.next-case .project-media{aspect-ratio:4/3}
.next-case:hover .project-media{transform:translateY(-8px);box-shadow:0 40px 60px -34px rgba(17,17,17,.4)}
.next-case:hover .project-media .ph{transform:scale(1.05)}
.next-case-cta{font-size:14px;font-weight:500;display:inline-flex;align-items:center;gap:7px;color:var(--ink);white-space:nowrap}
.next-case-cta .arrow{transition:transform .35s var(--ease)}
.next-case:hover .next-case-cta .arrow{transform:translate(4px,0)}
@media (max-width:640px){
  .next-case{grid-template-columns:1fr;gap:18px}
}

/* buttons */
.btn{display:inline-flex;align-items:center;gap:10px;font-size:15px;font-weight:500;padding:14px 24px;border-radius:100px;background:var(--ink);color:var(--bg);transition:transform .35s var(--ease),box-shadow .35s var(--ease)}
.btn:hover{transform:translateY(-3px) scale(1.015);box-shadow:0 16px 34px -16px rgba(17,17,17,.5)}
.btn .arrow{transition:transform .35s var(--ease)}
.btn:hover .arrow{transform:translate(3px,0)}

/* footer */
footer{border-top:1px solid var(--hair);padding:30px 0}

/* reveal */
.reveal{opacity:0;transform:translateY(24px);transition:opacity .9s var(--ease),transform .9s var(--ease)}
.reveal.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.reveal{opacity:1!important;transform:none!important;transition:none!important}html{scroll-behavior:auto}}

/* ============================================================
   INTERACTIVE EXHIBITS (Denefits design-system case study)
   The exhibit frame (card, spacing, labels) stays on the portfolio's
   own tokens (--card/--line/--display/--body). Only the content
   rendered *inside* an exhibit (swatches, type, buttons) is the real
   Denefits system, driven by --nunito and the palette data in script.js.
============================================================ */
.exhibit{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:30px 30px 34px;margin:32px 0}
/* room under the title before the exhibit's own content. Where a standfirst
   follows it stays tight, since the standfirst carries its own spacing */
.exhibit-head{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin-bottom:22px;flex-wrap:wrap}
.exhibit-head:has(+ .exhibit-sub){margin-bottom:8px}
.exhibit-head h4{font-family:var(--display);font-weight:700;font-size:17px;margin:0;letter-spacing:-.005em}
.exhibit-tag{font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--sub)}
.exhibit-sub{font-size:13.5px;color:var(--sub);line-height:1.55;max-width:60ch;margin:0 0 22px}

/* colour ramps */
.fam-row{display:grid;grid-template-columns:76px 1fr;align-items:center;gap:14px;margin-bottom:9px}
.fam-row:last-child{margin-bottom:0}
.fam-label{font-family:var(--display);font-weight:700;font-size:12.5px;color:var(--ink)}
.ramp{display:grid;gap:4px}
.ramp-11{grid-template-columns:repeat(11,1fr)}
.ramp-13{grid-template-columns:repeat(13,1fr)}
.swatch{position:relative;aspect-ratio:1;border-radius:6px;cursor:pointer;transition:transform .18s var(--ease);border:1px solid rgba(0,0,0,.06)}
.swatch:hover{transform:scale(1.14);z-index:2;box-shadow:0 8px 20px -10px rgba(17,17,17,.4)}
/* .tone's colour is set per-swatch in script.js from that swatch's own luminance */
.swatch .tone{position:absolute;bottom:3px;left:4px;font-size:8px;font-weight:700;font-family:var(--body)}
.contrast-badge{position:absolute;top:3px;right:3px;font-size:7.5px;font-weight:700;background:rgba(255,255,255,.92);color:#111;padding:1.5px 4px;border-radius:20px;font-family:var(--body)}
.exhibit-tooltip{position:fixed;pointer-events:none;background:var(--ink);color:var(--bg);font-size:11.5px;font-family:var(--body);padding:6px 10px;border-radius:7px;transform:translate(-50%,-130%);opacity:0;transition:opacity .12s ease;z-index:200;white-space:nowrap}
.exhibit-tooltip.show{opacity:1}
.variance-readout{display:flex;gap:26px;margin-top:20px;padding-top:20px;border-top:1px solid var(--hair);flex-wrap:wrap}
.vr .n{font-family:var(--display);font-weight:700;font-size:21px;letter-spacing:-.02em}
.vr .c{font-size:11px;color:var(--sub);margin-top:2px}

/* light/dark + font toggles (shared look) */
.pill-toggle{display:inline-flex;border:1px solid var(--line);border-radius:100px;padding:3px;gap:2px;margin-bottom:20px}
.pill-toggle button{border:0;background:transparent;font-family:var(--body);font-size:12.5px;font-weight:600;padding:7px 16px;border-radius:100px;cursor:pointer;color:var(--sub);transition:background .2s ease,color .2s ease}
.pill-toggle button.active{background:var(--ink);color:var(--bg)}

/* semantic token preview card — the "real" Denefits card inside our frame */
.sem-card{border-radius:14px;padding:24px;transition:background .3s ease,color .3s ease,border-color .3s ease;border:1px solid;max-width:340px}
.sem-card .sem-eyebrow{font-family:var(--nunito);font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;opacity:.65;margin-bottom:10px}
.sem-card .sem-amount{font-family:var(--nunito);font-weight:800;font-size:34px;letter-spacing:-.02em;margin-bottom:2px;font-variant-numeric:tabular-nums}
.sem-card .sem-status{display:inline-flex;align-items:center;gap:6px;font-family:var(--nunito);font-size:12.5px;font-weight:700;padding:5px 12px;border-radius:100px;margin-top:12px}
.sem-card .sem-row{display:flex;justify-content:space-between;align-items:center;margin-top:18px;padding-top:16px;border-top:1px solid;border-color:inherit}
.sem-card .sem-row span{font-family:var(--nunito);font-size:13px;font-weight:600}
.sem-card .sem-btn{font-family:var(--nunito);font-weight:700;font-size:13.5px;padding:10px 18px;border-radius:8px;border:0;cursor:default}

/* typography specimen */
.type-row{display:grid;grid-template-columns:96px 1fr 76px;align-items:baseline;gap:16px;padding:13px 0;border-bottom:1px solid var(--hair)}
.type-row:last-child{border-bottom:0}
.type-role{font-family:var(--body);font-size:11px;font-weight:600;color:var(--sub);text-transform:uppercase;letter-spacing:.06em}
.type-sample{font-family:var(--nunito);color:var(--ink)}
.type-meta{font-family:var(--body);font-size:10.5px;color:var(--sub);text-align:right}
.digit-demo{margin-top:26px;padding-top:24px;border-top:1px solid var(--hair)}
.money-col{font-size:24px;font-weight:700;line-height:1.65;color:var(--ink);font-variant-numeric:tabular-nums}
.money-col.font-nunito{font-family:var(--nunito)}
.money-col.font-inter{font-family:"Inter",-apple-system,sans-serif;font-variant-numeric:normal}
.money-col div{display:flex;justify-content:space-between;max-width:220px}
.digit-note{font-size:12.5px;color:var(--sub);margin-top:12px;max-width:52ch;line-height:1.55}

/* button component demo: real Action/* tokens rendered as actual buttons,
   built by renderDsBtn() in script.js rather than one class per combination */
.dsbtn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:var(--nunito);font-weight:700;white-space:nowrap;border-radius:10px;border:1.5px solid transparent;cursor:default;box-sizing:border-box}
.dsbtn svg{flex-shrink:0}
.dsbtn-block{margin-top:6px}
.dsbtn-row{display:grid;grid-template-columns:repeat(3,1fr);gap:36px}
.dsbtn-item{display:flex;flex-direction:column;gap:10px;align-items:flex-start}
.dsbtn-item-label{font-family:var(--display);font-weight:700;font-size:13px;color:var(--ink)}
.dsbtn-item-caption{font-size:11.5px;color:var(--sub);max-width:24ch;line-height:1.4}

.dsbtn-colorgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:20px}
.dsbtn-colorgroup{display:flex;flex-direction:column;gap:14px;align-items:flex-start;padding:18px;border-radius:12px;background:var(--bg);border:1px solid var(--hair)}
.dsbtn-colorgroup--panel{border:none}
.dsbtn-stack{display:flex;flex-direction:column;gap:8px;align-items:flex-start;width:100%}

.dsbtn-statewrap{overflow-x:auto;margin:-4px;padding:4px}
.dsbtn-stategrid{display:grid;grid-template-columns:repeat(5,minmax(94px,1fr));align-items:start;gap:16px 10px;min-width:560px}
.dsbtn-statecell{display:flex;flex-direction:column;gap:10px;align-items:flex-start}
.dsbtn-state-head{font-size:10px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--sub)}

.dsbtn-sizegrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:24px}
.dsbtn-sizecol{display:flex;flex-direction:column;gap:10px;align-items:flex-start}
.dsbtn-sizerow{display:flex;align-items:center;gap:10px}

/* one axis per row, full width: each card carries a live demo plus a
   paragraph of reasoning, and two of those side by side halves the room
   the demos need to be read at their real size */
.axis-cards{display:grid;grid-template-columns:1fr;gap:24px}
.axis-cards .exhibit{margin:0}
/* the demo carries the whole card, so it needs air under the head rather than
   sitting tight against it and reading as part of the title block */
.axis-cards .exhibit-head{margin-bottom:30px}
@media (max-width:640px){
  .dsbtn-row{grid-template-columns:1fr;gap:24px}
}

/* ---- button simulator ----------------------------------------------------
   The component's own properties panel, rebuilt. The chrome is this site's:
   the same cards, dropdowns and pill toggle every other exhibit uses. The
   button inside the stage is not — its geometry and colour come from the
   measured tables in script.js, so nothing here restyles it. ---- */
/* controls on top, preview under them, readouts at the foot: the same order the
   reader works in, and it keeps every row full width instead of squeezing a
   five-across properties bar into a side column */
.bsim{display:block}

.bsim-controls{border:1px solid var(--hair);border-radius:12px;background:var(--bg);padding:14px 16px 16px;margin-bottom:14px}
.bsim-row{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px;align-items:start}
/* the second row is everything past the divider in the Figma panel */
.bsim-row--second{margin-top:13px;padding-top:14px;border-top:1px solid var(--hair)}

/* the stage is the whole preview: a thin outline and the button, nothing else */
.bsim-stage{display:grid;place-items:center;padding:54px 24px;min-height:240px;
  border:1px solid var(--hair);border-radius:12px;transition:background .3s ease}
/* laid out across, so the label sits over its control rather than beside it */
.bsim-field{display:flex;flex-direction:column;gap:7px;min-width:0}
.bsim-field > label,.bsim-sw-label{font-size:10px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--sub)}
/* a property whose control still applies but has nothing to act on yet: dimmed,
   so flicking its toggle back does not move the row around */
.bsim-field.is-inert{opacity:.34;pointer-events:none}
/* a property this variant cannot reach at all: taken off the panel entirely */
.bsim-field.is-off{display:none}

.bsim-input{width:100%;font-family:var(--body);font-size:12.5px;font-weight:600;color:var(--ink);background:var(--card);
  border:1px solid var(--line);border-radius:9px;padding:8px 11px;transition:border-color .15s ease}
.bsim-input:hover{border-color:var(--ink)}
.bsim-input:focus-visible{outline:2px solid var(--ink);outline-offset:2px}

/* the switch sits on the same baseline as the controls beside it, so it gets
   the height of a trigger and parks its track at the top of that box */
.bsim-switch{align-self:flex-start;margin-top:8px;width:34px;height:20px;border-radius:100px;border:1px solid var(--line);background:var(--card);
  position:relative;cursor:pointer;padding:0;transition:background .18s ease,border-color .18s ease}
.bsim-switch::after{content:"";position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:var(--sub);
  transition:transform .18s var(--ease),background .18s ease}
.bsim-switch[aria-checked="true"]{background:var(--ink);border-color:var(--ink)}
.bsim-switch[aria-checked="true"]::after{transform:translateX(14px);background:var(--bg)}
.bsim-switch:focus-visible{outline:2px solid var(--ink);outline-offset:2px}

.bsim-hint{font-size:11px;line-height:1.5;color:var(--sub);margin:13px 0 0;max-width:78ch}
.bsim-hint:empty{display:none}

/* the button: box only. every value arrives inline from the measured tables */
.bsim-btn{display:inline-flex;align-items:center;justify-content:center;position:relative;
  font-family:var(--nunito);font-weight:500;border-style:solid;white-space:nowrap;cursor:default;
  transition:background .12s ease,border-color .12s ease,color .12s ease}
.bsim-btn svg{display:block;flex:none}
.bsim-ring{position:absolute;border-style:solid;pointer-events:none}

/* the site's dropdown, one size down to sit in a properties panel */
.bsim-field .ddn{--ddn-fs:12.5px}
.bsim-field .ddn-trigger{min-height:0;padding:8px 11px;border-radius:9px;font-weight:600;background:var(--card)}
.bsim-field .ddn-menu{border-radius:10px}
.bsim-field .ddn-opt{padding:8px 10px}

/* five across needs the room; below that the row wraps rather than crushing
   every control to an unusable width */
@media (max-width:900px){
  .bsim-row{grid-template-columns:repeat(3,minmax(0,1fr));gap:15px 12px}
}
@media (max-width:640px){
  .bsim-row{grid-template-columns:repeat(2,minmax(0,1fr))}
  .bsim-controls{padding:13px 13px 15px}
  .bsim-stage{padding:40px 16px;min-height:180px}
}

/* semantic layer: one group per row, its tokens laid out across it */
.sem-scope{position:relative}
/* align-items must not be the flex default here: with stretch, the pill's
   height is driven by this row instead of its own content and the labels
   spill outside their background */
.sem-sticky{position:sticky;top:20px;z-index:60;display:flex;justify-content:flex-end;align-items:flex-start;pointer-events:none;margin-bottom:14px}
.sem-sticky .pill-toggle{pointer-events:auto}
/* the switch lives inside the exhibit it governs: zero-height so it overlays the
   head rather than pushing it down, and sticky across the whole box */
.sem-sticky--inset{height:0;margin-bottom:0}

/* sub-section heading: a heavy rule with the foundation named above the title.
   Editorial rather than a pill, so it reads as a level below the numbered
   section head without competing with it */
/* all the spacing lives on the heading itself: a generous gap above to break
   from the previous block, a tight one below so it binds to what it titles */
.subhead{margin:62px 0 14px}
@media (max-width:640px){.subhead{margin:46px 0 12px}}
.subhead-t{font-family:var(--display);font-weight:700;font-size:clamp(20px,2.3vw,27px);letter-spacing:-.022em;line-height:1.1;margin:0}

/* colour semantics: name · square swatch · what it is for.
   Double class so this wins over .ramp-row, which is defined further down */
/* four real columns: name · swatch · the primitive it aliases · what it is for.
   Every row is its own grid container, so max-content would size the alias
   column per row and the use-for column would start at a different x on each
   line. A fixed track is what keeps the table a table. */
.ramp-row.sem-trow{grid-template-columns:170px 36px 148px minmax(0,1.4fr);align-items:center;column-gap:24px}
.sem-trow .sem-name{display:block;margin-top:0;font-size:13px;font-weight:600}
.sem-trow .sem-chip{width:36px;height:36px;border-radius:7px;margin:0}
.sem-trow .sem-use{display:block;margin-top:0;font-size:12.5px;line-height:1.45}
.sem-hex{font-family:var(--plex);font-size:10.5px;color:var(--sub);font-variant-numeric:tabular-nums;transition:color .3s ease;white-space:nowrap}
.sem-groups.is-dark .sem-hex{color:#8C8F92}
@media (max-width:860px){
  .ramp-row.sem-trow{grid-template-columns:130px 34px minmax(0,1fr);column-gap:14px;row-gap:4px}
  .sem-trow .sem-use{grid-column:1 / -1;margin-top:2px}
}
.applied-block{margin-top:34px;padding-top:28px;border-top:1px solid var(--line)}
.pill-toggle--float{background:rgba(250,250,248,.82);backdrop-filter:blur(16px) saturate(1.2);-webkit-backdrop-filter:blur(16px) saturate(1.2);box-shadow:0 8px 20px -14px rgba(17,17,17,.4);margin-bottom:0}
/* pinned under the floating Contents button: step clear of it. Short and linear
   on purpose, an eased slide reads as an animation rather than a nudge */
.sem-sticky--inset > .pill-toggle{transform:translateX(0);transition:transform .18s ease-out}
.sem-sticky--inset > .pill-toggle.is-shifted{transform:translateX(calc(-1 * var(--toc-shift, 160px)))}
@media (max-width:640px){.sem-sticky{justify-content:center}}

.sem-groups{display:block}
.sem-group{background:var(--bg);border:1px solid var(--hair);border-radius:12px;padding:18px 18px 16px;margin-bottom:12px;transition:background .3s ease,border-color .3s ease}
.sem-group:last-child{margin-bottom:0}
.sem-groups.is-dark .sem-group{background:#1b1d1f;border-color:rgba(255,255,255,.09)}
.sem-group-t{font-family:var(--display);font-weight:700;font-size:14px;letter-spacing:-.005em;margin-bottom:14px;display:flex;gap:10px;align-items:baseline;transition:color .3s ease}
.sem-group-t span{font-family:var(--body);font-weight:500;font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--sub)}
.sem-groups.is-dark .sem-group-t{color:#F4F5F6}
.sem-row{display:grid;grid-template-columns:repeat(auto-fill,minmax(146px,1fr));gap:16px 12px}
.sem-tok{display:block;min-width:0}
.sem-chip{display:block;width:100%;height:32px;border-radius:7px;border:1px solid rgba(17,17,17,.12);transition:background .3s ease,border-color .3s ease}
.sem-groups.is-dark .sem-chip{border-color:rgba(255,255,255,.16)}
.sem-name{display:block;font-size:12.5px;font-weight:600;color:var(--ink);margin-top:8px;line-height:1.25;transition:color .3s ease}
.sem-groups.is-dark .sem-name{color:#F4F5F6}
.sem-use{display:block;font-size:11px;color:var(--sub);line-height:1.4;margin-top:3px;transition:color .3s ease}
.sem-groups.is-dark .sem-use{color:#8C8F92}

/* the applied card: a real screen built only from the tokens above */
.sem-stage{margin-top:16px;border-radius:14px;padding:26px;transition:background .3s ease;display:flex;justify-content:center}
.stage-card{width:100%;max-width:420px;border-radius:14px;padding:20px;transition:background .3s ease,border-color .3s ease,box-shadow .3s ease;font-family:var(--nunito)}
.stage-head{display:flex;justify-content:space-between;align-items:flex-start;gap:14px}
.stage-eyebrow{font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;margin-bottom:5px}
.stage-amount{font-size:30px;font-weight:800;letter-spacing:-.02em;font-variant-numeric:tabular-nums;line-height:1.05}
.stage-sub{font-size:12.5px;font-weight:600;margin-top:4px}
.stage-chip{font-size:10.5px;font-weight:700;padding:4px 10px;border-radius:100px;white-space:nowrap;flex:0 0 auto}
.stage-rows{margin-top:18px}
.stage-row{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:10px 0}
.stage-row-l{font-size:13px;font-weight:700}
.stage-row-d{font-size:11.5px;font-weight:600;margin-top:2px;font-variant-numeric:tabular-nums}
.stage-actions{display:flex;gap:9px;margin-top:16px}
.stage-btn{font-family:var(--nunito);font-weight:700;font-size:13px;padding:10px 16px;border-radius:8px;border:0;cursor:default;flex:1}

/* the contract matrix, recomputed live */
.cm-table{font-size:12.5px;overflow-x:auto}
.cm-row{display:grid;grid-template-columns:minmax(140px,1.5fr) repeat(5,minmax(58px,1fr)) 44px 48px 62px;gap:7px;align-items:center;padding:5px 0;min-width:660px}
.cm-head{font-size:10.5px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--sub);padding-bottom:9px;border-bottom:1px solid var(--hair);margin-bottom:5px}
.cm-head span{text-align:center}
.cm-head span:first-child{text-align:left}
.cm-label{color:var(--sub);font-size:12px}
.cm-cell{text-align:center;padding:9px 4px;border-radius:7px;font-weight:700;font-variant-numeric:tabular-nums;border:1px solid rgba(17,17,17,.07)}
.cm-min,.cm-var{text-align:center;color:var(--sub);font-variant-numeric:tabular-nums}
.cm-result{text-align:center;font-size:10.5px;font-weight:700;letter-spacing:.06em;padding:6px 0;border-radius:100px;background:#EDF9EF;color:#00853B}
.cm-result.is-fail{background:#FFF2F0;color:#CB443D}

/* typography: the two families */
.fam-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media (max-width:760px){.fam-grid{grid-template-columns:1fr}}
.fam-card{background:var(--bg);border:1px solid var(--hair);border-radius:12px;padding:20px}
.fam-tok{font-family:var(--plex);font-size:11px;color:var(--sub);margin-bottom:6px}
.fam-name{font-family:var(--nunito);font-size:14px;font-weight:600;color:var(--ink);margin-bottom:16px}
.fam-spec{font-family:var(--nunito);font-size:30px;font-weight:700;letter-spacing:-.02em;color:var(--ink);line-height:1.15}
.fam-spec--mono{font-family:var(--plex);font-weight:600;letter-spacing:-.01em}
.fam-digits{font-family:var(--nunito);font-size:17px;font-weight:500;color:var(--ink);margin-top:12px;font-variant-numeric:tabular-nums}
.fam-digits--mono{font-family:var(--plex);font-size:15px}
.fam-note{font-size:12.5px;color:var(--sub);line-height:1.5;margin-top:16px;max-width:none}

/* tabbed primitive ramps */
.tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:18px}
.tab{font-family:var(--body);font-size:13px;font-weight:500;color:var(--sub);background:var(--bg);border:1px solid var(--line);border-radius:100px;padding:8px 15px;cursor:pointer;display:inline-flex;align-items:center;gap:7px;transition:background .2s ease,color .2s ease,border-color .2s ease}
.tab span{font-size:10.5px;font-weight:600;color:var(--sub);background:var(--card);border-radius:100px;padding:1px 7px;transition:background .2s ease,color .2s ease}
.tab:hover{color:var(--ink);border-color:var(--ink)}
.tab.active{background:var(--ink);color:var(--bg);border-color:var(--ink)}
.tab.active span{background:rgba(255,255,255,.18);color:var(--bg)}
.ramp-note{font-size:13px!important;color:var(--sub)!important;line-height:1.55!important;margin:0 0 18px!important;max-width:none!important}
.ramp-rows{display:flex;flex-direction:column}
.ramp-row{display:grid;grid-template-columns:132px minmax(0,1fr) minmax(0,210px);gap:20px;align-items:center;padding:11px 0;border-bottom:1px solid var(--hair)}
.ramp-row:last-child{border-bottom:0}
.ramp-tok{font-family:var(--plex);font-size:11.5px;color:var(--sub)}
.ramp-render{color:var(--ink);min-width:0;overflow:hidden}
.ramp-meta{font-size:11.5px;color:var(--sub);line-height:1.4}
.lh-box{display:block;width:170px;background:#EEF6FF;border:1px solid #75B3FC;border-radius:4px}
@media (max-width:760px){.ramp-row{grid-template-columns:1fr;gap:6px;padding:14px 0}.ramp-meta{font-size:11px}}

/* the ten roles, per breakpoint */
.type-scope{position:relative}
.role-row{padding:18px 0;border-bottom:1px solid var(--hair)}
.role-row:last-child{border-bottom:0}
.role-head{display:flex;justify-content:space-between;align-items:baseline;gap:14px;margin-bottom:9px}
.role-name{font-family:var(--display);font-weight:700;font-size:13px;color:var(--ink);display:inline-flex;align-items:center;gap:8px}
.role-flag{font-family:var(--body);font-size:9.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:#0061B4;background:#EEF6FF;border-radius:100px;padding:2px 8px}
.role-spec{font-family:var(--plex);font-size:11.5px;color:var(--sub);white-space:nowrap;font-variant-numeric:tabular-nums}
.role-sample{color:var(--ink);margin-bottom:8px;overflow-wrap:anywhere;transition:font-size .3s var(--ease),letter-spacing .3s var(--ease),line-height .3s var(--ease)}
.role-use{font-size:12.5px;color:var(--sub);line-height:1.5;max-width:72ch}
.role-row.is-changed .role-spec{color:#0061B4;font-weight:600}

.bp-summary{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:22px;padding-top:20px;border-top:1px solid var(--line)}
@media (max-width:760px){.bp-summary{grid-template-columns:1fr}}
.bps{background:var(--bg);border:1px solid var(--hair);border-radius:11px;padding:16px 18px}
.bps--changed{border-color:#BCDBFF;background:#F7FBFF}
.bps-t{font-family:var(--display);font-weight:700;font-size:13px;color:var(--ink);margin-bottom:7px}
.bps p{font-size:12.5px!important;color:var(--sub)!important;line-height:1.55!important;margin:0!important;max-width:none!important}
.bps p b{color:var(--ink);font-weight:600}

/* space & size ramps and semantics */
.space-scope{position:relative}
.sp-row{grid-template-columns:132px minmax(72px,1fr) 74px 52px minmax(0,1.5fr)}
/* the ramp table drops the consumer column: four columns, not five */
.ramp-row.sp-row--ramp{grid-template-columns:132px minmax(72px,1fr) 74px 60px}
.sp-group-t{font-family:var(--display);font-weight:700;font-size:14px;color:var(--ink);margin-bottom:14px}
.sp-bar{display:block;height:15px;background:#0073D4;border-radius:4px;min-width:4px}
.sp-square{display:block;background:#0073D4;border-radius:5px}
.sp-val{font-family:var(--plex);font-size:12px;color:var(--ink);font-variant-numeric:tabular-nums;display:flex;flex-direction:column;gap:3px}
.sp-ratio{font-family:var(--plex);font-size:11.5px;color:var(--sub);font-variant-numeric:tabular-nums}
.sp-pair{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}
.sp-pair b{font-weight:500;color:var(--sub);transition:color .2s ease,font-weight .2s ease}
.sp-pair b.on{color:#0061B4;font-weight:700}
.sp-pair i{font-style:normal;color:var(--sub);font-size:10px}
.sp-row .ramp-render{display:flex;align-items:center;min-height:20px}
@media (max-width:860px){
  .sp-row{grid-template-columns:110px minmax(60px,1fr) 66px;gap:12px}
  .sp-row .sp-ratio{display:none}
  .sp-row .ramp-meta{grid-column:1 / -1;margin-top:2px}
}

/* curated vs generated */
.cvg{display:grid;grid-template-columns:auto 1fr;gap:28px;align-items:start}
@media (max-width:720px){.cvg{grid-template-columns:1fr;gap:22px}}
.cvg-t{font-family:var(--display);font-weight:700;font-size:13px;margin-bottom:10px;display:flex;gap:8px;align-items:baseline}
.cvg-t span{font-family:var(--body);font-weight:500;font-size:11px;color:var(--sub)}
.cvg-grid{display:grid;grid-template-columns:repeat(6,10px);gap:4px}
.cvg-grid--dense{grid-template-columns:repeat(20,10px)}
@media (max-width:720px){.cvg-grid--dense{grid-template-columns:repeat(14,10px)}}
.cvg-cell{width:10px;height:10px;border-radius:2.5px;background:var(--line)}
.cvg-cell.is-fail{background:#CB443D}
.cvg-c{font-size:12.5px;color:var(--sub);line-height:1.55;margin-top:11px;max-width:40ch}
.cvg-c b{color:var(--ink);font-weight:600}

/* focus-ring near miss */
.focus-demo{display:flex;align-items:center;gap:28px;flex-wrap:wrap}
.focus-demo-btn{font-family:var(--nunito);font-weight:700;font-size:14px;padding:12px 22px;border-radius:8px;border:none;cursor:default;position:relative}
.focus-demo-btn::after{content:"";position:absolute;inset:-4px;border-radius:11px;pointer-events:none}
.focus-demo-note{font-size:13px;color:var(--sub);line-height:1.6;max-width:34ch}
.focus-demo-note b{color:var(--ink)}

@media (max-width:720px){
  .fam-row{grid-template-columns:1fr}
  .type-row{grid-template-columns:1fr;gap:3px}
  .type-meta{text-align:left}
  .sem-row{grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:14px 10px}
  .sem-stage{padding:16px}
}

/* refusals: what got built and then deleted */
.refuse-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:18px;margin-top:8px}
/* three cards read better as one row than as 2+1 with an empty gap on the left */
.refuse-grid--3{grid-template-columns:repeat(3,1fr)}
@media (max-width:900px){.refuse-grid--3{grid-template-columns:1fr}}
@media (max-width:720px){.refuse-grid{grid-template-columns:1fr}}
.refuse{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:24px}
.refuse h4{font-family:var(--display);font-weight:600;font-size:16px;margin-bottom:8px;letter-spacing:-.005em;line-height:1.3}
.refuse p{font-size:10.5px;color:var(--sub);line-height:1.5;margin:0 0 10px;max-width:none}
.refuse .cost{font-size:12.5px;color:var(--ink);font-weight:500;padding-top:10px;border-top:1px solid var(--hair)}
.refuse .cost b{font-weight:600}

/* ---- test screens: three throwaway compositions, shown as phones ----------
   The disclaimer is a block rather than a footnote on purpose: these read as
   product design at a glance, and they are not. ---- */
.tnote{background:var(--card);border:1px solid var(--line);border-left:3px solid var(--ink);
  border-radius:0 12px 12px 0;padding:16px 20px;margin:26px 0 30px;
  font-size:13.5px;line-height:1.6;color:var(--sub)}
.tnote a{color:var(--ink);text-decoration:underline;text-underline-offset:2px}

.tscreens{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:26px;align-items:start}
.tscreen{margin:0;min-width:0}
.tscreen img{width:100%;height:auto;border:1px solid var(--line);border-radius:14px;background:var(--card)}
.tscreen figcaption{display:flex;flex-direction:column;gap:5px;margin-top:14px}
.tscreen figcaption b{font-family:var(--display);font-weight:600;font-size:15px;letter-spacing:-.005em}
.tscreen figcaption span{font-size:12.5px;color:var(--sub);line-height:1.5}
@media (max-width:820px){
  .tscreens{grid-template-columns:1fr;gap:34px;max-width:400px;margin:0 auto}
  .tnote{max-width:none}
}

/* the documentation's own six parts, named and nothing else: this section
   argues that documentation matters, so its one exhibit is the table of
   contents itself rather than a description of it */
.doc-parts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0 28px;margin-top:18px;
  border-top:1px solid var(--hair)}
.doc-part{display:flex;align-items:baseline;gap:12px;padding:15px 0;border-bottom:1px solid var(--hair)}
.doc-part-n{font-family:var(--plex);font-size:12px;color:var(--sub);width:16px;flex:0 0 auto}
.doc-part-t{font-family:var(--display);font-weight:600;font-size:14.5px;letter-spacing:-.005em;color:var(--ink)}
@media (max-width:700px){.doc-parts{grid-template-columns:1fr}}

/* the closing manifest: what else the system contains, named and counted. One
   line each on purpose, because this section exists to show scope rather than
   to walk anything through */
.roster{display:grid;grid-template-columns:repeat(2,1fr);gap:18px;margin-top:8px}
.roster-group{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:20px 24px 6px}
.roster-t{font-family:var(--body);font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--sub);margin:0}
.roster-item{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:5px 16px;align-items:baseline;padding:15px 0;border-bottom:1px solid var(--hair)}
.roster-group .roster-item:last-child{border-bottom:0}
.roster-item b{font-family:var(--display);font-weight:600;font-size:15px;letter-spacing:-.005em;line-height:1.3}
.roster-item em{font-style:normal;font-family:var(--plex);font-size:10.5px;color:var(--sub);white-space:nowrap;font-variant-numeric:tabular-nums}
.roster-item span{grid-column:1/-1;font-size:13px;color:var(--sub);line-height:1.5}
@media (max-width:760px){
  .roster{grid-template-columns:1fr}
  .roster-item em{white-space:normal}
}

/* core-rules visuals: layer flow, file page-tree, variable-architecture timeline */
.rule-head{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-bottom:10px}
.rule-head h4{margin-bottom:0}
.rule-tag{font-family:var(--body);font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--bg);background:var(--ink);border-radius:100px;padding:5px 12px;white-space:nowrap}
#view-case-case .section .rule-law,#view-case-design-system .section .rule-law{margin-top:22px}
.rule-law{padding-top:18px;border-top:1px solid var(--hair)}

.layer-flow{display:flex;align-items:stretch;gap:10px;flex-wrap:wrap;margin-top:22px}
.layer-box{flex:1 1 160px;background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:18px}
.layer-n{font-family:var(--display);font-weight:700;font-size:12px;color:var(--accent);margin-bottom:8px}
.layer-box h5{font-family:var(--display);font-weight:700;font-size:15px;margin:0 0 4px;letter-spacing:-.005em}
/* specific enough to beat "#view-case-... .section p", which sets 17.5px */
#view-case-design-system .section .layer-box p{font-size:11.5px;color:var(--sub);margin:0 0 16px;max-width:none;line-height:1.4}
.layer-ex{font-family:"SF Mono",Consolas,monospace;font-size:11px;color:var(--ink);background:var(--card);border:1px solid var(--hair);border-radius:6px;padding:6px 9px;line-height:1.4;word-break:break-word}
.layer-arrow{display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--sub);flex:0 0 auto;padding:0 2px}
@media (max-width:760px){.layer-flow{flex-direction:column}.layer-arrow{transform:rotate(90deg);padding:2px 0}}

/* text left, the file tree beside it: the tree is reference, not the argument,
   so it sits compact and unframed rather than as a full-width exhibit */
.rule-split{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:36px;align-items:start}
/* the tree is taller than its copy, so it must take the row's height rather than
   set it. height:0 removes it from the row's size calculation (the copy column
   decides that), and min-height:100% then stretches it back to fill. */
.rule-split--fill{align-items:stretch}
.rule-split--fill > .page-tree--mini,
.rule-split--fill > .var-tree{height:0;min-height:100%}
@media (max-width:820px){
  .rule-split{grid-template-columns:1fr;gap:24px}
  .rule-split--fill{align-items:start}
  /* the height:0 + min-height:100% pairing only works while the graphic shares a
     row with the copy. Stacked, 100% resolves against an auto-height parent and
     the block collapses, so both have to be reset here. */
  .rule-split--fill > .page-tree--mini,
  .rule-split--fill > .var-tree{height:auto;min-height:0;max-height:340px}
  .page-tree--mini,.var-tree,.flowchart{width:100%;max-width:340px}
}

.page-tree{background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:6px;font-size:13.5px}
/* tinted block so the tree reads as a diagram rather than as more copy.
   It scrolls inside the height its row gives it, with a hairline scrollbar left
   visible on purpose so the overflow is legible rather than a surprise */
.page-tree--mini{background:var(--bg);border:1px solid var(--hair);border-radius:12px;padding:14px 18px;font-size:11.5px;width:252px;
  overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(17,17,17,.22) transparent}
.page-tree--mini::-webkit-scrollbar{width:5px}
.page-tree--mini::-webkit-scrollbar-track{background:transparent}
.page-tree--mini::-webkit-scrollbar-thumb{background:rgba(17,17,17,.2);border-radius:100px}
.page-tree--mini::-webkit-scrollbar-thumb:hover{background:rgba(17,17,17,.34)}
.page-tree--mini .page-row{padding:3px 0;color:var(--sub)}
.page-tree--mini .page-band{padding:11px 0 5px;font-size:9px;letter-spacing:.13em;gap:8px}
.page-tree--mini .page-band::before{display:none}
.page-band{font-size:10.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--sub);padding:14px 12px 8px;display:flex;align-items:center;gap:10px}
.page-band::before,.page-band::after{content:"";flex:1;height:1px;background:var(--hair)}
.page-row{padding:8px 12px;border-radius:8px;color:var(--ink)}
.page-row--current{background:var(--card);font-weight:600;box-shadow:0 1px 0 var(--hair) inset}

.revision-timeline{display:flex;align-items:stretch;gap:8px;flex-wrap:wrap;margin-bottom:22px}
.rev-step{flex:1 1 130px;text-align:center;background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:16px 12px}
.rev-step--final{background:var(--ink);border-color:var(--ink)}
.rev-step--final .rev-n,.rev-step--final .rev-c{color:var(--bg)}
.rev-n{font-family:var(--display);font-weight:700;font-size:26px;letter-spacing:-.02em;margin-bottom:8px}
.rev-c{font-size:11.5px;color:var(--sub);line-height:1.4}
.rev-arrow{display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--sub);flex:0 0 auto}
@media (max-width:760px){.revision-timeline{flex-direction:column}.rev-arrow{transform:rotate(90deg);padding:2px 0}}

/* minimal flow diagrams for the process rules, same tinted-block language as
   the file tree and the collection table so all four read as one set */
.flowchart{background:var(--bg);border:1px solid var(--hair);border-radius:12px;padding:18px;width:252px;align-self:start;text-align:center}
.flow-node{background:var(--card);border:1px solid var(--line);border-radius:8px;padding:9px 12px;font-size:11.5px;font-weight:500;color:var(--ink);line-height:1.35}
.flow-node--gate{background:var(--ink);color:var(--bg);border-color:var(--ink);font-weight:600}
.flow-arrow{font-size:12px;color:var(--sub);line-height:1;padding:6px 0}
.flow-arrow--labelled{display:flex;flex-direction:column;align-items:center;gap:3px;padding:8px 0}
.flow-arrow--labelled span{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--sub)}
.flow-loop{margin-top:12px;padding-top:11px;border-top:1px dashed var(--line);font-size:10.5px;color:var(--accent);line-height:1.4}
.flow-loop span{font-size:13px;vertical-align:-1px}
.flow-branch{display:flex;flex-direction:column;gap:3px;padding:8px 0;font-size:10px;line-height:1.4}
.flow-yes{color:var(--sub)}
.flow-no{color:var(--accent)}

/* friction: process history, deliberately not styled like the settled spec.
   Unfilled on purpose, so it reads as a margin note rather than competing with
   the filled diagram panels that sit beside it */
.friction{margin-top:24px;padding:1px 0 2px 18px;background:transparent;border-left:2px solid var(--accent);border-radius:0}
.friction-l{display:block;font-family:var(--body);font-size:9.5px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--accent);margin-bottom:7px}
#view-case-design-system .section .friction p{font-size:14.5px;line-height:1.6;color:var(--sub);margin:0;max-width:none}
#view-case-design-system .section .friction p b{color:var(--ink);font-weight:600}
/* the evolution as steps: the counts are the scannable part, so they lead */
.evo{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-direction:column;gap:11px}
.evo li{display:grid;grid-template-columns:24px minmax(0,1fr);gap:12px;align-items:baseline;font-size:13.5px;line-height:1.55;color:var(--sub)}
.evo b{color:var(--ink);font-weight:600}
.evo-n{font-family:var(--display);font-weight:700;font-size:16px;color:var(--accent);letter-spacing:-.02em}
/* the same evolution note, sitting inside an exhibit rather than beside a
   diagram. Only the spacing changes: a top rule here would fight the accent
   border and box the note into a corner */
.friction--inset{margin-top:32px}

/* ---- certification gates ----------------------------------------------
   Grouped by tier rather than by pass/fail, because who set the threshold is
   the part that carries weight. Bands read as headers, not as rows. */
.gates{border:1px solid var(--hair);border-radius:12px;overflow:hidden}
/* a table-only exhibit: the card is already the table's frame, so the card
   drops its padding and the table drops its own border */
.exhibit--flush{padding:0;overflow:hidden}
.exhibit--flush .gates{border:0;border-radius:0}
/* the hairline is kept as transparent rather than removed so every row's
   box height stays exactly as it was — hiding the line must not reflow the table */
.gate-row{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(0,.9fr) minmax(0,1.1fr);gap:16px;padding:13px 16px;font-size:13.5px;line-height:1.45;border-bottom:1px solid transparent}
.gate-row:last-child{border-bottom-color:transparent}
.gate-row span:first-child{color:var(--ink);font-weight:500}
.gate-row span:nth-child(2),.gate-row span:last-child{color:var(--sub);font-variant-numeric:tabular-nums}
.gate-row em{display:block;font-style:normal;font-size:11px;color:var(--sub);letter-spacing:.02em;margin-top:3px}
.gate-head{background:var(--bg);font-size:10px;font-weight:600;letter-spacing:.12em;text-transform:uppercase}
.gate-head span,.gate-head span:first-child{color:var(--sub);font-weight:600}
.gate-band{padding:14px 16px 7px;font-size:10px;font-weight:600;letter-spacing:.11em;text-transform:uppercase;color:var(--accent);border-bottom:1px solid var(--hair)}
@media (max-width:720px){
  .gate-row{grid-template-columns:minmax(0,1fr) minmax(0,.8fr);gap:10px 14px;font-size:12.5px}
  /* the measured column drops under the gate name rather than off the edge */
  .gate-row span:last-child{grid-column:1 / -1;padding-top:2px}
  .gate-head span:last-child{display:none}
}

/* ---- keyline template: four shapes, each in its own 24-unit frame, scaled
   up so the difference in optical weight (square smaller than circle) reads
   at a glance rather than needing the caption to explain it ---- */
.keyline-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:22px}
.keyline-card{display:flex;flex-direction:column;align-items:center;gap:12px}
.keyline-frame{width:96px;height:96px;border:1px dashed var(--hair);border-radius:10px;background:var(--bg);
  display:flex;align-items:center;justify-content:center;position:relative}
.keyline-shape{border:2px solid #0073D4;background:transparent;box-sizing:border-box}
.keyline-label{text-align:center}
.keyline-name{display:block;font-size:12.5px;font-weight:600;color:var(--ink)}
.keyline-dim{display:block;font-size:11px;color:var(--sub);font-variant-numeric:tabular-nums;margin-top:2px}
@media (max-width:640px){
  .keyline-grid{grid-template-columns:repeat(2,1fr);row-gap:24px}
  .keyline-frame{width:76px;height:76px}
}

/* ---- icon library: a dropdown picks the category, pills pick the icon,
   and the grid below shows that one icon at every Size/Icon/* it's built
   for. Boxes are empty on purpose, no artwork wired in yet. ---- */
.iconlib-picker{display:flex;flex-direction:column;align-items:flex-start;gap:12px;margin-bottom:20px}
.iconlib-select-wrap{position:relative;display:inline-flex}
.iconlib-select-wrap::after{content:'';position:absolute;right:15px;top:50%;width:6px;height:6px;border-right:1.5px solid var(--sub);border-bottom:1.5px solid var(--sub);transform:translateY(-65%) rotate(45deg);pointer-events:none}
.iconlib-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;font-family:var(--body);font-size:12.5px;font-weight:600;color:var(--ink);background:var(--bg);border:1px solid var(--line);border-radius:100px;padding:8px 32px 8px 16px;cursor:pointer;transition:border-color .15s ease}
.iconlib-select:hover{border-color:var(--ink)}
.iconlib-box{border:1.5px dashed var(--line);border-radius:5px;background:var(--card);transition:border-color .15s ease,background .15s ease}
.iconlib-svg{color:var(--ink);flex-shrink:0}
.iconlib-provenance{font-size:11px;color:var(--sub);font-style:italic;margin-bottom:12px}
.iconlib-sizeset{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:14px}
.iconlib-sizecard{background:var(--bg);border:1px solid var(--hair);border-radius:12px;padding:20px 16px;display:flex;flex-direction:column;gap:10px}
.iconlib-sizecard-box{min-height:52px;display:flex;align-items:center;justify-content:flex-start}
.iconlib-sizecard-label{font-family:var(--display);font-weight:700;font-size:12.5px;letter-spacing:-.005em;color:var(--ink)}
.iconlib-sizecard-meta{font-size:10.5px;color:var(--sub);font-variant-numeric:tabular-nums;text-transform:uppercase;letter-spacing:.04em}
.iconlib-sizecard-use{font-size:11.5px;color:var(--sub);line-height:1.45}
@media (max-width:640px){
  .iconlib-sizeset{grid-template-columns:repeat(auto-fit,minmax(130px,1fr))}
}

.varch-table{border:1px solid var(--line);border-radius:12px;overflow:hidden}
/* the collections as an expandable tree, same scale as the file tree in rule 2 */
.var-tree{background:var(--bg);border:1px solid var(--hair);border-radius:12px;padding:6px;width:252px;align-self:start;font-size:11.5px;
  overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(17,17,17,.22) transparent}
.var-tree::-webkit-scrollbar{width:5px}
.var-tree::-webkit-scrollbar-track{background:transparent}
.var-tree::-webkit-scrollbar-thumb{background:rgba(17,17,17,.2);border-radius:100px}
.vt-head{display:flex;justify-content:space-between;gap:12px;padding:8px 10px 10px;font-size:9px;font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:var(--sub);border-bottom:1px solid var(--hair);margin-bottom:4px}
.vt-row,.var-tree summary{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:8px 10px;border-radius:8px}
.var-tree summary{cursor:pointer;list-style:none;transition:background .2s ease}
.var-tree summary::-webkit-details-marker{display:none}
.var-tree summary:hover{background:rgba(17,17,17,.045)}
.vt-col[open] > summary{background:rgba(17,17,17,.05)}
.vt-name{color:var(--ink);font-weight:500;display:flex;align-items:center;gap:7px}
/* caret only where there is something to open */
.var-tree summary .vt-name::before{content:"›";display:inline-block;font-size:13px;color:var(--sub);transition:transform .2s ease;line-height:1}
.vt-col[open] > summary .vt-name::before{transform:rotate(90deg)}
.vt-row .vt-name{padding-left:15px}
.vt-n{color:var(--sub);font-variant-numeric:tabular-nums}
.vt-groups{padding:2px 0 6px 26px}
.vt-g{display:flex;justify-content:space-between;gap:12px;padding:5px 10px 5px 0;color:var(--sub)}
.vt-g span:last-child{font-variant-numeric:tabular-nums}
.varch-row{display:flex;justify-content:space-between;padding:13px 16px;font-size:13.5px;border-bottom:1px solid var(--hair)}
.varch-row:last-child{border-bottom:0}
.varch-head{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--sub);background:var(--bg)}
.varch-row--hi{background:var(--card);font-weight:700}
.varch-row span:last-child{color:var(--sub);font-weight:500}
.varch-row--hi span:last-child{color:var(--ink);font-weight:700}
.varch-row.varch-head span:last-child{color:var(--sub);font-weight:600}

/* Stacked layouts: the side graphics carry fixed pixel widths so they line up as
   a set on desktop. Those are declared after the .rule-split media block, so the
   responsive override has to come last in the file to win the cascade. */
@media (max-width:820px){
  .page-tree--mini,.var-tree,.flowchart{width:100%;max-width:340px}
}

/* ===== clubbed glue ===== */
.view[hidden]{display:none}
body:not(.reading) .progress{display:none}

/* ============================================================
   MOBILE (≤640px) — case-study exhibits
   Everything below is scoped inside a max-width query on purpose:
   the desktop layouts above are untouched. The exhibits are the
   dense part of the page, and dense is what breaks on a phone.
============================================================ */

/* the select that replaces a wrapping pill row on small screens.
   Built by mobilizeTabs() in script.js, hidden until the query fires */
.tabs-select-wrap{display:none}

/* ---- custom dropdown -------------------------------------------
   The native select is kept in the DOM for state and assistive tech
   and hidden from sight; this is what a reader actually sees. The
   OS list cannot be styled and renders its options at a different
   size from the closed control, which is the one thing this page
   cannot afford: it is documenting a type scale. Trigger and option
   share --ddn-fs so the text does not resize when the list opens. */
.ddn{--ddn-fs:14px;position:relative;width:100%}
.ddn > select{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.ddn-trigger{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;min-height:44px;font-family:var(--body);font-size:var(--ddn-fs);font-weight:600;color:var(--ink);background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:11px 16px;cursor:pointer;text-align:left;transition:border-color .15s ease,background .15s ease}
.ddn-trigger:hover{border-color:var(--ink)}
.ddn-trigger:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.ddn-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ddn-caret{flex:0 0 auto;color:var(--sub);transition:transform .18s ease}
.ddn.is-open .ddn-caret{transform:rotate(180deg)}
.ddn.is-open .ddn-trigger{border-color:var(--ink)}
.ddn-menu{display:none;position:absolute;z-index:80;top:calc(100% + 6px);left:0;right:0;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:6px;box-shadow:0 18px 40px -18px rgba(17,17,17,.42);max-height:min(58vh,340px);overflow-y:auto}
.ddn.is-open .ddn-menu{display:block}
.ddn-opt{display:block;width:100%;font-family:var(--body);font-size:var(--ddn-fs);font-weight:500;color:var(--sub);background:transparent;border:0;border-radius:8px;padding:11px 12px;text-align:left;cursor:pointer;transition:background .15s ease,color .15s ease}
.ddn-opt:hover{background:var(--bg);color:var(--ink)}
.ddn-opt:focus-visible{outline:2px solid var(--ink);outline-offset:-2px}
/* selected option: tinted and weighted, with a tick. Same reasoning as
   the contents list — a solid fill overstates a resting state */
.ddn-opt.is-on{background:rgba(17,17,17,.055);color:var(--ink);font-weight:600}
.ddn-opt.is-on::after{content:"";float:right;width:5px;height:9px;margin:4px 2px 0 8px;border-right:2px solid var(--ink);border-bottom:2px solid var(--ink);transform:rotate(42deg)}
/* the wrappers drew their own caret for the native control; the
   custom trigger carries one, so those would double up */
.tabs-select-wrap::after,.iconlib-select-wrap::after{display:none}
.iconlib-select-wrap{display:block;min-width:0}
.iconlib-picker .ddn{max-width:260px}

@media (max-width:640px){

  /* ---- tabs become a dropdown -------------------------------
     Six pills wrapping onto three lines is a list pretending to
     be a control. A select is one tap, one row, and native. */
  .tabs{display:none}
  .tabs-select-wrap{display:block;position:relative;margin-bottom:16px}
  .ddn{--ddn-fs:15px}
  .iconlib-picker .ddn{max-width:none}

  /* the group picker travels with the reader: the rows below it are a long
     scroll and the control that chose them should not be a scroll away.
     Bled to the exhibit's edges so nothing shows through underneath.
     Only a picker that is the exhibit's own child sticks — the icon
     library nests two of them, and that whole cluster sticks as one. */
  .exhibit > .tabs-select-wrap{position:sticky;top:74px;z-index:55;background:var(--card);margin:0 -16px 14px;padding:8px 16px 10px}
  /* where a mode switch sticks above it, the picker parks underneath */
  .exhibit:has(> .sem-sticky) > .tabs-select-wrap{top:132px}
  .iconlib-picker{width:100%;position:sticky;top:74px;z-index:55;background:var(--card);margin:0 -16px 16px;padding:10px 16px 12px;gap:10px}

  /* ---- colour ramps -----------------------------------------
     Eleven swatches on one row gives 24px cells with an 8px
     number inside them: unreadable and untappable. Drop the
     number, fix the cell at a tappable size, and let it wrap. */
  .fam-row{grid-template-columns:1fr;gap:8px;margin-bottom:20px}
  .fam-label{font-size:13px}
  .ramp,.ramp-11,.ramp-13{display:flex;flex-wrap:wrap;gap:6px;grid-template-columns:none}
  .swatch{width:34px;height:34px;aspect-ratio:auto;flex:0 0 auto;border-radius:8px}
  .swatch .tone{display:none}
  .swatch:hover{transform:none;box-shadow:none}
  /* tapped swatch, so the hex is still reachable without a cursor */
  .swatch.is-tapped{outline:2px solid var(--ink);outline-offset:2px}
  .exhibit-tooltip{transform:translate(-50%,-150%);font-size:12.5px;padding:8px 12px}

  /* ---- semantic colour tokens -------------------------------
     Name, then the swatch beside its primitive, then the note.
     The three-column grid could not hold on a 375px viewport. */
  .ramp-row.sem-trow{grid-template-columns:1fr;row-gap:8px;column-gap:0;padding:16px 0}
  .sem-trow .sem-name{font-size:14px}
  .sem-trow .sem-chip{width:100%;height:34px;border-radius:8px}
  .sem-trow .sem-use{grid-column:auto;font-size:12.5px}
  .sem-hex{white-space:normal;word-break:break-word;font-size:11px}
  /* swatch and its primitive name read as one unit, side by side */
  .sem-trow-pair{display:grid;grid-template-columns:56px minmax(0,1fr);align-items:center;gap:12px}

  /* ---- semantic type roles ------------------------------------
     Name-plus-flag on one side and a nowrap spec value on the other
     can't share a line at 375px without crowding. Stack them like
     every other exhibit row here already does. */
  .role-head{flex-direction:column;align-items:flex-start;gap:6px}
  .role-name{flex-wrap:wrap}

  /* ---- the mode switch --------------------------------------
     On a phone it stops being a floating overlay and becomes a
     real full-width control sitting under the exhibit title. It
     still sticks, pinned below the back and Contents buttons,
     and releases when its own exhibit ends. No lateral slide:
     there is nothing here for it to slide out of the way of.

     The switch is written before the title in the source, so the
     reorder is done with flex `order` rather than by moving the
     markup, which keeps the desktop overlay exactly as it was. */
  .exhibit:has(> .sem-sticky){display:flex;flex-direction:column}
  .exhibit:has(> .sem-sticky) > .exhibit-head{order:-1}
  .sem-sticky{justify-content:stretch}
  .sem-sticky--inset{height:auto;margin:0 0 16px;top:74px}
  .sem-sticky--inset > .pill-toggle{transform:none!important;transition:none;width:100%;display:flex}
  .sem-sticky--inset > .pill-toggle button{flex:1 1 0}
  .pill-toggle button{min-height:40px;padding:9px 18px;font-size:13.5px}

  /* ---- space & size rows -------------------------------------
     `.ramp-row.sp-row--ramp` is a two-class selector, so the plain
     `.ramp-row` stack at 760px never reached it and these kept
     four desktop columns on a 375px screen. Matched here. */
  .ramp-row.sp-row,
  .ramp-row.sp-row--ramp{grid-template-columns:1fr;row-gap:6px;column-gap:0;padding:14px 0}
  .sp-row .sp-ratio{display:none}
  .sp-row .ramp-meta{grid-column:auto;margin-top:0}
  .sp-row .ramp-render{max-width:100%;overflow-x:auto}

  /* ---- exhibit chrome ---------------------------------------- */
  .exhibit{padding:20px 16px 24px;border-radius:14px;margin:24px 0}
  .exhibit-head{margin-bottom:14px;gap:8px}
  .exhibit-head h4{font-size:16px}
  .exhibit--flush{padding:0}
  .exhibit-sub{font-size:13px;margin-bottom:18px}

  /* ---- gate tables ------------------------------------------
     Three columns of prose at 375px is four words per line.
     Stack, and let the header row go: the labels carry it. */
  .gates{border-radius:0}
  /* the three cells are now three stacked lines rather than three columns,
     so the gap between them is what separates "what is claimed" from "what
     was required" from "what was read". Tight and they run together. */
  .gate-row{grid-template-columns:1fr;row-gap:14px;padding:20px 16px}
  .gate-row.gate-head{display:none}
  .gate-row > span:first-child{font-weight:600;font-size:14.5px;color:var(--ink);line-height:1.4}
  .gate-row > span:nth-child(2),
  .gate-row > span:nth-child(3){font-size:13px;line-height:1.5}
  /* the column name, stamped on each cell by script.js from that table's own
     head row, so a stacked cell still says what it is */
  .gate-row > span[data-label]::before{content:attr(data-label);display:block;font-size:9.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--sub);margin-bottom:4px}
  /* the em inside the first cell is the standard being cited; it is a
     second line, not a continuation of the gate name */
  .gate-row > span:first-child em{display:block;margin-top:5px}

  /* ---- button component exhibits -----------------------------
     Each cell owns its own label, so dropping to two columns
     re-flows as five labelled buttons rather than five labels
     followed by five buttons. */
  .dsbtn-stategrid{grid-template-columns:repeat(2,minmax(0,1fr));min-width:0;gap:20px 12px}
  .dsbtn-statewrap{overflow-x:visible}
  .dsbtn-colorgrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:20px 14px}
  .dsbtn-sizegrid{grid-template-columns:1fr;gap:20px}
  .dsbtn{max-width:100%}

  /* ---- icon library ------------------------------------------ */
  .iconlib-sizeset{grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
  .iconlib-sizecard{padding:16px 14px}

  /* the shapes inside are drawn at a fixed 4× scale in script.js,
     so shrinking the frame crops and squashes them. Keep the
     desktop frame: two 96px cells fit a 375px viewport easily. */
  .keyline-frame{width:96px;height:96px}

  /* ---- rule cards: graphic before its footnote ----------------
     The "How it evolved" note is written inside the copy column,
     which stacks it above the diagram it annotates. `display:contents`
     lifts that column's children into the grid so the note can be
     ordered after the graphic without moving the markup. */
  .rule-split > div:first-child{display:contents}
  .rule-split > div:first-child > h4{order:1}
  .rule-split > div:first-child > p{order:2}
  .rule-split > .page-tree,.rule-split > .var-tree,.rule-split > .flowchart{order:3}
  .rule-split > div:first-child > .friction{order:4}
  .rule-split{gap:18px}
  .rule-split > div:first-child > h4{margin-bottom:0}
  .rule-split > div:first-child > .friction{margin-top:0}

  /* ---- misc exhibits that carried desktop-width assumptions --- */
  .lh-box{width:100%;max-width:170px}
  .money-col{font-size:20px}
  .money-col div{max-width:100%}
  .variance-readout{gap:18px 26px;margin-top:16px;padding-top:16px}
  .keyline-grid{gap:18px}
  .lib-placeholder{padding:36px 18px}
  .axis-cards{gap:18px}
  .focus-demo{grid-template-columns:1fr}

  /* a table that genuinely needs its width scrolls inside its own
     box rather than pushing the page sideways */
  .cm-wrap,.contract-matrix{overflow-x:auto;-webkit-overflow-scrolling:touch}
}

/* No `overflow-x:hidden` on html/body here, deliberately: it turns the root
   into a scroll container and kills `position:sticky` on the mode switches.
   The overflow is fixed at source instead — every exhibit above either fits
   or scrolls inside its own box. */

/* ── insight payoff: the two surface gaps sit side by side, the root cause
      spans both — the layout itself carries "one root cause, two broken homes" ── */
.gm{display:grid;grid-template-columns:1fr 1fr;gap:22px 30px;margin-top:20px}
.gm-i .gm-where{font-family:var(--body);font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:rgba(250,250,248,.55);margin-bottom:7px}
.gm-i h5{font-family:var(--display);font-weight:700;font-size:19px;letter-spacing:-.015em;line-height:1.2;color:var(--bg);margin-bottom:7px}
.view-case .section .requirement--key .gm-i p{font-size:14.5px;line-height:1.55;font-weight:400;color:rgba(250,250,248,.7);margin:0;max-width:none}
.gm-root{grid-column:1 / -1;border-top:1px solid rgba(250,250,248,.15);padding-top:20px}
.gm-root h5{font-size:22px}
.view-case .section .requirement--key .gm-root p{color:rgba(250,250,248,.82)}
@media (max-width:640px){.gm{grid-template-columns:1fr;gap:20px}.gm-root{padding-top:18px}}

/* module-stack diagram, shown as phone silhouettes rather than a text list —
   reuses the exact .device chrome the real before/after screenshots use, so
   it reads as "this is about a phone screen" at a glance. Contents inside are
   deliberately abstract blocks (no fake copy, no attempted pixel accuracy):
   this is a structure diagram, not a reconstructed wireframe. three frames
   per home (Before / Rejected / Final) because the real story wasn't one
   clean pass — an in-between direction got tested or reviewed and dropped,
   and pretending otherwise is what caused the contradiction with the
   "Explored and rejected" blocks later in each act. */
.mstack-pair{margin-top:44px}
.mstack-pair:first-child{margin-top:12px}
.mframes{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,300px));justify-content:center;gap:22px;margin-top:18px}
.mframe-cap{display:flex;align-items:center;gap:9px;margin-bottom:12px;justify-content:center}
.mframe-cap-note{font-size:12px;color:var(--sub);line-height:1.5;text-align:center;margin-top:10px;max-width:none}
.mframe-screen{position:relative;border-radius:44px;overflow:hidden;background:#fff;aspect-ratio:1206/2622;display:flex;flex-direction:column;padding:4% 4.5% }
.mframe-stack{display:flex;flex-direction:column;gap:1.4%;height:100%}
.mframe-blk{background:var(--card);border:1px solid var(--line);border-radius:10px;display:flex;flex-direction:column;justify-content:center;padding:2.6% 6%;flex-grow:1}
.mframe-blk--sm{flex-grow:.6}
.mframe-blk--md{flex-grow:1}
/* size controls proportion only, never color — a black fill on the
   stepper/dark-hero/idle-card blocks read as "this one is highlighted" in
   every frame, including Before and Initial structure, where nothing
   should be singled out */
.mframe-blk--lg{flex-grow:1.6}
.mframe-blk-t{font-family:var(--display);font-weight:600;font-size:11.5px;line-height:1.3;letter-spacing:-.005em;color:var(--ink)}
.mframe-blk-tag{display:inline-block;font-size:8.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#736B5E;margin-top:5px}
@media (max-width:640px){.mframes{grid-template-columns:repeat(auto-fit,minmax(200px,260px))}}

/* grouped hero block — the headline and the two product cards render as one
   visual unit on the real screen (no gap between them), so the diagram
   shouldn't split them into separate boxes just because two of the three
   lines carry a tag. one box, an internal divider per sub-item instead. */
/* the whole card centers by default (inherited from .mframe-blk). the hero
   label needs to stay pinned to the top instead, with only the item list
   centering in whatever space is left below it. */
.mframe-blk--group{padding:0;justify-content:flex-start}
.mframe-blk-sub{display:flex;flex-direction:column;justify-content:center;flex:1;min-height:0}
.mframe-blk-subitem{display:flex;flex-direction:column;gap:3px;padding:2.2% 6%;border-bottom:1px solid transparent}
.mframe-blk-subitem:last-child{border-bottom:0}
.mframe-blk-subitem span:first-child{font-family:var(--display);font-weight:600;font-size:10.5px;line-height:1.3;color:var(--ink)}
.mframe-blk-subitem .mframe-blk-tag{margin-top:0}

/* centered "Hero" label above the itemized contents of every hero block —
   the group card already shows what's inside it; this names the section
   itself, once, above the list, instead of repeating "Hero:" inline in
   every card's first line. */
.mframe-blk-herolabel{position:relative;text-align:center;font-family:var(--body);font-size:9.5px;font-weight:700;letter-spacing:.13em;text-transform:uppercase;color:var(--sub);padding:3% 6% 2%;border-bottom:1px solid var(--line)}

/* deliberate emphasis for the specific rows that changed between Initial
   structure and Final — everything else in the final frame already existed
   by the initial pass, per the panel notes below each pair. Black fill,
   same "key result" treatment as the black box in 03, with a small badge
   marking exactly what moved. Scoped to individual blocks on purpose, not
   the accidental --lg fill from earlier: that one was a bug, this one is
   a deliberate, limited call-out. */
.mframe-blk--updated{background:var(--ink);position:relative;border-color:rgba(255,255,255,.14)}
.mframe-blk--updated .mframe-blk-t{color:var(--bg)}
.mframe-blk--updated .mframe-blk-herolabel{color:rgba(250,250,248,.55);border-bottom-color:rgba(255,255,255,.16)}
.mframe-blk--updated .mframe-blk-subitem{border-bottom-color:rgba(255,255,255,.1)}
.mframe-blk--updated .mframe-blk-subitem span:first-child{color:var(--bg)}
.mframe-blk--updated .mframe-blk-tag{color:rgba(250,250,248,.72)}
/* pinned to the herolabel row specifically (not the tall card), vertically
   centered against "HERO" regardless of how much content sits below it */
.mframe-blk-updated-badge{position:absolute;top:50%;right:6%;transform:translateY(-50%);font-size:7.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--bg);background:rgba(255,255,255,.18);padding:3px 6px;border-radius:100px;line-height:1;white-space:nowrap}
/* the standalone (non-group) updated block has no herolabel to anchor to,
   so it falls back to the card's own top-right corner */
.mframe-blk--updated:not(:has(.mframe-blk-herolabel)) > .mframe-blk-updated-badge{position:absolute;top:8px;right:8px;transform:none}
