/* ══════════════════════════════════════════════════════════════════════════
   AXIS — THE APP SHELL.  One chrome for every surface except the homepage.

   WHY THIS FILE EXISTS. Founder: "Build white label into the main
   application… everything goes into one except the homepage: all the brands,
   the white label, the vault, the center application. Then the homepage is the
   only thing outside it. That way it's easy to design for web and mobile."

   Before this there were THREE chromes running at once — axis-header.js (brand
   dropdown + top sheet) on two pages, axis-rail.js (side column) on nine, and
   tabbar.js (bottom dock) on fifteen — and a page could carry two of them at
   the same time. That is the whole of "when you go to the different platforms,
   the menu, when it collapses, still screws up": there was no single thing to
   collapse. Now there is one column of truth, presented two ways.

     >= 1080px   a fixed left rail, expanded or collapsed
     <  1080px   a slim top bar + the five-tab dock, and the SAME column as a
                 full-height sheet behind the menu button

   ONE VARIABLE DRIVES THE LAYOUT. `--shell-w` is set on <html> and is read by
   BOTH the rail's width and the body's padding, so the two can never disagree
   — which is how a collapse breaks a page. Collapsing writes one number.

   TRAPS THIS FILE IS WRITTEN AGAINST (see AXIS-1000X-V2.md §8):
     · §1  a cap on an inline box does nothing — every label is display:block
     · §2  source order beats a media query at equal specificity — the two
           presentations are in ONE @media each, declared after the shared base
     · §4  a width:100% child in an `auto` grid track resolves to zero — every
           grid track here is named
     · §7  an unresolvable var() kills the whole declaration — every var() in
           this file carries a fallback
     · §8  overflow-x:clip clips paint, not layout — the rail caps its width
     · §12 a :hover that says transform:none throws away a positioning
           transform, and on touch :hover sticks — nothing here hovers to none
   ══════════════════════════════════════════════════════════════════════════ */

:root {
  --shell-w-open:  268px;
  --shell-w-shut:   72px;   /* the later glass block hardcoded this; now one number */
  --shell-w:       var(--shell-w-open);
  /* ── THE ISLAND ─────────────────────────────────────────────────────────
     Founder, five times: "the side menu in the web app is still horrible. I
     hate it. You need to completely reimagine it and upgrade it… dramatically
     upgrade it instead of patching it." Then he sent the Pons launchpad as the
     reference, and that page answers the question it was sent to answer: every
     surface on it is a FLOATING PANEL with a real gap around it, nothing runs
     edge to edge. So the rail detaches from the window edge and floats as a
     rounded slab over the molten field — his "liquid glass floating between
     the cells", applied to the surface he looks at most.
     `--shell-gap` is the clear space on every side. It is read by the slab AND
     by the body's padding, so the two cannot disagree — the same discipline
     that made --shell-w work. */
  --shell-gap:      16px;
  --shell-r:        24px;   /* the reference's radius, 24-28px               */
  --shell-bar-h:    64px;   /* the mobile dock                                */
  --shell-top-h:    56px;   /* the mobile top bar                             */
  --shell-top-d:    64px;   /* the desktop header, right of the rail          */
  --shell-tap:      44px;
  --shell-ease:     cubic-bezier(.16, 1, .3, 1);
  --shell-line:     rgba(255, 255, 255, .11);
  --shell-line-2:   rgba(255, 255, 255, .06);
  --shell-bg:       #060607;
  --shell-ink:      #ffffff;
  --shell-ink-2:    rgba(255, 255, 255, .72);
  --shell-ink-3:    rgba(255, 255, 255, .50);
  --shell-ink-4:    rgba(255, 255, 255, .40);
  --shell-z:        900;

  /* AXIS IS SILVER IN THE MENU, NOT LIME. Brief §4. The lime is the brand's
     accent on its own page; in a list of six it has to read as the parent, and
     a lime row next to ZERA's lime row reads as two of the same brand. */
  --shell-tone:     #C9CBD1;
}

