/* ═══════════════════════════════════════════════════════════════════════
   The six watermelon.sh components, rebuilt for this site.

   They ship as React + Tailwind + framer-motion off a shadcn registry, and
   `npx shadcn add` wants a components.json, a React tree and a build step.
   This site has none of those — it is HTML, one stylesheet and plain JS, and
   that is the whole reason it loads the way it does. So each one was read and
   rebuilt here: same geometry, same motion, same states, no framework.

   What deliberately changed, and why:
     · Colours come from the site tokens, not zinc/neutral. Dropping in a
       second grey ramp next to --ink is how a page starts looking assembled
       rather than designed.
     · No dark variants. Every page here declares `color-scheme: light only`;
       a dark block that can never match is dead weight.
     · framer-motion springs are cubic-beziers with the same overshoot. The
       `layoutId` shared element in the pagination is done with FLIP, which is
       what the library does underneath anyway.

   Prefixes: cvep edit-profile · cvrd region dialog · cvlk licence key
             cvop option picker · cvpg pagination · dsb dashboard
   ═══════════════════════════════════════════════════════════════════════ */

/* The spring the originals use everywhere: stiffness 300, damping 20. This is
   that curve, and it is the only easing in this file that overshoots. */
:root{
  --cv-spring:cubic-bezier(.34,1.56,.64,1);
  --cv-ease:cubic-bezier(.22,.61,.36,1);
}

/* This file's icons are <i data-cvicon> holding an <svg>, the same shape
   site.css gives [data-icon]. It needs its own rule because ui.css loads on
   pages site.css's icon block does not reach, and because these carry an
   explicit pixel size rather than inheriting a font-size. */
/* The white these components paint on, and the ink they tint with — as
   tokens, so the studio's dark theme has one place to override and the
   public pages keep exactly the values they had. Deliberately not `--card`:
   that is #FCFBFE on the public site, a shade off this white, and reusing it
   would have quietly restyled every card in the shop. */
:root{--panel:#fff;--tint:20,26,60}

[data-cvicon]{
  display:inline-flex;align-items:center;justify-content:center;
  flex-shrink:0;line-height:0;width:20px;height:20px;
}
[data-cvicon]>svg{display:block;width:100%;height:100%}

/* ── shared overlay ─────────────────────────────────────────────────────
   Both dialogs use it. Backdrop fades, panel scales up out of a blur, and
   the whole thing is pointer-events:none until it is in, so a stray click
   during the 260ms cannot land on the row behind it. */
.cvov{
  position:fixed;inset:0;z-index:900;
  display:flex;align-items:center;justify-content:center;padding:16px;
  opacity:0;transition:opacity .22s var(--cv-ease);
}
.cvov.is-in{opacity:1}
.cvov__bd{
  position:absolute;inset:0;
  background:rgba(var(--tint),.28);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);
}
/* min()/percentage widths on the panel leave its intrinsic contribution
   indefinite, and this wrapper then stretched to the full overlay — which
   put every dialog against the left edge instead of centred. The panels
   below all carry a definite width plus max-width:100% for the same reason. */
.cvov__pnl{
  position:relative;z-index:1;flex:0 0 auto;max-width:100%;min-width:0;
  transform:scale(.94) translateY(18px);filter:blur(4px);
  transition:transform .34s var(--cv-spring),filter .24s var(--cv-ease);
}
.cvov.is-in .cvov__pnl{transform:none;filter:blur(0)}
@media (prefers-reduced-motion:reduce){
  .cvov,.cvov__pnl{transition-duration:.01ms}
}

/* ════════════════════════════════════════════ edit profile ══ */
/* The original is a two-pane sheet: the form on the left, a live preview of
   the card on the right, a dashed rule between them, and the last-updated
   stamp sitting opposite the buttons. All four survive. */
