/* ════════════════════════════════════════════════════════════════════
   case.css — the case-study page type. Shared by every case page.

   The homepage is ink-on-dark; a case is the opposite: a long read on
   a light ground, with the product's own dark screens dropped in as full-bleed
   bands. Everything here is built on the tokens in styles.css — this
   file only re-grounds them and adds the page-type layout.
   ════════════════════════════════════════════════════════════════════ */

.case-page{
  background:var(--ground);color:var(--ink);
  /* Re-ground the shared tokens: ink on paper instead of paper on ink.
     The ground is a cool light grey, not a warm bone — bone reads as a
     borrowed look, and against a yellow-green accent a slightly blue
     neutral makes the lime read as signal rather than as decoration.
     Cards sit a clear step above it in pure white, which is what lets the
     bento blocks separate without heavy borders. */
  --ground:#F5F7F9;
  --card:#FFFFFF;
  --muted:rgba(9,10,11,.64);
  --dim:rgba(9,10,11,.46);
  --line:rgba(9,10,11,.12);
  --line-2:rgba(9,10,11,.07);
  --tint:rgba(9,10,11,.04);
  --radius-section:clamp(20px,2.4vw,32px);
  --radius-card:16px;
  --radius-media:12px;
  --radius-inner:8px;
  --radius-label:4px;
  --col:1240px;
}
.case-page ::selection{background:var(--ink);color:var(--lime)}

/* The browser paints the overscroll gutter from html, not body. */
html:has(.case-page){background:#F5F7F9}

/* One header across the whole site. The homepage nav is a translucent dark
   bar, which works because it blurs near-black; over a light ground the same
   resolves to mid-grey and the dark body text bleeds into it. So the nav
   re-declares the ink-ground tokens and raises the alpha until it matches
   the homepage bar exactly. */
.case-page .nav{
  color:var(--paper);
  background:rgba(9,10,11,.94);
  --muted:rgba(240,241,235,.62);
  --dim:rgba(240,241,235,.40);
  --line:rgba(240,241,235,.13);
  --line-2:rgba(240,241,235,.06);
  --tint:rgba(240,241,235,.05);
}
.case-page .nav.tight{background:rgba(9,10,11,.97)}
.case-page .nav ::selection{background:var(--lime);color:var(--ink)}

.wrap{width:min(100% - var(--pad) * 2,var(--col));margin-inline:auto}
.case-page section{position:relative}

/* ── shared type ─────────────────────────────────────────────────── */
.eyebrow-m{
  font-family:var(--mono);font-size:var(--t-lab);letter-spacing:.16em;
  text-transform:uppercase;color:var(--dim);
  display:flex;align-items:center;gap:10px;margin-bottom:24px;
}
.eyebrow-m::before{content:"";width:6px;height:6px;background:var(--lime);border-radius:1px;flex:none}
.h2{font-size:clamp(28px,4vw,60px);letter-spacing:-.05em;line-height:.98;font-weight:500;text-wrap:balance}
.h3{font-size:clamp(20px,1.9vw,30px);letter-spacing:-.04em;line-height:1.08;font-weight:500}
.lede{color:var(--muted);font-size:clamp(15px,1.14vw,19px);line-height:1.55;max-width:62ch}
.body{color:var(--muted);font-size:var(--t-body);line-height:1.62;max-width:64ch}
.lab-m{font-family:var(--mono);font-size:var(--t-lab);letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}

/* ════════════════════════════════════════════════════════════════════
   1 · HERO
   ════════════════════════════════════════════════════════════════════ */
.chero{
  position:relative;padding-block:clamp(40px,6vh,80px) clamp(48px,7vh,96px);
  border-bottom:1px solid var(--line);
}
.chero-grid{
  display:grid;grid-template-columns:minmax(0,1fr);
  gap:clamp(32px,4vw,64px);align-items:center;
}
@media(min-width:1000px){
  .chero-grid{grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr)}
}
.chero-copy{position:relative;z-index:2}
.chero h1{
  font-size:clamp(34px,5.2vw,82px);letter-spacing:-.055em;line-height:.94;
  font-weight:500;text-wrap:balance;margin:0 0 clamp(18px,2vw,28px);
}
.chero h1 em{font-family:var(--serif);font-style:italic;letter-spacing:-.02em}
.chero-meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:clamp(24px,3vw,36px)}
.chip{
  font-family:var(--mono);font-size:var(--t-lab);letter-spacing:.12em;
  text-transform:uppercase;padding:7px 11px;border:1px solid var(--line);
  border-radius:var(--radius-label);color:var(--muted);
}
.chip-fill{background:var(--lime);border-color:var(--lime);color:var(--ink)}

/* The hero carries the mockup made for this case — the same tablet-and-phone
   composition the homepage card uses, so the case opens on an image the
   reader may already have seen and recognises. It ships on transparent
   ground with its own shadow baked in, so nothing is drawn around it.
   --s is the entrance, --px/--py the live pointer rotation. */
