/* LSS.works v5 - case study layer.
   Architecture built to expose the full span of the work: business analysis →
   information architecture → system design → UX flows → UI → AI-assisted build.
   Visual language and colour semantics are taken from the product's own Figma
   documentation (file jt3zFZg65ANpWFy2BZIFd1, "❖DESIGN library"), so the page
   reads as an extension of the artefact rather than a skin over it. */

/* product-doc token set, straight from the Figma library */
:root{
  --p-ink:#181D27; --p-body:#414651; --p-desc:#535862; --p-label:#636874;
  --p-line:#E9EAEB; --p-line-2:#F5F5F5; --p-card:#FFFFFF; --p-page:#FAFAFA;
  /* node-type semantics used across all nine diagrams */
  --n-screen:#F8F9FC; --n-screen-ink:#414651;
  --n-process:#D1E9FF; --n-process-ink:#175CD3;
  --n-decision:#FFFAEB; --n-decision-ink:#B54708;
  --n-data:#F0FDF9; --n-data-ink:#107569;
  --n-external:#F4F3FF; --n-external-ink:#5925DC;
  --n-ai:#FDF2FA; --n-ai-ink:#C11574;
  --n-error:#FEF3F2; --n-error-ink:#B42318;
  --n-ok:#ECFDF3; --n-ok-ink:#027A48;
}
:root[data-theme="dark"]{
  --p-ink:#EDEDEE; --p-body:#C3C6CC; --p-desc:#A0A4AC; --p-label:#8C8C92;
  --p-line:#282A30; --p-line-2:#212329; --p-card:#17181B; --p-page:#0D0D0E;
  --n-screen:#1C1E25; --n-screen-ink:#C3C6CC;
  --n-process:#10243B; --n-process-ink:#84CAFF;
  --n-decision:#2A1F0B; --n-decision-ink:#FDB022;
  --n-data:#0A2621; --n-data-ink:#5FE9D0;
  --n-external:#1B1433; --n-external-ink:#BDB4FE;
  --n-ai:#2B1020; --n-ai-ink:#FAA7E0;
  --n-error:#2A1311; --n-error-ink:#FDA29B;
  --n-ok:#0A2318; --n-ok-ink:#6CE9A6;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --p-ink:#EDEDEE; --p-body:#C3C6CC; --p-desc:#A0A4AC; --p-label:#8C8C92;
    --p-line:#282A30; --p-line-2:#212329; --p-card:#17181B; --p-page:#0D0D0E;
    --n-screen:#1C1E25; --n-screen-ink:#C3C6CC;
    --n-process:#10243B; --n-process-ink:#84CAFF;
    --n-decision:#2A1F0B; --n-decision-ink:#FDB022;
    --n-data:#0A2621; --n-data-ink:#5FE9D0;
    --n-external:#1B1433; --n-external-ink:#BDB4FE;
    --n-ai:#2B1020; --n-ai-ink:#FAA7E0;
    --n-error:#2A1311; --n-error-ink:#FDA29B;
    --n-ok:#0A2318; --n-ok-ink:#6CE9A6;
  }
}

/* ---------- case shell ---------- */
.case-wrap{max-width:1120px;margin:0 auto;padding:0 40px}
@media (max-width:760px){ .case-wrap{padding:0 20px} }
.case-narrow{max-width:720px}

.crumb{display:flex;align-items:center;gap:10px;font-size:12px;color:var(--faint);padding-block:26px 0}
.crumb a{display:inline-flex;align-items:center;min-height:24px;border-bottom:1px solid transparent;transition:border-color .18s,color .18s}
.crumb a:hover{color:var(--ink);border-color:var(--p-brand)}

.case-hero{padding-block:56px 40px}
.case-hero .tagrow{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 22px}
.tag{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:var(--p-label);
  border:1px solid var(--line);border-radius:99px;padding:5px 12px;background:var(--surface)}
.tag.brand{color:var(--p-brand);border-color:color-mix(in srgb,var(--p-brand) 40%,transparent)}
.case-hero h1{margin:0 0 18px;font-size:clamp(36px,6vw,76px);font-weight:900;letter-spacing:-.03em;line-height:.98}
.case-hero .lede{margin:0;max-width:62ch;font-size:clamp(17px,1.9vw,21px);line-height:1.6;color:var(--muted)}
.accent{color:var(--p-brand)}

.factbar{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:8px;overflow:hidden;margin-top:40px}
@media (max-width:760px){ .factbar{grid-template-columns:repeat(2,1fr)} }
.factbar .f{background:var(--bg);padding:18px 20px}
.factbar .k{font-size:12px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint);margin:0 0 6px}
.factbar .v{font-size:15px;font-weight:500;color:var(--ink);margin:0;line-height:1.4}

/* ---------- section rhythm ---------- */
.sec{padding-block:64px}
@media (max-width:760px){ .sec{padding-block:44px} }
.sec + .sec{border-top:1px solid var(--line)}
.sec-head{margin:0 0 34px}
.sec-num{font-size:12px;letter-spacing:.12em;color:var(--p-brand);font-variant-numeric:tabular-nums;margin:0 0 10px}
.sec h2{margin:0 0 10px;font-size:clamp(24px,3vw,38px);font-weight:600;letter-spacing:-.02em;line-height:1.15}
.sec .sub{margin:0;color:var(--muted);max-width:64ch;font-size:16px}
.sec p.body{margin:0 0 18px;color:var(--muted);max-width:68ch;line-height:1.7}
.sec p.body:last-child{margin-bottom:0}
.sec p.body strong{color:var(--ink);font-weight:500}

/* ---------- cards ---------- */
.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
@media (max-width:900px){ .cards{grid-template-columns:1fr} }
.card{background:var(--p-card);border:1px solid var(--line);border-radius:10px;padding:24px;
  box-shadow:0 1px 2px rgba(10,13,18,.05)}
.card .rule{width:46px;height:4px;border-radius:99px;background:var(--p-brand);margin:0 0 16px}
.card h3{margin:0 0 16px;font-size:19px;font-weight:600;letter-spacing:-.01em}
.card .row{margin:0 0 14px}
.card .row:last-child{margin-bottom:0}
.card .row .k{font-size:11px;letter-spacing:.09em;text-transform:uppercase;color:var(--p-label);margin:0 0 4px}
.card .row .t{margin:0;font-size:14px;line-height:1.55;color:var(--muted)}

/* hard constraints - presented as a rule sheet, not prose */
.rules{display:flex;flex-direction:column;gap:0;border:1px solid var(--line);border-radius:10px;overflow:hidden}
.rules .r{display:grid;grid-template-columns:150px 1fr;gap:22px;padding:20px 22px;background:var(--p-card);
  border-bottom:1px solid var(--line)}
.rules .r:last-child{border-bottom:0}
@media (max-width:760px){ .rules .r{grid-template-columns:1fr;gap:6px} }
.rules .r .k{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--p-brand);font-weight:600}
.rules .r .t{margin:0;font-size:15px;line-height:1.6;color:var(--muted)}
.rules .r .t code{font-family:ui-monospace,'IBM Plex Mono',monospace;font-size:13px;
  background:var(--surface);border:1px solid var(--line);border-radius:4px;padding:1px 6px;color:var(--ink)}

/* ---------- journey hierarchy: a CSS artefact, monochrome plus one brand rail ----------
   Drawn rather than exported, because no NDA-cleared diagram exists. It stacks, so it
   never becomes a horizontally scrolling strip on a phone (blueprint 3.2, 5.3). */
.hier{padding:26px 24px}
.hier-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;counter-reset:hier}
.hier-row{position:relative;margin-left:calc(var(--d) * 16px);padding:12px 16px 12px 18px;
  border:1px solid var(--line);border-radius:8px;background:var(--surface);
  display:flex;flex-wrap:wrap;align-items:baseline;column-gap:10px;row-gap:2px;min-width:0}
.hier-rail{position:absolute;left:0;top:10px;bottom:10px;width:2px;border-radius:2px;
  background:var(--p-brand);opacity:calc(1 - var(--d) * .16)}
.hier-row b{font-size:14px;font-weight:600;color:var(--ink);letter-spacing:-.005em}
.hier-row em{font-style:normal;font-size:12px;line-height:1.5;color:var(--muted)}
.hier-row::after{content:'';position:absolute;left:-8px;top:50%;width:6px;height:1px;background:var(--line-2,var(--line))}
.hier-row:first-child::after{display:none}
@media (max-width:760px){
  .hier{padding:20px 16px}
  .hier-row{margin-left:calc(var(--d) * 8px);padding:10px 12px 10px 14px}
  .hier-row b{font-size:13px}
}