.cvep{
  width:720px;max-width:100%;max-height:calc(100dvh - 32px);
  display:flex;flex-direction:column;
  background:var(--card-2);border:1px solid var(--line);border-radius:26px;
  box-shadow:var(--sh-lg);overflow:hidden;
}
.cvep__h{
  display:flex;align-items:center;justify-content:space-between;
  padding:16px 22px;flex:none;
}
.cvep__h h2{margin:0;font-size:18px;font-weight:800;letter-spacing:-.01em}
.cvep__x{
  border:0;background:none;cursor:pointer;padding:6px;line-height:0;border-radius:10px;
  color:var(--ink-3);transition:color .2s,background .2s;
}
.cvep__x:hover{color:var(--ink);background:rgba(var(--tint),.06)}
.cvep__body{
  display:flex;flex:1;min-height:0;overflow:auto;
  background:var(--panel);border-top:1.6px solid var(--line);border-bottom:1.6px solid var(--line);
  border-radius:18px;
}
.cvep__form{flex:1;padding:22px;display:flex;flex-direction:column;gap:15px;min-width:0}
.cvep__rule{
  flex:none;width:0;border-left:1.6px dashed var(--line);margin:18px 0;
}
.cvep__prev{
  flex:1;padding:30px 22px;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:2px;min-width:0;
}
.cvep__l{display:flex;flex-direction:column;gap:6px}
.cvep__l>span{font-size:14px;font-weight:600;color:var(--ink-2)}
.cvep__l input,.cvep__l select{
  width:100%;padding:11px 14px;border-radius:14px;
  border:1.5px solid var(--line);background:var(--panel);color:var(--ink);
  font:inherit;font-size:15px;font-weight:600;outline:none;
  transition:border-color .2s,box-shadow .2s;
}
.cvep__l select{appearance:none;-webkit-appearance:none;padding-right:38px}
.cvep__l input:focus,.cvep__l select:focus{
  border-color:var(--ink);box-shadow:0 0 0 3px rgba(var(--tint),.07);
}
.cvep__row{display:flex;gap:14px;flex-wrap:wrap}
.cvep__row>*{flex:1;min-width:150px}
.cvep__wrap{position:relative}
.cvep__wrap>i[data-cvicon]{
  position:absolute;right:12px;top:50%;transform:translateY(-50%);
  pointer-events:none;color:var(--ink-3);line-height:0;
}
.cvep__pl{font-size:14px;font-weight:600;color:var(--ink-2);margin:0 0 14px}
.cvep__av{position:relative;margin-bottom:14px}
.cvep__av img{
  width:128px;height:128px;border-radius:50%;object-fit:cover;object-position:top;
  box-shadow:var(--sh-sm);outline:1px solid var(--line);display:block;background:var(--card-2);
}
.cvep__avb{
  position:absolute;right:0;bottom:0;padding:8px;border-radius:50%;line-height:0;
  background:var(--panel);border:1px solid var(--line);color:var(--ink-2);cursor:pointer;
  box-shadow:var(--sh-sm);transition:transform .2s var(--cv-spring),color .2s;
}
.cvep__avb:hover{transform:scale(1.08);color:var(--ink)}
.cvep__nm{margin:0;font-size:18px;font-weight:800;text-align:center;letter-spacing:-.01em}
.cvep__ti{margin:2px 0 14px;font-size:14px;color:var(--ink-2);text-align:center}
.cvep__chip{
  display:inline-flex;align-items:center;gap:7px;
  padding:5px 13px;border-radius:999px;font-size:12px;font-weight:600;
  background:var(--card-2);color:var(--ink-2);box-shadow:var(--sh-sm);
}
.cvep__f{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:16px 22px;flex:none;flex-wrap:wrap;
}
.cvep__st{font-size:13px;color:var(--ink-3)}
.cvep__st b{color:var(--ink-2);font-weight:700}
.cvep__acts{display:flex;gap:10px;margin-left:auto}
.cvep__b{
  padding:9px 20px;border-radius:999px;font:inherit;font-size:14px;font-weight:800;
  cursor:pointer;border:1.6px solid transparent;transition:transform .18s var(--cv-spring),background .2s;
}
.cvep__b:active{transform:scale(.96)}
.cvep__b--ghost{background:var(--card-2);border-color:var(--line);color:var(--ink)}
.cvep__b--ghost:hover{background:var(--panel)}
.cvep__b--go{background:var(--ink);color:#fff;box-shadow:var(--sh-sm)}
.cvep__b--go:hover{background:#000}
@media (max-width:720px){
  .cvep__body{flex-direction:column}
  .cvep__rule{width:auto;height:0;border-left:0;border-top:1.6px dashed var(--line);margin:0 18px}
  .cvep__prev{padding:24px}
}

/* ════════════════════════════════════════════ region dialog ══ */
/* Uniswap's region sheet: a pill that shows the current flag, and a tall
   searchable list with a tick against the one you are on. */
.cvrd{position:relative;display:inline-block}
.cvrd__t{
  display:inline-flex;align-items:center;gap:8px;
  padding:6px 10px 6px 6px;border-radius:999px;border:1px solid var(--line);
  background:var(--card-2);cursor:pointer;color:var(--ink);
  font:inherit;font-size:14px;font-weight:700;
  transition:background .2s,transform .18s var(--cv-spring);
}
.cvrd__t:hover{background:var(--panel)}
.cvrd__t:active{transform:scale(.97)}
.cvrd__t i[data-cvicon]{line-height:0;color:var(--ink-2)}
.cvrd__flag{
  width:24px;height:24px;border-radius:50%;object-fit:cover;flex:none;display:block;
  border:1px solid var(--line);background:var(--card-2);
}
.cvrd__pnl{
  display:flex;flex-direction:column;
  width:400px;max-width:100%;height:520px;max-height:calc(100dvh - 32px);
  background:var(--panel);border:1px solid var(--line);border-radius:28px;
  box-shadow:var(--sh-lg);overflow:hidden;
}
.cvrd__h{display:flex;align-items:center;justify-content:space-between;padding:16px 16px 8px}
.cvrd__h h2{margin:0 0 0 4px;font-size:14px;font-weight:700;color:var(--ink)}
.cvrd__srch{position:relative;display:flex;align-items:center;margin:0 16px 12px}
.cvrd__srch>i[data-cvicon]{position:absolute;left:14px;color:var(--ink-3);line-height:0;pointer-events:none}
.cvrd__srch input{
  width:100%;padding:10px 16px 10px 40px;border-radius:12px;
  border:1px solid var(--line);background:var(--card-2);color:var(--ink);
  font:inherit;font-size:14px;outline:none;transition:border-color .2s,background .2s;
}
.cvrd__srch input::placeholder{color:var(--ink-3)}
.cvrd__srch input:focus{border-color:var(--ink-3);background:var(--panel)}
.cvrd__list{flex:1;overflow-y:auto;padding-bottom:16px;scrollbar-width:thin}
.cvrd__list::-webkit-scrollbar{width:4px}
.cvrd__list::-webkit-scrollbar-thumb{background:var(--line);border-radius:10px}
.cvrd__list::-webkit-scrollbar-track{background:transparent}
.cvrd__o{
  display:flex;width:100%;align-items:center;justify-content:space-between;gap:12px;
  padding:10px 16px;border:0;background:none;cursor:pointer;text-align:left;
  font:inherit;transition:background .16s;
}
.cvrd__o:hover{background:var(--card-2)}
.cvrd__o.on{background:var(--card-2)}
.cvrd__o>div{display:flex;align-items:center;gap:12px;min-width:0}
.cvrd__o span{font-size:14px;font-weight:600;color:var(--ink-2);transition:color .16s}
.cvrd__o:hover span,.cvrd__o.on span{color:var(--ink)}
.cvrd__o i[data-cvicon]{color:var(--ink);line-height:0;flex:none}
.cvrd__none{
  display:flex;height:150px;align-items:center;justify-content:center;
  font-size:14px;color:var(--ink-3);
}

/* ════════════════════════════════════════════ licence key ══ */
/* Closed it reads "I have a License Key" and is 230px. Open it is 400px, the
   lead-in fades out, the label slides left into a placeholder and an input
   plus an Activate button take the space. The original animates the width and
   shares the label between both states with a layoutId; here the width is a
   transition and the label is the same node, so it moves for free. */
.cvlk{
  position:relative;display:flex;align-items:center;gap:6px;
  height:60px;width:236px;padding:0 12px 0 18px;
  border-radius:999px;border:1px solid var(--line);background:var(--card-2);
  cursor:pointer;overflow:hidden;
  font-size:17px;font-weight:700;color:var(--ink);
  transition:width .38s var(--cv-spring),background .2s;
}
.cvlk:hover{background:var(--panel)}
.cvlk.is-open{width:min(410px,100%);cursor:default;background:var(--panel)}
.cvlk__lead{
  flex:none;white-space:nowrap;
  transition:opacity .2s var(--cv-ease),filter .2s var(--cv-ease);
}
/* Out of flow, not just invisible. Faded to zero opacity it still held its
   59px, which on a 390px phone was exactly the width the key needed — so the
   front of the code scrolled out of sight behind a word nobody could see. */
.cvlk.is-open .cvlk__lead{
  opacity:0;filter:blur(4px);pointer-events:none;
  position:absolute;left:18px;
}
.cvlk__lab{
  flex:none;white-space:nowrap;color:var(--ink);
  transition:transform .38s var(--cv-spring),color .25s var(--cv-ease),opacity .25s var(--cv-ease);
}
/* Open, the label stops being the button's text and becomes the input's
   placeholder — same node, slid back over the lead-in it replaced. */
.cvlk.is-open .cvlk__lab{
  position:absolute;left:18px;pointer-events:none;
  font-weight:500;color:var(--ink-3);
}
.cvlk.is-open.has-val .cvlk__lab{opacity:0}
.cvlk__in{
  flex:1;min-width:0;width:0;border:0;background:none;outline:none;padding-right:10px;
  /* Mono, because it is a licence key — and because the site face at this
     size puts a 20-character code just past the width of the pill on a
     phone, which is what was scrolling the front of it out of view. */
  font-family:"Mono",ui-monospace,"JetBrains Mono",monospace;
  font-size:16.5px;font-weight:700;color:var(--ink);
  letter-spacing:.02em;text-transform:uppercase;
  opacity:0;transition:opacity .25s var(--cv-ease);
}
.cvlk.is-open .cvlk__in{opacity:1}
.cvlk__go{
  flex:none;padding:9px 18px;border-radius:999px;border:0;
  background:var(--ink);color:#fff;font:inherit;font-size:15px;font-weight:700;
  cursor:pointer;opacity:0;filter:blur(2px);pointer-events:none;
  transition:opacity .3s var(--cv-ease),filter .3s var(--cv-ease),transform .18s var(--cv-spring);
}
.cvlk.is-open .cvlk__go{opacity:1;filter:blur(0);pointer-events:auto}
.cvlk__go:active{transform:scale(.95)}
.cvlk.is-bad{border-color:var(--hot);animation:cvlk-no .4s}
@keyframes cvlk-no{
  25%{transform:translateX(-5px)}50%{transform:translateX(5px)}75%{transform:translateX(-3px)}
}
.cvlk.is-ok{border-color:#2FA89A;background:#EAF7F5}

/* ════════════════════════════════════════════ option picker ══ */
/* A pill that opens a second pill above it, flipping up on the X axis out of
   a -70deg rotation. The tab of options has a rotated square for the arrow
   and the outer two get one round end each. */
.cvop{position:relative;display:inline-block;perspective:1200px}
/* A row action, not a standalone control: the default padding makes it half
   again as tall as the .tiny buttons it sits beside. */
.cvop--sm .cvop__t{padding:7px 12px;gap:6px}
.cvop--sm .cvop__lab{font-size:13.5px}
.cvop--sm .cvop__ic i[data-cvicon],
.cvop--sm .cvop__cv i[data-cvicon]{width:16px;height:16px}
.cvop--sm .cvop__o{padding:8px 15px;font-size:14px}
.cvop__t{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:13px 16px;border-radius:999px;border:1px solid transparent;
  background:var(--card-2);cursor:pointer;font:inherit;
  transition:background .3s,transform .18s var(--cv-spring),border-color .3s;
}
.cvop__t:active{transform:scale(.97)}
.cvop.is-open .cvop__t{background:#E9E9F3;border-color:var(--line)}
.cvop__ic{line-height:0;color:var(--ink-3);transition:color .3s}
.cvop__lab{font-size:17px;font-weight:700;color:var(--ink);letter-spacing:-.01em;white-space:nowrap}
/* The label animates per character on change, the way AnimatedText does. */
.cvop__lab span{display:inline-block;animation:cvop-ch .38s var(--cv-spring) backwards}
@keyframes cvop-ch{from{opacity:0;transform:translateY(5px) scale(.7)}}
.cvop__cv{line-height:0;color:var(--ink-3);transition:transform .3s var(--cv-spring)}
.cvop.is-open .cvop__cv{transform:rotate(180deg)}
.cvop__menu{
  position:absolute;bottom:100%;left:50%;z-index:60;margin-bottom:8px;
  transform-origin:bottom center;transform-style:preserve-3d;
  opacity:0;pointer-events:none;
  transform:translateX(-50%) translateY(10px) scale(1.1) rotateX(-70deg);
  filter:blur(4px);
  transition:opacity .28s var(--cv-ease),transform .38s var(--cv-spring),filter .28s var(--cv-ease);
}
.cvop.is-open .cvop__menu{
  opacity:1;pointer-events:auto;filter:blur(0);
  transform:translateX(-50%) translateY(-5px) scale(1) rotateX(0);
}
/* The menu is centred on the trigger by default, which puts half of it past
   the trigger's right edge. In a row whose control sits at the right of a
   pane with `overflow:hidden` that half is simply cut off, so this anchors
   the menu's right edge to the trigger's instead. */
.cvop--right .cvop__menu{
  left:auto;right:0;transform-origin:bottom right;
  transform:translateY(10px) scale(1.1) rotateX(-70deg);
}
.cvop--right.is-open .cvop__menu{transform:translateY(-5px) scale(1) rotateX(0)}
.cvop--right .cvop__arw{left:auto;right:26px;transform:rotate(45deg)}

/* Above the trigger there is not always room — a project row near the top of
   a pane flips it down instead, same motion mirrored. */
.cvop--down .cvop__menu{
  bottom:auto;top:100%;margin:8px 0 0;transform-origin:top center;
  transform:translateX(-50%) translateY(-10px) scale(1.1) rotateX(70deg);
}
.cvop--down.is-open .cvop__menu{transform:translateX(-50%) translateY(5px) scale(1) rotateX(0)}
.cvop--down.cvop--right .cvop__menu{
  transform-origin:top right;
  transform:translateY(-10px) scale(1.1) rotateX(70deg);
}
.cvop--down.cvop--right.is-open .cvop__menu{transform:translateY(5px) scale(1) rotateX(0)}
.cvop__in{
  position:relative;display:flex;gap:8px;min-width:max-content;white-space:nowrap;
  padding:4px 6px;border-radius:999px;border:1px solid var(--line);background:#F3F3F7;
  box-shadow:var(--sh);
}
.cvop__o{
  position:relative;display:flex;align-items:center;gap:8px;
  padding:11px 20px;border:0;background:#FEFEFE;cursor:pointer;font:inherit;
  font-size:17px;font-weight:700;color:var(--ink-3);
  transition:color .3s,transform .2s var(--cv-spring);
}
.cvop__o:hover{transform:translateY(-1px)}
.cvop__o:active{transform:scale(.95)}
.cvop__o.on{color:var(--ink)}
.cvop__o:first-child{border-radius:999px 16px 16px 999px}
.cvop__o:last-child{border-radius:16px 999px 999px 16px}
.cvop__o:only-child{border-radius:999px}
.cvop__o:not(:first-child):not(:last-child){border-radius:0}
.cvop__o i[data-cvicon]{line-height:0}
.cvop__arw{
  position:absolute;bottom:-6px;left:50%;width:12px;height:12px;
  transform:translateX(-50%) rotate(45deg);
  background:#F3F3F7;border-right:1px solid var(--line);border-bottom:1px solid var(--line);
}
.cvop--down .cvop__arw{
  bottom:auto;top:-6px;
  border:0;border-left:1px solid var(--line);border-top:1px solid var(--line);
}

/* ════════════════════════════════════════════ pagination ══ */
.cvpg{display:flex;align-items:center;justify-content:center;gap:10px}
.cvpg__b{
  position:relative;display:flex;align-items:center;justify-content:center;
  width:56px;height:56px;border-radius:16px;
  border:1px solid rgba(90,97,134,.2);background:var(--panel);color:var(--ink-3);
  cursor:pointer;font:inherit;box-shadow:0 4px 10px rgba(var(--tint),.12);
  transition:transform .26s var(--cv-spring),box-shadow .26s var(--cv-ease),color .26s,opacity .2s;
}
.cvpg__b:hover:not(:disabled):not(.on){
  transform:translateY(-6px) scale(1.06);box-shadow:0 12px 22px rgba(var(--tint),.18);color:var(--ink);
}
.cvpg__b:active:not(:disabled){transform:scale(.92)}
.cvpg__b:disabled{opacity:.4;cursor:default}
.cvpg__b i[data-cvicon]{line-height:0}
.cvpg__pages{position:relative;display:flex;gap:10px}
.cvpg__n{font-size:19px;font-weight:700;position:relative;z-index:2}
/* The active page in the original is a shared element that slides between
   buttons. Same idea: one absolutely-positioned plate, moved with a transform
   so the browser can interpolate it, plus the shimmer that sweeps across it
   every eight seconds. */
.cvpg__plate{
  position:absolute;top:0;left:0;width:56px;height:56px;border-radius:16px;z-index:1;
  background:linear-gradient(135deg,#2a2a2e 0%,#1a1a1c 50%,#0a0a0c 100%);
  border:1px solid #3a3a3e;overflow:hidden;pointer-events:none;
  box-shadow:0 8px 16px -4px rgba(0,0,0,.55),inset 0 1px 1px 0 rgba(255,255,255,.15);
  transition:transform .42s var(--cv-spring);
}
.cvpg__plate::before{
  content:"";position:absolute;inset:-100%;
  background:linear-gradient(to top right,transparent,rgba(255,255,255,.1),transparent);
  transform:skewX(12deg);animation:cvpg-sheen 8s ease-in-out infinite;
}
.cvpg__plate::after{
  content:"";position:absolute;inset:0;border-radius:inherit;
  box-shadow:inset 0 -4px 8px 0 rgba(0,0,0,.6);
}
@keyframes cvpg-sheen{0%,62%{transform:translateX(-100%) skewX(12deg)}100%{transform:translateX(200%) skewX(12deg)}}
.cvpg__b.on{color:#fff;border-color:transparent;background:transparent;box-shadow:none;cursor:default}
@media (prefers-reduced-motion:reduce){
  .cvpg__plate::before{animation:none}
  .cvpg__plate{transition-duration:.01ms}
}
@media (max-width:560px){
  .cvpg{gap:6px}
  .cvpg__b{width:40px;height:40px;border-radius:12px}
  .cvpg__plate{width:40px;height:40px;border-radius:12px}
  .cvpg__n{font-size:16px}
}

/* ════════════════════════════════════════════ dashboard ══ */
/* Demostack's home: a row of three action cards, a horizontally scrolled tip
   rail that can be dismissed, and a Resources grid beside a Get-Inspired
   list. The chrome around it is this site's shell, which already exists and
   which Elad asked for explicitly — one shell, not a second one.  */
/* D1 · Elad, 25 Aug: the dashboard is cut in the middle and should not need
   scrolling. Every gap and plate below is sized against the viewport rather
   than in fixed pixels, so the same screen closes up on a laptop and opens
   out on a large display instead of overflowing both. */
.dsb{display:flex;flex-direction:column;gap:clamp(18px,2.4vh,34px);max-width:1180px}
.ap__pad .dsb+*,.ap__pad .dhead:has(+.dsb){margin-bottom:0}
.dhead:has(+.dsb){margin-bottom:clamp(10px,1.4vh,18px)}
.dsb__acts{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
@media (max-width:900px){.dsb__acts{grid-template-columns:1fr}}
.dsb__act{
  display:flex;flex-direction:column;align-items:flex-start;gap:clamp(14px,2.2vh,30px);
  width:100%;padding:14px 15px 13px;border-radius:18px;border:1px solid var(--line);background:var(--panel);
  cursor:pointer;text-align:left;font:inherit;color:inherit;
  transition:background .2s,transform .22s var(--cv-spring),box-shadow .22s var(--cv-ease);
}
.dsb__act:hover{background:var(--card-2);transform:translateY(-3px);box-shadow:var(--sh-sm)}
.dsb__act:active{transform:scale(.99)}
.dsb__actic{
  position:relative;display:flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:11px;overflow:hidden;color:#fff;
}
.dsb__actic>span{position:absolute;inset:0}
.dsb__actic>i[data-cvicon]{position:relative;z-index:1;line-height:0}
.dsb__act b{display:block;font-size:16px;font-weight:700;letter-spacing:-.01em}
.dsb__actd{display:block;margin-top:4px;font-size:14px;line-height:1.35;color:var(--ink-2);font-weight:500}
.dsb__sec{display:flex;flex-direction:column;gap:clamp(12px,1.8vh,22px)}
.dsb__sh{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;flex-wrap:wrap}
.dsb__sh h2,.dsb__sec>h2{margin:0;font-size:19px;font-weight:800;letter-spacing:-.015em}
.dsb__sh p{margin:6px 0 0;max-width:56ch;color:var(--ink-2);font-size:15px}
.dsb__close{
  display:inline-flex;align-items:center;gap:8px;flex:none;
  padding:11px 20px;border-radius:999px;border:1px solid var(--line);
  background:var(--card-2);cursor:pointer;font:inherit;font-size:14px;font-weight:700;color:var(--ink);
  transition:background .2s,transform .18s var(--cv-spring);
}
.dsb__close:hover{background:var(--panel)}
.dsb__close:active{transform:scale(.96)}
.dsb__railw{position:relative;overflow:hidden}
.dsb__rail{display:flex;gap:18px;overflow-x:auto;scroll-behavior:smooth;padding-bottom:6px;scrollbar-width:none}
.dsb__rail::-webkit-scrollbar{display:none}
.dsb__tip{display:flex;flex-direction:column;align-items:flex-start;gap:14px;width:256px;flex:none;
  cursor:pointer;border:0;background:none;padding:0;font:inherit;text-align:left;color:inherit}
.dsb__tip{width:clamp(190px,20vw,240px)}
.dsb__tipim{
  position:relative;aspect-ratio:3/2;width:100%;border-radius:16px;overflow:hidden;
  background:var(--card-2);
}
/* --k is measured (fitFrame) rather than hard-coded: the card is a clamp()
   now, so a fixed scale would leave the frame short of the tile at one size
   and overflowing it at another. */
.dsb__tipim>iframe{
  position:absolute;top:0;left:0;width:1280px;height:854px;border:0;
  transform:scale(var(--k,.2));transform-origin:top left;pointer-events:none;
}
.dsb__tipim>span{position:absolute;inset:0}
.dsb__tip:hover .dsb__tipim>span,.dsb__tip:hover .dsb__tipim>iframe{
  transition:transform .3s var(--cv-ease);transform:scale(.205)
}
.dsb__tip:hover .dsb__tipim>span{transform:scale(1.05)}
.dsb__tipn{display:block;font-size:16px;font-weight:700}
.dsb__tipd{display:block;margin-top:3px;font-size:14px;color:var(--ink-2);font-weight:500}
/* The studio pane is `background:none` over the shell's gradient, so the
   demo's solid gradient overlay would paint a white smear across whatever is
   behind it. Fading the rail's own pixels with a mask works on any ground. */
.dsb__railw.has-more .dsb__rail{
  -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 130px),transparent);
  mask-image:linear-gradient(to right,#000 calc(100% - 130px),transparent);
}
.dsb__more{
  position:absolute;top:104px;right:0;z-index:2;
  display:flex;align-items:center;justify-content:center;width:44px;height:44px;
  border-radius:50%;border:0;background:var(--card-2);color:var(--ink);cursor:pointer;
  box-shadow:var(--sh-sm);transition:transform .2s var(--cv-spring),background .2s;
}
.dsb__more:hover{transform:scale(1.08);background:var(--panel)}
.dsb__cols{display:flex;flex-direction:column;gap:clamp(18px,2.4vh,34px)}
@media (min-width:1020px){.dsb__cols{flex-direction:row;gap:clamp(20px,2vw,34px)}.dsb__cols>*{flex:1;min-width:0}}
.dsb__res{display:grid;grid-template-columns:1fr 1fr;gap:clamp(10px,1.4vh,16px)}
.dsb__r{display:flex;flex-direction:column;align-items:flex-start;gap:12px;cursor:pointer;border:0;background:none;font:inherit;text-align:left;padding:0}
.dsb__rp{
  display:flex;align-items:center;justify-content:center;
  width:100%;height:clamp(84px,11vh,124px);border-radius:14px;
  background:var(--card-2);overflow:hidden;
}
.dsb__rf{
  position:relative;display:flex;align-items:center;justify-content:center;
  width:clamp(56px,7vh,80px);height:clamp(56px,7vh,80px);
  transition:transform .2s var(--cv-ease);
}
.dsb__r:hover .dsb__rf{transform:translateY(-4px) rotate(1deg)}
.dsb__rf>svg{position:absolute;inset:0;width:100%;height:100%}
.dsb__rf>i[data-cvicon]{position:relative;z-index:1;margin-top:14px;line-height:0;color:var(--ink);transition:transform .2s var(--cv-ease)}
.dsb__r:hover .dsb__rf>i[data-cvicon]{transform:scale(1.1)}
.dsb__r b{font-size:16px;font-weight:700}
.dsb__insp{display:flex;flex-direction:column;gap:clamp(6px,.9vh,10px)}
.dsb__i{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  width:100%;min-height:clamp(56px,7.4vh,72px);padding:clamp(10px,1.4vh,15px) 24px clamp(10px,1.4vh,15px) 20px;
  border:0;border-radius:18px;background:var(--card-2);cursor:pointer;font:inherit;text-align:left;
  transition:background .2s,transform .2s var(--cv-spring);
}
.dsb__i:hover{background:#EEEDF7;transform:translateX(2px)}
/* `> div` never matched — the row is built from spans, so the icon and the
   text were two block-ish children stacking on a narrow screen instead of
   sitting on one line. Named the group. */
.dsb__i>span:first-child{display:flex;align-items:center;gap:14px;min-width:0;flex:1}
.dsb__i>span:first-child>span:last-child{min-width:0;flex:1}
.dsb__i b{font-size:16px;font-weight:700}
.dsb__i span.dsb__isub{display:block;font-size:13px;font-weight:500;color:var(--ink-2)}
.dsb__iic{
  display:flex;align-items:center;justify-content:center;width:34px;height:34px;flex:none;
  border-radius:10px;color:#fff;line-height:0;
  transition:transform .2s var(--cv-ease);
}
.dsb__i:hover .dsb__iic{transform:scale(1.06)}
.dsb__go{color:var(--ink-3);line-height:0;flex:none;transition:transform .2s var(--cv-ease)}
.dsb__i:hover .dsb__go{transform:translate(4px,-4px)}
@media (max-width:560px){
  .dsb__res{grid-template-columns:1fr}
  .dsb__acts{gap:12px}
}

/* ════════════════════════════════════════════ store ══ */
/* Elad, 25 Aug: the whole catalogue on one page, three rows of four, and the
   frame never changes shape. Past twelve products the pager appears and the
   *products* swap inside the frame — the grid does not grow a fourth row and
   the page does not start scrolling. An unused slot is not blank: it is an
   in-build tile, crossed out and not openable.
   The tile lost its blurb and its feature list in the same pass. Twelve of
   those do not fit a screen, and every word of it is on the product page now,
   which is where somebody deciding to buy actually is. */

/* one bar: search, what it is, how it is priced, where you are */
.st__bar{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:8px;margin-bottom:22px;
  border:1px solid var(--line);border-radius:999px;background:var(--panel);
  box-shadow:var(--sh-sm);
}
.st__srch{position:relative;display:flex;align-items:center;flex:1;min-width:220px}
.st__srch>i[data-cvicon]{position:absolute;left:16px;color:var(--ink-3);pointer-events:none}
.st__srch input{
  width:100%;padding:11px 16px 11px 44px;border:0;background:none;
  font:inherit;font-size:15px;color:var(--ink);outline:none;
}
.st__srch input::placeholder{color:var(--ink-3)}
.st__sep{width:1px;align-self:stretch;background:var(--line);margin:2px 0}
.st__sel{position:relative;display:flex;align-items:center}
.st__sel select{
  appearance:none;-webkit-appearance:none;
  padding:10px 34px 10px 14px;border:0;border-radius:999px;background:var(--card-2);
  font:inherit;font-size:14px;font-weight:700;color:var(--ink);cursor:pointer;outline:none;
  transition:background .2s;
}
.st__sel select:hover{background:#EEEDF7}
.st__sel>i[data-cvicon]{position:absolute;right:11px;color:var(--ink-3);pointer-events:none}
.st__bar .cvrd__t{border-color:transparent;background:var(--card-2)}
.st__bar .cvrd__t:hover{background:#EEEDF7}
.st__count{
  width:100%;margin:0 0 18px;padding-left:20px;
  font-size:13.5px;color:var(--ink-3);font-variant-numeric:tabular-nums;
}

/* the frame: four across, three down, and it does not change */
.st__grid{
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  /* Equal rows, so a row of in-build tiles is the same height as a row of
     products. Without it the last row collapses to the holder's own height
     and the "frame that never changes" visibly changes. */
  grid-auto-rows:1fr;
  gap:18px;align-items:stretch;
}
@media (max-width:1080px){.st__grid{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (max-width:820px){.st__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:520px){.st__grid{grid-template-columns:1fr}}

.st__c{
  display:flex;flex-direction:column;overflow:hidden;text-align:left;
  border-radius:22px;border:1px solid var(--line);background:var(--panel);
  padding:0;font:inherit;color:inherit;cursor:pointer;
  transition:transform .26s var(--cv-spring),box-shadow .26s var(--cv-ease);
}
.st__c:hover{transform:translateY(-5px);box-shadow:var(--sh)}
.st__c:active{transform:translateY(-2px) scale(.99)}
/* The artwork is the tile. Everything that used to sit under it in prose is
   on the product page now, so the picture gets the room. */
.st__shot{position:relative;aspect-ratio:16/11;overflow:hidden;flex:none}
.st__fill{position:absolute;inset:0}
.st__shot:has(.st__owned) .st__tag{left:auto;right:10px}
.st__tag{
  position:absolute;top:10px;left:10px;padding:4px 11px;border-radius:999px;
  background:rgba(255,255,255,.92);color:#141A3C;font-size:11.5px;font-weight:700;
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
}
/* Both marks on the left. Top-right is where the site's own floating menu
   button sits on a phone, and a badge that spends half its life underneath
   another control is a badge nobody can read. */
.st__owned{
  position:absolute;top:10px;left:10px;display:inline-flex;align-items:center;gap:5px;
  padding:4px 11px;border-radius:999px;background:#DFF3EF;color:#1E6E64;
  font-size:11.5px;font-weight:800;
}
.st__b{display:flex;flex-direction:column;gap:9px;padding:13px 14px 14px;flex:1}
.st__nm{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.st__nm b{font-size:16px;font-weight:800;letter-spacing:-.015em;line-height:1.2}
.st__price{font-size:18px;font-weight:800;letter-spacing:-.02em;font-variant-numeric:tabular-nums;white-space:nowrap}
.st__price small{font-size:11px;font-weight:600;color:var(--ink-3);margin-left:2px}
.st__by{display:flex;align-items:center;gap:8px;margin-top:auto}
/* `.st__by span` is (0,1,1) and `.st__byav` was (0,1,0), so the disc's
   letter was taking the row's slate colour and disappearing into its own
   background. Elad, 25 Aug: "the letter is the same colour as the circle so
   you cannot see the text." Named the child explicitly rather than raising
   the avatar's weight, so the next span added to this row cannot repeat it. */
.st__by>.st__byav{
  width:22px;height:22px;border-radius:999px;flex:none;
  display:flex;align-items:center;justify-content:center;
  font-size:10.5px;font-weight:800;color:#fff;line-height:1;
}
.st__by>span:not(.st__byav){font-size:12.5px;color:var(--ink-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.st__c:hover .st__go{transform:translate(3px,-3px)}
.st__go{margin-left:auto;color:var(--ink-3);line-height:0;transition:transform .2s var(--cv-ease)}

/* an unused slot in the frame */
.st__hold{
  position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:10px;border-radius:22px;border:1.5px dashed var(--line);background:rgba(252,251,254,.5);
  color:var(--ink-3);cursor:default;min-height:180px;overflow:hidden;
}
.st__hold::before,.st__hold::after{
  content:"";position:absolute;left:8%;right:8%;top:50%;height:1.5px;
  background:var(--line);
}
.st__hold::before{transform:rotate(31deg)}
.st__hold::after{transform:rotate(-31deg)}
.st__hold span{
  position:relative;z-index:1;padding:5px 13px;border-radius:999px;background:var(--panel);
  border:1px solid var(--line);font-size:12px;font-weight:700;letter-spacing:.01em;
}

.st__none{grid-column:1/-1;padding:50px 0;text-align:center;color:var(--ink-2)}
.st__pgw{display:flex;flex-direction:column;align-items:center;gap:14px;margin-top:34px}
.st__lk{display:flex;justify-content:center;margin-top:34px}

/* ── the shop, shut ────────────────────────────────────────────────────
   Nothing published is not an empty grid — it is a closed shop that says so.
   The tape is one repeating strip rotated across the whole viewport, with the
   card sitting over the middle of it. */
.st__closed{
  position:relative;display:flex;align-items:center;justify-content:center;
  min-height:min(70vh,620px);overflow:hidden;
  /* Break out of .wrap's 1180px column: the tape has to run corner to
     corner of the window, which is the whole gesture. The body carries a
     small padding of its own, so this is inset by it rather than 100vw. */
  width:auto;margin:10px calc(50% - 50vw + var(--pad-x,0px)) 30px;
  padding-inline:clamp(16px,4vw,40px);
}
.st__tape{
  position:absolute;top:50%;left:-20%;width:140%;
  transform:translateY(-50%) rotate(-14deg);
  background:var(--brand);color:#fff;padding:14px 0;
  box-shadow:0 20px 50px -24px rgba(98,96,255,.9);
  overflow:hidden;pointer-events:none;
}
.st__tapein{display:flex;width:max-content;animation:st-tape 26s linear infinite}
.st__tapein span{
  display:inline-flex;align-items:center;gap:26px;padding-right:26px;
  font-size:19px;font-weight:800;letter-spacing:.34em;white-space:nowrap;
}
.st__tapein i{width:6px;height:6px;border-radius:999px;background:rgba(255,255,255,.75);flex:none}
@keyframes st-tape{to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){.st__tapein{animation:none}}
.st__shut{
  position:relative;z-index:1;width:min(430px,100%);
  padding:32px 30px;border-radius:26px;text-align:center;
  /* Nearly opaque, not translucent: the reference has the tape pass behind a
     solid card, and a blurred purple stripe reading through the text is a
     legibility problem dressed as an effect. */
  background:rgba(255,255,255,.97);border:1px solid var(--line);
  box-shadow:var(--sh-lg);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
}
.st__shutic{
  display:inline-flex;align-items:center;justify-content:center;
  width:52px;height:52px;border-radius:16px;margin-bottom:16px;
  background:var(--lav);color:var(--brand);
}
.st__shut h2{margin:0 0 8px;font-size:25px;font-weight:800;letter-spacing:-.025em}
.st__shut p{margin:0 0 22px;color:var(--ink-2);font-size:15px;line-height:1.5}
.st__shutb{display:flex;gap:10px;justify-content:center;flex-wrap:wrap}
/* The pair sits on one line; the page .btn is padded for a hero and two of
   them do not fit a 430px card at that size. */
.st__shutb .btn{padding:12px 20px;font-size:14.5px;gap:7px}

/* ── checkout ──────────────────────────────────────────────────────────
   back.css already carries a .pp block for the studio's own pay windows.
   It is repeated here, byte for byte, so the shop floor does not have to
   pull a 105kb stylesheet to draw one button. If one of them changes, both
   change — they are the same button and a buyer sees both. */
.pp{margin-top:4px}
.pp__btn{width:100%;height:48px;border:0;border-radius:999px;cursor:pointer;
  background:#FFC439;color:#0d1a3a;font:inherit;font-weight:800;font-size:16px;
  display:flex;align-items:center;justify-content:center;gap:8px;
  transition:filter .2s,transform .2s}
.pp__btn:hover{filter:brightness(1.05);transform:translateY(-1px)}
.pp__btn.is-busy{background:#F0E0B4;cursor:default}
.pp__btn.is-done{background:var(--sage);color:var(--mint,#03363D);cursor:default}
.pp__mark{font-size:19px;letter-spacing:-.03em}
.pp__mark b{color:#003087;font-weight:900}
.pp__mark i{color:#009cde;font-style:italic;font-weight:900}
.pp__spin{width:16px;height:16px;border-radius:99px;border:2.5px solid rgba(0,0,0,.2);
  border-top-color:#0d1a3a;animation:ppspin .7s linear infinite}
.pp__note{margin:10px 0 0;font-size:12px;color:var(--ink-3);text-align:center}
@keyframes ppspin{to{transform:rotate(360deg)}}

/* the sheet the shop checks out in */
.stco{
  width:420px;max-width:100%;max-height:calc(100dvh - 32px);overflow:auto;
  background:var(--panel);border:1px solid var(--line);border-radius:26px;
  box-shadow:var(--sh-lg);padding:24px;
}
.stco__h{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:18px}
.stco__h h2{margin:0;font-size:19px;font-weight:800;letter-spacing:-.015em}
.stco__h p{margin:4px 0 0;font-size:14px;color:var(--ink-2)}
.stco__line{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:14px 0;border-top:1px solid var(--line);
}
.stco__line b{font-size:15px;font-weight:700}
.stco__line span{color:var(--ink-2);font-size:14px}
.stco__tot{
  display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding:14px 0 18px;border-top:1px solid var(--line);
}
.stco__tot b{font-size:28px;font-weight:800;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.stco__reg{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 0}
.stco__reg>span{font-size:14px;color:var(--ink-2)}
.stco__note{margin:14px 0 0;font-size:12.5px;color:var(--ink-3);line-height:1.5}
.stco__key{
  display:block;margin:14px 0 0;padding:12px 14px;border-radius:14px;
  background:var(--card-2);border:1px dashed var(--line);
  font-family:"JetBrains Mono",ui-monospace,monospace;font-size:14px;
  text-align:center;letter-spacing:.04em;color:var(--ink);word-break:break-all;
}

/* The release row reuses the studio's .dnote, which was built for a
   left-icon / text / right-action row. The picker is that action, and it
   needs the row to stop stretching it to full height. */
.dnote .cvop{margin-left:auto;flex:none;align-self:center}
/* ── the store's list in the admin studio ──────────────────────────────
   A grid, not a wrapping flex row. The three controls at the end were a
   standalone-sized picker beside two table-sized buttons, on a row that broke
   onto a second line the moment the rail took its width back. Fixed tracks
   keep every row's columns on the same x, whatever is in them. */
.stadm__row{
  display:grid;align-items:center;gap:14px;padding:12px 14px;
  grid-template-columns:40px minmax(0,1fr) auto auto;
  border:1px solid var(--line);border-radius:16px;background:var(--panel);
}
.stadm__row+.stadm__row{margin-top:8px}
.stadm__sw{width:40px;height:40px;border-radius:11px}
.stadm__nm{min-width:0}
.stadm__nm b{display:block;font-size:15px;font-weight:700;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.stadm__nm span{font-size:12.5px;color:var(--ink-2)}
.stadm__pr{font-size:18px;font-weight:800;font-variant-numeric:tabular-nums;
  text-align:right;white-space:nowrap}
.stadm__acts{display:flex;gap:7px;align-items:center}
@media (max-width:900px){
  .stadm__row{grid-template-columns:40px minmax(0,1fr) auto;row-gap:10px}
  .stadm__acts{grid-column:1/-1;justify-content:flex-end}
}
.stadm__row.is-draft{opacity:.62}
.stadm__row.is-draft:hover{opacity:1}

/* ── the product sheet ─────────────────────────────────────────────────
   Fields on the left, and what they produce on the right — the shop tile and
   the head of the product page, both live. Elad, 25 Aug: the old sheet was a
   stack of bare inputs with no way to see what any of them did. */
.stps{
  width:920px;max-width:100%;max-height:calc(100dvh - 32px);
  display:flex;flex-direction:column;
  background:var(--card-2);border:1px solid var(--line);border-radius:26px;
  box-shadow:var(--sh-lg);overflow:hidden;
}
.stps__h{display:flex;align-items:center;justify-content:space-between;padding:16px 22px;flex:none}
.stps__h h2{margin:0;font-size:18px;font-weight:800;letter-spacing:-.01em}
.stps__body{
  display:flex;flex:1;min-height:0;overflow:hidden;background:var(--panel);
  border-top:1.6px solid var(--line);border-bottom:1.6px solid var(--line);border-radius:18px;
}
.stps__form{flex:1.05;min-width:0;padding:20px;overflow-y:auto;
  display:flex;flex-direction:column;gap:14px}
.stps__prev{
  flex:.95;min-width:0;padding:20px;overflow-y:auto;
  border-left:1.6px dashed var(--line);background:var(--card-2);
  display:flex;flex-direction:column;gap:14px;
}
.stps__l{display:flex;flex-direction:column;gap:6px}
.stps__l>span{font-size:13px;font-weight:600;color:var(--ink-2)}
.stps__l input,.stps__l select,.stps__l textarea{
  width:100%;padding:10px 13px;border-radius:12px;
  border:1.5px solid var(--line);background:var(--panel);color:var(--ink);
  font:inherit;font-size:14.5px;font-weight:600;outline:none;resize:vertical;
  transition:border-color .2s,box-shadow .2s;
}
.stps__l select{appearance:none;-webkit-appearance:none;padding-right:34px}
.stps__l input:focus,.stps__l select:focus,.stps__l textarea:focus{
  border-color:var(--ink);box-shadow:0 0 0 3px rgba(var(--tint),.07);
}
.stps__row{display:flex;gap:12px}
.stps__row>*{flex:1;min-width:0}
.stps__wrap{position:relative}
.stps__wrap>i[data-cvicon]{position:absolute;right:11px;top:50%;transform:translateY(-50%);
  pointer-events:none;color:var(--ink-3);line-height:0}
.stps__hint{margin:0;font-size:12px;color:var(--ink-3);line-height:1.5}

/* the colour picker — the catalogue's own palette, plus the OS one */
.stps__cols{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.stps__sw{
  width:30px;height:30px;border-radius:10px;border:2px solid transparent;
  cursor:pointer;padding:0;outline-offset:2px;
  box-shadow:inset 0 0 0 1px rgba(var(--tint),.12);
  transition:transform .18s var(--cv-spring),border-color .18s;
}
.stps__sw:hover{transform:scale(1.12)}
.stps__sw.on{border-color:var(--ink);transform:scale(1.06)}
.stps__any{
  position:relative;width:30px;height:30px;border-radius:10px;overflow:hidden;
  border:1.5px dashed var(--ink-3);cursor:pointer;flex:none;
  background:conic-gradient(#f5822a,#c2557a,#6260ff,#2fa89a,#f5822a);
}
.stps__any input{position:absolute;inset:-20%;width:140%;height:140%;
  border:0;padding:0;cursor:pointer;opacity:0}
.stps__hex{
  width:104px!important;font-family:"Mono",ui-monospace,monospace!important;
  font-size:12.5px!important;text-transform:uppercase;
}

.stps__plab{
  margin:0;font-family:"Mono",ui-monospace,monospace;font-size:9px;font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3);
}
.stps__tile{max-width:290px}
.stps__pdp{
  border-radius:18px;border:1px solid var(--line);background:var(--panel);
  padding:16px;display:flex;flex-direction:column;gap:9px;
}
.stps__pdp h3{margin:0;font-size:22px;font-weight:800;letter-spacing:-.025em;line-height:1.1}
.stps__pdp p{margin:0;font-size:13.5px;line-height:1.45;color:var(--ink-2)}
.stps__pdp ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
.stps__pdp li{position:relative;padding-left:22px;font-size:12.5px;color:var(--ink-2)}
.stps__pdp li i[data-cvicon]{position:absolute;left:0;top:1px;color:#2FA89A;line-height:0}
.stps__pdp .pdp__cost b{font-size:24px}
.stps__f{display:flex;align-items:center;justify-content:flex-end;gap:10px;
  padding:14px 22px;flex:none}
.stps__f .cvep__st{margin-right:auto}
@media (max-width:820px){
  .stps__body{flex-direction:column;overflow-y:auto}
  .stps__prev{border-left:0;border-top:1.6px dashed var(--line)}
}

/* ── the region picker, opened in place ────────────────────────────────
   Same panel as the modal, anchored under its trigger. It is shorter than
   the modal on purpose: a dropdown that runs the height of the window is a
   modal that forgot to draw its backdrop. */
.cvrd--inline{position:relative}
.cvrd--inline .cvrd__pnl{
  position:absolute;top:calc(100% + 8px);inset-inline-end:0;z-index:70;
  width:340px;max-width:min(340px,calc(100vw - 32px));height:360px;
  opacity:0;transform:translateY(-6px) scale(.98);transform-origin:top right;
  transition:opacity .2s var(--cv-ease),transform .26s var(--cv-spring);
}
.cvrd--inline.is-open .cvrd__pnl{opacity:1;transform:none}
.cvrd--inline .cvrd__h{padding:14px 14px 6px}
.cvrd--inline .cvrd__srch{margin:0 14px 10px}
/* Anchored to the left edge instead, for a trigger near the left of a row. */
.cvrd--inline.cvrd--start .cvrd__pnl{
  inset-inline-end:auto;inset-inline-start:0;transform-origin:top left;
}

/* The store's hero is the page's own, cut down. Three rows of tiles have to
   sit under it without the page turning into a scroll, and the shop does not
   need the full-height statement the portfolio opens with. */
/* The top figure has to clear the fixed nav pill — .phead's own 120px is
   there for exactly that. Cutting it to 18px put the eyebrow under the nav.
   Only the bottom half is trimmed here. */
.phead--tight{padding:clamp(104px,11vw,150px) 0 clamp(10px,1.6vw,20px)}
.phead--tight .h1--page{font-size:clamp(30px,4.4vw,52px);margin-bottom:10px}
.phead--tight .lead{max-width:62ch;font-size:16px}
.phead--tight .eyebrow{margin-bottom:10px}

/* ════════════════════════════════════════════ product page ══
   Elad, 25 Aug: full screen, and it does not scroll. So the shell is a
   column of exactly viewport height, the bar is a fixed row and the body
   takes the rest — everything inside lays out into what is left rather than
   pushing the page taller. On a phone that is not possible or desirable, so
   below 860px it becomes a normal scrolling page; a locked screen you cannot
   reach the bottom of is worse than a scroll. */
body.pdp{
  padding:0;height:100dvh;overflow:hidden;
  background:
    radial-gradient(120% 90% at 12% 0%,var(--sky-1),transparent 60%),
    radial-gradient(110% 80% at 100% 8%,var(--sky-4),transparent 55%),
    var(--card);
}
.pdp__shell{height:100dvh;display:flex;flex-direction:column}
.pdp__bar{
  flex:none;display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:14px clamp(16px,3vw,30px);
}
.pdp__back,.pdp__mark{
  display:inline-flex;align-items:center;gap:8px;text-decoration:none;
  font-size:14px;font-weight:700;color:var(--ink);
}
.pdp__back{
  padding:8px 16px 8px 12px;border-radius:999px;
  border:1px solid var(--line);background:var(--panel);box-shadow:var(--sh-sm);
  transition:transform .18s var(--cv-spring),background .2s;
}
.pdp__back:hover{background:var(--card-2);transform:translateX(-2px)}
.pdp__mark img{border-radius:7px}
.pdp__main{flex:1;min-height:0;padding:0 clamp(16px,3vw,30px) clamp(16px,3vw,30px)}
.pdp__main--mid{display:flex;align-items:center;justify-content:center;overflow:auto}
.pdp__mid{width:100%;display:flex;align-items:center;justify-content:center;padding:8px 0}

.pdp__grid{
  height:100%;display:grid;gap:clamp(16px,2vw,26px);
  grid-template-columns:minmax(0,1.45fr) minmax(340px,.55fr);
}
/* min-width:0 on both columns. A grid item's default is min-content, and the
   tab row is nowrap — so its four tabs set a 377px floor that pushed the whole
   column past a 320px screen. With the floor removed the row scrolls inside
   itself, which is what its own overflow-x is for. */
.pdp__show{min-height:0;min-width:0;display:flex;flex-direction:column;gap:12px}
.pdp__thumbs{min-width:0;max-width:100%}
.pdp__stage{
  flex:1;min-height:0;position:relative;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:10px;
  border-radius:var(--r-lg);border:1px solid var(--line);background:var(--panel);
  box-shadow:var(--sh);padding:clamp(14px,2vw,26px);overflow:hidden;
}
.pdp__shot{
  flex:1;min-height:0;width:100%;display:flex;align-items:center;justify-content:center;
}
.pdp__shot img{
  max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;
  border-radius:16px;box-shadow:var(--sh-sm);
}
.pdp__shot.is-phone img{max-width:min(300px,42%);border-radius:26px}
.pdp__film{
  max-width:100%;max-height:100%;border-radius:16px;background:#0b0d1a;
  box-shadow:var(--sh-sm);
}
.pdp__ph{
  flex:none;font-size:12px;color:var(--ink-3);text-align:center;max-width:52ch;
}
.pdp__thumbs{flex:none;display:flex;gap:8px;flex-wrap:wrap}
.pdp__th{
  display:inline-flex;align-items:center;gap:7px;
  padding:8px 14px;border-radius:999px;border:1px solid var(--line);background:var(--panel);
  font:inherit;font-size:13px;font-weight:700;color:var(--ink-2);cursor:pointer;
  transition:background .2s,color .2s,transform .18s var(--cv-spring);
}
.pdp__th:hover{background:var(--card-2);color:var(--ink)}
.pdp__th:active{transform:scale(.96)}
.pdp__th.on{background:var(--ink);border-color:var(--ink);color:#fff}
.pdp__th i[data-cvicon]{line-height:0}

.pdp__side{
  min-height:0;min-width:0;display:flex;flex-direction:column;gap:16px;overflow:auto;
  padding:clamp(18px,2vw,26px);
  border-radius:var(--r-lg);border:1px solid var(--line);background:var(--panel);
  box-shadow:var(--sh);
}
.pdp__tag{
  display:inline-block;padding:4px 12px;border-radius:999px;margin-bottom:10px;
  background:var(--lav);color:var(--brand-2);font-size:12px;font-weight:700;
}
.pdp__side h1{margin:0;font-size:clamp(26px,2.6vw,38px);font-weight:800;letter-spacing:-.03em;line-height:1.05}
.pdp__blurb{margin:10px 0 0;color:var(--ink-2);font-size:15px;line-height:1.5}
.pdp__by{
  display:flex;align-items:center;gap:10px;margin-top:14px;padding-top:14px;
  border-top:1px solid var(--line);
}
.pdp__by .st__byav{width:32px;height:32px;font-size:13px}
.pdp__by b{display:block;font-size:14px;font-weight:700}
.pdp__by span{font-size:12.5px;color:var(--ink-2)}
.pdp__lab{
  margin:0 0 10px;font-family:"Mono",ui-monospace,monospace;font-size:9.5px;font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3);
}
.pdp__gets ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:9px}
.pdp__gets li{
  position:relative;padding-left:26px;font-size:14.5px;line-height:1.4;color:var(--ink-2);
}
.pdp__gets li i[data-cvicon]{position:absolute;left:0;top:1px;color:#2FA89A;line-height:0}
.pdp__buy{
  margin-top:auto;padding-top:16px;border-top:1px solid var(--line);
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;
}
/* 31 Aug: "a small video of what it does" for a project not open to the
   public yet — capped width so it reads as a preview clip, not a second
   hero shot competing with the real screenshot above it. */
.pdp__demo{
  width:100%;max-width:280px;border-radius:16px;box-shadow:var(--sh-sm);
  border:1px solid var(--line);display:block;background:var(--card-2);
}
.pdp__cost{display:flex;flex-direction:column}
.pdp__cost span{font-size:12.5px;color:var(--ink-3);font-weight:600}
.pdp__cost b{font-size:30px}
.pdp__go{margin-left:auto;gap:9px}
.pdp__own{
  margin-left:auto;display:inline-flex;align-items:center;gap:8px;
  padding:12px 20px;border-radius:999px;background:#DFF3EF;color:#1E6E64;
  font-size:15px;font-weight:800;
}
.pdp__own i[data-cvicon]{line-height:0}

/* the three sheets: pay, approved, key */
.pdp__card{
  width:min(440px,100%);padding:clamp(22px,3vw,32px);
  border-radius:var(--r-card);border:1px solid var(--line);background:var(--panel);
  box-shadow:var(--sh-lg);text-align:left;
}
.pdp__card h2{margin:0 0 6px;font-size:26px;font-weight:800;letter-spacing:-.025em}
.pdp__step{
  margin:0 0 14px;font-family:"Mono",ui-monospace,monospace;font-size:9.5px;font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3);
}
.pdp__cardsub{margin:0 0 18px;color:var(--ink-2);font-size:14.5px;line-height:1.5}
.pdp__line{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:13px 0;border-top:1px solid var(--line);font-size:14.5px;color:var(--ink-2);
}
.pdp__line b{font-size:15px;font-weight:800;color:var(--ink)}
.pdp__line--tot{padding-bottom:18px}
.pdp__line--tot b{font-size:26px;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.pdp__fine{margin:14px 0 0;font-size:12.5px;line-height:1.55;color:var(--ink-3)}
.pdp__cancel{
  display:block;margin:16px auto 0;border:0;background:none;cursor:pointer;
  font:inherit;font-size:13.5px;font-weight:700;color:var(--ink-3);
  text-decoration:underline;text-underline-offset:3px;
}
.pdp__cancel:hover{color:var(--ink)}
.pdp__card--ok{text-align:center}
.pdp__card--ok .pdp__step{text-align:center}
.pdp__ok{
  display:inline-flex;align-items:center;justify-content:center;
  width:58px;height:58px;border-radius:999px;margin-bottom:14px;
  background:#DFF3EF;color:#1E6E64;
  animation:pdp-pop .5s var(--cv-spring) both;
}
@keyframes pdp-pop{from{transform:scale(.5);opacity:0}}
/* 2 Sep: the confirm-details step (real name/email before the code is
   revealed) needed field styling this file never had — product.html
   doesn't load back.css/studio.css, where .mdl__l already exists, so this
   is its own, minimal version rather than borrowing a class from a
   stylesheet this page doesn't include. */
.pdp__field{display:block;text-align:left;margin-bottom:12px;font-size:13px;
  font-weight:700;color:var(--ink-2)}
.pdp__field input{display:block;width:100%;margin-top:6px;padding:11px 14px;
  border-radius:13px;border:1.5px solid var(--line);background:var(--card-2);
  font:inherit;font-size:14.5px;font-weight:500;color:var(--ink)}
.pdp__field input:focus{outline:none;border-color:var(--brand)}
.pdp__err{margin:0 0 12px;font-size:12.5px;font-weight:700;color:#B3261E;text-align:left}
.pdp__ack{
  display:flex;gap:11px;text-align:left;margin:20px 0 18px;padding:14px;
  border-radius:16px;background:var(--card-2);border:1px solid var(--line);
  font-size:13px;line-height:1.5;color:var(--ink-2);cursor:pointer;
}
.pdp__ack input{
  flex:none;width:19px;height:19px;margin:1px 0 0;accent-color:var(--brand);cursor:pointer;
}
.pdp__ack b{color:var(--ink);font-weight:800}
.pdp__card--ok .pdp__go{margin:0;width:100%;justify-content:center}
.pdp__card--ok .pdp__go:disabled{opacity:.45;cursor:not-allowed;transform:none}
/* Wider than the other two sheets, and the copy button drops to its own row:
   a twenty-character key plus a button does not fit 440px, and the code
   breaking across two lines is exactly the thing this screen exists to
   avoid. One line, at any width. */
.pdp__card--key{text-align:center;width:min(540px,100%)}
.pdp__card--key .pdp__key{flex-direction:column;gap:14px}
.pdp__card--key .pdp__key code{width:100%;word-break:normal;white-space:nowrap;
  overflow-x:auto;font-size:clamp(18px,2.6vw,26px)}
/* The code is the thing they have to copy, so it is the thing that gets the
   room: its own line, mono, big enough to read across a desk, and it wraps
   rather than scrolling half of itself out of sight. */
.pdp__key{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:center;
  margin:6px 0 4px;padding:18px 16px;border-radius:18px;
  background:var(--card-2);border:1px dashed var(--ink-3);
}
.pdp__key code{
  flex:1;min-width:0;font-family:"Mono",ui-monospace,monospace;
  font-size:clamp(17px,2.2vw,23px);font-weight:700;letter-spacing:.06em;
  color:var(--ink);word-break:break-all;user-select:all;
}
.pdp__copy{
  flex:none;display:inline-flex;align-items:center;gap:7px;
  padding:9px 15px;border-radius:999px;border:1px solid var(--line);background:var(--panel);
  font:inherit;font-size:13px;font-weight:700;color:var(--ink);cursor:pointer;
  transition:background .2s,transform .18s var(--cv-spring);
}
.pdp__copy:hover{background:var(--card-2)}
.pdp__copy:active{transform:scale(.95)}
.pdp__copy.is-ok{background:#DFF3EF;border-color:#BDD9D7;color:#1E6E64}
.pdp__copy i[data-cvicon]{line-height:0}
.pdp__card--key .st__shutb{margin-top:18px}

@media (max-width:1040px){
  .pdp__grid{grid-template-columns:minmax(0,1.2fr) minmax(300px,.8fr)}
}
/* Below this the two-column lock stops helping — a phone gets a page. */
@media (max-width:860px){
  body.pdp{height:auto;overflow:auto}
  .pdp__shell{height:auto;min-height:100dvh}
  .pdp__grid{height:auto;grid-template-columns:1fr}
  .pdp__stage{min-height:44vh}
  .pdp__side{overflow:visible}
}

/* ── D1 · the dashboard fits the window ────────────────────────────────
   Elad, 25 Aug: it is cut in the middle, it should sit evenly, and it should
   not need scrolling.

   Shrinking every number until it happens to fit at one height is how a
   screen ends up cut at a different one. The fix is the shape, not the sizes:
   four blocks stacked down the page cannot fit a laptop pane, so below the
   action row it becomes two columns — the project rail on the left, taking
   whatever height is left, and resources over the jump list on the right.
   Nothing scrolls, and both columns end on the same line, which is the
   "evenly" half of the ask.

   Below a floor there is genuinely not enough room for any of it, and then
   scrolling is the right answer rather than a screen squashed to nothing —
   hence the media query at the end. */
/* overflow-y:auto, not hidden. An auto-row grid inside a fixed-height flex
   child compresses its rows when the content is taller than the box, and
   compression here means the bottom of the jump list is simply gone — the
   very thing being fixed. Scrolling on a small window is the honest fallback;
   at any normal height nothing scrolls because nothing has to. */
/* Elad, 28 Aug: "fill the whole page, top to bottom" — on a tall monitor
   this content (sized for a normal laptop pane) was pinned to the top with
   everything past it left as dead space, which reads as unfinished rather
   than deliberate. `safe center` balances that space above and below
   instead of dumping it all below — and the "safe" half is what keeps a
   short window's title from being scrolled half off the top the way a plain
   `center` would, falling back to start-aligned the moment content actually
   overflows. */
.ap__pad--fit{display:flex;flex-direction:column;overflow-y:auto;justify-content:safe center}
.ap__pad--fit .dhead{margin-bottom:clamp(6px,1vh,16px)}
.ap__pad--fit .dhead h1{font-size:clamp(22px,2.8vh,30px)}
.ap__pad--fit .dsb{
  /* The base .dsb carries max-width:1180px for the phone/tablet stacked
     layout, where a wide column of short rows reads badly. This is the wide
     desktop layout instead — capped the same way, it left a dead strip the
     full height of the screen on anything wider than 1180px, which on
     [Elad's own monitor, 28 Aug] is most of the window. Two real columns
     want the width actually on offer, not a third of a 2000px screen. */
  max-width:none;
  flex:none;display:grid;
  grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);
  /* Both rows size to their content and the block sits at the top. Stretching
     the second row to fill was what cut the right column: a 1fr row hands
     both columns the same height and the taller one loses its bottom to
     overflow — which is the exact complaint, produced by the fix for it.
     Slack under a short dashboard is fine; a clipped list is not. */
  grid-template-rows:auto auto;
  align-content:start;
  gap:clamp(12px,1.8vh,26px) clamp(16px,1.8vw,28px);
}
.ap__pad--fit .dsb__acts{grid-column:1/-1}
.ap__pad--fit .dsb__sec--rail{grid-column:1;grid-row:2;min-height:0}
.ap__pad--fit .dsb__cols{
  grid-column:2;grid-row:2;min-height:0;
  flex-direction:column;gap:clamp(12px,1.8vh,22px);
}
.ap__pad--fit .dsb__cols>*{flex:none;min-height:0}
.ap__pad--fit .dsb__railw{flex:none}
/* Elad, 29 Aug: admin's home dropped its project rail entirely (that's what
   All Projects is for) — with no rail claiming column 1, .dsb__cols spans
   both instead of sitting alone in column 2 with an empty half beside it.
   Full width and nothing left to balance against, so its sections sit
   side by side rather than stacked. */
.ap__pad--fit .dsb--no-rail .dsb__cols{
  grid-column:1/-1;flex-direction:row;align-items:flex-start;
}
.ap__pad--fit .dsb--no-rail .dsb__cols>*{flex:1;min-width:0}
.ap__pad--fit .dsb__rail{align-items:flex-start}
/* The card keeps a width and the picture takes the leftover height, cropping
   rather than letter-boxing — a site preview cut off at the fold is what a
   site preview looks like. */
/* A website preview is landscape. Letting the card take the whole leftover
   height made it taller than it was wide, which reads as a phone. */
.ap__pad--fit .dsb__tip{width:clamp(210px,17vw,320px);height:auto}
.ap__pad--fit .dsb__tipim{flex:none;width:100%;aspect-ratio:3/2}
.ap__pad--fit .dsb__tipim>img{width:100%;height:100%;object-fit:cover;object-position:top}
.ap__pad--fit .dsb__more{top:50%;transform:translateY(-50%)}
.ap__pad--fit .dsb__more:hover{transform:translateY(-50%) scale(1.08)}

/* Four across rather than 2×2: half the height, and in the right-hand
   column four plates is still a comfortable target. */
.ap__pad--fit .dsb__res{grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}
.ap__pad--fit .dsb__rp{height:clamp(54px,6.6vh,106px)}
.ap__pad--fit .dsb__r b{font-size:13.5px;line-height:1.25}
/* The jump list is a shortlist — a label, a line of detail, no more room
   spent on it than that. */
.ap__pad--fit .dsb__i{min-height:0;padding:10px 18px 10px 15px}
.ap__pad--fit .dsb__i b{font-size:14.5px}
.ap__pad--fit .dsb__isub{font-size:12px}
.ap__pad--fit .dsb__iic{width:29px;height:29px}
.ap__pad--fit .dsb__sh h2,.ap__pad--fit .dsb__sec>h2{font-size:17px}
.ap__pad--fit .dsb__sh p{font-size:13.5px;margin-top:3px}
.ap__pad--fit .dsb__act b{font-size:15px}
.ap__pad--fit .dsb__actd{font-size:13px}

/* One column again when there is no width to split, and a scroll when there
   is no height to fit. */
@media (max-width:1080px){
  .ap__pad--fit .dsb{grid-template-columns:minmax(0,1fr);grid-template-rows:auto auto auto}
  .ap__pad--fit .dsb__sec--rail,.ap__pad--fit .dsb__cols{grid-column:1}
  .ap__pad--fit .dsb__cols{grid-row:3}
  /* 31 Aug: found on a real phone — admin's no-rail row layout (above,
     "sits side by side rather than stacked") assumed the full desktop width
     it was written for. Below that width there's nothing left to spend on
     three side-by-side columns, and "Run the studio" / "The team" / "Needs
     you next" piled on top of each other instead — Settings' label bleeding
     into the team list, team cards stacked on team cards. Same one-column
     fallback as every other dashboard already gets at this width. */
  .ap__pad--fit .dsb--no-rail .dsb__cols{flex-direction:column;align-items:stretch}
  .ap__pad--fit .dsb--no-rail .dsb__cols>*{flex:none;min-width:0}
}
@media (max-height:660px),(max-width:1080px){
  .ap__pad--fit{overflow-y:auto}
  .ap__pad--fit .dsb{flex:none;grid-template-rows:auto auto auto}
  .ap__pad--fit .dsb__railw{flex:none}
  .ap__pad--fit .dsb__tip{width:clamp(200px,20vw,250px);height:auto}
  .ap__pad--fit .dsb__tipim{flex:none;width:100%;aspect-ratio:3/2}
}

/* A settings card is not a filter bar: its dropdown hangs from the left of
   the field, and the card must not clip it. */
.dcard{overflow:visible}
.dcard .cvrd--inline .cvrd__pnl{inset-inline-end:auto;inset-inline-start:0;transform-origin:top left}
.field .cvrd{margin-top:4px}

/* the profile mark: a real logo when there is one, the disc when there is not */
.cvep__av img{object-fit:cover}
.cvep__avnote{margin:-4px 0 12px;font-size:11.5px;color:var(--ink-3);text-align:center}
/* The pencil is a label wrapping the file input. The input is transparent and
   laid over the label rather than `hidden`: a display:none input inside a
   label is not reliably activated on iOS Safari, which is why picking a
   picture did nothing on a phone. Present, sized, invisible — and the label
   keeps its own look. */
.cvep__avb{position:absolute;right:0;bottom:0;overflow:hidden;display:flex}
.cvep__avb input[type=file]{
  position:absolute;inset:0;width:100%;height:100%;
  opacity:0;cursor:pointer;font-size:0;
}
@media (max-width:760px){
  /* A 34px target is small for a thumb. */
  .cvep__avb{padding:11px}
  .cvep__avc,.cvep__avx{width:34px;height:34px}
}
.cvep__avc,.cvep__avx{
  position:absolute;left:0;bottom:0;width:30px;height:30px;border-radius:50%;
  border:2px solid #fff;cursor:pointer;box-shadow:var(--sh-sm);padding:0;line-height:0;
  display:flex;align-items:center;justify-content:center;
  transition:transform .2s var(--cv-spring);
}
.cvep__avc:hover,.cvep__avx:hover{transform:scale(1.1)}
.cvep__avx{background:var(--panel);border-color:var(--line);color:var(--ink-2)}

/* An avatar carrying an uploaded mark: the image fills the plate, and the
   background colour stays as the letterbox behind a transparent PNG. */
.t__av.has-logo,.prj__sw.has-logo,.ap__sw.has-logo,
.who3__av.has-logo,.cl__sw.has-logo{overflow:hidden;padding:0}
.has-logo>img{width:100%;height:100%;object-fit:cover;display:block}
.stg__who i.has-logo{overflow:hidden;padding:0}
.stg__who i.has-logo img{width:100%;height:100%;object-fit:cover;display:block}

/* ══════════════════════════════════════════════ on a phone ══
   Elad, 25 Aug: the store is not laid out for a phone and it is a mess.
   It was designed at 1500px and never opened at 390 — that is the gap, and
   everything below is what 390 actually needs rather than what the desktop
   rules happen to collapse into. */
@media (max-width:760px){

  /* The bar was one pill holding four controls. Wrapped onto three rows it
     kept its 999px radius, so the controls floated inside a giant lozenge
     with the corners empty. On a phone it is a card, and each control takes
     a full row it can actually be tapped on. */
  .st__bar{
    border-radius:22px;padding:10px;gap:8px;
    display:grid;grid-template-columns:1fr 1fr;
  }
  .st__srch{grid-column:1/-1;min-width:0;
    border:1px solid var(--line);border-radius:14px;background:var(--card-2)}
  .st__srch input{padding:11px 14px 11px 40px;font-size:16px}  /* 16px: iOS zooms below it */
  .st__srch>i[data-cvicon]{left:13px}
  .st__sep{display:none}
  .st__sel{min-width:0}
  .st__sel select{width:100%;padding:11px 30px 11px 13px;font-size:14px}
  .st__bar>[data-reg]{grid-column:1/-1}
  .st__bar .cvrd,.st__bar .cvrd__t{width:100%;justify-content:flex-start}
  .st__bar .cvrd__t{padding:9px 13px;border-radius:14px}
  .st__bar .cvrd__t>span{margin-inline-end:auto}
  .st__count{padding-left:4px;margin-bottom:14px}
  /* The hero keeps its clearance under the fixed nav, but not the desktop's
     statement height — on a phone it was pushing the first product a full
     screen down. */
  .phead--tight{padding:clamp(78px,20vw,110px) 0 8px}
  .phead--tight .lead{font-size:15.5px}

  /* One card per row is already a big picture — it does not also need the
     desktop's tall crop. */
  .st__grid{gap:14px}
  /* No aspect override here. The generated artwork is 16:11 and the box has
     to stay 16:11 with it — overriding one side of that pair is what cropped
     the name in the first place. */
  .st__nm b{font-size:17px}
  .st__price{font-size:19px}
  /* The in-build tiles are the frame's filler. Twelve of them under a phone's
     one-column grid is a very long scroll of nothing, so the frame shows a
     couple and stops — the products are the point. */
  .st__hold{min-height:120px}
  .st__hold:nth-of-type(n+3){display:none}

  /* One row, the same shape as the desktop pill — it just uses the full
     width. Giving the label and the field a row each turned a 58px control
     into a 133px block, which is not "readable", it is enormous. The key
     fits: 20 mono characters at 16px is about 210px, and 390 minus the
     padding and the button leaves more than that. */
  .cvlk{width:100%;max-width:420px;height:58px;flex-wrap:nowrap;padding:0 8px 0 16px}
  .cvlk.is-open{width:100%}
  .cvlk__in{font-size:16px;letter-spacing:.01em;padding-right:4px}  /* 16px: iOS zooms below it */
  .cvlk__go{padding:9px 13px}
  .cvlk.is-open .cvlk__lab{left:16px}
  .cvlk__go{padding:9px 15px;font-size:14px}
  .st__lk{padding:0 4px}

  /* Both dialogs: a phone gets the sheet at the width of the screen. */
  .cvov{padding:12px}
  .cvrd--inline .cvrd__pnl{
    position:fixed;inset:auto 12px 12px 12px;width:auto;max-width:none;
    height:min(62vh,440px);transform-origin:bottom center;
    transform:translateY(12px) scale(.98);
  }
  .cvrd--inline.is-open .cvrd__pnl{transform:none}
  .cvrd__h h2{font-size:15px}
  /* Elad, 27 Aug: two bugs on this same picker, inside the studio shell. One
     — the sheet docked 12px off the true bottom of the screen, which is
     exactly where the app's own tab bar (measured: ~73px + its own 8px
     offset) already sits, on top of it (z-index 120 to this panel's 70) — so
     the last rows opened already covered. The other — its search field was
     14px, and iOS zooms the whole page in on any input under 16px, which is
     what "opening where I can write" was actually doing.

     `.dcard .cvrd--inline .cvrd__pnl` further down (0,3,0) is the exact same
     out-specifying bug its own comment already names, just against this new
     rule instead of the old mobile one — so this has to match `.dcard` too,
     not only `.ap`, or it loses the tie on source order alone. */
  .ap .dcard .cvrd--inline .cvrd__pnl,
  .ap .field .cvrd--inline .cvrd__pnl,
  .ap .cvrd--inline .cvrd__pnl{
    bottom:calc(90px + env(safe-area-inset-bottom,0px));
  }
  .cvrd__srch input{font-size:16px}

  /* The product page stops pretending to be one screen. */
  .pdp__grid{gap:14px}
  .pdp__stage{min-height:38vh;padding:14px}
  .pdp__shot img{max-height:100%;width:100%;object-fit:contain}
  .pdp__shot.is-phone img{max-width:min(220px,62%)}
  /* Four tabs wrapped to two rows and pushed the picture up. One row that
     scrolls sideways keeps the shape of the screen. */
  .pdp__thumbs{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;
    margin-inline:-2px;padding:2px}
  .pdp__thumbs::-webkit-scrollbar{display:none}
  .pdp__th{flex:none}
  .pdp__side{padding:18px}
  .pdp__side h1{font-size:26px}
  /* Not sticky. `.pdp__side` scrolls with the page on a phone rather than
     being its own pane, so a sticky footer inside it detaches and lands over
     the title — which is what it was doing. In flow, at the end, full width. */
  .pdp__buy{position:static;margin-top:18px;padding-top:16px}
  .pdp__go{width:100%;justify-content:center;margin-left:0}
  .pdp__own{width:100%;justify-content:center;margin-left:0}
  .pdp__card{padding:22px 18px}
  .pdp__card h2{font-size:23px}
  .pdp__key{padding:16px 12px}
  .pdp__key code{font-size:clamp(15px,4.6vw,21px)}
  .st__shutb{flex-direction:column}
  .st__shutb .btn{width:100%;justify-content:center}

  /* The admin's product sheet is a form on a phone, not two panes. */
  .stps__form,.stps__prev{padding:16px}
  .stps__row{flex-direction:column;gap:12px}
  .stps__tile{max-width:none}
}

/* iOS gives the URL bar the bottom of the screen and takes it back as you
   scroll, so a control sitting flush with the bottom is a control you cannot
   press. dvh follows the bar; the safe-area inset covers the home indicator. */
@supports (padding:env(safe-area-inset-bottom)){
  @media (max-width:760px){
    .pdp__shell{padding-bottom:env(safe-area-inset-bottom)}
    .cvrd--inline .cvrd__pnl{bottom:calc(12px + env(safe-area-inset-bottom))}
  }
}

/* The receipt's two actions, on a phone: full width, stacked, and clear of
   the browser's own bottom bar — which is where the second one was hiding. */
@media (max-width:760px){
  .rcp__act{flex-direction:column;gap:10px;
    padding-bottom:calc(18px + env(safe-area-inset-bottom,0px))}
  .rcp__act .btn{width:100%;justify-content:center}
}

/* Under 380px there is genuinely not room for a 20-character key and a button
   on one line, whatever the padding. Two rows — not the three it had — so the
   whole code is visible and the control is still a control rather than a
   block. Above this it stays a single row. */
@media (max-width:380px){
  /* Only the open state wraps. Closed it is one line of text and stays a
     58px pill — wrapping it too made the resting control 98px tall for no
     reason at all. */
  .cvlk{height:58px;flex-wrap:nowrap;padding:0 10px 0 16px}
  .cvlk.is-open{height:auto;min-height:58px;flex-wrap:wrap;row-gap:8px;
    padding:10px 10px 12px 16px}
  .cvlk.is-open .cvlk__in{flex:1 1 100%;order:1;padding-right:0}
  .cvlk.is-open .cvlk__lab{position:absolute;left:16px;top:18px}
  .cvlk.is-open .cvlk__go{order:2;margin-inline-start:auto}
}

/* ══════════════════════════════════════════ G1 · the buttons ══
   Elad, 25 Aug: the buttons disagree across every page and there is no
   proportion between them.

   Measured before writing this — every button on thirteen routes:

     .btn/--hot/--ghost   59–66px   16.5px/800   r22   pad-x 30
     .dsb__close             46px     14px/700   r999  pad-x 20
     .cvlk__go               42px     15px/700   r999  pad-x 18
     .cvop__t                40px   17.5px/500   r999  pad-x 12
     .b3                  39–40px   13–13.5/800  r999  pad-x 14–17
     .pdp__th                39px     13px/700   r999  pad-x 14
     .cvrd__t                38px     14px/700   r999  pad-x 6
     .stg__who               36px     13px/700   r999  pad-x 5
     .ibtn3                  32px          —     r11   pad-x 6
     .tiny                   31px     12px/700   r999  pad-x 11

   Ten heights, seven type sizes, three weights, four radii — and `.btn--hot`
   disagreeing with itself by 7px depending on whether it carried an icon.
   That is not ten decisions, it is one decision never made.

   Three rungs and an icon square. Every family maps onto one of them; the
   rungs are the only place a number is written, so a family cannot drift
   again without editing the rung it sits on.

   `.btn` keeps its full height on the marketing pages, where it is a hero
   call to action and is meant to be big. Inside the studio, the shop's
   sheets and the receipt it drops to `lg` — it was landing at 66px next to
   31px row actions, which is the single loudest source of "no proportion". */
/* md — the default across the app */
.b3,
.dsb__close,
.cvlk__go,
.pdp__th,
.cvrd__t,
.cvop__t,
.cvep__b,
.stps__sw + .stps__sw,
.st__buy{
  min-height:var(--btn-md-h);
  padding-block:0;
  padding-inline:var(--btn-md-px);
  font-size:var(--btn-md-fs);
  font-weight:var(--btn-fw);
  line-height:1;
}
/* The two that carry their own weight on purpose: a primary action reads
   heavier than a neutral one at the same size. */
.b3--hot,.st__buy,.cvep__b--go{font-weight:800}
/* Triggers that are mostly a value, not a label, keep a tighter left inset
   because the flag or glyph sits in it. */
.cvrd__t{padding-inline:6px 12px}
.cvop__t{padding-inline:14px}
.cvop__lab{font-size:var(--btn-md-fs);font-weight:var(--btn-fw)}
.cvop__ic i[data-cvicon],.cvop__cv i[data-cvicon]{width:18px;height:18px}
.cvlk__go{min-height:36px;padding-inline:16px}   /* inside a 58px pill */

/* sm — actions that live in a row of them */
.tiny,.cvop--sm .cvop__t{
  min-height:var(--btn-sm-h);
  padding-inline:var(--btn-sm-px);
  font-size:var(--btn-sm-fs);
  font-weight:var(--btn-fw);
  line-height:1;
}
/* min-height is a floor, not a cap — the icon plus the label's own line box
   pushed this to 40px next to 30px row buttons. An explicit height and a
   line-height of 1 put it on the rung. */
.cvop--sm .cvop__t{height:var(--btn-sm-h);padding-block:0;gap:6px}
.cvop--sm .cvop__lab{font-size:var(--btn-sm-fs);line-height:1}
.cvop--sm .cvop__lab span{line-height:1}
.cvop--sm .cvop__ic i[data-cvicon],.cvop--sm .cvop__cv i[data-cvicon]{width:15px;height:15px}

/* icon — square, and square with each other */
.ibtn3,.dsb__more,.cvep__x{
  width:var(--btn-ico);height:var(--btn-ico);
  min-width:var(--btn-ico);padding:0;
  display:inline-flex;align-items:center;justify-content:center;
}
.cvep__x{border-radius:11px}

@media (max-width:760px){
  /* --btn-lg-h is set in site.css, which every page loads; only the two
     rungs this file owns are touched here. */
  :root{--btn-md-h:40px;--btn-sm-h:32px}
  /* A row action on a phone is still a thumb target. */
  .tiny{--btn-sm-fs:13px;padding-inline:14px}
}

/* ══════════════════════════════════════════ G2 · the studio bar ══
   Elad, 25 Aug: make the bar nicer — our own design, the waves, like the
   site, something custom, and more motion.

   It was `background: var(--ink)` and nothing else: a flat navy strip with a
   logo at one end and a name at the other. The public site's whole visual
   language is soft light moving behind a surface — the sky blobs on the
   homepage, the shape under every page head — and none of it had reached the
   one piece of chrome a signed-in person looks at all day.

   So: two aurorae drifting behind the content on a long cycle, a wave along
   the bottom edge that reads as the site's own curve rather than a border,
   and a top highlight so the bar sits on the page instead of being painted
   onto it. Everything is behind `z-index`, so the text is never fighting a
   gradient for contrast.

   The motion is slow on purpose — 34 and 46 seconds, prime-ish so the two
   never sync into a pulse. Chrome that animates at a speed you can notice is
   chrome you end up watching. */
.ap__bar{
  /* NOT overflow:hidden. It was, so the lights would stay inside the rounded
     ends — and it clipped the notifications panel, which hangs out of the
     bar's bottom edge. The clip belongs to the decoration, not to the bar
     that has to let a dropdown escape it. */
  background:
    radial-gradient(120% 180% at 8% -40%, #232C63 0%, transparent 62%),
    linear-gradient(100deg, #12183A 0%, #1A2150 48%, #12183A 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.13),
    0 10px 26px -18px rgba(var(--tint),.85);
}
/* Elad, 28 Aug: "the notifications screen jumped again" — the ninth bare/
   wildcard selector this codebase has had reach into a sibling it was never
   meant to style. `.notes` is a direct child of `.ap__bar` too (it opens
   there, then escapes it — see the overflow note above), so this gave it
   position:relative at the same specificity as its own position:absolute,
   and ui.css loading after back.css settled the tie. Relative instead of
   absolute meant `top:64px` moved it 64px down from its spot *in the flex
   row*, not 64px below the bar — which is how it ended up rendered above
   the viewport instead of hanging under the bell. */
.ap__bar > *:not(.notes){position:relative;z-index:2}

/* One clipped layer holding everything decorative. */
.ap__glow{
  position:absolute;inset:0;z-index:0;
  overflow:hidden;border-radius:inherit;pointer-events:none;
}
.ap__glow::before,
.ap__glow::after{
  content:"";position:absolute;z-index:0;border-radius:50%;
  pointer-events:none;filter:blur(26px);
}
.ap__glow::before{
  width:280px;height:150px;top:-70%;left:6%;
  background:radial-gradient(circle,rgba(98,96,255,.72),transparent 70%);
  animation:ap-drift-a 34s ease-in-out infinite;
}
.ap__glow::after{
  width:230px;height:130px;top:-40%;right:14%;
  background:radial-gradient(circle,rgba(47,168,154,.5),transparent 70%);
  animation:ap-drift-b 46s ease-in-out infinite;
}
@keyframes ap-drift-a{
  0%,100%{transform:translate3d(0,0,0) scale(1)}
  50%    {transform:translate3d(46vw,14px,0) scale(1.22)}
}
@keyframes ap-drift-b{
  0%,100%{transform:translate3d(0,0,0) scale(1.1)}
  50%    {transform:translate3d(-40vw,-10px,0) scale(.9)}
}

/* The wave. One repeating curve masked to the bottom edge — the site's own
   line, not a hairline border. */
.ap__wave{
  position:absolute;z-index:1;left:0;right:0;bottom:0;height:9px;
  pointer-events:none;opacity:.42;
  background-repeat:repeat-x;background-size:260px 9px;background-position:0 100%;
  /* Shallow — 3px of travel over a 260px period. A 52px bar cannot carry a
     real wave without it becoming a band through the middle of the text;
     what it can carry is the site's curve as the edge it sits on. */
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='9' viewBox='0 0 260 9'%3E%3Cpath d='M0 5c32 0 33-3 65-3s33 3 65 3 33-3 65-3 33 3 65 3v4H0Z' fill='%236260FF' fill-opacity='.75'/%3E%3Cpath d='M0 7c32 0 33-2 65-2s33 2 65 2 33-2 65-2 33 2 65 2v2H0Z' fill='%232FA89A' fill-opacity='.5'/%3E%3C/svg%3E");
  animation:ap-wave 22s linear infinite;
}
@keyframes ap-wave{to{background-position:-260px 100%}}

/* the pieces on top of it */
.ap__bar .stg__mark{letter-spacing:-.02em}
.ap__bar .stg__mark img{
  border-radius:7px;
  box-shadow:0 0 0 1px rgba(255,255,255,.16),0 3px 10px -4px rgba(0,0,0,.7);
  transition:transform .5s var(--cv-spring);
}
.ap__bar .stg__mark:hover img{transform:rotate(-6deg) scale(1.08)}

/* The crumb tells you where you are, so it should arrive rather than just be
   there — it is the only part of the bar that changes as you move. */
.ap__bar .stg__crumb{animation:ap-crumb .42s var(--cv-ease) both}
@keyframes ap-crumb{from{opacity:0;transform:translateX(-6px)}}

.ap__bar .stg__who{
  background:rgba(255,255,255,.1);
  border:1px solid rgba(255,255,255,.14);
  color:#fff;border-radius:999px;
  transition:background .22s,transform .2s var(--cv-spring);
}
.ap__bar .stg__who:hover{background:rgba(255,255,255,.18);transform:translateY(-1px)}
.ap__bar .stg__who:active{transform:scale(.97)}
.ap__bar .stg__who b{color:#fff}

/* An unread count should catch the eye once, not blink at it forever. */
.ap__bar [data-bell] .dot{box-shadow:0 0 0 0 rgba(245,130,42,.75);animation:ap-ping 3.4s ease-out infinite}
@keyframes ap-ping{
  0%{box-shadow:0 0 0 0 rgba(245,130,42,.7)}
  14%,100%{box-shadow:0 0 0 9px rgba(245,130,42,0)}
}

@media (prefers-reduced-motion:reduce){
  .ap__glow::before,.ap__glow::after,.ap__wave,
  .ap__bar .stg__crumb,.ap__bar [data-bell] .dot{animation:none}
}
@media (max-width:760px){
  .ap__glow::before{width:190px}
  .ap__glow::after{display:none}
  .ap__wave{opacity:.38}
}

/* ══════════════════════════════════ the studio, on a phone ══
   Elad, 25 Aug: on the phone the dashboard should feel like an app made for
   him — all of it.

   What was there: the desktop screen, narrowed. An action card 380px tall for
   two lines of text, a greeting that took a whole screen, 929px of scroll to
   reach the bottom, and the rail laid on its side as ten destinations with
   the projects mixed in, scrolling sideways.

   What an app does instead, and every app on his phone already does it:
   a header you barely notice, the numbers first, actions as targets rather
   than paragraphs, content that moves sideways under a thumb, and four
   destinations that never move at the bottom of the screen. */
@media (max-width:760px){

  /* ── the tab bar ─────────────────────────────────────────────────── */
  .ap__rail{display:none}
  .ap{grid-template-areas:"bar" "main";grid-template-rows:auto minmax(0,1fr)}
  .ap__tabs{
    position:fixed;z-index:120;left:8px;right:8px;
    bottom:calc(8px + env(safe-area-inset-bottom,0px));
    display:grid;grid-auto-flow:column;grid-auto-columns:1fr;
    padding:6px;border-radius:22px;
    /* Elad, 27 Aug: "floating in the air, a weird effect behind the bar" —
       a saturated card (the violet "Take them on" button, most often) sliding
       underneath read as a smear rather than a frost. `saturate(1.5)` was
       making whatever passed behind the glass *more* vivid right as the blur
       tried to soften it, and .86 opacity was thin enough to carry that
       through. Less see-through, less saturation boost: still glass, not a
       colour bleeding under it. */
    background:rgba(255,255,255,.94);border:1px solid var(--s-line,var(--line));
    backdrop-filter:blur(20px) saturate(1.1);-webkit-backdrop-filter:blur(20px) saturate(1.1);
    box-shadow:0 16px 40px -18px rgba(var(--tint),.5);
  }
  .ap__tab{
    display:flex;flex-direction:column;align-items:center;gap:3px;
    padding:7px 2px 6px;border:0;background:none;cursor:pointer;font:inherit;
    color:var(--s-ink-3,var(--ink-3));border-radius:15px;
    transition:color .2s,background .2s;
  }
  .ap__tab>span:last-child{font-size:10.5px;font-weight:700;letter-spacing:-.01em}
  /* The glyph must not be the hit target. Playwright refused to click the tab
     because the <svg> was on top at the centre point — which is a real
     finding, not a test artefact: a thumb landing dead centre hits the same
     node, and any handler bound to the button relies on the event bubbling
     from wherever it started. */
  .ap__tab>*{pointer-events:none}
  .ap__tabi{position:relative;display:flex;align-items:center;justify-content:center;
    width:26px;height:26px;line-height:0}
  .ap__tabi i[data-icon]{font-size:22px}
  .ap__tab.on{color:var(--s-brand,var(--brand));background:var(--lav)}
  .ap__tab:active{transform:scale(.94)}
  .ap__tabi em{
    position:absolute;top:-1px;right:-2px;width:8px;height:8px;border-radius:99px;
    background:var(--hot);border:2px solid #fff;
  }
  /* The pane has to end above the bar, not behind it. */
  .ap__main{padding-bottom:calc(80px + env(safe-area-inset-bottom,0px))}

  /* ── the bottom sheet ────────────────────────────────────────────── */
  .apsheet{position:fixed;inset:0;z-index:200;opacity:0;transition:opacity .24s var(--cv-ease)}
  .apsheet.is-in{opacity:1}
  .apsheet__bd{position:absolute;inset:0;background:rgba(var(--tint),.34);
    backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}
  .apsheet__p{
    position:absolute;left:0;right:0;bottom:0;
    max-height:82dvh;display:flex;flex-direction:column;
    padding:8px 14px calc(18px + env(safe-area-inset-bottom,0px));
    background:var(--s-chrome,var(--card));border-radius:26px 26px 0 0;
    box-shadow:0 -20px 50px -22px rgba(var(--tint),.55);
    transform:translateY(102%);transition:transform .38s var(--cv-spring);
  }
  .apsheet.is-in .apsheet__p{transform:none}
  .apsheet__grab{display:block;width:38px;height:4px;border-radius:99px;margin:2px auto 10px;
    background:var(--s-line-2,var(--ink-3));opacity:.55}
  .apsheet__h{display:flex;align-items:center;justify-content:space-between;padding:0 4px 12px}
  .apsheet__h b{font-size:17px;font-weight:800;letter-spacing:-.02em}
  /* Down, not back — the gesture this button stands in for is a swipe down. */
  .apsheet__b{overflow-y:auto;-webkit-overflow-scrolling:touch}
  .apsheet__list{display:flex;flex-direction:column;gap:7px}
  .apsheet__row{
    display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;
    border:1px solid var(--s-line,var(--line));border-radius:16px;background:var(--s-core,#fff);
    font:inherit;text-align:left;cursor:pointer;color:inherit;
  }
  .apsheet__row:active{transform:scale(.99)}
  .apsheet__row>*,.apsheet__tile>*{pointer-events:none}
  /* Named, not `> span`. The avatar the row starts with is a <span> too, so
     the generic rule grew it to fill the row — the fourth time in this
     codebase a bare element selector has caught a class it was never meant
     to. The text column is .apsheet__pb and says so. */
  .apsheet__row b{display:block;font-size:15px;font-weight:700}
  .apsheet__row span span{font-size:12.5px;color:var(--s-ink-2,var(--ink-2))}
  .apsheet__row i[data-icon]{color:var(--s-ink-3,var(--ink-3))}
  .apsheet__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
  .apsheet__tile{
    display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;
    padding:16px 6px;border:1px solid var(--s-line,var(--line));border-radius:18px;
    background:var(--s-core,#fff);font:inherit;cursor:pointer;color:inherit;
    font-size:12px;font-weight:700;text-align:center;
  }
  .apsheet__tile i[data-icon]{font-size:22px;color:var(--s-ink-2,var(--ink-2))}
  .apsheet__tile.on{border-color:var(--s-brand,var(--brand));color:var(--s-brand,var(--brand))}
  .apsheet__tile.on i[data-icon]{color:inherit}

  /* ── the dashboard itself ────────────────────────────────────────── */
  .ap__pad{padding:14px 14px 0}
  .ap__pad--fit .dhead{margin-bottom:14px}
  .ap__pad--fit .dhead h1{font-size:26px}
  .ap__pad--fit .dhead p{font-size:14px;line-height:1.4}
  .ap__pad--fit .dsb{display:flex;flex-direction:column;gap:24px}

  /* Actions become targets, not paragraphs: three across, icon over label,
     and the description goes — on a phone it is a caption nobody reads under
     a button they already understand. */
  .ap__pad--fit .dsb__acts{grid-template-columns:repeat(3,1fr);gap:9px}
  .ap__pad--fit .dsb__act{
    align-items:center;text-align:center;gap:9px;padding:14px 6px;border-radius:18px;
  }
  .ap__pad--fit .dsb__actic{width:40px;height:40px;border-radius:13px}
  .ap__pad--fit .dsb__act b{font-size:12.5px;line-height:1.25}
  .ap__pad--fit .dsb__actd{display:none}

  /* Projects move sideways under a thumb, and snap. */
  .ap__pad--fit .dsb__rail{
    scroll-snap-type:x mandatory;gap:12px;
    margin-inline:-14px;padding-inline:14px;
  }
  .ap__pad--fit .dsb__tip{scroll-snap-align:start;width:78vw;max-width:300px}
  .ap__pad--fit .dsb__more{display:none}
  .ap__pad--fit .dsb__sh p{display:none}
  .ap__pad--fit .dsb__close{--btn-sm-h:32px;min-height:32px;padding-inline:14px;font-size:12.5px}

  /* Resources: four across, small. */
  .ap__pad--fit .dsb__res{grid-template-columns:repeat(4,1fr);gap:8px}
  .ap__pad--fit .dsb__rp{height:64px}
  .ap__pad--fit .dsb__r b{font-size:11.5px;text-align:center;width:100%}
  .ap__pad--fit .dsb__r{align-items:center}
  .ap__pad--fit .dsb__cols{gap:24px}
}

/* The numbers first — an app opens on the state of things, not on a sentence
   about it. Desktop keeps the sentence; the strip is the phone's version of
   the same information, and it is drawn from the same three values. */
.dsb__stats{display:none}
@media (max-width:760px){
  .dsb__stats{
    display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:-4px 0 4px;
  }
  .dsb__stat{
    padding:12px 10px;border-radius:16px;background:var(--s-core,#fff);
    border:1px solid var(--s-line,var(--line));
  }
  .dsb__stat b{display:block;font-size:20px;font-weight:800;letter-spacing:-.03em;
    font-variant-numeric:tabular-nums;line-height:1.1}
  .dsb__stat span{display:block;margin-top:3px;font-size:11px;font-weight:600;
    color:var(--s-ink-2,var(--ink-2));line-height:1.25}
  .ap__pad--fit .dhead p{display:none}
}

/* ══════════════════════════ the phone, second pass ══
   Elad, 25 Aug, from his own screen. Four of the seven are the same root
   cause wearing different clothes: a flex row whose text column is allowed
   to shrink below the width of its own words, so a sentence comes out one
   word per line. `min-width:0` lets it shrink; `flex-basis:0` makes it. */
@media (max-width:760px){

  /* A · Where are you. The panel was 247px wide and pinned to the left edge
     because `.dcard .cvrd--inline .cvrd__pnl` (0,3,0) out-specifies the
     mobile rule (0,2,0) and put `inset-inline-start:0` back. Same shape of
     bug as the tag pill and the author's initial — a more specific selector
     written for one context reaching into another. Scoped to match. */
  .dcard .cvrd--inline .cvrd__pnl,
  .field .cvrd--inline .cvrd__pnl,
  .cvrd--inline .cvrd__pnl{
    position:fixed;inset:auto 12px calc(12px + env(safe-area-inset-bottom,0px)) 12px;
    width:auto;max-width:none;height:min(62vh,440px);
    transform-origin:bottom center;
  }

  /* B · The text that came out one word per line. The body gets its own
     full-width line and the trimmings sit under it, rather than every
     element on the row competing for 390px. */
  .prow{flex-wrap:wrap;row-gap:8px;align-items:flex-start}
  .prow__b{flex:1 0 100%;order:2}
  .prow__ic,.prow__av{order:1}
  .prow__amt{order:1;margin-inline-start:auto;align-self:center}
  .prow .pill{order:3}
  .prow .tiny{order:4;margin-inline-start:auto}
  .prow__b b{display:block;margin-bottom:2px}
  .prow__b .pill{display:inline-block;margin-inline-start:6px}

  .nrow{flex-wrap:wrap;row-gap:6px}
  .nrow__b,.nrow>div{flex:1 0 100%;min-width:0}
  .nrow__ic{order:1}
  .nrow time,.nrow .pill{order:3}

  /* The team table and the request cards, same treatment. */
  .wk__req,.wk__reqh{flex-wrap:wrap}
  .wk__reqh>div{flex:1 0 100%;min-width:0;order:2}
  .em__act{white-space:normal}

  /* C · The visibility menu opened past the left edge of the screen. On a
     phone it is centred under its row and clamped to the viewport — the
     right-anchor exists for a desktop pane, not for a 390px screen. */
  .cvop--right .cvop__menu,
  .cvop--down.cvop--right .cvop__menu{
    left:50%;right:auto;transform-origin:top center;
    transform:translateX(-50%) translateY(-10px) scale(1.05) rotateX(70deg);
  }
  .cvop--down.cvop--right.is-open .cvop__menu{transform:translateX(-50%) translateY(5px) scale(1) rotateX(0)}
  .cvop--right .cvop__arw{left:50%;right:auto;transform:translateX(-50%) rotate(45deg)}
  .cvop__in{max-width:calc(100vw - 24px)}
  .cvop__o{padding-inline:14px}

  /* D · The first project card was cut on its left. A snap container with
     inline padding needs matching scroll-padding, or the snap point lands
     14px inside the card and the first one is always clipped. */
  .ap__pad--fit .dsb__rail{scroll-padding-inline:14px}

  /* E · The notifications panel was a 285px column under the bell, so every
     line of it broke. On a phone it is a sheet at the width of the screen. */
  .ap__bar .notes{
    position:fixed;inset:auto 10px calc(10px + env(safe-area-inset-bottom,0px)) 10px;
    top:auto;width:auto;max-width:none;border-radius:24px;
  }
  .notes__b{max-height:min(56vh,420px)}
}

/* the richer project row in the sheet */
.apsheet__row--p{align-items:flex-start;gap:12px}
.apsheet__pb{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.apsheet__row>.prj__sw,.apsheet__row>.t__av{flex:none;align-self:flex-start;margin-top:2px}
.apsheet__meta{font-size:12.5px;color:var(--s-ink-2,var(--ink-2))}
.apsheet__st{
  display:inline-block;margin-inline-start:6px;padding:1px 8px;border-radius:999px;
  background:var(--lav);color:var(--s-brand-2,var(--brand-2));
  font-style:normal;font-size:10.5px;font-weight:700;
}
.apsheet__bar{
  display:block;height:4px;border-radius:99px;margin-top:3px;
  background:var(--s-line,var(--line));overflow:hidden;
}
.apsheet__bar i{
  display:block;height:100%;border-radius:99px;background:var(--s-brand,var(--brand));
  transform-origin:left;transition:transform .5s var(--cv-ease);
}
.apsheet__sub{font-size:11.5px;color:var(--s-ink-3,var(--ink-3));font-variant-numeric:tabular-nums}
.apsheet__row--p .ap__owe{align-self:center}

/* the team's threads, on the home screen */
.dsb__th{display:flex;flex-direction:column;gap:8px}
.dsb__thr{
  display:flex;align-items:center;gap:12px;width:100%;
  padding:11px 14px;border-radius:16px;border:1px solid var(--s-line,var(--line));
  background:var(--s-core,#fff);cursor:pointer;font:inherit;text-align:left;color:inherit;
  transition:background .2s,transform .2s var(--cv-spring);
}
.dsb__thr:hover{background:var(--s-core-2,var(--card-2));transform:translateX(2px)}
.dsb__thr>*{pointer-events:none}
.dsb__thb{flex:1;min-width:0}
.dsb__thb b{display:block;font-size:14.5px;font-weight:700}
.dsb__thb span{
  display:block;font-size:12.5px;color:var(--s-ink-2,var(--ink-2));
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.dsb__thn{
  flex:none;min-width:21px;height:21px;padding:0 6px;border-radius:99px;
  display:flex;align-items:center;justify-content:center;
  background:var(--s-brand,var(--brand));color:#fff;
  font-size:11px;font-weight:800;font-style:normal;
}
@media (max-width:760px){
  .dsb__thb span{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
}

/* The phone's tab bar is a phone's. Above 760px the rail is the navigation
   and this must not render at all — it was appended to the shell
   unconditionally and, with no styles outside the media query, dropped into
   the grid as a plain row of buttons. Invisible enough on a light ground to
   miss; not on a dark one. */
@media (min-width:761px){ .ap__tabs{display:none} }

/* ── the theme switch ──────────────────────────────────────────────────
   A button in the bar, not a setting buried in a page: it is a preference you
   change because of the room you are in, and the room changes. */
.ap__theme{position:relative;overflow:hidden}
/* The global [data-cvicon] rule fixes every icon at 20x20 — left unset here,
   `inset:0` with a definite width/height drops `right`/`bottom` per spec and
   the icon pins to the top-left corner instead of centering. width/height:
   auto is what lets inset:0 actually stretch it, so the flex centers a full
   34x34 box rather than a 20x20 one glued to one corner. */
.ap__theme i[data-cvicon]{
  position:absolute;inset:0;width:auto;height:auto;
  display:flex;align-items:center;justify-content:center;
  transition:transform .45s var(--cv-spring),opacity .3s var(--cv-ease);
}
/* Elad, 28 Aug: the fix above stretched the *box* to fill the button so the
   flex-centering had something to centre in — it also, as a side effect,
   stretched the glyph itself, because `[data-cvicon]>svg{width:100%}` in
   this same file (0,1,1) then had a 32px box to be 100% of instead of a
   20px one. The icon was never meant to grow past this — it is what every
   other `.ibtn3` icon in the bar renders at. */
.ap__theme i[data-cvicon] svg{width:15px;height:15px}
.ap__theme .ap__sun{transform:translateY(0) rotate(0);opacity:1}
.ap__theme .ap__moon{transform:translateY(120%) rotate(-40deg);opacity:0}
.studio[data-theme="dark"] .ap__theme .ap__sun{transform:translateY(-120%) rotate(40deg);opacity:0}
.studio[data-theme="dark"] .ap__theme .ap__moon{transform:none;opacity:1}

/* The bar keeps its own dark ground in both themes — it is the one surface
   that was already dark, and flipping it would leave the shell with no
   anchor. What changes is how hard the aurorae burn against the page. */
.studio[data-theme="dark"] .ap__bar{
  box-shadow:inset 0 1px 0 rgba(255,255,255,.09),0 10px 26px -18px #000;
}
.studio[data-theme="dark"] .ap__glow::before{opacity:.75}
.studio[data-theme="dark"] .ap__glow::after{opacity:.6}

/* Panels and sheets that live outside .studio's subtree still have to follow
   it — they are appended to <body>. */
body.studio[data-theme="dark"]{background:var(--s-bg)}
body.studio[data-theme="dark"] .mdl__panel,
body.studio[data-theme="dark"] .cvep,
body.studio[data-theme="dark"] .stps,
body.studio[data-theme="dark"] .cvrd__pnl,
body.studio[data-theme="dark"] .apsheet__p,
body.studio[data-theme="dark"] .notes{
  background:var(--s-chrome);color:var(--s-ink);border-color:var(--s-line);
}
body.studio[data-theme="dark"] .cvep__body,
body.studio[data-theme="dark"] .stps__body,
body.studio[data-theme="dark"] .apsheet__row,
body.studio[data-theme="dark"] .apsheet__tile{background:var(--s-core)}
body.studio[data-theme="dark"] .cvep__l input,
body.studio[data-theme="dark"] .cvep__l select,
body.studio[data-theme="dark"] .stps__l input,
body.studio[data-theme="dark"] .stps__l select,
body.studio[data-theme="dark"] .stps__l textarea,
body.studio[data-theme="dark"] .cvrd__srch input{
  background:var(--s-core-2);border-color:var(--s-line);color:var(--s-ink);
}
body.studio[data-theme="dark"] .cvrd__o:hover,
body.studio[data-theme="dark"] .cvrd__o.on{background:var(--s-core-2)}
body.studio[data-theme="dark"] .cvop__in{background:var(--s-core-2);border-color:var(--s-line)}
body.studio[data-theme="dark"] .cvop__o{background:var(--s-core)}
body.studio[data-theme="dark"] .cvop__arw{background:var(--s-core-2);border-color:var(--s-line)}
body.studio[data-theme="dark"] .ap__tabs{background:rgba(18,22,47,.94);border-color:var(--s-line)}
body.studio[data-theme="dark"] .st__c,
body.studio[data-theme="dark"] .stps__tile .st__c{background:var(--s-core);border-color:var(--s-line)}

/* The page's own background is a fixed element, not the body's paint — see
   the note in site.css. In the dark studio it has to go, or a light sky sits
   behind a dark shell. */
body.studio[data-theme="dark"]::before{opacity:0}
body.studio[data-theme="dark"]{background:var(--s-bg)!important}

/* The Resources plates are a drawn folder with hardcoded paper colours. They
   read as two bright white cards on a dark ground, so the drawing takes the
   surface tokens instead. */
.studio[data-theme="dark"] .dsb__rf svg path:first-child{fill:#2A2150}
.studio[data-theme="dark"] .dsb__rf svg path:last-child{fill:var(--s-core-2);stroke:var(--s-line)}
.studio[data-theme="dark"] .dsb__rp{background:var(--s-chrome)}
