/* ═══════════════════════════════════════════════════════════════════════════
   THE WORLD BELOW — theme layer
   ───────────────────────────────────────────────────────────────────────────
   This file is loaded LAST on every page, after sweet_theme.css, _shared.css,
   topbar.css and any page styles. It does three things and nothing else:

     1. re-points the --sw-* token block (the site's single source of truth)
        at the new brand values
     2. re-colours the two atmosphere systems that carry hardcoded candy hues
        (fi-atmos and space-bg) so they read as storm, rock and water
     3. re-shapes the topbar into the new navigation: five big labelled tabs
        on the primary row, a contextual sub-tab row underneath

   Every colour traces to a pixel in the two reference images Ishaq sent on
   2026-08-02. The sampler is tools/extract_palette.py, the receipts are in
   brand/palette.json. Nothing here is an invented hue.

   WHY A LAYER AND NOT A REWRITE
   _shared.css already derives its whole token set (--bone, --accent, --glass,
   --display …) from --sw-*, so every app page re-themes from this one block
   with no markup change. Rewriting the pages would risk twenty regressions to
   achieve what eight lines of token override achieve safely.

   MUST NOT be "fixed" back:
   - The page floor is near-black on purpose. 26% of the reference is darker
     than #01050a. Lifting it to a comfortable grey kills the world.
   - The glow is an ACCENT. Never fill a large area with it.
   - Nav labels are ALWAYS VISIBLE. The old bar hid them behind a hover pill,
     which is the thing that made thirteen tabs unnavigable.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── display face: heavy condensed caps, closest match to the carved stone ──
   Anton is already self-hosted elsewhere in the fleet, so this adds no new
   external request, no hotlink and no new licence question. */