/* ---------- animated app screens ---------- */
.screens{display:grid;grid-template-columns:1.15fr 1fr;gap:28px;align-items:center}
@media (max-width:900px){ .screens{grid-template-columns:1fr} }
.screens .copy h3{margin:0 0 10px;font-size:21px;font-weight:600;letter-spacing:-.01em}
.screens .copy p{margin:0 0 14px;color:var(--muted);line-height:1.7;font-size:15px}
.screens .copy .note{font-size:13px;color:var(--faint);border-left:2px solid var(--p-brand);padding-left:12px}

.app{position:relative;border:1px solid var(--line);border-radius:10px;overflow:hidden;
  background:var(--p-card);box-shadow:0 1px 2px rgba(10,13,18,.05),0 18px 40px -24px rgba(0,0,0,.35);
  container-type:inline-size}
.app .titlebar{height:30px;background:var(--surface);border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:6px;padding:0 12px}
.app .titlebar i{width:8px;height:8px;border-radius:99px;background:var(--line)}
.app .titlebar i:first-child{background:var(--p-brand);opacity:.75}
.app .titlebar span{margin-left:10px;font-size:10px;letter-spacing:.07em;text-transform:uppercase;color:var(--p-label)}
.app .body{display:grid;grid-template-columns:112px 1fr;min-height:290px}
@container (max-width: 430px){ .app .body{grid-template-columns:74px 1fr} }
.app .side{border-right:1px solid var(--line);padding:14px 10px;display:flex;flex-direction:column;gap:7px;background:var(--surface)}
.app .side i{height:9px;border-radius:3px;background:var(--line);display:block}
.app .side i.on{background:var(--p-brand);opacity:.85;width:72%}
.app .main{padding:16px;display:flex;flex-direction:column;gap:12px}

/* ZBB: To Be Budgeted driving to zero as categories get funded */
.zbb .tbb{display:flex;align-items:baseline;justify-content:space-between;padding-bottom:12px;
  border-bottom:1px solid var(--line)}
.zbb .tbb .lab{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--p-label)}
.zbb .tbb .amt{font-size:22px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--p-brand);
  animation:tbbdrain 7s steps(1) infinite}
@keyframes tbbdrain{
  0%,14%{content:''} /* value swapped via ::after below */
}
.zbb .tbb .amt::after{content:'4 820,00 zł';animation:tbbtext 7s steps(1) infinite}
@keyframes tbbtext{
  0%,18%{content:'4 820,00 zł'}
  19%,36%{content:'3 100,00 zł'}
  37%,54%{content:'1 640,00 zł'}
  55%,72%{content:'480,00 zł'}
  73%,100%{content:'0,00 zł'}
}
.zbb .cat{display:grid;grid-template-columns:1fr 96px 62px;gap:12px;align-items:center}
.zbb .cat .nm{height:8px;border-radius:3px;background:var(--line)}
.zbb .cat .bar{height:10px;border-radius:99px;background:var(--surface);overflow:hidden}
.zbb .cat .bar i{display:block;height:100%;width:0;border-radius:99px;background:var(--n-ok-ink);opacity:.75;
  animation:zbbfund 7s var(--ease-out) infinite}
.zbb .cat:nth-of-type(1) .bar i{--w:100%;animation-delay:.2s}
.zbb .cat:nth-of-type(2) .bar i{--w:100%;animation-delay:1.5s}
.zbb .cat:nth-of-type(3) .bar i{--w:100%;animation-delay:2.8s}
.zbb .cat:nth-of-type(4) .bar i{--w:100%;animation-delay:4.1s}
@keyframes zbbfund{0%{width:0}12%,96%{width:var(--w)}100%{width:var(--w)}}
.zbb .cat .val{height:8px;border-radius:3px;background:var(--line);justify-self:end;width:100%}
.zbb .goal{margin-top:auto;display:flex;align-items:center;gap:8px;font-size:11px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--n-ok-ink);opacity:0;animation:goalhit 7s steps(1) infinite}
@keyframes goalhit{0%,72%{opacity:0}73%,100%{opacity:1}}
.zbb .goal i{width:8px;height:8px;border-radius:99px;background:var(--n-ok-ink)}

/* transaction import: rows arriving, dedup catching one, rule auto-categorising */
.imp .row{display:grid;grid-template-columns:1fr 74px 58px;gap:10px;align-items:center;
  padding:8px 0;border-bottom:1px solid var(--line-2,var(--line));opacity:0;
  animation:impin 8s var(--ease-out) infinite}
.imp .row:nth-of-type(1){animation-delay:.3s}
.imp .row:nth-of-type(2){animation-delay:.9s}
.imp .row:nth-of-type(3){animation-delay:1.5s}
.imp .row:nth-of-type(4){animation-delay:2.1s}
@keyframes impin{0%,4%{opacity:0;transform:translateY(6px)}10%,94%{opacity:1;transform:none}100%{opacity:0}}
.imp .row .d{height:8px;border-radius:3px;background:var(--line)}
.imp .row .chip{font-size:9px;letter-spacing:.05em;text-transform:uppercase;padding:3px 7px;border-radius:99px;
  text-align:center;background:var(--n-screen);color:var(--n-screen-ink)}
.imp .row.dup .chip{background:var(--n-error);color:var(--n-error-ink)}
.imp .row.ruled .chip{background:var(--n-process);color:var(--n-process-ink)}
.imp .row .amt{height:8px;border-radius:3px;background:var(--line);justify-self:end;width:100%}
.imp .foot{margin-top:auto;font-size:11px;color:var(--p-label);letter-spacing:.05em}

/* sync: outbox draining to the sheet, offline tolerated */
.syn .lane{display:flex;align-items:center;gap:10px}
.syn .box{flex:1;border:1px dashed var(--line);border-radius:8px;padding:10px;display:flex;gap:6px;min-height:46px;align-items:center}
.syn .box i{width:15px;height:15px;border-radius:4px;background:var(--n-data-ink);opacity:.8;
  animation:outbox 6s steps(1) infinite}
.syn .box i:nth-child(2){animation-delay:.7s} .syn .box i:nth-child(3){animation-delay:1.4s}
.syn .box i:nth-child(4){animation-delay:2.1s}
@keyframes outbox{0%,40%{opacity:.8;transform:none}41%,100%{opacity:0;transform:translateX(22px)}}
.syn .cloud{width:92px;border:1px solid var(--line);border-radius:8px;padding:10px;text-align:center;
  background:var(--n-external);color:var(--n-external-ink);font-size:10px;letter-spacing:.05em;text-transform:uppercase}
.syn .state{display:flex;gap:7px;margin-top:4px}
.syn .state span{font-size:10px;letter-spacing:.05em;text-transform:uppercase;padding:4px 9px;border-radius:99px;
  background:var(--surface);color:var(--p-label)}
.syn .state span.on{background:var(--n-ok);color:var(--n-ok-ink);animation:syncok 6s steps(1) infinite}
@keyframes syncok{0%,44%{opacity:.35}45%,100%{opacity:1}}

/* v3 AI: receipt → extraction → parse → propose → human accept */
.ai .pipe{display:grid;grid-template-columns:repeat(5,1fr);gap:5px;align-items:stretch}
.ai .st{position:relative;min-width:0;border-radius:6px;padding:9px 4px;text-align:center;font-size:9px;
  letter-spacing:.03em;text-transform:uppercase;background:var(--surface);color:var(--p-label);
  display:flex;align-items:center;justify-content:center;
  animation:pipestep 7.5s steps(1) infinite}
.ai .st:nth-child(1){animation-delay:0s}
.ai .st:nth-child(2){animation-delay:1.1s}
.ai .st:nth-child(3){animation-delay:2.2s}
.ai .st:nth-child(4){animation-delay:3.3s}
.ai .st:nth-child(5){animation-delay:4.4s}
@keyframes pipestep{0%,8%{background:var(--n-ai);color:var(--n-ai-ink)}9%,100%{background:var(--surface);color:var(--p-label)}}
.ai .lnk{display:none}
.ai .gate{margin-top:12px;border:1px solid var(--line);border-radius:8px;padding:12px;background:var(--p-card)}
.ai .gate .g{display:flex;align-items:center;justify-content:space-between;gap:10px}
.ai .gate .g .txt{height:8px;border-radius:3px;background:var(--line);flex:1 1 0;min-width:0}
.ai .gate .g .yes{flex:none;white-space:nowrap;font-size:10px;letter-spacing:.05em;text-transform:uppercase;padding:5px 11px;border-radius:99px;
  background:var(--n-ok);color:var(--n-ok-ink)}
.ai .gate .cap{margin:10px 0 0;font-size:11px;color:var(--p-label)}

@media (prefers-reduced-motion:reduce){
  .app *{animation:none!important}
  .zbb .tbb .amt::after{content:'0,00 zł'}
  .zbb .cat .bar i{width:100%} .zbb .goal{opacity:1}
  .imp .row{opacity:1;transform:none}
  .syn .box i{opacity:.8;transform:none} .syn .state span.on{opacity:1}
  .ai .st:nth-child(3){background:var(--n-ai);color:var(--n-ai-ink)}
}

