/* tokens.css — BitSoul3D design tokens (final polish, 2026-10-01).
   The ONE source for colour, type, spacing, radius, elevation and motion. Every page links it before its own styles
   (FAVICON_TAGS for the server-rendered pages, a <link> in each static page). It holds custom properties, @font-face
   and zero-specificity (:where) base rules only, so any page rule still wins over it.
   Palette: Canvas #08080c · Panel #101017 · Raised #17171f · Bone #f5f5f7 · Ash #9d9da9 · Voltage #ffc814.
   Voltage has one job — buy / join. The 3D models carry the colour; depth comes from surface steps and hairlines. */

/* Text + display face: Archivo (OFL), one variable file with width (62-125%) and weight axes, latin subset. */
@font-face{font-family:'Archivo';font-style:normal;font-weight:100 900;font-stretch:62% 125%;font-display:swap;
  src:url('/marketplace/assets/fonts/v3/archivo.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}
/* Archivo draws "&" as an et-form that reads as "E" at button sizes ("Buy & download"); the classic ampersand is borrowed
   from the brand's own Jakarta file (already loaded for the wordmark). Later @font-face wins for overlapping ranges. */
@font-face{font-family:'Archivo';font-style:normal;font-weight:100 900;font-stretch:62% 125%;font-display:swap;
  src:url('/marketplace/assets/fonts/jakarta-400.woff2') format('woff2');unicode-range:U+0026;
  ascent-override:87.8%;descent-override:21%;line-gap-override:0%}   /* Archivo's own metrics (measured .878/.21), so a line with '&' keeps its baseline */
/* Metric-matched fallback (measured: ascent .88, descent .21, advance .983 of Arial), so the swap does not shift text. */
@font-face{font-family:'Archivo Fallback';src:local('Arial'),local('ArialMT'),local('Liberation Sans');
  size-adjust:98.3%;ascent-override:89.5%;descent-override:21.4%;line-gap-override:0%}

:root{
  color-scheme:dark;

  /* surfaces — canvas, recessed well, panel, raised, active */
  --s0:#08080c; --well:#0b0b10; --s1:#101017; --s2:#17171f; --s3:#202027;
  /* hairlines — inside a surface, a card edge, an interactive edge */
  --line-1:rgba(255,255,255,.06); --line-2:rgba(255,255,255,.10); --line-3:rgba(255,255,255,.16);
  /* elevation — a top-edge catch light for panels; a real shadow only for things that float (menus, sheets, toasts) */
  --lift:inset 0 1px 0 rgba(255,255,255,.04);
  --float:0 18px 48px -16px rgba(0,0,0,.7);
  /* text tiers (contrast on #08080c: 18.4 / 11.8 / 7.45 / 4.09 — t4 is for disabled and separators only) */
  --t1:#f5f5f7; --t2:#c6c6cf; --t3:#9d9da9; --t4:#70707c;
  --link:var(--t1); --link-line:rgba(255,255,255,.32); --focus:#f5f5f7;
  /* the one accent: voltage = buy / join; its "selected" recipe is a hairline + wash, never a fill */
  --volt:#ffc814; --volt-hi:#ffd447; --volt-press:#f0b400; --volt-ink:#12100a;
  --volt-line:rgba(255,200,20,.55); --volt-wash:rgba(255,200,20,.10);
  /* state only — included, rigged, free, errors */
  --ok:#34d399; --ok-wash:rgba(52,211,153,.10); --ok-line:rgba(52,211,153,.35);
  --err:#f87171; --err-wash:rgba(248,113,113,.10); --err-line:rgba(248,113,113,.35);
  /* the logo keeps its indigo — the only indigo outside the workspace tools, Stripe's button and the certificate */
  --brand-mark:#6366f1;
  /* one warm graphite plate behind every model thumbnail, so mixed renders read as one catalogue */
  --plate:radial-gradient(115% 78% at 50% 14%,rgba(236,232,224,.10),transparent 58%),linear-gradient(180deg,#1b1b21,#131317 58%,#0c0c10);

  /* type — Archivo for text and display (display uses the expanded width), Jakarta only for the wordmark */
  --font-text:'Archivo','Archivo Fallback',system-ui,-apple-system,'Segoe UI',sans-serif;
  --font-display:var(--font-text);
  --font-mark:'Plus Jakarta Sans','Jakarta-fb',system-ui,-apple-system,'Segoe UI',sans-serif;
  --font-code:ui-monospace,'Cascadia Mono','SF Mono',Menlo,Consolas,monospace;
  --wd-display:115%; --wd-title:106%; --wd-text:100%;
  --fs-xs:12px; --fs-sm:13px; --fs-md:15px; --fs-base:16px; --fs-lg:18px;
  --fs-xl:clamp(20px,calc(14px + 1.2vw),24px);
  --fs-2xl:clamp(24px,calc(14px + 2vw),32px);
  --fs-3xl:clamp(30px,calc(14px + 3.2vw),44px);
  --fs-4xl:clamp(34px,calc(14px + 4.4vw),58px);
  --lh-tight:1.08; --lh-snug:1.25; --lh-ui:1.45; --lh-body:1.6;
  --track-display:-0.022em; --track-title:-0.01em; --track-caps:.08em;
  --w-text:400; --w-medium:500; --w-semi:600; --w-bold:700; --w-display:700;
  --measure:68ch;

  /* spacing — 4px base; --section is the gap between page sections */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px; --sp-7:48px; --sp-8:64px; --sp-9:96px;
  --section:clamp(56px,7vw,96px);
  --w-page:1180px; --w-wide:1280px; --gutter:clamp(16px,4vw,32px);

  /* radius — chips/badges 4, inputs 8, cards 12, panels and sheets 16, buttons pill */
  --r-xs:4px; --r-sm:8px; --r-md:12px; --r-lg:16px; --r-pill:999px;

  /* motion — custom curves only; every transition and keyframe uses these */
  --ease-out:cubic-bezier(.16,1,.3,1);
  --ease-in-out:cubic-bezier(.65,0,.35,1);
  --ease-exit:cubic-bezier(.55,0,.8,.2);
  --ease-spring:cubic-bezier(.2,1.12,.3,1);
  --dur-1:120ms; --dur-2:200ms; --dur-3:320ms; --dur-4:480ms;
}

/* the wordmark keeps the brand's own face (the logo stays) */
:where(.logo, .gnav-logo, .wh-logo){font-family:var(--font-mark)}

/* one focus ring for every interactive element: 2px bone, 2px off the edge, appears instantly (never transitioned) */
:where(a, button, summary, select, textarea, input, [tabindex], model-viewer):focus-visible{outline:2px solid var(--focus);outline-offset:2px}
:where(model-viewer):focus-visible{outline-offset:-2px}

/* headings: display width for the two top levels, balanced lines everywhere; prose wraps without orphans.
   (Jakarta has no width axis, so the wordmark and the Tier C tools are unaffected.) */
:where(h1, h2){font-stretch:var(--wd-display)}
:where(h3){font-stretch:var(--wd-title)}
:where(h1, h2, h3, h4){text-wrap:balance}
:where(p, li, dd, blockquote, figcaption){text-wrap:pretty}

/* ── finish (final polish, Phase 4) ─────────────────────────────────────────────────────────────────────────────────── */
/* selection in the action colour's wash; anchors clear the sticky nav; smooth scrolling only when motion is welcome */
::selection{background:rgba(255,200,20,.28);color:var(--t1)}
:where([id]){scroll-margin-top:calc(var(--nav-h,64px) + 16px)}
@media (prefers-reduced-motion: reduce){:where(html){scroll-behavior:auto !important}}
/* page transitions: a short cross-fade between pages of the site (progressive enhancement; off under reduced motion) */
@view-transition{navigation:auto}
::view-transition-old(root),::view-transition-new(root){animation-duration:var(--dur-3);animation-timing-function:var(--ease-out)}
@media (prefers-reduced-motion: reduce){@view-transition{navigation:none}}

/* a phone menu sheet is open: the page behind it does not scroll */
html.bs-sheet-open, html.bs-sheet-open body{overflow:hidden;overscroll-behavior:none}
/* and the bars drop their blur while it is open: a backdrop-filter makes the bar the containing block of the fixed sheet,
   which would hang the sheet below the store's launch banner */
html.bs-sheet-open .navbar, html.bs-sheet-open .gnav{-webkit-backdrop-filter:none;backdrop-filter:none}
/* while the sheet is parked (closed) the blurred bar is still its containing block, so the parked sheet would count as
   page width and a phone could pan sideways into it; the bars clip sideways only (dropdowns still hang below) */
@media (max-width:880px){.navbar,.gnav{overflow-x:clip}}
@supports not (overflow-x:clip){@media (max-width:880px){.gnav-panel:not(.open),.mnav-panel:not(.open){display:none}}}
/* the phone sheets' close control (motion.js adds it at the top of each sheet) */
.bs-sheet-x{position:sticky;top:0;z-index:2;align-self:flex-end;flex:none;display:flex;align-items:center;justify-content:center;width:44px;height:44px;margin:0 0 6px;padding:0;border-radius:50%;border:1px solid var(--line-2);background:var(--s2);color:var(--t1);cursor:pointer}
.bs-sheet-x[hidden]{display:none}
@media (hover:hover) and (pointer:fine){.bs-sheet-x:hover{border-color:var(--line-3)}}