@font-face {
  font-family: 'Anton';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/anton-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2212, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Anton';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/anton-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ══ 1. TOKENS ═══════════════════════════════════════════════════════════ */

/* ── THE SHARED BRAND PALETTE ──────────────────────────────────────────────
   These nine names (void, ink, stone, carved, ice, cyan, mint, lightning,
   ember) are NOT this file's invention. Sibling sessions doing the Discord
   surfaces — chat log, utility panel, status, weather, kill feed — derived them
   from the same two reference images and have already rendered mockups Ishaq
   has looked at. The website has to be the same brand as the cards he sees in
   Discord, so this file adopts their values verbatim rather than shipping a
   second, slightly-different World Below.

   My own sampler agreed with them on hue and disagreed only on how bright the
   accent should be; theirs is the published one, so theirs wins. The surface
   ramp below extends their void/ink pair upward into the raised-panel steps a
   website needs and a Discord card does not — that part is mine, mixed in
   their hue.

   Contrast, measured against these surfaces: stone AA, everything else AAA.
   ─────────────────────────────────────────────────────────────────────────── */
:root,
:root[data-theme="dark"] {
  /* the shared nine */
  /* the page-floor scrim, as an rgb triplet. Every hero wash, nav bar and
     fade reads THIS rather than a literal, so the light variant is one line
     instead of thirteen that drift apart. */
  --twb-scrim-rgb: 5, 8, 11;
  --twb-void: #05080B;
  --twb-ink: #0B1116;
  --twb-stone: #7C8D9A;
  --twb-carved: #B0C0CB;
  --twb-ice: #DFEBF2;
  --twb-cyan: #4FD8D8;
  --twb-mint: #63D2A8;
  --twb-lightning: #9BAFF6;
  --twb-ember: #D77A5F;

  /* depth — void and ink extended into the panel steps a site needs */
  --sw-bg: #05080B;
  --sw-bg-2: #0B1116;
  --sw-panel: #101A22;
  --sw-panel-2: #16222C;

  /* stone — the carved letterforms under moonlight */
  --sw-ink: #DFEBF2;
  --sw-ink-2: #B0C0CB;
  --sw-ink-3: #7C8D9A;
  --sw-ink-rgb: 223, 235, 242;
  --sw-hair: rgba(223, 235, 242, .13);
  --sw-hair-2: rgba(223, 235, 242, .065);

  /* the cyan that glows out of the chasm is THE accent. Every --sw-pink* alias
     is repointed at it rather than deleted, so no page has to learn a new token
     name for the rename to reach it. */
  --sw-pink: #4FD8D8;
  --sw-pink-soft: #8FE9E9;
  --sw-accent: #4FD8D8;
  --sw-accent-rgb: 79, 216, 216;
  --sw-accent-ink: #05080B;
  --sw-wash-pink: rgba(79, 216, 216, .10);

  /* lightning is the second accent — the strike over the chasm */
  --sw-teal: #9BAFF6;
  --sw-teal-deep: #6C81C9;
  --sw-accent-2: #9BAFF6;
  --sw-wash-teal: rgba(155, 175, 246, .10);

  /* ember is the world's ONLY warm mark. It is rationed the way the bolt is
     rationed in the key art: warnings and the admin lane, nothing else. */
  --sw-orange: #D77A5F;
  --sw-warm-rgb: 215, 122, 95;

  --sw-shadow: 0 18px 48px -18px rgba(0, 0, 0, .78);
  --sw-nav-bg: rgba(var(--twb-scrim-rgb), .90);
  --sw-nav-sheet: rgba(var(--twb-scrim-rgb), .985);

  --sw-ok: #63D2A8;
  /* ★WARN IS NOT EMBER. Ember (#D77A5F) is the brand's warm mark, but it sits
     10 degrees of hue from any red, so using it for WARN made a warning and a
     failure the same colour to the eye - measured on the casino's own crash
     ladder, where a 1.5x payout and a total bust became indistinguishable.
     Warn is gold (36 degrees off), danger is a true red. Status colour is
     FUNCTIONAL; it does not get to be pretty at the cost of being read. */
  --sw-warn: #C2A15F;
  --sw-warn-rgb: 194, 161, 95;
  --sw-bad: #D3564E;
  --twb-ember-rgb: 215, 122, 95;

  --sw-glass: rgba(223, 235, 242, .05);
  --sw-glass-2: rgba(223, 235, 242, .03);
  --sw-glass-border: rgba(223, 235, 242, .12);

  --sw-tip-bg: #16222C;
  --sw-tip-ink: #DFEBF2;
  --sw-tip-border: rgba(223, 235, 242, .14);

  /* the display slot. --sw-sans stays Rajdhani: Anton is a poster face and is
     unreadable as body copy, so it is scoped to headings and nav below. */
  --sw-serif: 'Rajdhani', 'Segoe UI', system-ui, -apple-system, Arial, sans-serif;
  --twb-display: 'Anton', 'Rajdhani', 'Arial Narrow', system-ui, sans-serif;

  /* new layout metrics for the two-row nav */
  --twb-navrow: 72px;
  --twb-subrow: 48px;
}

/* The reference is a night world, so there is no true light mode any more.
   The toggle survives as an OVERCAST variant — the same storm at midday, wet
   pale rock instead of black — rather than being ripped out and leaving a dead
   button in the bar. */
/* ★Every token declared in the dark block MUST also be declared here. The dark
   block is `:root, :root[data-theme="dark"]`; in light mode only its `:root`
   half matches, and `sweet_theme.css`'s own `:root[data-theme="light"]` block
   is MORE specific, so any token missing from this list silently reverts to its
   old candy value. --sw-warm-rgb, --sw-warn-rgb and --sw-tip-border were all
   missing and were reverting across 35 usages in 8 files. */
:root[data-theme="light"] {
  --twb-scrim-rgb: 214, 222, 230;
  --twb-void: #C3CDD6;
  --twb-ink: #B4C0CB;
  --twb-stone: #566B7B;
  --twb-carved: #2F4250;
  --twb-ice: #0B141C;
  --twb-cyan: #1F5F69;
  --twb-mint: #1E7A5A;
  --twb-lightning: #3E5468;
  --twb-ember: #8A5030;
  --twb-warm-rgb: 138, 80, 48;
  --sw-warm-rgb: 155, 59, 54;
  --sw-warn-rgb: 138, 108, 46;
  --sw-tip-border: rgba(11, 20, 28, .16);
  --twb-navrow: 72px;
  --twb-subrow: 48px;
  --sw-bg: #C3CDD6;
  --sw-bg-2: #B4C0CB;
  --sw-panel: #E4EAEF;
  --sw-panel-2: #D6DEE6;
  --sw-ink: #0B141C;
  --sw-ink-2: #2F4250;
  --sw-ink-3: #566B7B;
  --sw-ink-rgb: 11, 20, 28;
  --sw-hair: rgba(11, 20, 28, .18);
  --sw-hair-2: rgba(11, 20, 28, .09);

  --sw-pink: #1F5F69;
  --sw-pink-soft: #2F7D88;
  --sw-accent: #1F5F69;
  --sw-accent-rgb: 31, 95, 105;
  --sw-accent-ink: #F2FAFB;
  --sw-wash-pink: rgba(31, 95, 105, .12);

  --sw-teal: #3E5468;
  --sw-teal-deep: #2A3D4E;
  --sw-accent-2: #3E5468;
  --sw-wash-teal: rgba(62, 84, 104, .12);

  --sw-orange: #8A6C2E;
  --sw-ok: #1F5F69;
  --sw-warn: #8A6C2E;
  --sw-bad: #9B3B36;

  --sw-glass: rgba(255, 255, 255, .5);
  --sw-glass-2: rgba(255, 255, 255, .32);
  --sw-glass-border: rgba(11, 20, 28, .16);

  --sw-nav-bg: rgba(224, 231, 237, .92);
  --sw-nav-sheet: rgba(228, 234, 239, .99);
  --sw-tip-bg: #FFFFFF;
  --sw-tip-ink: #0B141C;
  --sw-shadow: 0 18px 44px -18px rgba(11, 20, 28, .34);
}

/* ══ 2. THE WORLD ════════════════════════════════════════════════════════ */

/* The page's own tab strip is redundant once the sub-row carries the same
   tabs, and two rows of the same four words is the exact "hard to navigate"
   problem. topbar.js sets this attribute ONLY after proving the sub-row's
   hashes all match real buttons on the page, so if the router is ever wrong
   the strip stays visible and nothing becomes unreachable. */
body[data-twb-subtabs="on"] #shop-nav,
body[data-twb-subtabs="on"] #inv-nav { display: none !important; }

/* Two accents in one headline reads as an accident unless each means
   something. Above is the lightning; below is the glow out of the chasm. */
.hero h1 em,
.sw-display em { color: var(--twb-lightning); font-style: normal; }
.hero h1 strong,
.sw-display strong { color: var(--twb-cyan); font-weight: inherit; }

/* The landing hero was a flat wash. It gets the key art — but only the RIGHT
   portion of it. The full image has THE WORLD BELOW carved into its left half,
   and a page whose own headline sits over that reads as two headlines fighting.
   twb-plate-*.webp is the clean cliff/storm/valley region with no lettering in
   it, anchored right so the copy sits on near-solid void. */
/* ★★★OWNER, seeing it on his own wide monitor: "improve the design here it
   looks so bad and so wide".
   Two things were wrong and only the second is obvious in a 1600px screenshot:

   1. THE SEAM. Anchoring the art to the right edge and fading it with a
      left-to-right gradient works at 1600 and falls apart at 1950+ — the fade
      runs out of room and you get a hard vertical line down the middle of the
      page with black on one side and a photograph on the other. A scrim CANNOT
      hide an edge; the image has to stop being rectangular. It now carries a
      radial MASK, so it dissolves into the page on every side and there is no
      edge left to see at any width.
   2. THE STRANDED COLUMN. `.sw-wrap` centres at 1180px, so on a 1950px screen
      the copy starts 385px in with nothing to its left but black. The hero is a
      grid now: the column is pinned to the content edge and the art fills the
      rest, so widening the window grows the ART, not the dead band. */
.hero.sw-hero-wash {
  position: relative;
  overflow: hidden;
  padding: clamp(40px, 6vh, 72px) 0 clamp(36px, 6vh, 64px);
  background:
    radial-gradient(120% 90% at 50% 116%, rgba(79, 216, 216, .13), transparent 58%),
    linear-gradient(180deg, var(--sw-bg-2) 0%, var(--sw-bg) 74%);
}
.hero.sw-hero-wash::before {
  content: "";
  position: absolute;
  top: -6%;
  right: -2%;
  bottom: -6%;
  width: clamp(420px, 58%, 1080px);
  background: url('/assets/brand/twb-plate-1200.webp') center right / cover no-repeat;
  /* ★A radial mask still had ~29% alpha where the element's own left edge fell,
     and an element cannot fade past its own box - so the fade ended in a hard
     line anyway, which is exactly the seam he pointed at. A HORIZONTAL mask
     that reaches zero AT the left edge cannot leave one. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, .18) 22%, rgba(0, 0, 0, .62) 46%, #000 74%);
  mask-image: linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, .18) 22%, rgba(0, 0, 0, .62) 46%, #000 74%);
  opacity: .9;
  pointer-events: none;
  z-index: 0;
}
/* the floor still closes the hero off, but softly and without a hard band */
.hero.sw-hero-wash::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0; height: 46%;
  background: linear-gradient(180deg, transparent, var(--sw-bg) 92%);
  pointer-events: none;
  z-index: 0;
}
.hero.sw-hero-wash > * { position: relative; z-index: 1; }
/* the rim closes the hero off, the way the cliff edge does in the art */
.hero.sw-hero-wash::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0; height: 140px;
  background: linear-gradient(180deg, transparent, var(--sw-bg) 90%);
  pointer-events: none;
}
/* Below the two-column width the art would sit under the copy, so it drops to
   a faint wash instead of competing with it. */