/* ---------- process spine ----------
   One axis per step: the node on the spine, the phase label and the step title
   share a single centre line. Label and title form their own row, both trimmed
   to cap height, so the row's centre is the centre of the capitals rather than
   of two line boxes of different sizes. The node hangs off the label, so it
   follows it on phones too, where the label sits on its own line. */
.spine{display:flex;flex-direction:column;border-left:2px solid var(--line);padding-left:0}
.spine .step{--step-pad:26px;position:relative;padding:24px 0 24px var(--step-pad)}
.spine .step-head{display:grid;grid-template-columns:132px minmax(0,1fr);column-gap:26px;align-items:center;justify-items:start}
.spine .step-body{margin:12px 0 0 158px}
@media (max-width:760px){
  .spine .step{--step-pad:20px}
  .spine .step-head{grid-template-columns:minmax(0,1fr);row-gap:12px}
  .spine .step-body{margin-left:0}
}
.spine .step .ph{position:relative;font-size:12px;line-height:1.2;letter-spacing:.09em;text-transform:uppercase;color:var(--p-brand)}
.spine .step .ph::before{content:'';position:absolute;left:calc(-6px - var(--step-pad));top:50%;translate:0 -50%;
  width:10px;height:10px;border-radius:99px;background:var(--bg);border:2px solid var(--p-brand)}
.spine .step h3{margin:0;font-size:18px;line-height:1.3;font-weight:600}
@supports (text-box: trim-both cap alphabetic){
  .spine .step .ph,.spine .step h3{text-box:trim-both cap alphabetic}
  .spine .step-body{margin-top:16px}
}
.spine .step p{margin:0;color:var(--muted);font-size:14px;line-height:1.6;max-width:62ch}
.spine .step .out{margin:10px 0 0;display:flex;flex-wrap:wrap;gap:6px}
.spine .step .out span{font-size:12px;color:var(--p-label);background:var(--surface);border:1px solid var(--line);
  border-radius:4px;padding:4px 8px}

/* ---------- role / skills ---------- */
.roles{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:10px;overflow:hidden}
@media (max-width:760px){ .roles{grid-template-columns:1fr} }
.roles .r{background:var(--bg);padding:20px 22px}
.roles .r h3{margin:0 0 6px;font-size:15px;font-weight:600}
.roles .r p{margin:0;font-size:14px;color:var(--muted);line-height:1.6}

/* ---------- pull quote ---------- */
.pull{border-left:3px solid var(--p-brand);padding:6px 0 6px 24px;margin:0}
.pull p{margin:0;font-size:clamp(19px,2.3vw,26px);line-height:1.4;font-weight:500;letter-spacing:-.01em;max-width:26ch}
.pull cite{display:block;margin-top:12px;font-style:normal;font-size:12px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint)}


/* ---------- walkthrough: shared window frame (also used by section 05) ---------- */
.wt-frame{position:relative;border:1px solid var(--line);border-radius:10px;overflow:hidden;
  background:var(--p-card);box-shadow:0 1px 2px rgba(10,13,18,.05),0 22px 50px -30px rgba(0,0,0,.45)}
.wt-bar{height:30px;background:var(--surface);border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:6px;padding:0 12px}
.wt-bar i{width:8px;height:8px;border-radius:99px;background:var(--line-2)}
.wt-bar i:first-child{background:var(--p-brand);opacity:.8}
.wt-bar .wt-screen{margin-left:10px;font-size:10px;letter-spacing:.07em;text-transform:uppercase;color:var(--p-label)}
.wt-edges{border-top:1px solid var(--line);padding-top:14px}
.wt-edges summary{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--p-brand);
  cursor:pointer;font-weight:600}
.wt-edges ul{margin:12px 0 0;padding-left:18px;display:flex;flex-direction:column;gap:8px}
.wt-edges li{font-size:13px;line-height:1.6;color:var(--muted)}

/* ---------- screen gallery ---------- */
.gallery{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
@media (max-width:900px){ .gallery{grid-template-columns:repeat(2,1fr)} }
@media (max-width:600px){ .gallery{grid-template-columns:1fr} }
.shot{border:1px solid var(--line);border-radius:9px;overflow:hidden;background:var(--p-card);
  box-shadow:0 1px 2px rgba(10,13,18,.05);transition:transform .25s var(--ease),box-shadow .25s var(--ease)}
.shot:hover{transform:translateY(-2px);box-shadow:0 1px 2px rgba(10,13,18,.05),0 16px 32px -20px rgba(0,0,0,.4)}
.shot img{width:100%;height:186px;object-fit:cover;object-position:top center;display:block;
  border-bottom:1px solid var(--line)}
.shot .cap{padding:12px 14px}
.shot .cap b{display:block;font-size:14px;font-weight:600}
.shot .cap span{display:block;margin-top:3px;font-size:12px;color:var(--faint);line-height:1.5}

/* ---------- introduction / conclusion two-column ---------- */
.intro-copy .body{font-size:16px;line-height:1.75}

/* ---------- image plates: screens that are actually visible ---------- */
.plate{margin:0;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--p-card);
  box-shadow:0 1px 2px rgba(10,13,18,.05),0 20px 44px -28px rgba(0,0,0,.45)}
.plate img{display:block;width:100%;height:auto}
.plate figcaption{padding:13px 16px;border-top:1px solid var(--line);font-size:13px;color:var(--muted);
  display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.plate figcaption b{color:var(--ink);font-weight:600}
.plate figcaption span{margin-left:auto;font-size:11px;letter-spacing:.05em;color:var(--faint);white-space:nowrap}
.plate a{display:block}
.plate.wide img{max-height:none}

/* ---------- section 05: one screen, one decision ----------
   Same vertical rhythm as the walkthrough in section 04: the decision is named
   first (screen label and headline left, the argument right), then the screen
   runs the full content width in the same window frame, trimmed only in
   height. Items are separated by space alone. */
.scr-list{display:grid;gap:96px;margin-top:56px}
.scr{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);column-gap:56px;row-gap:32px;align-items:start}
.scr-head{grid-column:1}
.scr-copy{grid-column:2}
.scr-shot{grid-column:1 / -1;margin:0;border-radius:12px}
.scr-k{margin:0 0 12px;display:flex;flex-wrap:wrap;align-items:baseline;column-gap:10px;row-gap:2px;
  font-size:14px;line-height:1.4;color:var(--muted)}
.scr-k b{font-size:12px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--p-brand)}
.scr-head h3{margin:0;font-size:clamp(22px,2.4vw,30px);font-weight:700;letter-spacing:-.015em;line-height:1.2;
  color:var(--ink);text-wrap:balance}
.scr-copy p{margin:0 0 14px;max-width:64ch;font-size:16px;line-height:1.65;color:var(--muted)}
.scr-copy p:last-child{margin-bottom:0}
.scr-copy strong{color:var(--ink);font-weight:600}
.scr-copy code{font-family:ui-monospace,'IBM Plex Mono',monospace;font-size:14px;background:var(--surface);
  border:1px solid var(--line);border-radius:4px;padding:0 6px;color:var(--ink)}
.scr-copy .note-inline{font-size:14px;color:var(--faint);border-left:2px solid var(--p-brand);padding-left:12px}
.scr-shot img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;object-position:50% var(--y,0%);
  background:var(--surface)}
/* Full-height screen: same frame width, no crop (the image is taller than 3:2). */
.scr-shot--full img{aspect-ratio:auto;object-fit:fill}
/* Wide board (more than ~3:1): shown at a readable size inside a sideways scroller instead of being shrunk or cropped. */
.scr-shot--scroll .scr-scroll{overflow-x:auto;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch}
.scr-shot--scroll .scr-scroll:focus-visible{outline:2px solid var(--p-brand);outline-offset:-2px}
.scr-shot--scroll img{display:block;width:4800px;max-width:none;height:auto;aspect-ratio:auto;object-fit:fill}
/* A list of screens followed by a rule list needs the same gap as between the screens. */
.scr-list + .rules{margin-top:56px}
@media (max-width:760px){ .scr-list + .rules{margin-top:40px} }
/* NDA-safe placeholder body: it lives INSIDE the shared .wt-frame, so the frame
   supplies the border, radius, shadow and the window bar. The body owns no
   transform: scroll-motion.js writes the tilt on .scr-shot (the frame), and an
   inline transform always beats a rule here (blueprint 6.1, one transform owner). */
