html{scroll-padding-top:100px;}
.case-main{background:var(--bg);color:var(--ink);}
.case-hero{position:relative;overflow:hidden;padding:clamp(116px,15vh,164px) 0 clamp(40px,6vw,72px);}
.case-breadcrumb{display:inline-flex;align-items:center;gap:9px;margin-bottom:clamp(30px,3.6vw,42px);padding:9px 16px 9px 13px;border:1px solid var(--line-strong);border-radius:999px;color:var(--ink);font-family:var(--sans);font-size:14px;font-weight:560;letter-spacing:-.01em;background:rgba(255,255,255,.03);transition:border-color .2s ease,color .2s ease,background .2s ease;}
.case-breadcrumb:hover{border-color:var(--accent);color:var(--accent);background:rgba(var(--accent-rgb),.06);}
.case-breadcrumb b{font-size:17px;line-height:1;}
.case-eyebrow{margin:0 0 clamp(12px,1.1vw,16px);color:var(--accent);font-family:var(--mono);font-weight:650;text-transform:uppercase;}
.case-title{margin:0;max-width:15ch;font-weight:520;text-wrap:balance;}
.case-title em{color:var(--accent);font-family:var(--serif);font-style:italic;font-weight:400;}
.case-deck{max-width:58ch;margin:clamp(20px,2.2vw,28px) 0 0;color:#b4b1bb;font-size:clamp(17px,1.5vw,21px);line-height:1.62;}
.case-meta{display:grid;grid-template-columns:repeat(4,1fr);margin-top:clamp(38px,4.2vw,56px);overflow:hidden;border:1px solid var(--line);background:rgba(255,255,255,.025);}
.case-meta div{min-width:0;padding:18px 20px;}
.case-meta div + div{border-left:1px solid var(--line);}
.case-meta dt{color:#b4b1bb;font-family:var(--mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;}
.case-meta dd{margin:7px 0 0;font-size:14px;font-weight:600;}
.case-body{margin:0 auto;}
.case-body h2{margin:58px 0 18px;font-weight:540;}
.case-body h2:first-child{margin-top:0;}
.case-body h3{margin:40px 0 12px;font-weight:620;}
.case-body p{margin:0 0 20px;color:#b4b1bb;font-size:17px;line-height:1.76;}
.case-body strong{color:var(--ink);font-weight:650;}
.case-note{margin:26px 0;padding:20px 24px;border-left:2px solid var(--accent);border-radius:0 var(--radius-sm) var(--radius-sm) 0;background:rgba(var(--accent-rgb),.05);color:#c7c4ce;font-size:15px;}
.case-note strong{color:var(--ink);}
.case-figure{margin:40px 0;overflow:hidden;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);}
.case-figure img{width:100%;}
.case-figure figcaption{padding:13px 18px;border-top:1px solid var(--line);color:#a6a3ad;font-size:13px;}
.case-evidence{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;margin:clamp(24px,3vw,36px) 0 clamp(30px,4.5vw,52px);}
.evidence-card{padding:24px;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);}
.evidence-card strong{display:block;font-family:var(--serif);font-size:33px;font-weight:400;color:var(--accent);letter-spacing:-.03em;line-height:1;}
.evidence-card span{display:block;margin-top:12px;color:#aaa7b2;font-size:13.5px;line-height:1.6;}
/* Inline prose link. Uses the lens accent, so it recolours with the viewpoint. */
.case-inline-link{color:var(--accent);border-bottom:1px solid rgba(var(--accent-rgb),.4);transition:border-color .2s ease;}
.case-inline-link:hover{border-bottom-color:var(--accent);}
.case-inline-link:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:2px;}
.case-inline-link .ico{width:12px;height:12px;margin-left:5px;vertical-align:-1px;}

/* Published skills, presented as what they are: repositories. Compact, no
   description (the prose above already gave it), whole card is the link. */
.case-repos{display:grid;grid-template-columns:repeat(auto-fit,minmax(275px,1fr));gap:14px;margin:46px 0 8px;}
.repo-card{display:grid;gap:15px;align-content:start;padding:20px 22px 18px;border:1px solid var(--line);border-radius:var(--radius-sm);
  background:var(--panel);transition:border-color .25s ease,transform .25s var(--ease),background .25s ease;}
.repo-card:hover{transform:translateY(-3px);border-color:rgba(var(--accent-rgb),.5);
  background:linear-gradient(140deg,rgba(var(--accent-rgb),.08),var(--panel) 58%);}
.repo-card:focus-visible{outline:2px solid var(--accent);outline-offset:3px;}
.repo-card__head{display:flex;align-items:center;gap:9px;min-width:0;font-family:var(--mono);font-size:15px;color:var(--ink);}
.repo-card__head svg{flex:none;opacity:.75;}
.repo-card__head b{font-weight:700;overflow-wrap:anywhere;}
.repo-card__owner{color:var(--muted);}
.repo-card__go{display:inline-flex;align-items:center;gap:7px;color:var(--muted);font-size:13px;font-weight:650;transition:color .25s ease;}
.repo-card:hover .repo-card__go{color:var(--accent);}
@media (prefers-reduced-motion: reduce){.repo-card,.repo-card__go{transition:none;}
  .repo-card:hover{transform:none;}}

/* Paper card: same shape as the homepage skill cards, where the icon gets its
   own bordered tile above the title, rather than sitting inline with a path
   the way the repo cards do. In a .paper-cards row they sit side by side where
   there is room and stack on a phone; they stretch to equal height, so the
   action line is pushed to the bottom of each. */
.paper-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px;margin:30px 0 0;}
.paper-cards .paper-card{margin:0;}
.paper-card{display:flex;flex-direction:column;margin:30px 0 0;padding:clamp(24px,3vw,32px);
  border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);color:inherit;text-decoration:none;
  transition:border-color .25s ease,transform .25s var(--ease);}
.paper-card:hover{border-color:var(--accent);transform:translateY(-2px);}
.paper-card:focus-visible{outline:2px solid var(--accent);outline-offset:3px;}
.paper-icon{display:grid;place-items:center;width:46px;height:46px;margin-bottom:20px;
  border:1px solid var(--line-strong);border-radius:13px;color:var(--accent);}
.paper-card__title{font-size:19px;font-weight:600;letter-spacing:-.02em;line-height:1.32;color:var(--ink);}
.paper-card__blurb{margin-top:9px;color:var(--muted);font-size:15px;line-height:1.6;}
.paper-card__go{display:inline-flex;align-items:center;gap:7px;margin-top:auto;padding-top:20px;color:var(--accent);font-size:14.5px;font-weight:560;}

/* Introduces the press pills. Body-text face and size, from .case-body p. */
.case-press__intro{margin:30px 0 0!important;}
.case-press{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px;}
.case-press a{display:inline-flex;align-items:center;gap:8px;padding:9px 15px;border:1px solid var(--line);border-radius:999px;color:#bbb8c2;font-size:13px;transition:border-color .2s,color .2s;}
.case-press a:hover{border-color:var(--accent);color:var(--ink);}
.case-body .button{margin-top:8px;}
.case-next{border-top:1px solid var(--line);background:var(--bg);}
.case-next a{display:grid;grid-template-columns:minmax(0,auto) auto;width:min(calc(100% - 56px),var(--shell));margin-inline:auto;}
.case-next .k{color:#96939e;font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;}
.case-next h3{font-weight:520;transition:color .25s ease;}
.case-next a:hover h3{color:var(--accent);}
.case-next .arrow{display:inline-flex;color:var(--accent);transform:translateY(-.14em);transition:transform .3s var(--ease);}
.case-next .arrow svg{width:26px;height:26px;display:block;}
.case-next a:hover .arrow{transform:translate(6px,-.14em);}
@media (max-width: 820px){
  .case-hero{padding-top:96px;}
  .case-meta{grid-template-columns:1fr 1fr;}
  .case-meta div:nth-child(3),.case-meta div:nth-child(4){border-top:1px solid var(--line);}
  .case-meta div:nth-child(3){border-left:0;}
  .case-evidence{grid-template-columns:1fr;}
  /* .case-next a keeps its two columns here on purpose. Collapsing it to 1fr
     dropped the arrow onto its own row below the title, which desktop never
     does. The title track shrinks and wraps instead; the arrow's 26px svg is
     its own min-content, so it can't be squeezed away. */
}
/* case title = the page's primary statement, same weight as the homepage hero */
.case-title{font-size:var(--fs-display);line-height:1.04;letter-spacing:-.022em;}

/* in-article section headings read at article scale, not homepage-section scale
   (which dwarfed the lead statement and turned the page into a stack of giant
   headlines) */
.case-body h2{scroll-margin-top:100px;font-size:clamp(25px,2.5vw,32px);line-height:1.18;letter-spacing:-.022em;}

/* "next project" is a display title inside a section, not a page title */
.case-next h3{margin:11px 0 0;font-size:var(--fs-h3-lg);line-height:1.1;letter-spacing:-.014em;}

/* body subheads sit at card level */
.case-body h3{font-size:var(--fs-h3);line-height:1.16;letter-spacing:-.01em;}

/* shared vertical rhythm with the homepage sections */
.case-body{max-width:772px;padding:var(--section-pad) 0;}
.case-next a{justify-content:start;align-items:end;gap:clamp(14px,1.6vw,22px);padding:calc(var(--section-pad) * .62) 0;}

/* radii: .case-meta bypassed the tokens */
.case-meta{backdrop-filter:blur(12px);border-radius:var(--radius);}

/* =========================================================================
   Branding case: moodboard gallery. Column masonry keeps each piece at its
   natural aspect ratio (no cropping — important for logos), mixing flat brand
   assets and photographic mockups gracefully. #case-body images stay
   right-clickable (the site-wide protection exempts them).
   ========================================================================= */
.brand-gallery{columns:3;column-gap:14px;margin:28px 0 8px;}
.brand-gallery figure{margin:0 0 14px;
  border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);}
.brand-gallery img{display:block;}
@media (max-width: 915px){.brand-gallery{columns:2;}}
@media (max-width: 560px){.brand-gallery{columns:1;}}

/* per-brand summary tags */
.brand-tags{display:flex;flex-wrap:wrap;gap:8px;margin:14px 0 20px;padding:0;list-style:none;}
.brand-tags li{padding:6px 12px;border:1px solid var(--line-strong);border-radius:999px;
  color:var(--muted);text-transform:uppercase;}
.brand-tags li[data-todo]{border-style:dashed;opacity:.7;}
[data-todo]{opacity:.7;}

/* gallery figures are clickable → lightbox */
.brand-gallery img{width:100%;height:auto;cursor:zoom-in;}

/* =========================================================================
   Lightbox: full-screen viewer with per-brand prev/next.
   ========================================================================= */
.lightbox{position:fixed;inset:0;z-index:9000;display:flex;align-items:center;justify-content:center;
  /* Vertical padding tracks vh, not vw, so a wide but short window keeps its
     height for the image. */
  padding:clamp(12px,2vh,40px) clamp(16px,4vw,64px);background:rgba(6,6,10,.94);backdrop-filter:blur(8px);}
.lightbox[hidden]{display:none;}
.lightbox .lb-stage{margin:0;max-width:100%;max-height:100%;display:flex;flex-direction:column;align-items:center;gap:14px;}
/* No pixel cap: width:auto never upscales past an image's natural size, so a
   1100px ceiling only ever shrank the large ones. 92vw leaves room for the
   prev/next buttons (52px + 18px offset) on either side. */
.lightbox img{max-width:92vw;max-height:84vh;width:auto;height:auto;object-fit:contain;
  border-radius:10px;box-shadow:0 30px 90px rgba(0,0,0,.6);}
.lightbox .lb-count{color:var(--muted);font-size:12px;letter-spacing:.04em;}
.lb-btn{position:absolute;display:grid;place-items:center;width:52px;height:52px;border:1px solid var(--line-strong);
  border-radius:50%;background:rgba(20,20,28,.7);color:var(--ink);font-size:26px;line-height:1;cursor:pointer;
  transition:background .2s ease,border-color .2s ease,transform .2s var(--ease);}
.lb-btn:hover{background:rgba(30,30,40,.92);border-color:var(--accent);}
/* display:grid above beats the hidden attribute's UA display:none, so prev/next
   would stay visible (and focusable) on a single-image viewer without this. */
.lb-btn[hidden]{display:none;}
.lb-btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px;}
.lb-close{top:clamp(14px,3vw,28px);right:clamp(14px,3vw,28px);font-size:30px;}
.lb-prev{left:clamp(10px,2.5vw,32px);top:50%;transform:translateY(-50%);}
.lb-next{right:clamp(10px,2.5vw,32px);top:50%;transform:translateY(-50%);}
.lb-prev:hover{transform:translateY(-50%) scale(1.06);}
.lb-next:hover{transform:translateY(-50%) scale(1.06);}
@media (max-width: 560px){.lb-prev{left:8px;}
  .lb-next{right:8px;}
  .lb-btn{width:44px;height:44px;}}
@media (prefers-reduced-motion: reduce){.lb-btn{transition:none;}}

/* =========================================================================
   Full-width hero banner: the project image sits behind the hero content,
   edge to edge, with a legibility scrim that also fades into the page below.
   ========================================================================= */
.case-hero-media{position:absolute;inset:0;z-index:0;}
.case-hero-media img{width:100%;height:100%;object-fit:cover;object-position:center;}
.case-hero-media::after{content:"";position:absolute;inset:0;background:
  linear-gradient(90deg, rgba(8,8,12,.9) 0%, rgba(8,8,12,.62) 46%, rgba(8,8,12,.3) 100%),
  linear-gradient(180deg, rgba(8,8,12,.55) 0%, rgba(8,8,12,.12) 26%, rgba(8,8,12,.86) 82%, var(--bg) 100%);}
.case-hero > .shell{position:relative;z-index:1;}

@media (max-width: 720px){
  /* On a phone the hero stacks breadcrumb, eyebrow, title, deck and the meta table
     into one narrow column, so almost the whole image sits under text. The old .2
     stop at 22% landed exactly where the title and deck start. Keep a window of the
     image at the very top, then go dark and stay dark. */
  .case-hero-media::after{background:
    linear-gradient(180deg,
      rgba(8,8,12,.55) 0%,
      rgba(8,8,12,.42) 15%,
      rgba(8,8,12,.88) 40%,
      rgba(8,8,12,.96) 72%,
      var(--bg) 100%);}
}

/* more breathing room between brand blocks (a gallery is always followed by
   the next brand's heading) */
.brand-gallery + h2{margin-top:clamp(60px,8vw,104px);}

/* gallery hover: lift + zoom + shadow only — no colour, to avoid clashing with
   each brand's own palette */
.brand-gallery figure{break-inside:avoid;overflow:hidden;transition:transform .3s var(--ease),box-shadow .3s ease,border-color .3s ease;}
.brand-gallery figure:hover{transform:translateY(-4px);box-shadow:0 20px 46px rgba(0,0,0,.5);border-color:var(--line-strong);}
.brand-gallery img{transition:transform .5s var(--ease);}
.brand-gallery figure:hover img{transform:scale(1.045);}
@media (prefers-reduced-motion: reduce){
  .brand-gallery figure,.brand-gallery img{transition:none;}
  .brand-gallery figure:hover{transform:none;}
  .brand-gallery figure:hover img{transform:none;}
}

/* full-width process strip, between the story and the applications */
.case-experiments{margin:8px 0 26px;overflow:hidden;border:1px solid var(--line);
  border-radius:var(--radius-sm);background:var(--panel);}
.case-experiments img{display:block;width:100%;height:auto;}

/* per-brand link button spacing */
.brand-link{margin:6px 0 24px;}

/* =========================================================================
   Case layout: executive read, sticky TOC rail, AI card.
   ========================================================================= */
/* Executive read — a scannable lead under the hero */
.case-exec-band{padding:clamp(30px,4vw,48px) 0 0;}
.case-exec{max-width:74ch;padding:clamp(22px,3vw,30px) 0;border-top:1px solid var(--line);}
.case-exec-label{margin:0 0 12px;color:var(--accent);font-family:var(--mono);font-size:12.5px;letter-spacing:.07em;text-transform:uppercase;}
.case-exec-text{margin:0;font-size:clamp(18px,1.7vw,22px);line-height:1.5;color:var(--ink);}

/* Two-column: sticky TOC on the left, content on the right */
.case-content{padding:clamp(44px,6vw,84px) 0;}
.case-layout{display:grid;grid-template-columns:186px minmax(0,1fr);gap:clamp(34px,5vw,76px);align-items:start;}
.case-toc{position:sticky;top:100px;align-self:start;}
.case-toc-label{margin:0 0 12px;color:#a7a4ae;font-family:var(--mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase;}
.case-toc ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;border-left:1px solid var(--line);}
.case-toc a{display:block;padding:8px 0 8px 16px;margin-left:-1px;border-left:2px solid transparent;
  color:var(--muted);font-size:14.5px;line-height:1.4;text-decoration:none;transition:color .2s ease,border-color .2s ease;}
.case-toc a:hover{color:var(--ink);}
.case-toc a.is-active{color:var(--ink);border-left-color:var(--accent);}

/* content column: the body fills the column, with prose capped to a
   comfortable measure */
.case-layout .case-body{max-width:none;margin:0;padding:0;}
.case-layout #case-body > p,
.case-layout #case-body > h2,
.case-layout #case-body > h3,
.case-layout #case-body > .case-note,
.case-layout #case-body > .brand-tags,
.case-layout #case-body > .case-press{max-width:68ch;}

/* AI prompt card at the bottom of the content */
.case-ai{margin:clamp(44px,6vw,72px) 0 0;padding:clamp(26px,3.5vw,40px);border:1px solid var(--line-strong);
  border-radius:var(--radius);background:radial-gradient(circle at 100% 0%, rgba(var(--accent-rgb),.10), transparent 62%), var(--panel);}
.case-ai .case-ai-eyebrow{margin:0;color:var(--accent);font-family:var(--mono);font-size:13px;font-weight:650;letter-spacing:.07em;text-transform:uppercase;}
.case-ai .case-ai-title{margin:6px 0 0;font-size:clamp(24px,2.6vw,34px);font-weight:600;letter-spacing:-.02em;line-height:1.16;}
.case-ai .case-ai-title em{color:var(--accent);font-style:normal;}
.case-ai .case-ai-body{margin:16px 0 24px;max-width:56ch;color:var(--muted);font-size:15px;line-height:1.62;}

/* On desktop the TOC is a sidebar: "On this page" is a visible label, the list
   is always shown, and the disclosure is inert. Forcing the <ol> visible means
   the TOC survives even if JS never sets [open]. */
@media (min-width: 916px){
  .case-toc-d > summary{list-style:none;cursor:default;pointer-events:none;}
  .case-toc-d > summary::-webkit-details-marker{display:none;}
  .case-toc-d > ol{display:flex;}
}

/* Below the nav breakpoint the article gets much taller, which is exactly when
   section shortcuts matter most — so the TOC becomes a sticky collapsible bar
   rather than disappearing. It sits just under the fixed header. */
@media (max-width: 915px){
  /* block, not a 1-column grid: a grid item's sticky containing block is its own
     grid area, so the bar would pin to its row and never follow the reader down */
  .case-layout{display:block;}
  /* Same material as the scrolled nav pill: translucent, blurred, hairline border.
     A solid slab reads as a second header competing with the real one. */
  .case-toc{position:sticky;top:78px;z-index:6;margin:0 0 26px;
    border:1px solid var(--line);border-radius:var(--radius-sm);
    background:rgba(17,17,26,.55);
    backdrop-filter:saturate(165%) blur(24px);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.07),0 10px 34px rgba(0,0,0,.32);}
  /* Closed it is a pill, like the nav; open it squares off to hold the list.
     No transition here: <details> shows its content instantly, so animating the
     radius leaves an oval wrapped around an already-visible menu. */
  .case-toc:has(.case-toc-d:not([open])){border-radius:999px;}
  .case-toc-d > summary{display:flex;align-items:center;justify-content:space-between;gap:12px;
    min-height:48px;padding:11px 20px;margin:0;cursor:pointer;list-style:none;}
  .case-toc-d > summary::-webkit-details-marker{display:none;}
  .case-toc-d > summary::after{content:"";flex:none;width:8px;height:8px;
    border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
    transform:translateY(-2px) rotate(45deg);transition:transform .2s ease;}
  .case-toc-d[open] > summary::after{transform:translateY(1px) rotate(-135deg);}
  /* the desktop rail and its indent make no sense inside a pill */
  .case-toc-d > ol{max-height:52vh;padding:0 20px 14px;border-left:0;
    overflow-y:auto;overscroll-behavior:contain;}
  .case-toc a{padding:9px 0;margin-left:0;border-left:0;}
  .case-toc a.is-active{color:var(--accent);}
  /* anchors must clear both the header and the sticky bar */
  html{scroll-padding-top:150px;}
}
@media (prefers-reduced-motion: reduce){
  .case-toc-d > summary::after{transition:none;}
}