@media (max-width: 900px) {
  .hero.sw-hero-wash {
    background:
      linear-gradient(180deg, rgba(var(--twb-scrim-rgb), .86) 0%, rgba(var(--twb-scrim-rgb), .93) 58%, var(--sw-bg) 100%),
      radial-gradient(110% 70% at 50% 112%, rgba(79, 216, 216, .16), transparent 58%),
      url('/assets/brand/twb-plate-640.webp') center top / cover no-repeat,
      var(--sw-bg);
  }
}

/* In overcast mode the copy goes dark, and the key art is a night photograph —
   dark type on it is unreadable. The art drops to a faint wash behind a pale
   scrim instead of being removed, so the hero still has a horizon in it.
   Everything below is here because the FIRST light-mode render came back as
   dark text on the dark plate with a dark bar: the theme layer had hardcoded
   the night values in a dozen places. A toggle that ships broken is a defect,
   so either it works or it goes. */
:root[data-theme="light"] .hero.sw-hero-wash {
  background:
    linear-gradient(90deg,
      var(--sw-bg) 0%, rgba(var(--twb-scrim-rgb), .97) 26%, rgba(var(--twb-scrim-rgb), .90) 40%,
      rgba(var(--twb-scrim-rgb), .82) 54%, rgba(var(--twb-scrim-rgb), .78) 76%, rgba(var(--twb-scrim-rgb), .86) 100%),
    linear-gradient(180deg, rgba(var(--twb-scrim-rgb), .70) 0%, rgba(var(--twb-scrim-rgb), .55) 40%, var(--sw-bg) 100%),
    url('/assets/brand/twb-plate-1200.webp') right center / auto 118% no-repeat,
    var(--sw-bg);
}
:root[data-theme="light"] h1,
:root[data-theme="light"] h2,
:root[data-theme="light"] .sw-display,
:root[data-theme="light"] .hero h1 { text-shadow: none; }
:root[data-theme="light"] .hero h1 em,
:root[data-theme="light"] .sw-display em { color: #2A3D6B; }
:root[data-theme="light"] .hero h1 strong,
:root[data-theme="light"] .sw-display strong { color: #10646E; }
:root[data-theme="light"] .sweet-nav {
  background: linear-gradient(180deg, rgba(var(--twb-scrim-rgb), .97), rgba(var(--twb-scrim-rgb), .90)) !important;
  border-bottom-color: rgba(11, 20, 28, .14) !important;
}
:root[data-theme="light"] .twb-subnav {
  background: linear-gradient(180deg, rgba(var(--twb-scrim-rgb), .97), rgba(var(--twb-scrim-rgb), .93));
  border-bottom-color: rgba(11, 20, 28, .12);
}
:root[data-theme="light"] .sweet-nav .brand > span { text-shadow: none; }
:root[data-theme="light"] .sweet-nav .links > a.active {
  background: linear-gradient(180deg, rgba(31, 95, 105, .07), rgba(31, 95, 105, .18)) !important;
  border-color: rgba(31, 95, 105, .34);
  box-shadow: inset 0 -2px 0 var(--sw-pink), 0 0 22px -8px rgba(31, 95, 105, .5);
}
:root[data-theme="light"] .sweet-nav .links > a:hover { background: rgba(11, 20, 28, .06) !important; }
:root[data-theme="light"] .twb-subnav a:hover { background: rgba(11, 20, 28, .07); }
:root[data-theme="light"] .twb-subnav a.active { background: rgba(31, 95, 105, .14); }
:root[data-theme="light"] .sw-card,
:root[data-theme="light"] .card,
:root[data-theme="light"] .sw-panel {
  background: linear-gradient(180deg, var(--sw-panel), var(--sw-panel-2)) !important;
  border-color: rgba(11, 20, 28, .12) !important;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5), 0 10px 26px -16px rgba(11, 20, 28, .45) !important;
}
:root[data-theme="light"] .sweet-nav .icon-btn,
:root[data-theme="light"] .sweet-nav .wallet-chip {
  border-color: rgba(11, 20, 28, .16) !important;
  background: rgba(255, 255, 255, .45) !important;
}
:root[data-theme="light"] .fi-bgposter,
:root[data-theme="light"] .fi-atmos,
:root[data-theme="light"] #space-bg { opacity: .18 !important; }

/* The mark used to be an absolutely-positioned lollipop on the right. The key
   art now owns that side, so the two were overlapping. The mark moves into the
   left column above the headline, in normal flow, where it reads as a logo
   rather than as decoration — and every one of the old absolute-position rules
   has to be unset explicitly or it keeps its old corner.

   Note the animation: the old keyframes animated `transform` outright, which
   silently threw away the -52% centring transform the base rule depended on.
   Anything that both centres and animates has to animate a property the
   centring does not use. */
.hero { display: block; }
.hero .hero-art,
img.hero-art {
  position: static !important;
  right: auto !important;
  top: auto !important;
  transform: none !important;
  display: block;
  width: clamp(120px, 13vw, 188px);
  height: auto;
  margin: 0 0 26px;
  opacity: .98;
  animation: twb-breathe 9s ease-in-out infinite;
  /* static, so it rasterises once */
  filter: drop-shadow(0 0 34px rgba(79, 216, 216, .32));
  will-change: opacity;
}
/* The copy column. On a wide monitor a centred 1180px wrap leaves a dead black
   band on the left and pushes the text into the middle of the art; capping the
   COLUMN and letting the hero grow around it keeps the reading line where the
   eye starts. */
@media (min-width: 901px) {
  .hero .sw-wrap {
    max-width: min(1320px, 92vw);
    display: grid;
    grid-template-columns: minmax(0, 34rem) 1fr;
    align-items: center;
    align-content: center;
    /* ★row-gap 0. `gap` is BOTH axes, and with one item per row that put 64px
       between the logo, the pill, the kicker, the headline and the lede. */
    row-gap: 0;
    column-gap: clamp(24px, 4vw, 64px);
    min-height: min(58vh, 520px);
  }
  .hero .sw-wrap > * { grid-column: 1; }
  .hero h1 { max-width: 14ch; font-size: clamp(38px, 4.4vw, 62px); }
  .hero .sw-lede { max-width: 44ch; }
}
/* past this width the column would drift away from the art, so the whole hero
   stops growing and centres instead */
@media (min-width: 1700px) {
  .hero .sw-wrap { max-width: 1520px; }
  .hero.sw-hero-wash::before { width: clamp(420px, 52%, 1000px); right: 0; }
}
@media (max-width: 900px) {
  .hero .hero-art,
  img.hero-art { display: block !important; width: clamp(104px, 22vw, 150px); }
}
/* ★★★ANIMATE OPACITY, NEVER A BLUR RADIUS. The first version animated
   drop-shadow's blur on a 512px PNG, which forces a full re-rasterisation of a
   large blur EVERY FRAME - it pegged a whole CPU core, and it did not show up
   as "the design looks wrong", it showed up as every headless render and even
   the live browser tab hanging. Opacity is compositor-only and free. */
@keyframes twb-breathe {
  0%, 100% { opacity: .9; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .hero .hero-art, img.hero-art { animation: none !important; }
}

/* The page floor. The old radial lifted the centre toward --bg-2; here the
   light comes from BELOW, the way it does in both reference images. */
html, body {
  background:
    radial-gradient(ellipse 120% 62% at 50% 118%, rgba(79, 216, 216, .10), transparent 58%),
    radial-gradient(ellipse 90% 55% at 50% -8%, rgba(155, 175, 246, .07), transparent 60%),
    linear-gradient(180deg, var(--sw-bg-2) 0%, var(--sw-bg) 62%) !important;
  background-attachment: fixed !important;
}

/* Headings are carved. Anton only ever appears at display sizes, in caps, with
   the airy tracking the reference lettering has. */
h1, h2,
.sw-display,
.hero h1,
.gate-msg h2 {
  font-family: var(--twb-display) !important;
  font-weight: 400 !important;
  letter-spacing: .055em;
  text-transform: uppercase;
  color: var(--sw-ink);
  text-shadow:
    0 2px 0 rgba(0, 0, 0, .55),
    0 0 42px rgba(79, 216, 216, .16);
}

/* Rock does not have a 12px radius. Everything squares off; the softness in the
   reference comes from light and erosion, not from corners. */
.sw-card, .sw-panel, .brand, .card, .panel,
.sw-btn, .icon-btn, .wallet-chip, button, input, select, textarea {
  border-radius: 3px !important;
}
.wallet-chip, .chip, .pill, .badge { border-radius: 999px !important; }

/* Panels read as wet stone slabs: a hairline top highlight where the moon
   catches the edge, and a floor shadow deep enough to sit in near-black. */
.sw-card, .card, .sw-panel {
  background: linear-gradient(180deg, var(--sw-panel-2), var(--sw-panel)) !important;
  border: 1px solid rgba(223, 235, 242, .10) !important;
  box-shadow:
    inset 0 1px 0 rgba(223, 235, 242, .055),
    0 10px 30px -14px rgba(0, 0, 0, .85) !important;
}

/* ── atmosphere: fi-atmos was ported from a green forest fork. Same layers,
   re-lit for storm and chasm. ─────────────────────────────────────────────── */
.fi-bgposter {
  background: #05080B url('/assets/brand/twb-hero-soft.webp') center / cover no-repeat !important;
  opacity: .55 !important;
}
@media (min-width: 1200px) {
  .fi-bgposter { background-image: url('/assets/brand/twb-hero-1920.webp') !important; opacity: .42 !important; }
}

.fi-bgscrim {
  background:
    linear-gradient(180deg, rgba(var(--twb-scrim-rgb), .92) 0%, rgba(var(--twb-scrim-rgb), .66) 26%, rgba(var(--twb-scrim-rgb), .58) 52%, rgba(var(--twb-scrim-rgb), .74) 78%, rgba(var(--twb-scrim-rgb), .98) 100%),
    radial-gradient(120% 80% at 50% 42%, transparent 32%, rgba(var(--twb-scrim-rgb), .6) 94%) !important;
}

/* the glow now comes up from the bottom of the screen, out of the chasm */
.fi-atmos {
  background:
    radial-gradient(120% 90% at 50% 118%, rgba(79, 216, 216, .16), transparent 54%),
    radial-gradient(80% 60% at 82% -10%, rgba(155, 175, 246, .07), transparent 60%) !important;
}
.fi-atmos .fi-f1 {
  background: radial-gradient(circle, rgba(20, 72, 72, .45), transparent 64%) !important;
}
.fi-atmos .fi-f2 {
  background: radial-gradient(circle, rgba(79, 216, 216, .20), transparent 62%) !important;
}
/* the canopy silhouette becomes the far cliff wall */
.fi-atmos .fi-canopy {
  background: linear-gradient(180deg, transparent, rgba(var(--twb-scrim-rgb), .68) 58%, #05080B 97%) !important;
}
.fi-atmos .fi-vig {
  background: radial-gradient(130% 100% at 50% 34%, transparent 50%, rgba(0, 0, 0, .6) 86%, rgba(0, 0, 0, .94)) !important;
}
.fi-atmos .fi-grain { opacity: .05 !important; }

/* ── space-bg: the /support backdrop shipped warm orange embers. Same motion,
   moonlit water instead. ──────────────────────────────────────────────────── */
#space-bg {
  background:
    radial-gradient(ellipse 56% 46% at 50% 118%, rgba(79, 216, 216, .12), transparent 58%),
    radial-gradient(ellipse 80% 60% at 20% 10%, rgba(155, 175, 246, .06), transparent 60%),
    radial-gradient(ellipse 60% 50% at 84% 70%, rgba(20, 72, 72, .07), transparent 60%),
    radial-gradient(ellipse at 50% 50%, #0B1116, #05080B 78%) !important;
}
#space-bg .sb-star { background: #DFEBF2 !important; }
#space-bg .sb-p {
  background: #8FE9E9 !important;
  box-shadow: 0 0 10px 2px rgba(79, 216, 216, .5) !important;
}
#space-bg .sb-pb { background: rgba(79, 216, 216, .16) !important; }

/* ── the voice dock ──────────────────────────────────────────────────────
   ★★★THIS ONE IS INVISIBLE TO EVERY SCREENSHOT. sws-voice-dock.js builds its
   stylesheet in JS and appendChild's it to <head> AFTER this file, and the dock
   itself stays `hidden` until a player actually connects to voice - so a hot
   pink pill was waiting to appear bottom-right on every page the moment anyone
   joined voice, and no render would ever have caught it. Its rules carry no
   !important, so !important here wins despite the later injection. Covering the
   state dots alone was not enough: the dock's own frame, ground and text are
   coloured too. */
#sws-voice-dock {
  background: rgba(11, 17, 22, .95) !important;
  border-color: rgba(79, 216, 216, .38) !important;
  color: var(--sw-ink) !important;
  box-shadow: 0 14px 34px -14px rgba(0, 0, 0, .9) !important;
}
#sws-voice-dock a,
#sws-voice-dock button { color: var(--sw-ink-2) !important; }
#sws-voice-dock .fvd-dot { background: var(--sw-pink) !important; }
#sws-voice-dock[data-state="reconnecting"] .fvd-dot {
  background: #D77A5F !important;
  box-shadow: 0 0 8px rgba(215, 122, 95, .8) !important;
}
#sws-voice-dock[data-state="error"] .fvd-dot {
  background: #C0554E !important;
  box-shadow: 0 0 8px rgba(192, 85, 78, .8) !important;
}
#sws-voice-dock a[data-active="true"] {
  background: rgba(79, 216, 216, .24) !important;
  border-color: #4FD8D8 !important;
  color: #DFEBF2 !important;
}
#sws-voice-dock a:hover { color: #DFEBF2 !important; }

