/* ==========================================================================
   sst top-tab — TOP tab presentation layer
   Ported from working/sst-top-tab-sample/v3-commerce-20260806/variant-c.html
   (the approved sample). Standalone file by design: quick-order-critical.min.css
   is NEVER regenerated, following the assets/qo-psa-grid.css precedent.

   LOAD-BEARING INVARIANT — read before editing anything here or in top-tab.js
   -------------------------------------------------------------------------
   The TOP presentation layer never owns product data and never talks to the
   cart. PHP renders real Quick Order <tr> rows into a visually-hidden "spine"
   table inside .sst-qo__group[data-group="top"]; every name, price, struck
   price, badge, stock figure and image on screen is read out of those rows,
   and every Add writes into the row's own <input type="number"> stepper so
   quick-order.js runs all of its stock gates, staging, batching and GA4
   events unchanged. If someone ever "optimises" this by rendering from a JSON
   payload, price/stock drift returns silently. Do not.

   Every class here is namespaced `sstt-` or `sst-top`. The sample's own class
   names (.btn .card .wrap .hero .stock .on .in ...) would collide with the
   430 KB of CSS this site already ships; none of them survive un-prefixed.
   ========================================================================== */

/* ---------- self-hosted fonts ----------
   Subsets lifted from the approved sample's own build (Google Fonts woff2,
   latin + latin-ext). Self-hosted rather than requested from fonts.googleapis
   .com because the theme routes its one Google Fonts <link> through the
   media="print"/onload deferred-stylesheet filter, which would FOUT the
   clamp(3rem,8.4vw,7.4rem) hero display line from the system stack.
   Archivo ships as ONE variable file per subset (verified: fvar + gvar
   present) covering the 400/500/800 the sample uses. IBM Plex Mono is static,
   one file per weight. Paths are relative to this stylesheet.
   Neither family carries CJK: ko / zh-hans / zh-hant fall through to the
   system stack below, which is correct — we do not ship CJK webfonts. */