/* looping brand reel as the hero background */
.case-hero-vid{display:block;width:100%;height:100%;object-fit:cover;object-position:center;}
/* App screens in one device frame, stepped through with arrows.
   A row of four sat each screen at ~178px wide; these are 393x852 exports, so
   width drives height hard and they were too small to read. One frame at 340px
   is legible, and the row's full width goes to the controls instead. A frame at
   the content width would be 772x1674, taller than most viewports.
   Rows are explicit: with sparse auto-placement the frame claimed row 1 / col 2
   and the cursor could not go back for col 1, so the arrows wrapped underneath. */
/* Same shell as .case-figure so the caption reads as every other caption on the
   site: bordered panel, caption attached below a hairline. */
/* Side columns are 1fr rather than auto, and there is no justify-content:center.
   Centring the tracks collapsed the grid to its content width, so the caption's
   grid-column:1/-1 spanned only the arrows-plus-phone and its hairline stopped
   short of the panel edges. Equal flexible gutters centre the phone just the
   same while the row actually reaches both sides. */
/* --phone-w is derived from viewport HEIGHT, not width: phones are more than
   twice as tall as they are wide, so height is what limits how big the device
   can be while staying fully in view. The 400px ceiling keeps it modest on
   large screens. */
.case-phone-carousel{--phone-w:clamp(220px,calc(40vh - 64px),400px);
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,var(--phone-w)) minmax(0,1fr);grid-template-rows:auto auto;
  align-items:center;gap:clamp(6px,2vw,22px);margin:44px 0 46px;padding:36px 0 0;
  overflow:hidden;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);}