/* ── strays that never went through a token ─────────────────────────────── */
.sw-rule::after,
.sw-rule::before { border-top-color: rgba(79, 216, 216, .4) !important; }

/* ══ 2b. THE THREE PAGES THAT OWN THEIR OWN PALETTE ══════════════════════
   Most of the site themes off --sw-*. Three pages do not, and each one had to
   be found by LOOKING at the render rather than by trusting the token graph:
   the studio bar came back hot pink, the voice hero came back plum, and the map
   came back warm amber. All three declare their own token block at :root, so
   this file has to redeclare the same names, later in document order. The
   theme-layer <link> is last in every page's head, which is what makes that
   work — moving it earlier silently restores the candy colours.
   ─────────────────────────────────────────────────────────────────────────── */

/* ── the skin studio: its own --sd-* system, deliberately never themed by the
      site toggle (a render stage needs a fixed dark surround to judge colour
      against). That ruling stands — the stage stays dark. Only the CHROME hue
      moves, so the studio stops being the one pink thing on the site. */
:root, body.studio {
  --sd-bg: #05080B;
  --sd-bg-2: #0B1116;
  --sd-panel: #101A22;
  --sd-panel-2: #16222C;
  --sd-raise: #1D2A35;
  --sd-ink: #DFEBF2;
  --sd-ink-2: #B0C0CB;
  --sd-ink-3: #7C8D9A;
  --sd-hair: rgba(223, 235, 242, .09);
  --sd-hair-2: rgba(223, 235, 242, .05);
  --sd-pink: #4FD8D8;
  --sd-teal: #9BAFF6;
  --sd-amber: #D77A5F;
  --sd-ok: #63D2A8;
  --sd-bad: #C0554E;

  --accent: #4FD8D8;
  --accent-rgb: 79, 216, 216;
  --accent-hot: #8FE9E9;
  --accent-glow: rgba(79, 216, 216, .55);
  --accent-06: rgba(79, 216, 216, .06);
  --accent-08: rgba(79, 216, 216, .08);
  --accent-10: rgba(79, 216, 216, .10);
  --accent-12: rgba(79, 216, 216, .12);
  --accent-14: rgba(79, 216, 216, .14);
  --accent-16: rgba(79, 216, 216, .16);
  --accent-22: rgba(79, 216, 216, .22);
  --accent-28: rgba(79, 216, 216, .28);
  --accent-32: rgba(79, 216, 216, .32);
  --accent-40: rgba(79, 216, 216, .40);
  --accent-45: rgba(79, 216, 216, .45);
  --accent-faint: rgba(79, 216, 216, .10);
  --amber: #D77A5F;
  --amber-hot: #E9A184;
  --glass-border-lit: rgba(79, 216, 216, .55);
}