.hero-mock{
  --s:0;--px:0;--py:0;
  position:relative;perspective:1700px;
}
.hero-mock img{
  width:100%;height:auto;display:block;
  transform:
    rotateY(calc(var(--px) * -5deg))
    rotateX(calc(var(--py) * 3deg))
    translateY(calc((1 - var(--s)) * 30px))
    scale(calc(.95 + .05 * var(--s)));
  opacity:calc(.15 + .85 * var(--s));
}

/* ── 2 · facts strip ─────────────────────────────────────────────── */
.facts{
  display:grid;gap:clamp(20px,2.4vw,34px);
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  padding-block:clamp(34px,4vw,54px);
  border-bottom:1px solid var(--line);
}
.fact b{display:block;font-size:clamp(19px,1.7vw,26px);letter-spacing:-.04em;font-weight:500;margin-bottom:7px}
.fact p{color:var(--muted);font-size:13px;line-height:1.5}
.fact .lab-m{margin-bottom:12px;display:block}

/* ── 3 · sections ────────────────────────────────────────────────── */
.chap{padding-block:clamp(64px,8vw,120px)}
/* NB: not `ch` here. ch resolves against this element's own font (15px),
   not the 60px h2 inside it, which would collapse the column to ~450px
   and break the heading one word per line. The lede caps itself in ch,
   where the unit does resolve against the right font. */
.chap-head{max-width:min(100%,880px);margin-bottom:clamp(32px,4vw,56px)}
.chap-head .lede{margin-top:clamp(16px,1.8vw,24px)}

/* problem / need columns */
.two{display:grid;gap:clamp(28px,4vw,64px);grid-template-columns:minmax(0,1fr)}
@media(min-width:860px){.two{grid-template-columns:repeat(2,minmax(0,1fr))}}
.stack{list-style:none;display:flex;flex-direction:column;gap:2px}
.stack li{
  display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:start;
  padding:15px 0;border-top:1px solid var(--line-2);
  font-size:var(--t-body);line-height:1.5;
}
.stack li:first-child{border-top:0}
.mk{
  width:20px;height:20px;border-radius:var(--radius-label);flex:none;margin-top:1px;
  display:grid;place-items:center;font-family:var(--mono);font-size:11px;line-height:1;
}
.mk-bad{background:rgba(255,46,136,.14);color:#C40E5F}
.mk-ok{background:var(--lime);color:var(--ink)}
.col-title{margin-bottom:18px}

/* ── Context → Solutions cover ────────────────────────────────────
   Match the homepage transition exactly: Context holds under the nav while
   the opaque Solutions sheet continues in normal document flow and covers
   it. No translated facsimile of the effect — the previous chapter really
   is sticky. Keeping this pair explicit prevents a stack effect elsewhere. */
#context{
  position:sticky;
  top:var(--context-pin-top,var(--nav-h));
  margin-bottom:var(--context-hold,0px);
  z-index:0;
  background:var(--ground);
}
#context.is-covered{visibility:hidden}
#solutions{
  --cover:0;
  position:relative;
  z-index:2;
  border-radius:var(--radius-section) var(--radius-section) 0 0;
}
#solutions::before{
  content:"";
  position:absolute;inset:0 0 auto;height:1px;z-index:4;pointer-events:none;
  background:linear-gradient(90deg,transparent,
    rgba(200,255,56,calc((1 - var(--cover)) * .62)),transparent);
  box-shadow:0 0 calc((1 - var(--cover)) * 18px)
             rgba(200,255,56,calc((1 - var(--cover)) * .28));
}