.ph-body{display:flex;flex-direction:column;aspect-ratio:3/2;min-height:0;overflow:hidden;background:var(--p-card)}
.ph-body .ph-main{display:grid;grid-template-columns:152px 1fr;flex:1;min-height:0}
.ph-body .ph-side{padding:20px 16px;border-right:1px solid var(--line);background:var(--surface)}
.ph-body .ph-side b{display:block;height:10px;margin-bottom:16px;border-radius:4px;background:var(--line)}
.ph-body .ph-side b:first-child{width:72%;background:var(--p-brand);opacity:.55}
.ph-body .ph-content{padding:30px;display:flex;flex-direction:column;gap:20px}
.ph-search{height:34px;width:58%;border:1px solid var(--line);border-radius:4px;background:var(--surface)}
.ph-filters,.ph-stepper,.ph-branch{display:flex;gap:10px}
.ph-filters i,.ph-stepper i,.ph-branch i{height:24px;min-width:68px;border-radius:99px;background:var(--surface);border:1px solid var(--line)}
.ph-stepper i{min-width:42px;border-radius:4px}
.ph-stepper i:first-child,.ph-branch i:first-child{background:var(--surface);border-color:var(--p-brand)}
.ph-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;flex:1}
.ph-cards i,.ph-form i,.ph-insights i{display:block;border:1px solid var(--line);border-radius:6px;background:var(--surface)}
.ph-cards i{min-height:150px}
.ph-form{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;flex:1}
.ph-form i{min-height:62px}
.ph-branch{align-items:center;min-height:78px}
.ph-branch i{min-width:0;flex:1;height:42px;border-radius:6px}
.ph-lanes{display:grid;grid-template-columns:repeat(5,1fr);gap:8px;min-height:150px}
.ph-lanes i{border:1px solid var(--line);background:var(--surface)}
.ph-lanes i:nth-child(3){border-color:var(--p-brand)}
.ph-insights{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:14px;flex:1}
.ph-insights i{min-height:72px}
/* Hover feedback on a screen frame is depth only. The canon frames carry no hover
   transform, and this one could not have one anyway - see the comment above. */
@media (hover:hover) and (pointer:fine){
  .scr .wt-frame.scr-shot{transition:box-shadow var(--dur-ui) var(--ease)}
  .scr:hover .wt-frame.scr-shot{box-shadow:0 1px 2px rgba(10,13,18,.05),0 26px 48px -24px rgba(0,0,0,.44)}
}
@media (max-width:760px){
  .ph-body .ph-main{grid-template-columns:78px 1fr}
  .ph-body .ph-side{padding:16px 10px}
  .ph-body .ph-content{padding:20px;gap:14px}
  .ph-cards{gap:8px}.ph-cards i{min-height:100px}
  .ph-form i{min-height:46px}
  .ph-lanes{min-height:100px}
}
@media (prefers-reduced-motion:reduce){.scr .wt-frame.scr-shot{transition:none}}
@media (max-width:900px){
  .scr-list{gap:72px;margin-top:40px}
  .scr{grid-template-columns:minmax(0,1fr);row-gap:20px}
  .scr-copy{grid-column:1}
}

.diagrams{display:flex;flex-direction:column;gap:26px}

/* ---------- diagrams as illustration, not as a readable spec ---------- */
.plate img{pointer-events:none;-webkit-user-drag:none;user-select:none}
.plate-sm{max-width:620px}
.diagrams.compact{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
@media (max-width:820px){ .diagrams.compact{grid-template-columns:1fr;max-width:420px} }
.diagrams.compact .plate img{height:190px;object-fit:cover;object-position:top center}
.diagrams.compact figcaption{font-size:12px;line-height:1.5;align-items:flex-start;flex-direction:column;gap:3px}
.diagrams.compact figcaption b{font-size:13px}

/* ---------- fixed nav that frosts once the page moves ---------- */
.nav{position:fixed;top:0;left:0;right:0;z-index:120;
  transition:background-color .3s var(--ease),backdrop-filter .3s var(--ease),
             border-color .3s var(--ease),padding .3s var(--ease);
  border-bottom:1px solid transparent}
.nav.frosted{
  background:color-mix(in srgb, var(--bg) 72%, transparent);
  -webkit-backdrop-filter:saturate(180%) blur(18px);
  backdrop-filter:saturate(180%) blur(18px);
  border-bottom-color:var(--line);
  padding-block:14px}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .nav.frosted{background:color-mix(in srgb, var(--bg) 96%, transparent)}
}
body{padding-top:74px}
@media (max-width:760px){ body{padding-top:62px} }

/* ---------- disclosure notice on anything we deliberately obscured ---------- */
.masked-note{display:flex;align-items:center;gap:7px;width:100%;margin-top:7px;
  font-size:11px;letter-spacing:.02em;color:var(--faint);font-style:normal}
.masked-note::before{content:'';width:12px;height:12px;flex:none;border-radius:3px;
  border:1px solid var(--line-2);
  background:repeating-linear-gradient(135deg,transparent 0 2px,var(--line-2) 2px 4px)}
.plate figcaption span.masked-note{margin-left:0}

/* ---------- nav: lighter frosting, more of the page shows through ---------- */
.nav.frosted{
  background:color-mix(in srgb, var(--bg) 42%, transparent);
  -webkit-backdrop-filter:saturate(190%) blur(26px);
  backdrop-filter:saturate(190%) blur(26px);
  border-bottom-color:color-mix(in srgb, var(--line) 60%, transparent)}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .nav.frosted{background:color-mix(in srgb, var(--bg) 88%, transparent)}
}

/* ---------- make the masking read as a decision, never as a broken image ---------- */
.plate:has(.mask-badge){position:relative}
/* No scrim and no desaturation over the image: only the text inside diagram
   boxes is blurred (in the asset itself), while titles, frames, arrows and
   their labels stay sharp and at full colour. The badge carries its own
   near-opaque background, so it needs nothing painted underneath it. */

.mask-badge{position:absolute;z-index:2;top:50%;left:50%;transform:translate(-50%,-50%);
  display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center;
  padding:16px 22px;border-radius:10px;max-width:82%;
  background:color-mix(in srgb, var(--p-card) 94%, transparent);
  border:1px solid var(--line-2);
  box-shadow:0 10px 30px -14px rgba(0,0,0,.5)}
.mask-badge svg{width:19px;height:19px;color:var(--p-brand);margin-bottom:2px}
.mask-badge b{font-size:13px;font-weight:600;letter-spacing:.01em;color:var(--ink);white-space:nowrap}
.mask-badge em{font-style:normal;font-size:11px;line-height:1.4;color:var(--muted)}
@media (max-width:820px){
  .mask-badge{padding:12px 16px}
  .mask-badge b{font-size:12px} .mask-badge em{display:none}
}

/* the caption notice now states the decision rather than murmuring it */
.masked-note{display:flex;align-items:flex-start;gap:8px;width:100%;margin-top:10px;
  padding-top:10px;border-top:1px solid var(--line);
  font-size:12px;line-height:1.5;color:var(--muted)}
.masked-note::before{content:'';width:13px;height:13px;margin-top:2px;flex:none;border-radius:3px;
  border:1px solid color-mix(in srgb, var(--p-brand) 55%, transparent);
  background:repeating-linear-gradient(135deg,transparent 0 2px,
    color-mix(in srgb, var(--p-brand) 45%, transparent) 2px 4px)}

/* the notice is a grid so long copy wraps instead of running off the plate */
.masked-note{display:grid;grid-template-columns:13px 1fr;gap:8px;align-items:start}
.masked-note::before{margin-top:3px}
.plate figcaption{flex-wrap:wrap}
.plate figcaption > b,.plate figcaption{min-width:0}

/* keep the supporting line until the plate is genuinely narrow */
@media (max-width:820px){ .mask-badge em{display:block} }
@media (max-width:520px){
  .mask-badge{padding:11px 14px}
  .mask-badge em{display:none}
}

/* ---------- section 06: the artefacts shown at scale ---------- */
.diagrams.wide{display:flex;flex-direction:column;gap:22px}
.diagrams.wide .plate{width:100%}
.diagrams.wide .plate img{height:480px;object-fit:cover;object-position:center top}
@media (max-width:820px){ .diagrams.wide .plate img{height:320px} }
@media (max-width:520px){ .diagrams.wide .plate img{height:240px} }
.diagrams.wide figcaption{flex-direction:row;align-items:baseline;gap:10px;padding:15px 20px}
.diagrams.wide figcaption b{font-size:15px}
.diagrams.wide .masked-note{margin-top:11px}
/* a bigger plate carries a bigger badge */
.diagrams.wide .mask-badge{padding:20px 28px;gap:5px}
.diagrams.wide .mask-badge svg{width:22px;height:22px}
.diagrams.wide .mask-badge b{font-size:15px}
.diagrams.wide .mask-badge em{font-size:12px}

/* crop is baked into the asset now (identical 1400×648 band for all three),
   so the plate just lets the image be itself - same height on every row */
.diagrams.wide .plate img{height:auto;object-fit:fill;aspect-ratio:1400/648}

/* the four process flows sit two-up: same band, half the width, so the row
   reads as a set of artefacts rather than four more hero plates */