/* ── the live map: it never loads _shared.css and instead hand-rolls its own
      copy of the derived token names, in WARM values (--bone is a cream
      #DFEBF2 there, against the real cool #DFEBF2). Two token blocks, same
      names, different colours — so both of the map's blocks get redeclared. */
:root {
  --bg-0: #05080B;
  --bg-1: #0B1116;
  --bg-void: #03060A;
  --bone: #DFEBF2;
  --bone-80: rgba(223, 235, 242, .82);
  --bone-62: rgba(223, 235, 242, .60);
  --bone-34: rgba(223, 235, 242, .36);
  --bone-18: rgba(223, 235, 242, .16);
  --bone-08: rgba(223, 235, 242, .07);
  --accent-hot: #8FE9E9;
  --crimson: #C0554E;
  --crimson-glow: rgba(192, 85, 78, .5);
  --crimson-text: #E39A94;
  --green: #63D2A8;
  --green-glow: rgba(99, 210, 168, .35);
  --amber-glow: rgba(215, 122, 95, .35);
  --glass: rgba(223, 235, 242, .05);
  --glass-2: rgba(223, 235, 242, .03);
  --glass-border: rgba(223, 235, 242, .10);
  --panel: rgba(16, 26, 34, .74);
  --panel-solid: rgba(11, 17, 22, .97);
}