/* Casing. The rail is four stacked gradients: a vertical sheen, two edge
   highlights where a machined band catches light, and the base metal. Button
   positions follow a real handset (action ~15%, volume pair ~21% and ~28% on the
   left, power ~22% on the right) rather than being bunched at the top. */
.phone__frame{grid-column:2;grid-row:1;position:relative;display:block;padding:11px;border-radius:52px;
  background:
    linear-gradient(180deg,rgba(255,255,255,.10) 0%,rgba(255,255,255,0) 14%,rgba(255,255,255,0) 86%,rgba(255,255,255,.06) 100%),
    linear-gradient(90deg,rgba(255,255,255,.22) 0%,rgba(255,255,255,0) 4%,rgba(255,255,255,0) 96%,rgba(255,255,255,.18) 100%),
    linear-gradient(148deg,#6a6a78 0%,#31313c 16%,#1b1b23 42%,#15151c 58%,#34343f 84%,#70707e 100%);
  box-shadow:0 34px 80px rgba(0,0,0,.6),0 3px 8px rgba(0,0,0,.65),
    inset 0 1px 0 rgba(255,255,255,.26),inset 0 -1px 0 rgba(255,255,255,.10);}
/* Inner lip: a real bezel steps down to the glass rather than butting into it. */
/* z-index 0, under the screen. At 2 this opaque fill sat ON TOP of the glass and
   blanked every screenshot. It only ever needs to show in the 5px band around it. */
.phone__frame::before{content:"";position:absolute;inset:6px;border-radius:47px;
  background:#050508;box-shadow:inset 0 0 0 1px rgba(0,0,0,.95),inset 0 0 6px rgba(0,0,0,.8);pointer-events:none;z-index:0;}
.phone__buttons::before,.phone__buttons::after{content:"";position:absolute;left:-2.5px;width:3px;
  border-radius:3px 1px 1px 3px;background:linear-gradient(180deg,#7b7b8a,#2b2b35 60%,#4a4a57);box-shadow:-1px 0 2px rgba(0,0,0,.5);}
.phone__buttons::before{top:21%;height:6%;}
/* volume up */
.phone__buttons::after{top:28.5%;height:6%;}
/* volume down */
.phone__frame .phone__buttons{position:absolute;inset:0;}
/* action button (left, above the volume pair) */
.phone__frame .phone__buttons i{position:absolute;left:-2.5px;top:14.5%;width:3px;height:4%;
  border-radius:3px 1px 1px 3px;background:linear-gradient(180deg,#7b7b8a,#2b2b35);}
/* power (right) */
.phone__frame::after{content:"";position:absolute;top:22%;right:-2.5px;width:3px;height:9.5%;
  border-radius:1px 3px 3px 1px;background:linear-gradient(180deg,#7b7b8a,#2b2b35 60%,#4a4a57);box-shadow:1px 0 2px rgba(0,0,0,.5);}
/* Dynamic island. The exports include an iOS status bar with the time at the
   left and the indicators at the right, so the centre is free and the island
   covers nothing. */
.phone__island{position:absolute;top:1.35%;left:50%;transform:translateX(-50%);z-index:3;
  width:31%;height:4.1%;border-radius:999px;background:#000;box-shadow:0 0 0 .5px rgba(255,255,255,.06);}
/* Fixed aspect so the frame cannot resize as screens are stepped through. */
/* #EDEAEB is the app's own canvas colour. The exports carry transparent rounded
   corners, so a dark screen showed through them as black wedges as slides moved. */
.phone__screen{position:relative;display:block;overflow:hidden;border-radius:41px;background:#EDEAEB;aspect-ratio:var(--screen-ar,393/852);z-index:1;}
/* bottom:-1px, not 0. The screen's height comes from aspect-ratio, so it lands
   on a fractional pixel; the images sat a sliver short of it and the #EDEAEB
   backing showed as a white hairline along the bottom edge. The extra pixel is
   clipped by the screen's overflow:hidden. */
.phone__track{position:absolute;inset:0 0 -1px;display:flex;width:100%;
  transition:transform 420ms cubic-bezier(.32,.72,0,1);will-change:transform;}
.phone__track img{flex:0 0 100%;width:100%;height:100%;object-fit:cover;cursor:zoom-in;}
.phone-nav{grid-row:1;display:inline-flex;align-items:center;justify-content:center;width:46px;height:46px;border-radius:50%;
  border:1px solid var(--line-strong);background:rgba(20,20,28,.7);color:var(--ink);cursor:pointer;
  transition:background .2s ease,border-color .2s ease,transform .2s var(--ease);}
.phone-nav:hover{background:rgba(30,30,40,.92);border-color:var(--accent);transform:scale(1.06);}
.phone-nav--prev{grid-column:1;justify-self:end;}
.phone-nav--next{grid-column:3;justify-self:start;}

.case-chart{color:var(--ink-5);}

/* A small portrait source (a phone photo of a sketch sheet) would be upscaled
   and soft at full column width; this keeps it near its real size. */
.case-figure--narrow img{display:block;width:auto;max-width:min(100%,420px);margin:26px auto;border-radius:8px;}

/* Page-structure comparison: two ordered stacks, stacked on a phone. */
.stack-cmp{display:grid;grid-template-columns:1fr;gap:26px;}
.stack-cmp__title{margin:0 0 12px!important;color:var(--ink)!important;font-size:15.5px!important;font-weight:650;}
.stack-cmp__list{list-style:none;display:grid;gap:6px;margin:0;padding:0;}
.stack-cmp__item{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;
  border:1px solid var(--line-strong);border-radius:10px;color:var(--ink-2);font-size:15px;line-height:1.35;}
.stack-cmp__item.is-tagged{border-color:var(--accent);}
.stack-cmp__item em{flex:none;color:var(--accent);font-size:12.5px;font-style:normal;font-weight:620;letter-spacing:.02em;}
@media (min-width:560px){.stack-cmp{grid-template-columns:1fr 1fr;gap:28px;}}

/* Interactive bar chart. Rows are focusable, so hover and keyboard focus share
   one state: the row lifts, the rest of the chart dims, and the value grows. */
.hbar{--lead:var(--accent);}
.hbar__q{margin:0 0 4px!important;color:var(--ink)!important;font-size:17px!important;font-weight:600;line-height:1.35!important;}
.hbar__list{list-style:none;margin:18px 0 0;padding:0;display:grid;gap:5px;}
.hbar__row{display:grid;grid-template-columns:minmax(0,1fr);gap:5px;padding:8px 10px;
  border-radius:9px;cursor:default;transition:background .22s ease,opacity .22s ease,transform .22s var(--ease);}
.hbar__label{color:var(--ink-2);font-size:15px;line-height:1.3;}
.hbar__track{position:relative;display:block;height:14px;border-radius:4px;background:rgba(255,255,255,.06);overflow:hidden;}
.hbar__track i{position:absolute;inset:0 auto 0 0;display:block;width:var(--v);border-radius:4px;
  background:var(--ink-6);transform:scaleX(0);transform-origin:left;
  transition:transform .7s var(--ease) calc(var(--i) * 70ms),background .22s ease;}
.hbar.is-in .hbar__track i{transform:scaleX(1);}
.hbar__row.is-lead .hbar__track i{background:var(--lead);}
.hbar__val{color:var(--muted);font-family:var(--sans);font-size:13px;font-weight:600;letter-spacing:.01em;transition:color .22s ease;}
/* Two columns once there is room for the label beside the bar. */
@media (min-width:560px){
  .hbar__row{grid-template-columns:minmax(0,210px) minmax(0,1fr) 46px;align-items:center;gap:16px;}
  .hbar__label{text-align:right;}
  .hbar__val{text-align:left;}
}
/* Dimming keys off a hovered or focused ROW, not off the chart. With
   .hbar:hover, passing the cursor over the title, the legend or the footnote
   dimmed every bar for no reason. Browsers without :has() simply never dim,
   which is the right way for this to fail. */
/* :not() on the dim rule, not a louder un-dim rule: :has() carries the
   specificity of its argument, so .hbar:has(.hbar__row:hover) .hbar__row is
   (0,4,0) and beat the (0,3,0) rule meant to keep the hovered bar bright.
   Excluding the hovered row from the dimming sidesteps the fight entirely. */
.hbar:has(.hbar__row:hover) .hbar__row:not(:hover),
.hbar:has(.hbar__row:focus-visible) .hbar__row:not(:focus-visible){opacity:.42;}
.hbar .hbar__row:hover,.hbar .hbar__row:focus-visible{opacity:1;background:rgba(255,255,255,.05);transform:translateX(3px);outline:none;}
.hbar .hbar__row:focus-visible{box-shadow:0 0 0 2px var(--accent);}
.hbar .hbar__row:hover .hbar__val,.hbar .hbar__row:focus-visible .hbar__val{color:var(--ink);}
.hbar .hbar__row:hover .hbar__track i,.hbar .hbar__row:focus-visible .hbar__track i{background:var(--accent);}
.hbar__legend{display:flex;flex-wrap:wrap;gap:8px 20px;margin:10px 0 0!important;font-size:0!important;}
.hbar__key{display:inline-flex;align-items:center;gap:8px;color:var(--muted);font-size:13.5px;}
.hbar__key::before{content:"";width:11px;height:11px;border-radius:3px;background:var(--ink-6);}
.hbar__key.is-lead::before{background:var(--accent);}
/* Sample comparison: a block per demographic, a labelled row per category,
   one bar per sample. */
/* The legend needs air before the first block under it. */
.hbar__legend + .hbar__block{margin-top:30px;}
.hbar__block + .hbar__block{margin-top:30px;}
.hbar__bname{margin:0 0 10px!important;color:var(--ink)!important;font-size:15.5px!important;font-weight:650;letter-spacing:.01em;}
.hbar__row--pair{align-items:center;}
.hbar__pair{display:grid;gap:5px;min-width:0;}
.hbar__sub{display:grid;grid-template-columns:minmax(0,1fr) 42px;align-items:center;gap:10px;}
.hbar__sub .hbar__track i{background:var(--ink-6);}
.hbar__sub.is-lead .hbar__track i{background:var(--accent);}
@media (min-width:560px){
  .hbar__row--pair{grid-template-columns:minmax(0,190px) minmax(0,1fr);}
  .hbar__row--pair .hbar__label{text-align:right;}
}
.hbar .hbar__row--pair:hover .hbar__sub .hbar__track i,.hbar .hbar__row--pair:focus-visible .hbar__sub .hbar__track i{background:var(--ink-3);}
.hbar .hbar__row--pair:hover .hbar__sub.is-lead .hbar__track i,.hbar .hbar__row--pair:focus-visible .hbar__sub.is-lead .hbar__track i{background:var(--accent);}
.hbar__note{margin:18px 0 0!important;color:var(--muted)!important;font-size:13px!important;line-height:1.55!important;}
@media (prefers-reduced-motion: reduce){
  .hbar__track i{transition:background .22s ease;transform:scaleX(1);}
  .hbar__row{transition:none;}
  .hbar .hbar__row:hover{transform:none;}
}

/* A 2017 Android, for screenshots of one. Three things had to change from the
   current-iPhone frame above, which Green Wallet keeps untouched: 16:9 instead
   of 19.5:9; square screen corners, because these exports have none of their
   own and a 41px radius was slicing the interface off; and real top and bottom
   bezels, which is where a phone of that year put its earpiece and its chin.
   The clamp is its own because a 16:9 body is shorter, so it can be wider
   before the figure stops fitting on one screen. */
.case-phone-carousel--classic{--phone-w:clamp(220px,calc(45vh - 62px),330px);}
.case-phone-carousel--classic .phone__frame{padding:15% 4.5%;border-radius:7%/3.6%;}
.case-phone-carousel--classic .phone__frame::before{inset:14.2% 3.6%;border-radius:2px;}
.case-phone-carousel--classic .phone__screen{border-radius:2px;}
/* The action-button sliver is reused as the earpiece slit; a 2017 handset had
   no third button to draw. */
.case-phone-carousel--classic .phone__frame .phone__buttons i{left:50%;top:6.6%;width:20%;height:1%;
  transform:translateX(-50%);border-radius:999px;background:#0a0a0f;box-shadow:inset 0 1px 1px rgba(0,0,0,.9);}
.case-phone-carousel--classic .phone__buttons::before{top:27%;height:6.4%;}
.case-phone-carousel--classic .phone__buttons::after{top:34.5%;height:6.4%;}
.case-phone-carousel--classic .phone__frame::after{top:28%;height:10%;}

/* Same shell and type as every other caption on the site: hairline across the
   full panel width, #a6a3ad / 13px / 1.55. */
.case-phone-carousel figcaption{grid-column:1 / -1;grid-row:2;display:flex;flex-wrap:wrap;align-items:baseline;
  justify-content:center;gap:6px 14px;margin-top:36px;padding:13px 18px;border-top:1px solid var(--line);
  color:#a6a3ad;font-size:13px;line-height:1.55;text-align:center;}
.phone-caption{max-width:52ch;}
.phone-count{color:var(--muted);font-size:12px;letter-spacing:.04em;}
@media (max-width: 560px){
  /* Under 560px the gutters give up their flex so the screen keeps the width,
     and the floor drops because the arrows now eat 88px of a ~390px viewport. */
  .case-phone-carousel{--phone-w:clamp(170px,calc(40vh - 64px),400px);
    grid-template-columns:auto minmax(0,var(--phone-w)) auto;gap:4px;}
  .phone-nav{width:40px;height:40px;}
  .phone__frame{padding:9px;border-radius:44px;}
  .phone__frame::before{inset:6px;border-radius:39px;}
  .phone__screen{border-radius:35px;}
}
@media (prefers-reduced-motion: reduce){
  .phone-nav{transition:none;}
  .phone-nav:hover{transform:none;}
  .phone__track{transition:none;}
}
.case-figure img{cursor:zoom-in;}

/* Thumbnails are wrapped in a real <button> by case.js so they can be reached and
   activated from the keyboard. The button is a transparent shell — it must add no
   box of its own — but it does take the focus ring. */
.zoom-btn{display:block;width:100%;padding:0;border:0;background:none;color:inherit;font:inherit;cursor:zoom-in;}
.zoom-btn img{display:block;width:100%;}
.zoom-btn:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:4px;}

.case-role-compare{margin:38px 0 52px;}
/* auto-fit rather than a fixed 2, so a comparator can carry two or three roles */
.case-role-tabs{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:8px;margin-bottom:10px;}
.case-role-tabs button{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:15px 18px;border:1px solid var(--line);border-radius:var(--radius-sm);background:transparent;color:var(--muted);font-family:var(--sans);text-align:left;cursor:pointer;transition:border-color .2s,background .2s,color .2s;}
.case-role-tabs button:hover{border-color:var(--line-strong);color:var(--ink);}
.case-role-tabs button[aria-selected="true"]{border-color:var(--accent);background:rgba(var(--accent-rgb),.07);color:var(--ink);}
.case-role-tabs button strong{font-size:15px;}
.case-role-tabs button span{font-family:var(--mono);font-size:11.5px;letter-spacing:.05em;text-transform:uppercase;}
/* var(--panel), matching .case-figure, so the panel edge never shows as a pale
   hairline. */
.case-role-stage{overflow:hidden;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);}
.case-role-stage figure{margin:0;}
/* Reserve the box before the image has an intrinsic size, so switching tabs
   never collapses the stage. `auto` keeps the real ratio once loaded; the
   fallback ratio only covers the first frame. */
.case-role-stage img{display:block;width:100%;aspect-ratio:auto 1440 / 1056;cursor:zoom-in;}
.case-role-stage figcaption{padding:13px 18px;border-top:1px solid var(--line);background:var(--panel);color:#aaa7b2;font-size:13px;line-height:1.55;}
.case-role-note{margin:12px 0 0!important;color:var(--ink)!important;font-size:14px!important;font-weight:650;text-align:center;}

.case-comparisons{display:flex;flex-direction:column;gap:clamp(42px,6vw,66px);margin:40px 0 54px;}
.case-comparison{margin:0;}
.case-comparison>header{display:grid;grid-template-columns:42px minmax(0,1fr);gap:14px;align-items:start;margin-bottom:18px;}
.case-comparison>header>span{color:var(--accent);font-family:var(--mono);font-size:11.5px;font-weight:700;letter-spacing:.08em;}
.case-comparison h3{margin:0 0 8px!important;font-size:var(--fs-h3)!important;line-height:1.14!important;}
.case-comparison header p{margin:0!important;max-width:62ch!important;font-size:14px!important;line-height:1.6!important;}

.case-hero-pair{display:grid;width:100%;height:100%;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;background:#11121a;}
.case-hero-pair img{width:100%;height:100%;object-fit:cover;}

@media (max-width: 720px){
  .case-role-tabs{grid-template-columns:1fr;}
}
@media (prefers-reduced-motion: reduce){.case-role-tabs button {transition:none;}}

/* Before/after wipe slider (MyAssets): drag the divider to compare the two states.
   Same-dimension images; the "before" is clipped to the divider position. */
/* No header above a bare slider, so it does not need to clear one. */
.case-comparisons--bare{margin:34px 0 46px;}
/* Same treatment as .case-figure figcaption: attached to the image inside one
   bordered box, not floating under it. The caption has to be a sibling of .ba
   rather than a child (the slider's children are absolutely positioned and would
   clip it), so the bottom of the box is reassembled here. Scoped through
   .case-comparison to outrank .case-body p, which is (0,1,1). */
.case-comparisons--bare .ba{margin-top:0;border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0;}
.case-comparison .ba__caption{margin:0!important;padding:13px 18px!important;max-width:none!important;
  border:1px solid var(--line-strong);border-top:1px solid var(--line);border-radius:0 0 16px 16px;
  background:var(--panel);color:#a6a3ad!important;font-family:var(--sans);font-size:13px!important;line-height:1.55!important;}
.ba{position:relative;margin-top:20px;border-radius:16px;overflow:hidden;border:1px solid var(--line-strong);background:#000;line-height:0;touch-action:pan-y;}
.ba__img{display:block;width:100%;height:auto;}
.ba__before{position:absolute;inset:0;overflow:hidden;clip-path:inset(0 calc(100% - var(--ba-pos,50%)) 0 0);}
.ba__before img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
.ba__divider{position:absolute;top:0;bottom:0;left:var(--ba-pos,50%);width:2px;margin-left:-1px;background:rgba(255,255,255,.92);box-shadow:0 0 0 1px rgba(0,0,0,.3);pointer-events:none;}
.ba__grip{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%;background:#fff;color:#111;box-shadow:0 4px 16px rgba(0,0,0,.5);}
.ba__tag{position:absolute;top:14px;z-index:2;padding:5px 11px;border-radius:999px;font-family:var(--mono);font-size:12px;letter-spacing:.05em;text-transform:uppercase;color:#fff;background:rgba(8,8,12,.62);backdrop-filter:blur(6px);pointer-events:none;line-height:1;}
.ba__tag--before{left:14px;}
.ba__tag--after{right:14px;}
.ba__range{position:absolute;inset:0;width:100%;height:100%;margin:0;padding:0;background:transparent;border:0;-webkit-appearance:none;appearance:none;cursor:ew-resize;opacity:0;}
.ba__range:focus-visible{opacity:1;outline:2px solid var(--accent);outline-offset:2px;}
.ba__range::-webkit-slider-thumb{-webkit-appearance:none;width:48px;height:48px;}
.ba__range::-moz-range-thumb{width:48px;height:48px;border:0;background:transparent;}
@media (max-width: 560px){ .ba__grip{width:34px;height:34px;} }
/* Phone variant: the slider takes the size of the screen it compares instead of
   the text column, so a portrait mockup is not blown up to the full width. It
   sits in the same panel as the phone carousel, and its caption uses the same
   shell as every other caption: hairline across the full panel width,
   #a6a3ad / 13px / 1.55. */
.case-comparisons--phone{margin:44px 0 46px;}
.case-comparisons--phone .case-comparison{display:flex;flex-direction:column;align-items:center;padding:36px 0 0;
  overflow:hidden;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);}
.case-comparisons--phone .ba{width:min(calc(100% - 36px),330px);border-bottom:1px solid var(--line-strong);border-radius:22px;}
.case-comparisons--phone .ba__tag{top:12px;padding:4px 9px;font-size:11px;}
.case-comparisons--phone .ba__tag--before{left:12px;}
.case-comparisons--phone .ba__tag--after{right:12px;}
.case-comparisons--phone .case-comparison .ba__caption{align-self:stretch;margin:36px 0 0!important;padding:13px 18px!important;
  border:0;border-top:1px solid var(--line);border-radius:0;background:none;text-align:center;
  font-size:13px!important;line-height:1.55!important;max-width:none!important;}

/* Before/after: expand affordance + fullscreen overlay (drag still works). */
.ba__expand{position:absolute;right:14px;bottom:14px;z-index:4;display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:11px;border:1px solid rgba(255,255,255,.28);background:rgba(8,8,12,.6);color:#fff;cursor:pointer;backdrop-filter:blur(6px);transition:background .2s ease,border-color .2s ease;}
.ba__expand:hover{background:rgba(8,8,12,.88);border-color:#fff;}
.ba-fs{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:clamp(12px,2vh,40px) clamp(16px,4vw,56px);background:rgba(6,6,10,.93);backdrop-filter:blur(8px);}
.ba-fs[hidden]{display:none;}
/* The wrapper shrink-wraps the image and never caps it independently: if it
   were narrower than the image, .ba{overflow:hidden} would crop the sides.
   Only the image is constrained, and 90vw always fits inside .ba-fs's
   clamp(16px,4vw,56px) padding. */
.ba--fs{width:auto;max-width:none;margin:0;border-radius:12px;background:#000;}
.ba--fs .ba__img{width:auto;height:auto;max-height:86vh;max-width:90vw;}
/* .ba is position:relative and comes later in the DOM, so it would paint over the
   arrows wherever the two overlap on a narrower window. */
.ba-fs .lb-btn{z-index:3;}
/* Positioned rather than placed in flow: .ba-fs centers the slider as its only
   flex item, so a counter in the flow would push it off-center. */
.ba-fs__count{position:absolute;left:50%;bottom:clamp(10px,2vh,22px);transform:translateX(-50%);
  color:var(--muted);font-size:12px;letter-spacing:.04em;pointer-events:none;}
.ba-fs__close{position:absolute;top:18px;right:20px;z-index:2;width:44px;height:44px;border-radius:50%;border:1px solid rgba(255,255,255,.28);background:rgba(8,8,12,.6);color:#fff;font-size:24px;line-height:1;cursor:pointer;}
.ba-fs__close:hover{background:rgba(8,8,12,.9);border-color:#fff;}
body.ba-fs-open{overflow:hidden;}
.case-eyebrow,
.case-meta dt,
.case-next .k,
.case-exec-label,
.case-toc-label,
.case-ai .case-ai-eyebrow,
.case-role-tabs button span,
.case-comparison > header > span,
.ba__tag { font-family: var(--sans); }

.case-eyebrow { font-size: 13px; letter-spacing: .07em; }
.case-exec-label, .case-toc-label, .case-ai .case-ai-eyebrow { font-size: 13px; letter-spacing: .05em; }
.case-meta dt, .case-next .k { font-size: 12.5px; letter-spacing: .05em; }
.brand-tags li{ font-family: var(--sans); font-size: 12px; letter-spacing: .04em; font-weight: 600; }

/* Logo system as one specimen sheet rather than five identical tiles: the
   backdrops were knocked out of the exports so each mark sits on the page. */
.logo-spec{display:grid;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));gap:10px;align-items:stretch;
  margin:40px 0;padding:26px 18px;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);}
.logo-spec__item{display:flex;flex-direction:column;align-items:center;gap:24px;margin:0;}
.logo-spec__art{display:flex;align-items:center;justify-content:center;flex:1;width:100%;min-height:104px;}
/* Each mark carries its own --logo-h, set so all four cover the same amount of
   ink (~6800px^2 measured from their alpha channels). A shared max-height can't
   work here: the aspect ratios run 0.86 to 4.71, so one height made the wide
   header four times the width of the stacked lockup. Add a mark -> measure it
   and give it a height, don't fall back on the default. */
.logo-spec__art img{max-width:100%;max-height:var(--logo-h,86px);width:auto;height:auto;cursor:zoom-in;}
/* The lightbox wraps every zoomable image in .zoom-btn, which is block and
   100% wide — that would left-align the marks against the tile. Re-centre it. */
.logo-spec__art .zoom-btn{display:flex;align-items:center;justify-content:center;width:100%;height:100%;}
.logo-spec figcaption{color:var(--muted);font-size:12px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;text-align:center;}
@media (max-width: 560px){.logo-spec{grid-template-columns:repeat(2,1fr);gap:22px;}}

/* Palette drawn from the report's own hex values, so it stays crisp and weighs
   nothing. Each tone prints its hex; the label flips to dark on the light end. */
/* .case-figure--inset gives a non-image figure the same panel + caption strip. */
.case-figure--inset{margin:34px 0;}
.case-figure--inset > div{padding:24px 22px;}
.swatches{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px;}
.swatches__family{display:flex;flex-direction:column;gap:6px;min-width:0;}
.swatches__name{color:var(--ink-2);font-size:12.5px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;}
.swatch{display:flex;align-items:flex-end;height:58px;padding:8px 11px;border-radius:7px;background:var(--sw);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.07);}
/* Label colour comes from --fg, set per swatch from its relative luminance, so
   the light end of Sand, Sky and Fog gets ink instead of unreadable white. */
.swatch i{font-size:13px;font-style:normal;font-weight:500;letter-spacing:.02em;color:var(--fg,rgba(255,255,255,.92));}
@media (max-width: 560px){.swatches{grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;}
  .swatch{height:50px;}}