.diagrams-lead{max-width:64ch;margin:44px 0 20px;color:var(--muted);font-size:15px;line-height:1.7}
.diagrams.grid2{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
.diagrams.grid2 .plate img{width:100%;height:auto;object-fit:fill;aspect-ratio:1400/648}
.diagrams.grid2 figcaption{flex-direction:column;align-items:flex-start;gap:0;padding:13px 16px;font-size:13px}
.diagrams.grid2 figcaption b{font-size:14px}
.diagrams.grid2 .masked-note{margin-top:9px;font-size:11px}
.diagrams.grid2 .mask-badge{padding:13px 18px;gap:3px}
.diagrams.grid2 .mask-badge svg{width:18px;height:18px}
.diagrams.grid2 .mask-badge b{font-size:12px}
.diagrams.grid2 .mask-badge em{font-size:11px}
@media (max-width:820px){
  .diagrams.grid2{grid-template-columns:1fr}
  .diagrams.grid2 .mask-badge em{display:block}
}
/* the catalogue is a wide table - it only reads at full width */
.diagrams.wide + .diagrams-lead{margin-top:44px}
.diagrams.grid2 + .diagrams.wide{margin-top:20px}

/* ═══════════ motion, case-study specific ═══════════
   The page-level reveal lives in styles-v5.css. These are the details that
   only exist here: parts of a component that should arrive a beat after the
   component itself, so a plate reads as "image, then the note about it"
   rather than everything landing at once. */

/* the masking badge lands after its plate has settled - it is an annotation
   about the image, and reading it that way is the whole point */
html.motion .plate .mask-badge{
  opacity:0;transform:translate(-50%,-50%) scale(.97);
  transition:opacity 400ms var(--ease-out-strong) 280ms,
             transform 400ms var(--ease-out-strong) 280ms}
html.motion .plate.in .mask-badge{opacity:1;transform:translate(-50%,-50%)}

/* the spine's node draws itself onto the line as its step arrives */
html.motion .spine > .step .ph::before{
  opacity:0;transform:scale(.35);
  transition:opacity 260ms var(--ease) 160ms,
             transform 420ms var(--ease-out-strong) 160ms}
html.motion .spine > .step.in .ph::before{opacity:1;transform:none}

/* each rule card draws its accent bar rather than arriving with it */
html.motion .cards > .card .rule{transform:scaleX(0);transform-origin:0 50%;
  transition:transform 520ms var(--ease-out-strong) 200ms}
html.motion .cards > .card.in .rule{transform:none}

/* tabs change state, they do not blink into it */
.wt-tab{transition:background-color var(--dur-ui) var(--ease),
                   border-color var(--dur-ui) var(--ease),
                   transform var(--dur-press) var(--ease-out-strong)}

@media (prefers-reduced-motion:reduce){
  html.motion .plate .mask-badge{opacity:1;transform:translate(-50%,-50%)}
  html.motion .spine > .step .ph::before{opacity:1;transform:none}
  html.motion .cards > .card .rule{transform:none}
}

/* ═══════════ entrances for grids and the factbar (stage 3) ═══════════
   UI Incubator "bento scroll reveal" (vote D5): each card arrives from its own
   side - 650ms, cubic-bezier(.16,1,.3,1), 70ms stagger, once - with the blur
   flavour from CodeFronts (D6). In the factbar and the roles grid the hairlines
   are the container showing through a 1px gap, so their cells stay still and
   only the content moves; no grey strip ever opens between cells. The stagger
   is --rd, set on each cell in styles-v5.css and inherited by its content. */
html.motion :where(.rules > .r, .cards > .card, .roles > .r > *, .factbar > .f > *){
  --from-x:0px; --from-y:30px; --from-s:1; --from-blur:6px; --in-dur:650ms; --rd-in:0ms;
  transform:translate3d(var(--from-x),var(--from-y),0) scale(var(--from-s));
  filter:blur(var(--from-blur));
  transition:opacity var(--in-dur) cubic-bezier(.16,1,.3,1) calc(var(--rd,0ms) + var(--rd-in)),
             transform var(--in-dur) cubic-bezier(.16,1,.3,1) calc(var(--rd,0ms) + var(--rd-in)),
             filter var(--in-dur) cubic-bezier(.16,1,.3,1) calc(var(--rd,0ms) + var(--rd-in))}
html.motion :where(.rules > .r, .cards > .card, .roles > .r > *, .factbar > .f > *).in{transform:none;filter:none}

/* 03: left column from the left, middle grows into place, right from the right */
html.motion .cards > .card:nth-child(3n+1){--from-x:-30px;--from-y:0px}
html.motion .cards > .card:nth-child(3n+2){--from-y:0px;--from-s:.92}
html.motion .cards > .card:nth-child(3n){--from-x:30px;--from-y:0px}
/* 08: the left column's content from the left, the right column's from the right */
html.motion .roles > .r:nth-child(odd) > *{--from-x:-30px;--from-y:0px}
html.motion .roles > .r:nth-child(even) > *{--from-x:30px;--from-y:0px}
html.motion .roles > .r > p{--rd-in:60ms}
/* factbar: the value lifts a little and sharpens, a beat after its label */
html.motion .factbar > .f > *{--from-y:12px;--from-blur:8px;--in-dur:900ms}
html.motion .factbar > .f > .v{--rd-in:80ms}
@media (max-width:900px){
  html.motion .cards > .card:nth-child(n){--from-x:0px;--from-y:30px;--from-s:1}
}
@media (max-width:760px){
  html.motion .roles > .r:nth-child(n) > *{--from-x:0px;--from-y:24px}
}
@media (prefers-reduced-motion:reduce){
  html.motion :where(.rules > .r, .cards > .card, .roles > .r > *, .factbar > .f > *){transform:none;filter:none}
}

/* ═══════════ stage 5: plates, details, footer ═══════════ */

/* 06: every diagram plate wipes open from its top edge down (Codrops clip-path
   grid preview, vote G3: power2.inOut); the masking badge waits for the wipe */
html.motion :where(.diagrams > .plate){--rise:0px;clip-path:inset(0 0 100% 0 round 10px);
  transition:opacity 400ms var(--ease) var(--rd,0ms),clip-path 900ms cubic-bezier(.45,0,.55,1) var(--rd,0ms)}
html.motion :where(.diagrams > .plate).in{clip-path:inset(0 0 0 0 round 10px)}
html.motion .diagrams > .plate .mask-badge{transition-delay:720ms}

/* 04: variants and edge cases open and close smoothly (nerdy.dev, CodeFronts:
   ::details-content + interpolate-size). Browsers without them open instantly. */
@media (prefers-reduced-motion:no-preference){
  .wt-edges{interpolate-size:allow-keywords}
  .wt-edges::details-content{block-size:0;opacity:0;overflow-y:clip;
    transition:content-visibility 380ms allow-discrete,opacity 300ms var(--ease),block-size 380ms cubic-bezier(.4,0,.2,1)}
  .wt-edges[open]::details-content{block-size:auto;opacity:1}
}


@media (prefers-reduced-motion:reduce){
  html.motion :where(.diagrams > .plate){clip-path:none}
}

/* ═══════════ scroll-linked moments (js/scroll-motion.js) ═══════════
   Active only while html.motion-scrub is set. The script sets it after it has
   found what it animates, and never under reduced motion; without it the page
   is the static, complete version. */

/* 05: screens settle from a backward tilt (the transform is written by the script) */
html.motion-scrub .scr-shot{backface-visibility:hidden}

/* headlines: the hero title's letters stretch in once; section titles are
   entered by the script (lean + words), so they are not in the generic reveal */
html.motion-scrub .case-hero > h1{opacity:1;transform:none;transition:none}
/* each word clips at its own baseline: letters rise out of it stretched and never
   spill over the lede; the generous top inset keeps the ż dot and ó accent whole */
.case-hero > h1 .hw{display:inline-block;white-space:nowrap}
html.motion-scrub .case-hero > h1.split .hw{clip-path:inset(-60% -12% -24% -12%)}
.case-hero > h1 .hc{display:inline-block}
html.motion-scrub .case-hero > h1.split .hc{opacity:0;transform-origin:50% 0;transform:translate3d(0,120%,0) scale(.7,2.3)}
html.motion-scrub .case-hero > h1.split.go .hc{opacity:1;transform:none;
  transition:transform 900ms cubic-bezier(.68,-.6,.32,1.6) calc(var(--i,0) * 30ms + 120ms),
             opacity 360ms var(--ease) calc(var(--i,0) * 30ms + 120ms)}

/* 01: the paragraph being read keeps its At a glance items in full ink */
html.motion-scrub .intro-layout .glance-item{transition:opacity 300ms var(--ease)}
html.motion-scrub .intro-layout .glance-item dt{transition:color 300ms var(--ease)}
html.motion-scrub .intro-layout.is-reading .glance-item:not(.on){opacity:.65}
html.motion-scrub .intro-layout.is-reading .glance-item.on dt{color:var(--p-brand)}

/* 06: the two-column plates lean out and settle (transform is written by the
   script, so it must not be smoothed by the reveal transition) */
html.motion-scrub .diagrams.grid2 > .plate{
  transition:opacity 400ms var(--ease) var(--rd,0ms),clip-path 900ms cubic-bezier(.45,0,.55,1) var(--rd,0ms)}

/* leaving through the footer: two layers sweep up over the page */
.page-wipe{position:fixed;inset:0;z-index:300;pointer-events:none}
.page-wipe i{position:absolute;inset:0;transform:translate3d(0,100%,0);
  transition:transform 760ms cubic-bezier(.7,0,.3,1)}
.page-wipe i:first-child{background:var(--p-brand)}
.page-wipe i:last-child{background:var(--bg);transition-delay:140ms}
.page-wipe.go i{transform:none}

/* 07: the spine draws itself, and each node lights as the line reaches it */
html.motion-scrub .spine{position:relative}
html.motion-scrub .spine::after{content:'';position:absolute;left:-2px;top:0;bottom:0;width:2px;z-index:0;
  background:var(--p-brand);transform-origin:50% 0;transform:scaleY(var(--draw,0));
  opacity:min(1, calc(var(--draw,0) * 10))}
html.motion-scrub .spine > .step .ph::before,
html.motion-scrub .spine > .step.in:not(.lit) .ph::before{z-index:1;opacity:1;transform:scale(.6);
  border-color:var(--line-2);background:var(--bg);
  transition:transform 420ms cubic-bezier(.34,1.56,.64,1),background-color 200ms var(--ease),border-color 200ms var(--ease)}
html.motion-scrub .spine > .step.lit .ph::before{transform:none;border-color:var(--p-brand);background:var(--p-brand)}

/* ═══════════ blurred diagrams: context without the know-how ═══════════
   A blurred artefact only earns its place if the reader knows what they are
   looking at. Each caption names the question the diagram answers, how to read
   the parts that stay sharp (titles, tags, arrow labels), a few structural
   counts, and - explicitly - what the blur is hiding. */

/* The diagram images keep Figma's light palette in both themes, so the legend
   swatches are literal copies of that palette rather than theme tokens. */
:root{
  --dg-screen:#F8F9FC; --dg-process:#D1E9FF; --dg-decision:#FFFAEB; --dg-data:#F0FDF9;
  --dg-external:#F4F3FF; --dg-error:#FEE4E2; --dg-ai:#FDF2FA;
}
.dlegend{display:grid;gap:12px;margin:0 0 24px;padding:16px 20px;
  border:1px solid var(--line);border-radius:10px;background:var(--p-card)}
.dlegend-lead{margin:0;max-width:86ch;font-size:14px;line-height:1.6;color:var(--muted)}
.dlegend-lead b{color:var(--ink);font-weight:600}
.dlegend-keys{display:flex;flex-wrap:wrap;gap:8px 18px;margin:0;padding:0;list-style:none}
.dlegend-keys li{display:inline-flex;align-items:center;gap:8px;font-size:12px;color:var(--p-label)}
.dlegend-keys i{width:18px;height:12px;flex:none;border-radius:4px;background:var(--k);
  border:1px solid rgba(16,24,40,.22)}

.plate figcaption.dcap{display:grid;grid-template-columns:minmax(0,1fr);gap:10px;align-items:start;padding:18px 20px 16px}
.dcap-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px}
.plate figcaption.dcap .dcap-head b{font-size:16px}
.dcap-src{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.dcap-q{margin:0;max-width:70ch;font-size:16px;line-height:1.5;font-weight:500;color:var(--ink);text-wrap:balance}
.dcap-body{margin:0;max-width:80ch;font-size:14px;line-height:1.65;color:var(--muted)}
.dcap-facts{display:flex;flex-wrap:wrap;gap:6px;margin:2px 0 0;padding:0;list-style:none}
.dcap-facts li{font-size:12px;line-height:1.4;color:var(--p-label);background:var(--surface);
  border:1px solid var(--line);border-radius:100px;padding:4px 10px;font-variant-numeric:tabular-nums}
.dcap-facts strong{color:var(--ink);font-weight:600}
.plate figcaption.dcap .masked-note{margin-top:4px}

/* half-width plates carry the same structure a size down */
.diagrams.grid2 .plate figcaption.dcap{gap:8px;padding:14px 16px}
.diagrams.grid2 .plate figcaption.dcap .dcap-head b{font-size:14px}
.diagrams.grid2 .dcap-q{font-size:14px}
@media (max-width:760px){
  .dlegend{padding:14px 16px}
  .plate figcaption.dcap{padding:14px 16px}
  .dcap-q{font-size:14px}
}

/* The badge was centred on the whole <figure> (top:50%). With the fuller
   captions a half-width plate is taller below the image than the image itself,
   so the badge landed on the caption text. Centre it on the image instead:
   every masked image has a fixed aspect ratio, so its height is known from the
   plate's width (container query units) without touching the markup. */
.plate:has(.mask-badge){container-type:inline-size;--img-ratio:.4629}     /* 1400 × 648 bands */
.plate.plate-sm:has(.mask-badge){--img-ratio:.76}                         /* 1600 × 1216 context */
.plate:has(.mask-badge) .mask-badge{top:calc(100cqw * var(--img-ratio) / 2)}

/* ---------- section 01: the whole case study in one card ----------
   A recruiter scans before reading, so the summary sits beside the section
   title instead of below the copy. Its top edge lines up with the cap line of
   the title; the section number stays above the title in the left column. */
.intro-layout{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,1fr);
  grid-template-rows:auto auto auto 1fr;column-gap:56px;align-items:start}