/* ── proximity voice: NOTHING HERE ANY MORE, ON PURPOSE (2026-08-03).
      This block used to re-colour the voice hero's plum-to-dark banner and its
      candy wrapper stripes, with `!important`, because voice.css hardcoded
      those two candy values. Re-colouring them stopped the page being pink but
      left it shaped like a sweet shop: a 960px banner ending in a hard seam
      over a narrow ribbon of content. /voice/voice.css has since been rewritten
      in brand tokens and owns the whole page look, so these overrides had
      become an `!important` wall in front of the real design. Do not restore
      them — a voice colour belongs in voice.css now. */

/* ══ 3. NAVIGATION ═══════════════════════════════════════════════════════ */
/* Five big labelled tabs, one contextual sub-row. The old bar carried thirteen
   icon-only chips whose labels only appeared on hover; that is the specific
   thing being fixed here, so the label is never hidden again at any width
   above the mobile sheet breakpoint. */

.sweet-nav {
  background:
    linear-gradient(180deg, rgba(var(--twb-scrim-rgb), .96), rgba(var(--twb-scrim-rgb), .88)) !important;
  border-bottom: 1px solid rgba(223, 235, 242, .09) !important;
  box-shadow: 0 14px 40px -22px rgba(0, 0, 0, .95);
  backdrop-filter: blur(14px) saturate(115%);
  -webkit-backdrop-filter: blur(14px) saturate(115%);
}

/* topbar.css pins the bar at 60px and every page clears it with
   body{padding-top:60px}. The bar is taller now and carries a second row, so
   both numbers move together — miss one and every page's first heading hides
   under the nav. */
.sweet-nav { height: var(--twb-navrow) !important; }
body { padding-top: calc(var(--twb-navrow) + var(--twb-subrow)) !important; }
/* Map, Voice, Studio and Casino are single pages with no sub-row. topbar.js
   stamps this when it renders nothing, so the page does not reserve 48px for a
   strip that is not there. --topbar-h follows too, or the map is short by
   exactly that much. */
body[data-twb-subnav="none"] {
  padding-top: var(--twb-navrow) !important;
  --topbar-h: var(--twb-navrow);
}
body.atmosphere,
body[data-embedded="true"] { padding-top: 0 !important; }
/* the studio owns its own grid but still sits under the bar */
body.atmosphere .twb-subnav { display: none; }

/* ★★★THE ACTUAL CAUSE of the ten tabs not fitting. topbar.css caps .nav-in at
   1380px, so on a 1920 monitor the bar leaves 540px of screen unused and then
   overflows anyway — measured, not guessed: at both 1600 and 1920 the row
   needed 860px and had 678. Padding tweaks could never have fixed that. The bar
   now uses the viewport, with a sane ceiling so it does not sprawl on ultrawide.
   Budget after this change, measured: 1600 needs 1478 of 1600, 1440 needs 1418
   of 1440, and below 1240 the two optional icon buttons drop out. */
.sweet-nav .nav-in {
  min-height: var(--twb-navrow);
  gap: 10px;
  max-width: min(1880px, 97vw) !important;
  padding-left: clamp(12px, 1.6vw, 28px) !important;
  padding-right: clamp(12px, 1.6vw, 28px) !important;
}

/* the sheet's sub rows are mobile-only; on desktop the sub-row below the bar
   carries them instead */
@media (min-width: 861px) {
  .sweet-nav .links > a.twb-sub { display: none !important; }
}
/* the user dropdown hangs off the bar and was positioned against the old 60px */
.sweet-nav .user-menu,
#sky-user-menu { top: calc(var(--twb-navrow) + 6px) !important; }

/* brand lockup: the stone mark plus the wordmark in the display face */
.sweet-nav .brand {
  gap: 12px;
  padding: 6px 14px 6px 6px;
  background: transparent !important;
  border: 0 !important;
}
.sweet-nav .brand img {
  width: 46px; height: 46px;
  filter: drop-shadow(0 0 14px rgba(79, 216, 216, .35));
}
.sweet-nav .brand > span {
  font-family: var(--twb-display) !important;
  font-weight: 400 !important;
  font-size: 20px;
  line-height: .96;
  letter-spacing: .075em;
  text-transform: uppercase;
  color: var(--sw-ink);
  display: block;
  max-width: 8.5ch;
  text-shadow: 0 0 26px rgba(79, 216, 216, .3);
}
.sweet-nav .brand > span i {
  font-style: normal;
  color: var(--sw-pink-soft);
  display: block;
}

/* ── primary row: the five big tabs ─────────────────────────────────────── */
/* SCOPED TO DESKTOP ON PURPOSE. Below 861px topbar.css turns this very same
   .links element into the burger sheet (transform + visibility, .open reveals
   it). Restyling it unscoped — or worse, display:none — silently kills the
   mobile menu, because the sheet and the tab row are the same node. */