.band{
  background:var(--ink);color:var(--paper);
  --muted:rgba(240,241,235,.62);
  --dim:rgba(240,241,235,.42);
  --line:rgba(240,241,235,.13);
  --line-2:rgba(240,241,235,.07);
  padding-block:clamp(52px,7vw,104px);
}
.band .mk-bad{background:rgba(255,46,136,.2);color:#FF6FA9}
/* ── the three-step feature sequence ─────────────────────────────── */
.steps3{
  display:grid;gap:clamp(18px,2.2vw,30px);
  grid-template-columns:minmax(0,1fr);counter-reset:s;
}
@media(min-width:860px){.steps3{grid-template-columns:repeat(3,minmax(0,1fr))}}
.step3{
  background:rgba(240,241,235,.04);border:1px solid rgba(240,241,235,.10);
  border-radius:var(--radius-card);padding:clamp(16px,1.6vw,22px);
  display:flex;flex-direction:column;gap:14px;
}
/* the three shots have very different aspects (1.30, 0.89, 2.81) — a common
   media box keeps the cards the same height instead of leaving a hole
   under the short one. */
.step3 img{width:100%;height:auto;display:block;border-radius:var(--radius-inner);
  aspect-ratio:4/3;object-fit:contain;object-position:center}
.step3 .n{
  font-family:var(--mono);font-size:var(--t-lab);letter-spacing:.14em;
  color:var(--lime);
}
.step3 p{font-size:13px;line-height:1.55;color:var(--muted);margin-top:auto}
.step3 b{display:block;font-size:15px;font-weight:500;letter-spacing:-.02em;color:var(--paper);margin-bottom:6px}

/* ── highlight: one number, on lime ──────────────────────────────── */
.hit{
  background:var(--lime);color:var(--ink);border-radius:var(--radius-card);
  padding:clamp(24px,3.2vw,44px);
  display:grid;gap:clamp(16px,2vw,32px);align-items:center;
  grid-template-columns:minmax(0,1fr);
}
@media(min-width:760px){.hit{grid-template-columns:auto minmax(0,1fr)}}
.hit b{font-size:clamp(48px,7vw,104px);letter-spacing:-.055em;line-height:.85;font-weight:500}
.hit p{font-size:clamp(15px,1.2vw,19px);line-height:1.45;letter-spacing:-.02em;max-width:46ch}
.hit small{display:block;margin-top:12px;font-family:var(--mono);font-size:var(--t-lab);letter-spacing:.1em;opacity:.62}

/* ── services rendered ─────────────────────────────────────────────
   Was a single grey slab split by 1px gaps — and the cards on top of it had
   lost their fill to a renamed token, so the slab was all you saw. Now four
   real cards on the same surface system as the bento, given their own
   texture by an oversized ghost index so the third grid on the page does
   not read as a repeat of the first two. */
.did{display:grid;gap:clamp(10px,1.1vw,14px);grid-template-columns:minmax(0,1fr)}
@media(min-width:760px){.did{grid-template-columns:repeat(2,minmax(0,1fr))}}
.did-item{
  position:relative;overflow:hidden;
  background:var(--card);border-radius:var(--radius-card);
  box-shadow:0 1px 2px rgba(9,10,11,.05),0 12px 30px -16px rgba(9,10,11,.14);
  padding:clamp(24px,2.6vw,38px);
  transition:transform .5s var(--ez),box-shadow .5s var(--ez);
}
.did-item:hover{
  transform:translateY(-3px);
  box-shadow:0 1px 2px rgba(9,10,11,.05),0 22px 44px -18px rgba(9,10,11,.2);
}
.did-item .n{
  position:absolute;top:clamp(2px,.6vw,10px);right:clamp(14px,1.6vw,24px);
  font-family:var(--mono);font-size:clamp(48px,5vw,78px);line-height:1;
  letter-spacing:-.04em;color:rgba(9,10,11,.055);pointer-events:none;
}
.did-item h4{
  display:flex;align-items:center;gap:10px;
  font-size:clamp(17px,1.5vw,23px);letter-spacing:-.035em;font-weight:500;
  margin-bottom:11px;
}
.did-item h4::before{content:"";width:7px;height:7px;border-radius:2px;flex:none;background:var(--lime)}
.did-item p{color:var(--muted);font-size:var(--t-body);line-height:1.6;max-width:42ch}

/* ── pull quote ──────────────────────────────────────────────────── */
.pull{
  font-family:var(--serif);font-style:italic;
  font-size:clamp(24px,3.4vw,52px);letter-spacing:-.03em;line-height:1.08;
  max-width:22ch;margin-block:clamp(8px,1vw,16px);
}

/* ── next case ───────────────────────────────────────────────────── */
.next{
  display:flex;flex-wrap:wrap;gap:20px;align-items:end;justify-content:space-between;
  padding-block:clamp(44px,5vw,72px);
}
.next-link{display:block}
.next-link .lab-m{display:block;margin-bottom:12px}
.next-link b{font-size:clamp(24px,3vw,46px);letter-spacing:-.05em;line-height:1;font-weight:500}
.next-link:hover b{text-decoration:underline;text-underline-offset:6px;text-decoration-thickness:1px}

/* ── reveal ──────────────────────────────────────────────────────── */
/* Reveals carry a slight scale as well as a lift — coming up *and* forward
   reads as arriving, where a pure translate reads as sliding. Bento children
   stagger so a row assembles rather than appearing whole. */
.case-page [data-reveal]{
  opacity:0;transform:translateY(28px) scale(.975);
  transition:opacity .8s var(--ez),transform .95s var(--ez);
}
.case-page [data-reveal].in{opacity:1;transform:none}
.bento > [data-reveal]:nth-child(2){transition-delay:.07s}
.bento > [data-reveal]:nth-child(3){transition-delay:.14s}
.bento > [data-reveal]:nth-child(4){transition-delay:.21s}
.bento > [data-reveal]:nth-child(5){transition-delay:.28s}
.bento > [data-reveal]:nth-child(6){transition-delay:.35s}
.did > [data-reveal]:nth-child(2){transition-delay:.07s}
.did > [data-reveal]:nth-child(3){transition-delay:.14s}
.did > [data-reveal]:nth-child(4){transition-delay:.21s}

@media (prefers-reduced-motion:reduce){
  .case-page [data-reveal]{opacity:1;transform:none;transition:none}
  .hero-mock{--s:1}
  .hero-mock img{transform:none}
}

/* ── narrow ──────────────────────────────────────────────────────── */
@media(max-width:720px){
  .hit b{font-size:clamp(44px,15vw,72px)}
}

/* ════════════════════════════════════════════════════════════════════
   Bento — mixed-weight cards on a 6-column bed
   ════════════════════════════════════════════════════════════════════ */
.bento{display:grid;gap:clamp(10px,1.1vw,14px);grid-template-columns:repeat(6,minmax(0,1fr))}
/* No hairline. A 1px outline on a light ground is what a wireframe looks
   like; a card should be a surface lifted off the ground, so the ground goes
   a shade down and two soft shadows do the separating — a tight one for the
   edge, a wide one for the lift. */
.bx{
  background:var(--card);border:0;border-radius:var(--radius-card);
  box-shadow:0 1px 2px rgba(9,10,11,.05),0 12px 30px -16px rgba(9,10,11,.14);
  padding:clamp(20px,2.2vw,32px);grid-column:span 6;
  display:flex;flex-direction:column;
}
@media(min-width:820px){
  .bx-3{grid-column:span 3}
  .bx-2{grid-column:span 2}
  .bx-4{grid-column:span 4}
}
.bx-title{
  font-size:clamp(17px,1.5vw,23px);letter-spacing:-.04em;font-weight:500;
  margin-bottom:clamp(14px,1.5vw,20px);
}
.bx .lab-m{display:block;margin-bottom:12px}

.bx p{color:var(--muted);font-size:var(--t-body);line-height:1.6}
.bx-lime{background:var(--lime);color:var(--ink);box-shadow:0 12px 30px -16px rgba(120,160,20,.5)}
.bx-lime p,.bx-lime .lab-m{color:rgba(9,10,11,.72)}
.bx-num b{
  display:block;font-size:clamp(30px,3.4vw,54px);letter-spacing:-.055em;
  line-height:.9;font-weight:500;margin-bottom:10px;
}
/* on the ink band a shadow reads as nothing — there the fill does the work */
.band .bx{background:rgba(240,241,235,.055);box-shadow:none}
.band .bx-lime{background:var(--lime);color:var(--ink);box-shadow:none}
.band .bx-lime p,.band .bx-lime .lab-m{color:rgba(9,10,11,.72)}

/* ════════════════════════════════════════════════════════════════════
   Scroll-scrubbed product reveal
   Borrowed from how strong product landings stage a screenshot: it does
   not fade in, it rises and grows into frame as you scroll, and its lower
   edge dissolves into the section instead of stopping on a hard line.
   --r is 0 while the shot is still below the fold and 1 once it has
   settled; script writes it, CSS does the rest, so no layout ever runs.
   ════════════════════════════════════════════════════════════════════ */
.rise{--r:0}
.rise img{
  transform:translateY(calc((1 - var(--r)) * 34px)) scale(calc(.94 + .06 * var(--r)));
  transform-origin:50% 100%;
  transition:transform .1s linear;
}
/* the caption is not part of the rise — only the screen moves */
.bx-fig.rise figcaption{transform:none}
@media (prefers-reduced-motion:reduce){
  .rise img{transform:none;transition:none}
  #context{position:relative;top:0}
  #context.is-covered{visibility:visible}
  #solutions::before{display:none}
}

/* ════════════════════════════════════════════════════════════════════
   Scope grid — the dense feature list that backs up "20+ features"
   Three tabs cannot carry that claim on their own. Every item here is a
   surface visible in the product's own navigation, so the grid is
   evidence rather than a list of adjectives.
   ════════════════════════════════════════════════════════════════════ */
.scope{
  display:grid;gap:1px;background:var(--line);
  border-radius:var(--radius-card);overflow:hidden;
  /* fixed tracks, not auto-fit: nine items divide by three exactly, and
     auto-fit was landing on five columns and leaving a hole in row two */
  grid-template-columns:minmax(0,1fr);
  margin-top:clamp(28px,3.4vw,52px);
}
@media(min-width:560px){.scope{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(min-width:900px){.scope{grid-template-columns:repeat(3,minmax(0,1fr))}}
.scope-i{
  background:var(--card);padding:clamp(16px,1.7vw,24px);
  transition:background .4s var(--ez);
}
.scope-i:hover{background:#fff}
.band .scope{background:rgba(240,241,235,.1)}
.band .scope-i{background:var(--ink-1)}
.band .scope-i:hover{background:var(--ink-2)}
.scope-i b{
  display:flex;align-items:center;gap:9px;
  font-size:15px;font-weight:500;letter-spacing:-.03em;margin-bottom:7px;
}
.scope-i b::before{content:"";width:6px;height:6px;border-radius:2px;flex:none;background:var(--lime)}
.scope-i p{color:var(--muted);font-size:13px;line-height:1.5}
.scope-more{
  grid-column:1/-1;background:var(--lime);color:var(--ink);
  padding:clamp(16px,1.7vw,24px);
  font-size:15px;font-weight:500;letter-spacing:-.03em;
}
.band .scope-more{background:var(--lime);color:var(--ink)}

/* ════════════════════════════════════════════════════════════════════
   Before / after wipe
   Both captures are the same frame (2000x1136), so a wipe is the honest
   comparison: one screen, two states, and the reader controls where the
   line sits. The top layer is clipped rather than resized — clip-path on
   a full-bleed image costs nothing per frame and never reflows.
   ════════════════════════════════════════════════════════════════════ */
.wipe{
  --x:35%;
  position:relative;aspect-ratio:2000/1136;
  border-radius:var(--radius-media);overflow:hidden;
  cursor:ew-resize;touch-action:pan-y;user-select:none;-webkit-user-select:none;
  box-shadow:0 2px 6px rgba(9,10,11,.3),0 30px 64px -30px rgba(9,10,11,.55);
}
.wipe img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.wipe-top{position:absolute;inset:0;clip-path:inset(0 calc(100% - var(--x)) 0 0)}
.wipe-bar{
  position:absolute;top:0;bottom:0;left:var(--x);width:2px;margin-left:-1px;
  background:var(--lime);pointer-events:none;
}
.wipe-grip{
  position:absolute;top:50%;left:var(--x);transform:translate(-50%,-50%);
  width:clamp(38px,3.4vw,48px);aspect-ratio:1;border-radius:50%;
  background:var(--lime);color:var(--ink);display:grid;place-items:center;
  pointer-events:none;box-shadow:0 8px 22px -8px rgba(9,10,11,.7);
}
.wipe-grip svg{width:60%;height:60%;display:block}
.wipe-lab{
  position:absolute;top:clamp(10px,1.2vw,16px);
  font-family:var(--mono);font-size:var(--t-lab);letter-spacing:.16em;
  text-transform:uppercase;padding:6px 10px;border-radius:var(--radius-label);pointer-events:none;
}
.wipe-lab-b{left:clamp(10px,1.2vw,16px);background:var(--alert);color:#fff}
.wipe-lab-a{right:clamp(10px,1.2vw,16px);background:var(--lime);color:var(--ink)}
.wipe:focus-visible{outline:2px solid var(--lime);outline-offset:3px}
/* dragging must be 1:1, so no transition by default; .easing is switched on
   only for the intro nudge and for keyboard steps, where a jump would read
   as a glitch rather than as movement */
.wipe.easing .wipe-top{transition:clip-path .45s var(--ez)}
.wipe.easing .wipe-bar,.wipe.easing .wipe-grip{transition:left .45s var(--ez)}

.wipe-caps{
  display:grid;gap:clamp(18px,2.4vw,40px);grid-template-columns:minmax(0,1fr);
  margin-top:clamp(20px,2.4vw,32px);
}
@media(min-width:820px){.wipe-caps{grid-template-columns:repeat(2,minmax(0,1fr))}}
.wipe-cap .lab-m{display:block;margin-bottom:10px}
.wipe-cap p{color:var(--muted);font-size:var(--t-body);line-height:1.6}
.wipe-cap-b .lab-m{color:#FF6FA9}
.wipe-cap-a .lab-m{color:var(--lime)}

/* an image that fills its bento card instead of sitting inside its padding */
.bx-img{padding:0;overflow:hidden}
.bx-img img{width:100%;height:100%;object-fit:cover;display:block}


/* ── state screens ────────────────────────────────────────────────
   These captures have wildly different aspects (0.46 to 1.83). Cover
   would crop a confirmation dialog, which is the one thing you cannot do
   to a screen whose whole point is its wording — so the card sets the
   aspect, the image is contained inside it, and the letterbox is invisible
   because the card ground matches the product's own. */
.bx-shot{padding:clamp(10px,1vw,16px);background:#0D0F14;box-shadow:none}
.bx-shot img{width:100%;height:100%;object-fit:contain;display:block;border-radius:var(--radius-inner)}
.band .bx-shot{background:rgba(240,241,235,.05)}

/* ── plain strip ──────────────────────────────────────────────────
   A row of screens with no card around them. Bento was starting to be the
   answer to every section, which made them all look alike — this is the
   quiet alternative: equal heights, natural widths, sitting straight on
   the band. Heights match by capping the image, never by cropping: these
   captures have their own rounded corners and a crop cuts the curve. */
.strip{display:grid;gap:clamp(16px,2vw,32px);grid-template-columns:minmax(0,1fr)}
@media(min-width:760px){.strip{grid-template-columns:repeat(3,minmax(0,1fr));align-items:end}}
.strip figure{margin:0}
.strip img{
  display:block;margin-inline:auto;width:auto;max-width:100%;
  max-height:clamp(300px,38vw,520px);height:auto;border-radius:var(--radius-media);
}
.strip-cap{
  margin-top:clamp(16px,1.8vw,24px);color:var(--muted);
  font-size:var(--t-body);line-height:1.6;max-width:74ch;
}

/* ── figure cards ─────────────────────────────────────────────────
   A bento card that is an image with its own caption underneath, rather
   than a picture in one card and the words in another. The image height
   is capped and centred: that keeps rows level without setting a per-card
   aspect, and it stops the widest screen from filling its card edge to
   edge, which read as oversized. contain, never cover — these captures
   carry their own rounded corners. */
.bx-fig{padding:0;overflow:hidden;display:flex;flex-direction:column}
.bx-fig .shot-box{
  flex:1;min-height:0;display:grid;place-items:center;
  background:#0D0F14;padding:clamp(12px,1.4vw,22px);
}
.bx-fig img{
  max-width:100%;max-height:clamp(260px,32vw,440px);
  width:auto;height:auto;object-fit:contain;display:block;border-radius:var(--radius-inner);
}
.bx-fig figcaption{padding:clamp(16px,1.7vw,24px)}
.bx-fig figcaption b{
  display:block;font-size:clamp(15px,1.25vw,18px);font-weight:500;
  letter-spacing:-.03em;margin-bottom:8px;
}
.bx-fig figcaption p{color:var(--muted);font-size:13px;line-height:1.55}
.band .bx-fig .shot-box{background:rgba(240,241,235,.045)}

/* ── plain figures ────────────────────────────────────────────────
   An image with its caption and no card around it. Cards had become the
   answer to every section, which flattened the page — this is the same
   information with the chrome removed, so the screens sit straight on the
   ground and the sections stop looking alike. */
.figs{display:grid;gap:clamp(16px,1.8vw,28px);grid-template-columns:minmax(0,1fr)}
@media(min-width:900px){
  .figs{grid-template-columns:repeat(6,minmax(0,1fr));align-items:start}
  .f-4{grid-column:span 4}.f-3{grid-column:span 3}.f-2{grid-column:span 2}
}
/* Grid rows are rigid: with ragged tile heights the second row waits for the
   tallest tile in the first, which opens a hole under the short one. So the
   tiles live in two independent flex columns instead, and the columns are
   loaded so they finish within ~30px of each other. */
.fcol{display:flex;flex-direction:column;gap:clamp(16px,1.8vw,28px);min-width:0}
.fig{margin:0}
/* The image fills its column at its own aspect: no letterbox padding, no
   crop. Cropping is out because these captures carry their own rounded
   corners; letterboxing was what made the tiles feel loose. */
.fig-shot{display:block;border-radius:var(--radius-media);overflow:hidden;line-height:0}
.fig img{width:100%;height:auto;display:block;border-radius:var(--radius-media)}
.fig figcaption{margin-top:clamp(12px,1.2vw,16px)}
.fig figcaption b{display:block;font-size:clamp(15px,1.2vw,17px);font-weight:500;
  letter-spacing:-.03em;margin-bottom:6px}
/* the tile itself is the measure — a 46ch cap inside a wide tile just made
   the caption tall and left the column below it waiting */
.fig figcaption p{color:var(--muted);font-size:13px;line-height:1.5}

/* ── media card: a screen and its caption as one object ───────────
   The card carries one background; the bottom edge of the image dissolves
   into the caption through a masked blur and a fade to that same colour.
   Four galleries use it — DEX solutions 01 and 02, CR redesign proofs and
   mobile proofs — and everything that differs between them is a --mcard-*
   value. Defaults below are the DEX solution 01 numbers.
   Works on both shapes: figure > .fig-shot + figcaption, and .step3. */
.mcard{
  --mcard-bg:#141518;
  --mcard-inset:12px;
  --mcard-blur:4px;
  --mcard-blur-h:38px;
  --mcard-mask:.88;
  --mcard-fade-h:44px;
  --mcard-fade-mid:rgba(20,21,24,.38) 54%;
  --mcard-cap-pad:14px 20px 22px;
  overflow:hidden;border:1px solid rgba(240,241,235,.10);
  border-radius:var(--radius-card);background:var(--mcard-bg);
}
.mcard .fig-shot,.mcard .step3-shot{
  position:relative;margin:var(--mcard-inset) var(--mcard-inset) 0;
}
.mcard .fig-shot::before,.mcard .fig-shot::after,
.mcard .step3-shot::before,.mcard .step3-shot::after{
  content:"";position:absolute;inset:auto 0 0;pointer-events:none;
}
/* the blur is masked so it fades in going down. An unmasked backdrop-filter
   reads as a soapy band straight across the screen — that is the whole
   difference between a dissolve and a mistake. */
.mcard .fig-shot::before,.mcard .step3-shot::before{
  z-index:1;height:var(--mcard-blur-h);
  backdrop-filter:blur(var(--mcard-blur));
  -webkit-backdrop-filter:blur(var(--mcard-blur));
  -webkit-mask-image:linear-gradient(to bottom,transparent,rgba(0,0,0,var(--mcard-mask)));
          mask-image:linear-gradient(to bottom,transparent,rgba(0,0,0,var(--mcard-mask)));
}
/* always a few px taller than the blur, otherwise the blur's own edge shows */
.mcard .fig-shot::after,.mcard .step3-shot::after{
  z-index:2;height:var(--mcard-fade-h);
  background:linear-gradient(to bottom,transparent,var(--mcard-fade-mid),var(--mcard-bg));
}
/* the caption sits above both layers and on the same colour the fade ends
   on — one step apart and a seam appears across the card */
.mcard figcaption,.mcard .step3-copy{
  position:relative;z-index:3;margin:0;
  padding:var(--mcard-cap-pad);background:var(--mcard-bg);
}
.mcard figcaption b{font-size:17px}
.mcard figcaption p{font-size:13px;line-height:1.5}

/* Solution 01 media system: an 8-column primary story beside a 4-column
   supporting rail. The second level splits into three equal 4-column cards,
   which keeps the editorial rhythm deliberate without adding card chrome. */
.figs.s1-media-grid{
  display:flex;flex-direction:column;gap:clamp(28px,4vw,44px);
}
.s1-media-main,.s1-media-side{
  display:flex;flex-direction:column;gap:clamp(24px,2vw,32px);min-width:0;
}
.s1-media-main-bottom{
  display:grid;grid-template-columns:minmax(0,1fr);gap:clamp(24px,2vw,32px);
}
.s1-media-grid .fig-shot{
  aspect-ratio:16/10;border-radius:var(--radius-media);background:#101216;
}
.s1-media-grid .fig-shot img{
  width:100%;height:100%;object-fit:cover;border-radius:var(--radius-media);
}
.s1-media-grid .fig-terminal .fig-shot img{object-position:center top}
.s1-media-grid .fig-profile .fig-shot img{object-position:center}
.s1-media-grid .fig-wallet .fig-shot img{object-position:center top}
.s1-media-grid .fig-trade .fig-shot img{object-position:center top}
.s1-media-grid .fig-filters .fig-shot img{object-position:center 25%}

@media(min-width:900px){
  .figs.s1-media-grid{
    display:grid;grid-template-columns:minmax(0,2fr) minmax(250px,1fr);
    align-items:stretch;gap:clamp(16px,1.8vw,28px);
  }
  .s1-media-main,.s1-media-side{justify-content:space-between;height:100%}
  .s1-media-main-bottom{grid-template-columns:repeat(2,minmax(0,1fr))}
  .s1-media-grid .fig-terminal .fig-shot{aspect-ratio:2.72/1}
  .s1-media-grid .fig-profile .fig-shot{aspect-ratio:1.826/1}
  .s1-media-grid .fig-wallet .fig-shot{aspect-ratio:1.18/1}
  .s1-media-grid .fig-trade .fig-shot{aspect-ratio:1.72/1}
  .s1-media-grid .fig-filters .fig-shot{aspect-ratio:1.2/1}
}

@media(max-width:899px){
  .s1-media-grid .s1-media-main,
  .s1-media-grid .s1-media-side,
  .s1-media-grid .s1-media-main-bottom{display:contents}
  .s1-media-grid .fig-terminal{order:1}
  .s1-media-grid .fig-profile{order:2}
  .s1-media-grid .fig-wallet{order:3}
  .s1-media-grid .fig-trade{order:4}
  .s1-media-grid .fig-filters{order:5}
}

/* ── plain two-column lists ───────────────────────────────────────── */
.cols{display:grid;gap:clamp(28px,4vw,72px);grid-template-columns:minmax(0,1fr)}
@media(min-width:860px){.cols{grid-template-columns:repeat(2,minmax(0,1fr))}}
.cols h3{font-size:clamp(16px,1.3vw,19px);font-weight:500;letter-spacing:-.03em;margin-bottom:16px}

/* ── inline number row ────────────────────────────────────────────── */
.nums{
  display:grid;gap:clamp(20px,2.6vw,40px);
  grid-template-columns:repeat(auto-fit,minmax(170px,1fr));
  margin-top:clamp(32px,4vw,56px);padding-top:clamp(24px,2.8vw,38px);
  border-top:1px solid var(--line);
}
.nums b{display:block;font-size:clamp(28px,3.2vw,50px);letter-spacing:-.055em;
  line-height:.9;font-weight:500;margin-bottom:10px}
.nums-lead b{background:var(--lime);width:max-content;padding:2px 10px;border-radius:var(--radius-label)}
.nums .lab-m{line-height:1.5}

/* a labelled note: the label sits above its prose, not in a column beside it —
   as a two-column row it left a hole the width of the page between them */
.note{max-width:74ch;margin-top:clamp(28px,3.4vw,48px)}
.note .lab-m{display:block;margin-bottom:12px}
.note p{color:var(--muted);font-size:var(--t-body);line-height:1.62}

/* a caption under a full-width figure needs a reading measure of its own —
   46ch is right beside a tile, far too narrow under one */
/* `.fig figcaption p` is 0,2,1 and was beating `.fig-wide p` at 0,1,1, so the
   caption stayed at the narrow tile measure under a full-width figure */
.fig figcaption.fig-wide{max-width:76ch}
.fig figcaption.fig-wide p{max-width:none;font-size:var(--t-body);line-height:1.62}
.fig figcaption.fig-wide b{font-size:clamp(17px,1.4vw,21px);margin-bottom:9px}

/* Image-hover toolbars come from browser extensions, not from the page, so a
   page cannot switch them off outright. What it can do is stop the <img>
   being a hover/drag target at all, which is the hook most of them use.
   Nothing here needs its own pointer events — the wipe reads them on its
   container, so events pass straight through. */
.case-page img{
  pointer-events:none;
  -webkit-user-drag:none;user-select:none;-webkit-user-select:none;
}

/* ── one ink ground under all three solutions ─────────────────────
   The wrapper supplies the top and bottom air; the sections inside only
   need the rhythm between them, or the run would be padded twice at its
   own edges. */
#solutions{padding-block:0}
#solutions > .chap:first-of-type{padding-top:clamp(64px,8vw,120px)}
#solutions > .chap:last-of-type{padding-bottom:clamp(64px,8vw,120px)}
#solutions > .chap{padding-block:clamp(48px,6vw,88px)}
#solutions .chap-cont{padding-top:clamp(28px,3.4vw,48px)}

/* ── video ────────────────────────────────────────────────────────
   Same frame as a .fig image: the media box owns the radius, the video
   fills it. Autoplay is muted and looped, so it reads as a moving
   screenshot rather than something to operate — no controls, no sound.
   Under reduced motion the script pauses it and shows controls, so the
   flow stays reachable on demand. */
.vid{background:var(--ink);line-height:0}
.vid video{width:100%;height:auto;display:block;border-radius:var(--radius-media)}

/* ════════════════════════════════════════════════════════════════════
   Vertical tabs — many screens without a wall of screenshots
   Quiet by construction: hairline rows, a mono index that lights up, and
   the benefit line revealed only on the selected row. No boxes and no
   colour wash — a lime fill over a row was tried on the DEX case and read
   as muddy once the ground stopped being ink.
   ════════════════════════════════════════════════════════════════════ */
.vtabs{display:grid;gap:clamp(24px,3vw,48px);grid-template-columns:minmax(0,1fr)}
@media(min-width:960px){.vtabs{grid-template-columns:minmax(0,.4fr) minmax(0,1fr);align-items:start}}

.vtab-list{display:flex;flex-direction:column;border-top:1px solid var(--line)}
.vtab{
  width:100%;text-align:left;background:none;border:0;
  border-bottom:1px solid var(--line);color:inherit;cursor:pointer;
  padding:clamp(14px,1.5vw,20px) 0;
}
.vtab:focus-visible{outline:2px solid var(--lime);outline-offset:-2px}
.vtab-h{display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:baseline}
.vtab-n{
  font-family:var(--mono);font-size:10px;letter-spacing:.14em;color:var(--dim);
  padding:4px 6px;border-radius:var(--radius-label);transition:background .35s var(--ez),color .35s var(--ez);
}
.vtab[aria-selected="true"] .vtab-n{background:var(--lime);color:var(--ink)}
.vtab-t{
  font-size:clamp(16px,1.5vw,21px);font-weight:500;letter-spacing:-.035em;line-height:1.15;
  color:var(--muted);transition:color .35s var(--ez),transform .5s var(--ez);
}
.vtab:hover .vtab-t,.vtab[aria-selected="true"] .vtab-t{color:var(--paper)}
.vtab:hover .vtab-t{transform:translateX(5px)}
/* rows 0fr→1fr animates to the real height; a max-height guess never does */
.vtab-b{display:grid;grid-template-rows:0fr;transition:grid-template-rows .5s var(--ez)}
.vtab[aria-selected="true"] .vtab-b{grid-template-rows:1fr}
.vtab-b > span{overflow:hidden;display:block}
.vtab-b p{
  color:var(--muted);font-size:13px;line-height:1.55;max-width:46ch;
  padding-top:9px;opacity:0;transition:opacity .4s var(--ez);
}
.vtab[aria-selected="true"] .vtab-b p{opacity:1;transition-delay:.14s}

.vpanels{position:relative;aspect-ratio:4/3}
.vpanel{
  position:absolute;inset:0;opacity:0;visibility:hidden;
  background:rgba(240,241,235,.05);border-radius:var(--radius-media);
  padding:clamp(12px,1.4vw,22px);
  transition:opacity .45s var(--ez),visibility .45s;
}
.vpanel[aria-hidden="false"]{opacity:1;visibility:visible}
/* Two dead ends before this one. As a centred grid item the row was
   auto-sized, so height:100% hit a circular dependency and degraded to auto —
   the portrait screen overflowed by 438px. Making the image absolute did not
   help either: for a replaced element with width:auto the insets are ignored
   in favour of the intrinsic size. In normal flow inside a panel that already
   has a definite height, the percentage simply resolves. */
.vpanel img{width:100%;height:100%;object-fit:contain;display:block}
@media(max-width:959px){
  .vpanels{aspect-ratio:16/12}
  .vtab-b{grid-template-rows:1fr}      /* no room to hide the reason behind a click */
  .vtab-b p{opacity:1}
}
@media (prefers-reduced-motion:reduce){
  .vtab-b,.vpanel,.vtab-t{transition:none}
}