.intro-layout > .sec-head{display:contents}
.intro-layout .sec-num{grid-column:1;grid-row:1}
.intro-layout .sec-head h2{grid-column:1;grid-row:2}
.intro-layout .sec-head .sub{grid-column:1;grid-row:3}
.intro-layout > .intro-copy{grid-column:1;grid-row:4;margin-top:36px}
.intro-layout > .glance{grid-column:2;grid-row:2 / span 3;align-self:start}

.glance{background:var(--p-card);border:1px solid var(--line);border-radius:12px;padding:28px 28px 30px;
  box-shadow:0 1px 2px rgba(10,13,18,.05),0 24px 48px -32px rgba(0,0,0,.4)}
.glance-k{margin:0 0 10px;font-size:12px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--p-brand)}
.glance-lede{margin:0 0 28px;font-size:18px;line-height:1.45;font-weight:600;color:var(--ink);text-wrap:balance}
/* subsections are separated by space alone - no rules, no dividers */
.glance-list{margin:0;display:grid;gap:22px}
.glance-item dt{margin:0 0 4px;font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink)}
.glance-item dd{margin:0;font-size:15px;line-height:1.55;color:var(--muted)}

/* trim the title's line box to its cap height so the card and the letters share one top line */
@supports (text-box: trim-start cap alphabetic){
  .intro-layout .sec-head h2{text-box:trim-start cap alphabetic}
  .intro-layout .sec-num{margin-bottom:18px}
}
@supports not (text-box: trim-start cap alphabetic){
  .intro-layout > .glance{margin-top:calc(clamp(24px,3vw,38px) * .24)}
}
@media (max-width:900px){
  .intro-layout{grid-template-columns:1fr;grid-template-rows:none}
  .intro-layout > .glance{grid-column:1;grid-row:4;margin-top:28px}
  .intro-layout > .intro-copy{grid-row:5;margin-top:32px}
}

/* section titles carry the page: black weight */
.sec h2{font-weight:900;letter-spacing:-.015em}

/* ---------- section 04: walkthrough ----------
   Read top to bottom: the path tabs and the path summary, what this step is, then
   the carousel. The progress bars sit 24px above the carousel's top edge so they
   read as part of it; previous / next live inside it, on its sides. */
.wt{margin-top:8px}
.wt-tabs{display:flex;gap:6px;overflow-x:auto;padding-bottom:14px;margin-bottom:22px;
  border-bottom:1px solid var(--line);scrollbar-width:thin}
.wt-tab{flex:0 0 auto;display:flex;flex-direction:column;gap:3px;align-items:flex-start;text-align:left;
  padding:10px 14px;border:1px solid var(--line);border-radius:8px;background:var(--p-card)}