@media (min-width: 861px) {

/* ★TEN labelled tabs is a real width problem, and the honest answer is a
   budget, not wishful padding. At 1600 the brand takes ~180px and the right
   cluster ~460, leaving ~920 for ten tabs. The labels alone are ~630, so the
   padding and tracking have to come down as the window does — and below the
   point where even that fails, the row SCROLLS rather than silently dropping a
   tab off the end. A tab you cannot see is worse than a tab you have to
   scroll to. */
.sweet-nav .links {
  display: flex;
  align-items: stretch;
  gap: 0;
  flex: 1 1 auto;
  min-width: 0;
  justify-content: center;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  scroll-snap-type: x proximity;
}
.sweet-nav .links::-webkit-scrollbar { display: none; }
.sweet-nav .links > a { scroll-snap-align: start; }

.sweet-nav .links > a,
.sweet-nav .links > .nav-group > a {
  display: flex !important;
  align-items: center;
  gap: 10px;
  height: 46px;
  padding: 0 18px !important;
  border-radius: 3px !important;
  font-family: var(--twb-display) !important;
  font-weight: 400 !important;
  font-size: 16px !important;
  letter-spacing: .12em !important;
  text-transform: uppercase;
  color: var(--sw-ink-2) !important;
  white-space: nowrap;
  position: relative;
  border: 1px solid transparent;
  transition: color .18s var(--sw-ease), background .18s var(--sw-ease), border-color .18s var(--sw-ease);
}

/* the label is a real inline label now, never a hover-revealed pill */
.sweet-nav .links a > span {
  position: static !important;
  visibility: visible !important;
  opacity: 1 !important;
  transform: none !important;
  background: none !important;
  border: 0 !important;
  padding: 0 !important;
  box-shadow: none !important;
  font: inherit !important;
  letter-spacing: inherit !important;
  color: inherit !important;
  white-space: nowrap;
  pointer-events: none;
}
.sweet-nav .links a::after,
.sweet-nav .links a::before { content: none !important; }

/* ★NINE labelled tabs plus icons overflow the bar by ~250px at 1600. The label
   is the navigable part and the icon is decoration, so the icon goes on the
   primary row. It stays in the mobile sheet, where rows are full-width and the
   icon genuinely helps scanning. */
/* !important because topbar.css sets this with `header.sweet-nav nav.links a
   .nav-ico`, which outranks anything reasonable here on specificity alone. */
header.sweet-nav nav.links > a .nav-ico { display: none !important; }
.sweet-nav .links .nav-ico {
  width: 20px; height: 20px;
  flex: 0 0 auto;
  opacity: .75;
  transition: opacity .18s var(--sw-ease), color .18s var(--sw-ease);
}
.sweet-nav .links .nav-ico svg { width: 100%; height: 100%; display: block; }

.sweet-nav .links > a:hover,
.sweet-nav .links > .nav-group:hover > a,
.sweet-nav .links > .nav-group:focus-within > a {
  color: var(--sw-ink) !important;
  background: rgba(223, 235, 242, .045) !important;
  border-color: rgba(223, 235, 242, .08);
}
.sweet-nav .links > a:hover .nav-ico,
.sweet-nav .links > .nav-group:hover .nav-ico { opacity: 1; }

/* the active section is lit from below, like everything else in this world */
.sweet-nav .links > a.active,
.sweet-nav .links > .nav-group.active > a {
  color: var(--sw-ink) !important;
  background:
    linear-gradient(180deg, rgba(79, 216, 216, .06), rgba(79, 216, 216, .16)) !important;
  border-color: rgba(79, 216, 216, .32);
  box-shadow: inset 0 -2px 0 var(--sw-pink), 0 0 26px -6px rgba(79, 216, 216, .45);
}
.sweet-nav .links > a.active .nav-ico,
.sweet-nav .links > .nav-group.active .nav-ico {
  opacity: 1;
  color: var(--sw-pink-soft);
}

}  /* end min-width:861px — everything above is desktop-tabs only */

/* On the mobile sheet the rows are full-width list items, so they get the
   display face and the lit-active treatment without the desktop chrome. */
@media (max-width: 860px) {
  /* the sheet drops from under the bar, which is no longer 60px tall */
  .sweet-nav .links {
    top: var(--twb-navrow) !important;
    /* the old sheet was translucent over a light page; over near-black the
       headline reads straight through the menu items */
    background: var(--sw-nav-sheet) !important;
    backdrop-filter: blur(18px) saturate(120%);
    -webkit-backdrop-filter: blur(18px) saturate(120%);
    border-bottom: 1px solid rgba(223, 235, 242, .10);
    box-shadow: 0 22px 44px -18px rgba(0, 0, 0, .9);
    max-height: calc(100vh - var(--twb-navrow));
    overflow-y: auto;
  }
  .sweet-nav .links a {
    font-family: var(--twb-display) !important;
    font-weight: 400 !important;
    letter-spacing: .1em !important;
    text-transform: uppercase;
  }
  .sweet-nav .links a.active {
    color: var(--sw-ink) !important;
    background: rgba(79, 216, 216, .12) !important;
    box-shadow: inset 3px 0 0 var(--sw-pink);
  }
  .sweet-nav .links a.twb-sub {
    padding-left: 52px !important;
    font-family: var(--sw-sans) !important;
    font-size: 13.5px !important;
    letter-spacing: .06em !important;
    color: var(--sw-ink-3) !important;
  }
  .sweet-nav .links a.twb-sub.active { color: var(--sw-ink) !important; }
}

/* ── the sub-tab row ────────────────────────────────────────────────────── */
/* FIXED, not sticky. topbar.css pins the header with position:fixed, which
   takes it out of flow — a sticky sub-row would resolve its offset against the
   document, not against the bar, and would sit underneath it. */
.twb-subnav {
  position: fixed;
  top: var(--twb-navrow);
  left: 0; right: 0;
  z-index: 999;
  background: linear-gradient(180deg, rgba(var(--twb-scrim-rgb), .96), rgba(var(--twb-scrim-rgb), .92));
  border-bottom: 1px solid rgba(223, 235, 242, .07);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.twb-subnav-in {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 clamp(14px, 3.5vw, 40px);
  height: var(--twb-subrow);
  display: flex;
  align-items: center;
  gap: 2px;
  overflow-x: auto;
  scrollbar-width: none;
}
.twb-subnav-in::-webkit-scrollbar { display: none; }
/* On a phone the row scrolls, and a flush-cut last item looks like the row
   simply ends there. A fade on the trailing edge is the only affordance a
   touch device gets that there is more to the right. */
.twb-subnav { position: fixed; }
.twb-subnav::after {
  content: "";
  position: absolute; top: 0; right: 0; bottom: 0; width: 44px;
  background: linear-gradient(90deg, transparent, rgba(var(--twb-scrim-rgb), .96));
  pointer-events: none;
}
@media (min-width: 861px) {
  /* everything fits on desktop, so the hint would just be a smudge */
  .twb-subnav::after { display: none; }
}

.twb-subnav a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 14px;
  border-radius: 3px;
  font-family: var(--sw-sans);
  font-weight: 600;
  font-size: 13.5px;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--sw-ink-3);
  white-space: nowrap;
  text-decoration: none !important;
  transition: color .16s var(--sw-ease), background .16s var(--sw-ease);
}
.twb-subnav a:hover {
  color: var(--sw-ink);
  background: rgba(223, 235, 242, .05);
}
.twb-subnav a.active {
  color: var(--sw-ink);
  background: rgba(79, 216, 216, .14);
  box-shadow: inset 0 -2px 0 var(--sw-pink);
}
.twb-subnav a .dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: currentColor; opacity: .5;
}
.twb-subnav a.active .dot { background: var(--sw-pink-soft); opacity: 1; }