@font-face{
  font-family:'Archivo';font-style:normal;font-weight:400 800;font-display:swap;
  src:url(fonts/archivo-var-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:'Archivo';font-style:normal;font-weight:400 800;font-display:swap;
  src:url(fonts/archivo-var-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/ibm-plex-mono-400-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/ibm-plex-mono-400-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;
  src:url(fonts/ibm-plex-mono-500-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;
  src:url(fonts/ibm-plex-mono-500-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

/* ---------- containment ----------
   The sample paints its light on <html> and pins #bg / #grid / the rails with
   position:fixed. Neither is portable: this block lives inside .sst-qo on a
   page that already has its own sticky header, footer and cart drawer, and a
   viewport-fixed canvas at z-index 0 paints over ordinary page content.

   So the whole procedural background moves INSIDE .sst-top:
     .sst-top          isolated stacking context, clipped on X only
     .sst-top__bg      absolute, spans the block, clips its contents
     .sst-top__bg-vp   position:sticky — viewport-sized and viewport-pinned
                       while TOP is on screen, but structurally contained,
                       so it can never paint outside the TOP block.
   overflow-x:clip (never `hidden`) keeps the Y axis visible so the coverflow
   slab shadows are not cut, and creates no scroll container — which is what
   lets the sticky layer keep resolving against the viewport. */
.sst-top{position:relative;z-index:0;isolation:isolate;overflow-x:clip}
.sst-top__bg{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:clip}
.sst-top__bg-vp{position:sticky;top:0;height:100vh;overflow:clip}
.sst-top__paper{
  position:absolute;inset:0;
  background:
    radial-gradient(120% 80% at 50% -10%, #FFFDF8 0%, rgba(255,253,248,0) 60%),
    radial-gradient(90% 60% at 88% 12%, rgba(46,203,245,.06) 0%, rgba(46,203,245,0) 55%),
    radial-gradient(80% 55% at 8% 34%, rgba(154,107,255,.055) 0%, rgba(154,107,255,0) 55%),
    linear-gradient(180deg,#F6F3EC 0%, #EFEBE3 45%, #E9E4DA 100%);
}

/* The scroll-progress rule keeps its job but loses its home (the sample's own
   sticky header is dropped — the live site header stays). It rides the top of
   the TOP block instead. */
.sst-top__prog-host{position:sticky;top:0;z-index:20;height:2px}

/* .sst-qo hardcodes font-family:Inter on itself in quick-order-critical.css and
   --sst-font never reaches inside it, so TOP must state its families outright
   (done on .sst-top above) rather than inherit them. */

/* ---------- the spine ----------
   Real QO rows. Visible and fully functional with no JS at all — that is the
   accessibility and failure floor. top-tab.js hides it only once the stage has
   actually mounted, by setting data-qo-top-stage="on" on the group. */
.sst-top-block .sst-top-spine{margin-top:24px}
.sst-top-block[data-qo-top-stage="on"] .sst-top-spine{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;
}

/* ============================================================
   GALLERY OF LIGHT — variant C
   warm bone daylight · prismatic refraction · editorial precision
   ============================================================ */
:root{
  --sstt-paper:#EFEBE3;
  --sstt-paper-2:#F7F4EE;
  --sstt-white:#FFFDF9;
  --sstt-ink:#14151A;
  --sstt-ink-2:#41444E;
  /* --sstt-mist is a HAIRLINE/RULE token only: #93949B on the bone paper is 2.54:1 and
     fails WCAG AA for text. Anything that renders as words uses --sstt-mist-t (4.95:1 on
     #EFEBE3, 4.65:1 on the darkest #E9E4DA stop of the page gradient). */
  --sstt-mist:#93949B;
  --sstt-mist-t:#63646B;
  --sstt-line:rgba(20,21,26,.13);
  --sstt-line-2:rgba(20,21,26,.07);
  --sstt-cy:#2ECBF5; --sstt-vi:#9A6BFF; --sstt-mg:#FF4FA8; --sstt-am:#FFB43C;
  --sstt-spectrum:linear-gradient(90deg,#2ECBF5,#9A6BFF,#FF4FA8,#FFB43C);
  --sstt-ease:cubic-bezier(.22,.72,.16,1);
  --sstt-ease-out:cubic-bezier(.16,1,.3,1);
}
/* Bare-element rules from the standalone sample are scoped to .sst-top. The
   sample's html/body/::selection rules are deliberately NOT ported: they would
   restyle the whole site. The html gradient stack becomes the .sst-top__paper
   layer instead (see the containment block in the preamble above). */
.sst-top,.sst-top *,.sst-top *::before,.sst-top *::after{box-sizing:border-box}
.sst-top{
  color:var(--sstt-ink);
  font-family:'Archivo',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  font-size:16px;line-height:1.55;
  -webkit-font-smoothing:antialiased;
  -webkit-text-size-adjust:100%;
}
/* P3 FIX (bug 2 — invisible CTA labels): these five are the sample's BARE
   ELEMENT resets. Scoping them as `.sst-top a` raised their specificity from
   (0,0,1) to (0,1,1), which outranks every single-class component rule at
   (0,1,0). Two DIFFERENT symptoms came out of that, and it is worth keeping
   them apart:
     * the 4 gateway CTAs are ANCHORS (.sstt-btn.sstt-btn-xl), so
       `.sst-top a{color:inherit}` beat `.sstt-btn{color:var(--sstt-paper)}`
       and they painted ink-on-ink — label colour == button fill, invisible.
       Measured: btnBg and labColor both rgb(20,21,26).
     * .sstt-oadd / .sstt-cf-add are BUTTONS, and were NOT invisible. The
       button reset beat .sstt-btn on all six of its properties, so they lost
       their chrome entirely — measured transparent background, 0 padding,
       0 border, 16px/400 instead of ink fill, 20px pad, 1px border, 10.5px/500.
   The single fix repairs both, but do not go looking for ink-on-ink on the
   Add buttons: that is not what was wrong with them.
   `:where()` contributes ZERO specificity, restoring the sample's (0,0,1)
   while keeping the scoping that stops these leaking into the rest of the site.
   Do not un-wrap these without re-checking .sstt-btn / .sstt-oadd / .sstt-cf-add. */
:where(.sst-top) img{display:block;max-width:100%}
:where(.sst-top) button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}
:where(.sst-top) a{color:inherit;text-decoration:none}
:where(.sst-top) h1,:where(.sst-top) h2,:where(.sst-top) h3{margin:0;font-weight:800;letter-spacing:-.035em;line-height:.95}
:where(.sst-top) p{margin:0}

.sstt-mono{font-family:'IBM Plex Mono',ui-monospace,monospace;font-variant-numeric:tabular-nums}
/* P3: full-bleed. Was `min(1380px,100% - clamp(32px,7vw,120px))`, which at
   1440px resolved to 1307px of content inside a 1408px inner — ~66px of dead
   gutter each side, and it never grew past 1380px however wide the display got.
   The cap is gone; only a minimal safety gutter remains so text never touches
   the glass. The gutter widens at >=1800px to give the side rails their lane
   back (see the .sstt-rail breakpoint below). */
.sstt-wrap{width:calc(100% - clamp(32px,3.4vw,48px));margin-inline:auto}

/* ---------- fixed procedural layers ---------- */
.sstt-bg{position:absolute;inset:0;width:100%;height:100%;z-index:0;display:block}
/* keep all page content permanently above the fixed background canvas —
   without this, revealed elements drop behind it once their transition ends */
.sst-top__main{position:relative;z-index:2}
/* section openers: the vault band now leads each section */
.sst-top__boxsec .sstt-vault{margin-top:clamp(18px,2.4vw,32px)}
.sst-top__psasec .sstt-vault{margin-top:clamp(26px,3.4vw,46px)}
.sstt-grid{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:
    linear-gradient(var(--sstt-line-2),var(--sstt-line-2)),
    linear-gradient(var(--sstt-line-2),var(--sstt-line-2)),
    linear-gradient(var(--sstt-line-2),var(--sstt-line-2));
  background-size:1px 100%;
  background-position:12.5% 0,50% 0,87.5% 0;
  background-repeat:no-repeat;
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 12%,#000 84%,transparent);
  mask-image:linear-gradient(180deg,transparent,#000 12%,#000 84%,transparent);
}
/* z-index 1, not 2: above the fixed canvas/grid (both z0) but BELOW main's content,
   so a wide carousel slab bleeding to the viewport edge is never painted over. */
.sstt-rail{
  position:absolute;top:50%;z-index:1;pointer-events:none;
  font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.34em;
  color:var(--sstt-mist-t);text-transform:uppercase;white-space:nowrap;
}
.sstt-rail-l{left:16px;transform:translateY(-50%) rotate(180deg);writing-mode:vertical-rl}
.sstt-rail-r{right:16px;transform:translateY(-50%);writing-mode:vertical-rl}
/* P3 (full-bleed): the rails are absolutely positioned 16px from .sst-top's
   edges, i.e. they live in the gutter the wrap used to leave empty. Now that
   content runs to within 24px of the glass they would sit UNDER the copy
   (.sst-top__main is z-index:2, the rail is z-index:1) — text over text. They
   are therefore shown only from 1800px up, where the wide-viewport rule below
   restores a 70px gutter that is wide enough to hold them clear of the content.
   Was `max-width:1320px`, tuned for the old 1380px-capped composition. */
/* P3 REVIEW FIX: 1799.98, not 1799. `max-width:1799px` + `min-width:1800px`
   left the open interval (1799,1800) matching NEITHER query — reachable with
   fractional viewport widths (browser zoom, Windows 125% scaling, fractional
   DPR). In that band the rails stayed visible while .sstt-wrap fell back to its
   narrow-gutter branch (content starting at x=24 against a rail occupying
   x=16..32) — the exact text-over-text overlap this rule exists to prevent. */
@media(max-width:1799.98px){.sstt-rail{display:none}}

.sstt-prog{position:absolute;left:0;top:0;height:2px;width:100%;background:var(--sstt-spectrum);transform:scaleX(0);transform-origin:0 50%;will-change:transform}

/* ---------- shared bits ---------- */
.sstt-eyebrow{display:flex;align-items:center;gap:10px;font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.28em;text-transform:uppercase;color:var(--sstt-ink-2)}
.sstt-eyebrow b{font-weight:500;color:var(--sstt-ink)}
.sstt-ebar{width:34px;height:1px;background:var(--sstt-spectrum)}
.sstt-badge{display:inline-flex;align-items:center;gap:6px;height:23px;padding:0 10px;border-radius:2px;font-family:'IBM Plex Mono',monospace;font-size:9.5px;font-weight:500;letter-spacing:.18em;text-transform:uppercase;white-space:nowrap}
.sstt-badge.sstt-ink{background:var(--sstt-ink);color:var(--sstt-paper)}
.sstt-badge.sstt-out{border:1px solid rgba(20,21,26,.28);color:var(--sstt-ink)}
.sstt-badge.sstt-spec{position:relative;color:var(--sstt-ink);background:var(--sstt-white)}
/* Same guard as the two other mask-composite ring constructs below: without support
   the ::before is not punched out and paints a solid spectrum block over the label. */
@supports((mask-composite:exclude) or (-webkit-mask-composite:xor)){
  .sstt-badge.sstt-spec::before{content:"";position:absolute;inset:0;border-radius:2px;padding:1px;background:var(--sstt-spectrum);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude}
}
.sstt-badge .sstt-sq{width:5px;height:5px;border-radius:1px;background:var(--sstt-spectrum)}
/* sst-preorder-v2-20260827: PRE-ORDER badge for featured slots. Brand magenta
   #9e2c5f is the colour Quick Order already uses for every pre-order/gate
   signal (.sst-qo__preorder-pill, .sst-qo__offer-card--preorder), so a
   pre-order reads the same wherever it appears. */
/* This badge carries the shipping date as visible text, so unlike HOT/SALE it
   is not a two-word pill: it must be allowed to wrap and to set its own height
   rather than overflow a card. */
.sstt-badge.sstt-pre{background:#9e2c5f;color:#fff;white-space:normal;height:auto;min-height:23px;padding-top:4px;padding-bottom:4px;max-width:100%;text-align:left;line-height:1.25}

.sstt-btn{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:10px;height:48px;padding:0 26px;border:1px solid var(--sstt-ink);border-radius:2px;background:var(--sstt-ink);color:var(--sstt-paper);font-size:11.5px;font-weight:500;letter-spacing:.19em;text-transform:uppercase;overflow:visible;transition:transform .3s var(--sstt-ease),background .3s,box-shadow .35s var(--sstt-ease)}
.sstt-btn::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--sstt-spectrum);transform:scaleX(0);transform-origin:0 50%;transition:transform .45s var(--sstt-ease)}
/* P3 REVIEW FIX (regression introduced by the :where() de-specificity above).
   style.min.css — enqueued at functions.php:345, i.e. BEFORE this file — carries
   two BARE hover rules at (0,1,1):
       a:hover{color:var(--sst-accent)}                                  #9e2c5f
       .sst-button:hover,.button:hover,button:hover,input[type=submit]:hover
              {color:#fff;background:var(--sst-accent-dark)}
   While the resets were `.sst-top a{color:inherit}` at (0,1,1) they tied those
   and won on source order, so the theme's hover colour never reached the TOP
   tab. At `:where(.sst-top) a` = (0,0,1) it does — and it also outranks
   `.sstt-btn{color:var(--sstt-paper)}` at (0,1,0). Measured result: hovering a
   gateway CTA painted the label #9e2c5f on the #000 hover fill = 2.98:1
   contrast, below WCAG AA for 14.5px/700 text, and the Add buttons drifted from
   --sstt-paper to #fff.
   Declaring the colour HERE, at (0,2,0), beats both bare rules without raising
   the resets back up. Do not move this declaration into .sstt-btn (0,1,0) —
   that is exactly the specificity that loses. */
.sstt-btn:hover{background:#000;color:var(--sstt-paper);transform:translateY(-2px);box-shadow:0 14px 26px -14px rgba(20,21,26,.6)}
.sstt-btn:hover::after{transform:scaleX(1)}
.sstt-btn:active{transform:translateY(0) scale(.985)}
.sstt-btn.sstt-ghost{background:transparent;color:var(--sstt-ink)}
.sstt-btn.sstt-ghost:hover{background:var(--sstt-ink);color:var(--sstt-paper)}
.sstt-btn:disabled{opacity:.38;cursor:default}
.sstt-btn:disabled:hover{transform:none;box-shadow:none;background:var(--sstt-ink)}
.sstt-btn.sstt-ghost:disabled:hover{background:transparent;color:var(--sstt-ink)}
.sstt-btn:disabled::after{transform:scaleX(0)}
.sstt-btn-l{display:block;transition:opacity .22s,transform .22s var(--sstt-ease)}
.sstt-btn-c{position:absolute;left:50%;top:50%;width:19px;height:19px;margin:-9.5px 0 0 -9.5px;stroke:currentColor;stroke-width:2.2;fill:none;stroke-linecap:round;stroke-linejoin:round;opacity:0;transition:opacity .18s}
.sstt-btn-c path{stroke-dasharray:26;stroke-dashoffset:26;transition:stroke-dashoffset .42s var(--sstt-ease-out) .06s}
.sstt-btn.sstt-added .sstt-btn-l{opacity:0;transform:translateY(-7px)}
.sstt-btn.sstt-added .sstt-btn-c{opacity:1}
.sstt-btn.sstt-added .sstt-btn-c path{stroke-dashoffset:0}
.sstt-spark{position:absolute;left:50%;top:50%;width:5px;height:5px;margin:-2.5px 0 0 -2.5px;border-radius:50%;pointer-events:none;animation:spark .72s var(--sstt-ease-out) forwards}
@keyframes spark{0%{transform:translate(0,0) scale(1);opacity:1}100%{transform:translate(var(--sstt-x),var(--sstt-y)) scale(.15);opacity:0}}

/* focus: one visible ring that reads on both bone paper and ink fills.
   :focus-visible only, so pointer users never see it. */
.sst-top :focus-visible{outline:2px solid var(--sstt-ink);outline-offset:3px;border-radius:2px}
.sstt-btn:focus-visible,.sstt-cf-nav:focus-visible{outline-color:var(--sstt-ink);outline-offset:4px}
.sstt-btn:not(.sstt-ghost):focus-visible{outline-color:var(--sstt-mg);outline-offset:3px}
.sstt-tick:focus-visible{outline:none}
.sstt-tick:focus-visible::before{outline:2px solid var(--sstt-ink);border-radius:3px}
.sstt-cf-stage:focus-visible{outline:2px solid var(--sstt-ink);outline-offset:-4px}

.sstt-rev{opacity:0;transform:translateY(26px);transition:opacity .9s var(--sstt-ease-out),transform .9s var(--sstt-ease-out)}
.sstt-rev.sstt-in{opacity:1;transform:none}

/* ---------- HERO ---------- */
.sstt-hero{position:relative;padding:clamp(30px,4.4vw,58px) 0 clamp(12px,1.4vw,16px);overflow:hidden}
.sstt-dust{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
.sstt-hero>.sstt-wrap,.sstt-hero .sstt-cue{position:relative}
.sstt-sweep{position:absolute;top:-25%;left:-40%;width:38%;height:150%;pointer-events:none;
  background:linear-gradient(100deg,rgba(255,255,255,0),rgba(255,255,255,.85),rgba(255,255,255,0));
  filter:blur(28px);transform:translate3d(0,0,0) rotate(9deg);
  animation:sweep 2.4s var(--sstt-ease-out) .35s both}
@keyframes sweep{0%{transform:translate3d(0,0,0) rotate(9deg);opacity:0}18%{opacity:1}100%{transform:translate3d(320%,0,0) rotate(9deg);opacity:0}}
.sstt-hero-head{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:clamp(24px,4vw,64px)}
.sstt-hero-copy{max-width:1000px}
.sstt-hero-idx{display:flex;flex-direction:column;gap:10px;padding:0 0 8px 22px;border-left:1px solid var(--sstt-line);
  font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sstt-mist-t);white-space:nowrap;
  opacity:0;animation:rise 1s var(--sstt-ease-out) .5s forwards;transform:translateY(16px)}
.sstt-hero-idx b{position:relative;color:var(--sstt-ink);font-weight:500}
.sstt-hero-idx b::before{content:"";position:absolute;left:-23px;top:50%;width:10px;height:1px;background:var(--sstt-spectrum)}
.sstt-hero-idx .sstt-hi-loc{color:var(--sstt-mist-t);margin-bottom:6px}
@media(max-width:1024px){.sstt-hero-head{grid-template-columns:1fr}.sstt-hero-idx{display:none}}
.sstt-display{margin-top:16px;font-size:clamp(3rem,8.4vw,7.4rem);letter-spacing:-.048em;line-height:.87;text-transform:uppercase}
.sstt-display span{display:inline-block;opacity:0;transform:translateY(.34em) rotate(1.2deg);animation:rise .95s var(--sstt-ease-out) forwards}
.sstt-display span:nth-child(1){animation-delay:.06s}
.sstt-display span:nth-child(2){animation-delay:.16s}
.sstt-display span:nth-child(3){animation-delay:.26s}
@keyframes rise{to{opacity:1;transform:none}}
.sstt-display em{font-style:normal;background:linear-gradient(96deg,#2ECBF5 4%,#9A6BFF 34%,#FF4FA8 62%,#FFB43C 92%);-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}
.sstt-lede{margin-top:20px;max-width:56ch;font-size:clamp(14.5px,1.35vw,17px);color:var(--sstt-ink-2);line-height:1.62;opacity:0;animation:rise .9s var(--sstt-ease-out) .42s forwards;transform:translateY(14px);text-wrap:pretty}
.sstt-hero-grid{display:grid;grid-template-columns:1fr 1px 1fr;gap:clamp(22px,3.4vw,54px);margin-top:clamp(28px,3.6vw,48px)}
.sstt-vdiv{background:linear-gradient(180deg,transparent,var(--sstt-line) 18%,var(--sstt-line) 82%,transparent)}
@media(max-width:900px){.sstt-hero-grid{grid-template-columns:1fr;gap:44px}.sstt-vdiv{height:1px;background:linear-gradient(90deg,transparent,var(--sstt-line),transparent)}}

.sstt-feat{position:relative}
.sstt-feat-stage{position:relative;height:clamp(270px,28vw,390px);display:grid;place-items:center}
.sstt-shaft{position:absolute;left:50%;top:-14%;width:min(340px,74%);height:128%;margin-left:-24%;
  background:linear-gradient(180deg,rgba(255,255,255,.8),rgba(255,255,255,.12) 62%,transparent);
  filter:blur(26px);transform:rotate(-13deg) translate3d(calc(var(--sstt-px, 0) * -16px),calc(var(--sstt-py, 0) * -8px),0);
  opacity:.85;pointer-events:none}
.sstt-halo{position:absolute;width:min(560px,100%);aspect-ratio:1;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,rgba(255,255,255,.5) 0%,rgba(255,255,255,.24) 42%,rgba(255,255,255,0) 70%);
  transform:translate3d(calc(var(--sstt-px, 0) * 10px),calc(var(--sstt-py, 0) * 7px),0)}
.sstt-prism{position:absolute;width:min(430px,88%);aspect-ratio:1;border-radius:50%;pointer-events:none;opacity:.3;
  background:conic-gradient(from 210deg,rgba(46,203,245,.5),rgba(154,107,255,.42),rgba(255,79,168,.4),rgba(255,180,60,.45),rgba(46,203,245,.5));
  filter:blur(46px);
  transform:translate3d(calc(var(--sstt-px, 0) * -22px),calc(var(--sstt-py, 0) * -14px),0) rotate(0deg);
  animation:spin 34s linear infinite}
@keyframes spin{
  from{transform:translate3d(calc(var(--sstt-px, 0) * -22px),calc(var(--sstt-py, 0) * -14px),0) rotate(0deg)}
  to{transform:translate3d(calc(var(--sstt-px, 0) * -22px),calc(var(--sstt-py, 0) * -14px),0) rotate(360deg)}}
.sstt-floor{position:absolute;bottom:7%;width:min(340px,70%);height:46px;pointer-events:none;
  background:radial-gradient(ellipse at center,rgba(20,21,26,.34) 0%,rgba(20,21,26,.12) 44%,rgba(20,21,26,0) 74%);
  filter:blur(8px);
  transform:translate3d(calc(var(--sstt-px, 0) * 14px),0,0) scaleX(1.05)}
/* Product photos are shot on white; multiply dissolves that white into the light field.
   NOTE: a compositor-driven animation/transition ON a blended element breaks the blend in
   Chrome, so blended <img>s carry no animation — motion lives on their wrappers instead. */
.sstt-feat-img{position:relative;max-height:clamp(230px,26vw,350px);width:auto}
.sst-top__feat-a .sstt-feat-img{translate:calc(var(--sstt-px, 0) * 22px) calc(var(--sstt-py, 0) * 15px)}
.sst-top__feat-b .sstt-feat-img{border-radius:4px}
.sstt-feat-slab{position:relative;padding:9px 9px 10px;border-radius:9px;background:linear-gradient(168deg,#FFFEFC 0%,#FDFBF6 58%,#F6F2EB 100%);border:1px solid rgba(20,21,26,.11);
  /* No CSS keyframe animation here: a compositor-animated ancestor makes Chrome
     drop the <img> layer inside it. Pointer parallax below is a plain style write. */
  translate:calc(var(--sstt-px, 0) * 22px) calc(var(--sstt-py, 0) * 15px);
  transition:translate .5s var(--sstt-ease),scale .7s var(--sstt-ease);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.95),inset 0 0 0 1px rgba(255,255,255,.55),0 30px 54px -22px rgba(20,21,26,.42),0 2px 5px rgba(20,21,26,.07)}
/* Static specular sheen on the acrylic. Deliberately NOT animated: an infinitely
   animating overlay above a product <img> makes Chrome drop the image's raster
   under GPU memory pressure, and the slab renders empty. */
.sstt-feat-slab .sstt-glint{position:absolute;inset:0;border-radius:9px;overflow:hidden;pointer-events:none}
.sstt-feat-slab .sstt-glint::before{content:"";position:absolute;left:-14%;top:-30%;width:46%;height:170%;
  background:linear-gradient(96deg,transparent,rgba(255,255,255,.4),transparent);
  transform:rotate(11deg)}
.sstt-feat-slab .sstt-glint::after{content:"";position:absolute;right:-26%;top:-30%;width:26%;height:170%;
  background:linear-gradient(96deg,transparent,rgba(255,255,255,.26),transparent);
  transform:rotate(11deg)}
.sst-top__feat-b:hover .sstt-feat-slab{scale:1.022}
.sstt-feat-info{margin-top:clamp(22px,3vw,34px);opacity:0;transform:translateY(24px);animation:rise 1s var(--sstt-ease-out) both}
.sst-top__feat-a .sstt-feat-info{animation-delay:.56s}
.sst-top__feat-b .sstt-feat-info{animation-delay:.68s}
.sstt-flags{display:flex;flex-wrap:wrap;gap:7px}
/* balance stops the product name breaking inside a token: without it
   "PSA10 Pikachu 400/SM-P PROMO" splits at the hyphen as "…400/SM-" / "P PROMO" */
.sstt-feat-name{margin-top:16px;font-size:clamp(1.28rem,2.1vw,1.86rem);letter-spacing:-.028em;line-height:1.13;max-width:19ch;text-wrap:balance}
.sstt-spec-line{margin-top:12px;font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.16em;color:var(--sstt-mist-t);text-transform:uppercase}
.sstt-cue{margin-top:clamp(16px,2vw,24px);display:flex;align-items:center;gap:12px;font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.3em;color:var(--sstt-mist-t);text-transform:uppercase}
.sstt-cue span{display:block;width:52px;height:1px;background:linear-gradient(90deg,var(--sstt-mist),transparent);animation:cue 2.6s var(--sstt-ease) infinite}
@keyframes cue{0%,100%{transform:translateX(0);opacity:.4}50%{transform:translateX(9px);opacity:1}}

/* ---------- COVERFLOW ---------- */
.sstt-cf-sec{margin-top:clamp(14px,2.2vw,30px)}
.sstt-cf{position:relative}
/* overflow-x:clip (NOT hidden) contains the off-stage slabs and the prism glow at the
   viewport edges: `hidden` would force overflow-y to `auto` and cut the slab shadows,
   `clip` leaves the vertical axis visible and creates no scroll container. Without it
   the stage adds 130-350px of phantom horizontal scroll at <=1024px. */
.sstt-cf-stage{position:relative;height:var(--sstt-sh,540px);perspective:2000px;perspective-origin:50% 44%;touch-action:pan-y pinch-zoom;cursor:grab;user-select:none;-webkit-user-select:none;
  overflow-x:clip;overflow-y:visible}
.sstt-cf-stage.sstt-drag{cursor:grabbing}
/* P3 FIX (bug 3 — mobile smoothness): promote the slabs FOR THE GESTURE ONLY.
   The note further down is right that 10 permanently promoted layers this size
   exhaust GPU raster memory and make Chrome drop the card images — but its
   last sentence ("Chrome promotes them anyway for the duration of the transform
   animation") does not hold here: auto-promotion fires for declarative CSS
   animations/transitions, and these transforms are per-frame inline-style writes
   from JS, which do not qualify. So on mobile every slab — two outer shadows, two
   inset shadows, a gradient, a masked conic ring, plus the art — was being
   re-rastered on the CPU every frame. .sstt-drag is added on pointerdown and
   removed on pointerup/cancel, so the layers exist only while the finger is
   down, which respects the memory concern. */
.sstt-cf-stage.sstt-drag .sstt-slab{will-change:transform,opacity}
.sstt-cf-spot{position:absolute;left:50%;top:50%;width:min(880px,92%);height:70%;margin-left:max(-440px,-46%);transform:translateY(-42%);pointer-events:none;
  background:radial-gradient(ellipse at center,rgba(255,255,255,.9) 0%,rgba(255,255,255,.35) 40%,rgba(255,255,255,0) 70%)}
/* Pedestal glow: ONE blurred prismatic element behind the centre slab.
   Deliberately outside the cards — a blurred layer inside a 3D-transformed card
   makes Chrome drop the card's <img> layer, and it costs 10x more to composite. */
.sstt-cf-prism{position:absolute;left:50%;top:50%;
  width:calc(var(--sstt-cw,300px) * 1.72);height:calc(var(--sstt-ch,420px) * 1.28);
  margin-left:calc(var(--sstt-cw,300px) * -0.86);border-radius:44px;
  transform:translateY(-50%);pointer-events:none;opacity:.5;
  filter:blur(calc(var(--sstt-cw,300px) * 0.11));
  background:conic-gradient(from 140deg,rgba(46,203,245,.75),rgba(154,107,255,.62),rgba(255,79,168,.58),rgba(255,180,60,.68),rgba(46,203,245,.75));
  animation:spin2 46s linear infinite}
@keyframes spin2{from{transform:translateY(-50%) rotate(0deg)}to{transform:translateY(-50%) rotate(360deg)}}
.sstt-cf-floor{position:absolute;left:0;right:0;bottom:8%;height:1px;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--sstt-line) 22%,rgba(20,21,26,.22) 50%,var(--sstt-line) 78%,transparent)}
.sstt-cf-floor::after{content:"";position:absolute;left:0;right:0;top:0;height:80px;
  background:linear-gradient(180deg,rgba(20,21,26,.05),rgba(20,21,26,0));
  -webkit-mask:linear-gradient(90deg,transparent,#000 26%,#000 74%,transparent);
  mask:linear-gradient(90deg,transparent,#000 26%,#000 74%,transparent)}

/* NOTE: deliberately NOT transform-style:preserve-3d — combined with a blurred
   sibling layer it makes Chrome drop the card <img> entirely. The perspective on
   .sstt-cf-stage already gives the slabs their 3D rotation. */
/* No will-change here either: 10 permanently promoted layers this size exhaust GPU
   raster memory on large displays and Chrome silently drops the card images.
   Chrome promotes them anyway for the duration of the transform animation. */
.sstt-slab{position:absolute;left:50%;top:50%;width:var(--sstt-cw,300px);height:var(--sstt-ch,420px);
  margin-left:calc(var(--sstt-cw,300px)/-2);margin-top:calc(var(--sstt-ch,420px)/-2)}
.sstt-slab-in{position:absolute;inset:0;border-radius:11px;padding:8px 8px 9px;
  background:linear-gradient(168deg,#FFFEFC 0%,#F6F2EA 52%,#EDE8DE 100%);
  border:1px solid rgba(20,21,26,.10);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.95),inset 0 0 0 1px rgba(255,255,255,.5),0 34px 58px -26px rgba(20,21,26,.45),0 3px 8px rgba(20,21,26,.07);
  transition:transform .3s var(--sstt-ease),box-shadow .5s var(--sstt-ease);
  display:flex;flex-direction:column}
.sstt-slab.sstt-on .sstt-slab-in{box-shadow:inset 0 1px 0 rgba(255,255,255,.95),inset 0 0 0 1px rgba(255,255,255,.55),0 48px 74px -28px rgba(20,21,26,.5),0 4px 12px rgba(20,21,26,.09)}
@supports((mask-composite:exclude) or (-webkit-mask-composite:xor)){
  .sstt-slab-in::after{content:"";position:absolute;inset:0;border-radius:11px;padding:1px;pointer-events:none;opacity:0;transition:opacity .5s var(--sstt-ease);
    background:conic-gradient(from 130deg,rgba(46,203,245,.9),rgba(154,107,255,.85),rgba(255,79,168,.8),rgba(255,180,60,.9),rgba(46,203,245,.9));
    -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;
    mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude}
  .sstt-slab.sstt-on .sstt-slab-in::after{opacity:.85}
}
.sstt-slab-top{display:flex;align-items:center;gap:9px;padding:5px 6px 8px}
.sstt-psa-mk{font-weight:800;font-size:13px;letter-spacing:-.02em;line-height:1;padding:4px 5px;border:1.4px solid var(--sstt-ink);border-radius:2px}
.sstt-psa-nm{flex:1;min-width:0}
.sstt-psa-t{display:block;font-size:9.5px;font-weight:500;letter-spacing:.06em;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-transform:uppercase}
.sstt-psa-s{display:block;font-family:'IBM Plex Mono',monospace;font-size:7.5px;letter-spacing:.13em;color:var(--sstt-mist-t);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}
.sstt-psa-g{font-weight:800;font-size:19px;line-height:1;letter-spacing:-.05em}
.sstt-slab-win{position:relative;flex:1;min-height:0;border-radius:6px;overflow:hidden;
  background:radial-gradient(ellipse at 50% 28%,#FFFEFC,#F2EEE6 78%);
  box-shadow:inset 0 0 0 1px rgba(20,21,26,.07),inset 0 2px 8px rgba(20,21,26,.07)}
.sstt-slab-win img{position:absolute;inset:5%;width:90%;height:90%;object-fit:contain}
/* Prismatic refraction radiates from BEHIND the slab, so the card art is never covered.
   No mix-blend-mode on these: blended layers with compositor-driven opacity/transform
   composite against a stale backdrop on first paint in Chrome. */
/* pointer-tracked specular sweep across the acrylic — light, never opaque */
.sstt-glint2{position:absolute;left:-45%;top:-25%;width:52%;height:150%;pointer-events:none;opacity:0;
  background:linear-gradient(100deg,rgba(255,255,255,0),rgba(255,255,255,.34) 44%,rgba(255,255,255,.5) 52%,rgba(255,255,255,0));
  transform:rotate(13deg) translate3d(var(--sstt-gx,0),0,0)}
.sstt-slab.sstt-on .sstt-glint2{opacity:1}
.sstt-slab-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 6px 2px;font-family:'IBM Plex Mono',monospace;font-variant-numeric:tabular-nums;font-size:8px;letter-spacing:.16em;color:var(--sstt-mist-t);text-transform:uppercase}
.sstt-slab-foot b{color:var(--sstt-ink);font-weight:500;letter-spacing:.06em}
.sstt-haze{position:absolute;inset:-1px;border-radius:11px;background:linear-gradient(180deg,#F4F1EA,#EAE5DC);opacity:0;pointer-events:none}

.sstt-cf-nav{position:absolute;top:44%;z-index:40;width:52px;height:52px;border:1px solid rgba(20,21,26,.22);border-radius:50%;display:grid;place-items:center;background:rgba(255,253,249,.72);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);transition:transform .35s var(--sstt-ease),background .3s,border-color .3s}
.sstt-cf-nav svg{width:15px;height:15px;stroke:var(--sstt-ink);stroke-width:1.6;fill:none;stroke-linecap:round;stroke-linejoin:round}
.sstt-cf-nav:hover{background:var(--sstt-ink);border-color:var(--sstt-ink);transform:scale(1.08)}
.sstt-cf-nav:hover svg{stroke:var(--sstt-paper)}
.sstt-cf-prev{left:clamp(6px,2.5vw,44px)}
.sstt-cf-next{right:clamp(6px,2.5vw,44px)}

.sstt-cf-meta{position:relative;z-index:5;margin-top:clamp(18px,2.6vw,30px);display:grid;justify-items:center;text-align:center;gap:0}
.sstt-cf-idx{font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.24em;color:var(--sstt-mist-t)}
.sstt-cf-idx b{color:var(--sstt-ink);font-weight:500}
/* min-height is measured in JS from the tallest of the ten names at the current
   width: PSA names run 1 to 4 lines, and without a reserved box the Add button,
   tick rail and hint jumped up to 65px on every navigation. Contents are centred in
   the reserved box so short names don't leave the price stranded above the button. */
.sstt-cf-swap{display:grid;align-content:center;transition:opacity .26s var(--sstt-ease),transform .26s var(--sstt-ease)}
.sstt-cf-swap.sstt-out{opacity:0;transform:translateY(9px)}
.sstt-cf-name{margin-top:14px;font-size:clamp(1.15rem,1.9vw,1.7rem);letter-spacing:-.026em;line-height:1.2;max-width:22ch;overflow-wrap:break-word;text-wrap:balance}
.sstt-cf-price{margin-top:12px;font-family:'IBM Plex Mono',monospace;font-variant-numeric:tabular-nums;font-weight:500;font-size:clamp(1.25rem,2vw,1.7rem);letter-spacing:-.03em}
/* carousel buy block: the stock chip is stable (every PSA card is a single copy), so
   it lives OUTSIDE .sstt-cf-swap and is never animated on navigation. */
.sstt-cf-buy{width:100%;margin-top:18px;display:flex;flex-direction:column;align-items:center;gap:12px}
.sstt-cf-head{display:flex;align-items:center;gap:14px;flex-wrap:wrap;justify-content:center}
.sstt-cf-row{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:12px;width:100%}
.sstt-cf-add{min-width:210px}
.sstt-ticks{display:flex;align-items:center;gap:5px;margin-top:26px}
.sstt-tick{position:relative;width:16px;height:2px;background:rgba(20,21,26,.16);border-radius:1px;transition:width .45s var(--sstt-ease),background .45s var(--sstt-ease)}
/* a 2px-tall bar is a 2px-tall hit target; this pseudo-element pads it out to a 24px
   clickable box (2 + 11 + 11) without adding a single pixel to the layout.
   NOTE the 24 is the VERTICAL figure. Horizontally this is 16 + 2 + 2 = 20px on a
   21px pitch (width 16 + .sstt-ticks gap 5), so the boxes tile with 1px to spare
   and do not reach 24. Widening them means moving the gap to 8px, which widens
   the visible rail — deliberately NOT done in p5, which changed no p4 geometry. */
.sstt-tick::before{content:"";position:absolute;left:-2px;right:-2px;top:-11px;bottom:-11px}
/* height:2px above is a REQUEST, not a result, until the theme's
   `button{min-height:40px}` floor is closed. The rule that closes it lives with
   the rest of the reasoning under "THE BUTTON RESET" in the P5 section below.
   (Deliberately not repeating the rule text here: verify-public-p5.sh asserts
   it appears EXACTLY ONCE in the served file, so a comment quoting it verbatim
   would let a deploy that dropped the real rule still pass.) */
.sstt-tick:hover{background:rgba(20,21,26,.42)}
.sstt-tick.sstt-on{width:40px;background:var(--sstt-ink)}
.sstt-cf-hint{margin-top:18px;font-family:'IBM Plex Mono',monospace;font-size:9.5px;letter-spacing:.24em;color:var(--sstt-mist-t);text-transform:uppercase}

/* ---------- BOX PICKS ---------- */
.sstt-boxes{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(14px,1.7vw,26px);margin-top:clamp(34px,5vw,56px)}
@media(max-width:1080px){.sstt-boxes{grid-template-columns:repeat(2,1fr)}}
@media(max-width:520px){.sstt-boxes{grid-template-columns:1fr}}
.sstt-card{perspective:1000px}
.sstt-card-in{position:relative;height:100%;display:flex;flex-direction:column;border-radius:8px;padding:14px 14px 16px;
  background:linear-gradient(172deg,rgba(255,254,251,.92),rgba(240,236,228,.86));
  border:1px solid var(--sstt-line);
  box-shadow:0 18px 34px -24px rgba(20,21,26,.4);
  transform-style:preserve-3d;
  transition:transform .45s var(--sstt-ease),box-shadow .45s var(--sstt-ease),border-color .45s}
.sstt-card:hover .sstt-card-in{box-shadow:0 40px 62px -30px rgba(20,21,26,.5);border-color:rgba(20,21,26,.2)}
.sstt-card-in::after{content:"";position:absolute;inset:0;border-radius:8px;pointer-events:none;opacity:0;transition:opacity .45s var(--sstt-ease)}
@supports((mask-composite:exclude) or (-webkit-mask-composite:xor)){
  .sstt-card-in::after{padding:1px;background:conic-gradient(from 160deg,rgba(46,203,245,.85),rgba(154,107,255,.8),rgba(255,79,168,.75),rgba(255,180,60,.85),rgba(46,203,245,.85));
    -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;
    mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude}
  .sstt-card:hover .sstt-card-in::after{opacity:.9}
}
.sstt-card-stage{position:relative;aspect-ratio:1/.92;border-radius:5px;overflow:hidden;display:grid;place-items:center;
  background:linear-gradient(180deg,#FFFEFC 0%,#FDFBF7 62%,#F7F3EC 100%);
  box-shadow:inset 0 0 0 1px rgba(20,21,26,.05)}
.sstt-card-glow{position:absolute;width:120%;aspect-ratio:1;border-radius:50%;pointer-events:none;opacity:.34;filter:blur(34px);
  background:conic-gradient(from 180deg,rgba(46,203,245,.5),rgba(154,107,255,.4),rgba(255,79,168,.38),rgba(255,180,60,.48),rgba(46,203,245,.5));
  transform:scale(.7);transition:transform .7s var(--sstt-ease),opacity .7s}
.sstt-card:hover .sstt-card-glow{transform:scale(1);opacity:.8}
.sstt-card-stage img{position:relative;max-width:82%;max-height:82%;width:auto;height:auto;object-fit:contain}
.sstt-card-floor{position:absolute;bottom:9%;width:64%;height:26px;pointer-events:none;
  background:radial-gradient(ellipse at center,rgba(20,21,26,.26),rgba(20,21,26,0) 70%);filter:blur(5px)}
.sstt-card-rank{position:absolute;left:11px;top:10px;font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.16em;color:var(--sstt-mist-t)}
.sstt-card-badges{position:absolute;right:10px;top:10px;display:flex;flex-direction:column;gap:6px;align-items:flex-end}
.sstt-card-body{display:flex;flex-direction:column;flex:1;padding-top:15px}
.sstt-card-name{font-size:14.5px;font-weight:500;letter-spacing:-.014em;line-height:1.36;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:2.72em;overflow-wrap:break-word}

/* ---------- vault CTA (shared by both sections) ---------- */
/* flex, not grid. The CTA side is intrinsically sized (845px for the two-up box
   gateway) and a grid `auto` track will crush the headline column to 58px anywhere
   between 861px and 1420px. With flex-wrap the CTA drops onto its own line the moment
   the headline can no longer hold its 420px basis — no magic breakpoint, and the
   two-up composition at >=1420px is byte-for-byte what it was. */
.sstt-vault{position:relative;display:flex;flex-wrap:wrap;align-items:flex-end;gap:clamp(20px,3.4vw,52px);
  margin-top:clamp(10px,1.4vw,18px);margin-bottom:clamp(18px,2.4vw,30px);padding-bottom:clamp(16px,2vw,24px);border-bottom:1px solid var(--sstt-line)}
/* The lopsided grow factors are deliberate. Sharing a line, the headline takes
   essentially all the free space (1000:1), so the gateway buttons keep their natural
   width and the band headline keeps its measure. Once the CTA wraps onto its own line
   it has no competition, so its grow:1 fills the row instead of leaving a ragged edge. */
.sstt-vault>div:first-child{flex:1000 1 420px;min-width:0}
.sstt-vault-cta{flex:1 1 auto}
.sstt-vault-cta .sstt-btn{flex:1 1 auto}
.sstt-vault-h{margin-top:15px;font-size:clamp(1.7rem,3vw,2.8rem);letter-spacing:-.042em;line-height:1.02;text-transform:uppercase;text-wrap:balance}
.sstt-vault-h em{font-style:normal;background:linear-gradient(96deg,#2ECBF5 4%,#9A6BFF 34%,#FF4FA8 62%,#FFB43C 92%);-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}
.sstt-vault-p{margin-top:13px;max-width:50ch;font-family:'IBM Plex Mono',monospace;font-size:11.5px;letter-spacing:.16em;line-height:1.75;color:var(--sstt-mist-t);text-transform:uppercase;text-wrap:balance}
.sstt-vault-cta{display:flex;flex-wrap:wrap;gap:10px;padding-bottom:3px}
/* :not(.sstt-btn-xl) is load-bearing. `.sstt-vault-cta .sstt-btn` (0,2,0) outranks `.sstt-btn-xl` (0,1,0),
   so without it the tight 18px padding won over the gateway's 42px on every CTA inside
   a vault band — and the 900px `.sstt-btn-xl{padding:0 26px}` override below was dead too. */
.sstt-vault-cta .sstt-btn:not(.sstt-btn-xl){padding:0 18px}
.sstt-btn-n{display:inline-flex;align-items:center;height:21px;padding:0 8px;border-radius:2px;
  border:1px solid rgba(239,235,227,.32);background:rgba(239,235,227,.11);
  font-size:10.5px;font-weight:500;letter-spacing:.1em;
  transition:border-color .3s,background .3s}
.sstt-btn:hover .sstt-btn-n{border-color:rgba(239,235,227,.5);background:rgba(239,235,227,.19)}
/* gateway CTA — the "view all" funnel buttons */
.sstt-btn-xl{height:70px;padding:0 42px;gap:16px;font-size:14.5px;letter-spacing:.22em;font-weight:700;overflow:hidden}
.sstt-btn-xl::after{height:4px;transform:scaleX(1)}
.sstt-btn-xl .sstt-btn-n{height:28px;padding:0 11px;font-size:13.5px;border-color:rgba(239,235,227,.42)}
.sstt-btn-a{width:21px;height:21px;stroke:currentColor;stroke-width:2.2;fill:none;stroke-linecap:round;stroke-linejoin:round;transition:transform .3s var(--sstt-ease)}
.sstt-btn-xl:hover{box-shadow:0 20px 38px -16px rgba(20,21,26,.75),0 5px 20px -6px rgba(154,107,255,.3)}
.sstt-btn-xl:hover .sstt-btn-a{transform:translateX(6px)}
.sstt-hero-cta-row{margin-top:clamp(18px,2.4vw,28px);opacity:0;animation:rise .9s var(--sstt-ease-out) .58s forwards;transform:translateY(14px)}
@media(prefers-reduced-motion:no-preference){
  .sstt-btn-xl::before{content:"";position:absolute;top:0;bottom:0;left:0;width:30%;
    background:linear-gradient(105deg,transparent,rgba(255,255,255,.22),transparent);
    transform:translateX(-140%) skewX(-18deg);animation:xlsweep 3.6s cubic-bezier(.4,0,.2,1) 1.2s infinite}
  @keyframes xlsweep{0%{transform:translateX(-140%) skewX(-18deg)}42%,100%{transform:translateX(480%) skewX(-18deg)}}
}
@media(max-width:900px){.sstt-btn-xl{height:60px;padding:0 26px;font-size:12.5px}}
@media(max-width:860px){
  .sstt-vault{gap:26px}
  .sstt-vault-cta{padding-bottom:0;flex:1 1 100%}
  .sstt-vault-cta .sstt-btn{flex:1 1 230px}
}
@media(max-width:520px){.sstt-vault-cta .sstt-btn{flex:1 1 100%}}

/* ============================================================
   COMMERCE LAYER — wall-label offer rows, steppers, lacquer drawer
   Every number rendered here comes from the measured offer set; nothing
   in this layer computes, rounds or infers a price.
   ============================================================ */
.sstt-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}

/* One variation = one ruled row on a museum wall label: label + stock on the
   upper line, money + controls below. No boxes and no fills anywhere — the
   hairline carries the structure, which is what keeps four of these in a grid
   from reading as a spreadsheet. */
.sstt-offers{margin-top:clamp(16px,2vw,22px)}
.sstt-offer{position:relative;padding-top:14px;border-top:1px solid var(--sstt-line-2)}
.sstt-offer:first-child{border-top-color:var(--sstt-line)}
.sstt-offer+.sstt-offer{margin-top:13px}
/* the row's own hairline wipes to spectrum on hover / keyboard focus — scaleX from
   the left, exactly as .sstt-btn::after already does it, so it composites
   instead of relaying out and needs no new vocabulary */
.sstt-offer::before{content:"";position:absolute;left:0;top:-1px;width:100%;height:1px;background:var(--sstt-spectrum);
  transform:scaleX(0);transform-origin:0 50%;transition:transform .55s var(--sstt-ease)}
.sstt-offer:hover::before,.sstt-offer:focus-within::before{transform:scaleX(1)}
.sstt-offer-head{display:flex;align-items:center;justify-content:space-between;gap:8px 12px;flex-wrap:wrap}
.sstt-offer-l{font-family:'IBM Plex Mono',monospace;font-size:10px;font-weight:500;letter-spacing:.22em;text-transform:uppercase;color:var(--sstt-ink)}
/* stock chip. --sstt-mist-t clears 4.5:1 on every paper stop and on the card fill;
   a low count swaps the dot to magenta and inks the text — the accent is carried
   by the mark, never by low-contrast coloured type. */
.sstt-stock{display:inline-flex;align-items:center;gap:6px;font-family:'IBM Plex Mono',monospace;font-variant-numeric:tabular-nums;font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--sstt-mist-t);white-space:nowrap}
.sstt-stock i{flex:0 0 5px;width:5px;height:5px;border-radius:50%;background:#2ECBF5;box-shadow:0 0 0 3px rgba(46,203,245,.18);font-style:normal}
.sstt-stock.sstt-low{color:var(--sstt-ink)}
.sstt-stock.sstt-low i{background:var(--sstt-mg);box-shadow:0 0 0 3px rgba(255,79,168,.24)}
.sstt-offer-buy{display:flex;align-items:center;flex-wrap:wrap;gap:10px 14px;margin-top:10px}
.sstt-offer-money{display:flex;align-items:baseline;gap:10px;min-width:0}
.sstt-offer-y{font-family:'IBM Plex Mono',monospace;font-variant-numeric:tabular-nums;font-weight:500;font-size:clamp(1.32rem,2.05vw,1.8rem);letter-spacing:-.03em;line-height:1}
.sstt-offer-w{font-family:'IBM Plex Mono',monospace;font-variant-numeric:tabular-nums;font-size:12px;color:var(--sstt-mist-t);text-decoration:line-through;text-decoration-thickness:1px}
/* 210px basis, not a breakpoint: the controls drop to their own line exactly when
   they stop fitting beside the price, at any column width from 320 to 1920. */
.sstt-offer-act{display:flex;align-items:center;gap:10px;flex:1 1 210px;justify-content:flex-end}
.sstt-oadd{flex:1 1 auto;height:44px;padding:0 20px;font-size:10.5px;max-width:300px;white-space:nowrap}

/* compact variant for the box grid: money always takes its own line so the
   stepper + add pair never has to fight a 6-figure price for 200px of card */
.sstt-offers.sstt-sm{margin-top:auto;padding-top:2px}
.sstt-offers.sstt-sm .sstt-offer{padding-top:12px}
.sstt-offers.sstt-sm .sstt-offer+.sstt-offer{margin-top:12px}
.sstt-offers.sstt-sm .sstt-offer-y{font-size:16.5px}
.sstt-offers.sstt-sm .sstt-offer-w{font-size:11px}
.sstt-offers.sstt-sm .sstt-offer-money{flex:1 1 100%}
.sstt-offers.sstt-sm .sstt-offer-buy{gap:8px 10px;margin-top:8px}
.sstt-offers.sstt-sm .sstt-offer-act{flex:1 1 100%;justify-content:flex-start}
.sstt-offers.sstt-sm .sstt-oadd{height:34px;padding:0 14px;font-size:10px;letter-spacing:.16em;max-width:none}

/* ---------- qty stepper ---------- */
.sstt-qty{display:inline-flex;align-items:center;flex:0 0 auto;border:1px solid var(--sstt-line);border-radius:2px;background:rgba(255,253,249,.55);transition:border-color .3s}
.sstt-qty:hover{border-color:rgba(20,21,26,.3)}
.sstt-qty-b{width:32px;height:32px;display:grid;place-items:center;color:var(--sstt-ink);transition:background .25s var(--sstt-ease),color .25s}
.sstt-qty-b svg{width:11px;height:11px;stroke:currentColor;stroke-width:1.9;fill:none;stroke-linecap:round}
.sstt-qty-b:hover:not(:disabled){background:var(--sstt-ink);color:var(--sstt-paper)}
.sstt-qty-b:disabled{opacity:.26;cursor:default}
/* P3 REVIEW: explicit [hidden] guards for the two elements this release toggles
   via the `hidden` IDL property. The UA rule is `[hidden]{display:none}`, and an
   AUTHOR `display` declaration beats the UA sheet at ANY specificity — so
   `.sstt-qty{display:inline-flex}` above would silently defeat `q.hidden=true`.
   .sstt-offer-w declares no display and works without this, but it is guarded
   too so a future `display:` on it cannot resurrect an empty struck price.
   Same pattern as .sstt-dw-x[hidden] further down. */
.sstt-qty[hidden],.sstt-offer-w[hidden]{display:none}
/* P3 REVIEW FIX, same cause as .sstt-btn:hover above. The enabled state is
   guarded by :not(:disabled) at (0,3,0), but the DISABLED one had no hover rule
   at all, so style.min.css's bare `button:hover` (0,1,1) beat
   `.sstt-qty-b{color:var(--sstt-ink)}` (0,1,0) and lit a greyed-out minus
   button up as white-on-#7a2149 — a dead control that looks interactive. */
.sstt-qty-b:disabled:hover{background:none;color:var(--sstt-ink)}
.sstt-qty-v{width:42px;height:32px;padding:0;margin:0;border:0;border-left:1px solid var(--sstt-line-2);border-right:1px solid var(--sstt-line-2);
  background:transparent;color:var(--sstt-ink);text-align:center;font-size:12.5px;line-height:32px;display:block}
input.sstt-qty-v{-moz-appearance:textfield;border-radius:0}
.sstt-qty-v:focus-visible{outline:2px solid var(--sstt-ink);outline-offset:-2px}
.sstt-qty.sstt-qs .sstt-qty-b{width:28px;height:28px}
.sstt-qty.sstt-qs .sstt-qty-v{width:36px;height:28px;line-height:28px;font-size:12px}
/* a single-copy item still shows the control, greyed at 1: the disabled ends say
   "one only" with no extra words */
.sstt-qty.sstt-fixed{background:transparent}

/* ---------- fly-to-cart ---------- */
.sstt-fly{position:fixed;z-index:350;width:56px;height:56px;pointer-events:none;border-radius:4px;overflow:hidden;
  background:linear-gradient(168deg,#FFFEFC,#F3EFE7);border:1px solid rgba(20,21,26,.12);
  box-shadow:0 18px 32px -12px rgba(20,21,26,.55)}
.sstt-fly img{width:100%;height:100%;object-fit:contain;padding:4px}

/* ---------- toasts ---------- */
.sstt-toasts{position:fixed;right:22px;bottom:22px;z-index:400;display:flex;flex-direction:column;gap:10px;align-items:flex-end;pointer-events:none}
.sstt-toast{display:flex;align-items:stretch;gap:12px;min-width:250px;max-width:330px;padding:0;border-radius:3px;overflow:hidden;
  background:rgba(255,253,249,.94);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  border:1px solid var(--sstt-line);box-shadow:0 24px 46px -22px rgba(20,21,26,.55);
  transform:translateY(16px) scale(.97);opacity:0;transition:transform .5s var(--sstt-ease-out),opacity .4s}
.sstt-toast.sstt-in{transform:none;opacity:1}
.sstt-toast .sstt-bar{width:3px;flex:0 0 3px;background:var(--sstt-spectrum)}
.sstt-toast .sstt-txt{padding:12px 16px 13px}
.sstt-toast b{display:block;font-size:11px;font-weight:500;letter-spacing:.19em;text-transform:uppercase}
.sstt-toast i{display:block;margin-top:5px;font-style:normal;font-size:12px;line-height:1.4;color:var(--sstt-ink-2);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:break-word}
.sstt-toast .sstt-tmeta{display:block;margin-top:6px;font-family:'IBM Plex Mono',monospace;font-variant-numeric:tabular-nums;
  font-size:9.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--sstt-mist-t)}
/* the floating order button owns the bottom-right corner once it appears */

/* With the drawer open the only toast that can fire is the checkout confirmation —
   and at 22px it lands exactly on the CHECKOUT button it is confirming. Lift it clear
   of the drawer's footer (measured on open into --sstt-dwfoot) so it reads as a slip of
   paper laid on the panel instead of a sticker over its own controls. */

@media(max-width:560px){
  .sstt-toasts{left:14px;right:14px;bottom:14px;align-items:stretch}
  .sstt-toast{max-width:none}

  
}

@media(max-width:900px){
  .sstt-cf-nav{width:44px;height:44px;top:42%}
}
@media(max-width:560px){
  .sstt-cf-nav{display:none}
  .sstt-cf-add{min-width:0;flex:1 1 100%}
}
@media(max-width:560px){
  .sstt-nav-mob{display:flex!important}
}
.sstt-nav-mob{display:none;gap:12px;margin-top:20px}
.sstt-nav-mob .sstt-cf-nav{position:static;display:grid}

/* Forced-colors (Windows High Contrast) drops background images, so the gradient-clipped
   headline words would render with -webkit-text-fill-color:transparent over nothing and
   vanish. Hand them back to the system text colour. */
@media (forced-colors:active){
  .sstt-display em,.sstt-vault-h em{-webkit-text-fill-color:currentColor;color:CanvasText}
}

/* Scoped deliberately. The sample could use a bare `*` because it was a
   standalone document; here an unscoped !important reset would stop animations
   and transitions site-wide for every reduced-motion visitor — the header, the
   home slider, the sale countdown, WooCommerce, and any infinite spinner
   (animation-iteration-count:1 halts them after one cycle). The body-level
   nodes this file appends are listed explicitly because they live outside
   .sst-top. */
@media(prefers-reduced-motion:reduce){
  .sst-top,.sst-top *,.sst-top *::before,.sst-top *::after,.sstt-toasts,.sstt-toasts *,.sstt-fly,.sstt-fly *{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
  .sstt-display span,.sstt-lede,.sstt-feat,.sstt-feat-info,.sstt-hero-idx,.sstt-feat-slab,.sstt-hero-cta-row{opacity:1!important;transform:none!important}
  /* neither animation-delay nor transition-delay is covered by the duration override
     above: without this, anything starting at opacity:0 flashes in late, and the
     drawer's visibility-by-delay scheme would keep the closed panel visible for .58s */
  .sst-top,.sst-top *,.sst-top *::before,.sst-top *::after,.sstt-toasts,.sstt-toasts *,.sstt-fly{animation-delay:0s!important;transition-delay:0s!important}
  .sstt-rev{opacity:1!important;transform:none!important}
  .sstt-sweep{display:none}
}

/* ==========================================================================
   INTEGRATION LAYER — how TOP sits inside the live Quick Order
   ========================================================================== */

/* ---------- tab-scoped visibility ----------
   The TOP block is deliberately NOT a `.sst-qo__group`: quick-order.js's
   groupCanShowInActiveView() returns true for any non-hot/non-sale group under
   the ALL tab (data-exclude-all only gates lazy groups), so a real group would
   have leaked TOP's rows into ALL and inflated the ALL headline count by 16.
   Keeping it outside that class-set means QO's group machinery never sees it —
   no counting, no ALL leak, no toolbar/pagination/stock-filter interference —
   and TOP's own visibility is decided here, declaratively, with no dependence
   on JS timing (so there is no frame where TOP flashes on the wrong tab).
   data-active-tab is mirrored onto the QO root by top-tab.js. */
.sst-top-block{display:none}
.sst-qo[data-active-tab="top"] .sst-top-block{display:block}

/* The curated view has no catalogue count, no toolbar and no search. Hide the
   headline counter while TOP is active — the same call the approved sample's
   integration build made. */
.sst-qo[data-active-tab="top"] .sst-qo__count-top{visibility:hidden}

/* ===========================================================================
 * PHASE 2 (2026-08-06) — TOP is the default view; ALL is retired from view.
 * ========================================================================= */

/* ALL stays in the DOM and stays clickable by script. It is the recovery target
   if top-tab.js ever throws (see the catch at the foot of that file), it keeps
   quick-order.js's per-button click bindings identical to phase 1, and it makes
   the rollback a one-line PHP change.

   The `hidden` attribute PHP writes is on its own already sufficient — checked
   against live: quick-order-critical.min.css carries an explicit
   `.sst-qo__tab[hidden],.sst-qo__tabs[hidden]{display:none}` rule, and
   setActive() only ever writes .hidden on SUBGROUP tabs (quick-order.js
   2618-2622), never on family tabs, so nothing un-hides it. This class is
   therefore deliberate redundancy, not load-bearing: it is the greppable marker
   for "retired from view", and it gives the failsafe a second thing to undo. */
.sst-qo .sst-qo__tab--family.sst-qo__tab--retired{display:none !important}

/* FIRST PAINT. PHP writes data-active-tab="top" onto the .sst-qo root, so every
   catalogue group — including the HOT block, whose only viewer was the ALL
   view — is hidden from the very first frame.

   This is the only rule that covers the whole window. Three things happen in
   order on a cold load: the server HTML arrives with all groups visible;
   quick-order.js runs its own boot setActive('all') at the end of its file;
   top-tab.js then clicks TOP. A rule keyed off any class that setActive()
   toggles would flash the full ALL catalogue somewhere in that sequence. This
   one cannot, because the attribute is already in the HTML and stays put.

   It stops applying the instant the visitor picks another tab, because
   top-tab.js mirrors the newly-pressed tab onto data-active-tab. */
.sst-qo[data-active-tab="top"] .sst-qo__group{display:none !important}
/* setActive() clears `hidden` on the subgroup strip for any family outside
   all/sale/psa10, then hides each button individually because none of them
   carries data-qo-family="top" — leaving an empty, padded tab band above the
   hero. 'top' cannot be added to that list without editing quick-order.js. */
.sst-qo[data-active-tab="top"] [data-qo-subgroup-tabs]{display:none !important}

/* the 1fr/320px layout loses its second column: the panel leaves the flow */

/* one ring colour for the whole panel: the site's dark focus ring is invisible
   on lacquer */

/* --- head --- */

/* the close control is injected by top-tab.js — the live panel has none,
   because as a sticky sidebar it never needed one */

/* --- body: the staged item list --- */

/* --- pre-order notice --- */

/* --- foot: totals, bulk meter, checkout --- */

/* the bulk-discount meter — value and geometry stay exactly as
   renderQoBulkDiscount() computes them; only the paint changes */

/* Styled in advance — see the FX note at the top of this section. No PHP
   renders [data-fx-converter] today, so these rules match nothing yet. */

/* the checkout button keeps [data-action="checkout"], so it still runs
   submitStaged() -> validate_checkout and the stock-reconciliation panel */

/* ---------- reduced motion ---------- */

/* =======================================================================
 * P3 — FULL-BLEED TOP TAB (client-approved, 2026-08-06)
 *
 * The TOP tab used to sit in a 1380px-capped .sstt-wrap nested inside the
 * Quick Order shell's own caps. Three separate constraints had to go:
 *
 *   1. .sstt-wrap's own `min(1380px, ...)`            -> handled at its rule
 *   2. .sst-qo__inner `width:min(1520px,100vw-32px)!important`
 *      (quick-order-critical.min.css, @media min-width:1025px)
 *   3. .sst-qo's `padding:48px 16px 64px`  -- ABOVE 749px ONLY. Below that,
 *      quick-order-critical.min.css's LAST .sst-qo rule is
 *      `padding:8px 8px calc(78px + env(safe-area-inset-bottom))!important`
 *      inside @media(max-width:749px), and !important beats this file's
 *      non-important longhands at any specificity. So on phones an 8px side
 *      pad survives, giving ~24px of total inset once .sstt-wrap's own 16px
 *      gutter is added. That is INTENTIONALLY left alone: 24px is inside the
 *      approved 16-24px safety gutter, phones never had the wide gutters this
 *      change exists to remove, and the 390px evidence is clean. If true
 *      edge-to-edge is ever wanted on mobile, the fix is `!important` on the
 *      two longhands below -- and .sst-qo__head's gutter must move with it.
 *
 * (2) and (3) belong to the shared Quick Order shell, which also renders the
 * ALL / SALE / PSA10 tabs and must not change there. Everything below is
 * therefore scoped to `.sst-qo[data-active-tab="top"]`, which is emitted only
 * when the TOP block is non-empty and — because samurai_sword_qo_top_enabled()
 * is is_front_page() — only ever on the home page. No other tab and no other
 * page can be reached by these rules.
 *
 * NOTE ON 100vw: deliberately not used anywhere here. lockScroll() sets
 * <html>{overflow:hidden} plus a scrollbar-width paddingRight when the cart
 * drawer opens; a 100vw-sized child would then overflow by the scrollbar width
 * and pop a horizontal scrollbar every time the cart opened. Because
 * #quick-order is already a body-level block, plain `width:100%` gives true
 * full-bleed with none of that.
 * ===================================================================== */
.sst-qo[data-active-tab="top"]{padding-left:0;padding-right:0}
.sst-qo[data-active-tab="top"] .sst-qo__inner{
  width:100%!important;max-width:none!important;margin-left:0!important;margin-right:0!important}

/* The tab strip / heading keep the same safety gutter as the content below.
   P3 REVIEW FIX: this was a flat 24px, which only matched .sstt-wrap's gutter
   at >=1412px (where the clamp below tops out at 48/2 = 24). Everywhere below
   that the wrap gutter is smaller and the head sat up to 8px further in than
   the content under it. Deriving both from the same clamp keeps the strip and
   the content on one vertical line at every width. */
.sst-qo[data-active-tab="top"] .sst-qo__head{
  padding-left:clamp(16px,1.7vw,24px);padding-right:clamp(16px,1.7vw,24px)}
/* sst-top-tab-tabs-gutter-20260825: the family strip (TOP / SALE / PSA10 ...)
   lives in .sst-qo__layout > .sst-qo__main, NOT in .sst-qo__head, so the gutter
   above never reached it: with the shell's side padding zeroed for full-bleed
   TOP, the strip sat at x=0 while the heading and the content under it sat at
   the gutter (screenshot 2026-08-25: "Quick Order" at 70px, "TOP" at 18px).
   Client choice 2026-08-25: the strip AND its 1px border-bottom both stop at
   the gutter, so this is margin, not padding. The shell declares
   `.sst-qo__tabs{width:100%!important;max-width:100%!important}` (critical css),
   which would overflow by the margins, hence the matching !important on
   width/max-width. Only left/right longhands are touched: the shell's
   margin-bottom rules keep winning untouched. */
.sst-qo[data-active-tab="top"] .sst-qo__tabs--family{
  width:auto!important;max-width:none!important;
  margin-left:clamp(16px,1.7vw,24px)!important;margin-right:clamp(16px,1.7vw,24px)!important}

/* =======================================================================
 * P8 — SHELL WIDTH PARITY ACROSS TABS (client-approved, 2026-08-25)
 *
 * TOP went full-bleed in P3 but SALE / PSA10 / POKEMON / ... stayed in the
 * shell's `min(1520px,100vw-32px)` cap, so every tab switch off TOP snapped
 * the whole block ~400px narrower at 1920 ("きゅっと縮む"). The client picked
 * the full-bleed width for every tab (2026-08-25, side-by-side at 1920).
 *
 * Same three shell constraints as P3, lifted for every tab, then the gutter
 * put back on each direct child of .sst-qo__inner that actually renders:
 *   .sst-qo__head            heading row (was TOP-only above)
 *   .sst-qo__how / .sst-qons the "How to Order" / "NO SHRINK" mode panels —
 *                            siblings of the layout, NOT inside it, so they
 *                            need their own gutter (they also render while
 *                            data-active-tab="top", hence not :not(top)-scoped)
 *   .sst-qo__layout          catalogue + cart aside — every tab EXCEPT TOP,
 *                            whose block is edge-to-edge with its own .sstt-wrap
 * The gutter is the P3 clamp / 70px pair so the tab strip, heading and content
 * sit on one vertical line whichever tab is active. Grid stays repeat(5,1fr)
 * (critical css): the cards widen, the column count does not change.
 *
 * Scope: `.sst-qo[data-active-tab]` — the attribute exists only on the home
 * page when the TOP block is rendered (see P3 note), so no other page moves.
 * Mobile (<=749px) is deliberately untouched: the shell's `padding:8px 8px
 * ...!important` wins there anyway and phones never had the 1520 cap.
 * 750-1024px: the shell padding was 16px and the clamp floor is 16px, so that
 * band is visually unchanged. Rollback: delete this block (and the
 * top-tab.css enqueue version bump that shipped with it).
 * ===================================================================== */
@media(min-width:750px){
  .sst-qo[data-active-tab]{padding-left:0;padding-right:0}
  .sst-qo[data-active-tab] .sst-qo__inner{
    width:100%!important;max-width:none!important;margin-left:0!important;margin-right:0!important}
  .sst-qo[data-active-tab] .sst-qo__head,
  .sst-qo[data-active-tab] .sst-qo__how,
  .sst-qo[data-active-tab] .sst-qons,
  .sst-qo[data-active-tab]:not([data-active-tab="top"]) .sst-qo__layout{
    box-sizing:border-box;
    padding-left:clamp(16px,1.7vw,24px);padding-right:clamp(16px,1.7vw,24px)}
}
@media(min-width:1800px){
  .sst-qo[data-active-tab] .sst-qo__head,
  .sst-qo[data-active-tab] .sst-qo__how,
  .sst-qo[data-active-tab] .sst-qons,
  .sst-qo[data-active-tab]:not([data-active-tab="top"]) .sst-qo__layout{
    padding-left:70px;padding-right:70px}
}

/* ---------- wide-viewport composition ----------
   top-tab.css previously had ZERO min-width queries — the "default" branch was
   the desktop branch and nothing above 1320px existed. With the width cap gone,
   a layout tuned for 1380px would otherwise stretch a 4-up box grid to ~625px
   cards and run headlines to a 1900px measure. These blocks re-balance it. */
@media(min-width:1600px){
  /* NOTE: the box grid deliberately stays at repeat(4,1fr). The TOP block
     renders exactly FOUR box picks (measured live: .sstt-boxes has 4 children),
     so a 5- or 6-column track list would leave 1-2 permanently empty cells and
     shrink the cards for no reason. Four across that grow with the container is
     what "the box grid widens accordingly" actually means here. If the number of
     picks ever becomes dynamic, revisit this with auto-fit rather than a count. */
  /* the hero copy may breathe, but not to an unreadable measure */
  .sstt-hero-copy{max-width:1240px}
  /* stop the vault headline column eating every pixel of free space, so the
     band still reads as headline-left / CTA-right rather than a stretched line.
     P3 REVIEW FIX: capping ONLY the headline was not enough. .sstt-vault is a
     flex row whose CTA is `flex:1 1 auto` (:529/:530), so every pixel the
     headline gave up landed on the button instead — measured 1188x70 at 2560px,
     which is a stretched CTA, i.e. the exact thing the cap was meant to avoid,
     just moved one element to the right. Capping the button too is what
     actually produces headline-left / CTA-right. */
  .sstt-vault>div:first-child{max-width:1180px}
  .sstt-vault-cta .sstt-btn{max-width:520px}
}
@media(min-width:1800px){
  /* restore a real gutter so the side rails have a lane clear of the content */
  .sstt-wrap{width:calc(100% - 140px)}
  .sst-qo[data-active-tab="top"] .sst-qo__head{padding-left:70px;padding-right:70px}
  .sst-qo[data-active-tab="top"] .sst-qo__tabs--family{margin-left:70px!important;margin-right:70px!important}
}
@media(min-width:2200px){
  .sstt-hero-copy{max-width:1360px}
}

/* ---------- P4.1 the floating order trigger ----------
   --sstt-fab-b / --sstt-fab-r are written by placeFab() in top-tab.js from the
   help-chat launcher's measured box. The fallbacks below are what placeFab()
   arrives at on its own, so the trigger is still correctly placed if the vars
   are never set. `!important` is required on bottom/right: the shell's
   `.sst-qo__fab{bottom:60px !important}` cannot be outranked any other way. */

@media(max-width:749px){
  /* the launcher sits at bottom:100px here, so the corner below it is free */
  
}
/* the toasts stack directly above the trigger; derive that offset from the
   same variable instead of the phase-3 hard-coded 90px, which assumed 22px */

/* ---------- P4.3 the sheet ---------- */
@media(max-width:1024px){

  /* the scrim has to clear the z-index:1000 the shell forces onto the panel */

  
  
  /* while a finger is on the handle the transform is driven from JS */
  
  /* the spectrum rail turns from a left edge into the sheet's top hairline */

  /* The cart becomes a plain flex column again.
     align-items MUST be restated. The shell's summary-bar rule sets
     `align-items:center!important` for its 3-column grid; left in place on a
     flex COLUMN it centres every child horizontally and shrinks each one to
     its content width — measured at 768px before this line was added, the
     header came out 286px wide and the item list 252px, floating in the
     middle of a 768px sheet. Same story for gap and justify-content. */

  

  /* --- head --- */

  
  /* the shell's own close control would be a second, differently-styled X */

  /* --- body: the one scrolling region --- */

  

  /* --- optional blocks: shown ONLY when not [hidden] --- */

  
  /* sst-qo-rank-gauge-20260820: シートは --expanded を持たないので、qo-rank.css の
     <=1024px 折りたたみルールに当たって消える。バルクゲージと同じくここで戻す。
     bulk と違い display:grid(パネルは copy + meter の2段)。 */

  /* --- foot --- */

  

  

  

  /* --- THE GUARD, deliberately last ---
     Everything above re-enables children the shell hid. quick-order.js also
     uses the `hidden` attribute for real, per-state hiding (empty bulk meter,
     no stock panel, no pre-order notice). Those must stay hidden, and this
     rule is last so it wins the ties. */
  
}

/* ---------- P4.4 reduced motion ---------- */

/* =========================================================================
 * P5 — PRODUCT MEDIA GALLERY
 *
 * Museum restraint. The page already owns three carefully lit ways of showing
 * a product, and none of them asked for a widget on top. So the gallery adds
 * no box, no frame and no background of its own: the artwork keeps its stage,
 * and the only new marks are a pair of arrows borrowed from .sstt-cf-nav, a
 * dot rail borrowed from .sstt-tick, and a mono counter borrowed from
 * .sstt-cf-idx. Nothing here invents an idiom the page did not already have.
 *
 * GEOMETRY — the one rule everything follows. The existing image sizing on all
 * three surfaces is written in PERCENTAGES against a specific box:
 *   .sstt-card-stage img  -> max-width/height 82%
 *   .sstt-slab-win img    -> inset 5%, 90% x 90%
 * A wrapper dropped between that box and the image would silently re-anchor
 * those percentages and every product would change size. So on those two
 * surfaces the gallery and its frames are absolutely positioned to inset:0 of
 * the original box — same box, same percentages, same rendering. It also means
 * the frames need no sizing rules of their own for <img>, because
 * `.sstt-card-stage img` and `.sstt-slab-win img` are element selectors that
 * go on matching frames 1..N exactly as they matched the primary. Only <video>
 * and the hero (whose rule is the CLASS .sstt-feat-img, which frames 1..N do
 * not carry) need explicit rules.
 *
 * The hero is the exception that keeps its in-flow frame 0: .sstt-feat-stage
 * has a height but the artwork inside it is sized by the bitmap, so frame 0
 * has to go on holding the box open. `display:inline-grid` is doing two jobs
 * there — inline-LEVEL so hero B's acrylic slab, which is sized entirely by
 * what is inside it, sees the same kind of box it saw when the <img> was its
 * direct child; and GRID so the image inside frame 0 is blockified and adds no
 * inline descender gap of its own.
 *
 * ZERO LAYOUT SHIFT. Nothing in here can move the page. Frames 1..N are out of
 * flow on every surface. Frame 0 is the primary photo, unchanged. The control
 * layer is absolutely positioned. The box is reserved before any gallery byte
 * arrives — by .sstt-card-stage's aspect-ratio, by .sstt-slab-win's flex, and
 * on the hero by the primary itself, now carrying the width/height attributes
 * mountGallery() copies off the contract.
 *
 * QUIET. Arrows are invisible until the surface is hovered, focused into, or
 * touched — and permanently visible where there is no hover to wait for. The
 * dot rail and counter are NOT hidden with them: a position readout is
 * information, not chrome, and it is the only thing telling a visitor there is
 * more than one photograph here. It rests at .62 and comes up to full on hover.
 * ====================================================================== */

/* ---------- the gallery box ---------- */
.sstt-g{position:relative;--sstt-gd:1}
.sstt-g--swipe{touch-action:pan-y pinch-zoom}

.sstt-g-f{position:absolute;inset:0;display:grid;place-items:center;
  opacity:0;pointer-events:none;
  transform:translate3d(calc(var(--sstt-gd) * 14px),0,0);
  transition:opacity .42s var(--sstt-ease),transform .42s var(--sstt-ease)}
.sstt-g-f.sstt-g-on{opacity:1;pointer-events:auto;transform:translate3d(0,0,0);z-index:2}
/* the frame being left travels the other way, so the pair reads as one move */
.sstt-g-f.sstt-g-out{transform:translate3d(calc(var(--sstt-gd) * -14px),0,0);z-index:1}
/* Transitions off for the duration of a swipe: the drag writes inline
   transform/opacity every pointermove and a transition would lag the finger.
   releaseDrag() removes this class and forces one style flush before it clears
   the inline styles, which is what gives the release something to animate. */
.sstt-g--drag .sstt-g-f{transition:none}

/* ---------- hero ---------- */
/* BLOCK-level grid, not inline-grid. Measured: hero B's acrylic slab is sized
   entirely by what is inside it, and the theme ships `img{display:block}`, so
   today the slab's height is exactly image + padding + border with no line box
   involved. An inline-level wrapper reintroduces the strut and its descent and
   made the slab 6.8px taller, which moved the photograph 3.4px up inside a
   centred stage. `width:fit-content` keeps the shrink-wrap that the slab needs
   while `display:grid` keeps the box out of inline layout entirely. */
.sstt-g--hero{display:grid;place-items:center;width:fit-content}
.sstt-g--hero .sstt-g-f--first{position:relative;inset:auto}
/* frames 1..N have no .sstt-feat-img class, so they need the clamp restated —
   as 100% of the box frame 0 opened, which IS that clamp, resolved */
.sstt-g--hero .sstt-g-i{max-width:100%;max-height:100%;
  width:auto;height:auto;object-fit:contain}
/* The photo reserves the hero box. Keep the video's intrinsic dimensions out
   of grid sizing when metadata replaces a landscape poster with portrait video. */
.sstt-g--hero .sstt-g-v{position:absolute;inset:0;width:100%;height:100%;
  max-width:100%;max-height:100%;object-fit:contain}
/* hero A's parallax moves to the gallery box so every frame drifts together;
   without this only the primary would answer the pointer */
.sst-top__feat-a .sstt-g{translate:calc(var(--sstt-px, 0) * 22px) calc(var(--sstt-py, 0) * 15px)}
.sst-top__feat-a .sstt-g .sstt-feat-img{translate:none}
.sst-top__feat-b .sstt-g-i{border-radius:4px}

/* ---------- box card ---------- */
.sstt-g--card{position:absolute;inset:0}
.sstt-g--card .sstt-g-v{position:absolute;inset:9%;width:82%;height:82%;max-width:82%;max-height:82%;object-fit:contain}

/* ---------- coverflow slab ---------- */
.sstt-g--slab{position:absolute;inset:0}
.sstt-g--slab .sstt-g-v{position:absolute;inset:5%;width:90%;height:90%;object-fit:contain}

.sstt-g-v{min-width:0;min-height:0;background:var(--sstt-ink);border-radius:4px}
/* Clip the media frame only; navigation, shadows and the stage keep their box. */
.sstt-g-f:has(> .sstt-g-v){overflow:hidden}
/* a frame whose bitmap never arrived shows the same prismatic plate as
   guard()'s .sstt-imgfail rather than a broken-image glyph */
.sstt-g-f.sstt-g-fail>.sstt-g-i{visibility:hidden}
.sstt-g-f.sstt-g-fail::after{content:"";position:absolute;inset:8%;border-radius:4px;
  background:linear-gradient(150deg,rgba(46,203,245,.28),rgba(154,107,255,.24),rgba(255,79,168,.22));
  box-shadow:inset 0 0 0 1px rgba(20,21,26,.1)}

/* ---------- control layer ---------- */
/* inset:0 of whatever host it was given; itself transparent to the pointer so
   the artwork underneath keeps every gesture the surface already had */
.sstt-g-ctl{position:absolute;inset:0;z-index:6;pointer-events:none}
.sstt-g-ctl>*{pointer-events:auto}

/* THE BUTTON RESET, AND WHAT :where() DOES AND DOES NOT ZERO.
   The site theme ships, verbatim (re-read off the LIVE style.min.css 2026-08-07;
   an earlier draft of this comment quoted a `min-width:40px` that is not in the
   file at all — measured: zero occurrences):
     .sst-button,.button,button,input[type=submit]{
       display:inline-flex;min-height:40px;align-items:center;justify-content:center;
       border:1px solid var(--sst-accent);border-radius:var(--sst-radius);
       padding:9px 14px;color:#fff;background:var(--sst-accent);
       font-weight:700;line-height:1.2;cursor:pointer}
   For a <button> the matching selector in that list is the bare `button`, so the
   rule lands at (0,0,1).

   THE ARITHMETIC, CORRECTED. This file's reset at the top is
   `:where(.sst-top) button`. :where() zeroes what is INSIDE its parentheses —
   `.sst-top` — and nothing else, so the `button` outside it still contributes:
   the reset is also (0,0,1), not (0,0,0). It therefore TIES the theme rule and
   wins on source order, because top-tab.css is enqueued after style.min.css
   (functions.php:345 vs :372). Everything the two rules SHARE — border, padding,
   background, font, colour, cursor — is already ours.

   WHICH LEAVES EXACTLY ONE LEAK, and it is the p4 bug (measured on live, both
   390 and 1440): `min-height:40px`. The reset does not name min-height, and a
   rule can only win a property it actually names — so the floor arrived
   unopposed and the coverflow's tick rail, which asks for 16x2, rendered its
   active tick as a 40x40 block. Nothing else about it was wrong: border and
   padding measured 0px in the before-state, exactly as the reset intended.

   A FLOOR IS THE ONE THING SPECIFICITY CANNOT ANSWER. min-height beats `height`
   whatever declared it and whatever it scored, so `.sstt-tick{height:2px}` at
   (0,1,0) never stood a chance. The fix is not more specificity, it is naming
   the property. Every gallery control below states its own box and so needs the
   same two lines; .sstt-tick is the one PRE-EXISTING control in this file that
   states width/height and nothing else, which is why it is the only one the
   theme rule could reshape.

   min-width:0 is defensive on both rules: the theme ships no min-width today,
   but it is the same class of floor and costs nothing to close now.

   The 24px hit box does NOT come from here. It is .sstt-tick::before, and a
   pseudo-element gets no part of the button rule's box, so restoring the 2px
   bar cannot shrink the target — verify_e2e.py measures the pair (visual height
   2px, hit height >= 24px) with elementFromPoint rather than trusting this. */
.sstt-g-nav,.sstt-g-play,.sstt-g-dot{
  min-width:0;min-height:0;padding:0;margin:0;font:inherit;color:inherit;
  letter-spacing:normal;text-transform:none;box-shadow:none;appearance:none;-webkit-appearance:none}
/* p5 fix for the p4 bug described above. Deliberately ONLY the two floors: the
   reset already wins every other property, and a wider reset here would be
   changing declarations that measured correct. */
.sstt-tick{min-width:0;min-height:0}

/* [hidden] is a UA rule, and ANY author `display` beats it whatever the
   specificity. Without this the play affordance is painted on every gallery —
   including the all-photographs ones — and, being 56px in the dead centre of
   the frame, it silently eats the coverflow's drag. */
.sstt-g-play[hidden]{display:none}

.sstt-g-nav{position:absolute;top:50%;translate:0 -50%;display:grid;place-items:center;
  border-radius:50%;border:1px solid rgba(20,21,26,.22);background:rgba(255,253,249,.72);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  transition:opacity .3s var(--sstt-ease),transform .35s var(--sstt-ease),background .3s,border-color .3s}
.sstt-g-nav svg{stroke:var(--sstt-ink);stroke-width:1.6;fill:none;stroke-linecap:round;stroke-linejoin:round}
.sstt-g-nav:hover{background:var(--sstt-ink);border-color:var(--sstt-ink);transform:scale(1.08)}
.sstt-g-nav:hover svg{stroke:var(--sstt-paper)}
.sstt-g-prev{left:8px}
.sstt-g-next{right:8px}

.sstt-g-ctl--hero .sstt-g-nav{width:40px;height:40px}
.sstt-g-ctl--hero .sstt-g-nav svg{width:14px;height:14px}
.sstt-g-ctl--hero .sstt-g-prev{left:10px}
.sstt-g-ctl--hero .sstt-g-next{right:10px}
.sstt-g-ctl--card .sstt-g-nav{width:32px;height:32px}
.sstt-g-ctl--card .sstt-g-nav svg{width:12px;height:12px}
.sstt-g-ctl--slab .sstt-g-nav{width:30px;height:30px}
.sstt-g-ctl--slab .sstt-g-nav svg{width:11px;height:11px}
.sstt-g-ctl--slab .sstt-g-prev{left:6px}
.sstt-g-ctl--slab .sstt-g-next{right:6px}

/* ---------- position readout ---------- */
/* NOT dimmed at rest. An earlier pass rested this at .62 and lifted it on
   hover, which is the wrong tool: opacity fades the SCRIM along with the marks,
   so the readout was least legible in exactly the state a visitor meets it in —
   measured 3.94:1 over dark artwork, below AA. The bar is quiet because it is
   9.5px of tracked mono on a soft ground, the way a wall label is quiet. Only
   the arrows, which are controls rather than information, wait for a hover. */
.sstt-g-bar{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;
  justify-content:center;gap:11px;padding:9px 10px}
/* The readout sits over a photograph whose colours this file cannot know, so it
   brings its own paper with it. Bone, not black: a dark scrim on a warm bone
   page would read as a different site's component.
   The ramp reaches most of its opacity by 38% of the bar's height rather than
   at the very bottom, because the DOTS AND THE COUNTER are centred in the bar,
   not sitting on its floor. A gradient that only arrives at full strength at
   the bottom edge leaves the text floating over roughly half-strength paper —
   which is fine on the white sweeps these products are shot on and fails badly
   on anything dark. Over white the scrim is invisible either way; it only shows
   up where it is actually needed. */
.sstt-g-bar::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(255,253,249,0) 0%,rgba(255,253,249,.72) 38%,rgba(255,253,249,.96) 72%)}
.sstt-g-bar>*{position:relative}

/* gap = 24 - dot width, so consecutive hit boxes tile at exactly a 24px pitch:
   every dot clears the 24px minimum and no two of them overlap */
.sstt-g-dots{display:flex;align-items:center;gap:10px}
.sstt-g-ctl--slab .sstt-g-dots{gap:14px}
/* .sstt-tick's exact idiom: a 2px bar that grows to mark the current item, with
   a pseudo-element padding it out to a 24px hit box at no layout cost */
.sstt-g-dot{position:relative;display:block;width:14px;height:2px;border:0;
  background:rgba(20,21,26,.28);border-radius:1px;
  transition:width .45s var(--sstt-ease),background .45s var(--sstt-ease)}
.sstt-g-dot::before{content:"";position:absolute;left:-5px;right:-5px;top:-11px;bottom:-11px}
.sstt-g-ctl--slab .sstt-g-dot::before{left:-7px;right:-7px}
.sstt-g-dot:hover{background:rgba(20,21,26,.5)}
.sstt-g-dot.sstt-g-on{width:30px;background:var(--sstt-ink)}
.sstt-g-dot:focus-visible{outline:none}
.sstt-g-dot:focus-visible::before{outline:2px solid var(--sstt-ink);border-radius:3px}
.sstt-g-ctl--slab .sstt-g-dot{width:10px}
.sstt-g-ctl--slab .sstt-g-dot.sstt-g-on{width:22px}

/* --sstt-ink-2, NOT the --sstt-mist-t the page's other mono microcopy uses.
   That token is calibrated against the page's own bone paper; this readout
   floats on a photograph, and the soft scrim behind it only ever lightens the
   ground part-way. Measured over the darkest artwork available: mist-t gives
   3.94:1 and fails, ink-2 gives 6.2:1. Over the white sweeps these products are
   actually shot on it reads as the same quiet label either way. */
.sstt-g-idx{font-size:9.5px;letter-spacing:.2em;color:var(--sstt-ink-2);white-space:nowrap}
.sstt-g-ctl--slab .sstt-g-idx{font-size:8px;letter-spacing:.16em}

/* ---------- video ---------- */
.sstt-g-play{position:absolute;left:50%;top:50%;translate:-50% -50%;width:56px;height:56px;
  border-radius:50%;display:grid;place-items:center;border:1px solid rgba(20,21,26,.22);
  background:rgba(255,253,249,.82);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  transition:transform .35s var(--sstt-ease),background .3s,border-color .3s}
.sstt-g-play svg{width:18px;height:18px;fill:var(--sstt-ink);stroke:none;margin-left:2px}
.sstt-g-play:hover{background:var(--sstt-ink);border-color:var(--sstt-ink);transform:scale(1.06)}
.sstt-g-play:hover svg{fill:var(--sstt-paper)}
.sstt-g-ctl--card .sstt-g-play{width:44px;height:44px}
.sstt-g-ctl--card .sstt-g-play svg{width:15px;height:15px}
.sstt-g-ctl--slab .sstt-g-play{width:40px;height:40px}
.sstt-g-ctl--slab .sstt-g-play svg{width:14px;height:14px}
/* The one spectrum moment in the whole component. Play is the only control that
   asks for something irreversible-feeling, and the page's own vocabulary for
   "this one is live" is the prismatic ring on .sstt-slab-in and .sstt-card-in. */
@supports((mask-composite:exclude) or (-webkit-mask-composite:xor)){
  .sstt-g-play::after{content:"";position:absolute;inset:-3px;border-radius:50%;padding:1px;
    pointer-events:none;opacity:.85;
    background:conic-gradient(from 130deg,rgba(46,203,245,.9),rgba(154,107,255,.85),rgba(255,79,168,.8),rgba(255,180,60,.9),rgba(46,203,245,.9));
    -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;
    mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude}
}
/* native video controls own the bottom edge once playback starts, so the
   readout moves to the top rather than being hidden or fought over */
.sstt-g--playing .sstt-g-bar{top:0;bottom:auto}
.sstt-g--playing .sstt-g-bar::before{background:linear-gradient(0deg,rgba(255,253,249,0) 0%,rgba(255,253,249,.72) 38%,rgba(255,253,249,.96) 72%)}

/* ---------- quiet until asked ---------- */
@media(hover:hover){
  .sstt-g-nav{opacity:0}
  .sstt-g-host:hover .sstt-g-nav,
  .sstt-g-host:focus-within .sstt-g-nav,
  .sstt-g-host.sstt-g-touched .sstt-g-nav{opacity:1}
}
/* Only the CENTRED slab is navigable. The other nine are off-stage decoration,
   already aria-hidden by setCenter(), and their controls must not be reachable
   by a stray tap on a card that is 60% rotated away from the visitor. */
.sstt-slab:not(.sstt-on) .sstt-g-ctl{opacity:0;pointer-events:none}
.sstt-g-ctl--slab{transition:opacity .4s var(--sstt-ease)}

@media(max-width:520px){
  .sstt-g-ctl--card .sstt-g-nav{width:36px;height:36px}
  .sstt-g-ctl--card .sstt-g-nav svg{width:13px;height:13px}
}

@media(prefers-reduced-motion:reduce){
  /* An instant swap, not a fast one. No !important anywhere in here: the swipe
     writes inline transforms and must keep winning — a visitor who asked for
     less motion did not ask for a drag that ignores their finger. */
  .sstt-g-f,.sstt-g-f.sstt-g-on,.sstt-g-f.sstt-g-out{transform:none}
  .sstt-g-nav:hover,.sstt-g-play:hover{transform:none}
}

@media (forced-colors:active){
  .sstt-g-nav,.sstt-g-play{background:ButtonFace;border-color:ButtonBorder;forced-color-adjust:none}
  .sstt-g-nav svg{stroke:ButtonText}
  .sstt-g-play svg{fill:ButtonText}
  .sstt-g-dot{background:ButtonBorder;forced-color-adjust:none}
  .sstt-g-dot.sstt-g-on{background:Highlight}
  .sstt-g-bar::before{background:Canvas}
}

/* (2)+(3) the list gets a hard bottom edge and breathing room before the
   button; the button becomes a legible control. The list keeps its own
   scroll — only the last 28px fade so a half-visible row reads as "more
   below", not as text hidden behind a button. */

/* (4) the sheet keeps its "Product subtotal" label */

/* the rest of the small print in the foot was already #B9B7B1 (≈6.9:1 on
   the lacquer); the two lines that were NOT — the pre-order bulk exclusion
   note and the coupon notice inside .sst-qo__checkout-extra — are lifted to
   the same tone so the foot reads as one block. */

/* ===========================================================================
 * PRE-ORDER FEATURE  (sst-qo-preorder-feature-20260828)
 *
 * The third featured slot, under the hero. Built on .sstt-feat so it inherits
 * the same light rig (.sstt-prism/.sstt-halo/.sstt-shaft/.sstt-floor) and the
 * same offer rows as the two heroes — this file only adds the two-column
 * arrangement, the magenta pre-order treatment and the countdown type.
 *
 * The magenta is #9e2c5f, which is already the site's pre-order colour: it is
 * what .sstt-badge.sstt-pre uses here and what the release-gate tiles and the
 * QO countdown use in functions.php. Nothing new is introduced.
 * ======================================================================== */
.sstt-presec{position:relative;margin-top:clamp(30px,4.2vw,58px)}
.sstt-feat--preorder{
  display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1fr);
  gap:clamp(20px,3.2vw,48px);align-items:center;
  padding:clamp(20px,2.6vw,34px);border-radius:14px;
  background:
    radial-gradient(120% 140% at 12% 0%,rgba(158,44,95,.09) 0%,rgba(158,44,95,0) 58%),
    linear-gradient(168deg,var(--sstt-white) 0%,#FFF7FA 52%,#FDF3F7 100%);
  border:1px solid rgba(158,44,95,.24);
  box-shadow:0 1px 0 rgba(255,255,255,.9) inset,0 18px 46px -30px rgba(158,44,95,.55);
}
/* The card is its own frame, so the hero's entrance animation on .sstt-feat-info
   would slide the copy inside a box that is already still. Reset it. */
.sstt-feat--preorder .sstt-feat-info{margin-top:0;opacity:1;transform:none;animation:none}
.sstt-feat--preorder .sstt-feat-stage{height:clamp(210px,22vw,300px)}
.sstt-feat--preorder .sstt-feat-img{max-height:clamp(180px,20vw,268px)}
.sstt-feat--preorder .sstt-feat-name{margin-top:12px;max-width:24ch;font-size:clamp(1.16rem,1.8vw,1.62rem)}
.sstt-feat--preorder .sstt-offers{margin-top:clamp(14px,1.7vw,20px)}

/* ---- countdown ---- */
.sstt-pre-cd{
  margin-top:clamp(14px,1.8vw,20px);padding:clamp(11px,1.4vw,15px) clamp(13px,1.6vw,18px);
  border-radius:9px;background:rgba(158,44,95,.06);border:1px solid rgba(158,44,95,.2);
}
.sstt-pre-cd-lab{font-size:9.5px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:#7c2249}
.sstt-pre-cd-clock{
  margin-top:5px;font-size:clamp(1.5rem,3.1vw,2.15rem);font-weight:700;line-height:1.04;
  letter-spacing:-.01em;color:#9e2c5f;font-variant-numeric:tabular-nums;
}
.sstt-pre-cd-line{margin-top:5px;font-size:10px;letter-spacing:.1em;color:#7c2249;opacity:.86}

/* ---- teaser price + allocation ---- */
.sstt-pre-meta{margin-top:clamp(12px,1.5vw,16px);display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 16px}
.sstt-pre-price{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
.sstt-pre-price-y{font-size:clamp(1.16rem,2vw,1.5rem);font-weight:700;color:#9e2c5f;letter-spacing:-.01em}
.sstt-pre-price-n{font-size:10.5px;letter-spacing:.06em;color:rgba(20,21,26,.6)}
.sstt-pre-alloc{font-size:11.5px;line-height:1.45;color:rgba(20,21,26,.72)}

/* ---- remaining allocation (sst-preorder-remaining-20260829) ----
   "137 of 200 left" on an OPEN card. It sits in the slot the teaser uses for
   its price line, so on an open card it lands directly under the product name
   and ABOVE the Add buttons — the scarcity has to be read before the decision,
   not after it. Chip rather than plain text: at 11.5px next to two priced offer
   rows the line simply disappears, and this number is the reason the card is
   here. Same pink as the countdown panel, so the two never read as unrelated
   widgets. */
.sstt-pre-left{
  margin-top:clamp(12px,1.5vw,16px);display:inline-flex;align-items:baseline;
  padding:6px 13px;border-radius:2px;
  background:rgba(158,44,95,.08);border:1px solid rgba(158,44,95,.28);
  font-size:clamp(.95rem,1.6vw,1.16rem);font-weight:700;letter-spacing:.01em;
  color:#9e2c5f;
}
.sstt-pre-left.is-low{background:rgba(158,44,95,.15);border-color:#9e2c5f}
/* .sstt-pre-left sets display, which beats the UA [hidden] rule — same guard
   the stepper needs (see .sstt-qty[hidden]). */
.sstt-pre-left[hidden]{display:none}

/* ---- notify bell ----
   Sized to sit under the offer rows as a secondary action: the Add buttons are
   the primary even here, because the moment the release opens they light up in
   this very card and the bell disappears. */
.sstt-pre-bell{
  margin-top:clamp(13px,1.6vw,17px);display:inline-flex;align-items:center;gap:8px;
  min-height:40px;padding:0 17px;border-radius:2px;cursor:pointer;
  background:#9e2c5f;color:#fff;border:1px solid #9e2c5f;
  font-family:'IBM Plex Mono',ui-monospace,monospace;
  font-size:10px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;
  transition:background .18s var(--sstt-ease),color .18s var(--sstt-ease);
}
.sstt-pre-bell:hover{background:#7c2249;border-color:#7c2249}
.sstt-pre-bell:focus-visible{outline:2px solid #9e2c5f;outline-offset:2px}
.sstt-pre-bell .sst-rsn-btn__icon{display:inline-flex;align-items:center}

/* ---- mobile ----
   One column, art first. The countdown keeps its size: it is the reason the
   card exists and it is what a phone visitor has come to read. */
@media (max-width:860px){
  .sstt-feat--preorder{grid-template-columns:minmax(0,1fr);gap:clamp(14px,3vw,22px)}
  .sstt-feat--preorder .sstt-feat-stage{height:clamp(180px,42vw,240px)}
  .sstt-feat--preorder .sstt-feat-img{max-height:clamp(160px,38vw,220px)}
  .sstt-feat--preorder .sstt-feat-name{max-width:none}
  .sstt-pre-bell{width:100%;justify-content:center}
}
@media (max-width:430px){
  .sstt-feat--preorder{padding:16px 14px;border-radius:11px}
  .sstt-pre-cd-clock{font-size:1.62rem}
}
@media (prefers-reduced-motion:reduce){
  .sstt-pre-bell{transition:none}
}

/* sst-qo-preorder-feature-20260828 (owner decision, 2026-08-28)
   On the pre-order feature card a coming-soon row said BOTH "Coming soon" and
   "Out of stock". The second is technically the server's own stock sentence,
   but next to a countdown it reads as a failure rather than an announcement —
   the box is not out of stock, it has not gone on sale yet.

   Scoped to the feature card and keyed off the row's own action state, so:
     - every other Quick Order surface keeps the stock sentence unchanged;
     - the moment the release opens and the row turns purchasable, the
       attribute changes and the real stock figure appears here too, with no
       second code path to keep in step. */
.sstt-feat--preorder .sstt-offer[data-offer-state="coming-soon"] .sstt-stock{display:none}

/* ==== sst-home-top-sections 2026-10-22 ==== */
/* =============================================================================
 * TOP tab — NEW sections: 01 News (announcement) + 02 About Us / Instagram
 * ec-home-redesign-20261022, lane M1 (mock) -> to be appended verbatim to
 * assets/top-tab.css by W1 (ver 0.5.12).
 *
 * Rules
 *  - Only the stage tokens (--sstt-*) and the site accent (--sst-accent*) are
 *    used, so html[data-sst-theme="dark"] (dark-mode-qo.css re-defines the
 *    --sstt-* tokens) needs no extra rule except the few hard-coded fills
 *    copied from existing card rules (marked DARK below).
 *  - Cards reuse the existing Box Picks vocabulary (.sstt-card / .sstt-card-in /
 *    .sstt-card-stage / .sstt-offers / .sstt-offer / .sstt-btn / .sstt-pre-bell).
 *    The only additions are the 3-up grid, the ship-date line and the bell width.
 *  - .sstt-gw did not exist in top-tab.css 0.5.11 (the gateways are
 *    .sstt-btn.sstt-btn-xl built in JS). It is defined here with the same look
 *    as .sstt-btn so server-rendered anchors in the pre-section match them.
 * ========================================================================== */

/* ---------- shared: section rhythm ---------- */
.sst-top-pre{position:relative}
.sstt-ann{position:relative;margin-top:clamp(18px,2.4vw,32px)}
.sstt-about{position:relative;margin-top:clamp(44px,6vw,84px)}
/* the existing Box Picks section now follows two sections instead of the hero */
.sst-top-pre + .sst-top__boxsec,
.sst-top__main > .sst-top-pre ~ .sst-top__boxsec{margin-top:clamp(44px,6vw,84px)}

/* ---------- gateway button (server-rendered twin of .sstt-btn) ---------- */
.sst-top a.sstt-gw,.sstt-gw{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:10px;
  min-height:48px;padding:0 26px;border:1px solid var(--sstt-ink);border-radius:2px;
  background:var(--sstt-ink);color:var(--sstt-paper);text-decoration:none;
  font-size:11.5px;font-weight:500;letter-spacing:.19em;text-transform:uppercase;white-space:nowrap;
  transition:transform .3s var(--sstt-ease),background .3s,box-shadow .35s var(--sstt-ease)}
.sstt-gw::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--sstt-spectrum);
  transform:scaleX(0);transform-origin:0 50%;transition:transform .45s var(--sstt-ease)}
.sst-top a.sstt-gw:hover,.sstt-gw:hover{color:var(--sstt-paper);transform:translateY(-2px);box-shadow:0 14px 26px -14px rgba(20,21,26,.6)}
.sstt-gw:hover::after{transform:scaleX(1)}
.sstt-gw:focus-visible{outline:2px solid var(--sstt-mg);outline-offset:3px}
/* DARK: same as dark-mode-qo.css html[data-sst-theme=dark] .sstt-btn (accent fill) */
html[data-sst-theme="dark"] .sstt-gw,html[data-sst-theme="dark"] .sst-top a.sstt-gw{background:var(--sst-accent);border-color:var(--sst-accent);color:var(--sst-btn-label,#fff)}
html[data-sst-theme="dark"] .sstt-gw:hover,html[data-sst-theme="dark"] .sst-top a.sstt-gw:hover{background:var(--sst-accent-dark);color:var(--sst-btn-label,#fff);box-shadow:0 14px 26px -14px rgba(0,0,0,.7)}

/* ---------- 01 News ---------- */
.sstt-ann-banner{display:block;overflow:hidden;border-radius:8px;border:1px solid var(--sstt-line);
  background:var(--sstt-white);box-shadow:0 18px 34px -26px rgba(20,21,26,.45)}
.sstt-ann-banner img{display:block;width:100%;height:auto;aspect-ratio:1904/246;object-fit:cover}
@media(max-width:899px){.sstt-ann-banner img{aspect-ratio:780/360}}
.sstt-ann-banner:focus-visible{outline:2px solid var(--sstt-ink);outline-offset:3px}
/* same band as .sstt-vault: headline column + CTA, hairline underneath */
.sstt-ann-head{display:flex;flex-wrap:wrap;align-items:flex-end;gap:clamp(18px,3vw,44px);
  margin-top:clamp(20px,2.6vw,34px);padding-bottom:clamp(16px,2vw,24px);border-bottom:1px solid var(--sstt-line)}
.sstt-ann-copy{flex:1000 1 420px;min-width:0}
.sstt-ann-head .sstt-vault-h{font-size:clamp(1.5rem,2.6vw,2.4rem)}
.sstt-ann-head .sstt-vault-p{max-width:64ch;margin-top:12px;font-family:inherit;font-size:clamp(14.5px,1.25vw,16px);letter-spacing:0;line-height:1.6;text-transform:none;color:var(--sstt-ink-2)}
.sstt-ann-head .sstt-gw{flex:0 0 auto;margin-bottom:3px}

/* 3 cards in one row at >=1024px; below that a snap rail, one card = 85vw */
.sstt-boxes.sstt-boxes--ann{grid-template-columns:repeat(3,minmax(0,1fr));margin-top:clamp(22px,3vw,36px)}
.sstt-boxes--ann:empty,.sstt-boxes--ann[hidden]{display:none}
@media(max-width:1023.98px){
  .sstt-boxes.sstt-boxes--ann{display:flex;gap:14px;overflow-x:auto;overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;scrollbar-width:none;
    padding:6px 2px 18px;margin-inline:-2px}
  .sstt-boxes--ann::-webkit-scrollbar{display:none}
  .sstt-boxes--ann > .sstt-card{flex:0 0 min(85vw,380px);scroll-snap-align:start}
}
/* ship date = the card's title: full-width header above the image box, built by
   top-tab.js from "… — Ships on Nov 2" (eyebrow "Ships on" + value "Nov 2") */
.sstt-ann-ship{position:relative;display:flex;flex-direction:column;justify-content:flex-end;gap:6px;
  min-height:calc(clamp(28px,2.6vw,40px) + 26px);margin:0 0 14px;padding:2px 2px 12px}
.sstt-ann-ship::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--sstt-spectrum)}
.sstt-ann-ship-k{min-height:1em;font-family:'IBM Plex Mono',monospace;font-size:10.5px;font-weight:500;line-height:1;
  letter-spacing:.28em;text-transform:uppercase;color:var(--sstt-ink-2)}
.sstt-ann-ship-v{font-family:'Archivo',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:clamp(28px,2.6vw,40px);
  font-weight:800;line-height:1;letter-spacing:-.035em;text-transform:uppercase;color:var(--sstt-ink);white-space:nowrap}
/* mu-plugin sst-site-font forces `.sst-top, .sst-top *{font-family:var(--sst-font)!important}` (Figtree) on the
   whole stage; the date is the one place that asks for the Archivo display face, so it needs (0,2,0) + !important */
.sst-top .sstt-ann-ship-v{font-family:'Archivo',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif!important}
.sst-top .sstt-ann-ship-k{font-family:'IBM Plex Mono',ui-monospace,monospace!important}
@media(max-width:1023.98px){.sstt-ann-ship-v{font-size:clamp(28px,8vw,36px)}
  .sstt-ann-ship{min-height:calc(clamp(28px,8vw,36px) + 26px)}}
.sstt-card--ann .sstt-card-name{min-height:0}
.sstt-card--ann .sstt-oadd{max-width:none}
/* coming soon: the bell replaces the hidden stepper, full card width */
.sstt-ann-bell{width:100%;justify-content:center;margin-top:12px}
.sstt-card--ann .sstt-offer[data-offer-state="coming-soon"] .sstt-oadd{display:none}

/* ---------- 02 About Us ---------- */
.sstt-about .sstt-vault-p.sstt-about-intro{max-width:72ch;margin-top:16px;font-family:inherit;font-size:clamp(14.5px,1.25vw,16px);
  letter-spacing:0;line-height:1.65;text-transform:none;color:var(--sstt-ink-2)}
.sstt-about-tiles{list-style:none;margin:clamp(22px,3vw,34px) 0 0;padding:0;
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:clamp(10px,1.2vw,16px)}
@media(max-width:1180px){.sstt-about-tiles{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:560px){.sstt-about-tiles{grid-template-columns:1fr}}
.sstt-about-tiles li{position:relative;display:flex;flex-direction:column;gap:8px;margin:0;padding:16px 16px 18px 16px;
  border:1px solid var(--sstt-line);border-radius:8px;background:var(--sstt-white)}
/* label tone = the review band (.sst-hrb): 11px uppercase, .14em, muted; accent check icon */
.sstt-about-tiles b{display:flex;align-items:center;gap:8px;font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--sstt-mist-t)}
.sstt-about-tiles b::before{content:"";flex:0 0 16px;width:16px;height:16px;background:var(--sst-accent,#9e2c5f);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm-1.2 14.2-4-4 1.4-1.4 2.6 2.6 5.6-5.6 1.4 1.4-7 7Z'/%3E%3C/svg%3E") center/contain no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm-1.2 14.2-4-4 1.4-1.4 2.6 2.6 5.6-5.6 1.4 1.4-7 7Z'/%3E%3C/svg%3E") center/contain no-repeat}
.sstt-about-tiles span{font-size:14px;line-height:1.55;color:var(--sstt-ink)}
.sstt-about-tiles a{color:var(--sst-accent,#9e2c5f);text-decoration:underline;text-underline-offset:2px}
html[data-sst-theme="dark"] .sstt-about-tiles a{color:var(--sst-accent-text,#e58bb5)}

/* Instagram */
.sstt-ig{margin-top:clamp(26px,3.4vw,44px)}
.sstt-ig-profile{display:flex;flex-wrap:wrap;align-items:center;gap:14px 18px;padding:16px 18px;
  border:1px solid var(--sstt-line);border-radius:8px;background:var(--sstt-white)}
.sstt-ig-avatar{flex:0 0 64px;width:64px;height:64px;border-radius:50%;object-fit:contain;padding:8px;box-sizing:border-box;
  background:#fff;border:1px solid var(--sstt-line)}
.sstt-ig-profile > div{flex:1 1 220px;min-width:0;display:flex;flex-direction:column;gap:2px}
.sstt-ig-profile b{font-size:16px;font-weight:700;letter-spacing:-.01em;color:var(--sstt-ink)}
.sstt-ig-profile span{font-size:13px;line-height:1.45;color:var(--sstt-mist-t)}
.sstt-ig-profile span:nth-of-type(1){font-family:'IBM Plex Mono',monospace;font-size:12px;letter-spacing:.04em;color:var(--sstt-ink-2)}
.sstt-ig-profile .sstt-gw{flex:0 0 auto}
.sstt-ig-cta{display:flex;align-items:center;gap:10px;margin:clamp(20px,2.6vw,30px) 0 14px;
  font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.28em;text-transform:uppercase;color:var(--sstt-ink-2)}
.sstt-ig-cta::before{content:"";width:34px;height:1px;background:var(--sstt-spectrum)}
/* embeds are min 326px wide (Instagram's own markup): desktop 3 across,
   narrower than 3 x 326 -> snap rail */
.sstt-ig-reels{display:flex;gap:16px;overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch;scrollbar-width:none;padding-bottom:6px}
.sstt-ig-reels::-webkit-scrollbar{display:none}
.sstt-ig-reel{flex:1 0 326px;max-width:540px;min-width:326px;scroll-snap-align:start}
.sstt-ig-reel .instagram-media{margin:0!important;min-width:326px!important;width:100%!important;max-width:none!important}
@media(max-width:749px){.sstt-ig-reel{flex:0 0 min(326px,calc(100vw - 64px));min-width:min(326px,calc(100vw - 64px))}
  .sstt-ig-reel .instagram-media{min-width:0!important}}

@media(max-width:749px){
  .sstt-ann-head .sstt-gw,.sstt-ig-profile .sstt-gw{width:100%}
}
@media(prefers-reduced-motion:reduce){
  .sstt-gw,.sstt-gw::after{transition:none}
  .sstt-ig-reels,.sstt-boxes--ann{scroll-behavior:auto}
}

/* W1 additions: the spec's empty-host rule, and [hidden] guards for nodes whose
   classes set display (the UA [hidden] rule loses to them). */
[data-qo-top-ann-cards]:empty{display:none}
[data-qo-top-ann-cards][hidden]{display:none}
.sstt-ann-bell[hidden]{display:none}
/* ==== /sst-home-top-sections 2026-10-22 ==== */