.wt-tab .id{font-size:10px;letter-spacing:.1em;color:var(--p-brand);font-weight:600}
.wt-tab .nm{font-size:13px;color:var(--muted);white-space:nowrap}
.wt-tab:hover{border-color:var(--line-2)}
.wt-tab[aria-selected="true"]{background:var(--p-brand);border-color:var(--p-brand)}
.wt-tab[aria-selected="true"] .id,.wt-tab[aria-selected="true"] .nm{color:#fff}
.wt-tab:focus-visible{outline:2px solid var(--p-brand);outline-offset:2px}

/* tab strip: scrollable, with affordances */
.wt-tabwrap{position:relative}
.wt-tabwrap .wt-tabs{scroll-behavior:smooth;-ms-overflow-style:none}
.wt-tabwrap .wt-tabs::-webkit-scrollbar{height:0}
.wt-nav{position:absolute;top:calc(50% - 16px);width:30px;height:30px;border-radius:99px;
  border:1px solid var(--line-2);background:var(--p-card);color:var(--muted);
  display:flex;align-items:center;justify-content:center;font-size:14px;line-height:1;
  box-shadow:0 2px 10px rgba(0,0,0,.18);z-index:2;transition:opacity .2s var(--ease),border-color .2s}
.wt-nav:hover{border-color:var(--p-brand);color:var(--ink)}
.wt-nav[hidden]{display:none}
.wt-nav.prev{left:-6px}
.wt-nav.next{right:-6px}
.wt-tabwrap::before,.wt-tabwrap::after{content:'';position:absolute;top:0;bottom:16px;width:46px;
  pointer-events:none;z-index:1;opacity:0;transition:opacity .2s var(--ease)}
.wt-tabwrap::before{left:0;background:linear-gradient(90deg,var(--bg),transparent)}
.wt-tabwrap::after{right:0;background:linear-gradient(270deg,var(--bg),transparent)}
.wt-tabwrap.can-prev::before{opacity:1}
.wt-tabwrap.can-next::after{opacity:1}
/* no scrollbar chrome, clearly visible controls */
.wt-tabwrap .wt-tabs{scrollbar-width:none;-ms-overflow-style:none;overflow-y:hidden}
.wt-tabwrap .wt-tabs::-webkit-scrollbar{height:0;width:0;display:none}
/* WCAG: 44px hit target, 3:1 contrast against the page, obvious focus ring */
.wt-nav{width:44px;height:44px;top:calc(50% - 22px);font-size:20px;font-weight:500;
  background:var(--ink);color:var(--bg);border:1px solid var(--ink);
  box-shadow:0 4px 16px rgba(0,0,0,.35);opacity:1}
.wt-nav:hover{background:var(--p-brand);border-color:var(--p-brand);color:#fff}
.wt-nav:focus-visible{outline:3px solid var(--p-brand);outline-offset:3px}
.wt-nav.prev{left:-10px}
.wt-nav.next{right:-10px}
@media (max-width:520px){ .wt-nav.prev{left:-6px} .wt-nav.next{right:-6px} }
/* the fades must not sit on top of the buttons */
.wt-tabwrap::before,.wt-tabwrap::after{width:56px}

.wt .wt-intro{margin:0 0 24px;max-width:72ch;font-size:16px;line-height:1.65;color:var(--muted)}

/* the caption is as tall as the longest caption in the path, so the carousel never jumps */
.wt-caption{display:grid;margin:0 0 32px}
.wt-caption > *,.wt-cap-sizers > *{grid-area:1 / 1}
.wt-cap-sizers{display:grid;visibility:hidden}
.wt-cap-title{margin:0 0 8px;font-size:clamp(22px,2.4vw,28px);font-weight:700;letter-spacing:-.01em;line-height:1.25;color:var(--ink)}
.wt-cap-desc{margin:0;max-width:72ch;font-size:16px;line-height:1.65;color:var(--muted)}

/* text that changes arrives with a short rise, so the eye follows the change */
@keyframes wt-rise{from{opacity:0;transform:translate3d(0,8px,0)}to{opacity:1;transform:none}}
.wt-enter{animation:wt-rise 520ms var(--ease-out-strong) both}

/* progress: bars only, their bottom edge 24px above the carousel. Each button is
   taller than its bar (space above it), so a 4px bar is still an easy target. */
.wt-stepper{list-style:none;margin:0 0 24px;padding:0;display:grid;
  grid-template-columns:repeat(var(--n,4),minmax(0,1fr));gap:8px}
.wt-seg{display:block;width:100%;padding:20px 0 0;border:0;background:none;cursor:pointer}
.wt-seg-bar{display:block;height:4px;border-radius:4px;background:var(--line-2);overflow:hidden;
  transition:background-color var(--dur-ui) var(--ease)}
.wt-seg:hover .wt-seg-bar{background:var(--faint)}
.wt-seg-bar i{display:block;height:100%;background:var(--p-brand);transform:scaleX(0);transform-origin:0 50%;
  transition:transform 320ms var(--ease-out-strong)}
.wt-seg.done .wt-seg-bar i,.wt-seg.on .wt-seg-bar i{transform:scaleX(1)}
/* the running step fills with its timer, on the compositor (transform, not width) */
.wt[data-run] .wt-seg.on .wt-seg-bar i{transform:scaleX(0);transition:none;
  animation:wt-fill var(--wt-ms,6000ms) linear forwards}
.wt[data-hold] .wt-seg.on .wt-seg-bar i{animation-play-state:paused}
@keyframes wt-fill{to{transform:scaleX(1)}}
.wt-seg:focus-visible{outline:2px solid var(--p-brand);outline-offset:4px;border-radius:4px}

/* carousel: screens push on one symmetric ease-in-out. 3:2 is the widest screen in
   the set, so with `cover` every screen fills the width and is trimmed only in
   height; a step can move the vertical window onto what it talks about (`focus`). */
.wt-panel > .wt-frame{border-radius:12px}
.wt-stage{position:relative}
.wt-shots{position:relative;aspect-ratio:3/2;background:var(--surface);overflow:hidden}
.wt-shots img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:top center;
  visibility:hidden;transition:transform var(--wt-push,720ms) cubic-bezier(.65,0,.35,1)}
.wt-shots img.on,.wt-shots img.out{visibility:visible}
/* previous / next sit on the screen itself, so they keep one dark style in both
   themes: the screens are always light */
.wt-ctl{position:absolute;top:50%;z-index:2;width:44px;height:44px;margin-top:-22px;display:grid;place-items:center;
  border:1px solid rgba(255,255,255,.18);border-radius:100px;background:rgba(10,13,18,.72);color:#fff;
  font-size:20px;line-height:1;cursor:pointer;box-shadow:0 8px 20px -8px rgba(10,13,18,.5);
  transition:background-color var(--dur-ui) var(--ease),border-color var(--dur-ui) var(--ease),
             transform var(--dur-press) var(--ease-out-strong)}
.wt-ctl.wt-prev{left:16px}
.wt-ctl.wt-next{right:16px}
.wt-ctl:hover{background:var(--p-brand);border-color:var(--p-brand)}
.wt-ctl:active{transform:scale(.94)}
.wt-ctl:focus-visible{outline:2px solid var(--p-brand);outline-offset:2px}

.wt-panel > .wt-edges{margin-top:24px}
.wt-panel > .wt-edges ul{columns:2;column-gap:40px}
.wt-panel > .wt-edges li{break-inside:avoid;margin-bottom:8px}

@media (max-width:760px){
  .wt-caption{margin-bottom:24px}
  .wt-stepper{gap:6px}
  .wt-ctl.wt-prev{left:8px}
  .wt-ctl.wt-next{right:8px}
  .wt-panel > .wt-edges ul{columns:1}
}
@media (prefers-reduced-motion:reduce){
  .wt[data-run] .wt-seg.on .wt-seg-bar i{animation:none;transform:scaleX(1)}
  .wt-enter{animation:none}
  .wt-shots img{visibility:visible;opacity:0;transition:opacity 300ms var(--ease)}
  .wt-shots img.on{opacity:1}
}

/* active walkthrough tab: white on the light-theme red passes; on the dark-theme red it does not, so text goes dark */
:root[data-theme="dark"] .wt-tab[aria-selected="true"] .id,
:root[data-theme="dark"] .wt-tab[aria-selected="true"] .nm{color:#0D0D0E}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .wt-tab[aria-selected="true"] .id,
  :root:not([data-theme="light"]) .wt-tab[aria-selected="true"] .nm{color:#0D0D0E}
}

/* keyboard parity: what hover shows, focus-visible shows too */
.crumb a:focus-visible{color:var(--ink);border-color:var(--p-brand)}
.scr:focus-within .wt-frame.scr-shot{box-shadow:0 1px 2px rgba(10,13,18,.05),0 26px 48px -24px rgba(0,0,0,.44)}
.wt-seg:focus-visible .wt-seg-bar{background:var(--faint)}

/* walkthrough arrows are SVG chevrons, not text glyphs */
.wt-nav,.wt-ctl{display:grid;place-items:center}
.wt-nav svg,.wt-ctl svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

/* LiftSystem case: sitemap (03) and the scroll story (04).
   Static first: without html.motion-scrub + .story.is-live the story is a poster and a list. */
.smap{border:1px solid var(--line);border-radius:12px;background:var(--surface);padding:24px;display:grid;gap:24px;margin-bottom:32px}
.smap-global{margin:0;display:flex;flex-wrap:wrap;gap:4px 12px;padding-bottom:16px;border-bottom:1px solid var(--line);font-size:14px;line-height:1.6;color:var(--muted)}
.smap-global>span:first-child{color:var(--ink);font-weight:600}
.smap-cols{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(176px,1fr));gap:24px 20px}
.smap-cols>li{display:grid;align-content:start;gap:12px}
.smap-cols .k{margin:0;padding-bottom:10px;border-bottom:2px solid var(--p-brand);font-size:14px;line-height:1.3;font-weight:700;color:var(--ink)}
.smap-cols ul{list-style:none;margin:0;padding:0;display:grid;gap:8px}
.smap-cols li li{font-size:14px;line-height:1.4;color:var(--muted);padding-left:12px;border-left:2px solid var(--line)}
.smap-chips{margin:0;display:flex;flex-wrap:wrap;gap:8px}
.smap-chips span{font-size:12px;line-height:1.2;color:var(--p-label);background:var(--bg);border:1px solid var(--line);border-radius:99px;padding:6px 12px}

/* 04 - the scroll story is full-bleed: it sits outside .case-wrap, the stage fills the screen
   and the four captions lie over the picture. Static first: without .is-live it is a
   full-width poster with the captions listed below it. */
.story-sec{padding-bottom:0}
.story{margin-top:48px}
.story-media{position:relative;margin:0;width:100%;aspect-ratio:16/9;overflow:hidden;background:#1c1c1c}
.story-poster,.story-canvas{position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:cover}
.story-canvas{opacity:0}
.story-copy{max-width:1120px;margin:0 auto;padding:40px 40px 0}
.story-steps{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:32px}
.story-steps .st{border-top:2px solid var(--p-brand);padding-top:16px}
.story-steps .n{margin:0 0 8px;display:flex;align-items:baseline;gap:12px;font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--p-label)}
.story-steps .n b{font-size:18px;letter-spacing:0;color:var(--p-brand)}
.story-steps h3{margin:0 0 8px;font-size:20px;line-height:1.2;font-weight:700;letter-spacing:-.01em}
.story-steps p:not(.n){margin:0;font-size:16px;line-height:1.6;color:var(--muted)}
.story-rail{display:none;margin:0;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
.story-rail i{display:block;height:4px;border-radius:2px;background:rgba(255,255,255,.28);transition:background-color .3s var(--ease,ease)}
.story-rail i.on{background:var(--p-brand)}
.story-note{margin:0;padding:16px 0 8px;font-size:14px;line-height:1.5;color:var(--faint)}
@media (max-width:900px){
  .story-copy{padding:32px 20px 0}
  .story-steps{grid-template-columns:minmax(0,1fr);gap:24px}
}

/* live: a tall track, a stage that fills the screen, the frame follows the scroll */
html.motion-scrub .story.is-live .story-track{height:500vh}
html.motion-scrub .story.is-live .story-stage{position:sticky;top:0;height:100vh;height:100svh;overflow:hidden;background:#1c1c1c}
html.motion-scrub .story.is-live .story-media{position:absolute;inset:0;width:100%;height:100%;aspect-ratio:auto}
html.motion-scrub .story.is-live .story-media::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to top,rgba(10,10,12,.86) 0%,rgba(10,10,12,.5) 34%,rgba(10,10,12,0) 62%),
             linear-gradient(to bottom,rgba(10,10,12,.5) 0%,rgba(10,10,12,0) 22%)}
html.motion-scrub .story.is-live.is-drawn .story-canvas{opacity:1}
html.motion-scrub .story.is-live .story-copy{position:absolute;left:0;right:0;bottom:0;max-width:none;margin:0;
  padding:0 max(40px,calc((100% - 1120px) / 2)) 48px;display:grid;gap:24px}
html.motion-scrub .story.is-live .story-steps{position:relative;display:block;min-height:232px}
html.motion-scrub .story.is-live .story-steps .st{position:absolute;left:0;right:0;bottom:0;top:auto;opacity:0;
  transform:translate3d(0,16px,0);pointer-events:none;border-top:0;padding-top:0;max-width:640px;
  transition:opacity .45s var(--ease,ease),transform .45s var(--ease,ease)}
html.motion-scrub .story.is-live .story-steps .st.on{opacity:1;transform:none;pointer-events:auto}
html.motion-scrub .story.is-live .story-steps .n{color:rgba(255,255,255,.72);font-size:14px;margin-bottom:12px}
html.motion-scrub .story.is-live .story-steps .n b{font-size:20px;color:var(--p-brand)}
html.motion-scrub .story.is-live .story-steps h3{margin:0 0 12px;font-size:clamp(32px,5vw,64px);line-height:1.04;letter-spacing:-.02em;color:#fff}
html.motion-scrub .story.is-live .story-steps p:not(.n){font-size:clamp(16px,1.6vw,20px);line-height:1.55;color:rgba(255,255,255,.86);max-width:44ch}
html.motion-scrub .story.is-live .story-rail{display:grid}
@media (max-width:900px){
  html.motion-scrub .story.is-live .story-track{height:400vh}
  html.motion-scrub .story.is-live .story-copy{padding:0 20px 32px;gap:20px}
  html.motion-scrub .story.is-live .story-steps{min-height:216px}
  html.motion-scrub .story.is-live .story-steps .st{max-width:none}
}
@media (prefers-reduced-motion:reduce){
  .story-rail i{transition:none}
}

html.story-in .nav.frosted{background:color-mix(in srgb, var(--bg) 94%, transparent)}

/* ═══════════ Skillspark additions (2026-10-09) ═══════════
   Raster artefact plates (brandbook pages, gadgets), a two-up poster frame and
   the spacing between modules that the older cases never stacked. All
   geometry is even-pixel; colours are the shared tokens. */
.diagrams.grid2.raster .plate img{width:100%;height:auto;aspect-ratio:var(--img-ar,3/2);object-fit:cover}
.diagrams + .diagrams,.diagrams-lead + .diagrams,.sec-head + .diagrams{margin-top:20px}
.diagrams + .cards,.diagrams.wide + .cards{margin-top:32px}
.scr-list + .diagrams,.diagrams + .scr-list{margin-top:72px}
.scr-duo{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2px;background:var(--line)}
.scr-duo img{display:block;width:100%;height:auto;aspect-ratio:16/9;object-fit:cover}
@media (max-width:600px){ .scr-duo{grid-template-columns:minmax(0,1fr)} }
.roles > .r:last-child:nth-child(odd){grid-column:1 / -1}
@media (max-width:760px){ .roles > .r:last-child:nth-child(odd){grid-column:auto} }
.roles + .plate{margin-top:32px}
/* SVG diagrams swap to a portrait file on phones (<picture>), so the plate takes
   the natural ratio of whichever file is loaded instead of the fixed 1400/648 band */
.diagrams.wide .plate.dia img,.diagrams .plate.dia img{height:auto;aspect-ratio:auto;object-fit:contain}
.diagrams + .rules,.diagrams.wide + .rules{margin-top:32px}
.diagrams + .spine,.diagrams.wide + .spine{margin-top:40px}
/* rule sheet row with two columns: what I decided / what I handed over */
.rules .r .pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:22px}
@media (max-width:760px){ .rules .r .pair{grid-template-columns:minmax(0,1fr);gap:14px} }
.rules .r .pair .lab{display:block;margin:0 0 4px;font-size:12px;letter-spacing:.09em;text-transform:uppercase;color:var(--p-label)}
.rules .r .pair p{margin:0;font-size:14px;line-height:1.6;color:var(--muted)}
/* value cards: text and an artefact chip pinned to the bottom edge */
.cards-art .card{display:flex;flex-direction:column}
.cards-art .card > p{margin:0;font-size:14px;line-height:1.6;color:var(--muted)}
.cards-art .card .out{margin:auto 0 0;padding-top:16px;display:flex;flex-wrap:wrap;gap:6px}
.cards-art .card .out span{font-size:12px;color:var(--p-label);background:var(--surface);border:1px solid var(--line);
  border-radius:4px;padding:4px 8px}
/* my own cell: one full-width card with a kept and a handed-over column */
.cards.me{grid-template-columns:minmax(0,1fr)}
.cards.me .rows{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:24px}
.cards.me .row{margin:0}
@media (max-width:760px){ .cards.me .rows{grid-template-columns:minmax(0,1fr);gap:16px} }
.cards.me + .roles{margin-top:18px}
/* caption under a framed screen, and the second frame of an article */
.scr-shot > figcaption.scr-cap{padding:12px 16px;border-top:1px solid var(--line);background:var(--p-card)}
.scr-shot > figcaption.scr-cap p{margin:0;font-size:14px;line-height:1.6;color:var(--muted)}
.scr-duo.ar32 img{aspect-ratio:3/2}

.case-hero .case-subtitle{margin:16px 0 16px;max-width:36ch;font-size:clamp(20px,2.4vw,28px);line-height:1.3;font-weight:700;color:var(--ink);text-wrap:balance}