/* pages that opt out (the 3D studio, the full-bleed map) hide the row */
body[data-twb-subnav="off"] .twb-subnav { display: none; }

/* ── right cluster ──────────────────────────────────────────────────────── */
/* Measured, the cluster was 471px against ~900 of tabs. It is chrome, not
   destinations, so it gives up the last pixels the tab row needs. */
.sweet-nav .nav-cta { gap: 8px !important; }
.sweet-nav .wallet-chip {
  height: 36px;
  padding: 0 10px !important;
  gap: 10px !important;
  border: 1px solid rgba(223, 235, 242, .11) !important;
  background: rgba(223, 235, 242, .035) !important;
}
.sweet-nav .wallet-chip .w-seg { gap: 5px !important; }
.sweet-nav .wallet-chip svg { width: 15px; height: 15px; }
/* Fossils are the everyday money and Underworld Shards are the rare one, so the
   GEM gets the accent and the bone gets stone. The old chip had these the other
   way round, which made the common currency the brightest thing in the bar. */
.sweet-nav .wallet-chip .w-seg.cc { color: var(--sw-ink-2) !important; }
.sweet-nav .wallet-chip .w-seg.lp { color: var(--sw-pink-soft) !important; }
.sweet-nav .wallet-chip .w-seg.lp svg { filter: drop-shadow(0 0 6px rgba(79, 216, 216, .5)); }
.sweet-nav .icon-btn {
  width: 32px; height: 32px;
  border: 1px solid rgba(223, 235, 242, .10) !important;
  background: rgba(223, 235, 242, .03) !important;
  color: var(--sw-ink-2) !important;
}
.sweet-nav .icon-btn:hover {
  color: var(--sw-ink) !important;
  border-color: rgba(79, 216, 216, .35) !important;
  background: rgba(79, 216, 216, .10) !important;
}
/* the staff shield: always present for an admin, on every page */
#twb-admin-btn { display: inline-flex; align-items: center; justify-content: center; }
#twb-admin-btn svg { width: 19px; height: 19px; }
#twb-admin-btn.active {
  color: var(--sw-ink) !important;
  border-color: rgba(79, 216, 216, .45) !important;
  background: rgba(79, 216, 216, .16) !important;
}
@media (max-width: 560px) { #twb-admin-btn { display: none; } }

/* ── responsive ─────────────────────────────────────────────────────────── */
/* Five tabs fit labelled down to about 1080px. Below that the icon drops
   before the label does — the label is the navigable part, the icon is not. */
/* the ladder that makes ten fit: tracking first (cheapest to read), then
   padding, then size, then the brand wordmark, then the optional icon buttons */
/* The breakpoints are where the MEASUREMENTS put them, not round numbers.
   Order of sacrifice: tracking, then padding and size, then the two icon
   buttons that are not destinations, then the second wallet figure. A tab is
   never dropped — losing a destination is worse than any of these. */
@media (min-width: 861px) and (max-width: 1750px) {
  .sweet-nav .links > a { padding: 0 12px !important; letter-spacing: .08em !important; }
}
@media (min-width: 861px) and (max-width: 1500px) {
  .sweet-nav .links > a { padding: 0 9px !important; font-size: 15px !important; letter-spacing: .05em !important; }
  /* the wordmark is worth ~70px and the mark alone still says whose site this
     is; a destination is worth more than a logotype */
  .sweet-nav .brand > span { display: none; }
  .sweet-nav .brand img { width: 40px; height: 40px; }
}
@media (min-width: 861px) and (max-width: 1300px) {
  /* Discord and sound are the two the bar can lose without losing a place to go */
  .sweet-nav .icon-btn.discord-btn,
  .sweet-nav #sky-sound-toggle { display: none !important; }
  .sweet-nav .links > a { padding: 0 7px !important; font-size: 14.5px !important; }
}
@media (min-width: 861px) and (max-width: 1120px) {
  /* the wallet keeps the everyday figure; the premium one lives on the pages */
  .sweet-nav .wallet-chip .w-seg.lp { display: none !important; }
  .sweet-nav .brand > span { display: none; }
  .sweet-nav .links > a { padding: 0 6px !important; font-size: 14px !important; }
}
@media (max-width: 900px) {
  .sweet-nav .brand > span { display: none; }
  .sweet-nav .brand img { width: 40px; height: 40px; }
}
/* At 860px topbar.css hands the primary row to the burger sheet. The sub-row
   stays on screen and becomes the visible horizontal nav, which is why it is
   scrollable rather than wrapping. */
@media (max-width: 860px) {
  /* ★Must repeat BOTH selectors from the dark block. `:root` alone is (0,1,0)
     and loses to `:root[data-theme="dark"]` (0,2,0) declared above, so this
     override was dead in the default theme and only ever worked in light. */
  :root,
  :root[data-theme="dark"],
  :root[data-theme="light"] { --twb-navrow: 62px; --twb-subrow: 44px; }
  .twb-subnav-in { padding: 0 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .sweet-nav .links > a,
  .twb-subnav a { transition: none !important; }
}

/* ══ 4. PAGES THAT SIZE THEMSELVES AGAINST THE BAR ═══════════════════════ */

/* The live map computes `main { height: calc(100vh - var(--topbar-h)) }` and
   declares --topbar-h:60px, describing its own bar - which this redesign
   force-hides. Against the new 120px of body padding that made the page
   100vh + 60, and since html/body are overflow:hidden there, the extra 60px was
   not scrollable, it was CUT: the zoom cluster lost 22px and the zoom readout
   fell off the screen entirely. Any page that does its own viewport maths has
   to be told the real chrome height. */
:root { --topbar-h: calc(var(--twb-navrow) + var(--twb-subrow)); }
html.embedded { --topbar-h: 0px; }

/* The skin studio's body is class="studio", not class="atmosphere" - the
   opt-out below was written against a class no page has, so the studio was
   taking 120px of padding it never had. It owns its own grid. */
body.studio {
  padding-top: var(--twb-navrow) !important;
}
body.studio .twb-subnav { display: none; }