[data-brand="jetr"] { --shell-tone: #C9A227; }
[data-brand="zera"] { --shell-tone: #B6EA00; }
[data-brand="szn"]  { --shell-tone: #D64C63; }
[data-brand="ycht"] { --shell-tone: #7C93C9; }
[data-brand="gld"]  { --shell-tone: #E3B341; }
[data-brand="axis"] { --shell-tone: #C9CBD1; }

html[data-shell="min"] { --shell-w: var(--shell-w-shut); }

/* ── the space the chrome occupies ────────────────────────────────────────
   Set on <body>, not on <main>, so a full-bleed section inside main still
   bleeds to the edge of the content column rather than to the edge of the
   window with the rail painted over it. */
html[data-shell] body {
  padding-bottom: calc(var(--shell-bar-h, 64px) + env(safe-area-inset-bottom, 0px));
}

/* ══ 1 · THE RAIL ═══════════════════════════════════════════════════════ */

/* ⛔ A FLOATING SLAB, NOT A COLUMN GLUED TO THE EDGE. It was `inset: 0 auto 0 0`
   with a single `border-right` — an edge-to-edge panel, which is the one thing
   the reference page never does. Now it clears every edge by --shell-gap, takes
   the full radius, and carries a border on all four sides because all four are
   visible.
   ⚠ DARK GLASS, THE MEASURED RECIPE (trap §22): a near-opaque ground plus
   brightness() INSIDE the backdrop-filter. The light-glass recipe — a
   translucent white veil over a plain blur — is what made this read "too
   white" over a lime page, and blur alone carries the page's luminance
   straight through. */
.axsh {
  position: fixed;
  inset: var(--shell-gap, 16px) auto var(--shell-gap, 16px) var(--shell-gap, 16px);
  z-index: var(--shell-z, 900);
  width: var(--shell-w, 268px);
  max-width: var(--shell-w, 268px);   /* §8: cap, never clip                 */
  display: none;                      /* the phone presentation owns < 1080  */
  grid-template-rows: auto auto 1fr auto;
  grid-template-columns: minmax(0, 1fr);
  border-radius: var(--shell-r, 24px);
  background: color-mix(in srgb, var(--shell-bg, #060607) 88%, transparent);
  -webkit-backdrop-filter: blur(26px) saturate(115%) brightness(.6);
  backdrop-filter: blur(26px) saturate(115%) brightness(.6);
  border: 1px solid var(--shell-line, rgba(255, 255, 255, .11));
  box-shadow:
    0 30px 70px -22px rgba(0, 0, 0, .82),
    0 2px 10px -4px rgba(0, 0, 0, .6),
    inset 0 1px 0 rgba(255, 255, 255, .05);
  font-family: var(--ff-sans, 'Manrope', system-ui, sans-serif);
  /* ⚠ THE WIDTH IS THE ONLY THING THAT MOVES ON COLLAPSE, and it moves on the
     expo curve the rest of the system uses. Everything inside is laid out from
     --shell-w, so one number changes and nothing can disagree with it. */
  transition: width .42s var(--shell-ease, ease),
              max-width .42s var(--shell-ease, ease);
  overscroll-behavior: contain;
  overflow: hidden;                   /* the radius has to clip the column   */
}

/* ⚠ TWO TRACKS, NOT THREE. The head has two children — the brand and the
   collapse control. A third track left the collapse button floating in the
   middle instead of sitting at the right edge (the same defect the desktop
   header had; trap §19: declaring columns is not assigning them). */
.axsh__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  height: 72px;
  padding: 0 12px 0 16px;
  border-bottom: 1px solid var(--shell-line-2, rgba(255, 255, 255, .06));
}

/* the desktop menu button — same job, same icon and same sheet as the phone's
   burger, because they are the same control on two surfaces */
.axsh__railmenu {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  flex: none;
  border-radius: 9px;
  cursor: pointer;
  color: var(--shell-tone, #C9CBD1);
  background: color-mix(in srgb, var(--shell-tone, #C9CBD1) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C9CBD1) 32%, transparent);
  transition: background .2s var(--shell-ease, ease), border-color .2s var(--shell-ease, ease);
}
.axsh__railmenu:hover {
  background: color-mix(in srgb, var(--shell-tone, #C9CBD1) 20%, transparent);
  border-color: color-mix(in srgb, var(--shell-tone, #C9CBD1) 60%, transparent);
}
.axsh__railmenu svg { width: 16px; height: 16px; }
/* collapsed, the rail is one column wide — the menu button is the one that
   stays, because the collapse control has its own affordance in the chevron */
html[data-shell="min"] .axsh .axsh__railmenu { display: none; }

.axsh__wordmark {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  color: var(--shell-ink, #fff);
  text-decoration: none;
  font-family: var(--ff-display, 'Bebas Neue', sans-serif);
  font-size: 19px;
  letter-spacing: .13em;
  line-height: 1;
}
.axsh__wordmark svg { width: 20px; height: 20px; flex: none; color: var(--shell-tone, #C9CBD1); }

/* ── the collapse control ─────────────────────────────────────────────────
   See-through in EVERY state — resting, hover and pressed. Founder, twice.
   A solid accent fill has been caught on three other controls already. */
.axsh__min {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  flex: none;
  border-radius: 9px;
  cursor: pointer;
  color: var(--shell-ink-3, rgba(255, 255, 255, .5));
  background: color-mix(in srgb, var(--shell-tone, #C9CBD1) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C9CBD1) 22%, transparent);
  transition: color .2s var(--shell-ease, ease), background .2s var(--shell-ease, ease),
              border-color .2s var(--shell-ease, ease);
}
.axsh__min:hover {
  color: var(--shell-tone, #C9CBD1);
  background: color-mix(in srgb, var(--shell-tone, #C9CBD1) 14%, transparent);
  border-color: color-mix(in srgb, var(--shell-tone, #C9CBD1) 52%, transparent);
}
.axsh__min svg { width: 15px; height: 15px; transition: transform .42s var(--shell-ease, ease); }
html[data-shell="min"] .axsh__min svg { transform: scaleX(-1); }

/* ── the brand chip ──────────────────────────────────────────────────────
   The one control that changes who the whole app belongs to. Pressing it
   opens the card spread. */
.axsh__brand {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) 16px;
  align-items: center;
  gap: 11px;
  width: 100%;
  min-height: var(--shell-tap, 44px);
  margin: 0;
  padding: 11px 14px 11px 18px;
  text-align: left;
  cursor: pointer;
  color: var(--shell-ink, #fff);
  background: color-mix(in srgb, var(--shell-tone, #C9CBD1) 7%, transparent);
  border: 0;
  border-bottom: 1px solid var(--shell-line-2, rgba(255, 255, 255, .06));
  font: inherit;
  transition: background .24s var(--shell-ease, ease);
}
.axsh__brand:hover { background: color-mix(in srgb, var(--shell-tone, #C9CBD1) 14%, transparent); }

/* ⛔ BLACK PLATE, THEN THE LOGO. Founder: "The GLD at the top left needs to be
   the black background, then the logo. The same thing for all of them when you
   click it: the black background, then the logo. For when you click any of the
   pages at the top left."
   It was a tinted plate — the brand's accent at 13% — so a gold mark sat on a
   gold wash and a lime mark on a lime wash, and every mark lost contrast
   against its own background. Black behind the mark is the one ground that
   works for all six, and it is how the mark is drawn everywhere else on the
   site. The accent stays on the hairline border, where it identifies the brand
   without competing with it. */
.axsh__glyph {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: 11px;
  color: var(--shell-tone, #C9CBD1);
  background: #000;
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C9CBD1) 34%, transparent);
}
.axsh__glyph svg { width: 22px; height: 22px; }

/* ── the real brand artwork ───────────────────────────────────────────────
   §13: a host page's `img{max-width:100%}` silently clamps anything a
   component sizes, so every one of these carries an explicit width AND height
   and an explicit max-width. The marks are square source files; object-fit
   keeps them square whatever the box does. */
.axsh__mk {
  width: 22px; height: 22px;
  max-width: none; max-height: none;
  object-fit: contain;
  display: block;
  flex: none;
}

/* ⛔ THE DARK SQUARE BEHIND EVERY BRAND MARK. Measured, not guessed: five of
   the six icon files (jetr, zera, szn, ycht, gld) are FULLY OPAQUE 256x256
   webps whose ground is near-black — alpha range 255-255, no transparency at
   all. Only icon-axis.webp has a real alpha channel. So every mark was
   painting its own black square inside the round chip and inside the orb,
   which is what put a hard-edged box around the JETR mark in the middle of a
   glowing sphere.
   `screen` makes black transparent by definition, so the baked ground drops
   out and the mark sits on whatever is behind it. This is the same fix, and
   the same root cause, as the staking-pool marks in axis-pools.js — the art
   was authored for a black page and then used on plates. */
/* ⛔ THE MASK IS GONE. It existed to strip the opaque near-black ground these
   icon files carry — but every plate that holds one is BLACK now, on his
   instruction ("the black background, then the logo"), and a black ground on a
   black plate is invisible without any help. Masking also cost the thing he
   actually wanted: a mask can only paint ONE colour, so JETR's gold, ZERA's
   lime and Google's four colours all came out as a single flat tone. Full
   colour, on black, no processing. */
img.axsh__mk { opacity: 1; }
.axsh__chip[aria-current] .axsh__mk,
.axsh__chip:hover .axsh__mk { filter: brightness(1.12); }
/* the AXIS mark is a vector and takes the tone; no plate, no radius — a second
   rounded container around a rounded mark is what made it read as a lump */
.axsh__mk--v { color: var(--shell-tone, #C9CBD1); }
.axsh__glyph .axsh__mk { width: 24px; height: 24px; }
.axsh__topbrand .axsh__glyph .axsh__mk { width: 19px; height: 19px; }
.axsh__chip .axsh__mk { width: 17px; height: 17px; }
.axsh__bcard .axsh__mk { width: 24px; height: 24px; }
.axsh__orb .axsh__core .axsh__mk { width: 23px; height: 23px; }

.axsh__brandtext { min-width: 0; display: block; }
.axsh__brandname {
  display: block;                     /* §1: the cap only works on a block   */
  font-family: var(--ff-display, 'Bebas Neue', sans-serif);
  font-size: 17px; letter-spacing: .1em; line-height: 1.1;
  color: var(--shell-ink, #fff);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.axsh__brandline {
  display: block;
  margin-top: 2px;
  font-size: 11.5px; line-height: 1.25; letter-spacing: .01em;
  color: var(--shell-ink-4, rgba(255, 255, 255, .4));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The chevron POINTS DOWN, because this control is a dropdown. One glyph is
   reused for the rail's back-arrow and this, so the rotation lives here rather
   than in a second path. */
.axsh__chev { transform: rotate(90deg); }
.axsh__brand svg.axsh__chev { width: 14px; height: 14px; color: var(--shell-ink-4, rgba(255, 255, 255, .4)); }

/* ── the scrolling column ────────────────────────────────────────────── */
.axsh__scroll {
  min-height: 0;
  overflow-y: auto;
  /* hidden, not clip: this column must never scroll sideways, and something
     inside it (the brand rail) legitimately does */
  overflow-x: hidden;
  padding: 14px 10px 18px;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, .14) transparent;
}
.axsh__scroll::-webkit-scrollbar { width: 7px; }
.axsh__scroll::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .13); border-radius: 4px; }

/* ⛔ A GROUP HEADING IS A DIVIDER, NOT A COMPETING LABEL. It was set at the
   same weight as the rows it introduces — 700, wide tracking, full ink-4 — so
   a column of five rows read as ten items. The reference does this with a
   hairline and a very quiet word: the rule carries the structure, the word
   only names it. Lower weight, tighter tracking, dimmer ink, and a line that
   runs to the end of the column. */
.axsh__gh {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  margin: 18px 0 8px;
  padding: 0 10px;
  font-size: 9.5px; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase;
  color: color-mix(in srgb, var(--shell-ink, #fff) 28%, transparent);
}
.axsh__gh::after {
  content: '';
  height: 1px;
  background: var(--shell-line-2, rgba(255, 255, 255, .06));
}
.axsh__scroll > .axsh__gh:first-child { margin-top: 2px; }

.axsh__row {
  position: relative;
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  align-items: center;
  gap: 11px;
  min-height: var(--shell-tap, 44px);
  padding: 0 10px;
  border-radius: 10px;
  color: var(--shell-ink-2, rgba(255, 255, 255, .72));
  text-decoration: none;
  font-size: 13.5px; font-weight: 500; letter-spacing: .005em;
  transition: color .2s var(--shell-ease, ease), background .2s var(--shell-ease, ease);
}
.axsh__row:hover { color: var(--shell-ink, #fff); background: rgba(255, 255, 255, .05); }
.axsh__row svg { width: 18px; height: 18px; justify-self: center; flex: none; }

/* ── a page SECTION is a position, not a destination ─────────────────────
   These rows used to carry an exclamation-mark-in-a-circle, so the menu read
   as a list of warnings. A section gets a position marker: a hollow dot that
   fills and takes the brand tone when you are standing in that section. The
   rows also sit one step in, so "this page" reads as a sub-list of where you
   already are rather than as more places to go. */
.axsh__row--sec { font-size: 13px; color: var(--shell-ink-3, rgba(255, 255, 255, .5)); }
/* ⛔ THE DOT IS GONE. Founder: "These little dots next to the page: those all
   need icons, not little dots next to them. That changes everything."
   Twice this row has carried a placeholder — an exclamation-in-a-circle, then
   a bare dot — and both said the same thing: we did not know what the section
   was. We do; its heading says so. iconForSection() matches it in
   axis-app.js, and the fallback is a real marker rather than a shape. */
.axsh__row--sec svg { opacity: .82; }
.axsh__row--sec:hover svg,
.axsh__row--sec[aria-current] svg { opacity: 1; color: var(--shell-tone, #C9CBD1); }
.axsh__label {
  display: block;                     /* §1 again                            */
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ══ THE SLIDING PILL ═════════════════════════════════════════════════════
   ONE element, positioned and sized from JS, travelling between rows on the
   expo curve. The row keeps its ink and icon tone; the pill is the only thing
   that carries the wash and the tone bar, so the lit state is a thing that
   MOVES rather than two backgrounds blinking.
   ⚠ It is `position:absolute` inside `.axsh__scroll`, which is the scrolling
   box — so it travels with the list instead of detaching from it, and the
   offsets JS writes are offsetTop/offsetHeight in that same box. */
.axsh__pill {
  position: absolute;
  left: 10px;
  top: 0;
  width: calc(100% - 20px);
  height: 0;
  margin: 0;
  border-radius: 10px;
  pointer-events: none;
  opacity: 0;
  background: color-mix(in srgb, var(--shell-tone, #C9CBD1) 13%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--shell-tone, #C9CBD1) 22%, transparent);
  transform: translate3d(0, 0, 0);
  transition:
    transform .44s var(--shell-ease, ease),
    height    .44s var(--shell-ease, ease),
    opacity   .22s var(--shell-ease, ease);
  will-change: transform, height;
}
/* the tone bar rides ON the pill, so it moves with it instead of being a
   separate mark that has to be repainted on a different row */
.axsh__pill::before {
  content: '';
  position: absolute;
  left: 0; top: 50%;
  width: 3px; height: 20px;
  margin-top: -10px;                  /* §17: never a % inside translate      */
  border-radius: 0 3px 3px 0;
  background: var(--shell-tone, #C9CBD1);
}
/* ⛔ COLLAPSED, THE ROWS ARE 44px CENTRED SQUARES, SO THE PILL MUST BE TOO.
   Left at the expanded geometry it would have been a full-width bar behind a
   44px icon — the lit state wider than the thing it lights. */
html[data-shell="min"] .axsh .axsh__pill {
  left: 50%;
  width: 44px;
  margin-left: -22px;
  border-radius: 12px;
}
html[data-shell="min"] .axsh .axsh__pill::before { display: none; }

/* ⛔ NO FIRST-PAINT FLIGHT. Without this the pill animates from 0,0 to the
   current row every single load — a menu that appears to be still settling
   when the page opens. The class is removed one frame after it is placed. */
.axsh__scroll--set .axsh__pill { opacity: 1; }
.axsh__pill--jump { transition: opacity .22s var(--shell-ease, ease) !important; }

/* the lit row keeps the INK; the pill brings the background */
.axsh__row[aria-current] { color: var(--shell-ink, #fff); }
.axsh__row[aria-current] svg { color: var(--shell-tone, #C9CBD1); }
/* the row sits above the pill it is lit by */
.axsh__row { z-index: 1; }

/* ── secondary controls are 999px pills, per the reference ─────────────── */
.axsh__row--pill {
  border-radius: 999px;
  background: color-mix(in srgb, var(--shell-ink, #fff) 4%, transparent);
  box-shadow: inset 0 0 0 1px var(--shell-line-2, rgba(255, 255, 255, .06));
}
.axsh__row--pill:hover {
  background: color-mix(in srgb, var(--shell-ink, #fff) 8%, transparent);
}

.axsh__foot {
  padding: 10px;
  border-top: 1px solid var(--shell-line-2, rgba(255, 255, 255, .06));
}

/* ── collapsed ───────────────────────────────────────────────────────────
   ⛔⛔ EVERY RULE HERE IS SCOPED TO `.axsh` — THE RAIL — AND NOT TO THE
   DOCUMENT. That scoping is the whole fix for a defect he caught on desktop:
   the sheet's rows had icons and NO TEXT.

   `html[data-shell="min"]` is set on the root, so a rule written as
   `html[data-shell="min"] .axsh__label` matched EVERY .axsh__label on the page
   — including the ones inside the menu sheet, which is a completely different
   surface that is never collapsed. Collapse the rail once and the sheet lost
   every label it had, permanently, on a state the sheet has no say in.
   The attribute still lives on <html> because the body's padding has to read
   it too; what changed is that the rail's own rules now say WHICH rail.

   Labels go to zero width AND zero opacity: width alone leaves a sliver,
   opacity alone leaves the grid track. */
html[data-shell="min"] .axsh .axsh__label,
html[data-shell="min"] .axsh .axsh__brandtext,
html[data-shell="min"] .axsh .axsh__wordmark span,
html[data-shell="min"] .axsh .axsh__chev {
  width: 0; opacity: 0; pointer-events: none;
}
/* collapsed, the heading IS the divider — the word has nowhere to go, and a
   truncated word is worse than none */
html[data-shell="min"] .axsh .axsh__gh {
  display: block;
  height: 1px; margin: 14px 18px 10px; padding: 0;
  overflow: hidden; color: transparent;
  background: var(--shell-line-2, rgba(255, 255, 255, .06));
}
html[data-shell="min"] .axsh .axsh__gh::after { display: none; }
html[data-shell="min"] .axsh .axsh__row,
html[data-shell="min"] .axsh .axsh__brand { grid-template-columns: minmax(0, 1fr); gap: 0; }
html[data-shell="min"] .axsh .axsh__brand { justify-items: center; padding-inline: 0; }
/* ⛔ COLLAPSED, THE HEAD IS ONE CENTRED COLUMN OF IDENTICAL TARGETS. This is
   the objection that sent the collapse control to the foot in the first place:
   at 72px the brand square sat at the top with a button of a different shape
   stacked under it, so the column opened with two mismatched things before the
   navigation started. The control stays at the top — his call — and the
   objection is answered properly instead, by making both children the same
   34px centred square with the same radius. Two of one shape is a column; one
   of each is a mess. */
html[data-shell="min"] .axsh .axsh__head {
  height: auto;
  padding: 13px 0;
  gap: 10px;
  justify-items: center;
  grid-template-columns: minmax(0, 1fr);
}
html[data-shell="min"] .axsh .axsh__wordmark { justify-content: center; gap: 0; }
html[data-shell="min"] .axsh .axsh__row[aria-current]::before { left: -2px; }

/* ⛔ THE PAGE-SECTION ROWS GO WHEN THE RAIL COLLAPSES. He screenshotted the
   collapsed rail and it was a column of real icons followed by five tiny
   identical DOTS — because a page section has no icon to shrink to, only a
   position marker, and a position marker with no label beside it marks
   nothing. A collapsed rail is an icon rail; anything with no icon does not
   belong in it. They come straight back on expand, and the sheet still lists
   every one of them. */
/* ⚠ THEY STAY WHEN COLLAPSED NOW. They were hidden because a page section had
   no icon to shrink to — only a dot, and a column of identical dots marks
   nothing. Every section carries a real icon matched to its own heading, so a
   collapsed rail can show them like any other row. */
html[data-shell="min"] .axsh .axsh__gh--sec { display: none; }

/* a collapsed row still says what it is, on hover */
.axsh__row .axsh__tip {
  position: absolute;
  left: calc(100% + 12px); top: 50%;
  margin-top: -14px;                  /* §17                                  */
  padding: 7px 11px;
  border-radius: 8px;
  background: var(--surface-4, #26262B);
  border: 1px solid var(--shell-line, rgba(255, 255, 255, .11));
  color: var(--shell-ink, #fff);
  font-size: 12px; white-space: nowrap;
  opacity: 0; visibility: hidden;
  transition: opacity .18s var(--shell-ease, ease), visibility .18s;
  pointer-events: none;
  z-index: 2;
}
html[data-shell="min"] .axsh .axsh__row:hover .axsh__tip { opacity: 1; visibility: visible; }

/* (the desktop block that used to sit here has MOVED to the end of this file.
   It re-declares .axsh__top, whose base rule is defined further down — and at
   equal specificity SOURCE ORDER decides, so the mobile base was overwriting
   the desktop override and the header rendered 56px tall at x=0, straight over
   the rail. Trap §2, for the third time on this project. An override has to
   come after the thing it overrides.) */


/* ══ 2 · THE PHONE — top bar + dock + sheet ════════════════════════════ */

.axsh__top {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: var(--shell-z, 900);
  height: calc(var(--shell-top-h, 56px) + env(safe-area-inset-top, 0px));
  padding: env(safe-area-inset-top, 0px) 12px 0 14px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  background: color-mix(in srgb, var(--shell-bg, #060607) 86%, transparent);
  backdrop-filter: blur(16px) saturate(1.3);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
  border-bottom: 1px solid var(--shell-line-2, rgba(255, 255, 255, .06));
}
html[data-shell] body { padding-top: calc(var(--shell-top-h, 56px) + env(safe-area-inset-top, 0px)); }
/* the sticky second row sits under whichever header is on screen */
@media (min-width: 1080px) { .axsh__sub { top: var(--shell-top-d, 64px); } }

.axsh__topbrand {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr) 14px;
  align-items: center;
  gap: 9px;
  min-width: 0;
  min-height: var(--shell-tap, 44px);
  padding: 0;
  background: none; border: 0;
  color: var(--shell-ink, #fff);
  font: inherit; text-align: left; cursor: pointer;
}
.axsh__topbrand .axsh__glyph { width: 30px; height: 30px; border-radius: 9px; }
.axsh__topbrand .axsh__glyph svg { width: 17px; height: 17px; }
.axsh__topbrand .axsh__brandname { font-size: 15px; }
.axsh__topbrand .axsh__brandline { display: none; }
.axsh__topbrand svg.axsh__chev { width: 12px; height: 12px; }

.axsh__burger {
  width: var(--shell-tap, 44px); height: var(--shell-tap, 44px);
  display: grid; place-items: center;
  border-radius: 11px;
  cursor: pointer;
  color: var(--shell-tone, #C9CBD1);
  background: color-mix(in srgb, var(--shell-tone, #C9CBD1) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C9CBD1) 32%, transparent);
  backdrop-filter: blur(6px);
}
.axsh__burger svg { width: 18px; height: 18px; }

/* ── the dock. His five, and they never rewrite themselves. ───────────── */
.axsh__dock {
  position: fixed;
  inset: auto 0 0 0;
  z-index: var(--shell-z, 900);
  height: calc(var(--shell-bar-h, 64px) + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  align-items: stretch;
  background: color-mix(in srgb, var(--shell-bg, #060607) 88%, transparent);
  backdrop-filter: blur(18px) saturate(1.3);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  border-top: 1px solid var(--shell-line, rgba(255, 255, 255, .11));
}
.axsh__tab {
  display: grid;
  grid-template-rows: auto auto;
  align-content: center;
  justify-items: center;
  gap: 4px;
  min-height: var(--shell-tap, 44px);
  text-decoration: none;
  color: var(--shell-ink-4, rgba(255, 255, 255, .4));
  font-size: 9px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  transition: color .2s var(--shell-ease, ease);
}
.axsh__tab svg { width: 20px; height: 20px; }
.axsh__tab span { display: block; }
.axsh__tab[aria-current] { color: var(--shell-tone, #C9CBD1); }

/* ══ THE ORB, EXACTLY AS IT WAS ═══════════════════════════════════════════
   Founder, 2026-09-19: "Put the center orb back to how it was before."
   The first version of this shell drew a plain disc with an icon in it, which
   threw away the thing he had already asked to have restored once: the
   counter-rotating orbital rings, the breathing core and the tracking spark.
   This is tabbar.js's orb, ported whole — same geometry, same four ring axes,
   same timings, same reasoning:
     · NO SOLID DISC. An opaque sphere with rings around it reads as a planet —
       on YCHT's pale periwinkle he called it "this spaceman". The body is an
       accent haze that fades to nothing at the rim, so it is a glow with rings
       through it rather than an object with a surface.
     · THE MARK IS NOT A DOT. It gets room, no fill behind it, and its glow cast
       outward — otherwise the brand glyph renders at 12px behind its own light
       and every orb on the site reads as a bright blob.
     · IT BREATHES ON ITS GLOW, NOT ITS SIZE. Scaling a logo every 2.8s makes
       the mark swim.
   §12: the orb is positioned BY a transform, and nothing here may ever say
   transform:none — on touch :hover sticks and it would stay moved. It also
   takes no pointer events at all; the anchor around it does. */
.axsh__orb {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  transform-style: preserve-3d;
  display: block;
  /* ⚠ BIGGER, ON HIS INSTRUCTION: "just make the center button a little bit
     larger." It can grow freely now that the label is gone AND because it is
     absolutely positioned — it contributes nothing to the bar's height, so a
     larger orb cannot push the dock open the way a negative margin did. */
  width: 68px; height: 68px;
  margin: 0;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle at 50% 46%,
    color-mix(in srgb, var(--shell-tone, #C8FF00) 20%, transparent) 0%,
    color-mix(in srgb, var(--shell-tone, #C8FF00) 9%, transparent) 52%,
    transparent 80%);
  box-shadow: 0 0 18px color-mix(in srgb, var(--shell-tone, #C8FF00) 26%, transparent);
}
.axsh__orb * { pointer-events: none; }

/* orbital rings — thin, luminous, each on its own axis */
.axsh__orb i {
  position: absolute; inset: 4px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C8FF00) 82%, transparent);
  border-left-color: transparent; border-right-color: transparent;
  box-shadow: 0 0 6px color-mix(in srgb, var(--shell-tone, #C8FF00) 62%, transparent),
              inset 0 0 5px color-mix(in srgb, var(--shell-tone, #C8FF00) 34%, transparent);
}
.axsh__orb .r1 { animation: axshRing1 3.1s linear infinite; }
.axsh__orb .r2 { animation: axshRing2 4.3s linear infinite; }
.axsh__orb .r3 { animation: axshRing3 5.2s linear infinite; }
.axsh__orb .r4 {
  inset: 11px;
  border-color: color-mix(in srgb, var(--shell-tone, #C8FF00) 55%, transparent);
  border-top-color: transparent; border-bottom-color: transparent;
  animation: axshRing4 2.4s linear infinite;
}
@keyframes axshRing1 { from { transform: rotateX(74deg) rotateZ(0); } to { transform: rotateX(74deg) rotateZ(360deg); } }
@keyframes axshRing2 { from { transform: rotateY(74deg) rotateZ(0); } to { transform: rotateY(74deg) rotateZ(-360deg); } }
@keyframes axshRing3 { from { transform: rotate3d(1,1,0,58deg) rotateZ(0); } to { transform: rotate3d(1,1,0,58deg) rotateZ(360deg); } }
@keyframes axshRing4 { from { transform: rotate3d(1,-1,0,46deg) rotateZ(0); } to { transform: rotate3d(1,-1,0,46deg) rotateZ(-360deg); } }

/* the mark at the centre — room, no fill, glow cast outward */
.axsh__orb .axsh__core {
  position: absolute; left: 50%; top: 50%;
  width: 26px; height: 26px;
  margin: -13px 0 0 -13px;          /* §17: never a % inside translate */
  z-index: 3;
  display: grid; place-items: center;
  color: var(--shell-tone, #C8FF00);
  /* ⛔ NO OPACITY ANIMATION ON THIS ELEMENT. It used to breathe between .92 and
     1, and ANY opacity below 1 creates a stacking context — which isolates the
     blend group and stops mix-blend-mode on the mark inside from ever reaching
     the orb behind it. That is the SECOND isolating property this element
     carried (the first was a drop-shadow filter), and each one on its own was
     enough to keep the black square on screen while the CSS read as correct.
     The breathe moves onto the mark itself, where it is the only thing in play. */
}
/* ⛔ .core USED TO CARRY A drop-shadow FILTER AND THAT IS WHY THE BLEND ABOVE
   DID NOTHING. A filtered element becomes a new BACKDROP ROOT: mix-blend-mode
   on anything inside it blends against that isolated group instead of against
   what is actually behind it. So the mark's baked black ground kept painting a
   hard square in the middle of the orb even with `screen` set on it, and the
   CSS looked completely correct while changing nothing on screen.
   The glow moves to the VECTOR mark only — an inline SVG has no ground to
   blend away, so it can afford a filter — and the raster marks get their lift
   from the orb's own light instead. */
.axsh__orb .axsh__core .axsh__mk--v {
  filter: drop-shadow(0 0 5px color-mix(in srgb, var(--shell-tone, #C8FF00) 78%, transparent))
          drop-shadow(0 0 13px color-mix(in srgb, var(--shell-tone, #C8FF00) 45%, transparent));
}
.axsh__orb .axsh__core img.axsh__mk {
  mix-blend-mode: screen;
  filter: brightness(1.3) contrast(1.05);
  animation: axshMark 2.6s cubic-bezier(.16, 1, .3, 1) infinite;
}
.axsh__orb .axsh__core .axsh__mk--v { animation: axshMark 2.6s cubic-bezier(.16, 1, .3, 1) infinite; }
.axsh__orb .axsh__core svg { width: 100%; height: 100%; display: block; }
.axsh__orb .axsh__core img { width: 23px; height: 23px; object-fit: contain; display: block;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .8)); }
@keyframes axshMark { 0%, 100% { opacity: .92; } 50% { opacity: 1; } }

/* a single particle tracking the outer orbit */
.axsh__orb .spark {
  position: absolute; left: 50%; top: 3px;
  width: 3px; height: 3px;
  margin-left: -1.5px;
  border-radius: 50%;
  background: #fff;
  border: 0; inset: auto;
  box-shadow: 0 0 6px 1px color-mix(in srgb, var(--shell-tone, #C8FF00) 85%, transparent);
  z-index: 4;
  transform-origin: 1.5px 26px;
  animation: axshSpark 3.1s linear infinite;
}
@keyframes axshSpark { from { transform: rotate(0); } to { transform: rotate(360deg); } }

/* The centre tab is a 60px column with the orb lifted OUT of the bar. The orb
   is absolutely positioned so it cannot set the bar's height — a negative
   margin still contributes to its column's box, which is how it previously
   measured 2-4px INSIDE the bar however far it was pulled up. */
.axsh__tab--centre {
  position: relative;
  gap: 0;
  min-width: 68px;
  overflow: visible;
}
/* with no label the orb centres on the bar itself and lifts clear of it */
.axsh__tab--centre > .axsh__orb { top: 42%; }
.axsh__orb .axsh__core { width: 30px; height: 30px; margin: -15px 0 0 -15px; }
.axsh__orb .axsh__core .axsh__mk { width: 27px; height: 27px; }
.axsh__orb .spark { transform-origin: 1.5px 31px; }
.axsh__dock { overflow: visible; overscroll-behavior: contain; }
/* the dock must never scroll or zoom under a double tap — that is what used to
   slide the whole bar sideways when the orb was tapped twice */
.axsh__dock, .axsh__dock * {
  touch-action: manipulation;
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
}

@media (min-width: 1080px) {
  html[data-shell] body { padding-top: 0; }
}

/* ══ 2b · THE SECOND ROW ══════════════════════════════════════════════════
   Founder: "we need the part when you're scrolling so it stays locked with all
   the different businesses. This is perfect how it is."
   So it is sticky, and it is sticky UNDER the top bar rather than at the top of
   the window — the two have to stack, not overlap, and the offset is computed
   from the same custom property the top bar's own height comes from. Two rules
   reading one number is the only way they stay in agreement. */

.axsh__sub {
  position: sticky;
  top: calc(var(--shell-top-h, 56px) + env(safe-area-inset-top, 0px));
  z-index: calc(var(--shell-z, 900) - 1);
  background: color-mix(in srgb, var(--shell-bg, #060607) 88%, transparent);
  backdrop-filter: blur(16px) saturate(1.3);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
  border-bottom: 1px solid var(--shell-line-2, rgba(255, 255, 255, .06));
}
.axsh__subrow {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px var(--gutter, 16px);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-x;
  scrollbar-width: none;
}
.axsh__subrow::-webkit-scrollbar { display: none; }

.axsh__chip {
  --t: var(--shell-tone, #C9CBD1);
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: none;
  scroll-snap-align: start;
  min-height: 38px;
  padding: 0 14px;
  border-radius: 999px;
  text-decoration: none;
  white-space: nowrap;
  font-size: 12.5px; font-weight: 600; letter-spacing: .04em;
  color: var(--shell-ink-3, rgba(255, 255, 255, .5));
  /* see-through in every state — resting, hover and selected */
  background: color-mix(in srgb, var(--t) 7%, transparent);
  border: 1px solid color-mix(in srgb, var(--t) 20%, transparent);
  transition: color .22s var(--shell-ease, ease), background .22s var(--shell-ease, ease),
              border-color .22s var(--shell-ease, ease);
}
.axsh__chip svg { width: 16px; height: 16px; flex: none; color: var(--t); }
/* the mark gets its own black ground inside the pill — same reason as the pool
   tiles: a brand mark on a plate tinted in its own colour has nothing to read
   against, so it "doesn't pop out at you on the tab bars and at the top" */
.axsh__chip .axsh__mk {
  width: 24px; height: 24px;
  padding: 4px;
  border-radius: 7px;
  background: #000;
  box-sizing: border-box;
}
.axsh__chip span { display: block; }
.axsh__chip:hover {
  color: var(--shell-ink, #fff);
  background: color-mix(in srgb, var(--t) 14%, transparent);
  border-color: color-mix(in srgb, var(--t) 44%, transparent);
}
.axsh__chip[aria-current] {
  color: var(--shell-ink, #fff);
  background: color-mix(in srgb, var(--t) 16%, transparent);
  border-color: color-mix(in srgb, var(--t) 62%, transparent);
}

@media (min-width: 1080px) {
  /* On a desktop the rail already carries the same destinations down the left,
     so the row would be the second copy of one list on one screen. It stays
     for the brands (a horizontal set reads better than a column for six peer
     items) and goes for the vault panes. */
  .axsh__sub { top: 0; }
  .axsh__sub[aria-label="This brand"] { display: none; }
  .axsh__subrow { padding-inline: clamp(16px, 3vw, 40px); }
}

/* (An earlier block here forced the deck to one column between 1080 and 1268px,
   on the theory that the rail's 268px was squeezing the two-column head. That
   diagnosis was WRONG — the overlap reproduced at 1680px too — and the real
   cause was an un-reset align-self in axis-deck.css. Removed rather than left
   in place: a fix shipped on a false premise is a trap for the next person.) */

/* ══ 3 · THE SHEET — the same column, full height ═════════════════════ */

.axsh__scrim {
  position: fixed; inset: 0;
  z-index: calc(var(--shell-z, 900) + 10);
  background: rgba(0, 0, 0, .66);
  backdrop-filter: blur(3px);
  opacity: 0; visibility: hidden;
  transition: opacity .32s var(--shell-ease, ease), visibility .32s;
}
.axsh__scrim[data-open] { opacity: 1; visibility: visible; }

.axsh__sheet {
  position: fixed;
  inset: auto 0 0 0;
  z-index: calc(var(--shell-z, 900) + 11);
  max-height: 88dvh;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-columns: minmax(0, 1fr);
  background: var(--shell-bg, #060607);
  border-top: 1px solid var(--shell-line, rgba(255, 255, 255, .11));
  border-radius: 20px 20px 0 0;
  transform: translateY(101%);
  visibility: hidden;
  transition: transform .44s var(--shell-ease, ease), visibility .44s;
  font-family: var(--ff-sans, 'Manrope', system-ui, sans-serif);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
.axsh__sheet[data-open] { transform: translateY(0); visibility: visible; }

.axsh__sheethead {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 16px 14px 12px 18px;
  border-bottom: 1px solid var(--shell-line-2, rgba(255, 255, 255, .06));
}
/* the mark leads, on its own black ground — same rule as everywhere else */
.axsh__sheetmk {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: 11px;
  background: #000;
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C8FF00) 34%, transparent);
  color: var(--shell-tone, #C8FF00);
}
.axsh__sheetmk .axsh__mk { width: 23px; height: 23px; }
/* ⛔ THE AXIS MARK IS SILVER HERE, NOT THE PANEL'S LIME. Founder: "on the
   brands menu, it should have the AXIS logo silver, not gold, not green."
   The sheet forces --shell-tone to lime because the panel belongs to the
   platform rather than to whichever brand opened it — and the vector mark takes
   currentColor, so it inherited that lime. But AXIS's identity IN A LIST OF
   BRANDS is silver (brief §4): a lime AXIS sitting above a lime ZERA reads as
   two of the same brand. The panel keeps its lime; the mark does not. */
.axsh__sheetmk .axsh__mk--v { color: #C9CBD1; }
.axsh__sheetmk { border-color: rgba(201, 203, 209, .34); }
.axsh__sheettitle {
  display: block;
  font-family: var(--ff-display, 'Bebas Neue', sans-serif);
  font-size: 19px; line-height: 1.05; letter-spacing: .09em;
  color: var(--shell-ink, #fff);
}
.axsh__sheettitle b {
  display: block;
  margin-bottom: 5px;
  font-family: var(--ff-sans, 'Manrope', system-ui, sans-serif);
  font-size: 10px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--shell-tone, #C9CBD1);
}
.axsh__sheettitle em {
  display: block;
  font-style: normal;
  color: var(--shell-tone, #C9CBD1);
}

/* ══ THE MENU IS AXIS, AND AXIS IS LIME ═══════════════════════════════════
   Founder: "It should be the lime green on the mobile. The menu should be
   based off of it. It should be Access, Tokenized Businesses, and Digital
   Rewards."
   The rest of the chrome takes the brand you are standing in, which is right —
   a GLD page should feel like GLD. But the menu is the one surface that
   belongs to the PLATFORM rather than to any one brand: it is where you leave
   a brand from. So it holds AXIS's own lime whatever page opened it, and each
   brand card keeps its own tone inside, which is what makes the six read as a
   set rather than as six tints of wherever you happened to be. */
.axsh__sheet, .axsh__scrim { --shell-tone: #C8FF00; }
.axsh__x {
  width: var(--shell-tap, 44px); height: var(--shell-tap, 44px);
  display: grid; place-items: center;
  border-radius: 11px; cursor: pointer;
  color: var(--shell-ink-2, rgba(255, 255, 255, .72));
  background: rgba(255, 255, 255, .06);
  border: 1px solid var(--shell-line, rgba(255, 255, 255, .11));
}
.axsh__x svg { width: 16px; height: 16px; }

/* ══ THE BRANDS SCROLLER ══════════════════════════════════════════════════
   Founder, four times: "The brands pop-up should be the five cards in a
   scrolling thing, like I gave you the React bit scroller for the brands
   pop-up at the bottom." And: "the brands cards should be really easily
   scrollable on that menu… because now it's still too slow."

   ⚠ NOT AxisBits.CardSpread. That component is a FANNED DECK — absolutely
   positioned cards rotating around a shared origin. It looks like a hand of
   cards and it cannot be flicked through, which is the one thing he keeps
   asking for. A picker for six peers wants momentum and a snap, so this is a
   real overflow scroller: one flick moves it, it lands on a card, and it costs
   nothing to open because there is no layout work and no JS driving it.

   THE CARDS ARE TALL AND THEY PEEK. 74% width means the next card is always
   visibly cut off at the right edge, which is what tells a thumb the row
   moves. A grid of six small tiles says "this is all of it" and invites no
   gesture at all — that was the version he rejected. */
.axsh__spread { padding: 14px 0 6px; }
.axsh__spreadgrid {
  display: flex;
  gap: 11px;
  padding: 18px var(--gutter, 16px) 26px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-x;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  /* ══ REAL 3D, NOT A FLAT ROW ═══════════════════════════════════════════
     Founder: "The cards need to be the actual 3D cards when you're scrolling…
     When you click the brands, it needs to be much better than actual 3D
     cards. We're missing 3D stuff."
     Perspective lives on the SCROLLER, not on each card, so all six share one
     vanishing point and the row reads as one object turning rather than as six
     separate tilted rectangles. axis-app.js writes --r (degrees) and --s
     (scale) per card from its distance to the centre of the viewport on every
     scroll frame, so the card under the thumb faces you and its neighbours
     rake away. That is a coverflow, and it is what a finger expects when cards
     are this size. */
  perspective: 1100px;
  perspective-origin: 50% 46%;
}
.axsh__spreadgrid::-webkit-scrollbar { display: none; }

.axsh__bcard {
  --t: var(--shell-tone, #C9CBD1);
  position: relative;
  flex: 0 0 74%;
  max-width: 260px;
  scroll-snap-align: center;
  display: grid;
  grid-template-rows: 1fr auto auto;
  align-content: end;
  gap: 6px;
  min-height: 212px;
  padding: 14px 14px 16px;
  border-radius: 18px;
  text-decoration: none;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 22% 8%, color-mix(in srgb, var(--t) 30%, transparent), transparent 62%),
    linear-gradient(168deg, color-mix(in srgb, var(--t) 12%, transparent), transparent 58%),
    var(--surface-2, #121215);
  border: 1px solid color-mix(in srgb, var(--t) 30%, transparent);
  /* driven from JS on every scroll frame; the defaults are the flat state so
     the row is correct before a single frame has run and with JS off */
  /* --r/--s come from the coverflow on every scroll frame; --dy/--dx come from
     a finger dragging THIS card. Two independent inputs, one transform, so the
     card can be turned by hand while the deck is still settling. */
  transform: perspective(1100px)
             rotateY(calc(var(--r, 0deg) + var(--dy, 0deg)))
             rotateX(var(--dx, 0deg))
             scale(var(--s, 1));
  transform-origin: 50% 50%;
  transform-style: preserve-3d;
  touch-action: pan-x;
  transition: border-color .34s var(--shell-ease, ease), box-shadow .34s var(--shell-ease, ease);
  will-change: transform;
}
/* the rake is lit: a card turning away catches less light */
.axsh__bcard::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(90deg, rgba(0, 0, 0, .55), transparent 42%, rgba(0, 0, 0, .55));
  opacity: var(--shade, 0);
  transition: opacity .12s linear;
}
.axsh__bcard:hover { border-color: color-mix(in srgb, var(--t) 62%, transparent); }
.axsh__bcard[aria-current] {
  border-color: color-mix(in srgb, var(--t) 78%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--t) 34%, transparent),
              0 14px 40px -16px color-mix(in srgb, var(--t) 46%, transparent);
}
/* the real card render, floated inside the panel and lit by the brand */
.axsh__bcardart {
  display: block;
  align-self: center;
  justify-self: center;
  width: 100%;
  padding: 2px 0 6px;
  filter: drop-shadow(0 14px 26px rgba(0, 0, 0, .7))
          drop-shadow(0 0 22px color-mix(in srgb, var(--t) 22%, transparent));
}
.axsh__bcardart img {
  display: block;
  width: 100%; height: auto;
  max-width: none;              /* §13: a host img{max-width:100%} would clamp it */
  border-radius: 9px;
}
.axsh__bcard .axsh__mk { width: 38px; height: 38px; align-self: start; }
.axsh__bcard .axsh__mk--v { color: var(--t); }
.axsh__bname {
  display: block;
  font-family: var(--ff-display, 'Bebas Neue', sans-serif);
  font-size: 22px; letter-spacing: .1em; color: var(--shell-ink, #fff);
}
.axsh__bline {
  display: block;
  font-size: 12px; line-height: 1.35;
  color: var(--shell-ink-3, rgba(255, 255, 255, .5));
}

@media (min-width: 1080px) {
  /* room for three at a time; it still scrolls, it just does not need to */
  .axsh__bcard { flex-basis: 30%; min-height: 168px; }
}

@media (min-width: 1080px) {
  .axsh__scrim, .axsh__sheet { z-index: calc(var(--shell-z, 900) + 11); }
  .axsh__sheet {
    /* ⛔ CENTRED ON THE CONTENT, NOT ON THE WINDOW. The sheet is position:fixed,
       so `left:50%` is half the VIEWPORT — and the rail owns the first 268px
       (84px collapsed) of that viewport. Measured at 1280 with the rail
       collapsed: the panel sat at x=260..1020 while the content area started at
       84, so it straddled the rail and looked shoved off-centre. Half the rail
       is added back so it centres on the space the reader is actually looking
       at, and it follows the rail when it collapses because both read the same
       --shell-w. */
    /* ⚠ THE SLAB NOW OCCUPIES --shell-w PLUS TWO GAPS, so the space the reader
       is looking at starts that much further in. Both lines carry the same
       correction or the sheet drifts back off-centre by a gap. */
    inset: 50% auto auto
      calc(50% + ((var(--shell-w, 268px) + (var(--shell-gap, 16px) * 2)) / 2));
    width: min(760px,
      calc(92vw - var(--shell-w, 268px) - (var(--shell-gap, 16px) * 2)));
    max-height: 82dvh;
    border-radius: 18px;
    border: 1px solid var(--shell-line, rgba(255, 255, 255, .11));
    /* §17: a % in translate resolves against the ELEMENT, which is exactly
       what centring wants here — the element is its own reference. */
    transform: translate(-50%, calc(-50% + 14px));
    opacity: 0;
    transition: transform .4s var(--shell-ease, ease), opacity .3s var(--shell-ease, ease),
                visibility .4s;
  }
  .axsh__sheet[data-open] { transform: translate(-50%, -50%); opacity: 1; }
}

/* ══ 4 · THE ROUTE TRANSITION ═════════════════════════════════════════ */

.axsh__cover {
  position: fixed; inset: 0;
  z-index: 10000;
  display: grid; place-items: center;
  background: var(--shell-bg, #060607);
  opacity: 0; visibility: hidden;
  transition: opacity .34s var(--shell-ease, ease), visibility .34s;
}
.axsh__cover[data-on] { opacity: 1; visibility: visible; }
.axsh__coverin {
  display: grid; justify-items: center; gap: 16px;
  transform: scale(.96);
  transition: transform .5s var(--shell-ease, ease);
}
.axsh__cover[data-on] .axsh__coverin { transform: scale(1); }
.axsh__coverin svg { width: 40px; height: 40px; color: var(--shell-tone, #C9CBD1); }
.axsh__coverword {
  display: block;
  font-family: var(--ff-display, 'Bebas Neue', sans-serif);
  font-size: 13px; letter-spacing: .34em;
  color: var(--shell-ink-4, rgba(255, 255, 255, .4));
}
.axsh__coverbar {
  width: 128px; height: 2px; border-radius: 2px;
  background: rgba(255, 255, 255, .1);
  overflow: hidden;
}
.axsh__coverbar i {
  display: block; height: 100%; width: 34%;
  border-radius: 2px;
  background: var(--shell-tone, #C9CBD1);
  animation: axshSlide 1.05s var(--shell-ease, ease) infinite;
}
@keyframes axshSlide {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(400%); }
}

/* main fades on the way out and rises on the way in */
html[data-routing] main { opacity: .001; }
main[data-axsh-in] { animation: axshIn .5s var(--shell-ease, ease) both; }
@keyframes axshIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* ══ 5 · REDUCED MOTION — every animation in this file, off ═══════════ */

@media (prefers-reduced-motion: reduce) {
  .axsh, .axsh__min svg, .axsh__row, .axsh__brand, .axsh__bcard,
  .axsh__sheet, .axsh__scrim, .axsh__cover, .axsh__coverin, .axsh__tab,
  html[data-shell] body {
    transition: none !important;
  }
  .axsh__coverbar i { animation: none; width: 100%; }
  main[data-axsh-in] { animation: none; }
  .axsh__bcard:hover { transform: none; }
}

/* ══ 6 · THE ANSWERS STREAM IN ════════════════════════════════════════════
   Founder: "Upgrade all of the FAQs. Make it more animated. Streaming as they
   come in, the FAQ should be streaming… the straight answers should be
   agentic."

   Every word is wrapped once by axis-app.js and carries its index as --i, so
   the reveal is one animation-delay calculation per word and nothing reflows.
   The cap on the delay matters: without it a long answer's last word would
   wait several seconds, and an answer you have to wait for is worse than one
   that was simply there. 14ms a word, capped at 620ms, so even the longest
   answer has finished before a reader could have read the first line. */
.axq-w {
  font-style: inherit;
  display: inline;
}
.axq-run .axq-w {
  animation: axqIn .34s cubic-bezier(.16, 1, .3, 1) both;
  animation-delay: min(calc(var(--i, 0) * 14ms), 620ms);
}
@keyframes axqIn {
  from { opacity: 0; filter: blur(3px); }
  to   { opacity: 1; filter: none; }
}

/* the caret — it sits at the end of the answer while the words land, then goes */
.axq-run > p::after,
.axq-run > div::after {
  content: '';
  display: inline-block;
  width: 2px; height: 1em;
  margin-left: 3px;
  vertical-align: -2px;
  background: var(--accent, #C8FF00);
  animation: axqBlink .62s steps(2, start) 4, axqGone 0s linear 2.5s forwards;
}
@keyframes axqBlink { 50% { opacity: 0; } }
@keyframes axqGone  { to { opacity: 0; width: 0; margin: 0; } }

/* ⛔ REDUCED MOTION GETS THE ANSWER, NOT A FASTER ANIMATION. A person who has
   asked the system not to animate should not be made to watch a quicker
   version of the thing they turned off. */
@media (prefers-reduced-motion: reduce) {
  .axq-run .axq-w { animation: none; opacity: 1; filter: none; }
  .axq-run > p::after, .axq-run > div::after { display: none; }
}

@media (min-width: 1080px) {
  /* ⛔ THE GAP IS COUNTED TWICE, DELIBERATELY: once for the slab's own clear
     space on the left, once for the space between the slab and the content.
     Getting this wrong is how a floating panel ends up painted over the page
     it is supposed to sit beside. One expression, derived from the same two
     variables the slab itself uses, so they cannot drift apart. */
  html[data-shell] body {
    padding-left: calc(var(--shell-w, 268px) + (var(--shell-gap, 16px) * 2));
    /* the content-width header, below */
    padding-top: var(--shell-top-d, 64px);
    padding-bottom: 0;
    transition: padding-left .42s var(--shell-ease, ease);
  }
  .axsh { display: grid; }
  .axsh__dock { display: none !important; }

  /* ══ THE DESKTOP HEADER ═══════════════════════════════════════════════
     Founder: "The header looks like shit on desktop… I don't want the select
     brands at the top on desktop like that. It needs to be full size."

     There WAS no desktop header. The rail ran full height and the page began
     at the top edge of the window — measured: the first heading sat 50px from
     the top of the viewport with nothing above it, which is why it read as
     unfinished. What sat at the top instead was a strip of small brand chips,
     which is the opposite of the full-size selector he has asked for four
     times.

     So: a real 64px header spanning the CONTENT, right of the rail. It names
     the brand you are in, and it carries the menu. The chip strip is gone from
     desktop entirely — pressing the brand opens the full-size card selector,
     which is where brand choosing belongs now that the cards are real. */
  .axsh__top {
    display: grid;
    /* same expression as the body's padding — see the note there */
    left: calc(var(--shell-w, 268px) + (var(--shell-gap, 16px) * 2));
    height: var(--shell-top-d, 64px);
    padding: 0 clamp(20px, 2.2vw, 36px);
    /* ⚠ TWO TRACKS, NOT THREE. The bar has two children — the brand and the
       menu — so a three-track grid put the menu button in the MIDDLE column,
       floating beside the brand instead of sitting at the right-hand edge
       where a menu belongs. */
    grid-template-columns: auto minmax(0, 1fr);
    gap: 14px;
    transition: left .42s var(--shell-ease, ease);
    border-bottom: 1px solid var(--shell-line, rgba(255, 255, 255, .11));
  }
  /* the brand reads in full here — mark, name AND what it is */
  .axsh__top .axsh__topbrand { grid-template-columns: 30px auto auto 14px; gap: 11px; }
  .axsh__top .axsh__brandline {
    display: block;
    font-size: 12.5px;
    color: var(--shell-ink-4, rgba(255, 255, 255, .4));
    position: relative;
    padding-left: 13px;
  }
  /* a dot between the name and the line, so they read as one label */
  .axsh__top .axsh__brandline::before {
    content: '';
    position: absolute; left: 0; top: 50%;
    width: 3px; height: 3px; margin-top: -1.5px;
    border-radius: 50%;
    background: var(--shell-ink-4, rgba(255, 255, 255, .4));
  }
  .axsh__top .axsh__brandname { font-size: 17px; }

  /* ⛔ NO CHIP STRIP ON DESKTOP, FOR EITHER KIND. His instruction. The vault
     chips were already hidden because the rail lists the same destinations;
     the brand chips go for the same reason plus a better one — they are the
     small version of a thing that is now full size. */
  .axsh__sub { display: none; }

  /* the menu sits at the right edge of the header */
  .axsh__top .axsh__burger { justify-self: end; }

  /* ⛔ ONE MENU BUTTON, NOT TWO. It was added to the rail head when desktop had
     no header at all; the header carries it now, at the right-hand edge, which
     is where it belongs and where it is always visible whether the rail is
     open or collapsed. Two of the same control on one screen is the thing the
     whole shell exists to stop. */
  .axsh .axsh__railmenu { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   7 · THE NAVIGATION, IN GLASS
   ──────────────────────────────────────────────────────────────────────────
   Founder: "Upgrade the navigation, please. The navigation is really sloppy…
   Make it more modern. Make the side menu more modern, with more
   glassmorphism. Upgrade it more." And: "more glass, more lively, it's pretty
   dead all of it."

   Three things were making it read as dead, and none of them was colour:
     · every surface was a FLAT opaque fill, so nothing sat in front of
       anything — a menu with no depth is a list with a border,
     · every state change was a background swap, which is the one transition
       the eye does not read as motion,
     · the brand appeared THREE times on one screen (header, rail wordmark,
       rail chip), so the eye had nowhere to land.

   The third one is fixed by deletion, below. The first two are fixed here:
   real translucency over a blurred backdrop, a brand-tinted bloom where the
   light would come from, and states that MOVE rather than merely recolour.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the rail, as glass ─────────────────────────────────────────────────── */
.axsh {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--shell-tone, #C9CBD1) 7%, transparent) 0%,
      transparent 32%),
    color-mix(in srgb, var(--shell-bg, #060607) 72%, transparent);
  backdrop-filter: blur(22px) saturate(1.35);
  -webkit-backdrop-filter: blur(22px) saturate(1.35);
  border-right: 1px solid color-mix(in srgb, var(--shell-tone, #C9CBD1) 13%, transparent);
}
/* the light source — a soft brand bloom behind the top of the column */
.axsh::before {
  content: '';
  position: absolute;
  inset: -40% -60% auto -60%;
  height: 340px;
  pointer-events: none;
  background: radial-gradient(60% 60% at 50% 50%,
    color-mix(in srgb, var(--shell-tone, #C9CBD1) 26%, transparent), transparent 70%);
  filter: blur(42px);
  opacity: .5;
}
.axsh > * { position: relative; }

.axsh__head,
.axsh__brand { border-color: color-mix(in srgb, var(--shell-tone, #C9CBD1) 10%, transparent); }

/* ── rows that move ─────────────────────────────────────────────────────── */
.axsh__row {
  transition: color .22s var(--shell-ease, ease),
              background .22s var(--shell-ease, ease),
              transform .22s var(--shell-ease, ease);
}
.axsh__row:hover {
  transform: translateX(3px);
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--shell-tone, #C9CBD1) 13%, transparent),
    transparent 82%);
}
.axsh__row[aria-current] {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--shell-tone, #C9CBD1) 17%, transparent),
    transparent 86%);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--shell-tone, #C9CBD1) 14%, transparent);
}
.axsh__row[aria-current]::before {
  box-shadow: 0 0 12px color-mix(in srgb, var(--shell-tone, #C9CBD1) 80%, transparent);
}

/* the group headings get a hairline that fades, so the column has rhythm */
.axsh__gh {
  display: flex;
  align-items: center;
  gap: 10px;
}
.axsh__gh::after {
  content: '';
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--shell-tone, #C9CBD1) 22%, transparent), transparent);
}

/* ── the header and the dock, matched to it ─────────────────────────────── */
.axsh__top, .axsh__dock {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--shell-tone, #C9CBD1) 6%, transparent), transparent 70%),
    color-mix(in srgb, var(--shell-bg, #060607) 70%, transparent);
  backdrop-filter: blur(22px) saturate(1.35);
  -webkit-backdrop-filter: blur(22px) saturate(1.35);
  border-color: color-mix(in srgb, var(--shell-tone, #C9CBD1) 13%, transparent);
}

/* ── the sheet, as real glass ───────────────────────────────────────────── */
.axsh__sheet {
  background:
    radial-gradient(120% 60% at 50% 0%,
      color-mix(in srgb, var(--shell-tone, #C8FF00) 14%, transparent), transparent 62%),
    color-mix(in srgb, var(--shell-bg, #060607) 78%, transparent);
  backdrop-filter: blur(30px) saturate(1.4);
  -webkit-backdrop-filter: blur(30px) saturate(1.4);
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C8FF00) 16%, transparent);
  box-shadow: 0 -20px 70px rgba(0, 0, 0, .7),
              inset 0 1px 0 rgba(255, 255, 255, .06);
}
.axsh__scrim { backdrop-filter: blur(6px); background: rgba(0, 0, 0, .58); }

/* ── the rows arrive, they do not just appear ───────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  .axsh__sheet[data-open] .axsh__scroll > * {
    animation: axshRise .42s var(--shell-ease, ease) both;
    animation-delay: min(calc(var(--n, 0) * 18ms), 260ms);
  }
  @keyframes axshRise {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
  }
}

/* ⛔ THE BRAND APPEARED THREE TIMES ON ONE SCREEN. Founder: "when you open and
   collapse it, it should not have the two things twice. You need to organize
   it."
   On desktop the header names the brand, and the rail ALSO carried a wordmark
   row and a brand chip — so /v2/axis read AXIS three times before a single
   navigation row. The header owns the brand now; the rail owns the navigation
   and keeps only the collapse control. One thing in one place. */
@media (min-width: 1080px) {
  .axsh .axsh__brand { display: none; }
  .axsh__head { grid-template-columns: minmax(0, 1fr) auto; height: 64px; }
}

/* ══ THE HOMEPAGE IS A LANDING PAGE, NOT AN APP SURFACE ═══════════════════
   Founder: "the homepage, the landing page, should not be inside the menu
   platform on desktop."
   So on desktop it loses the rail and gets its full width back. It keeps the
   header — he asked for that separately, and a landing page with no way into
   the app would be a worse mistake than a rail on a landing page. On a phone
   nothing changes: there the dock IS the navigation, and removing it would
   leave the homepage with no way anywhere. */
@media (min-width: 1080px) {
  html[data-shell-home] .axsh { display: none; }
  html[data-shell-home] body { padding-left: 0; }
  html[data-shell-home] .axsh__top { left: 0; }
}

/* ══ THE CENTRE BUTTON, SIMPLIFIED ════════════════════════════════════════
   Founder: "The center button is too confusing with all the radioactive. It
   should just have the center logo and then a ring around the main thing that
   kind of pulses, not this whole lunar orb thing."

   The four orbital rings, the tracking spark and the haze are gone. What is
   left is what the control actually is: the brand's mark, a single ring, and a
   slow pulse so it still reads as alive rather than as a sticker. This is the
   same judgement as the rest of the fintech pass — the references he sent make
   their centre button one shape and one colour, and they are right.
   ⚠ The rules below come AFTER the originals in this file, so they win on
   source order as well as on specificity. Trap §2. */
.axsh__orb {
  background: none;
  box-shadow: none;
  transform-style: flat;          /* no 3D context — nothing orbits now */
  width: 58px; height: 58px;
}
.axsh__orb i.r1, .axsh__orb i.r2, .axsh__orb i.r3, .axsh__orb i.r4,
.axsh__orb .spark { display: none; }

/* the single ring */
.axsh__orb .axsh__ring {
  position: absolute; inset: 0;
  border-radius: 50%;
  border: 1.5px solid color-mix(in srgb, var(--shell-tone, #C8FF00) 62%, transparent);
  box-shadow: 0 0 18px -2px color-mix(in srgb, var(--shell-tone, #C8FF00) 52%, transparent);
  background: radial-gradient(circle at 50% 50%,
    color-mix(in srgb, var(--shell-tone, #C8FF00) 12%, transparent), transparent 72%);
}
@media (prefers-reduced-motion: no-preference) {
  .axsh__orb .axsh__ring { animation: axshPulse 2.8s cubic-bezier(.4, 0, .2, 1) infinite; }
  @keyframes axshPulse {
    0%, 100% { transform: scale(1);
               box-shadow: 0 0 14px -3px color-mix(in srgb, var(--shell-tone, #C8FF00) 44%, transparent); }
    50%      { transform: scale(1.055);
               box-shadow: 0 0 26px -1px color-mix(in srgb, var(--shell-tone, #C8FF00) 74%, transparent); }
  }
}
/* ⚠ BIGGER, TOO — his note. With the viewBox fitted to the ink, 34px of mark
   inside a 58px ring is the same optical weight for every brand. */
.axsh__orb .axsh__core {
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  animation: none;                /* the ring carries the life, not the mark */
}

/* ══ THE MENU AS A LAUNCHER ═══════════════════════════════════════════════
   Quick actions and a search field, above the brands. Taken from how the
   fintech references treat their own menus — action first, navigation second
   — in our accent rather than theirs. */
.axsh__quick {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  padding: 2px 12px 10px;
}
.axsh__quick > * {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  min-height: 46px; padding: 0 10px;
  border-radius: var(--r-md, 14px);
  cursor: pointer; text-decoration: none;
  color: var(--shell-tone, #C8FF00);
  /* see-through in every state — the hard rule */
  background: color-mix(in srgb, var(--shell-tone, #C8FF00) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C8FF00) 30%, transparent);
  font: 700 12.5px/1 var(--ff-sans, 'Manrope', system-ui, sans-serif);
  letter-spacing: .04em;
  transition: background .2s var(--shell-ease, ease), border-color .2s var(--shell-ease, ease);
}
.axsh__quick > *:hover {
  background: color-mix(in srgb, var(--shell-tone, #C8FF00) 20%, transparent);
  border-color: var(--shell-tone, #C8FF00);
}
.axsh__quick svg { width: 16px; height: 16px; flex: none; }
.axsh__quick span { display: block; }

.axsh__find {
  display: flex; align-items: center; gap: 10px;
  margin: 0 12px 6px;
  padding: 0 14px;
  min-height: 46px;
  border-radius: var(--r-md, 14px);
  background: rgba(255, 255, 255, .045);
  border: 1px solid var(--shell-line, rgba(255, 255, 255, .11));
}
.axsh__find svg { width: 16px; height: 16px; flex: none; color: var(--shell-ink-4, rgba(255,255,255,.4)); }
.axsh__find input {
  flex: 1; min-width: 0;
  background: none; border: 0; outline: none;
  color: var(--shell-ink, #fff);
  font: 500 14px/1 var(--ff-sans, 'Manrope', system-ui, sans-serif);
}
.axsh__find input::placeholder { color: var(--shell-ink-4, rgba(255, 255, 255, .4)); }
.axsh__find:focus-within { border-color: color-mix(in srgb, var(--shell-tone, #C8FF00) 54%, transparent); }

/* the orb's mark is a vector, so it takes the tone and carries no ground */
.axsh__orb .axsh__core svg { width: 100%; height: 100%; display: block; color: var(--shell-tone, #C8FF00); }
.axsh__orb .axsh__core { color: var(--shell-tone, #C8FF00); }

/* ══════════════════════════════════════════════════════════════════════════
   8 · THE MENU — BLACK AND SILVER, CARDS FIRST, ONE GRID
   ──────────────────────────────────────────────────────────────────────────
   Founder: "Make it more black and less neon-colored, just black and kind of
   silver… add all the buttons in one interface, but don't overcomplicate it…
   and the white label should be very easy to find."

   The panel forces its own tone to SILVER rather than taking the page's brand
   accent. That is the whole "less neon" note: a lime sheet over a lime page is
   one colour shouting at itself, and the menu belongs to the platform anyway.
   The brand cards keep their own colours inside it — they are the one thing in
   here that should be coloured, because colour is how you tell them apart.
   ══════════════════════════════════════════════════════════════════════════ */
.axsh__sheet, .axsh__scrim { --shell-tone: #C9CBD1; }
.axsh__sheet {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .05), rgba(255, 255, 255, .012) 42%),
    color-mix(in srgb, #050506 86%, transparent);
  border: 1px solid rgba(255, 255, 255, .12);
  box-shadow: 0 -20px 70px rgba(0, 0, 0, .78), inset 0 1px 0 rgba(255, 255, 255, .07);
}
.axsh__sheettitle em { color: rgba(255, 255, 255, .58); }
.axsh__sheettitle b { color: rgba(255, 255, 255, .46); }

/* ── white label, wide, first, unmissable ──────────────────────────────── */
.axsh__wl {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr) 14px;
  align-items: center;
  gap: 14px;
  margin: 4px 12px 12px;
  padding: 16px 18px;
  border-radius: var(--r-md, 14px);
  text-decoration: none;
  background: linear-gradient(110deg, rgba(255, 255, 255, .09), rgba(255, 255, 255, .02));
  border: 1px solid rgba(255, 255, 255, .17);
  transition: border-color .22s var(--shell-ease, ease), transform .22s var(--shell-ease, ease);
}
.axsh__wl:hover { transform: translateY(-1px); border-color: rgba(255, 255, 255, .34); }
.axsh__wl svg { width: 22px; height: 22px; color: #fff; }
.axsh__wl .axsh__wlchev { width: 14px; height: 14px; color: rgba(255, 255, 255, .42); }
.axsh__wl b {
  display: block;
  font-family: var(--ff-display, 'Bebas Neue', sans-serif);
  font-size: 18px; letter-spacing: .09em; color: #fff;
}
.axsh__wl small {
  display: block; margin-top: 2px;
  font-size: 12.5px; color: rgba(255, 255, 255, .46);
}

/* ── one grid of tiles, not a column of rows ───────────────────────────── */
.axsh__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  padding: 0 12px 12px;
}
.axsh__grid--plat { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.axsh__tile {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 9px;
  min-height: 86px;
  padding: 12px 8px;
  border-radius: var(--r-md, 14px);
  text-decoration: none;
  text-align: center;
  background: rgba(255, 255, 255, .04);
  border: 1px solid rgba(255, 255, 255, .09);
  color: rgba(255, 255, 255, .78);
  transition: background .2s var(--shell-ease, ease), border-color .2s var(--shell-ease, ease),
              transform .2s var(--shell-ease, ease);
}
.axsh__tile:hover {
  transform: translateY(-2px);
  background: rgba(255, 255, 255, .075);
  border-color: rgba(255, 255, 255, .22);
  color: #fff;
}
.axsh__tile svg { width: 20px; height: 20px; }
.axsh__tile span {
  display: block;
  font: 600 12px/1.2 var(--ff-sans, 'Manrope', system-ui, sans-serif);
  letter-spacing: .02em;
}
@media (min-width: 560px) {
  .axsh__grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .axsh__grid--plat { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* the orb's mark: the real icon file as a luminance mask, painted in the brand
   tone. Exact silhouette, no baked plate, one colour — which is what the centre
   of a glowing ring wants. */
.axsh__orbmk {
  display: block;
  width: 100%; height: 100%;
  -webkit-mask-image: var(--mk);   mask-image: var(--mk);
  -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
  -webkit-mask-position: center;   mask-position: center;
  -webkit-mask-size: contain;      mask-size: contain;
  -webkit-mask-source-type: luminance;
  mask-mode: luminance;
  background-color: var(--shell-tone, #C8FF00);
}

/* ══ ONE HEADER ═══════════════════════════════════════════════════════════
   Founder: "at the top left it says AXIS, but it's really on ZERA crypto
   gaming. You only need that once… you shouldn't have it twice."
   The brand chip lives in the rail head now (see build()), so the wide bar
   underneath is a duplicate on every desktop app surface. It stays on the
   homepage, which drops the rail on purpose and would otherwise have no
   header at all. */
.axsh__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 6px;
  height: 64px;
  padding: 0 10px 0 12px;
  border-bottom: 1px solid var(--shell-line, rgba(255,255,255,.07));
}
.axsh__head .axsh__brand {
  margin: 0; width: 100%;
  background: none; border: 0; box-shadow: none;
  padding: 4px 6px; border-radius: 10px;
}
.axsh__head .axsh__brand:hover { background: rgba(255,255,255,.05); }

@media (min-width: 1080px) {
  .axsh .axsh__brand { display: grid; }          /* it is the header now */
  .axsh__top { display: none; }
  html[data-shell-home] .axsh__top { display: grid; left: 0; }
}
html[data-shell="min"] .axsh .axsh__head { grid-template-columns: 1fr; padding: 0 8px; }
html[data-shell="min"] .axsh .axsh__brandtext,
html[data-shell="min"] .axsh .axsh__chev { display: none; }
html[data-shell="min"] .axsh .axsh__brand { justify-content: center; padding: 4px 0; }

/* ══════════════════════════════════════════════════════════════════════════
   9 · DARK GLASS — THE WHOLE CHROME, ONE SYSTEM
   ──────────────────────────────────────────────────────────────────────────
   Founder: "that launcher should be more dark glass morphism, not light glass
   morphism. I'm on the wallet page, and it's too white. It needs to be a
   darker glass morphism with the neon accents or the secondary color accents.
   That's what it needs to be." · "The side menu: more modern, more fintech.
   The pop-up: more modern, more fintech."

   The panel was reading white for a reason that is not the fill: it is 86%
   black over a THIRTY-PIXEL BLUR of a lime wallet page, and a heavy blur of a
   bright page is a bright panel no matter what you layer on top of it. Every
   tile then sat on rgba(255,255,255,.09), which is the light-glass recipe —
   white veils over a blurred background — and that is the look he is naming.

   Dark glass inverts the recipe. The ground goes nearly opaque so the blur
   contributes texture rather than luminance, the veils go to near-nothing, and
   the colour comes back as ACCENT: tone on the icons, the hairlines and the
   live states, never as fill. That is the reconciliation of the two notes he
   has given — "black and silver, less neon" was about what the panel is MADE
   of; "neon accents" is about what it POINTS with.
   ⚠ Last block in the file, so it wins on source order as well. Trap §2.
   ══════════════════════════════════════════════════════════════════════════ */
.axsh, .axsh__sheet, .axsh__dock, .axsh__top, .axsh__sub {
  --gl-ground: #04050700;                    /* set per surface below */
  --gl-veil:   rgba(255, 255, 255, .028);
  --gl-line:   rgba(255, 255, 255, .065);
  --gl-line-2: rgba(255, 255, 255, .10);
  --gl-text:   rgba(255, 255, 255, .56);
}

/* ── the pop-up ─────────────────────────────────────────────────────────── */
.axsh__sheet {
  background:
    radial-gradient(90% 44% at 50% 0%,
      color-mix(in srgb, var(--accent, #C8FF00) 9%, transparent), transparent 64%),
    linear-gradient(180deg, rgba(255, 255, 255, .022), rgba(255, 255, 255, 0) 38%),
    color-mix(in srgb, #05060A 95%, transparent);
  backdrop-filter: blur(26px) saturate(.9) brightness(.62);
  -webkit-backdrop-filter: blur(26px) saturate(.9) brightness(.62);
  border: 1px solid var(--gl-line-2);
  box-shadow: 0 -24px 90px rgba(0, 0, 0, .86),
              inset 0 1px 0 rgba(255, 255, 255, .05);
}
.axsh__scrim { background: rgba(2, 3, 5, .72); backdrop-filter: blur(8px); }

/* tiles: a whisper of veil, a hairline, and the tone only on the glyph */
.axsh__tile, .axsh__wl {
  background: var(--gl-veil);
  border: 1px solid var(--gl-line);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .035);
}
.axsh__tile svg { color: color-mix(in srgb, var(--accent, #C8FF00) 86%, #fff); }
.axsh__tile:hover, .axsh__wl:hover {
  background: color-mix(in srgb, var(--accent, #C8FF00) 7%, var(--gl-veil));
  border-color: color-mix(in srgb, var(--accent, #C8FF00) 34%, transparent);
}
/* white label is the one row that carries the accent as an edge — founder:
   "the white label should be very easy to find" */
.axsh__wl {
  background:
    linear-gradient(110deg, color-mix(in srgb, var(--accent, #C8FF00) 10%, transparent),
                    rgba(255, 255, 255, .018) 58%);
  border-color: color-mix(in srgb, var(--accent, #C8FF00) 26%, transparent);
}
.axsh__wl > svg:first-child { color: var(--accent, #C8FF00); }

/* ── the side menu ──────────────────────────────────────────────────────── */
.axsh {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .016), rgba(255, 255, 255, 0) 30%),
    color-mix(in srgb, #05060A 96%, transparent);
  backdrop-filter: blur(20px) saturate(.9) brightness(.6);
  -webkit-backdrop-filter: blur(20px) saturate(.9) brightness(.6);
  border-right: 1px solid var(--gl-line);
}
.axsh__row { color: var(--gl-text); border-radius: 10px; }
.axsh__row svg { color: rgba(255, 255, 255, .34); transition: color .18s var(--shell-ease, ease); }
.axsh__row:hover { background: var(--gl-veil); color: #fff; }
.axsh__row:hover svg { color: color-mix(in srgb, var(--accent, #C8FF00) 80%, #fff); }
/* live: a tone edge, a tone glyph, and almost no fill — the fintech read */
.axsh__row[aria-current], .axsh__row.is-live {
  position: relative;
  background: color-mix(in srgb, var(--accent, #C8FF00) 8%, transparent);
  color: #fff;
}
.axsh__row[aria-current] svg, .axsh__row.is-live svg { color: var(--accent, #C8FF00); }
.axsh__row[aria-current]::before, .axsh__row.is-live::before {
  content: ""; position: absolute; left: -1px; top: 20%; bottom: 20%;
  width: 2px; border-radius: 2px; background: var(--accent, #C8FF00);
  box-shadow: 0 0 10px color-mix(in srgb, var(--accent, #C8FF00) 60%, transparent);
}
.axsh__head { border-bottom-color: var(--gl-line); }

/* ── the dock and the bars ──────────────────────────────────────────────── */
.axsh__dock, .axsh__top {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .02), rgba(255, 255, 255, 0) 60%),
    color-mix(in srgb, #05060A 94%, transparent);
  backdrop-filter: blur(24px) saturate(.9) brightness(.6);
  -webkit-backdrop-filter: blur(24px) saturate(.9) brightness(.6);
}
.axsh__dock { border-top: 1px solid var(--gl-line); }
.axsh__top  { border-bottom: 1px solid var(--gl-line); }
.axsh__sub {
  background: color-mix(in srgb, #05060A 92%, transparent);
  backdrop-filter: blur(20px) brightness(.62);
  -webkit-backdrop-filter: blur(20px) brightness(.62);
  border-bottom: 1px solid var(--gl-line);
}

/* ⛔ THE MENU BUTTON IS BACK IN THE RAIL. It was hidden on desktop because the
   wide header carried one — and that header has just been removed as the
   duplicate he reported. Removing a control's only twin without restoring the
   original would have left desktop with no way into the sheet at all. */
@media (min-width: 1080px) {
  .axsh .axsh__railmenu {
    display: grid; place-items: center;
    width: 34px; height: 34px; border-radius: 10px;
    background: var(--gl-veil); border: 1px solid var(--gl-line);
    color: rgba(255, 255, 255, .72);
  }
  .axsh .axsh__railmenu:hover {
    background: color-mix(in srgb, var(--accent, #C8FF00) 12%, transparent);
    border-color: color-mix(in srgb, var(--accent, #C8FF00) 34%, transparent);
    color: #fff;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   10 · THE COLLAPSE, THE DOCK, AND THE MOTION
   ──────────────────────────────────────────────────────────────────────────
   Founder: "still has a lot of issues opening and closing, and it just doesn't
   feel like it's done correctly… we don't need the menu button twice if we
   have the dropdown… the tab bar icons are too big. They're not done
   correctly. They need to be smaller, more uniform, and upgrade the design…
   the side menu needs more glassmorphism. We need more framer motion."

   MEASURED on the collapsed rail he screenshotted: 84px wide, the brand chip
   67x48 and the expand button stranded on a row of its own underneath it. That
   is why it reads unfinished — a collapsed rail is a column of equal targets,
   and this one had two different shapes at the top before the icons started.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── collapsed: one column of identical squares ─────────────────────────── */
/* ⛔ THESE WERE HARDCODED 72px AND THAT IS WHY THE COLLAPSE "SCREWED UP".
   `--shell-w` exists precisely so the slab's width and the body's padding
   cannot disagree — and this block, declared LATER in the same file, quietly
   replaced both with a literal (trap §2: source order beats an earlier rule at
   equal specificity). So the slab narrowed to 72px while the content reflowed
   to a padding that knew nothing about the gap, and the island sat on top of
   the page every time it collapsed. Both read the variable again. */
html[data-shell="min"] .axsh { width: var(--shell-w-shut, 72px); }
html[data-shell="min"] body  {
  padding-left: calc(var(--shell-w-shut, 72px) + (var(--shell-gap, 16px) * 2));
}
/* ⛔ height:60px WITH TWO 44px ROWS IN IT. The collapse control sits in the
   head now, so the head holds two stacked targets — 88px of content in a box
   pinned to 60. `overflow` is visible, so the button simply painted BELOW the
   head, on top of the scrolling column, where `.axsh__scroll` swallowed every
   click: a collapse button that could be seen and could not be pressed. The
   box sizes to its content, and the two targets are the same 44px square on
   one centre line so the column still reads as a column. */
html[data-shell="min"] .axsh .axsh__head {
  grid-template-columns: 1fr;
  justify-items: center;
  align-content: center;
  gap: 8px;
  padding: 12px 0;
  height: auto;
}

html[data-shell="min"] .axsh .axsh__brand {
  width: 44px; height: 44px; padding: 0;
  justify-items: center; align-content: center;
}
html[data-shell="min"] .axsh .axsh__glyph { width: 34px; height: 34px; }
/* ⛔ THE EXPAND CONTROL IS IN THE HEAD, NOT THE FOOT. This comment used to
   argue the opposite and the markup followed it. His call overrides the
   argument: it goes back to the top, beside the brand. */
/* ⛔ THE COLLAPSED STATE IS NOT IN THIS SELECTOR LIST ANY MORE. It pinned the
   control to 34px in BOTH states, so a collapsed head held a 44px brand square
   above a 34px button — the "two different shapes stacked" he rejected, now
   measured by `_island.mjs` rather than argued about. Collapsed, it matches the
   brand square exactly; expanded, it stays the smaller 34px control beside it.
   My earlier 44px rule lost to this one on source order (trap §2) — which is
   why the fix is HERE and not another rule further up hoping to win. */
.axsh .axsh__min {
  width: 34px; height: 34px; border-radius: 10px;
  background: var(--gl-veil); border: 1px solid var(--gl-line);
  color: rgba(255, 255, 255, .6);
  transition: background .2s var(--shell-ease, ease), color .2s var(--shell-ease, ease);
}
.axsh .axsh__min:hover { background: rgba(255, 255, 255, .07); color: #fff; }
html[data-shell="min"] .axsh .axsh__min { width: 44px; height: 44px; border-radius: 12px; }
html[data-shell="min"] .axsh .axsh__row {
  width: 44px; height: 44px; margin-inline: auto;
  justify-items: center; padding: 0;
}
html[data-shell="min"] .axsh .axsh__scroll { padding-inline: 0; }
html[data-shell="min"] .axsh .axsh__foot .axsh__row { width: 44px; }

/* ── more glass: a lit top edge and a shadow that belongs to an ISLAND ──── */
/* ⛔ THE OLD SHADOW WAS DIRECTIONAL — `24px 0 60px` cast to the RIGHT, with an
   `inset -1px 0 0` right-hand wall. Both are the lighting of a panel welded to
   the left edge of the window. A slab that floats is lit from above and casts
   underneath itself on every side. */
.axsh {
  box-shadow:
    0 30px 70px -22px rgba(0, 0, 0, .82),
    0 2px 10px -4px rgba(0, 0, 0, .6),
    inset 0 1px 0 rgba(255, 255, 255, .05);
}
.axsh::after {
  content: ""; position: absolute; inset: 0 12px auto; height: 1px;
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--accent, #C8FF00) 34%, transparent), transparent);
  pointer-events: none;
}

/* ── motion: the rail opens, it does not jump ──────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  .axsh { transition: width .34s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)); }
  body  { transition: padding-left .34s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)); }
  .axsh__label { transition: opacity .2s ease, transform .26s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)); }
  html[data-shell="min"] .axsh__label { opacity: 0; transform: translateX(-6px); }
  .axsh__row { transition: background .2s ease, color .2s ease, transform .2s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)); }
  .axsh__row:active { transform: scale(.97); }
}

/* ══ THE DOCK: SMALLER, UNIFORM ═══════════════════════════════════════════
   Founder: "the tab bar icons are too big… smaller, more uniform."
   MEASURED: the four side glyphs rendered 20x20 and the centre orb 58x58 —
   the centre was 2.9x its neighbours, which is why the row reads lopsided
   rather than like a set. 22 and 44 puts it at 2x: still clearly the primary
   action, no longer a different species. */
.axsh__dock { height: 62px; }
.axsh__tab svg { width: 22px; height: 22px; }
.axsh__tab { font-size: 10.5px; letter-spacing: .02em; }
.axsh__orb { width: 44px; height: 44px; }
.axsh__orb .axsh__core { inset: 9px; }
.axsh__tab--centre { margin-top: -10px; }
@media (prefers-reduced-motion: no-preference) {
  .axsh__tab { transition: color .18s ease; }
  .axsh__tab svg { transition: transform .22s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)); }
  .axsh__tab:active svg { transform: scale(.88); }
  .axsh__orb { transition: transform .26s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)); }
  .axsh__tab--centre:active .axsh__orb { transform: scale(.92); }
}

/* the foot holds the collapse control and Home, side by side when open and
   stacked when collapsed */
.axsh__foot {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-top: 1px solid var(--gl-line);
}
html[data-shell="min"] .axsh .axsh__foot {
  grid-template-columns: 1fr;
  justify-items: center;
  gap: 6px;
}
/* ⛔ TWO FLIPS CANCELLED EACH OTHER OUT. This rotated the BUTTON 180deg while
   `html[data-shell="min"] .axsh__min svg { transform: scaleX(-1) }` mirrored
   the CHEVRON inside it — so collapsed, the arrow ended up pointing LEFT
   again: a control that says "collapse" on a menu that is already collapsed.
   One flip, on the glyph, where the transition is already declared. */
@media (prefers-reduced-motion: no-preference) {
  .axsh__min svg { transition: transform .3s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)); }
}

/* ══════════════════════════════════════════════════════════════════════════
   11 · THE CENTRE BUTTON, REBUILT
   ──────────────────────────────────────────────────────────────────────────
   Founder: "the tab buttons in the center are major problems. If anybody goes
   on it, it's really ugly. We got to fix all that ASAP."

   He is right, and this is the fifth note on the same control, so here is what
   was actually wrong — MEASURED off the rendered dock, not guessed:
     · the ring was sized to 44px but the mark inside it was not, so the logo
       overflowed the top of the circle and sat half outside it;
     · `margin-top:-10px` then lifted the whole thing into a dock that clips,
       so the part that had escaped was also cut off.
   A logo hanging out of the top of its own ring, sliced by the bar it lives
   in, is exactly the "really ugly" he is describing.

   What it is now: one see-through glass disc, one hairline ring in the brand
   tone, the real mark centred inside it at a size derived from the disc rather
   than typed separately, and a slow breath on the ring alone. Dark, glass,
   see-through — his words for the whole platform, applied to its most-looked-at
   control.
   ══════════════════════════════════════════════════════════════════════════ */

/* the dock must not slice what it raises */
.axsh__dock { overflow: visible; }
.axsh__tab--centre { margin-top: 0; overflow: visible; }

.axsh__tab--centre .axsh__orb {
  --d: 46px;
  position: relative;
  display: grid;
  place-items: center;
  width: var(--d); height: var(--d);
  border-radius: 50%;
  /* see-through: the page moves behind it */
  background: color-mix(in srgb, #05060A 62%, transparent);
  backdrop-filter: blur(14px) saturate(1.1);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
  border: 1px solid color-mix(in srgb, var(--accent, #C8FF00) 46%, transparent);
  box-shadow: 0 6px 22px -8px color-mix(in srgb, var(--accent, #C8FF00) 42%, transparent),
              inset 0 1px 0 rgba(255, 255, 255, .1);
  transform-style: flat;
  overflow: visible;
}
/* the ring is a separate, larger circle so it can breathe without moving the
   mark — the mark never scales, which is what kept breaking it */
/* ⛔ ONE RING, NOT TWO. Founder: "it has double rings and the logo is not
   centred." He is right and it was two separate 1px circles: the orb's OWN
   border at 46px, and this element drawn at 58px. Both hard, both the same
   weight, 6px apart — which reads as a mistake, and it also made the 52% mark
   inside them look stranded, which is the "not centred" half of the note (the
   geometry measures centred to 0.03px; it was the framing that was wrong).
   This element keeps the LIFE and gives up the LINE: a soft pulsing bloom with
   no border at all, so the only circle on screen is the button's own edge. */
.axsh__tab--centre .axsh__orb .axsh__ring {
  position: absolute; inset: -7px;
  border-radius: 50%;
  border: 0;
  /* the bloom is neutral too, and much quieter — it is a hint of depth under
     the disc, not a glow around it */
  background: radial-gradient(circle at 50% 50%,
    rgba(255, 255, 255, .10) 38%, transparent 70%);
  box-shadow: none;
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .axsh__tab--centre .axsh__orb .axsh__ring { animation: axshBreath 3.4s ease-in-out infinite; }
  @keyframes axshBreath {
    0%, 100% { transform: scale(1);    opacity: .55; }
    50%      { transform: scale(1.12); opacity: .18; }
  }
}
/* ⛔ THE MARK IS SIZED FROM THE DISC, NOT TYPED. Every previous version set the
   ring and the mark as two independent numbers, so changing one left the other
   behind and the logo escaped its circle. 52% of the disc, centred, always. */
/* ⛔ MARGIN-CENTRED, NOT place-items-CENTRED. The orb's `display:grid` kept
   losing to a more specific rule elsewhere in this file, so the static core
   landed at the content box's top-left — MEASURED 8.5px up and 8.5px left of
   centre on all six brands. Absolute + 50% + a negative margin of exactly half
   the box does not care what the parent's display is, which is the whole point;
   it is the same fix that finally held the orbiting marks in axis-fx. */
.axsh__tab--centre .axsh__orb .axsh__core {
  position: absolute;
  inset: auto;
  left: 50%; top: 50%;
  /* ⚠ 50%. I took it to 62% when the second ring came off and that was too
     far — founder: "tab bar too large here the icon". Half the disc leaves a
     real margin inside the ring and matches the optical weight of the four
     flat glyphs either side of it, which is what makes the row read as a SET
     rather than as one big button with four small ones. Still sized FROM the
     disc, so the two can never drift apart. */
  width: calc(var(--d) * .50); height: calc(var(--d) * .50);
  margin: calc(var(--d) * -.25) 0 0 calc(var(--d) * -.25);
  display: block;
}
.axsh__tab--centre .axsh__orb .axsh__core .axsh__orbmk {
  width: 100%; height: 100%;
  background-color: var(--accent, #C8FF00);
}
.axsh__tab--centre[aria-current] .axsh__orb,
.axsh__tab--centre:active .axsh__orb {
  border-color: var(--accent, #C8FF00);
}

/* ══ THE CENTRE BUTTON WEARS THE BRAND'S OWN TONE ═════════════════════════
   Founder: "the center logo should be silver with silver ring."
   --ot is BRANDS[].tone, written inline by orbMark() — silver for AXIS, gold
   for JETR, lime for ZERA. --accent is the page's working colour, which on the
   AXIS pages is lime, and painting the parent's mark lime made it read as a
   sixth brand rather than the platform. */
/* ⛔ SILVER, NOT THE ACCENT. Founder, twice now: "the center logo should be
   silver with silver ring", and today "remove the neon green from it".
   `--ot: var(--accent)` painted the centre button in whatever lime or gold the
   PAGE was wearing, so on AXIS and ZERA it glowed neon green — a glowing green
   disc in the middle of a neutral bar, which is the thing he keeps pointing
   at. The orb is the PLATFORM's control, not a brand's: it goes to the wallet
   from every page. Silver says that; the page accent says "sixth brand".
   The brand still reads here — the MARK inside is the brand's own — but the
   furniture around it is neutral on every page. */
.axsh__tab--centre .axsh__orb {
  --ot: #C9CBD1;
  border-color: rgba(255, 255, 255, .22);
  box-shadow: 0 6px 20px -10px rgba(0, 0, 0, .9),
              inset 0 1px 0 rgba(255, 255, 255, .08);
}

.axsh__tab--centre .axsh__orb .axsh__core .axsh__orbmk { background-color: var(--ot); }

/* ══ THE MINI BRAND PICKER ════════════════════════════════════════════════
   Founder: "if you press the center button, it should have all five of them
   pop up, just real simple… It just has a little mini popup, a tiny one." */
.axsh__pop {
  position: fixed;
  left: 50%; bottom: calc(var(--shell-bar-h, 62px) + env(safe-area-inset-bottom, 0px) + 14px);
  transform: translateX(-50%);
  z-index: calc(var(--shell-z, 900) + 14);
  width: min(390px, calc(100vw - 20px));
  padding: 10px 10px 24px;
  border-radius: 24px;
  background: color-mix(in srgb, #05060A 88%, transparent);
  backdrop-filter: blur(26px) saturate(1.05) brightness(.7);
  -webkit-backdrop-filter: blur(26px) saturate(1.05) brightness(.7);
  border: 1px solid rgba(255, 255, 255, .11);
  box-shadow: 0 22px 60px -22px rgba(0, 0, 0, .92), inset 0 1px 0 rgba(255, 255, 255, .06);
}
.axsh__pop[hidden] { display: none; }
/* ══ THE CARD FAN ═════════════════════════════════════════════════════════
   Founder, with a reference to a card-fan carousel: "seems good here,
   especially when you press the centre button and then this pops out."

   This replaces six tiny chips with the six REAL cards, laid along an arc.
   The chips were the small version of the one asset this product has that
   nobody else does; the most-pressed control on a phone should open the best
   thing we own.

   THE GEOMETRY IS WRITTEN BY JS AS --rot/--x/--y/--z, one set per card, and
   the CSS owns everything that MOVES. That split is deliberate: it is the same
   one that stopped the dock orb fighting its own keyframes, and it means the
   arc can be retuned in one place without touching an animation. */
.axsh__fan {
  position: relative;
  /* ⚠ SIZED TO THE ARC'S REAL BOUNDING BOX, not to the card. A 126x79 card
     rotated 25deg occupies 125px of height, so anything above that is dead
     panel — it measured 50px of empty glass over the fan on the first render. */
  height: 134px;
  display: flex; align-items: flex-end; justify-content: center;
  padding: 0 0 6px;
  /* the cards overlap; the arc needs room to breathe past the panel edge */
  overflow: visible;
}
.axsh__fanc {
  position: absolute;
  bottom: 0; left: 50%;
  width: 118px;
  margin-left: -59px;
  padding: 0;
  border: 0; background: none; cursor: pointer;
  z-index: calc(var(--z, 1));
  /* ⚠ THE ORDER MATTERS. translate first, then rotate, so each card turns
     about its OWN centre after it has been placed on the arc — rotate-first
     swings it around the fan's centre and the ends fly off. */
  transform: translate3d(var(--x, 0), var(--y, 0), 0) rotate(var(--rot, 0deg));
  transform-origin: 50% 88%;          /* pivot near the base, like a held hand */
  transition: transform .34s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)),
              filter .24s ease;
  filter: drop-shadow(0 12px 22px rgba(0, 0, 0, .55));
}
.axsh__fanc img {
  width: 100%; height: auto; display: block;
  border-radius: 9px;
  border: 1px solid rgba(255, 255, 255, .13);
}
/* the name rides ON the card, so it turns with it. There is no mark overlay:
   the card art already carries the emblem and a second one sat on top of it. */
.axsh__fann {
  position: absolute; left: 0; right: 0; bottom: -17px;
  font: 700 9.5px/1 var(--ff-sans, Manrope, system-ui, sans-serif);
  letter-spacing: .14em; text-transform: uppercase;
  color: rgba(255, 255, 255, .5);
  opacity: 0; transition: opacity .2s ease;
}
/* ⛔ LIFT, DO NOT RE-LAY-OUT. Raising a card by changing its --y would move
   every neighbour's arithmetic; a translate on top of the placed transform
   lifts only the one under the thumb. */
.axsh__fanc:hover, .axsh__fanc:focus-visible {
  transform: translate3d(var(--x, 0), calc(var(--y, 0) - 20px), 0)
             rotate(var(--rot, 0deg)) scale(1.06);
  filter: drop-shadow(0 18px 30px rgba(0, 0, 0, .7));
  z-index: 20;
}
.axsh__fanc:hover .axsh__fann,
.axsh__fanc:focus-visible .axsh__fann { opacity: 1; }
.axsh__fanc:active { transform: translate3d(var(--x, 0), calc(var(--y, 0) - 14px), 0)
             rotate(var(--rot, 0deg)) scale(1.01); }
/* where you already are: lifted, named, and ringed in its own tone */
.axsh__fanc[aria-current] {
  transform: translate3d(var(--x, 0), calc(var(--y, 0) - 13px), 0) rotate(var(--rot, 0deg));
  z-index: 15;
}
.axsh__fanc[aria-current] img { border-color: color-mix(in srgb, var(--t, #C8FF00) 62%, transparent); }
.axsh__fanc[aria-current] .axsh__fann { opacity: 1; color: var(--t, #C8FF00); }

/* the entrance: they arrive from behind the orb and settle onto the arc */
@media (prefers-reduced-motion: no-preference) {
  .axsh__pop:not([hidden]) .axsh__fanc {
    animation: axshDeal .5s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)) both;
    animation-delay: calc(var(--i, 0) * 38ms);
  }
  @keyframes axshDeal {
    from { transform: translate3d(0, 34px, 0) rotate(0deg) scale(.82); opacity: 0; }
    to   { transform: translate3d(var(--x, 0), var(--y, 0), 0) rotate(var(--rot, 0deg)); opacity: 1; }
  }
}
@media (min-width: 1080px) { .axsh__pop { display: none; } }
@media (prefers-reduced-motion: no-preference) {
  .axsh__pop:not([hidden]) { animation: axshPop .3s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)) both; }
  @keyframes axshPop {
    from { opacity: 0; transform: translateX(-50%) translateY(10px) scale(.96); }
    to   { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
  }
}

/* ══ THE WALLET ACTIONS, ON A PHONE ═══════════════════════════════════════
   Founder: "the deposit, swap, and earn buttons are cramped on mobile, and
   they're not showing correctly."

   ⛔ THE FIRST FIX MADE IT WORSE, AND THE REASON IS WORTH KEEPING. I put
   `grid-template-columns: repeat(2,1fr)` on `.axv-act` assuming it was the
   ROW. It is each BUTTON. So every button became a two-column grid: the round
   icon was stretched into an ellipse across one 23px column and the label
   clipped to "De" / "Sw" / "Ea" / "Fr" in the other. He photographed it.
   MEASURED before touching it this time: `.axv-acts` is the row — display
   grid, four 75.75px columns in 327px. Four labelled pills in 327px is 75px
   each, and "Deposit" does not fit in 75px at any readable size. Two columns,
   163px each, fits every word with room for the icon.
   Never style a container you have not confirmed IS the container. */
@media (max-width: 719px) {
  html body .axv-acts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }
  html body .axv-acts > .axv-act {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    min-height: 54px;
    padding: 0 14px;
    white-space: nowrap;
    font-size: 14px;
  }
  /* the icon is a circle, and a circle needs a fixed box that flex cannot
     squeeze — this is the shape that came out as an ellipse */
  html body .axv-acts > .axv-act > i {
    flex: 0 0 auto;
    width: 30px; height: 30px;
    border-radius: 50%;
    display: grid; place-items: center;
  }
  html body .axv-acts > .axv-act > i svg { width: 15px; height: 15px; }

  /* the product gets the width on the surface it is the point of */
  html body .axv-cardart { max-width: 100%; }
  html body .axv-walgrid { gap: 18px; }
}


/* ⛔ THE CENTRE TAB WAS CENTRED; THE ORB INSIDE IT WAS NOT.
   Founder: "that bar is still off-center."
   MEASURED at 393px: the five tab columns are exactly even (79px each from 0),
   so column three spans 157-236 and its centre is 196.5 — the viewport's
   centre to the pixel. The ORB's centre was at 212.8. It was sitting flush
   against the right-hand edge of its own column, 16.3px out, because the centre
   tab carries no label span and whatever was laying the tab out had nothing to
   balance the orb against.
   Every previous fix aimed at the ring, the mark and the dock — all three of
   which measured correct — which is why the complaint kept coming back.
   Place the tab's contents explicitly and there is nothing left to drift. */
.axsh__dock .axsh__tab--centre {
  display: grid;
  place-items: center;
  justify-items: center;
  align-content: center;
  padding: 0;
}
.axsh__dock .axsh__tab--centre > .axsh__orb {
  margin-inline: auto;
  justify-self: center;
}

/* ⛔ AND HERE IS THE 16.3px, FOUND BY READING `inset` INSTEAD OF GUESSING.
   The orb computed to inset: 25.6px -39.3px -25.6px 39.3px — a relative box
   with `left: 50%` still applied. It was half of an old centring pair,
   `left:50%` + `transform:translateX(-50%)`, and the rebuild of this control
   replaced `transform` (for the press-scale) while leaving `left` behind. Half
   a centring idiom is not a smaller offset; it is the full push with nothing
   pulling it back — 50% of the 78.6px tab, which is 39.3px, landing as 16.3px
   of visible drift once the grid's own centring is taken off it.
   The tab centres its own contents. The orb needs no offsets at all. */
.axsh__dock .axsh__tab--centre > .axsh__orb {
  position: relative;
  left: auto; right: auto; top: auto; bottom: auto;
  inset: auto;
  transform: none;
}
@media (prefers-reduced-motion: no-preference) {
  .axsh__dock .axsh__tab--centre:active > .axsh__orb { transform: scale(.92); }
}

/* ══════════════════════════════════════════════════════════════════════════
   12 · WHAT THE SWEEP FOUND
   ──────────────────────────────────────────────────────────────────────────
   `.axi/_audit.mjs` across 9 pages x 3 widths. Four real defects, none of them
   guessed — each one is a measurement.
   ══════════════════════════════════════════════════════════════════════════ */

/* ⛔ 1 · THE SAME CLIPPED LABELS, ON DESKTOP. "Deposit", "Swap" and "Freeze"
   report scrollWidth > clientWidth at 1440 on /axis, /zera and /gld. The mobile
   fix went in at max-width:719 and stopped there, so the bug he photographed on
   a phone was still live on every desktop — which is why a sweep exists rather
   than a fix per screenshot.
   auto-fit with a floor that actually holds the longest word: four across when
   there is room, two when there is not, never a clipped label. */
html body .axv-acts {
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
}
html body .axv-acts > .axv-act { min-width: 0; white-space: nowrap; }
html body .axv-acts > .axv-act > span { overflow: visible; }

/* ⛔ 2 · THE VAULT / BRAND CHIPS ARE 38px ON A TOUCH SCREEN. Every page, both
   touch widths. 38 is under the 44px floor a thumb needs, and this row is the
   one he uses to move between panes — the single most-tapped control in the
   app after the dock. */
@media (pointer: coarse) {
  .axsh__chip { min-height: 44px; padding-block: 0; align-items: center; }
  .axsh__subrow { padding-block: 8px; }
}

/* ⛔ 3 · "View white label" CLIPS AT 393px on the homepage. A call to action
   that cannot show its own words is not one. */
@media (max-width: 460px) {
  .ax-cta { white-space: normal; text-align: center; line-height: 1.25; }
}

/* ══ THE BRAND SELECTOR EXISTS ON DESKTOP TOO ═════════════════════════════
   Founder: "why, when I keep selecting the white label on the top, does it keep
   messing up different views… you need that brand selector on mobile and on
   desktop."
   MEASURED on /whitelabel: at 393px the row is there, 61px tall, and switching
   brand moves the headline 0px and changes nothing but the brand. At 1440px it
   computes to `display: none` — so on a desktop the white-label and holders
   pages had NO WAY to choose which brand you were looking at, which is the
   whole point of those two pages.
   The row was hidden on desktop for a good reason that only applies to HALF of
   what it does. On a brand page it repeats the rail's vault links, and two
   copies of one menu is the thing this shell exists to stop. On a page with no
   vault of its own it is the BRAND SELECTOR, and the rail has no equivalent.
   The row already labels itself — aria-label="The brands" vs "This brand" —
   so the distinction is in the markup and does not need inventing. */
@media (min-width: 1080px) {
  .axsh__sub[aria-label="The brands"] {
    display: block;
    left: var(--shell-rail-w, 268px);
  }
  /* ⛔ DELETED: `left: 72px`. This is from the era when the row was a sticky
     bar pinned to the rail's right edge — 72px was the collapsed rail's width.
     The row is a CENTRED FLOATING CAPSULE now, and this rule is more specific
     than the centring one, so the moment the rail collapsed the capsule jumped
     to left:72px, then translateX(-50%) pulled it 302px further left.
     MEASURED at 1240 collapsed: the capsule sat at -230..375 — 591px off
     centre, with AXIS, JETR, ZERA and SZN sitting BEHIND the rail. That is
     exactly the screenshot he sent.
     The centring expression already reads --shell-w, which becomes
     --shell-w-shut on collapse, so it re-centres on its own. There is nothing
     to replace this with. */

  .axsh__sub[aria-label="The brands"] .axsh__subrow {
    justify-content: flex-start;
    padding-inline: clamp(20px, 2.4vw, 40px);
  }
}

/* ⛔ NO BRAND STRIP ON THE HOMEPAGE, AT ANY WIDTH. Founder: "these selectors
   should not be at the top on the home page at all."
   He is right and it was a third copy. The homepage already carries the six
   brands as a real list in its own body AND a draggable card carousel — both
   of which ARE the brand selector there, and both of which are better than a
   chip strip. The shell was adding a pinned row above them for no reason, on
   the one page that deliberately drops the rail because it is a landing page
   rather than an app surface. Two controls with one destination is the exact
   thing this shell was built to stop; three is worse.
   ⚠ NOT scoped to a media query. The complaint came from a 1006px window —
   below the desktop breakpoint — so a desktop-only rule would have left the
   strip exactly where he saw it. */
html[data-shell-home] .axsh__sub { display: none; }

/* ══ THE BRAND SELECTOR FLOATS, CENTRED, AT THE TOP ══════════════════════
   Founder, on the white-label deck at desktop width: "selectors should be
   floating at the top in the center for the desktop for this view."

   MEASURED before this rule: a STICKY bar at x=300, 1140px wide, 57px tall,
   square corners, running the full width of the content with the six chips
   bunched at its left edge (x=335) — and 64px of dead space above it, because
   the body still reserves room for a desktop header that is `display:none` on
   this page. An edge-to-edge bar is the one shape the rest of this shell no
   longer uses: the rail is an island, the panels are islands, and this was the
   last thing welded to the layout.

   So it becomes a capsule: shrink-to-fit, 999px, the same dark glass and the
   same shadow as the rail, floating at the SAME top edge as the rail (both at
   --shell-gap, so they line up rather than nearly lining up).

   ⚠ CENTRED ON THE CONTENT, NOT ON THE WINDOW — the same correction the sheet
   carries. `left:50%` is half the VIEWPORT, and the island owns the first
   --shell-w plus two gaps of it; without the correction the capsule reads as
   pushed left by half the rail on every desktop width. It transitions, so it
   re-centres when the rail collapses instead of jumping. */
/* ⚠ 720px, NOT 1080. The complaint that produced this arrived from a 1006px
   window, and a desktop-only rule would have left the bar exactly where he saw
   it. The capsule is ~605px wide, so it fits comfortably from 720 up; below
   that the six chips genuinely cannot share a line and the scrolling strip is
   the right answer, so that is where the old treatment still lives.
   Between 720 and 1079 there is no rail, so it centres on the viewport and
   sits below the top bar; the 1080 block further down corrects both. */
@media (min-width: 720px) {
  .axsh__sub[aria-label="The brands"] {
    position: fixed;
    top: calc(var(--shell-top-h, 56px) + var(--shell-gap, 16px));
    left: 50%;
    transform: translateX(-50%);
    width: max-content;
    max-width: calc(100vw - (var(--shell-gap, 16px) * 4));
    border: 1px solid var(--shell-line, rgba(255, 255, 255, .11));
    border-bottom: 1px solid var(--shell-line, rgba(255, 255, 255, .11));
    border-radius: 999px;
    background: color-mix(in srgb, var(--shell-bg, #060607) 88%, transparent);
    -webkit-backdrop-filter: blur(26px) saturate(115%) brightness(.6);
    backdrop-filter: blur(26px) saturate(115%) brightness(.6);
    box-shadow:
      0 30px 70px -22px rgba(0, 0, 0, .82),
      0 2px 10px -4px rgba(0, 0, 0, .6),
      inset 0 1px 0 rgba(255, 255, 255, .05);
    z-index: calc(var(--shell-z, 900) + 2);
    transition: left .42s var(--shell-ease, ease);
  }
  /* the homepage has no strip to correct for — see the rule below */
  .axsh__sub[aria-label="The brands"] .axsh__subrow {
    justify-content: center;
    padding: 7px 8px;
    /* §8: the capsule is shrink-to-fit, so nothing overflows and a scroller
       here would only clip the chips' own focus rings against the radius */
    overflow: visible;
  }
  /* ⛔ THE CONTENT HAS TO CLEAR A FIXED CAPSULE. Sticky reserved its own space;
     fixed does not, so without this the first heading paints underneath it.
     --shell-sub-h is measured and written by axis-app.js — the capsule's height
     depends on the chip type scale, and guessing it is how a 2px overlap ships.
     It falls back to whatever the page's own header reserves, which is what
     every page WITHOUT a capsule must keep. */
  html[data-shell] body {
    padding-top: var(--shell-sub-h, var(--shell-top-h, 56px));
  }
}

/* on desktop the rail exists, so the capsule re-centres on the CONTENT column
   and rises to the rail's own top edge — the two line up rather than nearly */
@media (min-width: 1080px) {
  .axsh__sub[aria-label="The brands"] {
    top: var(--shell-gap, 16px);
    left: calc(50% + ((var(--shell-w, 268px) + (var(--shell-gap, 16px) * 2)) / 2));
    max-width: calc(100vw - var(--shell-w, 268px) - (var(--shell-gap, 16px) * 4));
  }
  html[data-shell] body {
    padding-top: var(--shell-sub-h, var(--shell-top-d, 64px));
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE DOCK AS A FLOATING CAPSULE — matched 1:1 to the jetr.app reference
   (founder, 2026-09-21: "make the tab bars like this 1:1")
   ──────────────────────────────────────────────────────────────────────────
   ⛔ THE FIVE DO NOT CHANGE. Home · Brands · [orb] · Holders · White label is
   LOCKED — he revised that list three times inside one message. Nothing below
   touches an item, a label or the order; this is treatment only.

   What the reference actually is, measured off his screenshot rather than
   described: a detached pill with a hairline accent border ALL THE WAY ROUND
   (ours was a full-bleed bar with a top border only), the lit item in its own
   rounded chip instead of just tinted text, labels in sentence case, and the
   centre orb on a solid dark disc that BREAKS the capsule's top edge.

   ⚠ THE ORBITAL RINGS ARE BACK, and that reverses an earlier call of his —
   "a lot of machinery for a button whose job is 'your wallet'". They are in
   the reference he sent, so the reference wins, but it is one line to undo:
   delete the .axsh__ell block and the three <i> in axis-app.js.

   ⚠ THEY ARE 2D ELLIPSES, NOT 3D RINGS. The original r1–r4 orbited on
   rotateX/rotateY, which needs `transform-style:preserve-3d` on the orb — and
   axis-app.js:238 records that preserve-3d is exactly what disables blending
   in Chromium and reprints the mark's black ground as a hard square. Rotating
   flat ellipses reads identically at 54px and touches none of that.
   ══════════════════════════════════════════════════════════════════════════ */

.axsh__dock {
  inset: auto 12px calc(12px + env(safe-area-inset-bottom, 0px)) 12px;
  height: 62px;
  padding-bottom: 0;
  padding-inline: 5px;
  border-radius: 999px;
  /* The shorthand deliberately replaces the `border-top` declared earlier —
     a capsule needs the hairline on all four sides. */
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C9CBD1) 30%, transparent);
  background:
    linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,0) 62%),
    color-mix(in srgb, #05060A 93%, transparent);
  box-shadow:
    0 12px 36px rgba(0,0,0,.74),
    0 0 44px -12px color-mix(in srgb, var(--shell-tone, #C9CBD1) 42%, transparent),
    inset 0 1px 0 rgba(255,255,255,.06);
}

/* Sentence case, as in the reference — the uppercase came from .axsh__label's
   global rule, which also dresses the rail. Scoped to the dock so the rail is
   untouched. */
.axsh__dock .axsh__tab { text-transform: none; font-size: 11px; letter-spacing: .01em; }

/* THE LIT ITEM IS A CHIP, not tinted text. */
.axsh__dock .axsh__tab[aria-current]:not(.axsh__tab--centre) {
  margin: 5px 2px;
  border-radius: 15px;
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C9CBD1) 44%, transparent);
  background: color-mix(in srgb, var(--shell-tone, #C9CBD1) 11%, transparent);
  box-shadow: 0 0 20px -8px color-mix(in srgb, var(--shell-tone, #C9CBD1) 70%, transparent),
              inset 0 1px 0 rgba(255,255,255,.05);
}

/* THE ORB ON ITS OWN DISC, breaking the capsule's top edge.
   The 5px spread ring is painted in the page background, so the capsule's
   hairline appears to pass behind the disc instead of running through it. */
/* MEASURED against his reference: the orb's centre sits ON the capsule's top
   edge, so roughly half the disc is outside. At -20px it was still two-thirds
   inside and read as an inset button rather than a raised one. */
.axsh__dock .axsh__tab--centre { margin-top: -30px; }
/* The gold hairline at the disc edge is in the reference and was nearly
   invisible at 62% over a dark disc. */
.axsh__dock .axsh__orb .axsh__ring {
  border-width: 1.5px;
  border-color: color-mix(in srgb, var(--shell-tone, #C9CBD1) 86%, transparent);
}
.axsh__dock .axsh__orb {
  width: 54px; height: 54px; border-radius: 50%;
  background: radial-gradient(circle at 50% 40%, #0c0d10 0%, #050607 68%);
  box-shadow:
    0 0 0 5px var(--shell-bg, #05060A),
    0 10px 26px rgba(0,0,0,.82),
    0 0 34px -8px color-mix(in srgb, var(--shell-tone, #C9CBD1) 55%, transparent);
}
/* The mark reads as the subject only if the ellipses clear it. At 26px the
   widest ellipse cut straight through the wing. */
.axsh__dock .axsh__orb .axsh__core { width: 26px; height: 26px; margin: -13px 0 0 -13px; }
.axsh__dock .axsh__orb .axsh__core .axsh__mk { width: 23px; height: 23px; }

/* The orbital ellipses. §17: centred on margins, never a % inside a transform,
   because the transform is carrying the rotation. */
.axsh__dock .axsh__orb .axsh__ell {
  position: absolute; left: 50%; top: 50%; z-index: 2;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--shell-tone, #C9CBD1) 52%, transparent);
  box-shadow: 0 0 7px -2px color-mix(in srgb, var(--shell-tone, #C9CBD1) 60%, transparent);
  pointer-events: none;
}
.axsh__dock .axsh__orb .e1 { width: 50px; height: 21px; margin: -10.5px 0 0 -25px; }
.axsh__dock .axsh__orb .e2 { width: 50px; height: 34px; margin: -17px 0 0 -25px;
  border-color: color-mix(in srgb, var(--shell-tone, #C9CBD1) 38%, transparent); }
.axsh__dock .axsh__orb .e3 { width: 34px; height: 50px; margin: -25px 0 0 -17px;
  border-color: color-mix(in srgb, var(--shell-tone, #C9CBD1) 30%, transparent); }

@media (prefers-reduced-motion: no-preference) {
  .axsh__dock .axsh__orb .e1 { animation: axshEllA 11s linear infinite; }
  .axsh__dock .axsh__orb .e2 { animation: axshEllB 16s linear infinite; }
  .axsh__dock .axsh__orb .e3 { animation: axshEllA 21s linear infinite; }
  @keyframes axshEllA { from { transform: rotate(0deg); }    to { transform: rotate(360deg); } }
  @keyframes axshEllB { from { transform: rotate(360deg); }  to { transform: rotate(0deg); } }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE CARD FAN — CARDS, AND NOTHING BEHIND THEM
   ──────────────────────────────────────────────────────────────────────────
   Founder, 2026-09-21: "There should be no black thing behind the cards. It
   should just be the cards popping up on the screen. There should be no black
   rectangle behind them when you press the center button… It should just be
   the cards popping up and they should collapse the same way. They should
   reverse-collapse when you tap."

   The panel was a real surface — a blurred, bordered, shadowed sheet — and the
   cards were sitting ON it. He is asking for the cards to be the whole object,
   so the sheet is removed entirely rather than made darker or more transparent.

   ⚠ THE POP USES `scale` AND `translate`, NEVER `transform`. Each card's arc
   position IS its transform — translate3d(--x,--y) then rotate(--rot), in that
   order and for a documented reason — so animating `transform` here would
   throw every card back to the centre of the fan and flatten the arc. `scale`
   and `translate` are their own properties and compose with it.
   ══════════════════════════════════════════════════════════════════════════ */
.axsh__pop {
  background: none;
  backdrop-filter: none; -webkit-backdrop-filter: none;
  border: 0;
  box-shadow: none;
  padding: 0 0 12px;
}

/* ⚠ AND IT HAS TO CLEAR THE CAPSULE. The fan was bottom-anchored at
   bar-height + 14px, which was correct while the dock was a full-bleed bar
   sitting ON the bottom edge. The capsule now floats 12px up, so the same
   offset left 2px of clearance and the cards landed ON the tab bar — the JETR
   label printed over "Brands" and the hand covered Home. A floating dock has
   to be measured from the bottom of the SCREEN, not the bottom of the bar. */
.axsh__pop {
  bottom: calc(var(--shell-bar-h, 62px) + env(safe-area-inset-bottom, 0px) + 52px);
}

@media (prefers-reduced-motion: no-preference) {
  /* ⛔ "IT SHOULD BE A LITTLE BIT MORE ANIMATED. THEY SHOULD RETRACT THE SAME
     WAY THEY EXPAND." (founder, 2026-09-21)
     The first pass faded each card up in place — six cards appearing where
     they already were. A hand of cards does not appear, it OPENS: they come
     out of the button that was pressed and they fold back into it.

     Three properties carry that, and none of them is `transform`, because
     `transform` already holds the card's arc position (translate3d then
     rotate, in that order and for a documented reason). `translate`, `scale`
     and `rotate` are their own properties and compose on top of it:
       translate  calc(var(--x) * -.9) — pulls each card back toward the orb,
                  furthest for the outermost cards, zero for the centre;
       rotate     calc(var(--rot) * -.7) — takes most of the fan out, so the
                  hand starts nearly closed and spreads;
       scale      .46 — small at the button, full size on the arc.

     The exit is the same keyframes reversed, not a different move — that is
     what "retract the same way" means. It runs faster because an exit that
     takes as long as its entrance reads as hesitation. */
  .axsh__pop .axsh__fanc {
    animation: axshFanIn .52s var(--ease-out-expo, cubic-bezier(.16,1,.3,1)) both;
  }
  .axsh__pop.is-closing .axsh__fanc {
    animation: axshFanOut .30s cubic-bezier(.7, 0, .84, 0) both;
  }
  /* Centre-out on the way open, outside-in on the way closed. Written per
     card because CSS cannot take |i - 2.5| from a custom property. */
  .axsh__fan > :nth-child(1) { animation-delay:  88ms; }
  .axsh__fan > :nth-child(2) { animation-delay:  52ms; }
  .axsh__fan > :nth-child(3) { animation-delay:  16ms; }
  .axsh__fan > :nth-child(4) { animation-delay:  16ms; }
  .axsh__fan > :nth-child(5) { animation-delay:  52ms; }
  .axsh__fan > :nth-child(6) { animation-delay:  88ms; }
  .is-closing .axsh__fan > :nth-child(1) { animation-delay:  0ms; }
  .is-closing .axsh__fan > :nth-child(2) { animation-delay: 26ms; }
  .is-closing .axsh__fan > :nth-child(3) { animation-delay: 52ms; }
  .is-closing .axsh__fan > :nth-child(4) { animation-delay: 52ms; }
  .is-closing .axsh__fan > :nth-child(5) { animation-delay: 26ms; }
  .is-closing .axsh__fan > :nth-child(6) { animation-delay:  0ms; }

  @keyframes axshFanIn {
    from { opacity: 0; scale: .46;
           translate: calc(var(--x, 0px) * -.9) 42px;
           rotate: calc(var(--rot, 0deg) * -.7); }
    to   { opacity: 1; scale: 1; translate: 0 0; rotate: 0deg; }
  }
  @keyframes axshFanOut {
    from { opacity: 1; scale: 1; translate: 0 0; rotate: 0deg; }
    to   { opacity: 0; scale: .46;
           translate: calc(var(--x, 0px) * -.9) 42px;
           rotate: calc(var(--rot, 0deg) * -.7); }
  }
}
