:root {
      --bg: #15161b;
      --bg-2: #191b21;
      --bg-3: #1f2127;

      --glass-top: rgba(255,255,255,0.055);
      --glass-bot: rgba(255,255,255,0.022);
      --hairline: rgba(255,255,255,0.09);
      --hairline-strong: rgba(255,255,255,0.16);

      --fg: #f4f6f9;
      --fg-1: #e2e6ec;  /* body text one step below primary; was referenced in 19 rules but never defined */
      --fg-2: #aab2c0;
      /* Lifted from #6b7280, which computed 3.33:1 on --bg-3 and 3.74:1 on
         --bg - under WCAG AA's 4.5:1 at the small sizes it is used at. This
         clears 4.5 on all three surfaces while staying well under --fg-2, so
         the three-step text hierarchy survives. */
      --fg-3: #80899a;

      --accent: #ff4d44;
      --accent-deep: #e2382f;
      --accent-ink: #ff8079;
      --price: #34e08a;
      /* Ratings are not an action, so they are not ember. One token because
         the homepage reviews and the product cards were rendering the same
         thing in two different colours. */
      --star: #ffc24b;

      /* Referenced by .co-pay-btn--* svg in the checkout payment-method
         list but never actually defined here, so every mark silently fell
         back to inherited grey instead of its real brand colour. Values
         are each brand's own documented hex (Stripe's brand page, PayPal's
         and Bitcoin's Simple Icons entries). */
      --brand-stripe: #635bff;
      --brand-paypal: #002991;
      --brand-crypto: #f7931a;

      /* Status. These existed only as scattered literals, with two different
         greens both meaning "success". One value per meaning, plus the tint
         each uses as a badge background. */
      --ok: #6bd88a;
      --ok-tint: rgba(107,216,138,0.14);
      --warn: #f6c454;
      --warn-tint: rgba(246,196,84,0.12);
      --info: #7db8ff;
      --info-tint: rgba(70,140,255,0.12);
      --warn-line: rgba(246,196,84,0.28);

      /* Minecraft is a second brand fill, not an accent. Its three states were
         loose literals; the resting value was already documented, these are
         the hover and press steps that go with it. */
      --mc: #4a9a3f;
      --mc-hover: #58ad4a;
      --mc-press: #418a37;

      /* Shadow and scrim alphas. The file ran 15 near-identical black alphas
         (0.22 / 0.25 / 0.3 / 0.4 / 0.45 / 0.55 / 0.6 / 0.7 / 0.75 / 0.78 /
         0.8 / 0.82 / 0.85 / 0.9) in two different notations. Five steps. */
      --ink-25: rgba(0,0,0,0.25);
      --ink-40: rgba(0,0,0,0.4);
      --ink-60: rgba(0,0,0,0.6);
      --ink-75: rgba(0,0,0,0.75);
      --ink-85: rgba(0,0,0,0.85);

      /* Type ramp: whole-pixel steps only. The old system ran 9.5/10.5/11.5/12.5/
         13.5/14.5/15.5/16.5 alongside their integer neighbours, which is noise,
         not hierarchy. 127 declarations were collapsed onto these steps. */
      --t-display: clamp(40px, 5.6vw, 66px);
      --t-h2: clamp(32px, 4.4vw, 48px);
      /* Heading ladder. There was nothing between --t-lead (17px) and 26px, so
         27 declarations sat off-scale at 18/19/20/21/22/23/24/27/30/33. These
         three steps close the gap at a ~1.2 ratio. */
      --t-h3: 30px;
      --t-h4: 24px;
      --t-h5: 20px;
      --t-lead: 17px;
      --t-body: 16px;
      --t-md: 15px;
      --t-sm: 14px;
      --t-label: 13px;
      --t-micro: 12px;
      --t-nano: 11px;

      /* Elevation. Structural surfaces are neutral; nothing gets a colored halo. */
      --e-1: 0 1px 2px rgba(0,0,0,0.4);
      --e-2: 0 4px 12px -4px rgba(0,0,0,0.55);
      --e-3: 0 18px 44px -24px rgba(0,0,0,0.8);
      --e-4: 0 30px 70px -34px rgba(0,0,0,0.88);

      /* Radius scale. The system had run 18 distinct values between 2px and
         999px with no relationship between them; these are the five steps
         every literal now snaps to. */
      --r-xs: 4px;    /* badges, ticks, tiny chips */
      --r-ctl: 10px;  /* buttons, inputs, list rows */
      --r-sm: 16px;   /* panels, inner cards */
      --r: 24px;      /* large cards, section surfaces */
      --r-pill: 999px;

      /* Drawn link arrows (see .lnk-arrow / .lnk-back / .ico-ext). */
      --ico-arrow-r: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h13M12.5 5.5 19 12l-6.5 6.5'/%3E%3C/svg%3E");
      --ico-arrow-l: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 12H6M11.5 5.5 5 12l6.5 6.5'/%3E%3C/svg%3E");
      --ico-ext: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 16 16 8M9 8h7v7'/%3E%3C/svg%3E");
      --maxw: 1280px;
      --ease: cubic-bezier(0.22, 1, 0.36, 1);
      --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    }

    /* Opt-in light mode - a per-visitor accessibility/preference setting
       (dashboard > Settings), OFF by default, not a second brand identity.
       DESIGN.md's "committed dark system" rule is about not designing two
       parallel visual worlds; this is one token layer re-pointed for anyone
       who explicitly asks for it. Same variable names, same structural
       relationships (surfaces step up by getting *brighter* toward the
       viewer in both themes), so every rule elsewhere in this file that
       already uses var(--bg-3) etc. themes for free with zero changes.
       Status/price colors are darkened here specifically because they
       carry real text - the dark-mode values are legible as bright text on
       near-black but fail contrast as the same bright value on near-white. */
    html[data-theme="light"] {
      --bg: #f1f2f5;
      --bg-2: #ffffff;
      --bg-3: #fbfbfd;

      --glass-top: rgba(0,0,0,0.035);
      --glass-bot: rgba(0,0,0,0.014);
      --hairline: rgba(0,0,0,0.09);
      --hairline-strong: rgba(0,0,0,0.16);

      --fg: #15161b;
      --fg-1: #2b2e35;
      --fg-2: #565c68;
      --fg-3: #6b7280;

      --price: #16794a;
      --star: #d69c1f;
      --ok: #1f8a4c;
      --ok-tint: rgba(31,138,76,0.12);
      --warn: #9a6b06;
      --warn-tint: rgba(154,107,6,0.12);
      --info: #2a6fd1;
      --info-tint: rgba(42,111,209,0.1);
      --warn-line: rgba(154,107,6,0.25);
    }
    @media (prefers-color-scheme: light) {
      /* No system-preference auto-switch: dark stays the default even on a
         light-OS device unless the visitor opts in explicitly (see above).
         This block intentionally does nothing - it exists so a future
         change here is a deliberate decision, not an oversight. */
    }
    /* .backdrop/.scrim are hardcoded dark literals (a moody banner.jpg blur
       plus a near-black gradient) - the "Late-Night Workshop" atmosphere
       DESIGN.md describes, and it doesn't invert. Left on, hero text sits
       on a dark photo while the rest of the page is light. Light mode
       drops the atmosphere and shows a flat var(--bg) hero instead. */
    html[data-theme="light"] .backdrop,
    html[data-theme="light"] .scrim {
      display: none;
    }

    /* A recurring "active/hover" idiom across the site lightens a dark
       surface with a low-alpha white tint and punches the label to pure
       white - correct against the near-black base it was designed on, but
       inverted in light mode: the same white tint over a near-white base
       nearly disappears and white text goes with it (this is the
       "Account Settings looks blank" bug). Same idiom, opposite direction:
       a dark tint plus the normal foreground color. */
    html[data-theme="light"] .btn-tinted:hover,
    html[data-theme="light"] .nmt-tab.active,
    html[data-theme="light"] .sort-opt.active,
    html[data-theme="light"] .chip.active,
    html[data-theme="light"] .p-add:hover,
    html[data-theme="light"] .fc-cat.active,
    html[data-theme="light"] .fc-sub.active,
    html[data-theme="light"] .ref-method-opt.active,
    html[data-theme="light"] .adm-dd-opt.active,
    html[data-theme="light"] .bt-opt.active,
    html[data-theme="light"] .co-pay-btn.active {
      color: var(--fg);
      background: rgba(0,0,0,0.06);
    }
    /* Same idiom, red-tinted variant (rgba(255,77,68, low-alpha) + white
       text) - the tint reads as a soft rose highlight in light mode fine,
       but white text on light pink is the same illegible pairing. */
    html[data-theme="light"] .dash-nav a.active,
    html[data-theme="light"] .plat-opt.active,
    html[data-theme="light"] .co-sg-add {
      color: var(--accent-deep);
    }

    /* .cta (homepage "Join the coldd community" band) is a photo section
       with a hardcoded dark scrim, same atmosphere as .backdrop/.scrim -
       its h2 inherits var(--fg), which is correct against the dark photo
       in dark mode but flips to near-black in light mode while the photo
       stays dark, making the heading unreadable. Same fix as .backdrop:
       drop the photo in light mode and show a flat, normally-themed panel
       instead. */
    html[data-theme="light"] .cta {
      background: var(--bg-3);
    }
    html[data-theme="light"] .cta::after {
      display: none;
    }
    html[data-theme="light"] .cta h2 {
      text-shadow: none;
    }
    html[data-theme="light"] .cta p {
      color: var(--fg-2);
      text-shadow: none;
    }

    /* .resell-logo is a deliberately dark chip (see its own comment) built
       to host partner logo files that are light-colored artwork on
       transparent backgrounds. var(--bg-3) themes it to near-white in
       light mode same as every other surface, but these specific logos
       were never designed against a light background and disappear.
       Keep the chip dark in both themes instead of theming it. */
    html[data-theme="light"] .resell-logo {
      background: #1a1b21;
      border-color: rgba(255,255,255,0.08);
    }

    * { box-sizing: border-box; }
    html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; scroll-padding-top: 84px; }

    body {
      margin: 0; font-family: 'Archivo', 'Helvetica Neue', Helvetica, Arial, sans-serif;
      font-variation-settings: 'wdth' 100;
      color: var(--fg); background: var(--bg); line-height: 1.55; letter-spacing: -0.006em;
      -webkit-font-smoothing: antialiased; overflow-x: hidden;
    }

    /* Figures that are actually measurements (prices, counts, specs) align in columns. */
    .num, .price, .hn, [data-num] { font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1; }

    /* The blurred banner is an entrance, not wallpaper. It used to be fixed, so
       the artwork sat behind every section for the whole scroll and washed the
       entire page red. Anchored to the top of the document instead: the hero
       keeps its atmosphere and everything below it sits on clean graphite. */
    .backdrop { position: absolute; top: 0; left: 0; width: 100%;
      height: max(104vh, 940px); z-index: -4;
      background: #0b0c0f url('banner.jpg') center top / cover no-repeat;
      /* 4px left the artwork legible enough to compete with the headline it
         sits behind. Pushed to 14px so it reads as depth, not as content. */
      filter: blur(14px); }

    /* The 88% stop this used to resolve at was tuned against desktop's
       taller hero lead-in. banner.jpg has real warm/red content in its
       lower third, and at 58%-88% the scrim only darkens it to 86% opacity
       - on mobile the hero+copy above the first section is short enough
       (measured ~885px down the document at 375px wide) that the next
       panel starts only ~20px past where the gradient finally goes fully
       opaque, so a visible red line from the photo showed through right
       behind that panel's heading. Resolving to solid --bg by 68% instead
       gives a real margin regardless of viewport, and the extra 40px of
       height (plain solid --bg past that point) guards against
       filter:blur() bleeding a soft edge past .backdrop's own box, which
       isn't clipped by the box the way overflow:hidden would clip it. */
    .scrim { position: absolute; top: 0; left: 0; width: 100%;
      height: calc(max(104vh, 940px) + 40px); z-index: -3; pointer-events: none;
      background: linear-gradient(180deg,
        rgba(17,18,22,0.52) 0%,
        rgba(17,18,22,0.62) 24%,
        rgba(19,20,25,0.86) 45%,
        var(--bg) 68%); }

    /* Retired atmosphere: animated blur blobs, the red radial wash and the grain
       overlay. Kept as no-ops so pages still carrying the markup render clean. */
    .glow, .blob, .grain { display: none !important; }

    html { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.22) transparent; }
    ::-webkit-scrollbar { width: 12px; height: 12px; }
    ::-webkit-scrollbar-track { background: transparent; }
    ::-webkit-scrollbar-thumb { background-color: rgba(255,255,255,0.18); border-radius: 999px;
      border: 4px solid transparent; background-clip: content-box; }
    ::-webkit-scrollbar-thumb:hover { background-color: rgba(255,255,255,0.3); }

    .wrap { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }
    section { position: relative; }
    h1,h2,h3 { margin: 0; font-weight: 700; letter-spacing: -0.035em; line-height: 1.04; }
    .muted { color: var(--fg-2); }

    /* .eyebrow is retired: the heading carries its own weight. Neutralised rather
       than deleted so pages still emitting the element collapse it instead of
       printing an orphaned pill. */
    .eyebrow { display: none; }

    /* Solid, not translucent - was a glass gradient + backdrop blur (a
       masked gradient ring standing in for a border, so it could fade
       top-to-bottom like light on real glass). Every panel using this
       class now reads as a flat raised surface instead, same as the
       system's existing "solid" family (--bg-2 legal pages, --bg-3
       product cards) already did - this just makes it the default. */
    .glass { position: relative; border-radius: var(--r);
      background: var(--bg-3);
      border: 1px solid var(--hairline);
      box-shadow: var(--e-3); }

    .btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; cursor: pointer; font-family: inherit;
      font-size: var(--t-sm); font-weight: 600; letter-spacing: -0.004em; text-decoration: none;
      padding: 12px 22px; border-radius: 999px;
      border: 1px solid transparent; min-height: 46px;
      transition: background .18s ease, border-color .18s ease, color .18s ease, box-shadow .18s ease, transform .16s var(--ease-out); }
    /* Press feedback on every button in the site - the product-card buy/add
       buttons already had this (.p-buy:active, .p-add:active below); the
       shared .btn base never did, so every primary CTA, nav action, and
       form submit sat there unconfirmed on tap/click. */
    .btn:active { transform: scale(0.97); }
    .btn[disabled]:active, .btn:disabled:active { transform: none; }
    /* Flat ember, no gradient, no coloured halo. Depth comes from a real border
       and a neutral shadow; state comes from the fill stepping up. */
    .btn-primary { color: #fff; background: var(--accent-deep);
      border-color: rgba(255,255,255,0.14); }
    .btn-primary:hover { background: var(--accent); border-color: rgba(255,255,255,0.24); }
    .btn-primary:active { background: #cf2f27; }
    .btn-ghost { color: var(--fg); background: var(--glass-top); border-color: var(--hairline); }
    .btn-ghost:hover { background: rgba(255,255,255,0.10); border-color: var(--hairline-strong); }
    .btn-ghost:active { background: rgba(255,255,255,0.06); }
    /* The secondary half of a buy pair - deliberately NEUTRAL.

       This used to be a red-on-red tint: accent text, accent fill, accent
       border, sat next to a solid accent primary. Two ember buttons side by
       side is the single most recognisable generated-commerce tell, and it
       broke the accent-discipline rule two lines down in DESIGN.md's own Do
       list - if it is the third or fourth red thing on screen, make it
       neutral. Add to cart is a secondary action; it should not compete with
       Buy now for the page's one solid ember.

       It stays heavier than .btn-ghost (stronger border, brighter label) so
       the pair still reads as two real commerce actions rather than a button
       and an afterthought. */
    .btn-tinted { color: var(--fg); background: rgba(255,255,255,0.07); border-color: var(--hairline-strong); }
    .btn-tinted:hover { background: rgba(255,255,255,0.12); border-color: rgba(255,255,255,0.26); color: #fff; }
    .btn-tinted:active { background: rgba(255,255,255,0.09); }
    .btn-minecraft { color: #fff; background: var(--mc);
      border-color: rgba(255,255,255,0.16); box-shadow: var(--e-2); }
    .btn-minecraft:hover { background: var(--mc-hover); border-color: rgba(255,255,255,0.26); }
    .btn-minecraft:active { background: var(--mc-press); box-shadow: var(--e-1); }
    .btn[disabled], .btn:disabled { background: var(--bg-3); color: var(--fg-3);
      border-color: var(--hairline); box-shadow: none; cursor: not-allowed; }
    /* .btn sets display:inline-flex, and an author display beats the browser's
       [hidden]{display:none}. Without this, `btn.hidden = true` sets the
       attribute and changes nothing on screen - which is how the Place order
       button stayed visible on the Robux panel, where it does nothing. */
    .btn[hidden] { display: none; }
    .btn-plat-ic { display: block; border-radius: 4px; }
    /* Material Symbols ship as filled geometry on a 0 -960 960 960 viewBox, so
       they take `fill: currentColor` and must never be given a stroke - the
       hand-drawn icons elsewhere in this file are stroked, these are not.
       Inside a .btn they become a flex item and inherit the button's 8px gap. */
    .msym { flex: 0 0 auto; display: block; fill: currentColor; }
    /* A brand mark, not a cropped tile: no rounded corners to fake an app icon,
       and no fill behind it. The glyph is already white, so it reads directly
       on the ember primary the way the Studio tile's blue never could. Marks
       carry more optical weight than tiles, so they sit a step smaller. */
    .btn-plat-mark { border-radius: 0; width: 22px; height: 22px; object-fit: contain;
      /* Nudges the mark off the cap-height baseline so it centres against the
         label rather than against the button box. */
      margin-top: -1px; }
    .btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

    header.nav { position: fixed; top: 12px; left: 50%; transform: translateX(-50%); z-index: 100;
      width: max-content; max-width: calc(100% - 24px); }
    .nav-inner { position: relative; display: flex; align-items: center; gap: 18px; height: 50px;
      padding: 0 8px 0 14px; border-radius: 16px;
      background: var(--bg-2);
      box-shadow: 0 1px 0 var(--glass-top) inset, 0 18px 50px -22px var(--ink-75);
      transition: background .4s var(--ease), gap .55s cubic-bezier(0.6,0,0.2,1); }
    .nav-inner::before { content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
      background: linear-gradient(180deg, var(--glass-top), var(--glass-bot) 50%, var(--glass-bot));
      -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
      -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none; }
    header.nav.scrolled .nav-inner { background: var(--bg-2); opacity: 0.92; }
    .brand { display: flex; align-items: center; text-decoration: none; }
    .brand .logo { height: 34px; width: auto; display: block; }
    footer .brand .logo { height: 38px; }
    .nav-links { display: flex; align-items: center; justify-content: center; gap: 4px; }
    .nav-set { display: flex; align-items: center; gap: 2px; min-width: 0; opacity: 1; overflow: hidden;
      transition: max-width .55s cubic-bezier(0.6,0,0.2,1), opacity .3s ease, margin .55s cubic-bezier(0.6,0,0.2,1); }
    .nav-links.searching .nav-set { opacity: 0; pointer-events: none; margin-right: -4px; }

    .nav-inner:has(.nav-links.searching) { gap: 7px; }
    .nav-links.searching .nav-search-wrap { margin-left: -3px; }
    .nav-links a { color: var(--fg-2); text-decoration: none; font-size: 13px; font-weight: 500; white-space: nowrap;
      padding: 7px 13px; border-radius: 10px; transition: color .2s, background .2s; }
    .nav-links a:hover { color: var(--fg); background: rgba(255,255,255,0.06); }
    .nav-search-wrap { display: flex; align-items: center; transition: margin .55s cubic-bezier(0.6,0,0.2,1); }
    /* Matches .nav-account's 38px so the search/notif/account icon row reads
       as one consistent size - it was 32px, 6px smaller than its neighbours,
       which read as uneven spacing even though the flex gap is identical. */
    .nav-search { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; flex: 0 0 auto;
      border-radius: 10px; background: transparent; border: 1px solid transparent;
      color: var(--fg-2); cursor: pointer; transition: color .2s, background .2s; }
    .nav-search:hover, .nav-links.searching .nav-search { color: var(--accent); }
    .nav-search-input { width: 0; min-width: 0; opacity: 0; padding: 0; border: 0; outline: none; background: rgba(255,255,255,0); border-radius: 10px;
      color: var(--fg); font-family: inherit; font-size: 14px; letter-spacing: -0.01em;
      transition: width .55s cubic-bezier(0.6,0,0.2,1), opacity .35s ease, padding .4s ease, background .3s ease; }
    .nav-links.searching .nav-search-input { width: 190px; opacity: 1; padding: 6px 11px; background: rgba(255,255,255,0.05); }
    .nav-search-input::placeholder { color: var(--fg-3); }
    .nav-cta { display: flex; align-items: center; gap: 4px; }
    /* .btn's base radius is a full 999px pill, but this button sits inside
       .nav-inner's own much shallower 16px-rounded bar - a full pill next
       to a 16px corner reads as mismatched, not "rounded to match". */
    .nav-cta > a.btn-primary { border-radius: var(--r-sm); }
    /* Mobile-only nav-links stand-in - see the 1100px block below. Desktop
       already has "Shop" as a real nav-links item (this stays hidden
       there), so it's styled to match that plain-text link exactly rather
       than as a second filled button next to Join - two solid pills of
       different colours right next to each other reads as a generated-UI
       tell, not a real hierarchy. */
    .nav-shop-mobile { display: none; color: var(--fg-2); text-decoration: none; font-size: 13px; font-weight: 500;
      padding: 9px 13px; border-radius: 10px; transition: color .2s, background .2s; }
    .nav-shop-mobile:hover { color: var(--fg); background: rgba(255,255,255,0.06); }
    .nav-cta .btn { min-height: 38px; padding: 9px 16px; font-size: 13px; }
    .menu-btn { display: none; background: rgba(255,255,255,0.06); border: 1px solid var(--hairline);
      color: var(--fg); width: 38px; height: 38px; border-radius: 10px; cursor: pointer; }

    /* The header controls are deliberately compact - 34-43px - which leaves
       them under the 44px touch guideline. Rather than inflate the pill, each
       one projects an invisible 44px hit area, the same trick .nr-dot already
       uses for the carousel dots. The expansions stay smaller than the gaps
       between neighbours (8px in .nav-cta, 18px after .brand), so no control
       can swallow the one next to it. */
    .menu-btn, .nav-account, .brand, .cart-fab { position: relative; }
    .menu-btn::after, .nav-account::after, .brand::after, .cart-fab::after {
      content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
      width: 100%; height: 100%; min-width: 44px; min-height: 44px; }
    .nav-cta .btn { min-height: 44px; }

    .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }

    /* Link arrows, drawn. These used to be → / ← / ↗ typed into the copy, so
       they rendered at whatever weight and baseline the face happened to give
       them and never matched the SVG icons beside them. One geometry, one
       stroke weight, and they inherit the link's colour. */
    .lnk-arrow::after, .has-arrow::after, [data-arrow]::after,
    .lnk-back::before, .co-back::before,
    .ico-arrow, .ico-ext {
      content: ""; display: inline-block; width: 0.95em; height: 0.95em;
      vertical-align: -0.12em; flex: 0 0 auto;
      background-color: currentColor;
      -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
      -webkit-mask-position: center; mask-position: center;
      -webkit-mask-size: contain; mask-size: contain;
    }
    .lnk-arrow::after, .has-arrow::after, [data-arrow]::after, .ico-arrow {
      margin-left: 0.32em;
      -webkit-mask-image: var(--ico-arrow-r); mask-image: var(--ico-arrow-r);
    }
    .lnk-back::before, .co-back::before {
      margin-right: 0.32em;
      -webkit-mask-image: var(--ico-arrow-l); mask-image: var(--ico-arrow-l);
    }
    .ico-ext { margin-left: 0.32em; -webkit-mask-image: var(--ico-ext); mask-image: var(--ico-ext); }
    /* The arrow leans into the direction of travel on hover. */
    .lnk-arrow, .has-arrow, [data-arrow], .lnk-back, .co-back { display: inline-flex; align-items: center; }
    .lnk-arrow::after, .has-arrow::after, [data-arrow]::after { transition: transform .18s var(--ease-out); }
    .lnk-back::before, .co-back::before { transition: transform .18s var(--ease-out); }
    @media (hover: hover) and (pointer: fine) {
      .lnk-arrow:hover::after, .has-arrow:hover::after, [data-arrow]:hover::after,
      a:hover > .has-arrow::after { transform: translateX(3px); }
      .lnk-back:hover::before, .co-back:hover::before { transform: translateX(-3px); }
    }
    .hero-content { position: relative; z-index: 1; text-align: center; }
    .hero .lead { font-size: clamp(16px, 2.1vw, 20px); color: #d3d8e1; max-width: 520px; margin: 0 auto;
      text-shadow: 0 2px 22px var(--ink-75); }
    .hero-cta { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin-top: 22px; }
    .hero-body { margin-top: 0; }

    .bento { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: 210px; gap: 16px; margin-top: 0; }
    .tile { padding: 22px; overflow: hidden; display: flex; flex-direction: column;
      transition: transform .26s var(--ease), box-shadow .26s var(--ease); }

    a.tile, a.asset-visual { color: inherit; text-decoration: none; cursor: pointer; position: relative;
      -webkit-tap-highlight-color: transparent; will-change: transform; }
    a.asset-visual { transition: transform .26s var(--ease), box-shadow .26s var(--ease); }

    a.tile:hover, a.asset-visual:hover {
      box-shadow: var(--e-3), inset 0 0 0 1px var(--hairline-strong); }
    a.tile:focus-visible, a.asset-visual:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
    a.tile.is-pressing, a.asset-visual.is-pressing { transform: scale(0.99); transition: transform .12s var(--ease); }

    /* The tinted expanding ripple is retired. Press feedback is the scale above. */
    .card-ripple { display: none; }
    @media (prefers-reduced-motion: reduce) {
      a.tile.is-pressing, a.asset-visual.is-pressing { transform: none; }
    }
    .tile .label { font-size: 12px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--fg-3); }
    .tile .big { font-size: var(--t-h2); font-weight: 700; letter-spacing: -0.04em; line-height: 1; }
    .tile .sub { font-size: 13px; color: var(--fg-2); margin-top: 6px; }
    .grow { flex: 1; }
    .t-featured { grid-column: span 2; grid-row: span 2; padding: 0; }
    .scene { position: relative; height: 100%; border-radius: inherit; overflow: hidden; display: flex; flex-direction: column;
      justify-content: flex-end; padding: 26px;
      background:
        linear-gradient(180deg, rgba(10,11,14,0.05) 0%, rgba(10,11,14,0.45) 58%, rgba(10,11,14,0.88) 100%),
        url('new-release.jpg') center / cover no-repeat,
        #0f1014; }
    .scene::after { content: ""; position: absolute; inset: 0; pointer-events: none;
      background: linear-gradient(180deg, transparent 55%, rgba(10,11,14,0.35)); }

    .nr-carousel { position: absolute; inset: 0; z-index: 0; overflow: hidden; border-radius: inherit; }
    .nr-track { display: flex; height: 100%; width: 100%; transition: transform .7s var(--ease); will-change: transform; }
    .nr-slide { flex: 0 0 100%; height: 100%; background: center / cover no-repeat; }
    .nr-carousel::after { content: ""; position: absolute; inset: 0; pointer-events: none;
      background: linear-gradient(180deg, rgba(10,11,14,0.05) 0%, rgba(10,11,14,0.45) 58%, rgba(10,11,14,0.88) 100%); }
    .nr-dots { position: absolute; right: 22px; bottom: 22px; z-index: 2; display: flex; gap: 7px; }
    .nr-dot { position: relative; width: 7px; height: 7px; border-radius: 999px; background: rgba(255,255,255,0.4);
      transition: width .35s var(--ease), background .35s var(--ease); }
    .nr-dot::after { content: ""; position: absolute; inset: -8px; }
    .nr-dot:hover { background: rgba(255,255,255,0.7); }
    .nr-dot.active { width: 18px; background: var(--accent); }
    .nr-dot.active:hover { background: var(--accent); }
    @media (prefers-reduced-motion: reduce) { .nr-track { transition: none; } }

    .tile.t-builds { background:
        linear-gradient(180deg, rgba(10,11,14,0.30) 0%, rgba(10,11,14,0.58) 58%, rgba(10,11,14,0.90) 100%),
        url('builds.jpg') center / cover no-repeat, #0f1014; }
    .tile.t-scripts { background:
        linear-gradient(180deg, rgba(10,11,14,0.30) 0%, rgba(10,11,14,0.58) 58%, rgba(10,11,14,0.90) 100%),
        url('premade.jpg') center / cover no-repeat, #0f1014; }
    .tile.t-products { background:
        linear-gradient(110deg, rgba(10,11,14,0.78) 0%, rgba(10,11,14,0.50) 45%, rgba(10,11,14,0.30) 100%),
        url('products.jpg') center / cover no-repeat, #0f1014; }
    .tile.t-builds .label, .tile.t-scripts .label, .tile.t-products .label { color: #c7ccd6; }
    .tile.t-builds .big, .tile.t-builds .sub,
    .tile.t-scripts .big, .tile.t-scripts .sub,
    .tile.t-products .big, .tile.t-products .sub { text-shadow: 0 2px 10px var(--ink-60); }
    .tile.t-products .ico-chip { background: rgba(10,11,14,0.45); border-color: rgba(255,255,255,0.14); }
    .scene .chip { position: relative; z-index: 1; align-self: flex-start; font-size: 12px; font-weight: 600; letter-spacing: 0.03em;
      color: var(--fg); padding: 6px 12px; border-radius: 999px; background: rgba(8,10,14,0.5); border: 1px solid var(--hairline); backdrop-filter: blur(8px); }
    .scene .title { position: relative; z-index: 1; font-size: 24px; font-weight: 700; letter-spacing: -0.035em; margin-top: 14px; }
    .scene .row { position: relative; z-index: 1; display: flex; gap: 18px; margin-top: 10px; font-size: 13px; color: var(--fg-2); }
    .scene .row span { display: inline-flex; align-items: center; gap: 6px; }
    .t-status .live { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--fg); }
    .t-community { grid-column: span 2; flex-direction: row; align-items: center; gap: 20px; }
    .avatars { display: flex; }
    .avatars span { width: 36px; height: 36px; border-radius: 50%; margin-left: -10px; border: 2px solid #14151a; background: linear-gradient(160deg, var(--bg-3), var(--bg-2)); }
    .avatars span:first-child { margin-left: 0; }
    .avatars .more { display: grid; place-items: center; font-size: 12px; font-weight: 600; color: var(--fg-2); background: var(--glass-top); }
    .stars { display: inline-flex; align-items: center; gap: 3px; line-height: 0; color: var(--star); margin-top: 4px; }
    .stars svg { display: block; width: 15px; height: 15px; }
    .stars .off { display: inline-flex; color: rgba(255,255,255,0.16); }
    .ico-chip { width: 40px; height: 40px; border-radius: 16px; display: grid; place-items: center; background: var(--glass-top); border: 1px solid var(--hairline); color: var(--fg); }

    .cap-strip { margin-top: 14px; display: flex; align-items: stretch; padding: 6px;
      background: linear-gradient(180deg, rgba(30,32,40,0.62), rgba(22,24,30,0.6)); }
    .cap-strip .item { flex: 1; display: flex; align-items: center; gap: 12px; padding: 18px 20px; position: relative; }
    .cap-strip .item + .item::before { content: ""; position: absolute; left: 0; top: 22%; height: 56%; width: 1px; background: var(--hairline); }
    .cap-strip .item .ic { color: var(--accent); flex: 0 0 auto; display: flex; align-items: center; margin-top: -2px; }
    .cap-strip .item .t { font-size: 14px; font-weight: 600; }
    .cap-strip .item .s { font-size: 12px; color: var(--fg-3); }

    .section-pad { padding: 104px 0; }
    /* Section headings sat at hero scale (up to 52px), so every band on the page
       shouted at the same volume. Stepped down to a real second-level size. */
    .sec-head { max-width: 640px; margin: 0 auto 40px; text-align: center; padding-top: 26px; }
    .sec-head h2 { font-size: var(--t-h2); margin: 0; }
    .sec-head p { color: var(--fg-2); font-size: var(--t-lead); margin: 14px 0 0; }
    .sec-countdown { display: inline-block; margin-top: 12px; font-size: 12px; font-weight: 700;
      letter-spacing: 0.02em; color: var(--fg-3); font-variant-numeric: tabular-nums; }
    .sec-countdown[hidden] { display: none; }

    .asset-feature { display: block; overflow: hidden; padding: 0; }
    .asset-feature .asset-banner { width: 100%; height: auto; display: block; }
    .asset-feature .vis { position: relative; min-height: 360px; padding: 24px; display: flex; flex-direction: column; justify-content: space-between;
      background: linear-gradient(180deg, rgba(10,11,14,0.2), transparent 55%),
        radial-gradient(120% 120% at 20% 120%, rgba(70,72,80,0.5), transparent 60%), linear-gradient(180deg,#1d1f25,#101115); }
    .asset-feature .vis .chip { position: relative; z-index: 1; align-self: flex-start; font-size: 12px; font-weight: 600;
      padding: 6px 12px; border-radius: 999px; background: rgba(10,11,14,0.5); border: 1px solid var(--hairline); }
    .asset-feature .vis .ftitle { position: relative; z-index: 1; font-size: var(--t-h2); font-weight: 700; letter-spacing: -0.04em; }
    .asset-feature .info { padding: 32px 34px; display: flex; flex-direction: column; justify-content: center; gap: 16px; }
    .asset-feature .info p { color: var(--fg-2); font-size: 15px; margin: 0; }
    .stat-row { display: flex; gap: 26px; }
    .stat-row .n { font-size: 24px; font-weight: 700; letter-spacing: -0.03em; }
    .stat-row .l { font-size: 12px; color: var(--fg-3); }

    .asset-duo { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 16px; }

    .asset-visual { padding: 0; overflow: hidden; min-height: 260px; display: flex; align-items: flex-end; }
    .asset-visual .v { width: 100%; height: 100%; min-height: 260px; position: relative; padding: 24px; display: flex; flex-direction: column; justify-content: flex-end;
      background: linear-gradient(180deg, rgba(8,9,12,0.12) 0%, rgba(8,9,12,0.55) 52%, rgba(8,9,12,0.93) 100%), url('premade.jpg') center / cover no-repeat, #100d0f; }
    /* .v's own inline background gradient (hardcoded rgba(8,9,12,...), see the
       markup) is a dark photo scrim in both themes, not just dark mode - the
       label needs to stay white in both for the same reason .resell-points
       does, rather than inheriting a.asset-visual's var(--fg). */
    .asset-visual .v h3 { position: relative; z-index: 1; font-size: 24px; font-weight: 700; color: #fff; }
    .asset-visual .v p { position: relative; z-index: 1; font-size: 13px; color: #c7ccd6; margin: 8px 0 0; max-width: 340px; text-shadow: 0 2px 10px var(--ink-60); }
    .asset-visual .v .tag { position: absolute; top: 22px; left: 22px; font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
      color: var(--fg-2); padding: 5px 11px; border-radius: 999px; background: rgba(10,11,14,0.5); border: 1px solid var(--hairline); }

    .asset-list { background: var(--bg-3); border: 1px solid var(--hairline); border-radius: var(--r); padding: 26px 28px; }
    .asset-list h3 { font-size: 20px; font-weight: 600; }
    .asset-list .rows { margin-top: 16px; display: flex; flex-direction: column; }
    .asset-list .lrow { display: flex; align-items: center; justify-content: space-between; padding: 13px 0; border-top: 1px solid var(--hairline); font-size: 14px; }
    .asset-list .lrow:first-child { border-top: 0; }
    .asset-list .lrow .c { color: var(--fg-2); }
    .asset-list .lrow .v { font-weight: 600; color: var(--accent); }

    .resell { display: grid; grid-template-columns: 1.08fr 0.92fr; overflow: hidden; align-items: stretch; }
    .resell-body { padding: 46px 50px; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; gap: 16px; }
    .resell-tag { font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent);
      padding: 6px 13px; border-radius: 999px; background: rgba(255,77,68,0.12); border: 1px solid rgba(255,77,68,0.28); }
    .resell-body h2 { font-size: var(--t-h2); letter-spacing: -0.035em; }
    .resell-body p { color: var(--fg-2); font-size: 16px; line-height: 1.7; max-width: 460px; margin: 0; }
    .resell-cta { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 8px; }
    .resell-art { position: relative; min-height: 300px; background: center / cover no-repeat; }
    .resell-art::before { content: ""; position: absolute; inset: 0;
      background: linear-gradient(110deg, rgba(15,16,21,0.92) 0%, rgba(15,16,21,0.55) 45%, rgba(15,16,21,0.25) 100%); }
    .resell-points { position: relative; z-index: 1; list-style: none; margin: 0; padding: 30px 34px;
      display: flex; flex-direction: column; justify-content: center; gap: 20px; height: 100%; }
    /* Always sits over .resell-art's photo + dark scrim (see .resell-art::before
       above), in both themes - unlike .cta's band this one keeps its photo in
       light mode too, so the label stays hardcoded white rather than following
       var(--fg), which would go near-black-on-dark-photo the same way .cta's
       heading did. */
    .resell-points li { position: relative; padding-left: 32px; font-size: 17px; font-weight: 500; color: #fff; }
    /* top:8px was a guess at the checkmark's vertical position rather than
       an actual center - true centering against the li's own height (which
       text sizing/line-height actually determines) instead of a fixed guess. */
    .resell-points li::before { content: ""; position: absolute; left: 0; top: 50%; width: 17px; height: 10px;
      border-left: 3px solid var(--accent); border-bottom: 3px solid var(--accent); transform: translateY(-65%) rotate(-45deg); }

    /* Spans both .resell columns as its own row, closed off by a hairline
       so it reads as a distinct band rather than a continuation of the
       body copy above it. */
    .resell-trust { grid-column: 1 / -1; padding: 34px 50px 42px; border-top: 1px solid var(--hairline); }
    .resell-trust h3 { font-size: var(--t-h5); letter-spacing: -0.02em; margin: 0; }
    .resell-trust p { color: var(--fg-2); font-size: 15px; line-height: 1.6; margin: 8px 0 0; max-width: 520px; }
    /* flex:1 per chip (below) so three logos stretch to fill the row -
       fewer, bigger marks reads more confident than a cluster of small
       ones with dead space beside them. */
    .resell-logos { display: flex; align-items: center; gap: 16px; margin-top: 22px; }
    /* Neutral tile per logo, same reasoning as the payment-method marks
       elsewhere - the files vary wildly (transparent wordmarks vs. one
       that shipped on a solid card), so a uniform dark chip is what makes
       them read as one consistent row instead of mismatched floating
       images. */
    .resell-logo { flex: 1; display: flex; align-items: center; justify-content: center; height: 120px; padding: 0 24px;
      border-radius: var(--r-ctl); background: var(--bg-3); border: 1px solid var(--hairline); }
    .resell-logo img { max-height: 72px; max-width: 100%; width: auto; height: auto; object-fit: contain; display: block; }
    /* Broken Studios and Skater Studios both carry extra baked-in padding
       inside their source canvas (Skater's especially, from the black card
       it used to ship on), so at the same max-height they render visibly
       smaller than Destiny Tech's tighter-cropped wordmark. Sized up to
       compensate rather than re-exporting the source files. */
    .resell-logo img.resell-logo-lg { max-height: 96px; }
    @media (max-width: 860px) {
      .resell-trust { padding: 28px 28px 32px; }
      .resell-logo { height: 100px; }
      .resell-logo img { max-height: 58px; }
      .resell-logo img.resell-logo-lg { max-height: 78px; }
    }
    @media (max-width: 560px) {
      .resell-logos { flex-direction: column; }
      .resell-logo { width: 100%; height: 84px; }
      .resell-logo img { max-height: 46px; }
      .resell-logo img.resell-logo-lg { max-height: 62px; }
    }

    .reviews { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
    .review { padding: 26px 28px; display: flex; flex-direction: column; gap: 14px; }
    .review .stars { margin: 0; }
    .review .stars svg { width: 14px; height: 14px; }
    .review p { color: var(--fg); font-size: 15px; line-height: 1.62; margin: 0; flex: 1; }
    .review-by { display: flex; align-items: center; gap: 12px; }
    .review-by .ra { flex: 0 0 auto; width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
      font-weight: 700; font-size: 14px; color: var(--fg); background: linear-gradient(160deg, var(--bg-3), var(--bg-2)); border: 1px solid var(--hairline); }
    .review-by img.ra { object-fit: cover; }
    .review-by .rn { display: flex; flex-direction: column; font-size: 14px; font-weight: 600; }
    .review-by .rn small { font-weight: 500; font-size: 12px; color: var(--fg-3); }
    .reviews-more-wrap { text-align: center; margin-top: 30px; }
    .reviews-more { font-size: 15px; font-weight: 600; color: var(--fg); text-decoration: none;
      padding: 8px 4px; border-bottom: 1px solid var(--hairline-strong); transition: color .2s, border-color .2s; }
    .reviews-more:hover { color: var(--accent-ink); border-color: var(--accent); }
    @media (max-width: 860px) {
      .resell { grid-template-columns: 1fr; }
      .resell-body { padding: 34px 28px; }
      .resell-art { min-height: 180px; }
      .reviews { grid-template-columns: 1fr; }
    }

    .team { display: flex; justify-content: center; gap: 10px; max-width: 840px; margin: 0 auto; }
    .tpanel { position: relative; flex: 1; height: 520px; border-radius: 16px; overflow: hidden; cursor: pointer;
      border: 1px solid var(--hairline);
      transition: flex .9s cubic-bezier(0.6,0,0.2,1), box-shadow .8s ease, border-color .8s ease; }
    .tpanel.active { flex: 1.7; box-shadow: var(--e-4); border-color: var(--hairline-strong); }
    .tpanel .tbg { position: absolute; inset: 0; background: #0c0d11 url('team-bg.jpg') left center / cover no-repeat;
      filter: brightness(0.42) grayscale(0.45); transform: scale(1.05);
      transition: transform 1.1s cubic-bezier(0.6,0,0.2,1), filter .9s ease; }
    .tpanel.active .tbg { transform: scale(1.14) translateX(-18px); filter: brightness(0.98) grayscale(0); }
    .tpanel .toverlay { position: absolute; inset: 0;
      background: linear-gradient(180deg, rgba(8,9,12,0.45) 0%, rgba(8,9,12,0.08) 40%, rgba(8,9,12,0.95) 100%); }
    .tpanel .tcontent { position: relative; z-index: 2; height: 100%; display: flex; flex-direction: column; justify-content: space-between; padding: 28px; }
    .tpanel .trole-title { font-size: 30px; font-weight: 300; line-height: 1.08; letter-spacing: .02em; text-transform: uppercase;
      color: #fff; text-shadow: 0 2px 14px var(--ink-60);
      transform: translateY(-6px); opacity: .85; transition: transform .8s cubic-bezier(0.6,0,0.2,1), opacity .7s ease; }
    .tpanel.active .trole-title { transform: translateY(0); opacity: 1; }
    .tpanel .trole-title .indent { display: inline-block; margin-left: 34px; }
    /* Drawn at full width and scaled down, so the grow animates on the
       compositor instead of relaying out the heading every frame. */
    .tpanel .trole-title::after { content: ""; display: block; width: 64px; height: 3px; border-radius: 4px; background: var(--accent); margin-top: 14px; transform: scaleX(0.406); transform-origin: left center; transition: transform .6s cubic-bezier(0.6,0,0.2,1); }
    .tpanel.active .trole-title::after { transform: scaleX(1); }
    .tpanel .tinfo { transform: translateY(18px); opacity: .78; transition: transform .8s cubic-bezier(0.6,0,0.2,1), opacity .7s ease; }
    .tpanel.active .tinfo { transform: translateY(0); opacity: 1; }
    .tpanel .ttag { font-size: 11px; letter-spacing: .22em; text-transform: uppercase; color: rgba(255,255,255,.55); }
    .tpanel .tname { font-size: 24px; font-weight: 700; letter-spacing: -.02em; margin: 8px 0 0; color: var(--fg); text-shadow: 0 2px 14px var(--ink-60); }
    @media (max-width: 680px) { .team { flex-direction: column; max-width: 380px; } .tpanel { height: 280px; } .tpanel.active { flex: 1; }
      .tpanel .trole-title, .tpanel .tinfo { opacity: 1; transform: none; } .tpanel .trole-title::after { transform: none; } }
    .member { padding: 26px 22px; text-align: center; transition: transform .4s var(--ease); }
    .member:hover { transform: translateY(-2px); }
    .member .av { width: 72px; height: 72px; border-radius: 50%; margin: 0 auto 16px; display: grid; place-items: center;
      font-size: 24px; font-weight: 700; color: var(--fg); background: linear-gradient(160deg, #2b2e36, #1a1c22); border: 1px solid var(--hairline); }
    .member.lead .av { box-shadow: 0 0 0 2px var(--accent); }
    .member h3 { font-size: 16px; font-weight: 600; }
    .member .role { font-size: 13px; color: var(--accent); margin-top: 3px; font-weight: 500; }
    .member .bio { font-size: 12px; color: var(--fg-3); margin-top: 8px; }
    .member .badge { display: inline-block; margin-top: 12px; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
      color: var(--fg-2); padding: 4px 10px; border-radius: 999px; background: rgba(255,255,255,0.05); border: 1px solid var(--hairline); }

    .cta { padding: 78px 60px; text-align: center; position: relative; overflow: hidden;
      background:
        linear-gradient(180deg, rgba(8,9,12,0.80) 0%, rgba(8,9,12,0.68) 50%, rgba(8,9,12,0.86) 100%),
        url('cta-bg.jpg') center / cover no-repeat, #0c0d11; }
    .cta::after { content: ""; position: absolute; inset: 0; z-index: 0;
      background: linear-gradient(180deg, transparent, rgba(10,11,14,0.4)); }
    .cta > * { position: relative; z-index: 1; }
    .cta h2 { font-size: var(--t-h2); text-shadow: 0 2px 18px var(--ink-60); }
    .cta p { color: #cdd3dd; font-size: 17px; max-width: 480px; margin: 14px auto 0; text-shadow: 0 2px 14px var(--ink-60); }

    footer { border-top: 1px solid var(--hairline); margin-top: 40px; padding: 54px 0 34px; }
    /* Our Assets only holds 3 short links (Roblox / Minecraft / Releases &
       Updates) but was given more width (1.7fr) than Explore's 1fr despite
       Explore's own longest link ("Development Tutorials") being longer -
       the extra width Our Assets didn't need read as a gap before Account. */
    .foot-grid { display: grid; grid-template-columns: 1.5fr 1.2fr 1.1fr 0.9fr 1.1fr 1.2fr; gap: 34px 26px; }
    .foot-brand p { color: var(--fg-2); font-size: 13px; line-height: 1.6; margin: 16px 0 14px; max-width: 240px; }
    .foot-col h3 { font-size: 12px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: #fff; margin: 0 0 14px; }
    .foot-col a { display: block; color: var(--fg-2); text-decoration: none; font-size: var(--t-label); padding: 5px 0;
      transition: color .2s ease, transform .2s var(--ease); }
    .foot-col a:hover { color: var(--fg); transform: translateX(3px); }
    .foot-links2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; }
    .foot-bottom { margin-top: 44px; padding-top: 22px; border-top: 1px solid var(--hairline); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px; align-items: center; }
    .foot-bottom .muted { font-size: 12px; color: var(--fg-3); }
    .foot-social { display: flex; align-items: center; gap: 16px; }
    /* The marks are 18px, which is below the 24px WCAG target minimum and well
       under a comfortable thumb. The ::after projects a 44px hit area without
       changing layout or the visual size - the same trick .nr-dot already uses
       for the carousel dots. */
    .foot-social a { position: relative; display: inline-flex; color: var(--fg); opacity: 0.78; transition: opacity .2s, transform .2s var(--ease); }
    .foot-social a::after { content: ""; position: absolute; inset: -13px; }
    .foot-social a:hover { opacity: 1; }
    .foot-social a:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; border-radius: var(--r-xs); }
    .foot-social svg { width: 18px; height: 18px; display: block; }
    @media (max-width: 980px) { .foot-grid { grid-template-columns: 1fr 1fr 1fr; } }
    @media (max-width: 560px) { .foot-grid { grid-template-columns: 1fr 1fr; } }

    /* Entrance animates from an already-visible default. The hidden state is
       scoped to html.js, which an inline head script sets, so a failed or
       blocked app.js leaves the page fully readable instead of blank. */
    .reveal { opacity: 1; transform: none; }
    html.js .reveal { opacity: 0; transform: translateY(14px);
      transition: opacity .55s var(--ease-out), transform .55s var(--ease-out); }
    html.js .reveal.in { opacity: 1; transform: none; }
    .d1 { transition-delay: .06s; } .d2 { transition-delay: .12s; } .d3 { transition-delay: .18s; }
    .d4 { transition-delay: .24s; } .d5 { transition-delay: .30s; }

    /* The nav pill and the currency/cart pill (.tc) are two independently
       positioned fixed elements: the pill centres itself on the viewport while
       .tc is pinned right, so they collide whenever the viewport is too narrow
       to hold both. The full pill is ~580px and .tc claims ~235px including its
       gutter, which needs ~1100px of viewport - below that the links collapse
       into the hamburger so the two occupy disjoint bands by construction.
       This is a wider breakpoint than the 900px used for page layout below;
       they are separate concerns and deliberately not shared. */
    @media (max-width: 1100px) {
      .nav-links { display: none; } .menu-btn { display: grid; place-items: center; }
      .nav-cta .btn span.txt { display: none; }
      /* With the label hidden, this is an icon-only button sitting next to
         two other icon-only controls (.nav-account, .menu-btn) that are
         38px - but .nav-cta .btn's own 44px min-height (set for the
         labelled desktop pill, where the extra height reads as normal
         padding) still applies. Unmatched, it renders visibly taller than
         its neighbours - the "compressed/uneven" look on tablet. Matching
         it to 38px here brings all three controls to one height. */
      .nav-cta .btn { padding: 12px 14px; height: 38px; min-height: 38px; }

      /* Below 1100px "Shop" drops out of the collapsed nav-links entirely,
         so the only way to reach the catalog is the hamburger panel - on a
         storefront that's the one link visitors need fastest. Swaps in for
         the Discord Join button here (which becomes icon-only anyway once
         its label hides above), rather than adding a fourth control and
         blowing the header's tight width budget noted below. Discord stays
         reachable from the footer and the mega-menu banner. */
      .nav-shop-mobile { display: inline-flex; }
      .nav-cta > a[href="https://discord.gg/coldd"] { display: none; }

      /* Left-anchored rather than centred, so the pill grows away from .tc.
         brand+account+Join+burger (~208px) and currency+cart (~211px) still do
         not both fit the ~349px available at 375px, so the currency switcher -
         the one low-frequency control of the set - moves into the menu panel
         (app.js relocates the live node) and .tc keeps just the cart. */
      header.nav { left: 12px; right: auto; transform: none;
        max-width: calc(100% - 24px - 104px); }
      .tc > .cur-switch { display: none; }
      /* .cart-fab-sep exists to separate the cart pill from .cur-switch,
         which just got hidden above - without this it was a divider with
         nothing left of it to divide from. Padding moves onto the pill
         itself so the icon keeps balanced spacing instead of sitting
         flush against the left edge. */
      .cart-fab-sep { display: none; }
      /* .tc > .cart-fab (not just .cart-fab) so this beats the later,
         higher-in-source-order unconditional `.cart-fab { padding: 0 11px 0
         0; }` base rule at equal specificity - without the extra parent
         selector the cascade fell back to that rule's padding-left: 0 and
         the icon sat flush against the pill's left edge. */
      .tc > .cart-fab { padding-left: 11px; }
      .nav-links.mobile-open .cur-switch { display: inline-flex; align-self: flex-start; margin-top: 22px; }

      /* Mobile nav menu - .menu-btn previously had no JS wired to it at all,
         so on any collapsed viewport there was no way to reach Home/Shop/
         Blog/About from the header. app.js toggles this class; the panel
         reuses .nav-links itself (search bar included) instead of duplicating
         the link markup.
         The panel is a descendant of header.nav, so these z-indexes resolve
         against the header's own stacking context, not the page. The panel
         must stay under the brand and the .nav-cta that holds the close
         button - at z-index 90 over a static .nav-cta it covered its own
         close control and the menu could only be dismissed with Escape. */
      .brand, .nav-cta { position: relative; z-index: 2; }
      .nav-links.mobile-open {
        display: flex; position: fixed; inset: 0; z-index: 1;
        flex-direction: column; align-items: stretch; justify-content: flex-start;
        padding: 100px 24px 32px; background: var(--bg-2, #191b21);
        overflow-y: auto; -webkit-overflow-scrolling: touch;
      }
      .nav-links.mobile-open .nav-set {
        flex-direction: column; align-items: stretch; gap: 2px;
        opacity: 1; overflow: visible; margin: 0;
      }
      .nav-links.mobile-open .nav-set a {
        padding: 17px 4px; font-size: 17px; font-weight: 600;
        border-bottom: 1px solid var(--hairline);
      }
      .nav-links.mobile-open .nav-search-wrap { margin: 18px 0 0; display: flex; align-items: center; gap: 8px; }
      .nav-links.mobile-open .nav-search { width: 44px; height: 44px; flex: 0 0 auto; }
      .nav-links.mobile-open .nav-search-input {
        flex: 1; width: auto; min-width: 0; opacity: 1; padding: 12px 14px; font-size: 16px;
        background: var(--bg-3); border-radius: 10px;
      }
      body.nav-menu-open { overflow: hidden; }
      /* .tc (currency/cart pill) is a separate fixed element with its own
         z-index (250) so it stays clickable over normal page content - but
         that same z-index also floats it on top of the full-screen mobile
         menu (z-index 1) once open, reading as a stray button overlapping
         the menu rather than an actual navbar control. Hide it while the
         menu is open; the menu itself has no cart action to preserve. */
      body.nav-menu-open .tc { display: none; }
    }

    @media (max-width: 900px) {
      .bento { grid-template-columns: repeat(2, 1fr); }
      .t-featured, .t-community { grid-column: span 2; }
      .cap-strip { flex-wrap: wrap; }
      .cap-strip .item { flex: 1 1 44%; }
      .cap-strip .item + .item::before { display: none; }
      .asset-feature { grid-template-columns: 1fr; }
      .asset-feature .vis { min-height: 240px; }
      .asset-duo { grid-template-columns: 1fr; }
      .team { grid-template-columns: repeat(2, 1fr); }
      .cta { padding: 44px 24px; }
    }
    @media (max-width: 560px) {
      .bento { grid-template-columns: 1fr; grid-auto-rows: auto; }
      .t-featured { grid-row: span 1; min-height: 300px; }
      .t-community { flex-direction: row; }
      .tile { min-height: 168px; }
      .cap-strip .item { flex: 1 1 100%; }
      .team { grid-template-columns: 1fr; }
      .hero { padding: 84px 0 16px; }
    }
    @media (prefers-reduced-motion: reduce) {
      html { scroll-behavior: auto; }
      .reveal, html.js .reveal { opacity: 1; transform: none; transition: none; }
      /* Collapsing duration alone doesn't stop an infinite animation - a
         loading spinner (animation: spin 3s linear infinite) still loops
         that near-zero cycle forever, which renders as a rapid, chaotic
         flicker instead of stillness. Capping iteration count to 1 stops
         that flicker, but a frozen spinner or a marquee that can no longer
         scroll (most of its 30 logos are only ever visible mid-scroll) both
         read as broken, not calm - these aren't decorative motion, they're
         load-state and content-reveal, so they're exempted below rather
         than collapsed with everything else. */
      * { transition-duration: .01ms !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; }
      .cb-spin, .ty-spin, .btn-spinner {
        animation-duration: 3s !important;
        animation-iteration-count: infinite !important;
      }
      .pay-track { animation-duration: 38s !important; animation-iteration-count: infinite !important; }
    }

.nav-mega { position: fixed; z-index: 150; left: 0; top: -1000px; opacity: 0;
  transform: translateY(-6px); pointer-events: none; will-change: transform, opacity;
  transition: opacity .34s var(--ease), transform .34s var(--ease); }
.nav-mega.open { opacity: 1; transform: none; pointer-events: auto; }
.nav-mega-inner { width: 560px; max-width: calc(100vw - 24px); padding: 12px;
  display: flex; align-items: stretch; gap: 12px; background: var(--bg-2); }
.nav-mega-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.nav-mega-tabs { display: flex; gap: 6px; }
.nmt-tab { padding: 7px 15px; border-radius: 999px; background: rgba(255,255,255,0.04); border: 1px solid var(--hairline);
  color: var(--fg-2); font-family: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease); }
.nmt-tab:hover { color: var(--fg); background: rgba(255,255,255,0.07); }
.nmt-tab.active { background: rgba(255,255,255,0.11); color: #fff; border-color: var(--hairline-strong); }
.nav-mega-cats { position: relative; flex: 1; display: grid; grid-template-columns: 1fr 1fr; gap: 2px; align-content: start; }
.nav-mega-cats[hidden] { display: none; }
.nav-mega-cats a { position: relative; z-index: 1; display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 13px; border-radius: 10px;
  color: var(--fg-2); text-decoration: none; font-size: 13px; font-weight: 500; white-space: nowrap;
  background: rgba(255,255,255,0);
  transition: color .2s var(--ease), background .2s var(--ease); }
.nav-mega-cats a::after { content: ""; width: 14px; height: 14px; flex: 0 0 auto;
  background-color: var(--fg-2); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%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' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
  opacity: 0; transform: translateX(-5px); transition: opacity .2s var(--ease), transform .2s var(--ease); }
.nav-mega-cats a:hover { color: var(--fg); background: rgba(255,255,255,0.055); }
.nav-mega-cats a:hover::after { opacity: 1; transform: none; }
.nav-mega-feature { position: relative; flex: 0 0 196px; overflow: hidden;
  margin: -12px -12px -12px 0; border-radius: 0 var(--r) var(--r) 0;
  display: flex; flex-direction: column; justify-content: flex-end; padding: 16px; text-decoration: none; }
.nav-mega-feature .nmf-img { position: absolute; inset: 0; background-size: cover; background-position: center;
  transition: transform .6s var(--ease); }
.nav-mega-feature:hover .nmf-img { transform: scale(1.07); }
.nav-mega-feature::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(8,9,12,0.10) 28%, rgba(8,9,12,0.84)); }
.nav-mega-feature .nmf-tag { position: absolute; top: 12px; left: 12px; z-index: 2; font-size: 11px; font-weight: 600;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--fg); padding: 4px 9px; border-radius: 999px;
  background: rgba(8,9,12,0.55); border: 1px solid var(--hairline); }
.nav-mega-feature .nmf-title { position: relative; z-index: 2; color: #fff; font-size: 14px; font-weight: 700; letter-spacing: -0.01em; }
.nav-mega-feature .nmf-go { position: relative; z-index: 2; color: var(--fg-2); font-size: 12px; margin-top: 3px; transition: color .2s; }
.nav-mega-feature:hover .nmf-go { color: var(--accent); }

.page-head { padding: 132px 0 26px; text-align: center; }
.page-head h1 { font-size: clamp(40px, 6vw, 66px); font-weight: 800; letter-spacing: -0.04em; }
.page-head p { color: var(--fg-2); font-size: 17px; max-width: 560px; margin: 16px auto 0; }
.about-head-row { display: flex; align-items: center; justify-content: center; gap: 18px; }
.about-head-logo { height: clamp(40px, 6vw, 66px); width: auto; display: block; flex: 0 0 auto; }
@media (max-width: 560px) { .about-head-row { flex-direction: column; gap: 10px; } }
.shop { padding: 16px 0 70px; }
.shop-notitle { padding-top: 132px; }
.shop > .wrap { max-width: 1760px; padding: 0 40px; }
/* Not the shop grid's 280px-sidebar + content layout - this section has no
   sidebar of its own, so reserving that column (like an earlier version
   here did) just shifted the whole heading+grid right instead of centering
   it on the page. */
.free-section > .wrap { max-width: 1400px; padding: 0 40px; }
.shop-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; max-width: 900px; margin: 0 auto 28px; }
.shop-toolbar .shop-searchbar { flex: 1; min-width: 220px; margin: 0; max-width: none; }
.plat-select { position: relative; flex: 0 0 auto; }
.plat-btn { display: flex; align-items: center; gap: 10px; padding: 12px 15px; border-radius: 16px;
  background: var(--bg-3); border: 1px solid var(--hairline); color: var(--fg); font-family: inherit;
  font-size: 14px; font-weight: 600; cursor: pointer; transition: border-color .18s var(--ease); }
.plat-btn:hover { border-color: rgba(255,255,255,0.2); }
.plat-btn-ic { width: 22px; height: 22px; border-radius: 4px; object-fit: cover; flex: 0 0 auto; display: block; }
.plat-chev { color: var(--fg-3); transition: transform .18s var(--ease); flex: 0 0 auto; }
.plat-select.open .plat-chev { transform: rotate(180deg); }
.plat-menu { position: absolute; top: calc(100% + 8px); left: 0; z-index: 40; width: 208px;
  padding: 6px; border-radius: 16px; background: var(--bg-2); border: 1px solid var(--hairline);
  box-shadow: 0 20px 46px -18px var(--ink-75); }
.plat-menu[hidden] { display: none; }
.plat-opt { display: flex; align-items: center; gap: 11px; padding: 10px 12px; border-radius: 10px;
  color: var(--fg-2); text-decoration: none; font-size: 14px; font-weight: 600;
  transition: background .16s var(--ease), color .16s var(--ease); }
.plat-opt img { width: 26px; height: 26px; border-radius: 10px; object-fit: cover; flex: 0 0 auto; }
.plat-opt:hover { background: rgba(255,255,255,0.06); color: var(--fg); }
.plat-opt.active { background: rgba(255,77,68,0.14); color: #fff; }
.sort-field { position: relative; flex: 0 0 auto; }
.sort-btn { display: flex; align-items: center; gap: 9px; padding: 13px 17px; border-radius: 16px;
  background: var(--bg-3); border: 1px solid var(--hairline); color: var(--fg); font-family: inherit;
  cursor: pointer; transition: border-color .18s var(--ease); }
.sort-btn:hover { border-color: rgba(255,255,255,0.2); }
.sf-label { font-size: 13px; color: var(--fg-3); white-space: nowrap; }
/* "Sort by Recommended" alone was wide enough to force the Filters button
   (now living right beside it, see app.js) onto its own line below rather
   than sharing Sort's line - "Recommended" on its own reads fine without
   the redundant label at this width. */
@media (max-width: 480px) { .sf-label { display: none; } }
/* .sort-menu is right:0 (anchors under the button's right edge), sized for
   a Sort button sitting further right/centre on desktop. On mobile Sort is
   the toolbar's first item, hard against the left edge - a 220px-wide menu
   still anchored to the right edge of a button that's now this narrow runs
   well past x:0, clipped off the left side of the viewport entirely rather
   than just looking cramped. Opens from the button's left edge instead. */
@media (max-width: 480px) { .sort-menu { right: auto; left: 0; } }
.sort-btn-val { font-size: 13px; font-weight: 600; white-space: nowrap; }
.sort-chev { color: var(--fg-3); transition: transform .18s var(--ease); flex: 0 0 auto; }
.sort-field.open .sort-chev { transform: rotate(180deg); }
.sort-menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 40; width: 220px;
  padding: 6px; border-radius: 16px; background: var(--bg-2); border: 1px solid var(--hairline);
  box-shadow: 0 20px 46px -18px var(--ink-75); }
.sort-menu[hidden] { display: none; }
.sort-opt { display: flex; align-items: center; justify-content: space-between; gap: 14px; width: 100%;
  padding: 11px 13px; border-radius: 10px; background: transparent; border: none; color: var(--fg-1);
  font-family: inherit; font-size: 14px; font-weight: 600; text-align: left; cursor: pointer;
  transition: background .16s var(--ease), color .16s var(--ease); }
.sort-opt:hover { background: rgba(255,255,255,0.06); color: var(--fg); }
.sort-opt.active { background: rgba(255,255,255,0.06); color: #fff; }
.sort-radio { width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--hairline); flex: 0 0 auto; position: relative; }
.sort-opt.active .sort-radio { border-color: var(--accent); }
.sort-opt.active .sort-radio::after { content: ""; position: absolute; inset: 3px; border-radius: 50%; background: var(--accent); }
.shop-resultsbar { display: flex; align-items: center; gap: 12px; min-height: 40px;
  margin-bottom: 16px; font-size: 13px; color: var(--fg-3); }
.filters { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-bottom: 30px; }
.chip { font-family: inherit; font-size: 13px; font-weight: 500; color: var(--fg-2); cursor: pointer;
  padding: 8px 15px; border-radius: 999px; background: var(--bg-3); border: 1px solid var(--hairline);
  transition: color .2s, background .2s, border-color .2s; }
.chip:hover { color: var(--fg); border-color: rgba(255,255,255,0.2); }
.chip.active { color: #fff; background: rgba(255,255,255,0.11); border-color: var(--hairline-strong); font-weight: 600; }

.shop-searchbar { display: flex; align-items: center; gap: 10px; max-width: 580px; margin: 0 auto 28px;
  padding: 13px 17px; border-radius: 16px; background: var(--bg-3); border: 1px solid var(--hairline); color: var(--fg-3); }
.shop-searchbar svg { flex: 0 0 auto; }
.shop-searchbar input { flex: 1; min-width: 0; background: transparent; border: none; outline: none; color: var(--fg);
  font-family: inherit; font-size: 14px; }
.shop-searchbar input::placeholder { color: var(--fg-3); }
.shop-layout { display: grid; grid-template-columns: 280px 1fr; gap: 32px; align-items: start; }
.product-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; align-items: stretch; }
@media (min-width: 1400px) { .product-grid { grid-template-columns: repeat(4, 1fr); } }
/* [hidden]'s default display:none comes from the browser's UA stylesheet,
   which loses to the equal-specificity .product-grid rule above (author
   styles always win a specificity tie) - #grid never actually visually
   hid, it just carried the attribute uselessly while every unsorted
   product sat there fully visible underneath the spinner. */
.product-grid[hidden] { display: none; }
/* Shown while #grid ships [hidden] - the static markup is every product in
   build-time order, unsorted and unpaginated; app.js hides that raw dump
   until it's applied the real sort/filter/pagination pass, since revealing
   it first (then the grid violently reshuffling and shrinking down to 12
   sorted items a moment later) reads as "loaded the wrong catalog". This is
   what fills that gap instead of a jarring blank content area. */
.grid-loading { display: flex; align-items: center; justify-content: center; min-height: 360px; }
.grid-loading[hidden] { display: none; }
.grid-loading-spin { width: 32px; height: 32px; border: 3px solid var(--hairline); border-top-color: var(--accent); border-radius: 50%; animation: authbtnspin 3s linear infinite; }
.product { position: relative; border-radius: 16px; overflow: hidden; border: 1px solid var(--hairline); background: var(--bg-3);
  display: flex; flex-direction: column; transition: transform .3s var(--ease), border-color .3s, box-shadow .3s; cursor: pointer; }
.product:hover { transform: translateY(-2px); border-color: var(--hairline-strong); box-shadow: var(--e-3); }
.p-thumb { height: 168px; position: relative; background-size: cover; background-position: center; }
.p-off { position: absolute; top: 10px; left: 10px; z-index: 1; font-size: 12px; font-weight: 800; letter-spacing: 0.02em;
  color: #fff; padding: 5px 9px; border-radius: 10px; background: var(--accent-deep);
  box-shadow: 0 6px 16px -6px var(--ink-75); }
.p-owned-badge { position: absolute; top: 10px; right: 10px; z-index: 1; font-size: 12px; font-weight: 800; letter-spacing: 0.02em;
  color: #fff; padding: 5px 9px; border-radius: 10px; background: #1f9c53;
  box-shadow: 0 6px 16px -6px var(--ink-75); }
/* Sale-event ribbon: same idiom as .p-off, but the sale is an order-level
   discount applied at checkout (like an automatic coupon), so the card
   keeps its list price and this just flags that the sale covers it. Sits
   top-right unless an owned badge is already there, then it stacks under. */
.p-sale-ribbon { position: absolute; top: 10px; right: 10px; z-index: 1; font-size: 12px; font-weight: 800; letter-spacing: 0.02em;
  color: #fff; padding: 5px 9px; border-radius: 10px; background: var(--accent-deep);
  box-shadow: 0 6px 16px -6px var(--ink-75); }
.product.owned .p-sale-ribbon { top: 44px; }
.pd-sale-event { font-size: 12px; font-weight: 700; color: var(--accent-ink); margin-top: 6px; }
.p-body { padding: 14px 16px 16px; display: flex; flex-direction: column; flex: 1; }
.p-name { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.p-price-row { display: flex; align-items: baseline; gap: 9px; margin-top: 8px; }
.p-price-row .p-price { font-size: 17px; font-weight: 700; color: var(--price); }
.p-price-row .p-price-free { font-size: 17px; font-weight: 700; color: var(--price); }
.p-price-row .p-was { font-size: 13px; font-weight: 600; color: var(--fg-3); text-decoration: line-through; }
/* Ratings are drawn, not typed. The ★ / ☆ text glyphs rendered at whatever
   weight and baseline the font happened to give them, which is the tell. These
   are authored SVG at a fixed size with a real empty state. */
.p-stars { display: flex; align-items: center; gap: 2.5px; margin-top: 9px; line-height: 0; }
.p-stars .st { display: inline-flex; color: rgba(255,255,255,0.16); }
.p-stars .st.on { color: var(--star); }
.p-stars .st svg { display: block; width: 13px; height: 13px; }
.p-rc { margin-left: 7px; font-size: 11px; color: var(--fg-3); line-height: 1;
  font-variant-numeric: tabular-nums; }
.p-sum { font-size: 12px; color: var(--fg-2); line-height: 1.5; margin: 10px 0 14px;
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* Ghost by default, ember on intent. A grid of twelve identical filled-red
   buttons spends the accent on wallpaper; DESIGN.md's One Ember Rule wants red
   to mean "act here", so the card states its action quietly until you reach
   for it. This is also what DESIGN.md already documents for product cards. */
/* Two actions per card: add to cart carries the colour and leads, buy now sits
   beside it as the direct route to checkout. */
.p-actions { margin-top: auto; display: grid; grid-template-columns: 1.25fr 1fr; gap: 8px; }
.p-add, .p-buy { min-height: 40px; width: 100%; font-family: inherit; font-size: 13px; font-weight: 700;
  border-radius: 10px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  transition: background .18s var(--ease-out), border-color .18s var(--ease-out),
              color .18s var(--ease-out), transform .16s var(--ease-out); }

/* One rule across cards, product detail and the quick-view modal: Buy now is
   the filled ember and leads; Add to cart follows as the tinted variant. They
   used to disagree on both emphasis and order depending on the surface. */
.p-buy { color: #fff; background: var(--accent-deep); border: 1px solid rgba(255,255,255,0.16); }
/* The card-level twin of .btn-tinted, and the most-seen Add to cart on the
   site - it repeats once per product across the whole catalog grid. Kept in
   lockstep with .btn-tinted: neutral, so Buy now beside it stays the only
   ember in the pair. A grid of 25 cards each showing two red buttons was the
   loudest version of the problem. */
.p-add { color: var(--fg); background: rgba(255,255,255,0.07); border: 1px solid var(--hairline-strong); }

/* Hover is gated: on touch, tapping fires hover and leaves the control stuck
   in its hovered state after the finger lifts. */
@media (hover: hover) and (pointer: fine) {
  .p-buy:hover { background: var(--accent); border-color: rgba(255,255,255,0.28); }
  .p-add:hover { background: rgba(255,255,255,0.12); border-color: rgba(255,255,255,0.26); color: #fff; }
}

/* Press feedback: the control has to answer the finger before the network does. */
.p-buy:active { background: #cf2f27; transform: scale(0.97); }
.p-add:active { background: rgba(255,255,255,0.09); transform: scale(0.97); }
.p-add:disabled:active, .p-buy:disabled:active { transform: none; }

.p-add:focus-visible, .p-buy:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.p-add:disabled, .p-buy:disabled { background: var(--bg-2); color: var(--fg-3);
  border-color: var(--hairline); cursor: not-allowed; }
/* Owned products collapse to a single full-width state. */
.product.owned .p-actions { grid-template-columns: 1fr; }
.product.owned .p-buy { display: none; }
@media (max-width: 380px) { .p-actions { grid-template-columns: 1fr; } }
.cart-fab-count.bump { animation: cfBump .32s var(--ease); }
@keyframes cfBump { 0% { transform: scale(1); } 40% { transform: scale(1.35); } 100% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .cart-fab-count.bump { animation: none; } }
.shop-empty { text-align: center; color: var(--fg-2); padding: 64px 0; font-size: 16px; }
.shop-pager { display: flex; justify-content: center; align-items: center; gap: 6px; margin-top: 34px; }
.shop-pager button { min-width: 38px; height: 38px; padding: 0 11px; font-family: inherit; font-size: 14px; font-weight: 600;
  color: var(--fg-2); background: var(--bg-3); border: 1px solid var(--hairline); border-radius: 10px; cursor: pointer;
  transition: color .15s, background .15s, border-color .15s; }
.shop-pager button:hover:not(:disabled) { color: var(--fg); border-color: rgba(255,255,255,0.2); }
.shop-pager button.active { color: #fff; background: var(--accent-deep); border-color: transparent; }
.shop-pager button:disabled { opacity: 0.4; cursor: default; }
.shop-pager:empty { display: none; }

/* Sticky alone isn't enough once the panel (all categories/subcategories
   expanded) is taller than the viewport - a sticky element that's taller
   than its scroll range just rides along with the page like a normal one,
   cutting off whatever falls past the bottom edge. Capping it to the
   viewport and giving it its own scrollbar keeps the whole filter list
   reachable without scrolling the product grid out of view. */
.shop-side { position: sticky; top: 96px; order: -1; max-height: calc(100vh - 96px - 32px); overflow-y: auto; }
/* Reads as a tool panel bolted to the catalog, not a floating frosted card.
   The blurred translucent slab, the 24px radius, the inset white top-light and
   the wide-tracked micro-caps were the generic part; structure now comes from
   a flat surface, a real header rule and honest section headings. */
.filter-card { position: relative; border-radius: 16px; padding: 0; overflow: hidden;
  background: var(--bg-2);
  border: 1px solid var(--hairline); box-shadow: var(--e-2); }
.fc-head { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 0; padding: 15px 18px; border-bottom: 1px solid var(--hairline);
  background: rgba(255,255,255,0.028); }
/* No tracking. 0.01em resolved to 0.14px - below the threshold anyone can
   see, so it was noise standing in for a decision, not a decision. */
.fc-head-title { display: flex; align-items: center; gap: 9px; font-size: 14px; font-weight: 700; }
.fc-head svg { color: var(--fg-3); flex: 0 0 auto; }
.fc-clear { font-family: inherit; font-size: 12px; font-weight: 600; color: var(--fg-2); background: none;
  border: 1px solid var(--hairline); border-radius: 10px; cursor: pointer; padding: 5px 10px;
  transition: color .15s var(--ease-out), border-color .15s var(--ease-out),
              background .15s var(--ease-out), transform .16s var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .fc-clear:hover { color: var(--fg); background: rgba(255,255,255,0.07); border-color: var(--hairline-strong); }
}
.fc-clear:active { transform: scale(0.97); }
.fc-clear:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fc-clear[hidden] { display: none; }

/* Sections are separated by a full-bleed rule, so the panel reads as stacked
   rows rather than as free-floating groups inside a card. */
.fc-group { margin: 0; padding: 18px; border-top: 1px solid var(--hairline); }
.fc-group:first-of-type { border-top: none; }
.fc-check-list { display: flex; flex-direction: column; gap: 2px; }
/* The two price conditions now live inside the Price group, under the numeric
   boxes. They were their own section headed "Availability", which was the
   wrong noun - On Sale and Free describe price, not stock. */
.pr-vals + .fc-check-list { margin-top: 14px; }
.fc-check { display: flex; align-items: center; gap: 10px; cursor: pointer; user-select: none;
  font-size: 13px; color: var(--fg-1); position: relative; padding: 8px 8px; margin: 0 -8px;
  border-radius: 10px; transition: background .15s ease; }
@media (hover: hover) and (pointer: fine) {
  .fc-check:hover { background: rgba(255,255,255,0.05); }
}
/* [type="checkbox"] added to win specificity against the later, equal-
   specificity global `input[type="checkbox"]` reset below (same
   0,1,1) - without it, source order (that rule comes later in the
   file) let its 18px visible box leak through on top of this custom
   drawn checkbox, pushing .fc-check-box ~28px right of where every
   other row in this panel starts. */
.fc-check input[type="checkbox"] { position: absolute; opacity: 0; width: 0; height: 0; }
.fc-check-box { width: 16px; height: 16px; border-radius: 4px; border: 1px solid var(--hairline-strong);
  background: var(--ink-25);
  display: flex; align-items: center; justify-content: center; flex: 0 0 auto; position: relative;
  transition: background .15s var(--ease), border-color .15s var(--ease); }
/* A drawn checkmark instead of a coloured rounded square inside a rounded square. */
.fc-check-box::after { content: ""; width: 9px; height: 5px; margin-top: -2px;
  border-left: 2px solid #fff; border-bottom: 2px solid #fff; transform: rotate(-45deg) scale(0.6);
  opacity: 0; transition: opacity .13s var(--ease), transform .13s var(--ease); }
.fc-check input:checked ~ .fc-check-box { border-color: var(--accent-deep); background: var(--accent-deep); }
.fc-check input:checked ~ .fc-check-box::after { opacity: 1; transform: rotate(-45deg) scale(1); }
.fc-check input:focus-visible ~ .fc-check-box { outline: 2px solid var(--accent); outline-offset: 2px; }
.fc-label { display: block; font-size: 12px; font-weight: 600; letter-spacing: 0;
  text-transform: none; color: var(--fg-2); margin-bottom: 12px; }
.fc-search { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-radius: 10px;
  background: rgba(255,255,255,0.04); border: 1px solid var(--hairline); color: var(--fg-3); }
.fc-search input { flex: 1; min-width: 0; background: transparent; border: none; outline: none; color: var(--fg);
  font-family: inherit; font-size: 13px; }
.fc-search input::placeholder { color: var(--fg-3); }
.pr-slider { position: relative; height: 22px; }
.pr-rail { position: absolute; top: 50%; left: 2px; right: 2px; height: 4px; transform: translateY(-50%);
  background: rgba(255,255,255,0.11); border-radius: 999px; }
/* A flat white fill read as washed-out and disconnected from the rest of
   the panel's palette - everything else here (thumbs, checkboxes, focus
   rings) ties back to the accent. A soft ember tint keeps the same "not
   one hard red line" goal from reading grey instead of branded. */
.pr-fill { position: absolute; top: 0; bottom: 0; background: rgba(255,77,68,0.38); border-radius: 999px; }
.pr-slider input[type="range"] { position: absolute; top: 0; left: 0; width: 100%; height: 22px; margin: 0;
  background: transparent; pointer-events: none; -webkit-appearance: none; appearance: none; }
.pr-slider input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; pointer-events: auto;
  width: 14px; height: 14px; border-radius: 50%; background: var(--accent); border: 2px solid #14151a; cursor: pointer;
  box-shadow: 0 1px 3px var(--ink-60); transition: transform .15s var(--ease); }
.pr-slider input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.12); }
.pr-slider input[type="range"]::-moz-range-thumb { pointer-events: auto; width: 17px; height: 17px; border-radius: 50%;
  background: var(--accent); border: 2px solid #14151a; cursor: pointer; }
.pr-slider input[type="range"]::-moz-range-track { background: transparent; }
.pr-vals { display: flex; align-items: center; gap: 10px; margin-top: 15px; }
.pr-box-wrap { flex: 1; display: flex; align-items: center; gap: 4px; padding: 10px 10px; border-radius: 10px;
  background: rgba(255,255,255,0.04); border: 1px solid var(--hairline);
  transition: border-color .18s var(--ease), background .18s var(--ease); }
.pr-box-wrap:focus-within { border-color: var(--hairline-strong); background: rgba(255,255,255,0.07); outline: 2px solid var(--accent); outline-offset: 1px; }
.pr-cur { color: var(--fg-3); font-size: 13px; font-weight: 600; }
.pr-box { flex: 1; width: 100%; min-width: 0; text-align: left; padding: 0; border: none; background: transparent;
  color: var(--fg); font-family: inherit; font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; outline: none; }
.pr-box::-webkit-outer-spin-button, .pr-box::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pr-box { -moz-appearance: textfield; appearance: textfield; }
.pr-sep { color: var(--fg-3); }
/* One gap for every stacked list in this panel. It was 3px here, 2px in the
   checkbox list, 1px between category groups and 1px again inside the
   subcategory list - four different sub-pixel-feeling values in one panel,
   which reads as unconsidered rather than tight. */
.fc-cats { display: flex; flex-direction: column; gap: 2px; }
/* Selection is carried by fill and weight, not by a thick coloured tab down the
   side. The 2px accent border-left is the most recognisable generated-UI tell
   and the same one removed from the legal clauses. */
.fc-cat { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; padding: 11px 13px 11px 12px;
  border: none; background: transparent; color: var(--fg-1); font-family: inherit;
  font-size: 14px; font-weight: 500; line-height: 1.3; text-align: left; cursor: pointer; border-radius: 10px;
  transition: background .18s var(--ease), color .18s var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .fc-cat:hover { background: rgba(255,255,255,0.05); }
}
.fc-cat.active { background: rgba(255,255,255,0.09); color: #fff; font-weight: 600; }
.fc-cat:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.fc-cat svg { flex: 0 0 auto; color: var(--fg-3); }
.fc-cat.active svg { color: var(--fg-2); }

@media (max-width: 1040px) { .shop-layout { grid-template-columns: 1fr; } .shop-side { position: static; max-height: none; } }

/* .shop-side carries order:-1 so the filters sit left of the grid on desktop.
   Once the layout collapses to one column that order put the entire ~1050px
   filter panel above the grid, so a phone user scrolled two full screens of
   filter UI before reaching the first product. Below 1040px the panel becomes
   a sheet the user opens deliberately, and the grid is what loads.
   Scoped to html.js: the sheet needs the trigger app.js injects, so without
   scripting the panel stays in the flow rather than becoming unreachable. */
.shop-filters-btn { display: none; }
/* Both of these are mobile filter-sheet controls that app.js creates
   unconditionally, so both need a base display:none - the sheet's done button
   was missing one. Every rule styling it lived inside the 1040px breakpoint,
   so above that width it rendered as a raw native <button>: system font, white
   fill, square corners, parked at the bottom of the desktop filter panel. */
.shop-filters-done { display: none; }
@media (max-width: 1040px) {
  html.js .shop-filters-btn {
    display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto;
    min-height: 40px; padding: 0 15px; font-family: inherit; font-size: 13px; font-weight: 600;
    color: var(--fg); background: rgba(255,255,255,0.06); border: 1px solid var(--hairline);
    border-radius: 10px; cursor: pointer;
    transition: background .18s ease, border-color .18s ease;
  }
  html.js .shop-filters-btn:hover { border-color: var(--hairline-strong); }
  html.js .shop-filters-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  html.js .shop-filters-btn .shop-filters-n {
    min-width: 18px; height: 18px; padding: 0 5px; border-radius: 10px;
    background: var(--accent-deep); color: #fff; font-size: 11px; font-weight: 700;
    display: inline-flex; align-items: center; justify-content: center;
  }
  html.js .shop-filters-btn .shop-filters-n:empty { display: none; }

  /* Drops down from the top rather than sliding up from the bottom - the
     trigger that opens it (.shop-filters-btn) lives in the results bar at
     the top of the page, so a bottom sheet meant the panel appeared
     nowhere near the control that opened it. */
  html.js .shop-side {
    position: fixed; left: 0; right: 0; top: 0; bottom: auto; z-index: 301;
    max-height: 82vh; overflow-y: auto; -webkit-overflow-scrolling: touch;
    transform: translateY(-101%); transition: transform .34s var(--ease);
    background: var(--bg-2); border-bottom: 1px solid var(--hairline);
    border-radius: 0 0 var(--r) var(--r);
    box-shadow: 0 30px 80px -30px var(--ink-85);
    /* Bottom padding, not just the done button's own margin: a sticky
       child's trailing margin isn't reserved by an overflow:auto ancestor
       unless the ancestor's own box makes room for it, so without this the
       "Show N results" button rendered flush against the scroll edge with
       its bottom margin clipped off. */
    padding: env(safe-area-inset-top) 0 16px;
  }
  html.js .shop-side.open { transform: translateY(0); }
  html.js .shop-side .filter-card {
    background: none; border: none; box-shadow: none; backdrop-filter: none;
    -webkit-backdrop-filter: none; border-radius: 0; padding: 22px 20px 24px;
  }
  /* The sheet itself only covers the bottom 82vh, so whatever the page
     happened to be scrolled to stays visible in the top ~18vh behind the
     scrim. At 0.6 alpha that sliver read as a distracting half-lit "gap"
     rather than a dimmed backdrop, especially over the homepage/shop's
     red banner glow - darkened so the page behind reads as backdrop, not
     as a layout hole. */
  html.js .shop-filters-scrim {
    position: fixed; inset: 0; z-index: 300; background: rgba(6,7,10,0.86);
    backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  }
  html.js .shop-filters-done {
    display: block;
    position: sticky; bottom: 0; width: calc(100% - 40px); margin: 4px 20px 0;
    min-height: 46px; font-family: inherit; font-size: 14px; font-weight: 700;
    color: #fff; background: var(--accent-deep); border: 1px solid transparent;
    border-radius: 10px; cursor: pointer;
  }
  html.js .shop-filters-done:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  body.shop-filters-open { overflow: hidden; }
}
@media (max-width: 1040px) and (prefers-reduced-motion: reduce) {
  html.js .shop-side { transition: none; }
}
@media (max-width: 900px) { .product-grid { grid-template-columns: repeat(2, 1fr); } .nav-mega-inner { width: 380px; } }
@media (max-width: 440px) { .product-grid { grid-template-columns: 1fr; } }

.nav-links a.active { color: var(--fg); }

main { animation: pageIn .32s ease both; }
@keyframes pageIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.page-leaving { animation: none !important; opacity: 0 !important; transform: translateY(-8px);
  transition: opacity .17s ease, transform .17s ease; }
@media (prefers-reduced-motion: reduce) {
  main { animation: none; }
  .page-leaving { transition: none; opacity: 0 !important; transform: none; }
}

.tc { position: fixed; top: 16px; right: 24px; z-index: 250; display: inline-flex; align-items: center;
  padding: 4px; border-radius: 16px; border: 1px solid var(--hairline); background: var(--bg-2);
  box-shadow: 0 14px 34px -18px var(--ink-75); }
html[data-ann="on"] .tc { top: 56px; }
.cart-fab { position: relative; display: inline-flex; align-items: center; min-height: 32px; padding: 0 11px 0 0; margin: 0; border: none;
  background: transparent; box-shadow: none; color: var(--fg); cursor: pointer; font-family: inherit;
  border-radius: 10px; transition: background .2s var(--ease); }
.cart-fab:hover { background: rgba(255,255,255,0.06); }
.cart-fab-sep { flex: 0 0 auto; width: 1px; height: 20px; margin: 0 11px 0 9px; background: var(--hairline); }
.cart-fab-icon { position: relative; display: inline-flex; align-items: center; transition: color .2s; }
.cart-fab:hover .cart-fab-icon { color: var(--accent); }
.cart-fab-icon svg { width: 17px; height: 17px; }
.cart-fab-count { position: absolute; top: -6px; right: -8px; min-width: 15px; height: 15px; padding: 0 4px;
  border-radius: 10px; background: var(--accent); color: #fff; font-size: 11px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center; box-shadow: 0 2px 5px var(--ink-40);
  /* scale(0.5), not scale(0) - nothing in the real world pops from nothing. */
  transform: scale(0.5); opacity: 0; transition: transform .22s var(--ease), opacity .18s var(--ease); }
.cart-fab.has-items .cart-fab-count { transform: scale(1); opacity: 1; }
.cart-fab-total { flex: 0 0 auto; margin-left: 11px; font-size: 13px; font-weight: 700; letter-spacing: -0.01em;
  color: var(--fg-3); white-space: nowrap; transition: color .25s var(--ease); }
.cart-fab.has-items .cart-fab-total { color: var(--price); }
@media (max-width: 520px) { .tc { right: 14px; } }

body.no-scroll { overflow: hidden; }

.cart-overlay, .pm-overlay { position: fixed; inset: 0; z-index: 300; background: rgba(6,7,10,0.6);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); animation: ovIn .2s var(--ease); }
@keyframes ovIn { from { opacity: 0; } to { opacity: 1; } }
.pm-overlay:not([hidden]) { display: flex; align-items: center; justify-content: center; padding: 24px; }

.cart-drawer { position: fixed; top: 0; right: 0; z-index: 301; height: 100%; width: 394px; max-width: 92vw;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--bg-2);
  border-left: 1px solid var(--hairline);
  box-shadow: -30px 0 80px -30px var(--ink-85);
  transform: translateX(101%); transition: transform .38s var(--ease); }
.cart-drawer::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 2px; pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--accent), transparent); opacity: 0.75; }
.cart-drawer.open { transform: none; }

.cart-head { display: flex; align-items: flex-start; justify-content: space-between; padding: 22px 22px 18px; border-bottom: 1px solid var(--hairline); }
.cart-head h3 { font-size: 20px; font-weight: 700; letter-spacing: -0.02em; }
.cart-head-count { display: block; font-size: 12px; color: var(--fg-3); margin-top: 3px; font-weight: 500; }
.cart-x, .pm-x { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px;
  border-radius: 10px; border: 1px solid var(--hairline); background: rgba(255,255,255,0.02); color: var(--fg-2);
  cursor: pointer; transition: color .2s, background .2s, border-color .2s; }
.cart-x:hover, .pm-x:hover { color: var(--fg); background: rgba(255,255,255,0.08); border-color: rgba(255,255,255,0.16); }
/* 44px touch target on the close controls for the cart drawer and every
   modal (payment picker, product quick view) - both are reachable on any
   viewport, not just behind the mobile nav menu. */
@media (max-width: 640px) { .cart-x, .pm-x { width: 44px; height: 44px; } }

.cart-body { flex: 1; overflow-y: auto; padding: 14px 18px; display: flex; flex-direction: column; gap: 10px; }
/* When the cart's empty, #cartItems renders to innerHTML = '' (truly empty,
   not just visually), so .cart-body:empty still claims flex:1 - it collapses
   here instead, letting .cart-empty (its next sibling in this flex column)
   take that space and centre its own text in it, rather than being pushed
   down to sit just above the footer with .cart-body's now-unused flex:1
   space stacked entirely above it. */
.cart-body:empty { flex: none; padding: 0; }
.cart-empty { flex: 1; display: flex; align-items: center; justify-content: center; padding: 22px; text-align: center; color: var(--fg-3); font-size: 14px; }
/* The explicit display:flex above is an author rule, so it overrides the
   UA [hidden]{display:none} - renderCart() sets .hidden on this element
   when the cart has items, and without this it stayed visible, splitting
   the drawer with the real item list. */
.cart-empty[hidden] { display: none; }
.cart-item { display: flex; align-items: center; gap: 13px; padding: 12px; border-radius: 16px;
  background: rgba(255,255,255,0.035); border: 1px solid var(--hairline);
  transition: background .2s, border-color .2s; }
.cart-item:hover { background: rgba(255,255,255,0.06); border-color: rgba(255,255,255,0.14); }
.ci-thumb { flex: 0 0 58px; width: 58px; height: 58px; border-radius: 10px; background: #0f1014 center / cover no-repeat;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); cursor: pointer; transition: transform .2s var(--ease); }
.ci-thumb:hover { transform: scale(1.04); }
.ci-info { flex: 1; min-width: 0; cursor: pointer; }
.ci-info:hover .ci-title { color: var(--accent); }
.ci-title { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ci-price { font-size: 13px; color: var(--price); margin-top: 3px; font-weight: 700; }
.ci-qty { display: inline-flex; align-items: center; gap: 2px; background: var(--ink-25);
  border: 1px solid var(--hairline); border-radius: 999px; padding: 3px; }
.ci-qty button { width: 24px; height: 24px; border-radius: 999px; border: none; background: transparent; color: var(--fg);
  cursor: pointer; font-size: 15px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; transition: background .15s; }
.ci-qty button:hover { background: rgba(255,255,255,0.12); }
.ci-qty span { min-width: 18px; text-align: center; font-size: 13px; font-weight: 600; }
.ci-remove { flex: 0 0 auto; width: 28px; height: 28px; border: none; background: transparent; color: var(--fg-3);
  cursor: pointer; font-size: 20px; line-height: 1; border-radius: 10px; transition: color .2s, background .2s; }
.ci-remove:hover { color: var(--accent); background: rgba(255,77,68,0.12); }

.cart-foot { padding: 18px 22px 22px; border-top: 1px solid var(--hairline); background: var(--ink-25); }
.cart-sub { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 14px; }
.cart-sub span:first-child { font-size: 12px; color: var(--fg-2); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600; }
.cart-sub span:last-child { color: var(--price); font-size: 24px; font-weight: 700; letter-spacing: -0.02em; }
.cart-checkout { width: 100%; justify-content: center; }
.cart-note { font-size: 11px; color: var(--fg-3); text-align: center; margin-top: 11px; }

.pm-licence { display: grid; grid-template-columns: 1fr; gap: 10px; }
.pm-lic { text-align: left; cursor: pointer; padding: 13px 16px; border-radius: 16px;
  border: 1.5px solid var(--hairline); background: rgba(255,255,255,0.03); color: var(--fg-1);
  display: flex; align-items: center; gap: 13px; transition: border-color .16s var(--ease), background .16s var(--ease); }
.pm-lic:hover { border-color: rgba(255,255,255,0.22); }
.pm-lic.active { border-color: var(--accent); background: rgba(255,77,68,0.10); }
.pm-lic-radio { flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%; position: relative;
  border: 2px solid var(--fg-3); transition: border-color .16s var(--ease); }
.pm-lic.active .pm-lic-radio { border-color: var(--accent); }
.pm-lic.active .pm-lic-radio::after { content: ""; position: absolute; inset: 3px; border-radius: 50%; background: var(--accent); }
.pm-lic-text { flex: 1 1 auto; display: flex; flex-direction: column; gap: 3px; }
.pm-lic-name { font-size: 14px; font-weight: 700; letter-spacing: -0.01em; }
.pm-lic-note { font-size: 12px; color: var(--fg-3); line-height: 1.35; }
.pm-lic-price { flex: 0 0 auto; font-size: 15px; font-weight: 800; color: var(--fg); letter-spacing: -0.01em; white-space: nowrap; }
.pm-lic.active .pm-lic-price { color: var(--price); }

.pm-x { position: absolute; top: 14px; right: 14px; z-index: 2; background: rgba(10,11,14,0.55); }
.product { cursor: pointer; }

/* Breadcrumbs -------------------------------------------------------------
   Sitewide trail. The product page keeps its own .pd-crumb because it sits in
   a different container and is built by app.js from catalog data; this is the
   static-page equivalent and matches it visually. */
.crumbs { font-size: 13px; font-weight: 600; margin-bottom: 14px; }
.crumbs ol { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0; padding: 0; }
.crumbs li { display: flex; align-items: center; gap: 8px; }
.crumbs a { color: var(--fg-3); text-decoration: none; }
.crumbs a:hover { color: var(--fg); }
/* The current crumb is the one item you cannot click, so it should not wear
   the "act here" colour. Same rule as .pd-crumb-cur. */
.crumbs [aria-current="page"] { color: var(--fg-2); }
.crumbs li + li::before { content: '\203A'; color: var(--fg-3); }
/* .page-head centres its contents, so the trail has to follow. */
.page-head .crumbs ol { justify-content: center; }
.page-head .crumbs { margin-bottom: 18px; }
/* On the catalog pages the trail sits above the toolbar rather than a title. */
.shop .crumbs { margin-bottom: 16px; }

.product-page { padding-top: 92px; padding-bottom: 84px; }
.pd { max-width: 1200px; margin: 0 auto; padding: 0 24px; }
.pd-notfound { max-width: 460px; margin: 60px auto 90px; text-align: center; }
.pd-notfound h1 { font-size: 26px; font-weight: 800; letter-spacing: -0.02em; margin: 0 0 10px; }
.pd-notfound p { font-size: 15px; color: var(--fg-2); line-height: 1.6; margin: 0 0 22px; }
.pd-crumb { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: 13px; font-weight: 600; margin-bottom: 16px; }
.pd-crumb a { color: var(--fg-3); text-decoration: none; }
.pd-crumb a:hover { color: var(--fg); }
.pd-crumb span { color: var(--fg-3); }
/* The current crumb is the one item in the trail you cannot click, so it is
   the last thing that should be wearing the "act here" colour. */
.pd-crumb .pd-crumb-cur { color: var(--fg-2); }
.pd-head { margin-bottom: 24px; }
.pd-title { font-size: 30px; font-weight: 800; line-height: 1.1; letter-spacing: -0.02em; }
.pd-ver { color: var(--fg-3); font-weight: 700; }
.pd-sub { font-size: 15px; color: var(--fg-2); margin-top: 8px; }
.pd-grid { display: grid; grid-template-columns: minmax(0, 1fr) 364px; gap: 34px; align-items: start; }
.pd-left { min-width: 0; }

.pd-main { position: relative; width: 100%; aspect-ratio: 16 / 8.5; border-radius: 16px; overflow: hidden;
  background: #0f1014; border: 1px solid var(--hairline); }
.pd-main img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pd-sale { position: absolute; left: 16px; top: 16px; z-index: 3; font-size: 12px; font-weight: 800; letter-spacing: 0.04em;
  text-transform: uppercase; color: #fff; background: #1f9c53; padding: 6px 12px; border-radius: 10px; }
/* Prev/next controls for the main product image. Hidden entirely when the
   gallery has one image (app.js). Same drawn-arrow geometry as .lnk-arrow
   elsewhere, just sized up and given a solid neutral disc so it reads over
   any photo. Always visible, not hover-only - the gallery is the one place
   the arrow *is* the interaction, and touch has no hover to reveal it. */
.pd-img-nav { position: absolute; top: 50%; z-index: 3; width: 38px; height: 38px; margin-top: -19px;
  padding: 0; display: flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 1px solid var(--hairline); background: var(--ink-60); cursor: pointer;
  transition: background .15s var(--ease), border-color .15s var(--ease); }
.pd-img-nav::after { content: ""; display: block; width: 20px; height: 20px;
  background-color: #fff; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center; -webkit-mask-size: 20px; mask-size: 20px; }
.pd-img-prev { left: 12px; }
.pd-img-prev::after { -webkit-mask-image: var(--ico-arrow-l); mask-image: var(--ico-arrow-l); }
.pd-img-next { right: 12px; }
.pd-img-next::after { -webkit-mask-image: var(--ico-arrow-r); mask-image: var(--ico-arrow-r); }
@media (hover: hover) and (pointer: fine) {
  .pd-img-nav:hover { background: var(--ink-75); border-color: var(--hairline-strong); }
}
.pd-thumbs { display: flex; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
.pd-thumb { width: 96px; height: 62px; border-radius: 10px; background-size: cover; background-position: center;
  cursor: pointer; border: 2px solid var(--hairline); opacity: 0.9; transition: opacity .15s var(--ease), border-color .15s var(--ease); }
.pd-thumb:hover { opacity: 1; }
.pd-thumb.active { opacity: 1; border-color: var(--accent); }
.pd-video { margin-top: 14px; }
.pd-embed { position: relative; width: 100%; aspect-ratio: 16 / 9; border-radius: 16px; overflow: hidden; border: 1px solid var(--hairline); }
.pd-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.pd-tabs { display: flex; gap: 6px; margin-top: 26px; border-bottom: 1px solid var(--hairline); }
.pd-tab { position: relative; background: none; border: none; cursor: pointer; font-family: inherit;
  font-size: 14px; font-weight: 600; color: var(--fg-3); padding: 11px 14px; }
.pd-tab:hover { color: var(--fg); }
.pd-tab.active { color: var(--fg); }
.pd-tab.active::after { content: ""; position: absolute; left: 10px; right: 10px; bottom: -1px; height: 2px; background: var(--accent); border-radius: 4px; }
.pd-tab-n { color: var(--fg-3); font-weight: 600; }
/* Account settings reuses the same underline tab bar as the product
   page instead of inventing a second component. */
.ac-tabs { margin-top: 22px; }
.ac-tabpanel { margin-top: 20px; }
.ac-tabpanel[hidden] { display: none; }
/* Account settings ran tight - only 4px under each card heading (the
   global .dash-card-head), the Save button hard against the field, and
   the linked-account rows crowding the heading. */
#panel-account .dash-card { padding: 24px 26px; }
#panel-account .dash-card-head { margin-bottom: 16px; }
#panel-account #acctForm { margin-top: 24px; }
#panel-account #acctForm .auth-submit { margin-top: 5px; }
#panel-account .ntf-row:first-of-type { padding-top: 2px; }
#panel-account .ntf-row { padding: 16px 0; }
/* scroll-margin-top keeps the sticky header from covering the panel when
   "Review" deep-links here (app.js goToReviews). Doing it in CSS means the
   scroll code does not have to measure the header. */
.pd-panelbox { padding-top: 22px; scroll-margin-top: 88px; }
.pd-hi { display: flex; gap: 14px; align-items: center; padding: 16px 20px; border-radius: 16px;
  border: 1px solid var(--hairline); background: var(--bg-2); }
.pd-hi-ic { flex: 0 0 auto; width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 10px; background: var(--accent-deep); color: #fff; }
.pd-hi-tx h4 { font-size: 15px; font-weight: 700; margin: 0; }
.pd-hi-tx p { font-size: 13px; color: var(--fg-2); line-height: 1.5; margin: 4px 0 0; }
.pd-about { margin-top: 22px; border: 1px solid var(--hairline); border-radius: var(--r-sm); padding: 20px 22px; }
.pd-about h4 { font-size: 20px; font-weight: 700; margin: 0 0 12px; }
.pd-about p { font-size: 14px; color: var(--fg-2); line-height: 1.68; }

.pd-rev-form { padding: 18px 20px; border: 1px solid var(--hairline); border-radius: var(--r-sm); margin-bottom: 18px; }
.pd-rev-form h4 { margin: 0 0 12px; font-size: 15px; }
.pd-rev-stars-input { display: flex; gap: 4px; margin-bottom: 12px; }
.pd-rev-star-btn { display: inline-flex; background: none; border: none; cursor: pointer; line-height: 0;
  color: rgba(255,255,255,0.2); padding: 3px; border-radius: 4px; transition: color .15s ease; }
.pd-rev-star-btn svg { display: block; width: 22px; height: 22px; }
.pd-rev-star-btn.active { color: var(--star); }
.pd-rev-star-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.pd-rev-form textarea { width: 100%; box-sizing: border-box; background: var(--bg-2); border: 1px solid var(--hairline); border-radius: var(--r-sm); color: var(--fg-1); font: inherit; padding: 10px 12px; resize: vertical; margin-bottom: 12px; }
.pd-rev-form-msg { font-size: 13px; color: var(--fg-3); margin: 10px 0 0; }
.pd-rev { padding: 18px 20px; border: 1px solid var(--hairline); border-radius: var(--r-sm); margin-bottom: 14px; }
.pd-rev:last-child { margin-bottom: 0; }
.pd-rev-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.pd-rev-name { font-size: 14px; font-weight: 700; }
.pd-rev-dot { color: var(--fg-3); }
.pd-rev-stars { display: inline-flex; align-items: center; gap: 2.5px; line-height: 0; }
.pd-rev-stars svg { display: block; width: 13px; height: 13px; }
.pd-star { display: inline-flex; color: rgba(255,255,255,0.16); }
.pd-star.on { color: var(--star); }
.pd-star svg { display: block; width: 13px; height: 13px; }
.pd-rev-meta { font-size: 13px; color: var(--fg-3); }
.pd-rev-body { font-size: 14px; color: var(--fg-1); line-height: 1.6; margin-top: 9px; }
.pd-rev-origin { font-size: 12px; color: var(--fg-3); margin-top: 7px; }
.pd-rev-reply { background: rgba(255,77,68,0.07); border: 1px solid rgba(255,77,68,0.18); border-radius: 10px; padding: 12px 16px; margin-top: 12px; }
.pd-rev-reply-head { font-size: 12px; font-weight: 700; color: var(--accent); text-transform: uppercase; letter-spacing: .02em; }
.pd-rev-reply p { font-size: 13px; color: var(--fg-1); line-height: 1.55; margin: 5px 0 0; }
.pd-empty { font-size: 14px; color: var(--fg-3); }

.pd-upd { padding: 16px 18px; border-radius: 16px; border: 1px solid var(--hairline); background: var(--bg-2); margin-bottom: 12px; }
.pd-upd-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.pd-upd-v { font-size: 16px; font-weight: 700; }
.pd-upd-date { font-size: 13px; color: var(--fg-3); }
.pd-upd-note { font-size: 14px; color: var(--fg-2); line-height: 1.55; margin-top: 8px; }
.pd-upd-dl { margin-top: 12px; }

.pd-right { display: flex; flex-direction: column; gap: 16px; position: sticky; top: 92px; }
.pd-buycard, .pd-block, .pd-refer, .pd-faq-item, .pd-about, .pd-rev {
  background: var(--bg-2);
  box-shadow: 0 1px 0 rgba(255,255,255,0.05) inset, 0 20px 46px -26px var(--ink-75);
}
.pd-buycard { border: 1px solid var(--hairline); border-radius: var(--r-sm); padding: 20px; }
/* Staff-only edit/update strip above the buy card. The two actions are the
   point of it, so they lead and fill the width; the dismiss is a quiet
   affordance tucked at the end. Flat, hairline-bordered, no colour. */
.pd-admin-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 14px;
  padding: 6px; border: 1px solid var(--hairline); border-radius: var(--r-sm);
  background: rgba(255,255,255,0.03); }
/* display:flex above is an author rule and beats the browser's UA
   [hidden]{display:none}, so the attribute alone won't hide the bar. */
.pd-admin-bar[hidden] { display: none; }
.pd-admin-acts { display: flex; align-items: stretch; gap: 6px; flex: 1 1 auto; }
.pd-admin-act { flex: 1 1 0; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 12px; border-radius: 9px; font-size: 13px; font-weight: 600;
  color: var(--fg); text-decoration: none; border: 1px solid var(--hairline);
  background: rgba(255,255,255,0.04); transition: color .15s, background .15s, border-color .15s; }
.pd-admin-act svg { opacity: 0.8; flex: 0 0 auto; }
@media (hover: hover) and (pointer: fine) { .pd-admin-act:hover { background: rgba(255,255,255,0.09); border-color: var(--hairline-strong); } }
.pd-admin-x { flex: 0 0 auto; width: 26px; height: 26px; display: grid; place-items: center; border: none;
  border-radius: 7px; background: none; color: var(--fg-3); cursor: pointer; transition: color .15s, background .15s; }
@media (hover: hover) and (pointer: fine) { .pd-admin-x:hover { color: var(--fg); background: rgba(255,255,255,0.06); } }

/* Admin product-edit floating save - stays reachable while scrolling a long form */
.adm-sticky-save { position: fixed; top: 74px; right: 26px; z-index: 60; min-height: 40px; padding: 9px 20px;
  box-shadow: 0 8px 26px rgba(0,0,0,0.45); }
@media (max-width: 760px) { .adm-sticky-save { top: auto; bottom: 16px; right: 16px; } }
.pd-price-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.pd-price-main { display: flex; align-items: baseline; gap: 10px; }
.pd-price { font-size: 30px; font-weight: 800; color: var(--price); letter-spacing: -0.02em; }
.pd-price-was { font-size: 16px; font-weight: 600; color: var(--fg-3); text-decoration: line-through; }
.pd-price-rbx { font-size: 14px; font-weight: 700; color: var(--fg-2); }
.pd-price-note { font-size: 12px; color: var(--fg-3); margin-top: 6px; }
.pd-cta { display: flex; gap: 10px; margin-top: 15px; }
.pd-cta .btn { flex: 1; justify-content: center; }
.pd-wish { width: 100%; margin-top: 10px; display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: inherit; font-size: 13px; font-weight: 600; color: var(--fg-2); cursor: pointer;
  padding: 10px; border-radius: 10px; border: 1px solid var(--hairline); background: transparent; transition: color .15s var(--ease), border-color .15s var(--ease); }
.pd-wish:hover { color: var(--fg); border-color: rgba(255,255,255,0.22); }
.pd-wish.on { color: var(--accent); border-color: rgba(255,77,68,0.5); }
.pd-owned-badge { display: inline-block; font-size: 13px; font-weight: 700; color: var(--price); margin-bottom: 12px; }
.pd-upgrade { width: 100%; margin-top: 10px; justify-content: center; }
.pd-lic-label { display: block; font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--fg-3); margin: 18px 0 10px; }

.pd-block { border: 1px solid var(--hairline); border-radius: var(--r-sm); padding: 18px 20px; }
.pd-sec-h { font-size: 14px; font-weight: 700; margin-bottom: 12px; }
.pd-feat-list { list-style: none; display: grid; gap: 9px; margin-top: 14px; }
.pd-feat-list li { position: relative; padding-left: 22px; font-size: 13px; color: var(--fg-1); line-height: 1.45; }
/* Neutral marker, not ember. These bullets are decoration - nothing about them
   is actionable - and five of them stacked beside the buy button spent the
   accent on punctuation. DESIGN.md's One Ember Rule keeps red for "act here".
   Smaller too: a 8px dot beside 13px text was reading as a control. */
.pd-feat-list li::before { content: ""; position: absolute; left: 2px; top: 8px; width: 5px; height: 5px; border-radius: 50%; background: var(--fg-3); }
/* margin:0 kills the browser's default <dl> block margin, which was
   leaving ~1em of dead space between the last row and the card's border. */
.pd-tech-list { display: grid; gap: 0; margin: 0; }
.pd-tech-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--hairline); }
.pd-tech-row:last-child { border-bottom: none; padding-bottom: 0; }
.pd-tech-row dt { font-size: 13px; color: var(--fg-2); margin: 0; }
.pd-tech-row dd { font-size: 13px; font-weight: 700; color: var(--fg); margin: 0; }
.pd-refer { border: 1px solid var(--hairline); border-radius: var(--r-sm); padding: 18px 20px; }
.pd-refer-h { font-size: 15px; font-weight: 700; }
.pd-refer-p { font-size: 13px; color: var(--fg-2); line-height: 1.55; margin-top: 7px; }
.pd-refer-p strong { color: var(--warn); font-weight: 700; }
.pd-refer-btns { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.pd-refer-btns .btn { flex: 0 1 auto; justify-content: center; }

.pd-related { margin-top: 60px; }
.pd-related-h { font-size: 24px; font-weight: 800; letter-spacing: -0.01em; margin-bottom: 22px; }
.pd-faq { margin: 60px auto 0; max-width: 880px; }
.pd-faq-h { font-size: 24px; font-weight: 800; letter-spacing: -0.01em; margin-bottom: 20px; }
.pd-faq-item { border: 1px solid var(--hairline); border-radius: var(--r-sm); margin-bottom: 10px; padding: 0 18px; }
.pd-faq-item summary { list-style: none; cursor: pointer; font-size: 14px; font-weight: 600; padding: 15px 0; display: flex; align-items: center; justify-content: space-between; }
.pd-faq-item summary::-webkit-details-marker { display: none; }
.pd-faq-item summary::after { content: ""; width: 15px; height: 15px; flex: 0 0 auto; margin-left: 12px;
  background-color: var(--fg-3); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%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' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E") center / contain no-repeat; }
.pd-faq-item[open] summary::after { -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M5 12h14'/%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' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E") center / contain no-repeat; }
.pd-faq-item p { font-size: 14px; color: var(--fg-2); line-height: 1.65; padding: 0 0 16px; }
/* Links inside an answer body inherited no color rule, so they fell back to
   the browser's default link blue against this page's dark background -
   readable on white, not on near-black. */
.pd-faq-item p a { color: var(--accent); text-decoration: underline; }
.pd-faq-contact { font-size: 13px; color: var(--fg-3); margin-top: 18px; }
.pd-faq-contact a { color: var(--accent); text-decoration: none; }

.pd-related, .pd-faq { content-visibility: auto; contain-intrinsic-size: auto 620px; }
.pd-buycard, .pd-block, .pd-refer, .pd-faq-item, .pd-upd, .pd-hi, .pd-about, .pd-rev { contain: layout paint; }
.pd-right { transform: translateZ(0); }
@media (max-width: 940px) {
  .pd-grid { grid-template-columns: 1fr; gap: 30px; }
  .pd-right { position: static; transform: none; }
  .pd-title { font-size: 24px; }
}

@media (max-width: 640px) {
  .pm-modal { max-width: 94vw; max-height: 92vh; }
  .pm-info { padding: 22px 22px 16px; }
  .pm-licence { grid-template-columns: 1fr; }
  .cart-drawer { width: 100%; }
  /* Every text input/select/textarea site-wide renders under 16px (14.5px
     on most forms) - iOS Safari force-zooms the whole page on focus for
     any field under 16px, and there's no way back out except a manual
     pinch-zoom. One blanket override beats hunting down every 13-14.5px
     rule individually across auth/checkout/dashboard/admin forms. */
  input, select, textarea { font-size: 16px !important; }
}
@media (prefers-reduced-motion: reduce) {
  .cart-drawer { transition: none; }
  .cart-overlay, .pm-overlay, .pm-modal { animation: none; }
}

.about-info { padding: 46px 50px; display: grid; grid-template-columns: 0.82fr 1.18fr; gap: 44px; align-items: start; }
.about-info + .about-info { margin-top: 18px; }
.about-info .about-info-head h2 { font-size: var(--t-h2); margin-top: 16px; letter-spacing: -0.035em; line-height: 1.08; }
.about-info .about-info-body p { color: var(--fg-2); font-size: 16px; line-height: 1.78; margin: 0 0 18px; }
.about-info .about-info-body p:last-child { margin-bottom: 0; }
.about-info .about-info-body strong { color: var(--fg); font-weight: 600; }
.about-info-cta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.about-founder { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
.about-founder-pic { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; background: var(--bg-3); }
.about-info .about-info-body .about-founder p { margin: 0; }

/* The founder story runs 7 paragraphs - the shared .about-info two-column
   split (heading pinned left, text crammed into a ~0.6-width right column)
   turned that into an absurdly tall panel with a wall of empty space next
   to the heading. Stack the heading above a full-width, two-column-text
   body instead - same paragraph count reads in roughly half the height. */
.about-story { grid-template-columns: 1fr; gap: 6px; }
.about-story .about-info-head h2 { margin-top: 0; }
.about-story .about-info-body { column-count: 2; column-gap: 48px; margin-top: 18px; column-rule: 1px solid var(--hairline); }
.about-story .about-info-body p, .about-story .about-founder { break-inside: avoid; }
.about-story .about-info-body p { font-size: 15px; line-height: 1.66; margin: 0 0 15px; }

@media (max-width: 820px) {
  .about-story .about-info-body { column-count: 1; margin-top: 0; }
}
.about-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px; }
.about-tags span { font-size: 13px; font-weight: 500; color: var(--fg-2); padding: 7px 14px; border-radius: 999px;
  background: var(--glass-top); border: 1px solid var(--hairline); }

@media (max-width: 820px) {
  .about-info { grid-template-columns: 1fr; gap: 20px; padding: 32px 26px; }
}

@media (max-width: 560px) {
  .about-founder { gap: 10px; }
  .about-founder-pic { width: 38px; height: 38px; }
}

/* Its own panel, not a stacked-card row - a hairline-ruled grid of cells
   reads as a small data table (real figures) rather than the generated
   landing page's default gradient-card proof block. */
.about-stat-panel { padding: 44px 50px 40px; }
.about-stat-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px;
  background: var(--hairline); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); overflow: hidden;
}
.about-stat { background: var(--bg-2); padding: 26px 22px; display: flex; flex-direction: column; gap: 8px; }
.as-num { font-size: clamp(30px, 3vw, 40px); font-weight: 800; letter-spacing: -0.03em; color: var(--fg); }
.as-label { font-size: 13.5px; color: var(--fg-2); font-weight: 500; }

@media (max-width: 820px) {
  .about-stat-panel { padding: 32px 26px; }
  .about-stat-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
  .about-stat-grid { grid-template-columns: 1fr; }
}

.career-section-label { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap;
  gap: 6px 20px; padding-bottom: 16px; margin-bottom: 28px; border-bottom: 1px solid var(--hairline); }
.career-section-label h2 { font-size: 17px; font-weight: 700; letter-spacing: -0.01em; }
.career-section-label p { color: var(--fg-2); font-size: 13px; }

/* "How we work" and "What we look for" used to be two stacked clones of
   .about-info - identical panel, identical two-column split, twice.
   One panel, two columns, one hairline between them reads as a single
   thought instead of the landing-page habit of restating a section
   shape until the page is full. */
.careers-intro { padding: 46px 50px; display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 0; }
.ci-col { min-width: 0; }
.ci-col-look { padding-left: 52px; border-left: 1px solid var(--hairline); }
.careers-intro h2 { font-size: var(--t-h2); letter-spacing: -0.035em; line-height: 1.08; margin-bottom: 16px; }
.careers-intro p { color: var(--fg-2); font-size: 15.5px; line-height: 1.75; margin: 0 0 16px; }
.careers-intro p:last-of-type { margin-bottom: 0; }
.ci-meta { list-style: none; display: flex; flex-direction: column; gap: 12px; margin: 24px 0 0; padding: 22px 0 0; border-top: 1px solid var(--hairline); }
.ci-meta li { display: flex; align-items: baseline; gap: 12px; font-size: 14px; color: var(--fg-2); }
.ci-meta li b { font-weight: 700; color: var(--fg); min-width: 66px; flex: 0 0 auto; }

@media (max-width: 820px) {
  .careers-intro { grid-template-columns: 1fr; gap: 30px; padding: 32px 26px; }
  .ci-col-look { padding-left: 0; padding-top: 30px; border-left: none; border-top: 1px solid var(--hairline); }
}

/* FAQ as ruled rows instead of an accordion stack - every answer is
   already short, so there's nothing gained by hiding it behind a
   click except one more generic "click to expand" pattern. */
.career-faq { max-width: 820px; margin: 0 auto; border-top: 1px solid var(--hairline); }
.cf-row { display: grid; grid-template-columns: 240px 1fr; gap: 32px; padding: 26px 4px; border-bottom: 1px solid var(--hairline); }
.cf-row h3 { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.cf-row p { color: var(--fg-2); font-size: 14.5px; line-height: 1.7; margin: 0; }

@media (max-width: 640px) {
  .cf-row { grid-template-columns: 1fr; gap: 6px; padding: 20px 4px; }
}

/* A ruled listing, not a row of equal-width cards - reads like a real
   job board (one posting today, room for more later) instead of a
   grid built to hold nine generic tiles. */
.career-list { border-top: 1px solid var(--hairline); }
.career-listing { display: grid; grid-template-columns: 56px 1fr 1.5fr; gap: 28px; align-items: start;
  padding: 30px 4px; border-bottom: 1px solid var(--hairline); }
.cl-num { font-size: 13px; font-weight: 700; color: var(--fg-3); letter-spacing: -0.01em; padding-top: 3px; }
.cl-main h3 { font-size: 19px; font-weight: 800; letter-spacing: -0.02em; }
.cl-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.cl-tags span { font-size: 12px; font-weight: 600; color: var(--fg-2); padding: 5px 10px; border-radius: 999px;
  background: var(--glass-top); border: 1px solid var(--hairline); }
.cl-body p { color: var(--fg-2); font-size: 14.5px; line-height: 1.68; margin: 0 0 16px; max-width: 52ch; }
.career-apply { display: inline-flex; align-items: center; gap: 6px; color: var(--accent-ink); font-size: 13.5px; font-weight: 600; }
.career-apply svg { transition: transform .2s var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .career-apply:hover { color: var(--accent); }
  .career-apply:hover svg { transform: translate(2px, -2px); }
}

@media (max-width: 720px) {
  .career-listing { grid-template-columns: 1fr; gap: 10px; padding: 24px 4px; }
  .cl-num { display: none; }
  .career-section-label { flex-direction: column; align-items: flex-start; }
}

.search-panel { position: fixed; z-index: 150; left: 50%; top: 74px; width: 470px; max-width: calc(100vw - 24px);
  opacity: 0; transform: translateX(-50%) translateY(-6px); pointer-events: none;
  transition: opacity .2s var(--ease), transform .2s var(--ease); }
.search-panel.open { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }
.search-panel-inner { padding: 8px; background: var(--bg-2); max-height: 64vh; overflow-y: auto; }
.search-list { display: flex; flex-direction: column; gap: 2px; }
.sresult { display: flex; align-items: center; gap: 13px; width: 100%; text-align: left; padding: 10px 12px;
  border-radius: 16px; background: transparent; border: none; cursor: pointer; color: var(--fg); font-family: inherit; }
.sresult:hover, .sresult.active { background: rgba(255,255,255,0.07); }
.sresult-thumb { flex: 0 0 46px; width: 46px; height: 46px; border-radius: 10px; background: #0f1014 center / cover no-repeat;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); }
.sresult-info { flex: 1; min-width: 0; }
.sresult-title { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The matched-substring highlight had styling in .sresult-meta but not
   here, so a title's <b> fell back to the browser's own default bold
   weight instead of the site's accent treatment - same content, two
   different unstyled-vs-styled looks depending on which line it landed on. */
.sresult-title b { color: var(--accent); font-weight: 700; }
.sresult-meta { font-size: 12px; color: var(--fg-3); margin-top: 2px; }
.sresult-meta b { color: var(--accent); font-weight: 600; }
.sresult-price { flex: 0 0 auto; font-size: 14px; font-weight: 700; color: var(--price); }
.search-empty { padding: 26px 18px; text-align: center; color: var(--fg-3); font-size: 14px; }
.search-empty b { color: var(--fg-2); font-weight: 600; }
@media (max-width: 520px) { .search-panel { width: calc(100vw - 24px); } }

.search-group { font-size: 11px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--fg-3); padding: 12px 12px 6px; }
.search-group:first-child { padding-top: 6px; }
.sresult-cat { text-decoration: none; }
.sresult-cicon { flex: 0 0 46px; width: 46px; height: 46px; border-radius: 10px; display: grid; place-items: center;
  background: rgba(255,77,68,0.12); color: var(--accent); border: 1px solid rgba(255,77,68,0.25); }
.sresult-go { flex: 0 0 auto; color: var(--fg-3); font-size: 16px; transition: color .2s, transform .2s var(--ease); }
.sresult-cat:hover .sresult-go { color: var(--accent); transform: translateX(2px); }

@keyframes pmIn { from { opacity: 0; transform: translateY(12px) scale(0.98); } to { opacity: 1; transform: none; } }
.pay-overlay { position: fixed; inset: 0; z-index: 320; background: rgba(6,7,10,0.62);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); animation: ovIn .2s var(--ease); }
.pay-overlay:not([hidden]) { display: flex; align-items: center; justify-content: center; padding: 24px; }
.pay-modal { position: relative; z-index: 331; width: 420px; max-width: 100%; padding: 30px; background: var(--bg-2);
  animation: pmIn .3s var(--ease); }
.pay-title { font-size: 20px; font-weight: 700; letter-spacing: -0.02em; }
.pay-sub { color: var(--fg-2); font-size: 14px; margin: 8px 0 22px; line-height: 1.5; }
.pay-sub a { color: var(--accent); text-decoration: none; font-weight: 600; }

/* Required reseller-onboarding popup (success page). No close affordance -
   the buyer has to give the info before they're allowed to sell. */
.reseller-modal { width: 600px; max-width: 96vw; text-align: left; max-height: 90vh; overflow-y: auto; }
.reseller-modal .pay-sub { margin-bottom: 20px; }
.rs-field { margin-bottom: 18px; }
.rs-field > label { display: block; font-size: 13px; font-weight: 700; color: var(--fg-1); margin-bottom: 8px; }
.rs-opt { color: var(--fg-3); font-weight: 400; }
.rs-req-tag { font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--accent);
  background: rgba(255,77,68,0.12); border-radius: var(--r-pill); padding: 2px 7px; margin-left: 4px; vertical-align: 1px; }
.rs-input { width: 100%; min-height: 44px; padding: 0 13px; border-radius: 10px; background: rgba(255,255,255,0.04);
  border: 1px solid var(--hairline); color: var(--fg); font-family: inherit; font-size: 14px; }
.rs-input::placeholder { color: var(--fg-3); }
.rs-input:focus { outline: none; border-color: rgba(255,77,68,0.5); background: rgba(255,255,255,0.06); }
textarea.rs-input, .reseller-modal textarea { width: 100%; min-height: 84px; padding: 11px 13px; border-radius: 12px;
  background: rgba(255,255,255,0.04); border: 1px solid var(--hairline); color: var(--fg); font-family: inherit; font-size: 14px; line-height: 1.5; resize: vertical; }
.reseller-modal textarea::placeholder { color: var(--fg-3); }
.reseller-modal textarea:focus { outline: none; border-color: rgba(255,77,68,0.5); background: rgba(255,255,255,0.06); }
.bt-switch.rs-contact-switch { margin-bottom: 10px; margin-top: 2px; }
.rs-loc-row { display: flex; gap: 8px; margin-bottom: 8px; }
.rs-loc-row .rs-loc-platform { flex: 0 0 44%; }
.rs-loc-row .rs-loc-url { flex: 1; min-width: 0; }
.rs-loc-x { flex: 0 0 auto; width: 34px; border: 1px solid var(--hairline); border-radius: 10px; background: transparent;
  color: var(--fg-3); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
@media (hover: hover) and (pointer: fine) { .rs-loc-x:hover:not(:disabled) { color: var(--fg-1); border-color: var(--hairline-strong); } }
.rs-loc-x:disabled { opacity: 0.35; cursor: default; }
.rs-add-loc { min-height: 34px; padding: 0 14px; font-size: 12.5px; margin-top: 2px; }
.rs-msg { color: var(--accent); font-size: 12.5px; font-weight: 600; margin: 6px 0 0; min-height: 1em; }
.rs-msg:empty { min-height: 0; }
.rs-submit { width: 100%; justify-content: center; margin-top: 14px; }
.rs-required { font-size: 12px; color: var(--fg-3); text-align: center; margin: 12px 0 0; line-height: 1.5; }

.rs-done-links { display: flex; flex-direction: column; gap: 8px; margin: 18px 0 20px; }
.rs-done-link { display: flex; flex-direction: column; gap: 2px; padding: 12px 14px; border-radius: var(--r-sm);
  background: var(--bg-2); border: 1px solid var(--hairline); text-decoration: none; color: var(--fg-1);
  font-size: 13.5px; font-weight: 700; transition: border-color .15s var(--ease); }
@media (hover: hover) and (pointer: fine) { .rs-done-link:hover { border-color: var(--hairline-strong); } }
.rs-done-sub { font-size: 12px; font-weight: 400; color: var(--fg-3); }
@media (max-width: 520px) {
  .reseller-modal { padding: 26px 18px; }
  .rs-loc-row { flex-wrap: wrap; }
  .rs-loc-row .rs-loc-platform, .rs-loc-row .rs-loc-url { flex-basis: 100%; }
  .rs-loc-x { width: 100%; height: 34px; }
}

/* Seller profile tab (account settings) - reuses the .rs-* onboarding form
   and the .dash-row list layout from Purchase History. */
#acSellerForm textarea { width: 100%; min-height: 84px; padding: 11px 13px; border-radius: 12px;
  background: rgba(255,255,255,0.04); border: 1px solid var(--hairline); color: var(--fg);
  font-family: inherit; font-size: 14px; line-height: 1.5; resize: vertical; }
#acSellerForm textarea::placeholder { color: var(--fg-3); }
#acSellerForm textarea:focus { outline: none; border-color: rgba(255,77,68,0.5); background: rgba(255,255,255,0.06); }
.rs-prefill-note { margin: 8px 0 0; font-size: 12px; line-height: 1.5; color: var(--fg-3); }
/* Several admin popouts (campaign detail, payout, order) use this shell
   with an inline max-width and a .dash-stats grid inside - on a phone the
   fixed 420px width overflowed the viewport and the 4-col stat grid was
   unreadable. Fill the (smaller-padded) overlay, scroll if tall, drop the
   stats to two columns. */
@media (max-width: 620px) {
  .pay-overlay:not([hidden]) { padding: 14px; align-items: flex-start; overflow-y: auto; }
  .pay-modal { width: 100%; max-width: 100% !important; padding: 26px 18px;
    max-height: calc(100dvh - 28px); overflow-y: auto; }
  .pay-modal .dash-stats { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .pay-modal .dash-stat { padding: 14px; }
}
/* Campaign send confirmation */
.adm-campaign-confirm-count { font-size: 15px; font-weight: 700; color: var(--fg); margin: 6px 0 0; }
.adm-campaign-confirm-warn { font-size: 12.5px; line-height: 1.5; color: var(--warn); background: var(--warn-tint);
  border: 1px solid var(--warn-line); border-radius: 10px; padding: 10px 12px; margin: 12px 0 0; }
.adm-campaign-confirm-code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 19px;
  letter-spacing: 0.06em; font-weight: 700; color: var(--accent); padding: 10px 14px; background: var(--ink-25);
  border: 1px solid var(--hairline); border-radius: 10px; text-align: center; }
.cur-switch { position: relative; display: inline-flex; padding: 0; gap: 2px;
  border-radius: 10px; background: transparent; box-shadow: none; }

/* Green/white experiment: currency is a money context (like Price
   Green everywhere else - totals, "you saved" figures), so the active
   state reads as --price instead of the system's usual ember. */
.cur-thumb { position: absolute; top: 0; bottom: 0; left: 0; width: 0; border-radius: 10px; z-index: 0;
  background: #1e9d5e; box-shadow: 0 6px 16px -6px var(--ink-75); pointer-events: none; }
.cur-thumb.anim { transition: left .22s var(--ease), width .22s var(--ease); }
.cur-opt { position: relative; z-index: 1; display: inline-flex; align-items: center; gap: 4px; font-family: inherit;
  font-size: 12px; font-weight: 600; color: var(--fg-2); background: transparent; border: none; cursor: pointer;
  padding: 7px 12px; border-radius: 10px; transition: color .2s; white-space: nowrap; }
.cur-opt:hover { color: var(--fg); }
.cur-opt.active { color: #fff; }
.cur-opt svg { opacity: 0.85; transition: transform .25s var(--ease); }
#curFiat[aria-expanded="true"] svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .cur-thumb.anim { transition: none; } }

.cur-menu { position: absolute; right: 0; left: auto; top: calc(100% + 8px); bottom: auto; width: 256px; max-width: calc(100vw - 24px); padding: 8px; z-index: 260;
  border-radius: 16px; border: 1px solid var(--hairline); background: var(--bg-2);
  box-shadow: 0 26px 64px -24px var(--ink-85); }
.cur-search { width: 100%; padding: 9px 12px; border-radius: 10px; border: 1px solid var(--hairline);
  background: rgba(255,255,255,0.05); color: var(--fg); font-family: inherit; font-size: 13px; outline: none; margin-bottom: 6px; }
.cur-search::placeholder { color: var(--fg-3); }
.cur-search:focus { border-color: rgba(255,77,68,0.5); }
.cur-list { max-height: 264px; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; }
.cur-row { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 9px 11px; border-radius: 10px;
  background: transparent; border: none; cursor: pointer; color: var(--fg); font-family: inherit; }
.cur-row:hover { background: rgba(255,255,255,0.06); }
.cur-row.active { background: rgba(30,157,94,0.22); }
/* One ember symbol per row turned the open currency list into a column of
   red. The symbol is a label, not the action - picking the row is. */
.cur-row .cr-sym { flex: 0 0 38px; font-weight: 700; color: var(--fg-2); font-size: 13px; }
.cur-row .cr-code { flex: 0 0 auto; font-weight: 600; font-size: 13px; }
.cur-row .cr-name { flex: 1; min-width: 0; color: var(--fg-3); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: right; }
.cur-empty { padding: 16px; text-align: center; color: var(--fg-3); font-size: 13px; }

.announce { display: none; position: fixed; top: 0; left: 0; right: 0; z-index: 95; height: 40px; align-items: center;
  background: var(--bg-2); border-bottom: 1px solid var(--hairline); }
html[data-ann="on"] .announce { display: flex; }
html[data-ann="on"] { border-top: 40px solid var(--bg); }
html[data-ann="on"] header.nav { top: 52px; }
.announce-inner { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 56px;
  display: flex; align-items: center; justify-content: center; }
.announce-msg { margin: 0; font-size: 13px; color: var(--fg-2); text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.announce-msg strong { color: var(--fg); font-weight: 700; }
.announce-ends { color: var(--fg); font-weight: 700; }
.announce-ends::before { content: "\00b7"; color: var(--fg-3); font-weight: 400; margin: 0 7px; }
.announce-link { font-size: 13px; font-weight: 600; color: var(--accent); text-decoration: none; margin-left: 8px; white-space: nowrap; }
.announce-link:hover { text-decoration: underline; }
.announce-x { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); width: 26px; height: 26px;
  display: grid; place-items: center; border: none; border-radius: 10px; background: transparent; color: var(--fg-3);
  cursor: pointer; transition: color .16s var(--ease), background .16s var(--ease); }
.announce-x:hover { color: var(--fg); background: rgba(255,255,255,0.08); }
@media (max-width: 620px) { .announce-link { display: none; } .announce-msg { font-size: 12px; } .announce-inner { padding: 0 44px; }
  /* 44px touch target (was 26px) - this bar sits at the very top of every page on a phone. */
  .announce-x { width: 44px; height: 44px; right: 2px; }
}

.deal-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.deal-off { position: absolute; top: 12px; left: 12px; font-size: 12px; font-weight: 700; letter-spacing: 0.02em;
  color: #fff; padding: 5px 10px; border-radius: 999px; background: var(--accent-deep);
  box-shadow: 0 6px 16px -8px var(--ink-85); }
.p-prices { display: inline-flex; align-items: baseline; gap: 9px; }
.p-was { font-size: 13px; color: var(--fg-3); text-decoration: line-through; }
.product.deal .p-body { padding: 15px 17px 17px; }
.product.deal h3 { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.product.deal .p-meta { display: flex; align-items: center; justify-content: space-between; margin-top: 14px; }
.product.deal .p-prices .p-price { font-size: 16px; font-weight: 700; color: var(--price); }
.product.deal .p-btn { font-size: 13px; font-weight: 600; color: var(--fg); padding: 7px 14px; border-radius: 999px;
  background: rgba(255,255,255,0.05); border: 1px solid var(--hairline); transition: background .2s, border-color .2s; }
.product.deal:hover .p-btn { background: rgba(255,77,68,0.18); border-color: rgba(255,77,68,0.4); }
@media (max-width: 860px) { .deal-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .deal-grid { grid-template-columns: 1fr; } }

/* auto-fit instead of a fixed 3 columns: with only 3 real free products
   right now, a fixed-width 3-column grid inside a wide, centered container
   left big empty margins on either side rather than actually using the
   space. This stretches the cards themselves to fill it, and still wraps
   cleanly whenever a 4th free product gets added later. */
.free-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px; }
.free-card { overflow: hidden; display: flex; flex-direction: column; border-radius: 16px;
  border: 1px solid var(--hairline); background: var(--bg-3);
  transition: transform .3s var(--ease), border-color .3s, box-shadow .3s; }
.free-card:hover { transform: translateY(-2px); border-color: var(--hairline-strong); box-shadow: var(--e-3); }
.free-thumb { position: relative; height: 150px; background: #0f1014 center / cover no-repeat; }
.free-badge { position: absolute; top: 10px; left: 10px; font-size: 11px; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: #fff; padding: 5px 9px; border-radius: 10px;
  background: var(--accent-deep); box-shadow: 0 6px 16px -8px var(--ink-85); }
.free-body { padding: 14px 16px 16px; display: flex; flex-direction: column; align-items: stretch; flex: 1; }
.free-body h3 { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.free-body p { margin: 8px 0 14px; font-size: 12px; color: var(--fg-2); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.free-btn { margin-top: auto; width: 100%; min-height: 40px; justify-content: center; padding: 10px 16px; font-size: 13px; }
@media (max-width: 700px) { .free-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 440px) { .free-grid { grid-template-columns: 1fr; } }

body.auth-page { min-height: 100vh; min-height: 100dvh; }
.auth-home { position: fixed; top: 22px; left: 26px; z-index: 5; display: inline-flex; }
.auth-home .logo { height: 34px; width: auto; }
/* 100dvh over the static 100vh fallback - iOS Safari's address bar makes
   100vh taller than what's actually visible, so a vh-only center leaves
   this card looking too low until the chrome collapses on scroll. */
.auth-wrap { min-height: 100vh; min-height: 100dvh; display: flex; align-items: center; justify-content: center; padding: 96px 20px 56px; }
.auth-card { width: 100%; max-width: 426px; padding: 38px 36px;
  background: var(--bg-2);
  box-shadow: 0 40px 90px -28px var(--ink-85), 0 0 0 1px var(--glass-top); }
.auth-card h2 { font-size: 24px; font-weight: 700; letter-spacing: -0.03em; }
.auth-sub { color: var(--fg-2); font-size: 14px; line-height: 1.5; margin: 8px 0 26px; }
.auth-social { display: flex; flex-direction: column; gap: 10px; }
.auth-oauth { display: inline-flex; align-items: center; justify-content: center; gap: 10px; width: 100%; min-height: 46px;
  font-family: inherit; font-size: 14px; font-weight: 600; color: var(--fg); cursor: pointer;
  background: rgba(255,255,255,0.04); border: 1px solid var(--hairline); border-radius: 16px;
  transition: background .2s, border-color .2s, transform .15s var(--ease); }
.auth-oauth:hover { background: rgba(255,255,255,0.08); border-color: rgba(255,255,255,0.18); }
.auth-oauth svg { width: 18px; height: 18px; flex: 0 0 auto; }
.auth-divider { display: flex; align-items: center; gap: 14px; margin: 22px 0; color: var(--fg-3); font-size: 12px; white-space: nowrap; }
.auth-divider::before, .auth-divider::after { content: ""; flex: 1; height: 1px; background: var(--hairline); }
.auth-form { display: flex; flex-direction: column; gap: 15px; }
.auth-form[hidden] { display: none !important; }
.auth-field { display: flex; flex-direction: column; gap: 7px; }
.auth-field > label { font-size: 13px; font-weight: 600; color: var(--fg-2); }
.auth-input-wrap { position: relative; }
.auth-field select {
  width: 100%; min-height: 46px; padding: 12px 40px 12px 14px; border-radius: 16px; border: 1px solid var(--hairline);
  background: rgba(255,255,255,0.04) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="%23888" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>') no-repeat right 12px center / 16px 16px;
  color: var(--fg); font-family: inherit; font-size: 16px; outline: none; appearance: none; -webkit-appearance: none;
  transition: border-color .2s, background-color .2s; }
.auth-field select:invalid { color: var(--fg-3); }
.auth-field select:focus { border-color: rgba(255,77,68,0.5); background-color: rgba(255,255,255,0.06); }
.auth-field input[type="email"], .auth-field input[type="password"], .auth-field input[type="text"], .auth-field input[type="number"] {
  width: 100%; min-height: 46px; padding: 12px 14px; border-radius: 16px; border: 1px solid var(--hairline);
  /* 16px, not 14px. Below 16px iOS Safari zooms the viewport the moment the
     field is focused, which on the sign-in page means the layout lurches
     exactly when someone is trying to type their password. DESIGN.md sets this
     floor explicitly and the auth pages were the last place still breaking it. */
  background: rgba(255,255,255,0.04); color: var(--fg); font-family: inherit; font-size: 16px; outline: none;
  transition: border-color .2s, background .2s; }
/* Native step arrows don't take dark-theme styling and read as a stray
   browser control glued onto an otherwise designed field - dropped in
   favour of a plain typed amount, same as every other money field on
   the site (checkout, coupon values, etc.). */
.auth-field input[type="number"] { -moz-appearance: textfield; }
.auth-field input[type="number"]::-webkit-outer-spin-button,
.auth-field input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.auth-field input::placeholder, .auth-field textarea::placeholder { color: var(--fg-3); }
.auth-field input:focus, .auth-field textarea:focus { border-color: rgba(255,77,68,0.5); background: rgba(255,255,255,0.06); }
.auth-field.invalid input { border-color: var(--accent); }
.auth-field textarea { width: 100%; min-height: 90px; padding: 12px 14px; border-radius: 16px; border: 1px solid var(--hairline);
  background: rgba(255,255,255,0.04); color: var(--fg); font-family: inherit; font-size: 16px; outline: none; resize: vertical;
  transition: border-color .2s, background .2s; box-sizing: border-box; }
.auth-pw-toggle { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); background: transparent; border: none;
  color: var(--fg-3); cursor: pointer; padding: 7px; border-radius: 10px; display: inline-flex; }
.auth-pw-toggle:hover { color: var(--fg); }
/* Field-level error: was bare 12px red text with no spacing or affordance
   ("looks ugly / not well placed"). Now a small inline alert - a drawn
   warning glyph, a step up in size/weight, and real breathing room above
   the field it belongs to. Icon is a mask so it inherits the text colour
   (ember for errors, Price Green for the .ok success variant). */
.auth-err { font-size: 12.5px; font-weight: 600; color: var(--accent); line-height: 1.45; }
.auth-err:empty { display: none; }
.auth-err:not(:empty) { display: flex; align-items: flex-start; gap: 6px; margin-top: 8px; }
.auth-err:not(:empty)::before {
  content: ""; flex: 0 0 auto; width: 14px; height: 14px; margin-top: 0.5px; background: currentColor;
  -webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>') center / contain no-repeat;
  mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>') center / contain no-repeat;
}
.auth-err.ok { color: var(--price); }
.auth-err.ok:not(:empty)::before {
  -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>');
  mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>');
}
.auth-row { display: flex; justify-content: flex-end; margin-top: -2px; }
.auth-row a, .auth-alt a { color: var(--accent); text-decoration: none; font-weight: 600; }
.auth-row a:hover, .auth-alt a:hover { text-decoration: underline; }
.auth-check { display: flex; align-items: flex-start; gap: 10px; font-size: 13px; color: var(--fg-2); line-height: 1.5; cursor: pointer; }
.auth-check input { margin: 2px 0 0; width: 16px; height: 16px; accent-color: var(--accent); flex: 0 0 auto; cursor: pointer; }
.auth-check a { color: var(--accent); text-decoration: none; }
.auth-check a:hover { text-decoration: underline; }
.auth-submit { width: 100%; justify-content: center; margin-top: 4px; }
.auth-alt { text-align: center; font-size: 14px; color: var(--fg-2); margin: 22px 0 0; }
.auth-msg { margin-top: 8px; padding: 12px 14px; border-radius: 12px; font-size: 13px; font-weight: 500; line-height: 1.5;
  background: rgba(255,77,68,0.1); border: 1px solid rgba(255,77,68,0.28); color: var(--fg-1); display: none; }
.auth-msg.show { display: flex; align-items: flex-start; gap: 9px; }
.auth-msg a { color: var(--accent); font-weight: 600; }
.auth-msg a:hover { text-decoration: underline; }
.auth-msg::before { content: ""; flex: 0 0 auto; width: 16px; height: 16px; margin-top: 1px; background: var(--accent);
  -webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>') center / contain no-repeat;
  mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>') center / contain no-repeat; }
/* Success variant - a green-tinted panel with a tick, so "Thanks, we got
   your message" no longer renders in an alarm-red box. */
.auth-msg.ok { background: var(--ok-tint); border-color: rgba(107,216,138,0.3); }
.auth-msg.ok::before { background: var(--price);
  -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>');
  mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>'); }
@media (max-width: 480px) { .auth-card { padding: 30px 22px; } }

.nav-account { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px;
  border-radius: 10px; border: 1px solid transparent; background: transparent; color: var(--fg-2); cursor: pointer;
  transition: color .2s, background .2s; }
.nav-account:hover { color: var(--fg); background: rgba(255,255,255,0.06); }
.auth-overlay { position: fixed; inset: 0; z-index: 315; background: rgba(4,5,8,0.8);
  backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); animation: ovIn .2s var(--ease); }
.auth-overlay:not([hidden]) { display: flex; align-items: center; justify-content: center; padding: 24px; overflow-y: auto; }
.auth-modal { width: 100%; max-width: 426px; margin: auto; }
.auth-modal .auth-card { position: relative; width: 100%; max-width: none; animation: pmIn .3s var(--ease); }
.auth-x { position: absolute; top: 14px; right: 14px; z-index: 2; }

/* Site-wide gate overlays (maintenance / banned), injected by site-gate.js
   on every page. Full solid --bg, not a translucent modal over content -
   this replaces the page rather than sitting above it. */
.gate-overlay { position: fixed; inset: 0; z-index: 99999; background: var(--bg); color: var(--fg);
  font-family: Archivo, 'Helvetica Neue', Helvetica, Arial, sans-serif;
  display: flex; align-items: center; justify-content: center; padding: 24px; text-align: center; }
.gate-card { width: 100%; max-width: 420px; padding: 42px 38px; animation: pmIn .3s var(--ease); }
.gate-logo { height: 34px; width: auto; margin: 0 auto 22px; display: block; }
.gate-eyebrow { color: var(--accent-ink); font-size: 12px; font-weight: 700; letter-spacing: 0.02em; margin: 0 0 10px; }
.gate-card h2 { font-size: 24px; font-weight: 700; letter-spacing: -0.03em; }
.gate-msg { color: var(--fg-2); font-size: 14px; line-height: 1.55; margin: 10px 0 0; }
.gate-countdown { color: var(--fg-3); font-size: 13px; font-variant-numeric: tabular-nums; margin: 14px 0 0; }
.gate-lock { position: fixed; bottom: 20px; right: 20px; z-index: 2; }
.gate-signin { position: fixed; bottom: 66px; right: 20px; z-index: 2; width: 240px; padding: 16px; text-align: left; animation: pmIn .2s var(--ease); }
.gate-signin p { font-size: 12px; color: var(--fg-2); line-height: 1.5; margin: 0 0 12px; }
.gate-signin .auth-oauth + .auth-oauth { margin-top: 8px; }
.gate-banner { position: fixed; top: 0; left: 0; right: 0; z-index: 99998; background: var(--accent-deep); color: #fff;
  font-family: Archivo, 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-size: 13px; font-weight: 600; text-align: center; padding: 10px 14px; box-shadow: var(--e-2);
  display: flex; align-items: center; justify-content: center; gap: 14px; flex-wrap: wrap; }
.gate-banner button { flex: 0 0 auto; font-family: inherit; font-size: 12px; font-weight: 700; color: #fff; cursor: pointer;
  background: rgba(255,255,255,0.16); border: 1px solid rgba(255,255,255,0.28); border-radius: 999px; padding: 6px 14px;
  transition: background .18s; }
.gate-banner button:hover { background: rgba(255,255,255,0.26); }
#siteMaintBannerX { background: transparent; border-color: transparent; padding: 4px 8px; font-size: 18px; line-height: 1; }
#siteMaintBannerX:hover { background: rgba(255,255,255,0.16); }
@media (max-width: 480px) { .gate-card { padding: 34px 24px; } }

.dash-page { padding-top: 110px; padding-bottom: 90px; }
.dash { display: grid; grid-template-columns: 244px 1fr; gap: 22px; align-items: start; }
.dash-side { position: sticky; top: 96px; padding: 18px; display: flex; flex-direction: column; gap: 6px; }
.dash-user { display: flex; align-items: center; gap: 12px; padding: 8px 8px 16px; border-bottom: 1px solid var(--hairline); margin-bottom: 8px; }
.dash-av { flex: 0 0 auto; width: 42px; height: 42px; border-radius: 50%; display: grid; place-items: center; font-weight: 700; color: #fff;
  background: linear-gradient(160deg, #ff5247, #e23229); }
.dash-av-lg { width: 84px; height: 84px; font-size: 28px; }
.ac-avatar-row { display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.ac-avatar-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.ac-avatar-actions .btn { min-height: 36px; padding: 8px 16px; font-size: 13px; }
.dash-user-info { min-width: 0; }
.dash-name { font-size: 14px; font-weight: 700; }
.dash-email { font-size: 12px; color: var(--fg-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dash-nav { display: flex; flex-direction: column; gap: 2px; }
.dash-nav a { display: flex; align-items: center; gap: 11px; padding: 10px 12px; border-radius: 10px; text-decoration: none;
  color: var(--fg-2); font-size: 14px; font-weight: 500; transition: color .2s, background .2s; }
.dash-nav a svg { flex: 0 0 auto; opacity: .85; }
.dash-nav a:hover { color: var(--fg); background: rgba(255,255,255,0.05); }
.dash-nav a.active { color: #fff; background: rgba(255,77,68,0.14); }
.dash-nav a.active svg { color: var(--accent); opacity: 1; }
.dash-signout { display: flex; align-items: center; gap: 9px; margin-top: 10px; padding: 10px 12px; border-radius: 10px;
  background: transparent; border: 1px solid var(--hairline); color: var(--fg-2); font-family: inherit; font-size: 14px; font-weight: 600; cursor: pointer; transition: color .2s, background .2s, border-color .2s; }
.dash-signout:hover { color: var(--fg); background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.18); }
/* Quiet link under the dashboard nav: the customer-facing "Admin Panel"
   shortcut, and (reused) the admin panel's own "Account Settings" back-link.
   Styled like a nav row, not a call-to-action. */
.dash-admin-link { display: flex; align-items: center; gap: 11px; margin-top: 12px; padding: 10px 12px; border-radius: 10px;
  border: 1px solid var(--hairline); color: var(--fg-2); text-decoration: none;
  font-size: 14px; font-weight: 500; transition: color .2s, background .2s, border-color .2s; }
.dash-admin-link svg { flex: 0 0 auto; opacity: .85; }
.dash-admin-link:hover { color: var(--fg); background: rgba(255,255,255,0.05); border-color: var(--hairline-strong); }
.dash-admin-link[hidden] { display: none; }
/* The customer-facing shortcut into /admin gets a faint panel fill to set
   it apart from the nav rows above - matching the account-menu entry. */
#dashAdminLink { background: rgba(255,255,255,0.03); }
#dashAdminLink:hover { background: rgba(255,255,255,0.06); }

.dash-main { min-width: 0; }
.dash-claim-banner {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  margin-bottom: 20px; padding: 14px 18px; border-radius: 14px;
  border: 1px solid var(--accent);
  background: var(--bg-2);
}
/* display:flex above is an author rule and beats the UA [hidden] default,
   so the dismiss button (which sets .hidden) did nothing. */
.dash-claim-banner[hidden] { display: none; }
.dash-claim-banner .dcb-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.dash-claim-banner .dcb-text strong { font-size: 14px; }
.dash-claim-banner .dcb-text span { font-size: 12.5px; color: var(--fg-2); }
.dash-claim-banner .dcb-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.dash-claim-banner .dcb-x {
  width: 28px; height: 28px; border: 0; border-radius: 8px; background: transparent;
  color: var(--fg-2); font-size: 20px; line-height: 1; cursor: pointer;
}
.dash-claim-banner .dcb-x:hover { background: var(--hairline); color: var(--fg-1); }
#claimMsg.ok { color: var(--price); }
.dash-head { margin-bottom: 22px; }
.dash-head > .dt-link { display: inline-block; margin-bottom: 10px; font-size: 13px; }
.dash-head h1 { font-size: clamp(24px, 3vw, 30px); letter-spacing: -0.03em; }
.dash-head p { color: var(--fg-2); font-size: 15px; margin-top: 6px; }
.dash-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 18px; }
.dash-stat { padding: 20px; display: flex; flex-direction: column; gap: 8px; }
.ds-label { font-size: 12px; color: var(--fg-3); font-weight: 600; }
.ds-num { font-size: 24px; font-weight: 800; letter-spacing: -0.03em; display: inline-block; }
.ds-num .p-price { font-size: 24px; }
/* Plays once on a stat tile whose number actually changed since the
   last auto-refresh tick (see statTile's opts.flash) - a live-updating
   number should read as alive, not just silently different next time
   you look at it. */
.ds-num-flash { animation: dsNumFlash .6s var(--ease-out); }
@keyframes dsNumFlash { 0% { color: var(--price); transform: scale(1.1); } 100% { color: inherit; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .ds-num-flash { animation: none; } }
.ds-sub { font-size: 12px; color: var(--fg-3); }
.ds-delta { font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; gap: 3px; width: fit-content; }
.ds-delta.up { color: var(--price); }
.ds-delta.down { color: var(--accent); }
.ds-delta.flat { color: var(--fg-3); }
.adm-todo-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--hairline); }
.adm-todo-row:last-child { border-bottom: none; }
.adm-todo-text { font-size: 14px; color: var(--fg-1); }
.adm-todo-badge { flex-shrink: 0; }
.dash-card { padding: 22px 24px; margin-bottom: 16px; }
/* Purchase history / wishlist read as plain content panels, not floating
   cards - the .glass shadow every other dash-card keeps made an empty-state
   box look like it was popping off the page for no reason. */
.dash-card.flat { box-shadow: none; }
.dash-card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.dash-card-head h2 { font-size: 17px; font-weight: 700; }
.dash-card-head a { color: var(--accent); text-decoration: none; font-size: 13px; font-weight: 600; }
.dash-card-head a:hover { text-decoration: underline; }
.dash-rows { display: flex; flex-direction: column; }
.dash-row { display: flex; align-items: center; gap: 14px; padding: 14px 0; border-top: 1px solid var(--hairline); }
.dash-row:first-child { border-top: 0; }
.dr-thumb { flex: 0 0 46px; width: 46px; height: 46px; border-radius: 10px; background: #0f1014 center / cover no-repeat; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); }
.dr-main { flex: 1; min-width: 0; }
.dr-title { font-size: 14px; font-weight: 600; }
.dr-sub { font-size: 12px; color: var(--fg-3); margin-top: 3px; }
.dr-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dr-cart, .dr-btn { min-height: 36px; padding: 8px 14px; font-size: 13px; }
.dr-actions .p-buy, .dr-actions .p-add { width: auto; min-height: 36px; padding: 8px 14px; font-size: 13px; }

/* Thumb + title + up to 3 action buttons (View/Download/Review) all fighting
   for one row was fine on desktop's wide .dash-main, but on a phone's
   ~300px content width it squeezed everything into wrapped, uneven
   fragments. Wraps the actions onto their own full-width row instead. */
@media (max-width: 640px) {
  .dash-row { flex-wrap: wrap; }
  .dr-main { flex: 1 1 auto; }
  .dr-actions { flex: 1 1 100%; margin-top: 10px; }
  .dr-actions .dr-cart, .dr-actions .dr-btn { flex: 1 1 0; }
}
.wl-remove { width: 30px; height: 30px; border: none; background: transparent; color: var(--fg-3); font-size: 20px; line-height: 1; border-radius: 10px; cursor: pointer; }
.wl-remove:hover { color: var(--accent); background: rgba(255,77,68,0.1); }
.dr-title-link { display: block; font-size: 14px; font-weight: 600; color: inherit; text-decoration: none; }
.dr-title-link:hover { text-decoration: underline; color: var(--fg); }

/* Reuses .shop-searchbar's exact look (rounded pill, icon + input) for the
   Licenses/Purchase history search fields - same control, different page. */
.dash-searchbar { display: flex; align-items: center; gap: 10px; max-width: 420px; margin: 0 0 16px;
  padding: 12px 16px; border-radius: 14px; background: var(--bg-3); border: 1px solid var(--hairline); color: var(--fg-3); }
.dash-searchbar svg { flex: 0 0 auto; }
.dash-searchbar input { flex: 1; min-width: 0; background: transparent; border: none; outline: none; color: var(--fg); font-family: inherit; font-size: 14px; }
.dash-searchbar input::placeholder { color: var(--fg-3); }

/* Even grid rather than a wrap-flex of mismatched fixed widths - the old
   140px date/number inputs next to an auto-width select read as a phone
   layout squeezed onto desktop. */
.dash-filter-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  align-items: end; gap: 14px; margin: 0 0 18px; }
.dash-filter-field { display: flex; flex-direction: column; gap: 6px; font-size: 12px; color: var(--fg-3); font-weight: 600; min-width: 0; }
.dash-filter-field input, .dash-filter-field select { width: 100%; min-height: 42px; padding: 0 12px; border-radius: 10px;
  background: var(--bg-3); border: 1px solid var(--hairline); color: var(--fg); font-family: inherit; font-size: 13px; }
/* Without this, Chromium assumes the native <select> chrome is a light
   control and its dropdown-arrow hover state renders bright white against
   this dark field - color-scheme tells it to render that native chrome
   (arrow, hover tint) in dark instead. Only matters before __coldSelect
   enhances it (no-JS / first paint). */
.dash-filter-field select { color-scheme: dark; }
.dash-filter-field .csel, .dash-filter-field .cdate { width: 100%; }
.dash-filter-clear { align-self: end; min-height: 42px; }
@media (max-width: 560px) { .dash-filter-clear { grid-column: 1 / -1; } }

/* Custom <select> (window.__coldSelect) - button + menu over a hidden
   native element. Neutral-fill selection + drawn tick, same as the shop
   sort menu. */
.csel-native { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.csel { position: relative; }
.csel-btn { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
  min-height: 42px; padding: 0 13px; border-radius: 10px; background: var(--bg-3); border: 1px solid var(--hairline);
  color: var(--fg); font-family: inherit; font-size: 13px; font-weight: 600; text-align: left; cursor: pointer;
  transition: border-color .16s var(--ease); }
.csel-btn.csel-placeholder { color: var(--fg-3); font-weight: 500; }
@media (hover: hover) and (pointer: fine) { .csel-btn:hover { border-color: var(--hairline-strong); } }
.csel-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.csel-btn:active { transform: scale(0.99); }
.csel-val { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.csel-chev { flex: 0 0 auto; color: var(--fg-3); transition: transform .18s var(--ease); }
.csel.open .csel-chev { transform: rotate(180deg); }
.csel-menu { position: absolute; top: calc(100% + 6px); left: 0; min-width: 100%; width: max-content; max-width: 280px;
  z-index: 60; padding: 6px; border-radius: 12px; background: var(--bg-2); border: 1px solid var(--hairline);
  box-shadow: 0 20px 46px -18px var(--ink-75); animation: cselIn .14s var(--ease-out); }
.csel-menu[hidden] { display: none; }
@keyframes cselIn { from { opacity: 0; transform: translateY(-4px); } }
.csel-opt { display: flex; align-items: center; justify-content: space-between; gap: 16px; width: 100%;
  padding: 10px 12px; border-radius: 8px; background: transparent; border: none; color: var(--fg-1);
  font-family: inherit; font-size: 13.5px; font-weight: 600; text-align: left; white-space: nowrap; cursor: pointer;
  transition: background .14s var(--ease); }
@media (hover: hover) and (pointer: fine) { .csel-opt:hover { background: rgba(255,255,255,0.06); color: #fff; } }
.csel-opt.active { background: rgba(255,255,255,0.06); color: #fff; }
.csel-tick { flex: 0 0 auto; color: var(--accent); opacity: 0; }
.csel-opt.active .csel-tick { opacity: 1; }
/* In auth-style forms (contact page) the custom select matches the taller,
   softer, 16px input treatment those fields use. */
.auth-field .csel-btn { min-height: 46px; padding: 0 15px; border-radius: 16px; font-size: 16px;
  background: rgba(255,255,255,0.04); }
.auth-field .csel-btn.csel-placeholder { font-weight: 400; }
.auth-field .csel.open .csel-btn, .auth-field .csel-btn:focus-visible { border-color: rgba(255,77,68,0.5); }
@media (prefers-reduced-motion: reduce) { .csel-menu { animation: none; } .csel-chev, .csel-btn:active { transition: none; transform: none; } }

/* Custom date field (window.__coldDate) - same button-over-hidden-native
   idiom as .csel above, so the "From"/"To" purchase-history filters stop
   falling back to the browser's own unstyleable date picker. */
.cdate { position: relative; }
.cdate-btn { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
  min-height: 42px; padding: 0 13px; border-radius: 10px; background: var(--bg-3); border: 1px solid var(--hairline);
  color: var(--fg); font-family: inherit; font-size: 13px; font-weight: 600; text-align: left; cursor: pointer;
  transition: border-color .16s var(--ease); }
.cdate-btn.cdate-placeholder { color: var(--fg-3); font-weight: 500; }
@media (hover: hover) and (pointer: fine) { .cdate-btn:hover { border-color: var(--hairline-strong); } }
.cdate-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cdate-btn:active { transform: scale(0.99); }
.cdate-val { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cdate-ic { flex: 0 0 auto; color: var(--fg-3); }
.cdate-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 60; width: 264px;
  padding: 12px; border-radius: 12px; background: var(--bg-2); border: 1px solid var(--hairline);
  box-shadow: 0 20px 46px -18px var(--ink-75); animation: cselIn .14s var(--ease-out); }
.cdate-menu[hidden] { display: none; }
.cdate-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.cdate-head b { font-size: 13px; font-weight: 700; }
.cdate-nav { width: 28px; height: 28px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--fg-2); border-radius: 8px; cursor: pointer;
  transition: background .15s var(--ease), color .15s var(--ease); }
@media (hover: hover) and (pointer: fine) { .cdate-nav:hover { background: rgba(255,255,255,0.06); color: var(--fg); } }
.cdate-dow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; margin-bottom: 2px; }
.cdate-dow span { display: block; font-size: 10.5px; font-weight: 700; color: var(--fg-3); text-align: center; text-transform: uppercase; }
.cdate-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cdate-day { aspect-ratio: 1; border: none; background: transparent; color: var(--fg-1); font-family: inherit;
  font-size: 12.5px; font-weight: 600; border-radius: 8px; cursor: pointer;
  transition: background .14s var(--ease), color .14s var(--ease); }
@media (hover: hover) and (pointer: fine) { .cdate-day:not(:disabled):hover { background: rgba(255,255,255,0.06); color: #fff; } }
.cdate-day.out { color: var(--fg-3); opacity: .45; }
.cdate-day.today { box-shadow: inset 0 0 0 1px var(--hairline-strong); }
.cdate-day.active { background: var(--accent-deep); color: #fff; }
.cdate-foot { display: flex; justify-content: flex-end; margin-top: 6px; padding-top: 8px; border-top: 1px solid var(--hairline); }
.cdate-clear { font-family: inherit; font-size: 12px; font-weight: 600; color: var(--fg-3); background: transparent;
  border: none; cursor: pointer; padding: 4px 6px; border-radius: 6px; transition: color .15s var(--ease); }
.cdate-clear:hover { color: var(--accent); }
@media (prefers-reduced-motion: reduce) { .cdate-menu { animation: none; } }

/* __coldMenuFit flips a menu above its trigger when there's no room below.
   The open animations all slide down from above, so drop them once flipped
   rather than have the menu slide the wrong way into place. */
.menu-flipped { animation: none !important; }

/* Collapsible dash-card sections (AdBlox top-servers / activity log) -
   native <details>/<summary> so open/closed state needs no JS, capped at a
   fixed height with internal scroll even when open so a long table never
   pushes the rest of the page down. */
.adm-collapse > summary { cursor: pointer; list-style: none; margin-bottom: 0; }
.adm-collapse > summary::-webkit-details-marker { display: none; }
.adm-collapse-chevron { flex-shrink: 0; color: var(--fg-3); transition: transform 0.2s ease; }
.adm-collapse[open] > summary .adm-collapse-chevron { transform: rotate(180deg); }
.adm-collapse-body { max-height: 280px; overflow-y: auto; margin-top: 14px; }

.dash-tablewrap { overflow-x: auto; }
.dash-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.dash-table th { text-align: left; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--fg-3); font-weight: 600; padding: 0 14px 12px 0; }
.dash-table td { padding: 13px 14px 13px 0; border-top: 1px solid var(--hairline); color: var(--fg); }
.dash-table td:first-child { color: var(--fg-2); white-space: nowrap; }
.dash-table tbody tr { animation: rowIn .22s var(--ease) both; }
@keyframes rowIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .dash-table tbody tr { animation: none; } }
.dt-mono { font-variant-numeric: tabular-nums; color: var(--fg-3); }
.dt-link { color: var(--accent); text-decoration: none; font-weight: 600; }
.dt-link:hover { text-decoration: underline; }
.dt-badge { display: inline-block; font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 999px; white-space: nowrap; }
/* Author display above beats the browser's [hidden]{display:none}; some
   badges are persistent elements toggled via the hidden attribute. */
.dt-badge[hidden] { display: none; }
.dt-badge.ok { color: var(--price); background: rgba(52,224,138,0.12); border: 1px solid rgba(52,224,138,0.28); }
.dt-badge.warn { color: var(--warn); background: var(--warn-tint); border: 1px solid rgba(246,196,84,0.28); }
.dt-badge.err { color: var(--accent); background: rgba(255,77,68,0.12); border: 1px solid rgba(255,77,68,0.28); }
.dt-badge.info { color: #5b9bff; background: rgba(91,155,255,0.12); border: 1px solid rgba(91,155,255,0.28); }
.dt-badge.mute { color: var(--fg-3); background: rgba(255,255,255,0.05); border: 1px solid var(--hairline); }
html[data-theme="light"] .dt-badge.mute { background: rgba(0,0,0,0.04); }
button.dt-badge { font-family: inherit; cursor: pointer; transition: filter .15s var(--ease); }
button.dt-badge:hover:not(:disabled) { filter: brightness(1.2); }
button.dt-badge:disabled { cursor: default; opacity: 0.6; }

.dash-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 18px; }
/* .product-grid's fixed 3-up columns assume the shop page's full-width
   layout - crammed into .dash-main's much narrower column, 3 equal
   columns squeezed every card down to ~200px, which broke the fixed
   168px-tall thumbnail's proportions and wrapped "Add to cart" onto two
   lines. Reflows by actual available width instead of a fixed count. */
.dash-rec-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); margin-top: 12px; }
.dash-empty-note { color: var(--fg-2); font-size: 14px; grid-column: 1 / -1; }
.dash-empty-cta { grid-column: 1 / -1; display: flex; flex-direction: column; align-items: flex-start; gap: 12px;
  padding: 26px 24px; border-radius: var(--r-sm); border: 1px solid var(--hairline); background: var(--bg-2); }
.dash-empty-cta p { color: var(--fg-2); font-size: 14px; margin: 0; }
/* Same solid surface as .dash-card/.dash-stat, not the shop's flat --bg-3 -
   these sit inside the dashboard alongside those panels and need to read as
   the same material, not a product-grid card that wandered in. */
.dash-prod { display: flex; flex-direction: column; padding: 0; overflow: hidden; border-radius: var(--r-sm);
  border: 1px solid var(--hairline); background: var(--bg-2);
  transition: transform .22s var(--ease), border-color .22s, box-shadow .22s; }
@media (hover: hover) and (pointer: fine) {
  .dash-prod:hover { transform: translateY(-2px); border-color: var(--hairline-strong); box-shadow: var(--e-3); }
}
.dp-thumb { width: 100%; aspect-ratio: 16 / 9; flex: none; position: relative; background: #0f1014 center / cover no-repeat; }
/* Standard licence is the default, expected state - it gets no badge at
   all. Resell is the exception worth flagging, so it earns the one
   corner-ribbon treatment, matching .p-off/.p-owned-badge's exact idiom
   (solid chip, own drop shadow, top corner) rather than inventing a new
   inline-pill vocabulary that has to fight the button below it for
   attention. A generic rounded tag under every title was the tell. */
.dp-lic-badge { position: absolute; top: 10px; right: 10px; z-index: 1; display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; font-weight: 800; letter-spacing: 0.02em; color: #fff; padding: 5px 9px; border-radius: 10px;
  background: #3568c4; box-shadow: 0 6px 16px -6px var(--ink-75); }
.dp-lic-badge svg { width: 12px; height: 12px; flex: 0 0 auto; }
.dp-body { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 12px; padding: 16px; }
.dp-name { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.35; }
.dp-price { font-size: 16px; font-weight: 700; color: var(--price); margin-top: -6px; }
.dp-was { font-size: 13px; font-weight: 600; color: var(--fg-3); text-decoration: line-through; margin-right: 6px; }
/* Wishlist bundle-deal state (?bundle= token from a wishlist-reminder or
   post-purchase-upsell email, read via get-bundle-deal) - a discounted
   card gets the same corner-ribbon idiom the resell badge above uses,
   plus a strikethrough original price, and the panel gets one summary
   banner instead of repeating the offer terms on every card. */
.wish-deal-banner { grid-column: 1 / -1; padding: 13px 16px; border-radius: var(--r-ctl); margin-bottom: 4px;
  background: rgba(52,224,138,0.08); border: 1px solid rgba(52,224,138,0.22); font-size: 13px; color: var(--fg-2); line-height: 1.5; }
.wish-deal-banner strong { color: var(--price); }
.dp-in-deal { border-color: rgba(52,224,138,0.35); }
.dp-deal-badge { position: absolute; top: 10px; right: 10px; z-index: 1; font-size: 12px; font-weight: 800; letter-spacing: 0.02em;
  color: #06150c; padding: 5px 10px; border-radius: 10px; background: var(--price); box-shadow: 0 6px 16px -6px var(--ink-75); }
.dp-actions { margin-top: auto; width: 100%; display: flex; gap: 8px; }
.dp-btn, .dp-review-btn { flex: 1; min-width: 0; min-height: 40px; padding: 9px 12px; font-size: 13px;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px; }
.dp-btn svg { flex: 0 0 auto; }
/* Wishlist cards reuse the shop grid's Buy now / Add to cart pairing
   (.p-buy accent, .p-add neutral) rather than inventing a third button
   style - same actions, same visual weight, just laid out in the
   two-across .dp-actions row instead of full-width stacked. */
.dp-actions .p-buy, .dp-actions .p-add { flex: 1; min-width: 0; width: auto; min-height: 40px; }
.dp-remove { position: absolute; top: 10px; right: 10px; z-index: 1; width: 30px; height: 30px; border: none;
  border-radius: 10px; background: rgba(15,16,20,0.7); color: #fff; font-size: 18px; line-height: 1; cursor: pointer;
  box-shadow: 0 6px 16px -6px var(--ink-75); }
.dp-remove:hover { color: var(--accent); background: rgba(15,16,20,0.9); }

.dl-item { padding: 18px 22px; }
.dl-top { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.dl-thumb { flex: 0 0 52px; width: 52px; height: 52px; border-radius: 10px; background: #0f1014 center / cover no-repeat;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); }
.dl-info { flex: 1 1 auto; min-width: 0; }
.dl-name { font-size: 16px; font-weight: 700; }
.dl-meta { font-size: 13px; color: var(--fg-3); margin-top: 4px; }
.dl-meta strong { color: var(--fg-2); }
.dl-expired { margin: 12px 0 0; font-size: 13px; line-height: 1.5; color: var(--warn); }
.dl-expired a { color: var(--accent); font-weight: 600; text-decoration: none; }
.ty-guest-note { margin: 14px 0 0; padding: 12px 14px; font-size: 13px; line-height: 1.55; color: var(--fg-2);
  background: var(--bg-2); border: 1px solid var(--hairline); border-radius: var(--r-sm); }
.ty-guest-note a { color: var(--accent); font-weight: 600; text-decoration: none; }
.dl-get { min-height: 40px; padding: 9px 18px; font-size: 13px; }
.dl-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dl-review { min-height: 40px; padding: 9px 16px; font-size: 13px; white-space: nowrap; }
.dl-history { margin-top: 14px; border-top: 1px solid var(--hairline); padding-top: 12px; }
.dl-history summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--fg-2); list-style: none; }
.dl-history summary::-webkit-details-marker { display: none; }
.dl-history summary::before { content: ""; display: inline-block; vertical-align: -1px; width: 12px; height: 12px; margin-right: 5px;
  background-color: var(--fg-3); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%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' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: transform .18s var(--ease-out); }
.dl-history[open] summary::before { transform: rotate(90deg); }
.dl-ver { display: grid; grid-template-columns: 56px 110px 1fr; gap: 10px; align-items: baseline; padding: 9px 0; font-size: 13px; border-top: 1px solid var(--hairline); }
.dl-ver:first-of-type { border-top: 0; margin-top: 6px; }
.dl-v { font-weight: 700; color: var(--fg); }
.dl-ver.cur .dl-v { color: var(--price); }
.dl-d { color: var(--fg-3); }
.dl-note { color: var(--fg-2); }

.ref-prod-copy { padding: 7px 13px; min-height: 0; font-size: 12px; }

/* Payout method - the one plain browser <select> left on a customer-facing
   page, everywhere else on the site already uses a drawn dropdown. Native
   select stays in the DOM and keeps its value in sync (the payout submit
   handler reads it, unmodified) - only its own visibility is scoped to
   html.js, so a page with scripting blocked still gets a real, working
   native select instead of a dead custom button. */
.ref-method-dd { position: relative; }
html.js .ref-method-native { position: absolute; opacity: 0; pointer-events: none; width: 1px; height: 1px; }
.ref-method-btn { display: none; }
html.js .ref-method-btn { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
  min-height: 46px; padding: 12px 14px; border-radius: var(--r-sm); background: var(--bg-3);
  border: 1px solid var(--hairline); color: var(--fg); font-family: inherit; font-size: 14px; cursor: pointer;
  transition: border-color .18s, background .18s; }
html.js .ref-method-btn:hover { border-color: var(--hairline-strong); }
html.js .ref-method-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ref-method-chev { color: var(--fg-3); transition: transform .18s var(--ease); flex: 0 0 auto; }
.ref-method-dd.open .ref-method-chev { transform: rotate(180deg); }
.ref-method-menu { position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 40; padding: 6px;
  border-radius: var(--r-sm); background: var(--bg-2); border: 1px solid var(--hairline);
  box-shadow: 0 20px 46px -18px var(--ink-75); }
.ref-method-menu[hidden] { display: none; }
.ref-method-opt { display: flex; align-items: center; justify-content: space-between; gap: 14px; width: 100%;
  padding: 10px 12px; border-radius: var(--r-ctl); background: transparent; border: none; color: var(--fg-1);
  font-family: inherit; font-size: 14px; cursor: pointer; text-align: left; }
.ref-method-opt:hover { background: rgba(255,255,255,0.06); color: var(--fg); }
.ref-method-opt.active { background: rgba(255,255,255,0.06); color: #fff; }
.ref-method-radio { width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--hairline); flex: 0 0 auto; position: relative; }
.ref-method-opt.active .ref-method-radio { border-color: var(--accent); }
.ref-method-opt.active .ref-method-radio::after { content: ""; position: absolute; inset: 3px; border-radius: 50%; background: var(--accent); }

.ntf-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px 0; border-top: 1px solid var(--hairline); }
.ntf-row:first-of-type { border-top: 0; }
.ntf-label { font-size: 14px; font-weight: 600; }
.ntf-sub { font-size: 12px; color: var(--fg-3); margin-top: 2px; }
/* Linked-account rows carry a provider mark (Discord/Roblox/Google/mail)
   so the row reads at a glance instead of relying on the text label
   alone - matches the icon-plus-label idiom the dash sidebar nav and
   the OAuth buttons on sign-in already use. */
.ac-linked-text { display: flex; align-items: center; gap: 12px; }
.ac-linked-icon { flex: 0 0 auto; color: var(--fg-2); }
.switch { flex: 0 0 auto; position: relative; width: 44px; height: 26px; cursor: pointer; }
/* [type="checkbox"] added to win specificity against the later, equal-
   specificity global `input[type="checkbox"]` reset below (same
   0,1,1) - without it, source order let that rule force this input
   down to an 18x18 relatively-positioned box instead of an absolutely
   positioned one covering the full switch, shrinking every toggle's
   real click target site-wide (notifications, theme, admin) to a
   fraction of the visible pill. */
.switch input[type="checkbox"] { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch .track { position: absolute; inset: 0; border-radius: 999px; background: var(--hairline-strong); transition: background .2s; }
.switch .track::after { content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: #fff; box-shadow: var(--e-1); transition: transform .2s var(--ease); }
.switch input:checked + .track { background: var(--accent-deep); }
.switch input:checked + .track::after { transform: translateX(18px); }

/* Appearance theme picker (dashboard > Appearance) - a 3-way segmented
   control (Dark/Light/Sync), same pill-switch pattern as .bt-switch, not
   a plain on/off toggle: this is a choice between three states, not a
   boolean, and a checkbox misrepresented that. */
.theme-switch { flex: 0 0 auto; display: inline-flex; gap: 4px; padding: 4px; border-radius: 14px;
  background: var(--bg-3); border: 1px solid var(--hairline); }
.theme-opt { display: inline-flex; align-items: center; gap: 6px; font-family: inherit; font-size: 13px; font-weight: 600;
  color: var(--fg-2); background: none; border: none; padding: 8px 14px; border-radius: 10px; cursor: pointer;
  transition: background .2s var(--ease), color .2s var(--ease); }
.theme-opt svg { flex: 0 0 auto; opacity: .85; }
.theme-opt:hover { color: var(--fg); }
.theme-opt.active { color: var(--fg); background: var(--bg-2); box-shadow: var(--e-1); }
.theme-opt.active svg { color: var(--accent); opacity: 1; }
@media (max-width: 560px) {
  .theme-switch { width: 100%; }
  .theme-opt { flex: 1 1 0; justify-content: center; }
}

.dash-card.danger { border: 1px solid rgba(255,77,68,0.3); }
.danger-text { font-size: 14px; color: var(--fg-2); line-height: 1.6; margin: 4px 0 16px; max-width: 540px; }
.danger-btn { color: #fff; background: var(--accent-deep); }
.danger-btn:hover { box-shadow: var(--e-2); }
.danger-confirm { margin-top: 16px; }
.danger-confirm p { font-size: 14px; color: var(--fg); margin: 0 0 12px; }
.auth-hint { font-size: 12px; color: var(--fg-3); margin: 6px 0 0; }
.auth-field input[readonly] { opacity: 0.6; cursor: not-allowed; }
.danger-code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 20px; letter-spacing: 0.08em; font-weight: 700;
  color: var(--accent); user-select: none; -webkit-user-select: none; padding: 10px 14px; background: var(--ink-25);
  border: 1px solid var(--hairline); border-radius: 10px; display: inline-block; }
.danger-row { display: flex; gap: 10px; flex-wrap: wrap; }
.danger-row input { min-height: 46px; padding: 12px 14px; border-radius: 16px; border: 1px solid var(--hairline); background: var(--ink-25); color: var(--fg); font-family: inherit; }

@media (max-width: 900px) {
  .dash { grid-template-columns: 1fr; }
  .dash-side { position: static; flex-direction: column; }
  .dash-nav { display: grid; grid-template-columns: repeat(2, 1fr); }
  .dash-stats { grid-template-columns: repeat(2, 1fr); }
  .dash-grid { grid-template-columns: 1fr; }
}

.dash-side, .dash-card, .dash-stat {
  background: var(--bg-2); }
.ds-money { color: var(--price); }

.announce { z-index: 210; }

.checkout-page { padding-top: 108px; padding-bottom: 90px; }
.checkout { display: grid; grid-template-columns: 1fr 388px; gap: 22px; align-items: start; }
.co-main { min-width: 0; }
.co-back { display: inline-block; color: var(--fg-2); text-decoration: none; font-size: 13px; font-weight: 600; margin-bottom: 12px; }
.co-back:hover { color: var(--fg); }
.co-main > h1 { font-size: var(--t-h3); letter-spacing: -0.03em; margin-bottom: 18px; }
/* OAuth interstitials ------------------------------------------------------
   /callback.html and /roblox-callback.html. Both had their own hardcoded
   palette and a monospace face, so the moment right after signing in looked
   like a terminal rather than coldd. Shared here rather than duplicated in two
   <style> blocks, since the two screens must not drift apart. */
.cb-wrap { min-height: 100vh; min-height: 100dvh; display: flex; align-items: center; justify-content: center; padding: 24px; }
.cb-card { display: flex; flex-direction: column; align-items: center; gap: 18px;
  padding: 40px 44px; border-radius: var(--r); min-width: 280px; max-width: 420px; text-align: center; }
.cb-logo { height: 30px; width: auto; display: block; opacity: 0.92; }
/* Ember, matching every other loading state on the site. */
.cb-spin { width: 26px; height: 26px; border: 2px solid var(--hairline);
  border-top-color: var(--accent); border-radius: var(--r-pill); animation: cbspin 3s linear infinite; }
@keyframes cbspin { to { transform: rotate(360deg); } }
.cb-card p { margin: 0; font-size: 14.5px; line-height: 1.5; color: var(--fg-2); max-width: 34ch; }
/* No reduced-motion guard here, matching every other loading spinner on the
   site (.btn-spinner etc.) - it's the only thing on this screen conveying
   "work in progress", not decorative motion. */

/* Failure state. The redirect that follows can itself fail (popup blockers,
   a stalled history entry), so the card grows a real link rather than leaving
   the visitor staring at an error with no way out. */
.cb-card[data-state="error"] .cb-spin { display: none; }
.cb-icon { display: none; width: 40px; height: 40px; border-radius: var(--r-pill);
  place-items: center; color: var(--accent); background: rgba(255,77,68,0.12);
  border: 1px solid rgba(255,77,68,0.3); }
.cb-icon svg { width: 21px; height: 21px; }
.cb-card[data-state="error"] .cb-icon { display: grid; }
.cb-out { display: none; font-size: 13px; font-weight: 600; color: var(--fg-2); text-decoration: none; }
.cb-out:hover { color: var(--fg); }
.cb-card[data-state="error"] .cb-out { display: inline-flex; }

/* Success state - used by /tiktok-callback.html once a token exchange
   actually completes, distinct from the error icon's ember/exclamation
   treatment above. */
.cb-card[data-state="ok"] .cb-spin { display: none; }
.cb-icon-ok { display: none; width: 40px; height: 40px; border-radius: var(--r-pill);
  place-items: center; color: var(--ok); background: var(--ok-tint);
  border: 1px solid rgba(107,216,138,0.32); }
.cb-icon-ok svg { width: 21px; height: 21px; }
.cb-card[data-state="ok"] .cb-icon-ok { display: grid; }
.cb-card[data-state="ok"] .cb-out { display: inline-flex; }

/* Thank-you page ----------------------------------------------------------
   Reuses the checkout shell, but there is no order summary rail here, so the
   single column centres instead of leaving a 388px hole. */
.success-page .checkout { grid-template-columns: min(720px, 100%); justify-content: center; }

.ty-mark { position: relative; display: grid; place-items: center; width: 56px; height: 56px;
  border-radius: var(--r-pill); margin-bottom: 18px; border: 1px solid var(--hairline);
  background: rgba(255,255,255,0.05); color: var(--fg-3);
  transition: color .3s var(--ease-out), background .3s var(--ease-out), border-color .3s var(--ease-out); }
.ty-mark svg { grid-area: 1 / 1; width: 26px; height: 26px; }
.ty-tick, .ty-cross { display: none; }

/* Pending: an ember ring sweeps the mark. A card capture can take a few
   seconds and a crypto confirmation genuinely takes minutes, so this is the
   only thing telling the buyer the page is still working rather than stuck. */
.ty-ring { position: absolute; inset: -1px; border-radius: var(--r-pill);
  border: 2px solid transparent; border-top-color: var(--accent);
  animation: tyspin 3s linear infinite; }
@keyframes tyspin { to { transform: rotate(360deg); } }
.ty-mark:not([data-state="pending"]) .ty-ring { display: none; }

.ty-mark[data-state="ok"] { color: var(--ok); background: var(--ok-tint); border-color: rgba(107,216,138,0.32); }
.ty-mark[data-state="ok"] .ty-tick { display: block; animation: tymark .3s var(--ease-out) both; }
.ty-mark[data-state="fail"] { color: var(--accent); background: rgba(255,77,68,0.12); border-color: rgba(255,77,68,0.3); }
.ty-mark[data-state="fail"] .ty-cross { display: block; animation: tymark .3s var(--ease-out) both; }
@keyframes tymark { from { opacity: 0; transform: scale(0.7); } to { opacity: 1; transform: scale(1); } }

/* A static ring still reads as "waiting"; the resolved states are meaning,
   not decoration, so they stay - they just arrive without the pop. */
@media (prefers-reduced-motion: reduce) {
  .ty-ring { animation: none; }
  .ty-mark[data-state="ok"] .ty-tick, .ty-mark[data-state="fail"] .ty-cross { animation: none; }
}

#successItems { display: flex; flex-direction: column; gap: 14px; margin-top: 24px; }
#successItems:empty { display: none; }

.ty-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; }
.ty-actions .btn { flex: 1 1 auto; justify-content: center; min-width: 170px; }

/* Post-purchase "Build more for less" - the highest-trust moment in the
   whole flow (they've already paid once), so it gets real card styling
   instead of reusing the plain upsell-row list checkout uses. */
.ty-survey { text-align: left; margin-top: 22px; padding: 18px 22px; }
.ty-survey[hidden] { display: none; }
.ty-survey-q { margin: 0 0 12px; font-size: 14px; font-weight: 700; }
.ty-survey-opts { display: flex; flex-wrap: wrap; gap: 8px; }
.ty-survey-opts[hidden] { display: none; }
.ty-survey-opts button { font-family: inherit; font-size: 12.5px; font-weight: 600; color: var(--fg-1);
  background: rgba(255,255,255,0.05); border: 1px solid var(--hairline); border-radius: 999px;
  padding: 7px 13px; cursor: pointer; transition: background .15s, border-color .15s; }
.ty-survey-opts button:hover { background: rgba(255,255,255,0.1); border-color: var(--hairline-strong); }
.ty-survey-done { margin: 0; font-size: 13px; color: var(--price); font-weight: 600; }
.ty-survey-done[hidden] { display: none; }

.ty-upsell { text-align: left; margin-top: 28px; padding: 24px; }
.ty-upsell-title { font-size: 18px; font-weight: 800; letter-spacing: -0.02em; margin: 0 0 6px; }
.ty-upsell-sub { font-size: 13px; color: var(--fg-2); margin: 0 0 18px; line-height: 1.5; }
.ty-upsell-list { display: flex; flex-direction: column; gap: 10px; }
/* Same row shell as the Licenses tab (.dl-item), so the post-purchase
   upsell reads as "more of your library" rather than an ad block. */
.tu-item { padding: 14px 16px; transition: border-color .15s var(--ease); }
.tu-item.is-in { border-color: rgba(52,224,138,0.4); }
.tu-item .dl-thumb { flex: 0 0 46px; width: 46px; height: 46px; }
.tu-was { color: var(--fg-3); text-decoration: line-through; margin-right: 5px; }
.tu-now { color: var(--price); font-weight: 700; }
.tu-off { display: inline-block; margin-left: 7px; font-size: 11px; font-weight: 800; letter-spacing: 0.02em;
  color: var(--accent); background: rgba(255,77,68,0.12); padding: 2px 7px; border-radius: var(--r-pill); vertical-align: 1px; }
.tu-toggle { min-height: 36px; padding: 0 16px; font-size: 13px; }
.ty-upsell-actions { display: flex; align-items: center; gap: 14px; margin-top: 18px; flex-wrap: wrap; }
.ty-upsell-selected-note { font-size: 12px; color: var(--fg-3); }

/* While the payment is still resolving there is nothing to download and no
   outcome to act on, so the exits and the "what happens next" steps - which
   talk about download buttons that do not exist yet - stay hidden. The
   support line below them is always visible, because a buyer stuck on a
   pending payment is exactly who needs it. */
.ty[data-state="pending"] .ty-actions,
.ty[data-state="pending"] .ty-next,
.ty[data-state="pending"] .ty-links { display: none; }
/* On failure the steps are wrong but the exits are the entire remedy. */
.ty[data-state="fail"] .ty-next { display: none; }

.ty-next, .ty-links { margin-top: 34px; }
.ty-next h2, .ty-links h2 { font-size: 17px; font-weight: 700; margin-bottom: 14px; }
.ty-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; counter-reset: ty; }
/* > (direct child), not a bare descendant selector - .ty-substeps nests its
   own <li> elements inside a .ty-steps <li>, and a descendant selector was
   matching those too: the numbered-badge counter climbed through every
   substep as well, so "Install them" (step 2) numbered its three file-type
   rows 3/4/5 and "Updates are free, forever" ended up labelled 6 instead of
   3. Scoping to direct children keeps the counter (and the badge) on the
   top-level steps only. */
.ty-steps > li { counter-increment: ty; position: relative; padding-left: 38px;
  font-size: 14px; line-height: 1.6; color: var(--fg-2); }
.ty-steps > li::before { content: counter(ty); position: absolute; left: 0; top: 1px;
  display: grid; place-items: center; width: 24px; height: 24px; border-radius: var(--r-pill);
  font-size: 12px; font-weight: 700; color: var(--fg-1);
  background: rgba(255,255,255,0.06); border: 1px solid var(--hairline); }
.ty-steps strong { color: var(--fg); font-weight: 700; display: block; }
.ty-steps a { color: var(--accent); text-decoration: none; font-weight: 600; }
.ty-steps a:hover { text-decoration: underline; }
.ty-steps code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; padding: 1px 5px; border-radius: var(--r-xs);
  background: rgba(255,255,255,0.07); color: var(--fg-1); }
.ty-substeps { list-style: none; margin: 8px 0 10px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.ty-substeps li { padding: 8px 12px; border-radius: var(--r-sm); background: rgba(255,255,255,0.04); border: 1px solid var(--hairline); }
.ty-substeps strong { display: inline; color: var(--fg); }

.ty-link-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.ty-link { display: flex; flex-direction: column; gap: 4px; padding: 16px 18px;
  border-radius: var(--r-sm); border: 1px solid var(--hairline);
  background: rgba(255,255,255,0.035); text-decoration: none;
  transition: background .18s var(--ease-out), border-color .18s var(--ease-out); }
.ty-link:hover { background: rgba(255,255,255,0.06); border-color: var(--hairline-strong); }
.ty-link-t { font-size: 14px; font-weight: 700; color: var(--fg); }
.ty-link-d { font-size: 12.5px; color: var(--fg-3); line-height: 1.5; }
.ty-support { margin-top: 30px; line-height: 1.6; }

@media (max-width: 620px) {
  .ty-link-grid { grid-template-columns: 1fr; }
}

.co-sec { padding: 22px 24px; margin-bottom: 16px;
  background: var(--bg-2); }
.co-sec-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.co-sec h2 { font-size: 17px; font-weight: 700; margin-bottom: 14px; }
.co-sec-head h2 { margin-bottom: 0; }
.co-guest-tag { font-size: 11px; font-weight: 600; color: var(--fg-2); padding: 4px 10px; border-radius: 999px; background: rgba(255,255,255,0.05); border: 1px solid var(--hairline); }
.co-hint { font-size: 12px; color: var(--fg-3); margin: 10px 0 0; }
.co-hint a { color: var(--accent); text-decoration: none; font-weight: 600; }
/* Points at the "Place Order" button by name - same accent emphasis the
   inline links in this hint already use. */
.co-hint strong { color: var(--accent); font-weight: 600; }
.co-secure { color: var(--price); font-weight: 600; }
.co-user-row { display: flex; align-items: center; gap: 12px; }
.co-pay-panel .auth-field { margin-bottom: 14px; }
/* These buttons sat with zero gap against the hint paragraph above them -
   .co-hint's own margin-top only pushes itself down from what's above it,
   it doesn't add space below, so a bare <button> right after touched the
   text directly. */
#coRobuxLinkBtn, #coSigninBtn { margin-top: 14px; }
.co-robux-actions { display: flex; align-items: center; gap: 12px; margin-top: 14px; flex-wrap: wrap; }
.co-robux-actions .btn { flex: 1 1 auto; justify-content: center; }
.co-robux-actions .btn[aria-disabled="true"] { pointer-events: none; opacity: 0.5; }
.co-check { display: flex; align-items: flex-start; gap: 11px; font-size: 13px; color: var(--fg-2); line-height: 1.5; cursor: pointer; padding: 7px 0; }
/* Any author `display` beats the browser's default [hidden]{display:none}, so
   a class that sets display MUST restate it - otherwise app.js sets .hidden,
   the attribute lands, and the element stays on screen anyway. That is exactly
   how the resell-licence agreement was showing on carts with no resell item.
   Same defect previously hit the tutorials/releases tabs. */
.co-check[hidden] { display: none; }
/* accent-color alone tints the browser's OWN checkbox rendering, corners
   and all - shape and rounding stay whatever that browser/OS draws by
   default, unrelated to anything else on the page. A custom checkbox
   (appearance:none + our own box/checkmark) puts it on the same var(--r-xs)
   radius the rest of the site's small ticks/badges/chips use, instead of
   an unstyled native control sitting in an otherwise fully custom UI. */
/* [type="checkbox"] added to win specificity against the later, equal-
   specificity global `input[type="checkbox"]` reset below (same
   0,1,1) - without it, source order let that rule's display:inline-
   block win over this rule's display:inline-grid;place-items:center,
   which is what centers the custom checkmark below (that pseudo-
   element itself does win on specificity, since it targets `:checked`
   too - it just had nothing centering it any more). */
.co-check input[type="checkbox"] { -webkit-appearance: none; appearance: none; margin-top: 1px; width: 17px; height: 17px; flex: 0 0 auto; cursor: pointer;
  border: 1.5px solid var(--hairline-strong); border-radius: var(--r-xs); background: var(--ink-25);
  display: inline-grid; place-items: center; transition: background .15s var(--ease), border-color .15s var(--ease); }
.co-check input[type="checkbox"]:hover { border-color: var(--accent); }
.co-check input[type="checkbox"]:checked { background: var(--accent); border-color: var(--accent); }
.co-check input:checked::after { content: ''; width: 5px; height: 9px; margin-bottom: 1px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg); }
.co-check a { color: var(--accent); text-decoration: none; }
.co-place { width: 100%; justify-content: center; min-height: 52px; font-size: 15px; }
.co-msg { margin-top: 14px; padding: 13px 16px; border-radius: 16px; font-size: 14px; line-height: 1.5; display: none;
  background: rgba(52,224,138,0.1); border: 1px solid rgba(52,224,138,0.3); color: var(--fg); }
.co-msg.show { display: block; }
.co-msg.err { background: rgba(255,77,68,0.1); border-color: rgba(255,77,68,0.3); }
.co-msg a { color: var(--accent); text-decoration: none; font-weight: 600; }

/* A vertical list, not a 2x2 grid of centred pills. Each row carries a name,
   what it actually is, and the total in that method's own unit - which needs
   a real left-to-right structure to sit in. */
.co-pay-methods { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
/* --r-sm, not a hand-picked 14px. The redesigned payment rows shipped with an
   off-scale radius, which is exactly the sixth-step drift DESIGN.md forbids -
   caught by the detector rather than by eye. */
.co-pay-btn { display: grid; grid-template-columns: 28px 1fr auto; align-items: center; gap: 13px; padding: 14px 16px; border-radius: var(--r-sm);
  background: var(--bg-3); border: 1px solid var(--hairline); color: var(--fg-2); font-family: inherit;
  font-size: 15px; font-weight: 700; cursor: pointer; transition: border-color .18s var(--ease), background .18s var(--ease), color .18s var(--ease); }
.co-pay-btn svg { display: block; }
@media (hover: hover) and (pointer: fine) {
  .co-pay-btn:hover { border-color: var(--hairline-strong); color: var(--fg); }
}
/* Radio-style selection, so an ember border is the correct device here per the
   Neutral Border Rule. The old inset ring doubled the border to ~2px and made
   the selected row bloom; one clean border plus a fill step is enough. */
.co-pay-btn.active { border-color: var(--accent); color: var(--fg); background: rgba(255,255,255,0.06); }
.co-pay-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.co-pay-ic { display: flex; align-items: center; justify-content: center; width: 28px; }
.co-pay-tx { display: flex; flex-direction: column; gap: 2px; min-width: 0; text-align: left; }
.co-pay-name { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; color: inherit; }
.co-pay-sub { font-size: 12.5px; font-weight: 500; line-height: 1.35; color: var(--fg-3); }
/* The per-method figure. Tabular so the four rows' amounts align down the
   right edge instead of jittering by digit width. */
.co-pay-amt { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; color: var(--fg);
  font-variant-numeric: tabular-nums; white-space: nowrap; text-align: right; }
.co-pay-btn:not(.active) .co-pay-amt { color: var(--fg-2); }
.co-pay-settle { font-size: 12.5px; line-height: 1.5; color: var(--fg-3); margin: 0 0 16px; }

/* Brand colour lives in the mark, not in the slab behind it. */
.co-pay-btn--stripe svg { color: var(--brand-stripe); }
.co-pay-btn--paypal svg { color: var(--brand-paypal); }
.co-pay-btn--crypto svg { color: var(--brand-crypto); }
.co-pay-btn--robux .co-pay-robux-ic { color: var(--price); }
/* Same "always-visible chip" treatment as .oauth-disabled - a dimmed row
   with no explanation is a dead end on touch, where there's no hover to
   reveal why. */
.co-pay-btn.co-pay-disabled { opacity: 0.55; cursor: not-allowed; }
@media (hover: hover) and (pointer: fine) {
  .co-pay-btn.co-pay-disabled:hover { border-color: var(--hairline); color: var(--fg-2); }
}
.co-pay-inactive-badge { padding: 3px 8px; border-radius: 999px; background: rgba(255,255,255,0.08);
  border: 1px solid var(--hairline); font-size: 11px; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--fg-2); white-space: nowrap; }
.co-pay-panel[hidden] { display: none; }
.co-pay-soon { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--warn);
  background: var(--warn-tint); border: 1px solid var(--warn-line); border-radius: 10px; padding: 12px 14px; }
.co-pay-soon[hidden] { display: none; }
@media (max-width: 460px) {
  /* The descriptor is the first thing to go when the row gets tight - the
     name and the amount are what the choice actually turns on. */
  .co-pay-sub { display: none; }
}

.co-side { position: sticky; top: 96px; display: flex; flex-direction: column; gap: 16px; }
.co-sum, .co-suggest { padding: 22px 24px;
  background: var(--bg-2); }
.co-sum h2 { font-size: 17px; font-weight: 700; margin-bottom: 14px; }
.co-items { display: flex; flex-direction: column; }
.co-item { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-top: 1px solid var(--hairline); }
.co-item:first-child { border-top: 0; }
.co-item-thumb { flex: 0 0 44px; width: 44px; height: 44px; border-radius: 10px; background: #0f1014 center / cover no-repeat; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); }
.co-item-info { flex: 1; min-width: 0; }
.co-item-title { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.co-item-sub { font-size: 12px; color: var(--fg-3); margin-top: 2px; }
.co-item-price { font-size: 13px; font-weight: 700; color: var(--price); white-space: nowrap; }
.co-item-x { flex: 0 0 auto; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--fg-3); border-radius: var(--r-ctl); cursor: pointer; transition: color .15s var(--ease), background .15s var(--ease); }
@media (hover: hover) and (pointer: fine) { .co-item-x:hover { color: var(--fg-1); background: rgba(255,255,255,0.07); } }
.co-item-x:active { transform: scale(0.92); }
.co-empty { padding: 24px 0; text-align: center; color: var(--fg-3); font-size: 14px; }
/* Automatic spend-more tier progress - the price just drops once the cart
   clears a threshold. Not a tinted box: one status line over a slim
   four-segment meter (one segment per tier, so the fill and the
   10/15/20/25% scale beneath it always line up - the real thresholds
   aren't evenly spaced, and placing labels by true proportion is what
   made the old version read like a spreadsheet). Price Green is the only
   colour here; ember never signals savings. */
.co-tier { margin: 16px 0 2px; padding-top: 15px; border-top: 1px solid var(--hairline); }
.co-tier-status { display: flex; align-items: baseline; gap: 7px; font-size: 12.5px; font-weight: 600; color: var(--fg-2); line-height: 1.4; }
.co-tier-status b { font-weight: 700; color: var(--fg-1); }
.co-tier-status.unlocked, .co-tier-status.unlocked b { color: var(--price); }
.co-tier-tick { flex: 0 0 auto; align-self: center; color: var(--price); }
.co-tier-meter { position: relative; height: 6px; margin-top: 10px; border-radius: var(--r-pill);
  background: rgba(255,255,255,0.06); overflow: hidden; }
.co-tier-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: var(--r-pill);
  background: var(--price); transition: width .55s var(--ease); }
/* Each step: the dollar (or Robux) threshold on top, the % it unlocks
   under it - so the buyer can read exactly what each mark on the bar is
   worth. */
.co-tier-scale { display: flex; margin-top: 7px; }
.co-tier-step { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-end; gap: 1px;
  text-align: right; transition: color .3s var(--ease); color: var(--fg-3); }
.co-tier-step b { font-size: 11.5px; font-weight: 800; color: var(--fg-2); letter-spacing: -0.01em; }
.co-tier-step i { font-size: 9.5px; font-weight: 700; font-style: normal; letter-spacing: 0.02em; }
.co-tier-step.done b, .co-tier-step.done { color: var(--price); }
.co-tier-step.next b { color: var(--fg-1); }
@media (prefers-reduced-motion: reduce) {
  .co-tier-fill, .co-tier-step, .co-tier-step b { transition: none; }
}
/* Same ladder in the cart drawer (the element carries both classes);
   the drawer keeps its own horizontal margin and, unlike checkout, still
   shows the one-click gap-closer nudge below the meter. */
.cd-tier { margin: 4px 18px 14px; }
/* The gap-closing pick: "spend $12 more" turned into a specific,
   one-click product instead of homework - shared by checkout and the
   drawer, same shape as the other upsell rows on the site. */
.co-tier-nudge { display: flex; align-items: center; gap: 10px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--hairline); cursor: pointer; }
.co-tier-nudge-thumb { flex: 0 0 44px; width: 44px; height: 44px; border-radius: var(--r-ctl); background: #0f1014 center / cover no-repeat; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); }
.co-tier-nudge-body { flex: 1; min-width: 0; }
.co-tier-nudge-title { font-size: 12.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.co-tier-nudge-sub { font-size: 11px; color: var(--fg-3); margin-top: 1px; }
.co-tier-nudge-add { flex: 0 0 auto; min-height: 32px; padding: 0 14px; font-size: 12px; }
/* "Add to your order" - relevance-ranked catalogue suggestions
   (get_checkout_cross_sell), each at the exact 10%-off price the server
   will honour (never below product_legal.min_sale_usd; skipped entirely
   for disallow_sales items). Sits right under the tier ladder in the
   order summary; compact rows, same shape as the offer-modal rows. */
.co-cross { display: flex; flex-direction: column; gap: 7px; margin: 14px 0 2px; padding-top: 15px; border-top: 1px solid var(--hairline); }
.co-cross-head { font-size: 12.5px; font-weight: 700; color: var(--fg-2); margin-bottom: 1px; }
.co-cross-row { display: flex; align-items: center; gap: 13px; padding: 12px;
  background: var(--bg-2); border: 1px solid var(--hairline); border-radius: var(--r-sm); }
.co-cross-thumb { flex: 0 0 62px; width: 62px; height: 62px; border-radius: var(--r-ctl);
  background: #0f1014 center / cover no-repeat; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); }
.co-cross-info { flex: 1; min-width: 0; }
.co-cross-title { font-size: 14px; font-weight: 700; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.co-cross-price { font-size: 13px; font-weight: 700; color: var(--price); margin-top: 4px; display: flex; align-items: baseline; gap: 5px; flex-wrap: wrap; }
.co-cross-was { color: var(--fg-3); text-decoration: line-through; font-weight: 400; font-size: 12px; }
.co-cross-off { color: var(--fg-3); font-weight: 600; font-size: 10.5px; letter-spacing: 0.02em; }
.co-cross-note { flex-basis: 100%; color: var(--fg-3); font-weight: 500; font-size: 11px; }
.co-cross-add { flex: 0 0 auto; min-height: 32px; padding: 0 16px; font-size: 12.5px; }

.mkt-popup-modal .mkt-popup-fine a { color: var(--accent); font-weight: 600; text-decoration: none; }

/* "Build more for less" - the one interruption before payment worth
   making: relevance-ranked items to add to the order, plus a single bar
   that puts a resell licence on everything eligible at once. Built on
   the confirm-modal shell but left-aligned and taller, with its own
   scrolling body so a long list never pushes the actions off screen.
   Everything is added live and re-priced server-side like a cart item. */
.confirm-modal.co-offer { width: 500px; max-width: 96vw; max-height: 88vh; padding: 0; text-align: left;
  display: flex; flex-direction: column; overflow: hidden; }
.co-offer .mkt-popup-x { top: 15px; right: 15px; }
.co-offer-title { font-size: 22px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.2; margin: 0; padding: 26px 26px 0; color: var(--fg); }
.co-offer-tier { margin: 20px 26px 2px; padding: 15px 16px 14px; border-radius: var(--r-sm); background: var(--bg-2); border: 1px solid var(--hairline); }
.co-offer-tier .co-tier { margin: 0; padding: 0; border: 0; }
.co-offer-tier .co-tier-status { color: var(--fg-1); }

/* Resell upsell - one bar, one action: add resell rights to every
   eligible item in the order. */
.co-offer-resell { display: flex; align-items: center; gap: 14px; margin: 12px 26px 2px; padding: 13px 15px;
  border-radius: var(--r-sm); background: var(--bg-2); border: 1px solid var(--hairline); transition: border-color .2s var(--ease); }
.co-offer-resell.added { border-color: rgba(52,224,138,0.4); }
/* display:flex above is an author rule, so [hidden] alone won't hide it -
   without this the bar showed as an empty box when there was nothing to
   upsell (cart already all resell). */
.co-offer-resell[hidden], .co-offer-tier[hidden], .co-offer-terms[hidden] { display: none; }
.co-offer-resell .cor-copy { flex: 1; min-width: 0; }
.co-offer-resell .cor-copy b { display: block; font-size: 13.5px; font-weight: 700; color: var(--fg); }
.co-offer-resell .cor-copy span { display: block; font-size: 12px; color: var(--fg-3); margin-top: 2px; line-height: 1.4; }
.co-offer-resell-btn { flex: 0 0 auto; min-height: 34px; padding: 0 18px; font-size: 12.5px; display: inline-flex; align-items: center; gap: 5px; }

.co-offer-list { flex: 1 1 auto; overflow-y: auto; padding: 14px 26px 4px; display: flex; flex-direction: column; gap: 9px; }
.co-offer-empty { font-size: 13px; color: var(--fg-3); padding: 8px 0; margin: 0; }
.co-offer-row { display: flex; align-items: center; gap: 14px; padding: 13px 14px;
  background: var(--bg-2); border: 1px solid var(--hairline); border-radius: var(--r-sm);
  transition: border-color .2s var(--ease); }
.co-offer-row.added { border-color: rgba(52,224,138,0.4); }
.co-offer-thumb { flex: 0 0 64px; width: 64px; height: 64px; border-radius: var(--r-ctl);
  background: #0f1014 center / cover no-repeat; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08); }
.co-offer-info { flex: 1; min-width: 0; }
.co-offer-name { font-size: 14.5px; font-weight: 700; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.co-offer-price { font-size: 17px; font-weight: 800; color: var(--price); margin-top: 5px;
  display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; letter-spacing: -0.01em; }
.co-offer-was { color: var(--fg-3); text-decoration: line-through; font-weight: 400; font-size: 13px; letter-spacing: 0; }
.co-offer-off { color: var(--fg-3); font-weight: 600; font-size: 11px; letter-spacing: 0.02em; }
.co-offer-add { flex: 0 0 auto; min-height: 34px; padding: 0 18px; font-size: 12.5px; display: inline-flex; align-items: center; gap: 5px; }
.co-offer-add.is-added, .co-offer-resell-btn.is-added { color: var(--price); box-shadow: inset 0 0 0 1px rgba(52,224,138,0.4); background: rgba(52,224,138,0.1); }
@media (hover: hover) and (pointer: fine) {
  .co-offer-add.is-added:hover, .co-offer-resell-btn.is-added:hover { background: rgba(52,224,138,0.16); color: var(--price); }
}
.co-offer-terms { margin: 12px 26px 0; }
.co-offer-msg { color: var(--accent); font-size: 12px; font-weight: 600; margin: 8px 26px 0; }
.co-offer-msg:empty { display: none; }
.co-offer-terms.shake { animation: coOfferShake .4s var(--ease); }
@keyframes coOfferShake { 10%, 90% { transform: translateX(-1px); } 30%, 70% { transform: translateX(2px); } 50% { transform: translateX(-3px); } }
.co-offer-foot { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  margin-top: 14px; padding: 16px 26px 20px; border-top: 1px solid var(--hairline); }
.co-offer-subtotal { font-size: 13px; font-weight: 700; color: var(--fg-1); }
.co-offer-actions { display: flex; gap: 10px; }
.co-offer-actions .btn { min-height: 42px; padding: 0 20px; }
@media (prefers-reduced-motion: reduce) { .co-offer-row, .co-offer-resell { transition: none; } .co-offer-terms.shake { animation: none; } }
@media (max-width: 520px) {
  .co-offer-title { font-size: 19px; padding: 22px 20px 0; }
  .co-offer-tier, .co-offer-resell, .co-offer-terms, .co-offer-msg { margin-left: 20px; margin-right: 20px; }
  .co-offer-list { padding-left: 20px; padding-right: 20px; }
  .co-offer-row { gap: 11px; padding: 10px; }
  .co-offer-thumb { flex-basis: 54px; width: 54px; height: 54px; }
  .co-offer-foot { padding: 14px 20px 18px; }
  .co-offer-actions { flex: 1 1 100%; }
  .co-offer-actions .btn { flex: 1; justify-content: center; }
}
/* "More payment methods" bar + popup on checkout - a bar under the default
   Card panel, opening a searchable tile grid of Stripe's local payment
   methods. Built on the same confirm-modal/.co-offer shell as the upsell
   modal, just swapping the scrolling list for a grid. */
.co-more-methods-bar { display: block; width: 100%; margin-top: 12px; padding: 13px 16px; text-align: center;
  background: var(--bg-3); border: 1px solid var(--hairline); border-radius: var(--r-sm);
  color: var(--fg-2); font-family: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer;
  transition: border-color .18s var(--ease), color .18s var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .co-more-methods-bar:hover { border-color: var(--hairline-strong); color: var(--fg); }
}
.confirm-modal.co-methods { width: 560px; max-width: 96vw; max-height: 88vh; padding: 0; text-align: left;
  display: flex; flex-direction: column; overflow: hidden; background: var(--bg-2); }
.co-methods .mkt-popup-x { top: 15px; right: 15px; }
/* .confirm-modal p (0,1,1) otherwise beats this (0,1,0) and wins the
   cascade - its own margin-left:0/font-size:15px/weight:600 was overriding
   this subtitle's indent and size, so it sat flush against the modal edge
   instead of lining up under the title. */
.co-methods .co-methods-sub { margin: 6px 26px 0; padding: 0; font-size: 13px; font-weight: 500; color: var(--fg-3); line-height: 1.5; }
.co-methods-search { margin: 16px 26px 0; padding: 11px 16px 11px 40px; border-radius: var(--r-pill);
  border: 1px solid var(--hairline); background: var(--ink-25); color: var(--fg); font-family: inherit; font-size: 13px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237d8494' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: 15px center; background-size: 15px;
  transition: border-color .18s var(--ease), background-color .18s var(--ease); }
.co-methods-search:focus { outline: none; border-color: var(--hairline-strong); background-color: rgba(255,255,255,0.05); }
/* Tile grid on a dark surface: every brand mark keeps its own required
   background (a monochrome logo gets a small white card so it stays
   legible, a mark that already ships on its own brand colour - Zip,
   Satispay, BLIK, EPS, Bizum, Pix - keeps that colour as-is), but none of
   them are allowed to fill the tile - the tile itself always reads dark,
   with the mark sitting inside it as a contained chip rather than a wall
   of white or colour. */
.co-methods-grid { flex: 1 1 auto; overflow-y: auto; padding: 18px 26px 26px;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.co-method-tile { padding: 18px 10px; border-radius: 14px; background: var(--bg-3); border: 1px solid var(--hairline);
  color: var(--fg-2); font-family: inherit; font-size: 12.5px; font-weight: 700; text-align: center; cursor: pointer;
  transition: border-color .18s var(--ease), background .18s var(--ease), transform .18s var(--ease);
  display: flex; align-items: center; justify-content: center; min-height: 72px; }
.co-method-badge { display: inline-flex; align-items: center; justify-content: center;
  padding: 9px 14px; border-radius: 9px; background: #fff; box-shadow: 0 4px 14px -6px var(--ink-75); }
.co-method-badge img { max-width: 100%; max-height: 20px; object-fit: contain; display: block; }
/* Marks that already carry their own brand-colour background (not white)
   keep that colour instead of sitting on an added white card - clipped to
   the same chip shape so the grid still reads as one consistent system. */
.co-method-badge.co-method-badge-tint { padding: 0; overflow: hidden; background: none; box-shadow: none; }
.co-method-badge.co-method-badge-tint img { max-height: 40px; border-radius: 9px; }
.co-method-tile-text { font-size: 13px; }
@media (hover: hover) and (pointer: fine) {
  .co-method-tile:hover { border-color: var(--accent); background: rgba(255,255,255,0.06); transform: translateY(-1px); }
}
.co-method-tile:active { transform: translateY(0) scale(0.98); }
.co-method-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.co-method-tile[hidden] { display: none; }
.co-methods-empty { font-size: 13px; color: var(--fg-3); text-align: center; padding: 0 26px 26px; margin: 0; }
@media (max-width: 520px) {
  .co-methods-sub { margin-left: 20px; margin-right: 20px; }
  .co-methods-search { margin-left: 20px; margin-right: 20px; }
  .co-methods-grid { grid-template-columns: repeat(2, 1fr); padding: 16px 20px 20px; }
}

.co-empty a { color: var(--accent); text-decoration: none; }
.co-coupon { display: flex; gap: 8px; margin: 16px 0 4px; }
/* Was border-radius: 10px - the Apply button right beside it (.btn, used
   site-wide) is a full pill (var(--r-pill)), so the two sat with visibly
   different corner rounding in the same row. Matching the pill here reads
   as one cohesive control instead of two conflicting shapes. */
.co-coupon input { flex: 1; min-height: 44px; padding: 11px 18px; border-radius: var(--r-pill); border: 1px solid var(--hairline); background: var(--ink-25); color: var(--fg); font-family: inherit; font-size: 13px; }
.co-coupon .btn { min-height: 44px; }
.co-coupon-msg { font-size: 12px; margin-top: 8px; min-height: 0; }
.co-coupon-msg.ok { color: var(--price); }
.co-coupon-msg.no { color: var(--accent); }
.co-totals { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--hairline); }
.co-line { display: flex; justify-content: space-between; align-items: center; font-size: 14px; color: var(--fg-2); padding: 6px 0; }
.co-line[hidden] { display: none; }
.co-line span:last-child { color: var(--fg); font-weight: 600; }
.co-disc span { color: var(--price) !important; }
.co-grand { margin-top: 8px; padding-top: 12px; border-top: 1px solid var(--hairline); font-size: 16px; }
.co-grand span { font-weight: 800 !important; }
.co-grand span:last-child { font-size: 20px; color: var(--fg); }
.co-fx { margin-top: 10px; font-size: 12px; color: var(--fg-3); line-height: 1.5; }
.co-suggest h3 { font-size: 15px; font-weight: 700; }
.co-suggest-sub { font-size: 12px; color: var(--fg-3); margin: 4px 0 14px; }
.co-suggest-item { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-top: 1px solid var(--hairline); }
.co-suggest-item:first-of-type { border-top: 0; }
.co-sg-thumb { flex: 0 0 40px; width: 40px; height: 40px; border-radius: 10px; background: #0f1014 center / cover no-repeat; }
.co-sg-info { flex: 1; min-width: 0; }
.co-sg-title { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.co-sg-price { font-size: 12px; margin-top: 2px; }
.co-sg-was { color: var(--fg-3); text-decoration: line-through; margin-right: 6px; }
.co-sg-now { color: var(--price); font-weight: 700; }
.co-sg-add { font-family: inherit; font-size: 12px; font-weight: 600; color: #fff; cursor: pointer; padding: 7px 13px; border-radius: 10px; border: none; background: rgba(255,77,68,0.18); border: 1px solid rgba(255,77,68,0.4); transition: background .2s; }
.co-sg-add:hover { background: rgba(255,77,68,0.3); }
@media (max-width: 920px) {
  .checkout { grid-template-columns: 1fr; }
  .co-side { position: static; }
}


.pay-sec { padding-top: 0; padding-bottom: 8px; }
.pay-marquee { position: relative; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent); }
.pay-track { display: flex; align-items: center; width: max-content; animation: pay-slide 38s linear infinite; }
.pay-marquee:hover .pay-track { animation-play-state: paused; }
.pay-logo { display: inline-flex; align-items: center; gap: 9px; margin-right: 48px; white-space: nowrap; flex: 0 0 auto; }
/* Real brand marks (Visa, Mastercard, PayPal, etc. via Simple Icons) - each
   ships its own official colour baked into the SVG's fill, so unlike the
   old plain-text-only version there's no uniform currentColor tint to dim
   on hover. A real payment strip doesn't grey out on hover either. */
.pay-ic { height: 22px; width: auto; flex: 0 0 auto; }
.pay-name { font-size: 14.5px; font-weight: 600; color: var(--fg-2); letter-spacing: 0.005em; }
/* Methods with no real brand mark available anywhere (regional schemes like
   Bancontact, BLIK, MB WAY - checked a dozen slug variants each against
   Simple Icons, none exist) get a generic type glyph instead: a bank icon
   for bank-transfer methods, a phone for mobile-app ones, matching the
   site's own stroke-icon convention (nav search icon etc.) rather than
   pretending they have a brand mark or falling back to plain text. */
.pay-ic-generic { height: 20px; color: var(--fg-3); }
@keyframes pay-slide { to { transform: translateX(-50%); } }

.hero { position: relative; padding: 104px 0 28px; overflow: visible; }
.hero-grid { display: grid; grid-template-columns: 1.04fr 0.96fr; gap: 54px; align-items: center; }
.hero-eyebrow { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600;
  color: var(--fg-2); padding: 7px 14px; border-radius: 999px; background: var(--bg-3); border: 1px solid var(--hairline); }
.he-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px var(--accent); }
.hero-h1 { font-size: clamp(40px, 5.6vw, 66px); font-weight: 800; line-height: 1.03; letter-spacing: -0.04em; margin: 22px 0 0; }
.hero-h1 .hl { color: var(--accent); display: inline-block; transition: opacity .35s ease; }
.hero-h1 .hl.hl-fade { opacity: 0; }
.hero-h1 .hl.hl-minecraft { color: #6cc25c; }
html[data-theme="light"] .hero-h1 .hl.hl-minecraft { color: #2e7d32; }
.hero-sub { font-size: var(--t-lead); color: var(--fg-1); line-height: 1.62; max-width: 520px; margin: 20px 0 0; }
.hero-copy .hero-cta { display: flex; justify-content: flex-start; gap: 12px; flex-wrap: wrap; margin-top: 30px; }
.hero-copy .hero-cta .btn { min-height: 50px; padding: 0 30px; font-size: 15px; }
/* Hero platform pair neutralised to grey - same reasoning as the .btn-tinted
   buy-pair rule above: red Roblox next to green Minecraft was two competing
   brand colours fighting the ember accent for attention above the fold.
   Solid var(--bg-3), not a translucent rgba fill - a see-through tint over
   the hero art is exactly the glassmorphism look the rest of the site just
   dropped, so this stays flat and opaque like everything else. No border:
   against the hero art a hairline ring read as a floating glass chip, so
   depth comes from a real shadow instead, same device .glass panels use. */
.hero-copy .hero-cta .btn-minecraft {
  color: var(--fg); background: var(--bg-3);
  border-color: transparent; box-shadow: var(--e-2);
}
.hero-copy .hero-cta .btn-minecraft:hover {
  background: var(--bg-2); border-color: transparent; color: var(--fg); box-shadow: var(--e-3);
}
.hero-copy .hero-cta .btn-minecraft:active {
  background: var(--bg-3); box-shadow: var(--e-1);
}
/* Join Discord sits beside the shop CTA in the old neutral chip look the
   shop button itself used to have, before that button became solid ember. */
.hero-copy .hero-cta .btn-discord {
  color: var(--fg); background: var(--bg-3);
  border-color: transparent;
}
.hero-copy .hero-cta .btn-discord:hover {
  background: var(--bg-2); border-color: transparent; color: var(--fg);
}
.hero-copy .hero-cta .btn-discord:active {
  background: var(--bg-3);
}
/* Read as a sentence of facts on one baseline, not as a stat-card row. The
   stacked big-number-over-tiny-uppercase-label arrangement is the generated
   landing page's default proof device; the figures here are real, so they are
   set inline at reading scale and separated by hairlines. */
.hero-stats { display: flex; align-items: center; flex-wrap: wrap; gap: 0; margin-top: 36px; }
.hstat { display: flex; align-items: baseline; gap: 7px; padding: 0 18px; }
.hstat:first-child { padding-left: 0; }
.hstat + .hstat { border-left: 1px solid var(--hairline); }
.hstat .hn { font-size: 20px; font-weight: 700; letter-spacing: -0.02em; color: var(--fg); }
.hstat .hl2 { font-size: var(--t-sm); color: var(--fg-2); font-weight: 500; letter-spacing: 0; text-transform: none; }
/* hn-live (products/Discord counts, not the static "purchases" copy) ships
   a hardcoded placeholder number as its actual text content - visibility
   (not display) hidden keeps its layout space reserved so nothing shifts
   when app.js reveals the real, live figure. See app.js's hero-stats IIFE. */
.hs-loading .hn-live { visibility: hidden; }

.hero-show { position: relative; min-height: 432px; }
.hero-card { position: absolute; inset: 0; display: block; border-radius: 24px; overflow: hidden; color: #fff; text-decoration: none;
  border: 1px solid var(--hairline); background: var(--bg-3); box-shadow: 0 44px 90px -34px var(--ink-75); }
.hsc-img { position: absolute; inset: 0; background-size: cover; background-position: center; transition: transform .5s var(--ease); }
.hero-card:hover .hsc-img { transform: scale(1.04); }
.hero-card::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(8,9,12,0) 42%, rgba(8,9,12,0.88)); }
.hsc-chip { position: absolute; top: 16px; left: 16px; z-index: 2; font-size: 11px; font-weight: 700; letter-spacing: 0.03em;
  color: #fff; padding: 6px 12px; border-radius: 999px; background: var(--accent-deep); box-shadow: 0 8px 20px -8px var(--ink-85); }
.hsc-foot { position: absolute; left: 20px; right: 20px; bottom: 18px; z-index: 2; display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; }
.hsc-cat { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--fg-2); margin-bottom: 4px; }
.hsc-title { font-size: 24px; font-weight: 800; letter-spacing: -0.02em; }
.hsc-price { font-size: 24px; font-weight: 800; color: var(--price); white-space: nowrap; }
.hero-mini { position: absolute; z-index: 3; width: 194px; height: 124px; border-radius: 16px; overflow: hidden;
  border: 1px solid rgba(255,255,255,0.14); background-size: cover; background-position: center; color: #fff; text-decoration: none;
  box-shadow: 0 24px 46px -18px var(--ink-75); display: flex; align-items: flex-end; transition: transform .3s var(--ease); }
.hero-mini::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(8,9,12,0) 35%, rgba(8,9,12,0.82)); }
.hero-mini span { position: relative; z-index: 1; padding: 9px 12px; font-size: 12px; font-weight: 600; color: #fff; }
.hm1 { right: -26px; top: 20px; transform: rotate(4deg); }
.hm2 { left: -26px; bottom: 118px; transform: rotate(-5deg); }
.hm3 { right: -26px; top: 200px; transform: rotate(-3deg); }
.hero-mini span { font-size: 13px; }
.hero-mini:hover { transform: rotate(0) translateY(-2px); }
.hero-cap { margin-top: 30px; }

@media (max-width: 920px) {
  .hero { padding-top: 88px; }
  .hero-grid { grid-template-columns: 1fr; gap: 40px; }
  .hero-show { min-height: 340px; }
  /* The showcase drops to a single 520px column here, but the mini cards keep
     their -26px outdents, so they covered the featured card and hung off the
     left edge of the viewport. They belong to the two-column composition. */
  .hero-mini { display: none; }
}
@media (max-width: 520px) {
  .hero-show { min-height: 260px; }
}
@media (max-width: 560px) {
  /* flex-wrap doesn't know which item started a new line, so a wrapped
     3rd stat kept both its left border (":first-child" only ever matches
     the actual 1st DOM child, never "first on this row") and its 14px
     left padding, misaligned against the row above it - an orphaned
     divider floating at the start of line 2. Shrunk to fit all three
     stats on one line instead of trying to make a wrap look intentional. */
  .hero-stats { flex-wrap: nowrap; gap: 0; }
  .hstat { padding: 0 10px; gap: 5px; }
  .hstat .hn { font-size: 17px; }
  /* "in Discord" was landing a few px short of its own unwrapped width in
     that stat's box, dropping "Discord" onto its own line and doubling
     the row's height. Text doesn't shrink, so hold it to one line and let
     the row (which has slack - three stats only use ~330 of 375px here)
     absorb the difference instead. */
  .hstat .hl2 { white-space: nowrap; }
  .hstat .hl2 { font-size: 11px; }
}

.fc-cat-group { margin-bottom: 0; }
.fc-cat .fc-chev { transition: transform .2s var(--ease); color: var(--fg-3); }
.fc-cat-group.open > .fc-cat .fc-chev { transform: rotate(90deg); color: var(--fg-2); }
.fc-cat-group.open > .fc-cat { background: rgba(255,255,255,0.045); }
/* Opens on grid-template-rows rather than a max-height guess. The old cap was
   600px and the largest category already measured 466px, so a few more
   subcategories added from the admin panel would have silently clipped the
   list. It also means the open/close easing now matches the real height
   instead of racing to an arbitrary number. Needs the single .fc-subs-inner
   row that app.js wraps the items in. */
.fc-subs { display: grid; grid-template-rows: 0fr; margin: 3px 0 0 22px; padding-left: 12px;
  border-left: 1px solid var(--hairline); opacity: 0; overflow: hidden;
  transition: grid-template-rows .26s var(--ease), opacity .2s var(--ease), margin .26s var(--ease); }
.fc-subs[hidden] { display: grid; }
.fc-subs-inner { display: flex; flex-direction: column; gap: 2px; min-height: 0; }
.fc-cat-group.open .fc-subs { grid-template-rows: 1fr; opacity: 1; margin-bottom: 8px; }
@media (prefers-reduced-motion: reduce) { .fc-subs { transition: none; } }
.fc-sub { display: block; width: 100%; text-align: left; padding: 8px 12px; border: none; background: transparent;
  color: var(--fg-2); font-family: inherit; font-size: 13px; line-height: 1.35; cursor: pointer; border-radius: 10px;
  transition: background .15s var(--ease), color .15s var(--ease); }
.fc-sub:hover { background: rgba(255,255,255,0.05); color: var(--fg); }
.fc-sub.active { background: rgba(255,255,255,0.09); color: #fff; font-weight: 600; }
/* Promoted, not actionable-in-red: a single ember row sitting among neutral
   categories read as the selected one. Weight carries the emphasis instead,
   the same neutral-fill-plus-weight-step the active category uses. */
.fc-cat-resell { color: var(--fg); font-weight: 600; }

.nr-frame { position: relative; height: clamp(280px, 38vw, 420px); border-radius: 24px; overflow: hidden;
  border: 1px solid var(--hairline); background: #0f1014; }
.nr-frame .nr-track { display: flex; height: 100%; width: 100%; transition: transform .7s var(--ease); will-change: transform; }
.nr-frame .nr-slide { flex: 0 0 100%; height: 100%; position: relative; background-size: cover; background-position: center; }
.nr-frame .nr-slide::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(8,9,12,0) 45%, rgba(8,9,12,0.82)); }
.nr-cap { position: absolute; left: 26px; bottom: 24px; z-index: 1; display: flex; flex-direction: column; gap: 8px; }
.nr-chip { align-self: flex-start; font-size: 11px; font-weight: 700; letter-spacing: 0.03em; color: #fff; padding: 5px 12px;
  border-radius: 999px; background: var(--accent-deep); box-shadow: 0 8px 20px -8px var(--ink-85); }
.nr-title { font-size: var(--t-h4); font-weight: 800; letter-spacing: -0.02em; color: #fff; }
.nr-view { align-self: flex-start; margin-top: 4px; padding: 10px 20px; min-height: 40px; font-size: 13px; font-weight: 700;
  color: #fff; background: var(--accent-deep); border: none; border-radius: 10px;
  box-shadow: 0 10px 24px -12px var(--ink-75); }
.nr-view:hover { box-shadow: var(--e-2); }

/* The track slid, but nothing inside the frame moved, so the change read as a
   static image swap rather than a transition. The active slide now drifts a
   slow scale while its caption staggers in behind it.

   Both are transform/opacity only, so the compositor rule holds. Everything is
   scoped to html.js and the resting state is fully visible: without scripting
   the caption still reads, per the Visible-Without-JS rule. The blanket
   reduced-motion rule caps these durations at 0.01ms, so no separate opt-out
   is needed. Scale only ever grows from 1, never below, so the slide can never
   expose an edge inside the frame. */
@keyframes nrDrift { from { transform: scale(1); } to { transform: scale(1.07); } }
@keyframes nrCapIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* The drift scales a dedicated background layer, NOT the slide.
   Scaling .nr-slide scaled its children too, so the caption and the View
   product button crept across the frame for 7.2s along with the image - which
   read exactly like broken button positioning. Only the photograph should
   move. */
.nr-bg { position: absolute; inset: 0; background: center / cover no-repeat; transform-origin: center; }
html.js .nr-frame .nr-slide.is-active .nr-bg { animation: nrDrift 7.2s var(--ease-out) forwards; }
/* `backwards` holds the from-state during the delay, so a caption never flashes
   at full opacity for a frame before its stagger begins. */
html.js .nr-frame .nr-slide.is-active .nr-cap > * { animation: nrCapIn .62s var(--ease-out) backwards; }
html.js .nr-frame .nr-slide.is-active .nr-cap > :nth-child(1) { animation-delay: .10s; }
html.js .nr-frame .nr-slide.is-active .nr-cap > :nth-child(2) { animation-delay: .18s; }
html.js .nr-frame .nr-slide.is-active .nr-cap > :nth-child(3) { animation-delay: .26s; }
.featured-grid { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1000px) { .featured-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 460px) { .featured-grid { grid-template-columns: 1fr; } }

.bundle-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
@media (max-width: 720px) { .bundle-grid { grid-template-columns: 1fr; } }
.bundle-card { display: flex; flex-direction: column; border: 1px solid var(--hairline); border-radius: var(--r-sm);
  overflow: hidden; background: var(--bg-2); }
.bundle-thumb { height: 150px; position: relative; background-size: cover; background-position: center; }
.bundle-off { position: absolute; top: 10px; left: 10px; font-size: 12px; font-weight: 800; letter-spacing: 0.02em;
  color: #fff; background: var(--accent); padding: 3px 8px; border-radius: 6px; }
.bundle-body { padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.bundle-name { font-size: 16px; font-weight: 800; letter-spacing: -0.01em; }
.bundle-meta { font-size: 12.5px; color: var(--fg-2); line-height: 1.5; }
.bundle-price-row { display: flex; align-items: baseline; gap: 10px; margin-top: 2px; }
.bundle-price-row .p-price { font-size: 18px; font-weight: 800; color: var(--price); }
.bundle-add { margin-top: auto; width: 100%; justify-content: center; }

/* ==================== Admin dashboard ==================== */
/* Flat neutral grey, not the storefront's red banner glow - this is a
   staff tool, not a landing page, and the backdrop/scrim's red-tinted hero
   treatment (built for the homepage) reads as an odd, distracting choice
   on every dashboard table and form here. */
.adm-body { min-height: 100vh; min-height: 100dvh; background: var(--bg); }
.adm-body .backdrop, .adm-body .scrim { display: none; }
/* Dashboard content length varies with how much a given account owns
   (purchase count, wishlist, referrals) - the shared 940px scrim floor
   only fully resolves to solid --bg by 88% of that height (~827px), so a
   short account's footer could land inside the still-fading gradient and
   show the banner's red glow bleeding into it. Account pages read as
   clean graphite surfaces already (dashboard cards are solid --bg-2/
   --bg-3), so drop the photography atmosphere entirely here rather than
   trying to guess a height that works for every account. */
body.dash-body .backdrop, body.dash-body .scrim { display: none; }
.adm-gate { position: fixed; inset: 0; z-index: 500; display: flex; align-items: center; justify-content: center; padding: 24px; }
.adm-gate[hidden] { display: none; }
.adm-gate-card { max-width: 380px; padding: 40px 32px; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 12px; }
.adm-gate-card .logo { height: 30px; margin-bottom: 6px; }
.adm-gate-card h1 { font-size: 24px; font-weight: 800; }
.adm-gate-card p { color: var(--fg-2); font-size: 14px; line-height: 1.6; margin-bottom: 10px; }

.adm-select { font-family: inherit; font-size: 13px; color: var(--fg); background: var(--bg-3); border: 1px solid var(--hairline);
  border-radius: 10px; padding: 7px 10px; transition: border-color .15s; }
.adm-select:focus { outline: none; border-color: rgba(255,77,68,0.5); }

.adm-dd { position: relative; width: 100%; }
.adm-dd-inline { width: auto; flex: 0 0 auto; }
.adm-dd-inline .adm-dd-btn { min-width: 160px; }
.adm-dd-inline .adm-dd-val { white-space: nowrap; }
.adm-dd-inline .adm-dd-menu { left: 0; right: auto; width: max-content; min-width: 100%; max-width: 260px; }
.adm-dd-inline .adm-dd-opt span:first-child { white-space: nowrap; }
.adm-staff-role-dd .adm-dd-btn { min-width: 110px; padding: 7px 12px; text-transform: capitalize; }
.adm-staff-role-dd .adm-dd-opt { text-transform: capitalize; }
.adm-staff-role-dd .adm-dd-btn:disabled { opacity: 0.5; cursor: default; }
.adm-dd-btn { display: flex; align-items: center; justify-content: space-between; gap: 9px; width: 100%;
  padding: 9px 12px; border-radius: 10px; background: var(--bg-3); border: 1px solid var(--hairline); color: var(--fg);
  font-family: inherit; font-size: 13px; font-weight: 600; cursor: pointer; transition: border-color .15s var(--ease); }
.adm-dd-btn:hover { border-color: rgba(255,255,255,0.2); }
.adm-dd.open .adm-dd-btn { border-color: rgba(255,77,68,0.5); }
.adm-dd-val { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.adm-dd-val.placeholder { color: var(--fg-3); font-weight: 500; }
.adm-dd-chev { color: var(--fg-3); transition: transform .18s var(--ease); flex: 0 0 auto; }
.adm-dd.open .adm-dd-chev { transform: rotate(180deg); }
.adm-dd-menu { position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 40; max-height: 280px; overflow-y: auto;
  padding: 6px; border-radius: 16px; background: var(--bg-2); border: 1px solid var(--hairline);
  box-shadow: 0 20px 46px -18px var(--ink-75); }
.adm-dd-menu[hidden] { display: none; }
.adm-dd-opt { display: flex; align-items: center; justify-content: space-between; gap: 14px; width: 100%;
  padding: 10px 12px; border-radius: 10px; background: transparent; border: none; color: var(--fg-1);
  font-family: inherit; font-size: 13px; font-weight: 600; text-align: left; cursor: pointer;
  transition: background .16s var(--ease), color .16s var(--ease); }
.adm-dd-opt:hover { background: rgba(255,255,255,0.06); color: var(--fg); }
.adm-dd-opt.active { background: rgba(255,255,255,0.06); color: #fff; }
.adm-dd-radio { width: 16px; height: 16px; border-radius: 50%; border: 2px solid var(--hairline); flex: 0 0 auto; position: relative; }
.adm-dd-opt.active .adm-dd-radio { border-color: var(--accent); }
.adm-dd-opt.active .adm-dd-radio::after { content: ""; position: absolute; inset: 3px; border-radius: 50%; background: var(--accent); }
/* Multi-select variant (product picker in the grant form) - square instead
   of round, otherwise identical states, so it reads as "checkbox" not
   "radio" without a second set of active/checked styles to maintain. */
.adm-dd-check { border-radius: var(--r-xs); }
.adm-dd-opt.active .adm-dd-check::after { border-radius: 3px; inset: 3px; }
/* Filter input injected at the top of a searchable dropdown's menu (see
   makeDropdown's opts.searchable) - a long, unsorted user/product list was
   flagged as unusable to scroll through by hand. */
.adm-dd-search { width: 100%; margin-bottom: 6px; padding: 9px 12px; font-size: 13px; }
.adm-dd-opt[hidden] { display: none; }
/* Per-row kebab action menu (order rows: view details / refund / revoke
   license / mark completed) - same open/close-on-outside-click pattern as
   .adm-staff-role-dd, just lighter since each row needs its own instance
   rather than one shared component. */
.adm-row-menu { position: relative; display: inline-block; }
.adm-row-menu-btn { width: 30px; height: 30px; border-radius: 8px; background: transparent; border: 1px solid transparent;
  color: var(--fg-2); cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s, border-color .15s, color .15s; }
.adm-row-menu-btn:hover, .adm-row-menu.open .adm-row-menu-btn { background: rgba(255,255,255,0.06); border-color: var(--hairline); color: var(--fg); }
.adm-row-menu-list { position: absolute; top: calc(100% + 6px); right: 0; z-index: 45; min-width: 180px;
  padding: 6px; border-radius: 12px; background: var(--bg-2); border: 1px solid var(--hairline);
  box-shadow: 0 20px 46px -18px var(--ink-75); }
.adm-row-menu-list[hidden] { display: none; }
.adm-row-menu-item { display: flex; align-items: center; width: 100%; padding: 9px 10px; border-radius: 8px;
  background: transparent; border: none; color: var(--fg-1); font-family: inherit; font-size: 13px; font-weight: 600;
  text-align: left; cursor: pointer; transition: background .15s, color .15s; }
.adm-row-menu-item:hover { background: rgba(255,255,255,0.06); color: var(--fg); }
.adm-row-menu-item.danger { color: #ff6b6b; }
.adm-row-menu-item.danger:hover { background: rgba(255,77,68,0.12); }
.adm-row-menu-item:disabled { opacity: 0.4; cursor: default; }
.adm-row-menu-item:disabled:hover { background: transparent; color: var(--fg-1); }

.adm-page { padding-top: 110px; padding-bottom: 80px; }
.adm-page .dash { grid-template-columns: 220px 1fr; }

.adm-head-row { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.adm-range { display: inline-flex; gap: 4px; padding: 4px; border-radius: 10px; background: var(--bg-3); border: 1px solid var(--hairline); }
.adm-range button { font-family: inherit; font-size: 12px; font-weight: 600; color: var(--fg-2); background: none; border: none;
  padding: 7px 12px; border-radius: 10px; cursor: pointer; transition: background .15s, color .15s; }
.adm-range button:hover { color: var(--fg); }
.adm-range button.active { background: var(--accent); color: #fff; }

/* Segmented tab control - same visual language as .adm-range, generalized.
   Replaces the old pattern of reusing storefront CTA colors (.btn-primary /
   .btn-minecraft, a red button next to a green "Minecraft brand" button) as
   a view switcher for things that have nothing to do with platform choice
   or checkout urgency - Site Access/Roblox/Staff, Sale Events/Discount
   Codes, Blog Posts/Tutorials/Releases, All/Released/Unreleased, Open/
   Maintenance/Locked. .btn-minecraft stays exactly as it was for the one
   place it's a real brand choice (product edit's Roblox/Minecraft platform
   picker) - this only replaces its use as a tab. */
.adm-tabs { display: inline-flex; gap: 4px; padding: 4px; border-radius: 12px;
  background: var(--bg-3); border: 1px solid var(--hairline); flex-wrap: wrap; }
.adm-tab { display: inline-flex; align-items: center; gap: 7px; font-family: inherit; font-size: 13px;
  font-weight: 600; color: var(--fg-2); background: none; border: none;
  padding: 9px 16px; border-radius: 9px; cursor: pointer; transition: background .15s, color .15s; }
.adm-tab:hover { color: var(--fg); background: rgba(255,255,255,0.05); }
.adm-tab.active { background: var(--accent); color: #fff; }

.adm-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
.adm-two-col .dash-card { margin-bottom: 0; }
.adm-stats-2 { grid-template-columns: repeat(2, 1fr); }
.adm-stats-3 { grid-template-columns: repeat(3, 1fr); }

.adm-chart { display: block; width: 100%; height: 140px; cursor: crosshair; }
.adm-chart-bar { transition: opacity .1s ease; cursor: pointer; }
.adm-chart-bar.hover { opacity: 1 !important; stroke: var(--fg-1); stroke-width: 0.6; }
.adm-chart-tip { position: fixed; z-index: 9999; background: var(--bg-2); border: 1px solid var(--hairline); color: var(--fg-1); font-size: 12px; padding: 6px 10px; border-radius: 4px; pointer-events: none; white-space: nowrap; box-shadow: 0 6px 20px var(--ink-40); }

/* Donut / pie */
.adm-donut-wrap { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; margin-top: 10px; }
.adm-donut { flex: 0 0 auto; width: 168px; height: 168px; overflow: visible; }
.adm-donut-seg { cursor: pointer; transition: opacity .12s ease; }
.adm-donut:hover .adm-donut-seg { opacity: .4; }
.adm-donut:hover .adm-donut-seg.hover { opacity: 1; }
.adm-donut-c1 { text-anchor: middle; dominant-baseline: middle; font-size: 15px; font-weight: 700; fill: var(--fg-1); font-variant-numeric: tabular-nums; }
.adm-donut-c2 { text-anchor: middle; dominant-baseline: middle; font-size: 9px; letter-spacing: .06em; text-transform: uppercase; fill: var(--fg-3); }
.adm-donut-key { display: flex; flex-direction: column; gap: 8px; font-size: 13px; min-width: 190px; flex: 1 1 190px; }
.adm-donut-key > span { display: grid; grid-template-columns: 11px 1fr auto auto; gap: 9px; align-items: center; }
.adm-donut-key i { width: 11px; height: 11px; border-radius: 3px; }
.adm-donut-key .adm-donut-lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fg-2); }
.adm-donut-key b { font-weight: 600; font-variant-numeric: tabular-nums; }
.adm-donut-key em { font-style: normal; color: var(--fg-3); font-variant-numeric: tabular-nums; min-width: 34px; text-align: right; }

/* Sparkline hover points */
.adm-spark-pt { fill: transparent; cursor: pointer; }
.adm-spark-pt.hover { fill: var(--hairline); }

/* Horizontal label | bar | value rows */
.adm-hbars { display: flex; flex-direction: column; gap: 9px; margin-top: 10px; }
.adm-hbar { display: grid; grid-template-columns: 92px 1fr auto; gap: 12px; align-items: center; font-size: 13px; cursor: default; }
.adm-hbar-lbl { color: var(--fg-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.adm-hbar-track { height: 10px; background: var(--hairline); border-radius: 5px; overflow: hidden; }
.adm-hbar-fill { display: block; height: 100%; border-radius: 5px; transition: width .35s ease, filter .12s ease; }
.adm-hbar.hover .adm-hbar-fill { filter: brightness(1.25); }
.adm-hbar-val { font-variant-numeric: tabular-nums; font-weight: 600; }

/* Cards laid out two-up need their own bottom gap - the child cards zero
   theirs out, and the grid itself had none, so the next full-width card
   sat flush against it. */
.adm-two-col { margin-bottom: 16px; }
/* Marketing channel rows. Replaced .adm-platform-row, whose "Revenue by
   platform" table listed four marketplaces that were all "Not tracked yet". */
.adm-channel-row { display: flex; justify-content: space-between; align-items: center; gap: 16px;
  padding: 13px 0; border-top: 1px solid var(--hairline); font-size: 13px; }
.adm-channel-row:first-child { border-top: 0; }
.adm-channel-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.adm-channel-name { color: var(--fg-1); font-weight: 600; }
.adm-channel-val { color: var(--fg); font-weight: 700; margin-left: auto; }
.adm-channel-trail { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.adm-channel-collapse > summary.adm-channel-row { padding-bottom: 13px; }
.adm-channel-collapse .adm-collapse-body { margin-top: 0; padding-bottom: 13px; max-height: none; overflow: visible; }
.adm-channel-collapse .adm-collapse-body .dash-stats { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
.adm-channel-collapse .adm-collapse-body .dash-stats.adm-grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 760px) { .adm-channel-collapse .adm-collapse-body .dash-stats.adm-grid-4 { grid-template-columns: repeat(2, 1fr); } }
.adm-social-sub { display: flex; align-items: baseline; gap: 8px; margin: 16px 0 8px; font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--fg-2); }
.adm-social-sub .adm-sub { font-weight: 500; letter-spacing: 0; text-transform: none; }
.adm-spark-card { margin-top: 12px; padding: 10px 12px; border: 1px solid var(--hairline); border-radius: 10px; background: rgba(255,255,255,0.02); }
.adm-spark-head { display: flex; justify-content: space-between; align-items: baseline; font-size: 12px; font-weight: 600; color: var(--fg-2); margin-bottom: 4px; }
.adm-spark { display: block; width: 100%; height: 90px; }
.adm-mini-head { margin: 18px 0 8px; font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--fg-2); }
.adm-mixbar { display: flex; height: 14px; border-radius: 7px; overflow: hidden; background: var(--surface-2, rgba(255,255,255,0.04)); }
.adm-mixseg { display: block; min-width: 2px; transition: width .3s ease; }
.adm-mixkey { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 8px; font-size: 12px; color: var(--fg-2); }
.adm-mixkey span { display: inline-flex; align-items: center; gap: 6px; }
.adm-mixkey i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
.adm-vidtable td:first-child { max-width: 240px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.adm-vidtable td { font-variant-numeric: tabular-nums; }
#admCouponTotals { margin-bottom: 16px; }
.adm-email-info {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; margin-right: 9px; vertical-align: middle;
  border: 1px solid var(--hairline); border-radius: 6px; background: transparent;
  color: var(--fg-3); cursor: pointer; flex: 0 0 auto;
}
.adm-email-info:hover { color: var(--accent); border-color: var(--accent); }
.adm-email-addr { vertical-align: middle; }
.adm-kv-list { display: flex; flex-direction: column; gap: 2px; margin: 4px 0 6px; }
.adm-kv { display: grid; grid-template-columns: 140px 1fr; gap: 14px; padding: 8px 0; border-top: 1px solid var(--hairline); font-size: 13px; }
.adm-kv:first-child { border-top: 0; }
.adm-kv > span:first-child { color: var(--fg-3); }
.adm-campaign-url { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 11px; word-break: break-all; }
.adm-catlist { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
.adm-catrow { display: flex; justify-content: space-between; font-size: 13px; padding: 6px 0; border-top: 1px solid var(--hairline); }
.adm-catrow:first-child { border-top: 0; }


.adm-sub { color: var(--fg-3); font-size: 12px; text-decoration: none; }
.adm-sub-muted { color: var(--fg-3); font-style: italic; opacity: 0.8; }
/* "No file attached" is a state that costs a customer a real download, so it
   is not allowed to look like ordinary help text. */
.adm-note-warn { color: var(--warn); font-weight: 600; }
a.adm-sub[href] { color: var(--fg-1); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.adm-note { color: var(--fg-3); font-size: 12px; line-height: 1.55; margin-top: 12px; }

/* Order detail page - label/value rows (Overview, Payment reference). */
.adm-detail-row { display: flex; align-items: baseline; gap: 14px; padding: 10px 0; border-top: 1px solid var(--hairline); font-size: 13px; }
.adm-detail-row:first-child { border-top: 0; }
.adm-detail-label { flex: 0 0 170px; color: var(--fg-3); }
.adm-detail-val { color: var(--fg); word-break: break-word; }

/* Audit log persistence warning. A log that quietly stops saving is worse
   than no log, so this is warn-weight, not a footnote. */
.adm-audit-warn { margin-bottom: 16px; padding: 14px 16px; border-radius: var(--r-sm);
  background: var(--warn-tint); border: 1px solid var(--warn-line);
  color: var(--fg-1); font-size: 13px; line-height: 1.6; }
.adm-audit-warn[hidden] { display: none; }
.adm-audit-warn strong { color: var(--warn); }
.adm-audit-warn code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px; padding: 1px 5px; border-radius: var(--r-xs); background: rgba(255,255,255,0.08); }
.adm-empty { color: var(--fg-3); font-size: 13px; padding: 18px 0; text-align: center; }

/* Client-side error reports (supabase/client_errors.sql) rendered inline
   in the audit log feed - see admin.js refreshAuditLog(). A distinct
   warm tint on the row and a monospace code badge (in place of a staff
   name, which errors don't have) keep them visually separate from staff
   actions at a glance without needing a second table. */
.adm-audit-err-row { background: rgba(255,77,68,0.05); }
.adm-err-code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; font-weight: 700;
  letter-spacing: 0.02em; color: var(--accent-ink); background: rgba(255,77,68,0.12); border: 1px solid rgba(255,77,68,0.28);
  border-radius: var(--r-ctl); padding: 3px 8px; white-space: nowrap; }
.adm-err-details-btn { font-family: inherit; font-size: 12px; font-weight: 600; color: var(--fg-2); cursor: pointer;
  background: rgba(255,255,255,0.06); border: 1px solid var(--hairline); border-radius: var(--r-ctl); padding: 4px 10px;
  margin-left: 6px; transition: background .18s, color .18s; }
.adm-err-details-btn:hover { background: rgba(255,255,255,0.11); color: var(--fg); }
.adm-err-detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 18px; }
.adm-err-detail-label { display: block; font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--fg-3); margin-bottom: 4px; }
.adm-err-detail-val { font-size: 14px; color: var(--fg-1); line-height: 1.5; word-break: break-word; margin: 0; }
.adm-err-pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; line-height: 1.6;
  color: var(--fg-2); background: var(--ink-25); border: 1px solid var(--hairline); border-radius: var(--r-ctl);
  padding: 12px 14px; max-height: 220px; overflow: auto; white-space: pre-wrap; word-break: break-word; margin: 0 0 16px; }
@media (max-width: 560px) { .adm-err-detail-grid { grid-template-columns: 1fr; } }
/* Account details has ~5x the fields the error-report modal (this grid's
   other user) ever shows - at the shared 2-column layout that ran 8 rows
   tall in a 640px-wide box, closer to a scroll than a "square" popup. 3
   columns at a wider modal cuts it to 5 rows without touching the error
   modal's own layout. min-width guard keeps mobile on the shared 1-column
   fallback above instead of fighting it on specificity. */
@media (min-width: 561px) {
  #admUserDetailOverlay .adm-err-detail-grid { grid-template-columns: 1fr 1fr 1fr; }
}
.adm-ud-section { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--hairline); }
.adm-ud-orders { display: flex; flex-direction: column; gap: 8px; max-height: 260px; overflow-y: auto; margin-top: 10px; }
.adm-ud-order-row { display: flex; align-items: center; gap: 12px; padding: 10px 12px;
  border: 1px solid var(--hairline); border-radius: var(--r-ctl); background: var(--ink-25); }
.adm-ud-order-date { flex: 0 0 92px; font-size: 12px; color: var(--fg-3); }
.adm-ud-order-title { flex: 1 1 auto; min-width: 0; font-size: 13px; color: var(--fg-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.adm-ud-order-total { flex: 0 0 auto; font-size: 13px; font-weight: 600; color: var(--fg-1); }
.adm-ud-cart-row { display: flex; align-items: center; gap: 14px; padding: 12px 14px;
  border: 1px solid var(--hairline); border-radius: var(--r-ctl); background: var(--ink-25); margin-top: 10px; }
.adm-ud-cart-row img { width: 40px; height: 40px; border-radius: 8px; object-fit: cover; flex: 0 0 auto; background: var(--bg-3); }
.adm-ud-cart-info { flex: 1 1 auto; min-width: 0; }
.adm-ud-cart-title { font-size: 13px; color: var(--fg-1); }
.adm-ud-cart-meta { font-size: 12px; color: var(--fg-3); margin-top: 2px; }
.adm-ud-cart-value { font-size: 13px; font-weight: 600; color: var(--fg-1); }
.adm-rank { flex: 0 0 auto; width: 26px; color: var(--fg-3); font-weight: 700; font-size: 13px; }

/* align-items defaults to stretch on a flex row - .adm-tabs (a padded pill
   wrapping its own flex children) and plain .adm-input/.adm-dd-btn/.btn
   siblings don't share a height without it, which is what made the filter
   tabs look vertically misaligned next to the search box. */
.adm-toolbar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.adm-input { font-family: inherit; font-size: 13px; color: var(--fg); background: var(--bg-3); border: 1px solid var(--hairline);
  border-radius: 10px; padding: 9px 13px; min-width: 200px; transition: border-color .15s; }
.adm-input-sm { min-width: 120px; }
.adm-input::placeholder { color: var(--fg-3); }
.adm-input:focus { outline: none; border-color: rgba(255,77,68,0.5); }
.adm-input[readonly] { color: var(--fg-2); background: rgba(255,255,255,0.02); cursor: default; }
.adm-input[readonly]:focus { border-color: var(--hairline); }

/* flex-end, not center: a plain .adm-input is a single box, but .adm-field
   (used for the "Expires" field here) stacks a label above its input,
   making it taller than its siblings. Centering on cross-axis centered
   each item on its OWN box height, so the .adm-field's actual input sat
   visibly lower than the plain inputs beside it in the same row -
   aligning by the bottom edge instead lines every real input up, labeled
   or not. */
.adm-form { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
/* A card-head's 4px margin-bottom is fine when a table or note paragraph
   sits between it and the next element (their own visual weight reads as
   the gap), but a form's controls have no such buffer of their own - the
   title read as crammed right on top of them. Only fires when nothing
   else already separates the two. */
.dash-card-head + .adm-form { margin-top: 12px; }
.adm-btn-sm { padding: 8px 13px; font-size: 12px; min-height: 0; }

.adm-table-tight td, .adm-table-tight th { white-space: nowrap; }
/* .adm-row-actions is applied directly to a <td> in several admin
   tables. display:flex on a table cell drops the browser's normal
   vertical-align:middle centering for that cell, so its buttons were
   sitting flush at the top of the row instead of centered like every
   other column's content - align-items:center restores that. */
.adm-row-actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; height: 100%; box-sizing: border-box; }
.adm-price-input { width: 84px; font-family: inherit; font-size: 13px; color: var(--fg); background: var(--bg-3);
  border: 1px solid var(--hairline); border-radius: 10px; padding: 6px 8px; transition: border-color .15s; }
.adm-price-input:focus { outline: none; border-color: rgba(255,77,68,0.5); }

.adm-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px;
  border-radius: 10px; background: rgba(255,255,255,0.04); border: 1px solid var(--hairline); color: var(--fg-2);
  cursor: pointer; transition: color .15s, background .15s, border-color .15s; }
.adm-icon-btn:hover { color: var(--fg); background: rgba(255,255,255,0.09); border-color: rgba(255,255,255,0.18); }
.adm-cat-tag { display: inline-block; font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 999px;
  color: var(--info); background: var(--info-tint); border: 1px solid rgba(70,140,255,0.28); white-space: nowrap; }
.adm-star { color: var(--warn); margin-left: 2px; font-size: 12px; }

/* Resellers panel - the flat table read as an afterthought next to the
   rest of admin; this is the same .dash-row list the Orders panel uses,
   made person-centric (monogram avatar) with the seller's storefront
   links as chips and a clear "needs info" treatment. */
.adm-reseller-row { align-items: flex-start; gap: 13px; cursor: pointer; padding-left: 8px; padding-right: 8px; margin: 0 -8px;
  border-radius: 10px; transition: background .14s var(--ease); }
.adm-reseller-row:hover { background: rgba(255,255,255,0.03); }
html[data-theme="light"] .adm-reseller-row:hover { background: rgba(0,0,0,0.03); }
.adm-reseller-row .dr-title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.adm-reseller-row .dr-sub { margin-top: 3px; }
.adm-reseller-row .dr-prod { margin-top: 4px; font-size: 13px; font-weight: 600; color: var(--fg-1); }
.adm-reseller-av { flex: 0 0 42px; width: 42px; height: 42px; margin-top: 1px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700;
  letter-spacing: 0.02em; background: var(--bg-3); color: var(--fg-2);
  box-shadow: inset 0 0 0 1px var(--hairline); }
.adm-reseller-av.needs-info { color: var(--warn); background: var(--warn-tint); box-shadow: inset 0 0 0 1px var(--warn-line); }
.adm-reseller-locs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.adm-reseller-loc { display: inline-flex; align-items: center; gap: 5px; max-width: 240px;
  font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 999px;
  background: var(--bg-3); border: 1px solid var(--hairline); color: var(--fg-2);
  text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: color .15s var(--ease), border-color .15s var(--ease); }
.adm-reseller-loc:hover { color: var(--fg); border-color: var(--hairline-strong); }
.adm-reseller-loc svg { flex: 0 0 auto; opacity: 0.5; }
.adm-reseller-loc.as-text, .adm-reseller-loc.as-text:hover { cursor: default; color: var(--fg-3); border-color: var(--hairline); }
@media (max-width: 620px) { .adm-reseller-row .dr-actions { flex: 0 0 auto; margin-top: 0; } }
/* Row title link - plain foreground text, not the shared .dt-link's ember.
   Ember is for primary actions/back-nav, not every row title in a table. */
.adm-prod-name { color: var(--fg); text-decoration: none; font-weight: 600; }
.adm-prod-name:hover { color: var(--fg); text-decoration: underline; text-underline-offset: 2px; }

.adm-edit-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.adm-field { display: flex; flex-direction: column; gap: 7px; font-size: 13px; color: var(--fg-2); }
.adm-field-wide { grid-column: 1 / -1; }
.adm-field-label { display: block; font-size: 13px; color: var(--fg-2); }
.adm-textarea { resize: vertical; font-family: inherit; }
.adm-field-check { display: flex; align-items: center; gap: 9px; font-size: 13px; color: var(--fg-1); margin-top: 16px; cursor: pointer; }
.adm-file-row { display: flex; align-items: center; gap: 12px; margin-top: 6px; flex-wrap: wrap; }
.adm-edit-actions { display: flex; align-items: center; gap: 12px; margin-top: 22px; }
#admEditDeleteBtn[hidden] { display: none; }
#admEventCancelBtn[hidden], #admCouponCancelBtn[hidden] { display: none; }
.adm-edit-msg { font-size: 13px; color: var(--price); margin-top: 10px; min-height: 1em; }

/* Email campaign HTML preview. White background is deliberate here, not a
   theme break - it simulates the actual inbox surface a recipient will see,
   which is white in nearly every mail client regardless of coldd's own
   dark system. sandbox="" (no tokens) blocks scripts, forms and same-origin
   access - admin-authored HTML still renders inert. */
.adm-campaign-preview { margin-top: -2px; border: 1px solid var(--hairline); border-radius: var(--r-sm); overflow: hidden; background: var(--bg-3); }
.adm-campaign-preview-head { padding: 9px 14px; font-size: 12px; font-weight: 600; color: var(--fg-2); border-bottom: 1px solid var(--hairline); }
.adm-campaign-preview-frame { display: block; width: 100%; height: 320px; border: none; background: #fff; }
.adm-hint { font-size: 12px; color: var(--fg-3); }

.adm-file-list { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.adm-file-item { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-radius: 10px;
  background: rgba(255,255,255,0.04); border: 1px solid var(--hairline); font-size: 13px; }
.adm-file-item span, .adm-file-item a { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.adm-file-item a { color: var(--fg-1); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
.adm-file-item a:hover { color: var(--fg); }
.adm-file-item button { flex: 0 0 auto; }
.adm-file-item .adm-file-meta { flex: 0 0 auto; color: var(--fg-3); font-size: 12px; }
.adm-file-rename { flex: 1; min-width: 0; background: transparent; border: 1px solid transparent; border-radius: 6px;
  color: var(--fg-1); font: inherit; font-size: 13px; padding: 3px 6px; margin: -3px -6px; transition: border-color .15s, background .15s; }
.adm-file-rename:hover { border-color: var(--hairline); }
.adm-file-rename:focus { outline: none; border-color: var(--accent); background: rgba(255,255,255,0.04); }

.adm-contact-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.adm-contact-row { display: flex; gap: 8px; align-items: center; }
.adm-contact-row .adm-input { flex: 1; min-width: 0; }

.adm-dropzone { position: relative; border: 2px dashed var(--hairline); border-radius: 16px; padding: 28px 20px; margin-top: 6px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center;
  cursor: pointer; transition: border-color .15s, background .15s; background: rgba(255,255,255,0.02); }
.adm-dropzone:hover { border-color: rgba(255,255,255,0.28); }
.adm-dropzone.dragover { border-color: var(--accent); background: rgba(255,77,68,0.07); }
.adm-dropzone-sm { padding: 18px; }
.adm-dropzone-ic { color: var(--fg-3); margin-bottom: 10px; }
.adm-dropzone p { margin: 0; font-size: 13px; color: var(--fg-2); }
.adm-dropzone p strong { color: var(--fg); }
.adm-dropzone-hint { font-size: 12px; color: var(--fg-3) !important; margin-top: 4px !important; }
.adm-dropzone-preview { position: relative; width: 100%; display: flex; justify-content: center; }
.adm-dropzone-preview[hidden] { display: none; }
.adm-dropzone-preview img { max-width: 100%; max-height: 160px; object-fit: contain; border-radius: 10px; display: block; cursor: pointer; }
.adm-dropzone-remove { position: absolute; top: -8px; right: calc(50% - 100px); background: var(--bg-2); border: 1px solid var(--hairline); border-radius: 50%; }

.adm-gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 10px; margin-top: 12px; }
.adm-gallery-item { position: relative; aspect-ratio: 4 / 3; border-radius: 10px; overflow: hidden; border: 1px solid var(--hairline); background: var(--bg-3); }
.adm-gallery-item a { display: block; width: 100%; height: 100%; }
.adm-gallery-item img { width: 100%; height: 100%; object-fit: cover; display: block; cursor: pointer; }
.adm-gallery-item button { position: absolute; top: 4px; right: 4px; width: 22px; height: 22px; padding: 0;
  display: flex; align-items: center; justify-content: center; background: var(--ink-60); border: none; border-radius: 50%; color: #fff; cursor: pointer; }

.adm-cost-toggle { display: flex; gap: 6px; flex: 0 0 auto; }
.adm-cost-toggle button { font-family: inherit; font-size: 12px; font-weight: 600; padding: 0 12px; border-radius: 10px;
  background: var(--bg-3); border: 1px solid var(--hairline); color: var(--fg-2); cursor: pointer; transition: background .15s, color .15s, border-color .15s; }
.adm-cost-toggle button.active { background: var(--accent); color: #fff; border-color: var(--accent); }

.adm-upd-results { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; max-height: 260px; overflow-y: auto; }
.adm-upd-result { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 10px;
  background: transparent; border: 1px solid transparent; color: var(--fg-1); font-family: inherit; font-size: 13px;
  cursor: pointer; text-align: left; width: 100%; }
.adm-upd-result:hover { background: rgba(255,255,255,0.05); border-color: var(--hairline); }
.adm-upd-selected-card { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: 16px;
  background: rgba(255,255,255,0.04); border: 1px solid var(--hairline); }
.adm-upd-selected-card .dr-thumb { width: 52px; height: 38px; border-radius: 10px; flex: 0 0 auto; }
.adm-upd-selected-card span:nth-child(2) { flex: 1; font-weight: 600; }
.adm-upd-change { background: none; border: none; cursor: pointer; font-family: inherit; }
.adm-upd-history { margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--hairline); }
.adm-ver-item { display: flex; align-items: baseline; gap: 10px; padding: 8px 0; font-size: 13px; }
.adm-ver-item .adm-ver-num { font-weight: 700; color: var(--fg); flex: 0 0 auto; }
.adm-ver-item .adm-ver-date { color: var(--fg-3); flex: 0 0 auto; }
.adm-ver-item .adm-ver-note { color: var(--fg-2); }

.adm-review { padding: 18px 20px; }
.adm-review-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.adm-review-text { font-size: 13px; color: var(--fg-1); line-height: 1.55; margin: 8px 0 12px; }
.adm-review-reply { background: rgba(255,77,68,0.07); border: 1px solid rgba(255,77,68,0.18); border-radius: 10px; padding: 10px 14px; margin: 0 0 12px; }
.adm-review-reply strong { font-size: 12px; color: var(--accent); text-transform: uppercase; letter-spacing: .02em; }
.adm-review-reply p { font-size: 13px; color: var(--fg-1); margin: 4px 0 0; line-height: 1.5; }
.adm-review-reply-form { display: flex; flex-direction: column; gap: 8px; margin: 0 0 12px; align-items: flex-start; }
.adm-review-reply-form[hidden] { display: none; }
.adm-review-reply-form textarea { width: 100%; }

@media (max-width: 900px) {
  .adm-page .dash { grid-template-columns: 1fr; }
  .adm-two-col { grid-template-columns: 1fr; }
  .adm-stats-3 { grid-template-columns: repeat(2, 1fr); }
}

.legal-wrap { max-width: 820px; margin: 0 auto; }
/* Solid, non-transparent background for legal pages (Terms, Privacy,
   Refund Policy, Resell License) - the default .glass card is intentionally
   translucent over the page backdrop/glow effects, which drops text
   contrast below comfortable reading levels on dense legal copy. */
.legal-card { padding: 50px 56px; background: var(--bg-2); }
.legal-card::before { display: none; }
.legal-meta { color: var(--fg-3); font-size: 13px; margin: 0 0 30px; }
.legal-card h2 { font-size: 20px; font-weight: 700; letter-spacing: -0.02em; margin: 34px 0 12px; }
.legal-card > h2:first-of-type, .legal-card > p:first-of-type { margin-top: 0; }
.legal-card p { color: var(--fg-2); font-size: 15px; line-height: 1.75; margin: 0 0 16px; }
.legal-card ul, .legal-card ol { color: var(--fg-2); font-size: 15px; line-height: 1.75; margin: 0 0 16px; padding-left: 22px; }
.legal-card li { margin-bottom: 8px; }
.legal-card li:last-child { margin-bottom: 0; }
.legal-card strong { color: var(--fg); font-weight: 600; }
.legal-card a { color: var(--accent-ink); font-weight: 700; text-decoration: none; }
.legal-card a:hover { text-decoration: underline; text-underline-offset: 2px; }
.legal-card hr { border: none; border-top: 1px solid var(--hairline); margin: 30px 0; }
.legal-note { border: 1px solid var(--hairline); border-radius: var(--r-sm); padding: 18px 20px;
  background: var(--glass-top); font-size: 14px; color: var(--fg-2); line-height: 1.7; margin: 0 0 28px; }
.legal-note strong { color: var(--fg); }
.legal-card .legal-sig { color: var(--fg-3); font-size: 14px; font-style: italic; margin: 26px 0 16px; }
.legal-card h3 { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; margin: 22px 0 12px; color: var(--fg); }
.legal-card h2:first-child, .legal-card h2:first-of-type { margin-top: 0; }
/* Related-document cluster ------------------------------------------------
   Closes the legal pages and the FAQ. These documents constantly refer to one
   another, so the trail between them should not run through the footer. */
.legal-more { margin-top: 38px; padding-top: 28px; border-top: 1px solid var(--hairline); }
.legal-more h2 { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; margin: 0 0 14px; color: var(--fg); }
.rel-link-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.rel-link { display: flex; flex-direction: column; gap: 4px; padding: 15px 17px;
  border-radius: var(--r-sm); border: 1px solid var(--hairline);
  background: rgba(255,255,255,0.03); text-decoration: none;
  transition: background .18s var(--ease-out), border-color .18s var(--ease-out); }
.rel-link:hover { background: rgba(255,255,255,0.06); border-color: var(--hairline-strong); }
.rel-link-t { font-size: 14px; font-weight: 700; color: var(--fg); line-height: 1.35; }
.rel-link-d { font-size: 12.5px; color: var(--fg-3); line-height: 1.5; }
/* .legal-card paints every descendant link accent-red and underlines it on
   hover. These are cards, not inline references, so opt them out. */
.legal-card .rel-link, .legal-card .rel-link:hover { color: inherit; text-decoration: none; }
.legal-card .legal-more h2 { margin-top: 0; }

@media (max-width: 620px) {
  .rel-link-grid { grid-template-columns: 1fr; }
}

.legal-badge { display: inline-block; padding: 3px 9px; border-radius: 4px; background: var(--bg-3);
  border: 1px solid var(--hairline); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; color: var(--fg); }
.legal-sub { border: 1px solid var(--hairline); border-radius: var(--r-sm); padding: 6px 22px 18px; margin: 0 0 22px; background: rgba(255,255,255,0.015); }
.legal-sub:last-child { margin-bottom: 0; }
.legal-clause { border-left: 1px solid var(--hairline); padding: 1px 0 1px 16px; margin: 16px 0; }
/* Clause numbers were separated from their text by an em-dash, which is
   punctuation doing layout's job. A real label sets them instead. */
.lc-num { display: inline-block; min-width: 62px; margin-right: 10px; color: var(--fg);
  font-weight: 700; font-variant-numeric: tabular-nums; }
.legal-clause:last-child { margin-bottom: 0; }
.legal-clause p { margin: 0; }
.legal-clause strong { color: var(--fg); }
.legal-btn-link { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 600; color: var(--fg);
  background: var(--glass-top); border: 1px solid var(--hairline); border-radius: 10px; padding: 10px 16px;
  text-decoration: none; margin: 4px 0 4px; }
.legal-btn-link:hover { border-color: rgba(255,77,68,0.5); text-decoration: none; }

@media (max-width: 640px) {
  .legal-card { padding: 32px 24px; }
  .legal-sub { padding: 6px 16px 16px; }
}

/* ================================================================
   ADMIN: blog/tutorial/release form additions
   ================================================================ */
.adm-textarea { width: 100%; flex: 1 1 100%; resize: vertical; font-family: inherit; line-height: 1.5; }
.adm-check { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--fg-2); }

/* ================================================================
   BLOG LISTING
   ================================================================ */
.blog-page .page-head { padding-bottom: 34px; }
.blog-layout { display: grid; grid-template-columns: 172px minmax(0, 1fr); gap: 36px; max-width: 1120px; margin: 0 auto; }
.blog-side { align-self: start; position: sticky; top: 96px; border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); }
.blog-filter-head { padding: 15px 0 12px; color: var(--fg-2); font-size: 13px; font-weight: 800; letter-spacing: -0.01em; }
.blog-filters { display: flex; flex-direction: column; align-items: stretch; gap: 0; margin: 0; }
.blog-filters .chip { display: flex; flex-direction: column; align-items: flex-start; width: 100%; padding: 13px 0; border: 0; border-top: 1px solid var(--hairline); border-radius: 0; background: none; color: var(--fg-3); text-align: left; }
.blog-filters .chip span { font-size: 14px; font-weight: 700; }
.blog-filters .chip small { margin-top: 3px; color: var(--fg-3); font-size: 11px; font-weight: 600; }
.blog-filters .chip.active { background: none; color: var(--fg); }
.blog-filters .chip.active small { color: var(--fg-2); }
.blog-filters .chip:hover { background: none; color: var(--fg); }
#blogGrid { display: flex; flex-direction: column; gap: 24px; }
.blog-grid-inner { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }

/* Cover image rides in its own layer inside a fixed-radius mask so it can
   scale on hover without the corners clipping square. */
.blog-card-img { position: absolute; inset: 0; background-size: cover; background-position: center;
  transition: transform .55s var(--ease); }
.blog-card:hover .blog-card-img,
.blog-feat:hover .blog-card-img { transform: scale(1.05); }

/* --- featured hero --- */
.blog-feat { display: grid; grid-template-columns: 1.1fr minmax(0, 1fr); gap: 0; align-items: stretch;
  min-height: 308px; overflow: hidden; text-decoration: none; color: inherit; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: var(--bg-2); }
.blog-feat-cover { position: relative; min-height: 0; overflow: hidden; }
.blog-feat-body { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; padding: 32px; }
.blog-feat-tags { display: flex; align-items: center; gap: 9px; }
.blog-feat-title { font-size: 27px; font-weight: 800; letter-spacing: -0.03em; line-height: 1.14; text-wrap: balance; }
.blog-feat-dek { font-size: 15px; color: var(--fg-2); line-height: 1.62;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.blog-feat-go { margin-top: auto; padding-top: 10px; font-size: 13px; font-weight: 700; color: var(--accent); }

/* --- standard card --- */
.blog-card { position: relative; display: flex; flex-direction: column; overflow: hidden; text-decoration: none; color: inherit; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: var(--bg-2); transition: border-color .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease); }
.blog-card:hover { border-color: var(--hairline-strong); transform: translateY(-2px); box-shadow: var(--e-2); }
.blog-card-cover { position: relative; display: block; aspect-ratio: 16 / 10; overflow: hidden; }
.blog-card-feat { font-size: 10.5px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--fg-2); padding: 5px 9px; border-radius: 6px; background: var(--bg-3); }
.blog-card-body { display: flex; flex-direction: column; padding: 16px 17px 18px; gap: 7px; min-width: 0; flex: 1; }
.blog-card-cat { font-size: 10.5px; font-weight: 800; letter-spacing: 0.09em; text-transform: uppercase; color: var(--fg-3); }
.blog-feat-tags .blog-card-cat { color: var(--fg-3); }
.blog-card-title { font-size: 17px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.3; text-wrap: balance;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.blog-card-dek { font-size: 13px; color: var(--fg-2); line-height: 1.55;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.blog-card-meta { margin-top: auto; padding-top: 8px; font-size: 12px; color: var(--fg-3); }
.blog-dot { opacity: .5; }
@media (max-width: 640px) {
  .blog-layout { grid-template-columns: 1fr; gap: 24px; }
  .blog-side { position: static; }
  .blog-filters { flex-direction: row; flex-wrap: wrap; gap: 0 16px; }
  .blog-filters .chip { width: auto; padding: 0 0 12px; border: 0; }
  .blog-filters .chip small { display: none; }
  .blog-grid-inner { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .blog-feat { grid-template-columns: 1fr; }
  .blog-feat-cover { aspect-ratio: 16 / 9; }
  .blog-feat-title { font-size: 24px; }
  .blog-feat-body { padding: 22px; }
  .blog-card-body { padding: 13px 14px 14px; }
  .blog-card-dek { display: none; }
}

/* ================================================================
   POST DETAIL
   ================================================================ */
.post-wrap { max-width: 880px; margin: 0 auto; padding: 0 24px; }
.post-head { text-align: center; margin-bottom: 26px; }
.post-cat { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); margin-bottom: 10px; }
.post-title { font-size: var(--t-h2); font-weight: 800; letter-spacing: -0.03em; line-height: 1.08; }
.post-dek { font-size: 16px; color: var(--fg-2); margin: 14px auto 0; max-width: 620px; }
.post-byline { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 18px; font-size: 13px; color: var(--fg-3); }
.post-ava { width: 26px; height: 26px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; color: var(--fg); background: linear-gradient(160deg, #2b2e36, #1a1c22); border: 1px solid var(--hairline); }
.post-dot { color: var(--fg-3); }
.post-cover { margin-top: 30px; height: 340px; border-radius: var(--r); background-size: cover; background-position: center; border: 1px solid var(--hairline); }
.post-layout { display: grid; grid-template-columns: minmax(0,1fr) 260px; gap: 40px; margin-top: 36px; align-items: start; }
.post-body { min-width: 0; font-size: 16px; line-height: 1.75; }
.post-body h2 { font-size: 24px; font-weight: 700; letter-spacing: -0.02em; margin: 32px 0 12px; }
.post-body h3 { font-size: 20px; font-weight: 700; margin: 26px 0 10px; }
.post-body p { margin: 0 0 16px; color: var(--fg-2); }
.post-body ul { margin: 0 0 16px; padding-left: 22px; color: var(--fg-2); }
.post-body li { margin-bottom: 8px; }
.post-body strong { color: var(--fg); font-weight: 700; }
.post-body code { background: rgba(255,255,255,0.07); border: 1px solid var(--hairline); border-radius: 4px; padding: 2px 6px; font-size: 0.9em; }
/* Prose links: the default UA dark blue is close to unreadable on the dark
   surface, so every in-content link renders bold in the lighter accent red. */
.post-body a { color: var(--accent-ink); font-weight: 700; text-decoration: none; }
.post-body a:hover { text-decoration: underline; text-underline-offset: 2px; }
.post-rail { display: flex; flex-direction: column; gap: 16px; position: sticky; top: 96px; }
.post-rail-card { border: 1px solid var(--hairline); border-radius: var(--r-sm); padding: 18px 20px;
  background: var(--bg-2); }
.post-rail-card h3 { font-size: 13px; font-weight: 700; margin-bottom: 12px; }
.post-rel { display: block; padding: 10px 0; border-top: 1px solid var(--hairline); text-decoration: none; color: inherit; }
.post-rel:first-of-type { border-top: 0; }
.post-rel-cat { display: block; font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--accent); margin-bottom: 3px; }
.post-rel-title { display: block; font-size: 13px; font-weight: 600; line-height: 1.4; }
.post-rail-cta p { font-size: 12px; color: var(--fg-2); margin: 0 0 12px; }
.post-rail-cta .btn { width: 100%; justify-content: center; }
.pd-empty { text-align: center; color: var(--fg-3); font-size: 14px; padding: 60px 0; }
@media (max-width: 900px) { .post-layout { grid-template-columns: 1fr; } .post-rail { position: static; } }

/* ================================================================
   TUTORIALS HUB + DETAIL
   ================================================================ */
.tut-hub { display: block; max-width: 1120px; margin: 0 auto; }
.tut-hub[hidden] { display: none; }
.tut-side { position: static; margin-bottom: 38px; }
.tut-count { margin-left: auto; font-size: 11px; color: var(--fg-3); font-variant-numeric: tabular-nums; }
.tut-side .filter-card { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 30px; padding: 20px 22px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: var(--bg-2); }
.tut-side .fc-head { align-self: start; padding-top: 3px; font-size: 15px; font-weight: 800; }
.tut-side .fc-cats { display: grid; grid-template-columns: repeat(auto-fit, minmax(142px, 1fr)); gap: 0; border-top: 1px solid var(--hairline); border-left: 1px solid var(--hairline); }
.tut-side .fc-cat { width: 100%; min-height: 50px; padding: 10px 12px; border: 0; border-right: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); border-radius: 0; background: transparent; }
.tut-side .fc-cat.active { background: rgba(255,255,255,0.07); color: var(--fg); }
.tut-content { display: flex; flex-direction: column; gap: 48px; }
.tut-track-h { display: flex; align-items: baseline; gap: 12px; margin-bottom: 18px;
  padding-bottom: 12px; border-bottom: 1px solid var(--hairline); }
.tut-track-h h2 { font-size: 19px; font-weight: 800; letter-spacing: -0.02em; }
.tut-track-n { font-size: 12px; color: var(--fg-3); }
.tut-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.tut-card { position: relative; display: flex; flex-direction: column; overflow: hidden; text-decoration: none; color: inherit; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: var(--bg-2); transition: border-color .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease); }
.tut-card:hover { border-color: var(--hairline-strong); transform: translateY(-2px); box-shadow: var(--e-2); }
.tut-card-cover { position: relative; aspect-ratio: 16 / 9; overflow: hidden; }
.tut-card-cover .tut-diff { position: absolute; left: 12px; top: 12px; z-index: 1;
  background: rgba(10,11,14,0.72); backdrop-filter: blur(6px); box-shadow: 0 2px 10px -2px var(--ink-75); }
.tut-card-body { padding: 16px 17px 17px; display: flex; flex-direction: column; gap: 7px; flex: 1; min-width: 0; }
.tut-diff { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; padding: 4px 9px; border-radius: 999px; }
.tut-diff.lv1 { color: var(--ok); background: var(--ok-tint); }
.tut-diff.lv2 { color: #ffc24b; background: rgba(255,194,75,0.14); }
.tut-diff.lv3 { color: var(--accent); background: rgba(255,77,68,0.14); }
.tut-card-title { font-size: 15.5px; font-weight: 700; line-height: 1.32; letter-spacing: -0.01em; text-wrap: balance;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.tut-card-sum { font-size: 12.5px; color: var(--fg-2); line-height: 1.55;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.tut-card-meta { margin-top: auto; padding-top: 8px; display: flex; align-items: center; justify-content: space-between;
  font-size: 11px; color: var(--fg-3); }
.tut-card-go { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--accent);
  opacity: 0; transform: translateX(-4px); transition: opacity .2s var(--ease), transform .2s var(--ease); }
.tut-card:hover .tut-card-go { opacity: 1; transform: none; }
@media (max-width: 760px) { .tut-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .tut-side .filter-card { grid-template-columns: 1fr; gap: 16px; padding: 18px; } .tut-grid { grid-template-columns: 1fr; } }

.tut-detail { max-width: 880px; margin: 0 auto; padding: 0 24px; }
.tut-detail-head { text-align: center; margin-bottom: 26px; }
.tut-detail-badges { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 14px; }
.tut-detail-plat, .tut-detail-mins { font-size: 12px; color: var(--fg-3); }
.tut-detail-title { font-size: var(--t-h2); font-weight: 800; letter-spacing: -0.03em; line-height: 1.1; }
.tut-detail-sum { font-size: 16px; color: var(--fg-2); margin: 14px auto 0; max-width: 600px; }
.tut-detail-layout { display: grid; grid-template-columns: minmax(0,1fr) 250px; gap: 40px; margin-top: 32px; align-items: start; }
.tut-detail-body { min-width: 0; font-size: 16px; line-height: 1.75; }
.tut-detail-body h2 { font-size: 20px; font-weight: 700; letter-spacing: -0.02em; margin: 30px 0 12px; scroll-margin-top: 100px; }
.tut-detail-body p { margin: 0 0 16px; color: var(--fg-2); }
.tut-detail-body strong { color: var(--fg); }
.tut-detail-body code { background: rgba(255,255,255,0.07); border: 1px solid var(--hairline); border-radius: 4px; padding: 2px 6px; font-size: 0.9em; }
.tut-detail-body a { color: var(--accent-ink); font-weight: 700; text-decoration: none; }
.tut-detail-body a:hover { text-decoration: underline; text-underline-offset: 2px; }
.tut-toc { position: sticky; top: 96px; display: flex; flex-direction: column; gap: 16px; }
.tut-toc-card { border: 1px solid var(--hairline); border-radius: var(--r-sm); padding: 18px 20px;
  background: var(--bg-2); }
.tut-toc-card h3 { font-size: 13px; font-weight: 700; margin-bottom: 12px; }
.tut-toc-card nav { display: flex; flex-direction: column; gap: 9px; }
.tut-toc-card nav a { font-size: 13px; color: var(--fg-2); text-decoration: none; }
.tut-toc-card nav a:hover { color: var(--fg); }
.tut-toc-cta p { font-size: 12px; color: var(--fg-2); margin: 0 0 12px; }
.tut-toc-cta .btn { width: 100%; justify-content: center; }
.tut-video { margin: 28px 0 0; }
@media (max-width: 900px) { .tut-detail-layout { grid-template-columns: 1fr; } .tut-toc { position: static; } }

/* ================================================================
   BLOG / TUTORIALS VIEW SWITCH
   ================================================================ */
.bt-switch { display: flex; align-items: end; gap: 30px; max-width: 820px; margin: 0 auto 30px; border-bottom: 1px solid var(--hairline); }
.bt-opt { position: relative; font-family: inherit; font-size: 14px; font-weight: 700; color: var(--fg-3); background: none; border: none;
  padding: 0 0 13px; border-radius: 0; cursor: pointer; transition: color .18s var(--ease);
  display: inline-flex; align-items: center; gap: 7px; text-decoration: none; }
.bt-opt svg { width: 15px; height: 15px; flex: 0 0 auto; }
.bt-opt:hover { color: var(--fg); }
.bt-opt.active { color: var(--fg); background: none; box-shadow: none; }
.bt-opt.active::after { content: ""; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; background: var(--accent); }
.bt-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 5px; }
@media (max-width: 560px) { .bt-switch { gap: 22px; margin-bottom: 24px; } .bt-opt { font-size: 13px; } }

/* ================================================================
   RELEASES TIMELINE
   ================================================================ */
.rel-page .page-head { padding-bottom: 34px; }
.rel-products-section { padding-bottom: 0; }
.rel-updates-section { padding-top: 54px; }
.rel-hub .rel-updates-head { margin-top: 54px; }
.rel-hub[hidden] { display: none; }
.rel-section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 18px; max-width: 1120px; margin: 0 auto 18px; }
.rel-section-head h2 { font-size: 20px; font-weight: 800; letter-spacing: -0.02em; }
.rel-section-head a { color: var(--fg-2); font-size: 13px; font-weight: 700; text-decoration: none; }
.rel-section-head a:hover { color: var(--fg); }
.rel-products { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.rel-product { min-width: 0; overflow: hidden; border-radius: 16px; border: 1px solid var(--hairline); background: var(--bg-2); color: var(--fg); text-decoration: none; transition: border-color .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease); }
.rel-product:hover { border-color: var(--hairline-strong); transform: translateY(-2px); box-shadow: var(--e-2); }
.rel-product-img { display: block; height: 174px; background: var(--bg-3) center / cover no-repeat; }
.rel-product-body { display: grid; gap: 5px; padding: 14px 16px 16px; }
.rel-product-cat { color: var(--fg-3); font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; }
.rel-product-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.rel-product-price { color: var(--price); font-size: 13px; font-weight: 700; }
/* A compact product changelog: dates and release type live on a fixed rail,
   while the updates keep the same practical density as the rest of the site. */
.rel-timeline { position: relative; display: flex; flex-direction: column; max-width: 820px; margin: 0 auto; padding-left: 156px; }
.rel-timeline::before { content: ""; position: absolute; left: 139px; top: 7px; bottom: 7px; width: 1px; background: var(--hairline); }
.rel-timeline[hidden] { display: none; }
.rel-item { position: relative; min-height: 116px; padding: 0 0 28px 28px; }
.rel-item + .rel-item { padding-top: 28px; border-top: 1px solid var(--hairline); }
.rel-item::before { content: ""; position: absolute; z-index: 1; left: -21px; top: 5px; width: 9px; height: 9px; border-radius: 50%; background: var(--accent); border: 3px solid var(--bg); }
.rel-item:has(.k-fix)::before { background: var(--ok); }
.rel-item:has(.k-ann)::before { background: #ffc24b; }
.rel-item-side { position: absolute; top: 0; left: -156px; width: 120px; display: flex; flex-direction: column; gap: 7px; }
.rel-item + .rel-item .rel-item-side { top: 28px; }
.rel-kind { display: inline-flex; width: fit-content; font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 4px 10px; border-radius: 999px; }
.rel-kind.k-feature { color: var(--accent); background: rgba(255,77,68,0.14); }
.rel-kind.k-fix { color: var(--ok); background: var(--ok-tint); }
.rel-kind.k-ann { color: #ffc24b; background: rgba(255,194,75,0.14); }
.rel-ver { font-size: 13px; font-weight: 700; color: var(--fg-2); font-variant-numeric: tabular-nums; }
.rel-date { font-size: 12px; color: var(--fg-3); }
.rel-item-main { min-width: 0; }
.rel-title { font-size: 21px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.2; margin-bottom: 9px; }
.rel-sum { font-size: 14.5px; color: var(--fg-2); line-height: 1.65; }
.rel-affects { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.rel-affect { font-size: 12px; color: var(--fg-3); text-decoration: none; padding: 5px 11px; border-radius: 999px;
  background: rgba(255,255,255,0.04); border: 1px solid var(--hairline); transition: color .2s, border-color .2s, background .2s; }
.rel-affect:hover { color: var(--fg); border-color: rgba(255,255,255,0.2); background: rgba(255,255,255,0.07); }
@media (max-width: 640px) {
  .rel-updates-section { padding-top: 42px; }
  .rel-section-head { margin-bottom: 14px; }
  .rel-products { grid-template-columns: 1fr; }
  .rel-product { display: grid; grid-template-columns: 126px minmax(0, 1fr); }
  .rel-product-img { height: 104px; }
  .rel-product-body { align-content: center; }
  .rel-timeline { padding-left: 26px; }
  .rel-timeline::before { left: 8px; }
  .rel-item { min-height: 0; padding: 0 0 26px 20px; }
  .rel-item + .rel-item { padding-top: 26px; }
  .rel-item::before { left: -25px; top: 4px; }
  .rel-item-side, .rel-item + .rel-item .rel-item-side { position: static; width: auto; flex-direction: row; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
}

/* auth full-page: submit spinner + verify-step controls */
.auth-submit { position: relative; }
.auth-submit .btn-spinner { width: 16px; height: 16px; border: 2px solid rgba(255,255,255,0.3); border-top-color: #fff; border-radius: 50%; display: inline-block; animation: authbtnspin 3s linear infinite; }
.auth-submit .btn-spinner[hidden] { display: none; }
.auth-submit.is-loading { opacity: 0.55; cursor: default; }
.auth-submit.is-loading .btn-label { visibility: hidden; }
.auth-submit.is-loading .btn-spinner { position: absolute; top: 50%; left: 50%; margin-top: -8px; margin-left: -8px; }
@keyframes authbtnspin { to { transform: rotate(360deg); } }
/* Generic version of the same pattern for any .btn (dashboard save
   actions, modals) - .auth-submit above is the form-page-specific one. */
.btn.is-loading { position: relative; opacity: 0.55; cursor: default; }
.btn .btn-spinner { width: 16px; height: 16px; border: 2px solid rgba(255,255,255,0.3); border-top-color: #fff; border-radius: 50%; display: inline-block; animation: authbtnspin 3s linear infinite; }
.btn .btn-spinner[hidden] { display: none; }
.btn.is-loading .btn-label { visibility: hidden; }
.btn.is-loading .btn-spinner { position: absolute; top: 50%; left: 50%; margin-top: -8px; margin-left: -8px; }
.auth-back { background: none; border: none; color: var(--fg-3); font-size: 13px; cursor: pointer; padding: 0; margin-top: 10px; text-align: left; }
.auth-back:hover { color: var(--fg-2); }
.auth-resend { background: transparent; border: 1px solid var(--hairline); color: var(--fg-2); }
.auth-resend[disabled] { opacity: 0.45; cursor: default; border-color: var(--hairline); color: var(--fg-3); }
.auth-resend:not([disabled]):hover { border-color: rgba(255,255,255,0.3); color: var(--fg); }

/* auth split-screen full page layout */
/* The auth pages sat on near-pure #050505 while every other surface on the
   site is the graphite --bg. Side by side they read as two different
   products; the sign-in screen is now the same ground as everything else. */
body.auth-page { background: var(--bg); }
/* .backdrop/.scrim's shared 940px floor is sized for the homepage's tall
   hero + scrolling page beneath it - correct there, but this is a single-
   viewport form with nothing to scroll to. On a short viewport that floor
   forces the whole document past the visible height, so the page scrolls
   to reveal nothing but more background.
   position:fixed rather than a computed height (100dvh alone did not fully
   fix this on real mobile Safari, where dvh/vh can disagree with the
   layout viewport during/after the URL bar's show-hide animation) - a
   fixed, inset:0 element is pinned to the viewport by definition and can
   never itself add to the document's scrollable area, regardless of which
   viewport unit a given engine resolves at a given moment. */
body.auth-page .backdrop, body.auth-page .scrim { position: fixed; inset: 0; height: auto; }
.auth-split { display: flex; min-height: 100vh; min-height: 100dvh; }
.auth-split-left { flex: 0 0 46%; max-width: 560px; display: flex; flex-direction: column; justify-content: center;
  padding: 118px 56px 56px; background: var(--bg); position: relative; z-index: 1; }
.auth-split-right { flex: 1; position: relative; overflow: hidden; background-image: url('builds.jpg'); background-size: cover; background-position: center; }
.auth-split-right::before { content: ""; position: absolute; inset: 0; background: linear-gradient(160deg, rgba(21,22,27,0.72) 0%, rgba(226,56,47,0.26) 100%); }
.auth-split-logo { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.auth-split-logo img { width: 260px; max-width: 60%; opacity: 0.92; filter: drop-shadow(0 10px 34px var(--ink-60)); }
.auth-split-left .auth-card { max-width: 100%; padding: 0; background: none; border: none; box-shadow: none; }
/* 118px of top padding exists to clear the fixed pill nav (12px offset +
   50px tall + breathing room) - comfortable on a normal-height screen, but
   on a short landscape viewport (iPad at 1024x768) it's the difference
   between the form fitting and needing a few pixels of scroll for content
   that's otherwise fully visible. The nav only needs ~70px cleared. */
@media (max-height: 820px) {
  .auth-split-left { padding-top: 88px; }
}
@media (max-width: 900px) {
  .auth-split { flex-direction: column; }
  .auth-split-right { display: none; }
  .auth-split-left { flex: 1 1 auto; max-width: none; padding: 118px 24px 56px; }
}

/* auth status text (e.g. "sending code" while spinner runs) */
.auth-status { margin: 8px 0 0; font-size: 12px; color: #ff4d44; }
.auth-status[hidden] { display: none; }

/* Confirm popup (sign-out, and any other Cancel/primary-action pair using
   these same classes) - was a flat #0b0b0b box with a sharp 10px corner
   and no entrance at all, standing out as the one dated-looking overlay
   next to every other modal on the site (.pay-modal, quick-view, etc.),
   which all get the blurred-backdrop-plus-pop-in treatment already. */
.confirm-overlay { position: fixed; inset: 0; z-index: 400; background: rgba(6,7,10,0.62);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  display: none; align-items: center; justify-content: center; padding: 24px; }
.confirm-overlay:not([hidden]) { display: flex; animation: ovIn .2s var(--ease); }
.confirm-modal { width: 380px; max-width: 96vw; background: var(--bg-3); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); padding: 30px; text-align: center; box-shadow: var(--e-3);
  animation: pmIn .3s var(--ease); }
.confirm-modal p { margin: 0 0 22px; font-size: 15px; font-weight: 600; color: var(--fg); }
.confirm-actions { display: flex; gap: 10px; }
.confirm-actions .btn { flex: 1; justify-content: center; }

/* Site-wide marketing-signup popup - same overlay/modal shell as the
   sign-out confirm structurally, but this is a promo moment (the one
   place on the site actively selling a discount for an email), not a
   Cancel/primary utility prompt, so it earns its own real design: set
   left-aligned like an actual editorial offer instead of centered around
   a badge icon, with the discount carried by the heading's own type
   (weight + Price Green, same convention product cards already use for
   a savings figure) rather than a separate glowing circle. No gradient,
   no radial wash - ember stays on the one button that is the actual
   primary action. */
.mkt-popup-modal { position: relative; width: 420px; max-width: 96vw; text-align: left; padding: 34px 32px 28px; }
.mkt-popup-x { position: absolute; top: 16px; right: 16px; z-index: 2; width: 30px; height: 30px; border-radius: var(--r-ctl);
  display: flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--fg-3); cursor: pointer; }
@media (hover: hover) and (pointer: fine) { .mkt-popup-x:hover { color: var(--fg); background: rgba(255,255,255,0.08); } }
.mkt-popup-modal .mkt-popup-title { font-size: 24px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.25; margin: 0 0 10px; color: var(--fg); }
/* the checkout co-offer modal keeps its X, so it needs the title inset back */
.co-offer .mkt-popup-modal .mkt-popup-title { margin-right: 30px; }
.mkt-popup-pct { color: var(--price); }
.mkt-popup-modal .mkt-popup-sub { font-size: 14px; color: var(--fg-2); line-height: 1.55; margin: 0 0 22px; font-weight: 400; max-width: 34ch; }
.mkt-popup-form { display: flex; flex-direction: column; gap: 10px; }
.mkt-popup-field { display: flex; gap: 8px; }
.mkt-popup-field input { flex: 1; min-width: 0; min-height: 46px; padding: 0 14px; border-radius: var(--r-sm);
  background: rgba(255,255,255,0.04); border: 1px solid var(--hairline); color: var(--fg); font-family: inherit; font-size: 14px; }
.mkt-popup-field input::placeholder { color: var(--fg-3); }
.mkt-popup-field input:focus { outline: none; border-color: rgba(255,77,68,0.5); background: rgba(255,255,255,0.06); }
.mkt-popup-field .btn { flex: none; min-height: 46px; font-size: 13px; padding: 12px 20px; white-space: nowrap; }
.mkt-popup-modal .mkt-popup-msg { font-size: 13px; font-weight: 400; margin: 14px 0 0; line-height: 1.6; }
.mkt-popup-msg:empty { display: none; }
.mkt-popup-modal .mkt-popup-msg.err { color: var(--accent); font-weight: 600; }
.mkt-popup-modal .mkt-popup-msg.ok { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; color: var(--fg-2); }
.mkt-popup-code { display: inline-block; padding: 8px 14px; border-radius: var(--r-ctl); background: var(--ok-tint);
  border: 1px solid rgba(107,216,138,0.3); color: var(--price); font-size: 15px; font-weight: 700; letter-spacing: 0.04em; }
.mkt-popup-modal .mkt-popup-fine { font-size: 11px; font-weight: 400; color: var(--fg-3); margin: 18px 0 0; padding-top: 16px; border-top: 1px solid var(--hairline); }
/* Explicit decline - the only thing (besides claiming a code) that stops
   the popup coming back as the corner tab. */
.mkt-popup-no { display: block; margin: 14px auto 0; padding: 4px 8px; background: none; border: none;
  color: var(--fg-3); font-size: 12px; font-weight: 500; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
@media (hover: hover) and (pointer: fine) { .mkt-popup-no:hover { color: var(--fg-2); } }

/* Corner tab an X'd popup collapses to. Sits just above the bottom edge,
   below the overlay's z-index so a reopened popup covers it. */
.mkt-claim-tab { position: fixed; right: 18px; bottom: 18px; z-index: 380;
  padding: 11px 18px; border: none; border-radius: 999px; cursor: pointer;
  font-family: inherit; font-size: 13px; font-weight: 700; letter-spacing: -0.01em; color: #fff;
  background: linear-gradient(135deg, #cc0011, #ff3344); box-shadow: 0 6px 22px rgba(0,0,0,0.4);
  animation: ovIn .2s var(--ease); }
@media (hover: hover) and (pointer: fine) { .mkt-claim-tab:hover { filter: brightness(1.08); } }
@media (max-width: 480px) {
  .mkt-popup-modal { padding: 30px 22px 24px; }
  .mkt-popup-field { flex-direction: column; }
  .mkt-popup-field .btn { width: 100%; }
  .mkt-claim-tab { right: 12px; bottom: 12px; font-size: 12px; padding: 10px 15px; }
}

/* password strength meter + checklist */
.pw-strength { max-height: 0; overflow: hidden; opacity: 0; transition: max-height 0.25s ease, opacity 0.2s ease, margin-top 0.25s ease; margin-top: 0; }
.pw-strength.open { max-height: 220px; opacity: 1; margin-top: 10px; }
.pw-strength-bar { height: 4px; border-radius: 4px; background: rgba(255,255,255,0.08); overflow: hidden; }
.pw-strength-fill { height: 100%; width: 0%; border-radius: 4px; background: var(--accent); transition: width 0.2s ease, background-color 0.2s ease; }
.pw-checklist { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.pw-checklist li { font-size: 12px; color: var(--fg-3); display: flex; align-items: center; gap: 7px; }
.pw-checklist li::before { content: ""; flex: 0 0 auto; width: 13px; height: 13px; border-radius: 50%; border: 1.5px solid var(--hairline); background: transparent; }
.pw-checklist li.met { color: var(--ok); }
.pw-checklist li.met::before { border-color: var(--ok); background: var(--ok-tint); }
/* The tick is drawn from two borders, the same construction the filter
   checkbox uses, rather than a typed check character. */
.pw-checklist li { position: relative; }
.pw-checklist li.met::after { content: ""; position: absolute; left: 4px; top: 50%;
  width: 5px; height: 2.5px; margin-top: -2px;
  border-left: 1.5px solid var(--ok); border-bottom: 1.5px solid var(--ok);
  transform: rotate(-45deg); }

/* custom checkboxes site-wide */
input[type="checkbox"] {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  width: 18px; height: 18px; flex: 0 0 auto;
  border: 1.5px solid var(--hairline); border-radius: 4px;
  background: rgba(255,255,255,0.03);
  position: relative; display: inline-block; vertical-align: middle;
  cursor: pointer; margin: 0;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
input[type="checkbox"]:hover { border-color: rgba(255,255,255,0.28); }
input[type="checkbox"]:checked { background: var(--accent); border-color: var(--accent); }
input[type="checkbox"]:checked::after {
  content: ""; position: absolute; left: 5px; top: 1px;
  width: 5px; height: 10px; border: solid #fff; border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
input[type="checkbox"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* account dropdown menu (matches account-menu classes built in app.js) */
.account-menu { position: absolute; top: calc(100% + 10px); right: 0; min-width: 244px; z-index: 250;
  background: var(--bg-2); border: 1px solid var(--hairline); border-radius: 12px; padding: 6px; box-shadow: var(--e-3); }
.account-menu[hidden] { display: none; }
.account-menu-head { display: flex; align-items: center; gap: 11px; padding: 10px 10px 11px; }
.account-menu-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.account-menu-id strong { font-size: 14px; font-weight: 700; color: var(--fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.account-menu-id em { font-size: 12px; font-style: normal; color: var(--fg-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.account-menu-list { padding-top: 4px; border-top: 1px solid var(--hairline); }
.account-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 10px; border-radius: 10px;
  background: none; border: none; color: var(--fg); font-size: 13px; font-family: inherit; cursor: pointer;
  text-align: left; text-decoration: none; box-sizing: border-box; }
.account-menu-item svg { flex: 0 0 20px; color: var(--fg-2); }
.account-menu-item:hover { background: rgba(255,255,255,0.05); }
.account-menu-item:hover svg { color: var(--fg); }
/* Admin-only shortcut into /admin. Reads as a normal menu row, just set
   into its own faint panel - no colour, no promo styling. */
.account-menu-admin { display: flex; align-items: center; gap: 10px; margin: 6px 2px 2px; padding: 9px 10px;
  border-radius: 10px; font-size: 13px; text-decoration: none; color: var(--fg);
  border: 1px solid var(--hairline); background: rgba(255,255,255,0.03); }
.account-menu-admin:hover { background: rgba(255,255,255,0.06); }
.account-menu-admin svg { flex: 0 0 20px; color: var(--fg-2); }
.account-menu-signout { margin-top: 4px; border-top: 1px solid var(--hairline); border-radius: 0 0 8px 8px; color: var(--accent-ink); }
.account-menu-signout:hover { background: rgba(255,255,255,0.05); }
/* Sign out's 16px SVG sat directly next to the text with no box of its own,
   while Your Account's 20px avatar circle does - that 4px difference in
   icon width was enough to shift where each label's text actually starts,
   reading as the two rows being misaligned. Same fixed-width box as the
   avatar, icon centered inside it. */
.account-menu-signout svg { flex: 0 0 20px; }
.account-menu-av { width: 20px; height: 20px; border-radius: 50%; flex: 0 0 auto; background: var(--accent);
  display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; color: #fff;
  background-size: cover; background-position: center; }
.account-menu-av-lg { width: 38px; height: 38px; font-size: 16px; }

/* notification bell + popover (matches notif-* classes built in app.js) */
/* .nav-notif is .nav-cta's first child, but .nav-inner's 18px gap (the
   same value that separates the brand from the nav links) lands right
   before it - meant to separate nav-links from the account/cart icon
   tray as groups, not to space out one icon within that tray. Pulled
   back to match .nav-cta's own internal gap (4px) instead, so the bell
   reads as one of that tray's icons rather than sitting off on its own. */
.nav-notif { position: relative; margin-left: -14px; transition: margin-left .55s cubic-bezier(0.6,0,0.2,1); }
/* .nav-inner's gap itself shrinks to 7px while searching (see
   .nav-inner:has(.nav-links.searching) above) so the bell has room to sit
   next to the growing search input - the -14px above is calibrated against
   the normal 18px gap alone, so left uncorrected here it overshoots by the
   same 11px the gap just lost, pulling the bell noticeably too far left. */
.nav-inner:has(.nav-links.searching) .nav-notif { margin-left: -3px; }
.nav-notif[hidden] { display: none; }
/* #notifWrap ships [hidden] in the static markup and only ever gets
   unhidden by loadNotifs() in app.js, which waits on both app.js itself
   loading (after catalog.js's fetch resolves - a second or more on a cold
   load) and its own getSession() call - a signed-in visitor watched the
   bell icon pop in a couple seconds after everything else on the page,
   the same class of flash the cart badge/total already had a fix for
   (see the inline localStorage sync script in every page's footer).
   coldd_auth is set synchronously on sign-in/out (supabase-init.js) and
   read here just as synchronously, in the same per-page <head> script
   that already avoids a dark/light theme flash - so a signed-in visitor's
   html tag carries .auth-in before first paint, and the bell shows
   immediately instead of waiting on two async steps. loadNotifs() still
   runs as before and is what removes .auth-in again if that cached flag
   turns out to be stale (session actually expired) - this only ever
   changes how soon the icon can show, never whether the real check
   happens. */
html.auth-in .nav-notif[hidden] { display: block; }
/* Signed out, the bell keeps its static [hidden] and (no .auth-in) stays
   display:none, so .nav-account becomes .nav-cta's first child and inherits
   the full 18px .nav-inner gap that the bell's -14px pull-back was there to
   absorb - the account icon then sits noticeably further from the search
   icon than it does once signed in and the bell is present. Give the
   account button the same pull-back the bell gets in that case. Desktop
   only: below 1100px .nav-links (search included) is display:none, so there
   is no search icon to sit tight against. */
@media (min-width: 1101px) {
  html:not(.auth-in) .nav-notif[hidden] + .nav-account { margin-left: -14px;
    transition: margin-left .55s cubic-bezier(0.6,0,0.2,1); }
  html:not(.auth-in) .nav-inner:has(.nav-links.searching) .nav-notif[hidden] + .nav-account { margin-left: -3px; }
}
.notif-badge { position: absolute; top: 2px; right: 2px; min-width: 15px; height: 15px; padding: 0 3px; border-radius: 999px;
  background: var(--accent); color: #fff; font-size: 10px; font-weight: 700; line-height: 15px; text-align: center; pointer-events: none; }
.notif-badge[hidden] { display: none; }
/* Matches every other popover on the site (.cur-menu, .adm-row-menu-list):
   same charcoal-not-pure-black fill, hairline border, soft drop shadow. This
   used to be a flat #0b0b0b box with no shadow at all - the only popover on
   the site without one, which is exactly what read as out of place. */
.notif-panel { position: absolute; top: calc(100% + 10px); right: 0; width: 366px; max-width: calc(100vw - 32px);
  max-height: 420px; overflow-y: auto; z-index: 250; background: var(--bg-2); border: 1px solid var(--hairline);
  border-radius: 16px; padding: 8px; box-shadow: 0 26px 64px -24px var(--ink-85); }
.notif-panel[hidden] { display: none; }
.notif-panel-head { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px 10px; }
.notif-panel-head h3 { font-size: 14px; font-weight: 700; margin: 0; }
.notif-mark-all { font-size: 11px; font-weight: 600; color: var(--fg-3); background: none; border: none; cursor: pointer; padding: 0; font-family: inherit; }
.notif-mark-all:hover { color: var(--fg); }
.notif-list { display: flex; flex-direction: column; gap: 1px; }
.notif-item { display: flex; gap: 11px; padding: 10px; border-radius: 10px; text-decoration: none; color: var(--fg); cursor: pointer; transition: background .15s var(--ease); }
.notif-item:hover { background: rgba(255,255,255,0.06); }
.notif-item.unread { background: rgba(255,77,68,0.08); }
.notif-item.unread:hover { background: rgba(255,77,68,0.12); }
.notif-item-thumb, .notif-item-icon { flex: 0 0 40px; width: 40px; height: 40px; border-radius: 9px; }
.notif-item-thumb { background: #0f1014 center / cover no-repeat; border: 1px solid var(--hairline); }
.notif-item-icon { display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.05); border: 1px solid var(--hairline); color: var(--fg-2); }
.notif-item-icon svg { width: 18px; height: 18px; }
.notif-item-icon[data-kind="tag"] { color: var(--accent); background: rgba(255,77,68,0.1); border-color: rgba(255,77,68,0.22); }
.notif-item.unread .notif-item-icon { color: var(--fg-1); }
.notif-item-main { flex: 1 1 auto; min-width: 0; }
.notif-item-title { font-size: 13px; font-weight: 600; }
.notif-item-body { font-size: 12px; color: var(--fg-2); margin-top: 2px; line-height: 1.45; }
.notif-item-date { font-size: 11px; color: var(--fg-3); margin-top: 4px; }
.notif-empty { padding: 30px 8px; text-align: center; font-size: 13px; color: var(--fg-3); }
@media (max-width: 480px) { .notif-panel { position: fixed; top: 64px; right: 16px; left: 16px; width: auto; } }

/* Identity fields ship empty rather than carrying a fabricated "Kaden R. /
   kaden@example.com", which rendered as a real signed-in user whenever the
   profile fetch had not resolved - or failed. These give the empty state a
   shape to hold so the panel does not jump when the real values land. */
#dashName:empty, #dashEmail:empty, #coUserName:empty, #coUserEmail:empty {
  display: block; min-width: 108px; height: 0.85em; border-radius: 4px;
  background: rgba(255,255,255,0.07);
}
#dashEmail:empty, #coUserEmail:empty { min-width: 148px; }
.dash-av:empty, #coAvatar:empty { background: rgba(255,255,255,0.07); }
@media (prefers-reduced-motion: no-preference) {
  #dashName:empty, #dashEmail:empty, #coUserName:empty, #coUserEmail:empty,
  .dash-av:empty, #coAvatar:empty { animation: skelPulse 1.5s ease-in-out infinite; }
}
@keyframes skelPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }

/* disabled OAuth buttons (Google/Roblox not yet available)
   The reason used to live in a hover-only tooltip, which a touch user can
   never see - they just tapped a dimmed button that did nothing. It is an
   always-visible chip now, and the dimming is light enough to keep the chip
   readable rather than the 0.4 that greyed out its own explanation. */
.auth-oauth.oauth-disabled { opacity: 0.62; cursor: not-allowed; position: relative; }
.auth-oauth.oauth-disabled:hover { opacity: 0.62; background: rgba(255,255,255,0.04); border-color: var(--hairline); }
.auth-oauth.oauth-disabled::after {
  content: "Soon";
  margin-left: 2px; padding: 3px 8px; border-radius: 999px;
  background: rgba(255,255,255,0.08); border: 1px solid var(--hairline);
  font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--fg-2); white-space: nowrap;
}

/* ---------------------------------------------------------------------
   Cookie consent bar

   A bar, not a modal. It never traps focus, never covers the page, and the
   visitor can read and use everything behind it - a consent prompt that
   holds the site hostage is a dark pattern, and DESIGN.md already refuses
   modals for tasks that need neither interruption nor protected focus.

   Both choices are equally weighted controls, side by side, in the standard
   buy-pair order: neither is hidden behind a link or styled as an
   afterthought, so declining is exactly as easy as accepting.
   --------------------------------------------------------------------- */
.cookie-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 400;
  background: var(--bg-2);
  border-top: 1px solid var(--hairline);
  box-shadow: 0 -24px 60px -30px var(--ink-85);
  padding: 16px 20px calc(16px + env(safe-area-inset-bottom));
  transform: translateY(100%);
  transition: transform .34s var(--ease-out);
}
.cookie-bar.in { transform: translateY(0); }
.cookie-bar-inner {
  max-width: 1200px; margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between; gap: 22px; flex-wrap: wrap;
}
.cookie-bar-tx { flex: 1 1 460px; min-width: 0; }
/* A real heading element so the region is navigable, sized as body-adjacent
   text - this is a notice, not a section title competing with the page. */
.cookie-bar-h { font-size: var(--t-sm); font-weight: 700; color: var(--fg); margin: 0 0 3px; letter-spacing: -0.01em; }
.cookie-bar-tx p { font-size: var(--t-sm); line-height: 1.5; color: var(--fg-2); margin: 0; max-width: 78ch; }
.cookie-bar-tx a { color: var(--fg-1); text-decoration: underline; text-underline-offset: 2px; }
.cookie-bar-tx a:hover { color: var(--fg); }
.cookie-bar-actions { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.cookie-bar-actions .btn { min-height: 42px; padding: 10px 20px; }
@media (max-width: 620px) {
  .cookie-bar-inner { gap: 14px; }
  .cookie-bar-actions { width: 100%; }
  /* Full-width and equal on small screens, so the two choices stay visibly
     equivalent rather than one shrinking to fit. */
  .cookie-bar-actions .btn { flex: 1 1 0; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
  .cookie-bar { transition: none; }
}

/* Author `display` overrides the browser's [hidden]{display:none}, so every
   class that sets display and is also toggled via the hidden attribute has to
   restate it. Found by auditing every element in the markup carrying `hidden`
   against the classes that declare a display - the resell-licence agreement
   and the Place order button were both visibly wrong because of this. */
.cart-drawer[hidden] { display: none; }
.confirm-overlay[hidden] { display: none; }
.adm-field[hidden] { display: none; }
.pm-lic[hidden] { display: none; }
.msym[hidden] { display: none; }
.ico-ext[hidden] { display: none; }

/* Cookie configure panel. Collapsed by default: the two common answers stay
   one click, while per-category control is one click further rather than
   hidden behind a link on another page. */
.cookie-cats { display: flex; flex-direction: column; gap: 14px; width: 100%;
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--hairline); }
.cookie-cats[hidden] { display: none; }
.cookie-cat { display: grid; grid-template-columns: 18px 1fr; gap: 12px; align-items: start; cursor: pointer; }
/* Essential is checked and disabled, not hidden. Showing it locked is more
   honest than implying there is nothing else stored. */
.cookie-cat.is-locked { cursor: default; opacity: 0.85; }
.cookie-cat input { width: 18px; height: 18px; margin: 2px 0 0; accent-color: var(--accent); cursor: inherit; }
.cookie-cat-tx { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cookie-cat-n { font-size: 14px; font-weight: 650; color: var(--fg); display: flex; align-items: center; gap: 8px; }
.cookie-cat-tag { font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--fg-3); border: 1px solid var(--hairline); border-radius: 4px; padding: 2px 6px; }
.cookie-cat-d { font-size: 12.5px; line-height: 1.5; color: var(--fg-3); max-width: 70ch; }
.cookie-cats .cookie-save { align-self: flex-start; min-height: 40px; padding: 9px 18px; }
@media (max-width: 620px) {
  .cookie-cats .cookie-save { align-self: stretch; justify-content: center; }
}

/* Back to top - mobile only. Desktop keeps the persistent floating nav pill
   as its own quick way back up; on mobile that pill collapses to a
   hamburger with no equivalent, so this fills the gap there specifically. */
.back-to-top {
  display: none;
  position: fixed; right: 16px; bottom: 16px; z-index: 250;
  width: 44px; height: 44px; border-radius: 50%;
  align-items: center; justify-content: center;
  background: var(--bg-3); border: 1px solid var(--hairline-strong); color: var(--fg);
  box-shadow: var(--e-2); cursor: pointer;
  opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity .2s var(--ease), transform .2s var(--ease), background .15s var(--ease);
}
@media (max-width: 1100px) {
  .back-to-top { display: flex; }
  .back-to-top.show { opacity: 1; transform: none; pointer-events: auto; }
}
@media (hover: hover) and (pointer: fine) {
  .back-to-top:hover { background: var(--bg-2); border-color: var(--hairline-strong); }
}
.back-to-top:active { transform: scale(0.94); }
@media (prefers-reduced-motion: reduce) {
  .back-to-top { transition: opacity .01ms; }
  .back-to-top:active { transform: none; }
}

/* ================================================================
   ROBUX PURCHASE MODAL (checkout - Place order with Robux selected)
   ================================================================ */
.robux-overlay { position: fixed; inset: 0; z-index: 400; background: rgba(6,7,10,0.6);
  display: none; align-items: center; justify-content: center; padding: 24px; }
.robux-overlay:not([hidden]) { display: flex; }
.robux-modal { position: relative; width: 420px; min-height: 420px; max-width: 92vw; max-height: 88vh; overflow-y: auto;
  display: flex; flex-direction: column; justify-content: center;
  background: var(--bg-2); border: 1px solid var(--hairline);
  /* Was a one-off 20px - matched neither var(--r) (24px, what every other
     card in checkout - Contact/Payment/Before you pay/Order summary -
     actually uses) nor var(--r-sm) (16px, the inner-panel token). This is
     a standalone card in the same flow, so it gets the same card radius. */
  border-radius: var(--r);
  box-shadow: 0 30px 70px -22px var(--ink-85); padding: 32px 30px 28px; }
.robux-modal-x { position: absolute; top: 16px; right: 16px; }
.robux-modal h2 { font-size: 21px; font-weight: 800; letter-spacing: -0.02em; margin: 0 0 18px; padding-right: 30px; }
.robux-steps { margin: 0 0 20px; padding-left: 22px; display: flex; flex-direction: column; gap: 12px; }
.robux-steps li { font-size: 14px; line-height: 1.55; color: var(--fg-2); }
.robux-steps li strong { color: var(--fg); }
.robux-modal-owned-note { font-size: 12.5px; line-height: 1.5; color: var(--fg-3);
  margin: 0 0 22px; padding-top: 16px; border-top: 1px solid var(--hairline); }
.robux-modal-owned-note strong { color: var(--fg-2); }
.robux-modal-total { font-size: 14.5px; color: var(--fg-2); margin: 0 0 18px; }
.robux-modal-total strong { color: var(--fg); font-weight: 700; }
.robux-modal-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.robux-modal-actions .btn { flex: 1 1 auto; min-width: 160px; justify-content: center; }
.robux-modal-actions .btn[aria-disabled="true"] { pointer-events: none; opacity: 0.5; }

#robuxModalWaiting { text-align: center; padding: 12px 4px 4px; }
.robux-modal-spin { display: flex; justify-content: center; margin-bottom: 18px; }
.robux-modal-status { font-size: 15.5px; font-weight: 700; color: var(--fg); margin: 0 0 8px; }
.robux-modal-hint { font-size: 13px; color: var(--fg-3); line-height: 1.5; margin: 0 0 14px; }
.robux-modal-warn { font-size: 13px; line-height: 1.5; color: var(--warn); margin: 0 0 22px;
  padding: 10px 12px; border-radius: var(--r-sm); background: rgba(240,175,90,0.08); border: 1px solid rgba(240,175,90,0.22); }

#robuxModalDone { text-align: center; padding: 8px 4px 4px; }
.robux-modal-msg { font-size: 14.5px; line-height: 1.6; color: var(--fg-2); margin: 0 0 18px; }
.robux-modal-msg.err { color: var(--warn); }
.robux-modal-help { text-align: left; margin: 0 0 20px; padding: 14px 16px; border-radius: var(--r-sm);
  background: rgba(255,255,255,0.03); border: 1px solid var(--hairline); }
.robux-modal-help-contact { font-size: 12.5px; line-height: 1.5; color: var(--fg-3); margin: 0; }
.robux-modal-help-contact a { color: var(--accent); font-weight: 600; text-decoration: none; }
.robux-modal-ref { font-size: 12px; line-height: 1.5; color: var(--fg-3); margin: -8px 0 18px; }
.robux-modal-ref strong { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-weight: 700; letter-spacing: 0.05em; color: var(--fg-2); }
.robux-modal-ref[hidden] { display: none; }
.co-msg-ref { display: block; margin-top: 6px; font-size: 11.5px; color: var(--fg-3); }
.co-msg-ref strong { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0.05em; }

@media (max-width: 480px) { .robux-modal { padding: 26px 20px 22px; } }

/* Roblox-only now - the mega-menu's platform tab row holds a single tab,
   so it's just a redundant label above the categories. */
.nav-mega-tabs { display: none; }

/* Admin sticky corner toast - save / failure feedback */
.adm-toast { position: fixed; right: 20px; bottom: 20px; z-index: 9000; max-width: 340px;
  padding: 13px 18px; border-radius: 10px; font-size: 13.5px; font-weight: 600; line-height: 1.4;
  color: #fff; background: #16181d; border: 1px solid var(--hairline); box-shadow: 0 10px 34px rgba(0,0,0,0.45);
  cursor: pointer; opacity: 0; transform: translateY(14px); pointer-events: none;
  transition: opacity .22s var(--ease), transform .22s var(--ease); }
.adm-toast.show { opacity: 1; transform: translateY(0); pointer-events: auto; }
.adm-toast.ok { border-color: rgba(52,224,138,0.4); box-shadow: 0 10px 34px rgba(0,0,0,0.45), inset 3px 0 0 var(--price); }
.adm-toast.err { border-color: rgba(255,77,68,0.45); box-shadow: 0 10px 34px rgba(0,0,0,0.45), inset 3px 0 0 var(--accent); }
@media (max-width: 480px) { .adm-toast { left: 12px; right: 12px; bottom: 12px; max-width: none; } }
