/* ═══════════════════════════════════════════════════════════════════════════
   SONO XR — THE HUB  (2026-08-06)

   ONE control panel. Everything in it. Hide and expand at will.

   WHAT THIS REPLACES. The showcase had grown THREE menu systems (grouped
   bottom dock · right side rail · title-card dropdown) plus roughly six
   free-floating controls (surface selector, settings popover, lead pill,
   narration toggle, back pill, full-screen button). Every one of them ate
   screen the space should have had.

   HOW IT REPLACES THEM — READ THIS BEFORE EDITING.
   The HUB is an ADAPTER, not a rewrite. The legacy chrome is still built by
   showcase-stage.js exactly as before — it keeps every listener, every bit of
   config gating, every feature flag. This file HIDES it; js/showcase-hub.js
   then either MOVES its panels into the HUB sheet or proxies clicks into it.
   That is why ~2,500 lines of wired engine JS needed no edits.

   Consequence: never "clean up" the hidden chrome by deleting it from
   showcase-template.html. It is the machine room. The HUB is the console.

   Governing test (sono-tokens.css): does this make the space more present, or
   the interface more present? The HUB is glass over a room — it recedes.
   ═════════════════════════════════════════════════════════════════════════ */

/* ═══ 1 · STAND THE LEGACY CHROME DOWN ═════════════════════════════════════
   Hidden, not removed. `visibility` rather than `display` where a script
   measures the node; `display` where nothing does. Everything here is
   re-presented inside the HUB. ═══════════════════════════════════════════ */

body.sxr-hub .sxr-dock-wrap,          /* menu system 1 — grouped bottom dock */
body.sxr-hub .sxr-rail,               /* menu system 2 — right side rail     */
body.sxr-hub .sxr-rail-panel,
body.sxr-hub #sxr-title-menu,         /* menu system 3 — title-card dropdown */
body.sxr-hub .sxr-title-cv,
body.sxr-hub .showcase-hud-header,    /* NO header at all (Michael, 08-06)   */
body.sxr-hub .showcase-sidebar-left,
body.sxr-hub .sxr-settings-btn,
body.sxr-hub .sxr-settings-pop,
body.sxr-hub .sxr-lead-pill,
body.sxr-hub .sxr-narr-btn,
body.sxr-hub #sxr-audio-pill,
body.sxr-hub .ar-hud-grid,
body.sxr-hub .ar-hud-filter {
  display: none !important;
}

/* The splash is gone as an EXPERIENCE — you land in the space. It stays in
   the DOM as an anchor: stage.js mounts the surface selector into
   .splash-content-box and reads #enter-experience-btn. Removing it would
   silently kill the kiosk/XR surface switch. The HUB proxies into it. */
body.sxr-hub #showcase-splash-screen {
  position: fixed;
  width: 1px; height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  z-index: -1;
}

/* THE STAGE GOES FULL BLEED. There is no header to clear and the HUB floats
   over the bottom, so the space finally gets the whole viewport. This is the
   single biggest "content is the star" win in the port. */
body.sxr-hub #sxr-stage .sxr-stage-inner {
  left: 0; right: 0; top: 0; bottom: 0;
  border-radius: 0;
}
body.sxr-hub .sxr-frame { border-radius: 0; }

/* Media goes edge to edge; READING modules do not. The welcome and overview
   cards are centred text, so with the stage at inset 0 their lower half sat
   underneath the HUB. Reserve the collapsed rail's height for them — the
   expanded sheet is a transient state and is allowed to overlap. */
body.sxr-hub .sxr-welcome,
body.sxr-hub .sxr-overview,
body.sxr-hub .sxr-card-view {
  padding-bottom: 120px;
}

/* While the guide is up the stage stands down. You still LAND IN THE SPACE —
   the background capture is behind everything and never hidden — but the
   stage's own overview/welcome card would otherwise print a second headline
   straight through the greeting. One title at a time. */
body.sxr-hub.sxr-guide-open #sxr-stage {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-chrome) var(--ease-out);
}

/* The presenter is appended to <body>, NOT into #sxr-stage, so hiding the
   stage does not hide it. On any showcase with cfg.presenter it auto-ran and
   printed its own headline and "Step inside" straight through the greeting —
   only the SONO XR flagship has a presenter configured, which is why the Dust
   showcase never showed this. One title at a time, same rule as the stage. */
body.sxr-hub.sxr-guide-open .sxr-pres {
  opacity: 0 !important;
  pointer-events: none !important;
}
body.sxr-hub #sxr-stage { transition: opacity var(--dur-chrome) var(--ease-out); }

/* THE CLIENT LOGO IS EXEMPT FROM THE IDLE FADE. sono-showcase.css fades all
   chrome out after inactivity, which is right for controls and wrong for
   branding: "client logo ALWAYS visible" (Michael, 2026-08-06). Beats the
   idle rule on specificity rather than !important so a skin can still
   restyle it. */
body.sxr-hub.sono-idle .sxr-brand-slot { opacity: 1; }

/* atmosphere: a darkened ROOM, not a black rectangle. Light pools at centre
   and falls off at the edges, so chrome and media weld into one image. */
body.sxr-hub .sxr-room-scrim {
  position: fixed; inset: 0; z-index: 41; pointer-events: none;
  background:
    radial-gradient(120% 90% at 50% 42%,
      rgba(0,0,0,0) 30%, rgba(8,7,6,0.60) 78%, rgba(8,7,6,0.90) 100%),
    linear-gradient(0deg, rgba(10,9,8,0.55) 0%, rgba(10,9,8,0) 26%);
}

/* ═══ 2 · CLIENT LOGO — top left, always, NO box ═══════════════════════════
   #sxr-brand-slot is the existing white-label slot; showcase.js already fills
   it with the client logo, the client name, or the "Powered by" mark. It is
   re-homed here rather than duplicated, so white-label keeps working. ═════ */

/* !important is load-bearing here, not laziness: sono-showcase.css pins this
   slot to the BOTTOM-RIGHT with `left:auto !important; right:… !important`
   inside a (min-width:769px) query. An important declaration can only be
   beaten by another important one, so matching it is the only way to move
   the logo to the top-left. */
body.sxr-hub .sxr-brand-slot {
  position: fixed !important;
  top: 26px !important;
  left: 28px !important;
  right: auto !important;
  bottom: auto !important;
  transform: none !important;
  z-index: 46;
  display: flex; align-items: center; gap: 11px;
  padding: 0;                 /* NO BOX — Michael, 2026-08-06 */
  background: none;
  border: 0;
  box-shadow: none;
  border-radius: 0;
  opacity: 1;
  pointer-events: none;
}
/* THE CLIENT'S REAL LOGO, IN ITS REAL COLOURS (Michael, 2026-08-06).
   This used to be pushed through grayscale + brightness so it would always
   read on live media. That is the wrong trade: a client's brand is the one
   thing in a showcase that must not be restyled, and stripping MBB's greens
   and chrome made it look like a placeholder. Legibility now comes from a
   drop-shadow behind the mark instead of from repainting it. */
body.sxr-hub .sxr-brand-slot img {
  height: 34px; width: auto; display: block;
  opacity: 1;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.65))
          drop-shadow(0 0 24px rgba(0, 0, 0, 0.45));
}
body.sxr-hub .sxr-brand-slot .sxr-brand-name,
body.sxr-hub .sxr-brand-slot .sxr-poweredby {
  font-family: var(--font-screen);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  /* secondary, not tertiary: this sits on live video, where tertiary ink
     disappears against a bright frame. It is still quieter than any content. */
  color: var(--ink-secondary);
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.55);
}

/* ═══ 2b · THE POWERED-BY LOCKUP ══════════════════════════════════════════
   Bottom right, always present, never the loudest thing on screen. The slate
   holds the bottom left and the HUB holds the centre, so this is the one
   corner left — and a credit belongs in a corner. Exempt from the idle fade
   for the same reason the client logo is: attribution is not chrome. ═════ */

/* ⚠️ MOVED TO THE LEFT, 2026-08-09. The credit and the Full Screen toggle
   swapped corners: Michael pulled Full Screen off the dock ("that way doc
   cleaner") and it needed the corner a screen control belongs in. A credit
   is happy anywhere quiet, so it takes the left. */
#sxr-powered {
  position: fixed;
  left: 28px; bottom: 26px;
  z-index: 46;
  display: flex; align-items: center; gap: 9px;
  pointer-events: none;
  font-family: var(--font-screen);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.6);
}
#sxr-powered img {
  height: 22px; width: auto; display: block;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.6));
}
#sxr-powered b { color: var(--ink-secondary); font-weight: 600; }
body.sxr-hub.sono-idle #sxr-powered { opacity: 1; }
/* it steps aside for the greeting, which owns the whole frame */
body.sxr-guide-open #sxr-powered {
  opacity: 0;
  transition: opacity var(--dur-chrome) var(--ease-out);
}

/* ═══ 2c · THE SONO LAUNCHER ══════════════════════════════════════════════
   Michael, 2026-08-06: "I want SONO GUIDE to really be visible and not just a
   random bottom [button] visitors might miss." It was one more text button in
   a rail of text buttons. It is now a lit mark that sits ABOVE the HUB, on
   its own, breathing — the only element in the interface that moves at rest,
   which is exactly why the eye finds it. ═══════════════════════════════ */

#sxr-sono-launch {
  position: fixed;
  right: 28px; bottom: 104px;
  z-index: 47;
  display: flex; align-items: center; gap: 11px;
  padding: 9px 16px 9px 9px;
  border: 0;
  border-radius: 999px;
  background: var(--glass);
  box-shadow: var(--edge), var(--lift), 0 0 0 1px var(--accent-dim);
  backdrop-filter: var(--blur-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  color: var(--ink-primary);
  font-family: var(--font-screen);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: transform var(--dur-press) var(--ease-out),
              box-shadow var(--dur-press) var(--ease-out),
              opacity var(--dur-chrome) var(--ease-out);
}
#sxr-sono-launch .orb {
  position: relative;
  display: grid; place-items: center;
  width: 34px; height: 34px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 40%,
    var(--accent-glow) 0%, var(--accent-dim) 55%, transparent 78%);
  color: var(--ink-primary);
  flex: 0 0 auto;
}
#sxr-sono-launch .orb svg { width: 22px; height: 22px; }
/* the breath — one slow pulse, the interface's only resting motion */
#sxr-sono-launch .orb::after {
  content: '';
  position: absolute; inset: -5px;
  border-radius: 50%;
  border: 1px solid var(--accent-glow);
  animation: sonoBeacon 3.4s var(--ease-out) infinite;
}
@keyframes sonoBeacon {
  0%   { transform: scale(0.86); opacity: 0.85; }
  70%  { transform: scale(1.18); opacity: 0; }
  100% { transform: scale(1.18); opacity: 0; }
}
@media (hover: hover) and (pointer: fine) {
  #sxr-sono-launch:hover {
    transform: translateY(-2px);
    box-shadow: var(--edge), var(--lift), 0 0 0 1px var(--accent), 0 0 34px var(--accent-dim);
  }
}
body.sxr-guide-open #sxr-sono-launch,
body.sxr-hub-hidden #sxr-sono-launch,
body.sxr-presenting #sxr-sono-launch {
  opacity: 0; pointer-events: none;
}

/* SONO ON THE PRESENTER BAR — the tour has a host, not captions */
.sxr-pres-sono {
  display: inline-flex; align-items: center; gap: 8px;
  margin-right: 14px;
  flex: 0 0 auto;
  color: var(--accent);
}
.sxr-pres-sono .m { display: grid; place-items: center; width: 26px; height: 26px; }
.sxr-pres-sono .m svg { width: 26px; height: 26px; }
.sxr-pres-sono .n {
  font-family: var(--font-screen);
  font-size: 0.625rem; font-weight: 500;
  letter-spacing: 0.18em; text-transform: uppercase;
}

/* ═══ 3 · THE HUB ══════════════════════════════════════════════════════════ */

#sxr-hub {
  position: fixed;
  left: 50%; bottom: 22px;
  transform: translateX(-50%);
  z-index: 45;
  width: min(1180px, calc(100vw - 56px));
  border-radius: var(--radius-panel);
  background: var(--glass);
  box-shadow: var(--edge), var(--lift);
  backdrop-filter: var(--blur-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  transition: opacity var(--dur-chrome) var(--ease-out),
              transform var(--dur-chrome) var(--ease-out);
}

/* HIDE AT WILL — the console is not a wall. It stands down while the guide
   is up, and on demand via the Hide control or the H key. */
body.sxr-guide-open #sxr-hub,
body.sxr-hub-hidden #sxr-hub {
  opacity: 0;
  transform: translateX(-50%) translateY(14px);
  pointer-events: none;
}

/* the peek tab — the only thing left when the HUB is hidden */
#sxr-hub-peek {
  position: fixed;
  left: 50%; bottom: 0;
  transform: translateX(-50%) translateY(100%);
  z-index: 44;
  display: flex; align-items: center; gap: 8px;
  padding: 9px 18px 11px;
  border: 0;
  border-radius: var(--radius-panel) var(--radius-panel) 0 0;
  background: var(--glass);
  box-shadow: var(--edge);
  backdrop-filter: var(--blur-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  color: var(--ink-secondary);
  font-family: var(--font-screen);
  font-size: 0.75rem;
  cursor: pointer;
  transition: transform var(--dur-chrome) var(--ease-out),
              color var(--dur-press) var(--ease-out);
}
body.sxr-hub-hidden #sxr-hub-peek { transform: translateX(-50%) translateY(0); }
body.sxr-guide-open #sxr-hub-peek { transform: translateX(-50%) translateY(100%); }
#sxr-hub-peek:hover { color: var(--ink-primary); }

/* ── the always-visible rail ───────────────────────────────────────────── */
#sxr-hub .hub-rail {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 10px;
}
#sxr-hub .hub-rail .grow { flex: 1; }
#sxr-hub .hub-rail .sep {
  width: 1px; height: 22px;
  background: rgba(255, 255, 255, 0.10);
  margin: 0 4px;
}

.hub-btn {
  position: relative;
  display: flex; align-items: center; gap: 9px;
  padding: 10px 14px;
  border: 0;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--ink-secondary);
  cursor: pointer;
  font-family: var(--font-screen);
  font-size: 0.82rem;
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--dur-press) var(--ease-out),
              color var(--dur-press) var(--ease-out);
}
.hub-btn svg { width: 17px; height: 17px; stroke-width: 1.6; flex: 0 0 auto; }
.hub-btn i { width: 17px; height: 17px; flex: 0 0 auto; }
@media (hover: hover) and (pointer: fine) {
  .hub-btn:hover { background: rgba(255, 255, 255, 0.07); color: var(--ink-primary); }
}
.hub-btn.on { color: var(--ink-primary); background: rgba(255, 255, 255, 0.08); }
.hub-btn[hidden] { display: none !important; }

/* the live pip — light, tiny. At rest this is the ONLY place the accent
   lands, which is what keeps the accent reading as state and not decoration. */
.hub-pip {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent-glow);
  flex: 0 0 auto;
}

/* ── the expanding sheet ───────────────────────────────────────────────── */
.sxr-dead-hub-sheet {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-sheet) var(--ease-drawer);
}
#sxr-hub.open .hub-sheet { grid-template-rows: 1fr; }
.sxr-dead-hub-clip { overflow: hidden; }
.sxr-dead-hub-pad {
  padding: 4px 14px 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.hub-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-3);
  padding: 14px 2px 12px;
}
.hub-head .t {
  font-family: var(--font-screen);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}

/* one sheet, many panels — exactly one mounted at a time */
.hub-panel { display: none; }
.hub-panel.on {
  display: block;
  animation: hubFade var(--dur-sheet) var(--ease-out) both;
}
@keyframes hubFade {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* ═══ 4 · CONTENT — a rail, not a wall ═════════════════════════════════════
   Michael's own metaphor is a smart TV: content lives in a row you scan
   sideways. The thumbnails ARE the colour in this interface — which is what
   lets every control stay monochrome without the result going flat. ═══════ */

.hub-grid {
  display: flex;
  gap: 9px;
  overflow-x: auto;
  scrollbar-width: none;
  padding: 2px 2px 4px;
  scroll-snap-type: x proximity;
}
.hub-grid::-webkit-scrollbar { display: none; }

.hub-tile {
  position: relative;
  flex: 0 0 186px;
  scroll-snap-align: start;
  border: 0; padding: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
  transition: transform var(--dur-sheet) var(--ease-out);
}
.hub-tile .frame {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-control);
  overflow: hidden;
  background: var(--ground-02);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08),
              0 6px 18px rgba(0, 0, 0, 0.45);
  transition: box-shadow var(--dur-sheet) var(--ease-out);
}
.hub-tile .img {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
  filter: saturate(0.9);
  transition: transform 0.6s var(--ease-out), filter var(--dur-chrome) var(--ease-out);
}
/* a tile with no poster gets the icon, not a broken frame */
.hub-tile .ico {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-tertiary);
}
.hub-tile .ico i, .hub-tile .ico svg { width: 26px; height: 26px; stroke-width: 1.25; }
.hub-tile .scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(0,0,0,0) 22%, rgba(6,5,4,0.62) 52%, rgba(4,3,3,0.95) 100%);
}
.hub-tile .cnt {
  position: absolute; top: 8px; right: 8px;
  font-family: var(--font-screen);
  font-size: 0.625rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-secondary);
  background: rgba(8, 7, 6, 0.6);
  padding: 3px 6px;
  border-radius: var(--radius);
}
.hub-tile .meta { padding: 8px 2px 0; }
.hub-tile .k {
  font-family: var(--font-screen);
  font-size: 0.5625rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.hub-tile .nm {
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: -0.012em;
  margin-top: 2px;
  color: var(--ink-primary);
}
@media (hover: hover) and (pointer: fine) {
  .hub-tile:hover { transform: translateY(-4px); }
  .hub-tile:hover .frame {
    box-shadow: inset 0 0 0 1px var(--accent-glow),
                0 14px 34px rgba(0, 0, 0, 0.55),
                0 0 34px var(--accent-dim);
  }
  .hub-tile:hover .img { transform: scale(1.07); filter: saturate(1.12); }
}
.hub-tile.active .frame {
  box-shadow: inset 0 0 0 1px var(--accent), 0 0 26px var(--accent-dim);
}
.hub-tile.active .nm { color: var(--accent); }

/* ═══ 4b · CONNECT — the third hub ════════════════════════════════════════
   Explore together, or reach the team. Cards are uniform because none of
   these outranks the others for every visitor — a buyer wants the live
   tour, a marketer wants the link, a team wants the notes. ══════════════ */

.connect-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(228px, 1fr));
  gap: 8px;
  padding-bottom: 4px;
}
.connect-card {
  position: relative;
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  gap: 3px;
  min-height: 116px;
  padding: 13px 15px 12px;
  border: 0;
  border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.035);
  color: var(--ink-primary);
  text-align: left;
  cursor: pointer;
  transition: background var(--dur-press) var(--ease-out),
              transform var(--dur-press) var(--ease-out),
              box-shadow var(--dur-press) var(--ease-out);
}
.connect-card .tag {
  justify-self: start;
  font-family: var(--font-screen);
  font-size: 0.5625rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  color: var(--ink-tertiary);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius);
  padding: 3px 6px;
  margin-bottom: 5px;
}
.connect-card strong { font-size: 0.95rem; font-weight: 600; letter-spacing: -0.012em; }
.connect-card small { font-size: 0.76rem; line-height: 1.4; color: var(--ink-secondary); }
.connect-card .act {
  font-family: var(--font-screen);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-top: 8px;
}
@media (hover: hover) and (pointer: fine) {
  .connect-card:not(.is-off):hover {
    background: rgba(255, 255, 255, 0.07);
    transform: translateY(-2px);
    box-shadow: 0 0 0 1px var(--accent-dim), 0 10px 26px rgba(0, 0, 0, 0.4);
  }
}
/* the ONE card wearing the accent as a surface — the primary next step */
.connect-card.is-cta { background: rgba(255, 255, 255, 0.06); box-shadow: inset 0 0 0 1px var(--accent-dim); }

/* AN HONEST OFF STATE. The feature is named and the reason is stated, so the
   showcase never opens something that does not work — and Michael can read
   what is left to build off the product itself. */
.connect-card.is-off { opacity: 0.4; cursor: default; }
.connect-card.is-off .act { color: var(--ink-tertiary); }

.hub-back {
  border: 0; background: transparent; cursor: pointer;
  font-family: var(--font-screen);
  font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-tertiary);
  padding: 4px 2px;
  transition: color var(--dur-press) var(--ease-out);
}
.hub-back:hover { color: var(--ink-primary); }

/* ═══ 5 · ROWS — Audio Hub and Settings share one honest component ════════ */

.hub-rows { display: grid; gap: 6px; padding-bottom: 4px; }
.hub-row {
  display: flex; align-items: center; gap: 14px;
  width: 100%;
  text-align: left;
  cursor: pointer;
  padding: 12px 14px;
  border: 0;
  border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.035);
  color: var(--ink-primary);
  transition: background var(--dur-press) var(--ease-out);
}
.hub-row:hover { background: rgba(255, 255, 255, 0.07); }
.hub-row .rl { flex: 1; min-width: 0; }
.hub-row .rl b { display: block; font-size: 0.86rem; font-weight: 600; letter-spacing: -0.01em; }
.hub-row .rl i {
  display: block; font-style: normal;
  font-size: 0.74rem; color: var(--ink-tertiary); margin-top: 1px;
}
.hub-row .rv {
  font-family: var(--font-screen);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.hub-row.on .rv { color: var(--accent); }
.hub-row .sw {
  position: relative;
  width: 34px; height: 19px;
  border-radius: 19px;
  background: rgba(255, 255, 255, 0.10);
  flex: 0 0 auto;
  transition: background var(--dur-tooltip) var(--ease-out);
}
.hub-row .sw::after {
  content: '';
  position: absolute; top: 3px; left: 3px;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--ink-tertiary);
  transition: transform var(--dur-tooltip) var(--ease-out),
              background var(--dur-tooltip) var(--ease-out);
}
.hub-row.on .sw { background: var(--accent-dim); }
.hub-row.on .sw::after {
  transform: translateX(15px);
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent-glow);
}
/* AN HONEST EMPTY STATE IS NOT A CONTROL. A layer with no file says so and
   does not pretend to toggle. Command-center rule 1: every readout is true. */
.hub-row.is-empty { opacity: 0.42; cursor: default; }
.hub-row.is-empty:hover { background: rgba(255, 255, 255, 0.035); }

/* segmented control — the surface selector, re-homed from the dead splash */
.hub-seg-wrap { display: flex; align-items: center; gap: 16px; margin-bottom: 10px; }
.hub-seg-l {
  font-family: var(--font-screen);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.hub-seg {
  display: inline-flex; gap: 2px;
  padding: 3px;
  border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.05);
}
.hub-sg {
  border: 0;
  background: transparent;
  color: var(--ink-secondary);
  cursor: pointer;
  font-family: var(--font-screen);
  font-size: 0.8rem;
  font-weight: 500;
  padding: 8px 15px;
  border-radius: var(--radius);
  transition: background var(--dur-press) var(--ease-out),
              color var(--dur-press) var(--ease-out);
}
.hub-sg:hover { color: var(--ink-primary); }
.hub-sg.on { background: rgba(255, 255, 255, 0.10); color: var(--ink-primary); }

/* ═══ 6 · SONO — the guide, and the mark ══════════════════════════════════
   Sono is an abstract visored mark with a feline silhouette. NEVER a face.
   The character appears once, at the link moment (command-center rule 3),
   and thereafter lives as one button in the rail. ═══════════════════════ */

.sono-halo { position: relative; width: 104px; height: 104px; margin: 0 auto 12px; }
.sono-halo .bloom {
  position: absolute; inset: -46px; border-radius: 50%;
  background: radial-gradient(circle,
    var(--accent-dim) 0%, var(--accent-dim) 42%, transparent 68%);
  animation: sonoBreathe 5.5s ease-in-out infinite;
}
.sono-halo .ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.22);
}
.sono-halo .ring::after {
  content: '';
  position: absolute; inset: -1px; border-radius: 50%;
  border: 1px solid transparent;
  border-top-color: var(--accent);
  opacity: 0.85;
  animation: sonoSpin 7s linear infinite;
}
.sono-halo .mark {
  position: absolute; inset: 11px;
  width: calc(100% - 22px); height: calc(100% - 22px);
  color: var(--ink-primary);
  opacity: 0.94;
}
/* the scanning sweep — ONE moving element, and it STOPS once the link is up.
   Motion that never resolves is decoration; motion that resolves is status. */
.sono-halo .sweep {
  position: absolute; inset: -5px; border-radius: 50%; pointer-events: none;
  background: conic-gradient(from 0deg,
    transparent 0deg, var(--accent-dim) 26deg, transparent 60deg);
  animation: sonoSpin 2.6s linear infinite;
  opacity: 0.9;
}
body.sxr-linked .sono-halo .sweep { opacity: 0; transition: opacity 1s var(--ease-out); }
body.sxr-linked .sono-halo .ring::after { animation-duration: 16s; opacity: 0.5; }

.sono-halo.sm { width: 74px; height: 74px; margin: 0; flex: 0 0 auto; }
.sono-halo.sm .bloom { inset: -26px; }

/* the Sono rail button wears the mark's light, not a generic icon */
.sono-chip {
  width: 16px; height: 16px; border-radius: 50%; flex: 0 0 auto;
  background: var(--accent-dim);
  box-shadow: inset 0 0 0 1px var(--accent-glow);
}
.hub-btn.on .sono-chip { background: var(--accent-glow); }

/* ═══ 7 · THE GUIDE — the entry moment ════════════════════════════════════
   NO SPLASH. You land in the space; the guide is a greeting laid over it,
   with a LOCAL scrim so the type can breathe. The space is never dimmed to
   a title card, because there is no title card any more. ═════════════════ */

#sxr-guide {
  position: fixed; inset: 0; z-index: 50;
  display: grid; place-items: center;
  transition: opacity var(--dur-chrome) var(--ease-out),
              transform var(--dur-chrome) var(--ease-out);
}
#sxr-guide::before {
  content: '';
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(58% 46% at 50% 52%,
    rgba(6,5,4,0.86) 0%, rgba(6,5,4,0.62) 52%, rgba(6,5,4,0) 100%);
  transition: opacity var(--dur-chrome) var(--ease-out);
}
#sxr-guide.gone {
  opacity: 0;
  transform: scale(1.04);
  pointer-events: none;
  visibility: hidden;
}
#sxr-guide.gone::before { opacity: 0; }
#sxr-guide .guide-in { width: min(880px, 92vw); text-align: center; }

/* STATUS LINE — the console readout. Calm, small, TRUE. Command-center rule
   1: the moment any value here is invented, this stops being a console and
   becomes a costume. Every field is populated from real config in
   js/showcase-hub.js — there is no placeholder telemetry. */
.guide-status {
  display: inline-flex; align-items: center; gap: 11px;
  margin-bottom: 26px;
  font-family: var(--font-screen);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--ink-tertiary);
  animation: hubRise 0.8s var(--ease-out) both;
}
.guide-status .sep { opacity: 0.4; }
.guide-status .val { color: var(--ink-secondary); }
.guide-status .dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent-glow);
  animation: sonoPulse 2.4s ease-in-out infinite;
}
body.sxr-linked .guide-status .dot { animation: none; opacity: 1; }

#sxr-guide .guide-title {
  font-family: var(--font-display);
  font-size: clamp(2.2rem, 5.4vw, 4.1rem);
  font-weight: 300;
  line-height: 0.98;
  letter-spacing: -0.03em;
  margin: 0 0 14px;
  animation: hubRise 0.9s var(--ease-out) 0.06s both;
}
#sxr-guide .guide-title em { font-style: normal; color: var(--ink-secondary); }

/* where the space IS — a caption under the name, never a second headline */
#sxr-guide .guide-sub {
  font-family: var(--font-screen);
  font-size: 1rem;
  line-height: 1.45;
  color: var(--ink-secondary);
  max-width: 46ch;
  margin: 0 auto;
  animation: hubRise 0.9s var(--ease-out) 0.12s both;
}
#sxr-guide .guide-sub[hidden] { display: none; }

/* SONO speaks — a presence on the link, not a mascot on a poster */
.guide-sono {
  display: flex; align-items: center; justify-content: center; gap: 20px;
  margin: 34px 0;
  animation: hubRise 0.9s var(--ease-out) 0.2s both;
}
.guide-sono .says { text-align: left; max-width: 34ch; }
.guide-sono .who {
  font-family: var(--font-screen);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 6px;
}
/* HUMAN WORDS, technical structure (command-center rule 4). Never
   "ENGAGE AUTOPILOT" — the concept lives in composition and behaviour. */
.guide-sono .line {
  margin: 0;
  font-size: 1.06rem;
  line-height: 1.45;
  color: var(--ink-primary);
  letter-spacing: -0.012em;
}

/* THE GUIDE OPTIONS — full-width bars, not quiet cards.
   Michael, 2026-08-06: "the SONO GUIDE OPTIONS need to be EASY AND CLEAR to
   see." Three changes carry that: an icon chip so each option is scannable
   without reading, one option per ROW so the name never wraps mid-phrase,
   and a heavier surface so the bar wins against whatever the space is doing
   behind it. The count is dynamic — only options this showcase can deliver
   are rendered — so the layout is a stack, not a fixed 3-up grid. */
.guide-paths {
  display: grid;
  gap: 10px;
  max-width: 560px;
  margin: 0 auto;
  text-align: left;
}
.guide-path {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 16px;
  cursor: pointer;
  padding: 16px 18px;
  /* buttons carry text-align:center from the UA sheet, and the guide is a
     centred block — without this the bars centre their own copy */
  text-align: left;
  border: 0;
  border-radius: var(--radius-panel);
  color: var(--ink-primary);
  /* denser than the old card: this has to read over live video */
  background: linear-gradient(180deg,
    rgba(30, 28, 26, 0.90) 0%, rgba(17, 16, 15, 0.94) 100%);
  box-shadow: var(--edge), var(--lift);
  backdrop-filter: var(--blur-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  transition: transform var(--dur-sheet) var(--ease-out),
              box-shadow var(--dur-sheet) var(--ease-out);
  animation: hubRise 0.8s var(--ease-out) both;
  animation-delay: calc(1.3s + var(--i, 0) * 0.09s);
}
.guide-path .chip {
  display: grid; place-items: center;
  width: 40px; height: 40px;
  border-radius: var(--radius-control);
  background: var(--accent-dim);
  box-shadow: inset 0 0 0 1px var(--accent-glow);
  color: var(--accent);
}
.guide-path .chip i, .guide-path .chip svg { width: 18px; height: 18px; stroke-width: 1.6; }
.guide-path .txt { min-width: 0; }
.guide-path .n {
  font-family: var(--font-screen);
  font-size: 0.5625rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.guide-path h3 { margin: 3px 0 2px; font-size: 1.05rem; font-weight: 600; letter-spacing: -0.015em; }
.guide-path p { margin: 0; font-size: 0.8rem; line-height: 1.4; color: var(--ink-secondary); }
.guide-path .go {
  font-size: 1.05rem;
  color: var(--ink-tertiary);
  transition: transform var(--dur-sheet) var(--ease-out),
              color var(--dur-sheet) var(--ease-out);
}
/* the compact set inside the Sono panel drops the description */
.guide-paths.compact { max-width: none; gap: 8px; }
.guide-paths.compact .guide-path { padding: 12px 14px; animation: none; }
.guide-paths.compact .guide-path p { display: none; }
.guide-paths.compact .chip { width: 32px; height: 32px; }
/* light RAKES across the card on hover. The energy is a highlight, not a
   colour — this is the whole Phase-1 "flat" fix in one rule. */
.guide-path::before {
  content: '';
  position: absolute; inset: 0; border-radius: var(--radius-panel);
  pointer-events: none;
  background: linear-gradient(115deg,
    transparent 34%, rgba(255, 255, 255, 0.09) 50%, transparent 66%);
  opacity: 0;
  transition: opacity var(--dur-chrome) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .guide-path:hover {
    transform: translateY(-4px);
    box-shadow: var(--edge),
                0 4px 10px rgba(0, 0, 0, 0.44),
                0 22px 54px rgba(0, 0, 0, 0.5),
                0 0 0 1px var(--accent-dim),
                0 0 40px var(--accent-dim);
  }
  .guide-path:hover::before { opacity: 1; }
  .guide-path:hover .go { transform: translateX(4px); color: var(--accent); }
}

/* Sono reachable for the whole visit, from the rail */
.sono-panel { display: flex; align-items: center; gap: 22px; padding: 2px 0 6px; }
.sono-panel .guide-paths { flex: 1; gap: 8px; }
.sono-panel .guide-path { animation: none; padding: 15px 16px 14px; }
.sono-panel .guide-path h3 { margin: 8px 0 0; font-size: 0.9rem; }
.sono-panel .guide-path .go { bottom: 13px; right: 15px; }

/* ═══ 8 · ADOPTED PANELS ══════════════════════════════════════════════════
   Nodes MOVED into the sheet by js/showcase-hub.js keep their own scripts and
   listeners; they only need their legacy fixed positioning neutralised. ═══ */

#sxr-modal .hub-adopt {
  max-height: min(52vh, 460px);
  overflow-y: auto;
  scrollbar-width: thin;
}
#sxr-modal .hub-adopt > * {
  position: static !important;
  inset: auto !important;
  transform: none !important;
  width: auto !important;
  max-width: none !important;
  max-height: none !important;
  margin: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  display: block !important;
  opacity: 1 !important;
  visibility: visible !important;
  z-index: auto !important;
}
/* the adopted audio hub already draws its own close button — the sheet has
   one way to close, so the duplicate stands down */
#sxr-modal .hub-adopt .sxa-x { display: none !important; }

.hub-empty {
  padding: 26px 2px 30px;
  font-size: 0.86rem;
  color: var(--ink-tertiary);
}

/* ═══ 9 · MOTION ═══════════════════════════════════════════════════════════ */

@keyframes hubRise   { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes sonoBreathe { 0%, 100% { opacity: 0.75; transform: scale(1); } 50% { opacity: 1; transform: scale(1.07); } }
@keyframes sonoSpin  { to { transform: rotate(360deg); } }
@keyframes sonoPulse { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }

/* ═══ 10 · SMALL SCREENS ═══════════════════════════════════════════════════ */

@media (max-width: 900px) {
  #sxr-hub { width: calc(100vw - 20px); bottom: 12px; }
  #sxr-hub .hub-rail { overflow-x: auto; scrollbar-width: none; }
  #sxr-hub .hub-rail::-webkit-scrollbar { display: none; }
  /* labels drop, targets stay 44px — the rail scrolls rather than wraps */
  .hub-btn { padding: 12px; min-height: 44px; }
  .hub-btn > span:not(.sono-chip):not(.hub-pip) { display: none; }
  .hub-tile { flex-basis: 148px; }
  /* the guide options are already a one-per-row stack; on mobile they only
     need to tighten, and the description drops so four options still fit
     above the fold on a phone */
  .guide-paths { gap: 8px; }
  .guide-paths .guide-path { padding: 12px 14px; gap: 12px; }
  .guide-paths .guide-path p { display: none; }
  .guide-paths .chip { width: 34px; height: 34px; }
  .connect-grid { grid-template-columns: 1fr 1fr; gap: 6px; }
  .connect-card { min-height: 0; padding: 11px 12px; }
  .connect-card small { display: none; }
  body.sxr-hub .sxr-brand-slot { top: 16px !important; left: 16px !important; }
  body.sxr-hub .sxr-brand-slot img { height: 20px; }
  #sxr-guide .guide-in { width: 92vw; }
  .guide-sono { flex-direction: column; gap: 14px; }
  .guide-sono .says { text-align: center; }

  /* the status line is one row or it is noise. Coordinates and the separator
     drop; the format count — the fact that actually tells you what you can
     do here — stays. */
  .guide-status { gap: 8px; margin-bottom: 18px; white-space: nowrap; }
  .guide-status .val-coords, .guide-status .sep { display: none; }

  /* clear the logo: it is fixed at the top-left and the guide is centred, so
     on a short screen the two were landing on each other */
  #sxr-guide .guide-in { padding-top: 44px; }
  .sono-halo { width: 84px; height: 84px; }
  #sxr-guide .guide-sub { font-size: 0.9rem; }
}

@media (prefers-reduced-motion: reduce) {
  #sxr-hub, #sxr-guide, .hub-panel.on, .guide-path, .sono-halo * {
    animation: none !important;
    transition-duration: 0.01ms !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   11 · COLOUR AND MOTION IN THE MENUS  (Michael, 2026-08-06)

   "On menu, I really like the old version or something close to it… I like
   some color/motion with menus and AUDIO HUB version better."

   What the earlier build actually had that Phase 1 removed was not neon — it
   was a LEGEND and some life. Ten media types wearing one ink read as an
   undifferentiated list; nothing moving read as dead. Both come back here,
   under the discipline set out in sono-tokens.css amendment 2: colour lands
   only on small identifying marks, never on a surface, never on the media.
   ═════════════════════════════════════════════════════════════════════════ */

/* resolve a category key to its legend colour, once */
[data-cat="overview"] { --cat: var(--cat-overview); }
[data-cat="photos"]   { --cat: var(--cat-photos); }
[data-cat="videos"]   { --cat: var(--cat-videos); }
[data-cat="aerial"]   { --cat: var(--cat-aerial); }
[data-cat="tours"]    { --cat: var(--cat-tours); }
[data-cat="360"]      { --cat: var(--cat-360); }
[data-cat="splats"]   { --cat: var(--cat-splats); }
[data-cat="models"]   { --cat: var(--cat-models); }
[data-cat="plans"]    { --cat: var(--cat-plans); }
[data-cat="area"]     { --cat: var(--cat-area); }
[data-cat="docs"]     { --cat: var(--cat-docs); }
[data-cat="graphics"] { --cat: var(--cat-graphics); }

/* ── CONTENT TILES ─────────────────────────────────────────────────────── */
/* the kicker IS the legend — the one place the category colour is named */
.hub-tile .k { color: var(--cat, var(--ink-tertiary)); }

/* a hairline of the category colour along the top of the frame. 2px, and only
   on the frame's edge, so it identifies without competing with the picture. */
.hub-tile .frame::after {
  content: '';
  position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--cat, transparent);
  opacity: 0.85;
  transition: opacity var(--dur-press) var(--ease-out);
}
.hub-tile .ico { color: var(--cat, var(--ink-tertiary)); opacity: 0.55; }
.hub-tile .cnt { color: var(--ink-primary); }

@media (hover: hover) and (pointer: fine) {
  /* the lift now carries the category's own light rather than the accent's,
     so hovering a splat and hovering a floor plan do not look identical */
  .hub-tile:hover .frame {
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cat) 55%, transparent),
                0 14px 34px rgba(0, 0, 0, 0.55),
                0 0 34px color-mix(in srgb, var(--cat) 22%, transparent);
  }
}
/* ACTIVE still wears --accent. State is the accent's job and only the
   accent's job — that is what keeps the legend from becoming decoration. */
.hub-tile.active .nm { color: var(--accent); }

/* the rail arrives, it does not just appear */
.hub-panel.on .hub-tile {
  animation: hubTileIn 0.5s var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 0.035s);
}
@keyframes hubTileIn {
  from { opacity: 0; transform: translateY(10px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

/* ── CONNECT CARDS ─────────────────────────────────────────────────────── */
.connect-card .tag {
  color: var(--cat, var(--ink-tertiary));
  border-color: color-mix(in srgb, var(--cat) 40%, transparent);
}
.connect-card .act { color: var(--cat, var(--accent)); }
.connect-card::after {
  content: '';
  position: absolute; left: 0; top: 13px; bottom: 13px; width: 2px;
  border-radius: 0 2px 2px 0;
  background: var(--cat, transparent);
  opacity: 0;
  transition: opacity var(--dur-press) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .connect-card:not(.is-off):hover { box-shadow: 0 10px 26px rgba(0, 0, 0, 0.4); }
  .connect-card:not(.is-off):hover::after { opacity: 1; }
}
.connect-card.is-off::after { display: none; }
.hub-panel.on .connect-card {
  animation: hubTileIn 0.45s var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 0.03s);
}

/* ── THE AUDIO HUB ─────────────────────────────────────────────────────────
   The panel is showacse-audio.js's own markup, adopted into the sheet. Its
   structure was already the one Michael liked — kicker, cards with chips,
   numbered 01–05 effects, HRTF badge. Phase 1 had only drained it. This is
   colour and life put back on the existing nodes; no markup is duplicated. */

#sxr-modal .hub-adopt .sxa-head h3 {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 1.6rem;
  letter-spacing: -0.02em;
  margin: 2px 0 4px;
}
#sxr-modal .hub-adopt .sxa-kicker { color: var(--cat-tours); letter-spacing: 0.2em; }
#sxr-modal .hub-adopt .sxa-head p { color: var(--ink-secondary); margin: 0 0 var(--s-3); }

#sxr-modal .hub-adopt .sxa-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 8px; }
#sxr-modal .hub-adopt .sxa-card {
  position: relative;
  padding: 13px 15px;
  border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.035) !important;
  transition: background var(--dur-press) var(--ease-out);
}
#sxr-modal .hub-adopt .sxa-card:hover { background: rgba(255, 255, 255, 0.07) !important; }
#sxr-modal .hub-adopt .sxa-card[data-card="media"]  { --cat: var(--cat-videos); }
#sxr-modal .hub-adopt .sxa-card[data-card="narr"]   { --cat: var(--cat-splats); }
#sxr-modal .hub-adopt .sxa-card[data-card="mixer"]  { --cat: var(--cat-tours); }
#sxr-modal .hub-adopt .sxa-chip {
  display: inline-block; margin-bottom: 8px;
  font-size: 0.5625rem; font-weight: 500; letter-spacing: 0.18em;
  padding: 3px 7px; border-radius: var(--radius);
  color: var(--cat, var(--ink-tertiary));
  border: 1px solid color-mix(in srgb, var(--cat) 40%, transparent);
  background: color-mix(in srgb, var(--cat) 10%, transparent);
}
#sxr-modal .hub-adopt .sxa-card b { display: block; font-size: 0.9rem; }
#sxr-modal .hub-adopt .sxa-card i { display: block; font-style: normal; font-size: 0.75rem; color: var(--ink-tertiary); margin-top: 2px; }
#sxr-modal .hub-adopt .sxa-act {
  margin-top: 10px; cursor: pointer;
  background: transparent; border: 0; padding: 0;
  font-family: var(--font-screen); font-size: 0.625rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--cat, var(--accent));
}
#sxr-modal .hub-adopt .sxa-none { display: block; margin-top: 10px; font-size: 0.72rem; color: var(--ink-tertiary); opacity: 0.7; }

/* the five effects — numbered, and each one lights when it is actually on */
#sxr-modal .hub-adopt .sxa-fx { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 6px; margin-top: 10px; }
#sxr-modal .hub-adopt .sxa-fxb {
  display: flex; align-items: center; gap: 11px;
  padding: 11px 13px; cursor: pointer;
  border: 0; border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.035);
  color: var(--ink-primary); text-align: left;
  transition: background var(--dur-press) var(--ease-out);
}
#sxr-modal .hub-adopt .sxa-fxb:hover { background: rgba(255, 255, 255, 0.07); }
#sxr-modal .hub-adopt .sxa-n {
  font-family: var(--font-screen); font-size: 0.625rem; font-weight: 500;
  letter-spacing: 0.1em; color: var(--cat-splats); flex: 0 0 auto;
}
#sxr-modal .hub-adopt .sxa-fxl { flex: 1; min-width: 0; }
#sxr-modal .hub-adopt .sxa-fxl b { display: block; font-size: 0.82rem; font-weight: 600; }
#sxr-modal .hub-adopt .sxa-fxl i { display: block; font-style: normal; font-size: 0.7rem; color: var(--ink-tertiary); }
#sxr-modal .hub-adopt .sxa-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto;
  background: rgba(255, 255, 255, 0.14);
  transition: background var(--dur-press) var(--ease-out), box-shadow var(--dur-press) var(--ease-out);
}
/* .on is set by showcase-audio.js from the LIVE graph — this dot is a true
   readout of a real node, not a styling flourish */
#sxr-modal .hub-adopt .sxa-fxb.on .sxa-dot { background: var(--accent); box-shadow: 0 0 10px var(--accent-glow); }
#sxr-modal .hub-adopt .sxa-fxb.on { background: rgba(255, 255, 255, 0.07); }

#sxr-modal .hub-adopt .sxa-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  margin-top: var(--s-3); padding-top: var(--s-2);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  font-size: 0.7rem; color: var(--ink-tertiary);
}
#sxr-modal .hub-adopt .sxa-badge { color: var(--cat-tours); letter-spacing: 0.14em; font-size: 0.625rem; }

@media (prefers-reduced-motion: reduce) {
  .hub-panel.on .hub-tile, .hub-panel.on .connect-card,
  #sxr-sono-launch .orb::after { animation: none !important; }
}

/* ── two corrections found on the SONO XR HQ media, 2026-08-06 ───────────── */

/* 1. THE PANEL HAS TO HOLD ITS OWN AGAINST BRIGHT MEDIA.
   --glass alone is a set of near-transparent whites, which reads beautifully
   over a dark capture and washes out completely over a bright one — the HQ's
   daylight interiors turned the whole HUB into haze. A dark base UNDER the
   glass fixes it without touching the material: the lit top edge, the
   occlusion and the blur all still do their work, they now just have
   something to sit on. Backdrop blur stays, so it is still glass, not a slab. */
#sxr-hub,
#sxr-hub-peek,
#sxr-sono-launch {
  background:
    var(--glass),
    linear-gradient(180deg, rgba(12, 11, 10, 0.86) 0%, rgba(9, 8, 7, 0.92) 100%);
}
.guide-path {
  background:
    var(--glass),
    linear-gradient(180deg, rgba(14, 13, 12, 0.90) 0%, rgba(9, 8, 7, 0.94) 100%);
}

/* 2. THE AUDIO HUB'S CHIPS. sono-showcase.css retires every chip in the
   product with `!important` — "what they carried was a label". True for a
   decorative badge; false for OFF / SONO / 3DA, which are STATE readouts and
   the thing Michael singled out as working. Matching !important is the only
   way past it, and the override is scoped to the adopted panel so the rest of
   the product keeps the Phase 1 rule. */
#sxr-modal .hub-adopt .sxa-chip {
  display: inline-block !important;
  padding: 3px 7px !important;
  border-radius: var(--radius) !important;
  border: 1px solid color-mix(in srgb, var(--cat) 45%, transparent) !important;
  background: color-mix(in srgb, var(--cat) 12%, transparent) !important;
  color: var(--cat, var(--ink-secondary)) !important;
  font-size: 0.5625rem !important;
  font-weight: 500 !important;
  letter-spacing: 0.18em !important;
  text-transform: uppercase !important;
  min-width: 0 !important;
  height: auto !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   12 · ONE LOCKED-IN FOOTER, A BIGGER LOGO, AND ITS OWN MENU
   (Michael, 2026-08-06)

   "The footer menu is good but honestly I like the Codex version better as
   it's easy to see each option and to navigate… better to have one locked in
   menu/footer for all pages."

   Three changes carry that: the dock is THREE primary destinations at a size
   you cannot miss; the media menu is a fixed GRID rather than a scrolling
   rail, so it is the same shape everywhere; and the logo grows and becomes
   the door to property, location and contact.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── THE THREE ─────────────────────────────────────────────────────────── */
.hub-btn.hub-primary {
  gap: 12px;
  padding: 12px 20px 12px 12px;
  font-size: 0.9rem;
  font-weight: 600;
}
.hub-btn.hub-primary .ic {
  display: grid; place-items: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.10);
  color: var(--ink-secondary);
  transition: background var(--dur-press) var(--ease-out),
              box-shadow var(--dur-press) var(--ease-out),
              color var(--dur-press) var(--ease-out);
}
.hub-btn.hub-primary .ic svg { width: 19px; height: 19px; stroke-width: 1.6; }
.hub-btn.hub-primary.on .ic,
.hub-btn.hub-primary:hover .ic {
  background: var(--accent-dim);
  box-shadow: inset 0 0 0 1px var(--accent-glow), 0 0 22px var(--accent-dim);
  color: var(--accent);
}
.hub-btn.hub-primary.on { background: rgba(255, 255, 255, 0.06); }

/* ── THE MEDIA MENU IS A GRID, NOT A RAIL ──────────────────────────────────
   A horizontal scroller hides its own contents and changes shape with the
   viewport, which is exactly the "looks different on different screens"
   problem. A wrapping grid is the same menu at every width. */
.hub-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 10px;
  overflow: visible;
  padding: 2px;
}
.hub-tile { flex: none; }
@media (max-width: 1180px) { .hub-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .hub-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .hub-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* a category this showcase has nothing for is SHOWN and disabled, so the menu
   is a full, stable list — you can tell "not here" from "not loaded" */
.hub-tile.is-off { opacity: 0.34; cursor: default; }
.hub-tile.is-off .frame::after { opacity: 0.3; }
.hub-tile.is-off .cnt { color: var(--ink-tertiary); }

/* ── THE LOGO: BIGGER, AND A DOOR ──────────────────────────────────────── */
body.sxr-hub .sxr-brand-slot {
  gap: 11px;
  padding: 10px 14px 10px 12px !important;
  border-radius: var(--radius-panel);
  background: var(--glass), linear-gradient(180deg, rgba(12,11,10,0.72) 0%, rgba(9,8,7,0.80) 100%);
  box-shadow: var(--edge);
  backdrop-filter: var(--blur-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  transition: box-shadow var(--dur-press) var(--ease-out);
}
body.sxr-hub .sxr-brand-slot img { height: 46px; }
body.sxr-hub .sxr-brand-slot:hover { box-shadow: var(--edge), 0 0 0 1px var(--accent-dim); }
/* the affordance — a menu says so */
/* Michael, 2026-08-09: "I also don't think we need ABOUT in property/contact/
   logo section. Maybe just have downward pointing Arrow so clear there's drop
   down menu." The word was doing the chevron's job twice — and next to a
   client's logo, our label is the one piece of text that isn't their brand. */
body.sxr-hub .sxr-brand-slot::after {
  content: '⌄';
  font-family: var(--font-screen);
  font-size: 0.95rem; font-weight: 500;
  line-height: 1;
  color: var(--ink-tertiary);
  white-space: nowrap;
  margin-top: -5px;            /* the glyph sits low in its box */
  transition: color var(--dur-press) var(--ease-out);
}
body.sxr-hub .sxr-brand-slot:hover::after { color: var(--ink-secondary); }

/* ── PROPERTY / LOCATION / CONTACT ─────────────────────────────────────── */
/* the panel stacks two things: the property data, then the person */
.info-grid {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-bottom: 4px;
}
/* the data blocks keep the auto-fit grid — and now nothing spans it, so
   auto-fit can collapse the empty tracks and two blocks fill the row */
.info-data {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 8px;
}
.info-block {
  padding: 14px 16px;
  border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.035);
}
.info-block .ib-t {
  font-family: var(--font-screen);
  font-size: 0.5625rem; font-weight: 500;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 9px;
}
.info-block .ib-r {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 5px 0;
  font-size: 0.82rem;
}
.info-block .ib-r span { color: var(--ink-tertiary); }
.info-block .ib-r b { color: var(--ink-primary); font-weight: 600; text-align: right; }
.info-block .ib-link {
  display: block; padding: 5px 0;
  font-size: 0.82rem; color: var(--ink-secondary);
  text-decoration: none;
  transition: color var(--dur-press) var(--ease-out);
}
.info-block .ib-link:hover { color: var(--accent); }

/* ── AUDIO HUB, TOP RIGHT ──────────────────────────────────────────────── */
#sxr-audio-launch {
  position: fixed;
  top: 26px; right: 28px;
  z-index: 46;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px 10px 10px;
  border: 0;
  border-radius: var(--radius-panel);
  background: var(--glass), linear-gradient(180deg, rgba(12,11,10,0.72) 0%, rgba(9,8,7,0.80) 100%);
  box-shadow: var(--edge);
  backdrop-filter: var(--blur-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  color: var(--ink-primary);
  cursor: pointer;
  text-align: left;
  transition: box-shadow var(--dur-press) var(--ease-out);
}
#sxr-audio-launch:hover { box-shadow: var(--edge), 0 0 0 1px var(--accent-dim); }
#sxr-audio-launch .st {
  display: grid; place-items: center;
  min-width: 40px; height: 34px;
  border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.10);
  font-family: var(--font-screen);
  font-size: 0.5625rem; font-weight: 500; letter-spacing: 0.16em;
  color: var(--ink-tertiary);
}
/* the chip is a READOUT — it says ON only when audio is actually playing */
#sxr-audio-launch .st.is-on {
  background: var(--accent-dim);
  box-shadow: inset 0 0 0 1px var(--accent-glow);
  color: var(--accent);
}
#sxr-audio-launch .tx b { display: block; font-size: 0.86rem; font-weight: 600; }
#sxr-audio-launch .tx i {
  display: block; font-style: normal;
  font-size: 0.7rem; color: var(--ink-tertiary);
}
#sxr-audio-launch svg { width: 15px; height: 15px; color: var(--ink-tertiary); }
body.sxr-guide-open #sxr-audio-launch,
body.sxr-presenting #sxr-audio-launch { opacity: 0; pointer-events: none; }
body.sxr-hub.sono-idle #sxr-audio-launch { opacity: 1; }

@media (max-width: 900px) {
  #sxr-audio-launch { top: 14px; right: 12px; padding: 8px 10px 8px 8px; }
  #sxr-audio-launch .tx { display: none; }
  body.sxr-hub .sxr-brand-slot { padding: 7px 10px !important; gap: 9px; }
  body.sxr-hub .sxr-brand-slot img { height: 28px; }
  body.sxr-hub .sxr-brand-slot::after { display: none; }
  .hub-btn.hub-primary { padding: 8px; }
  .hub-btn.hub-primary .ic { width: 34px; height: 34px; }
}

/* THE SHEET IS CAPPED. A full 11-category grid is three rows, which swallowed
   three quarters of the viewport — and the governing test is whether the SPACE
   stays present. The menu scrolls inside itself instead of growing without
   limit, so the showcase behind it is never walled off. */
.sxr-dead-hub-pad {
  max-height: min(46vh, 420px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
}
.sxr-dead-hub-pad::-webkit-scrollbar { width: 8px; }
.sxr-dead-hub-pad::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.16); border-radius: 99px;
}
/* the head stays put while the grid moves under it */
#sxr-hub .hub-head {
  position: sticky; top: 0; z-index: 2;
  background: linear-gradient(180deg, rgba(10,9,8,0.96) 62%, rgba(10,9,8,0) 100%);
  margin: 0 -2px;
  padding: 12px 2px 14px;
}
/* tighter tiles so a full list reads as a menu, not a wall of posters */
.hub-tile .frame { aspect-ratio: 16 / 9.5; }
.hub-tile .meta { padding: 7px 2px 0; }

/* showcase.js writes the client logo with an inline style="height:28px", and
   an inline style beats any selector. That inline height is why the mark read
   as small and hard to see no matter what this file asked for. !important is
   the only lever over an inline declaration. */
body.sxr-hub .sxr-brand-slot img { height: 46px !important; }
@media (max-width: 900px) { body.sxr-hub .sxr-brand-slot img { height: 30px !important; } }

/* ═══════════════════════════════════════════════════════════════════════════
   13 · THE CODEX LAYOUT  (Michael, 2026-08-06)

   "I just like the codex layout/design better. It's streamlined, everything
   works great, it's visually on brand, and I like how it looks when you click
   each menu header/footer."

   Two structural moves carry the whole thing:

   1. THE DOCK IS THREE LIT ORBS. Not text buttons — a coloured orb with its
      label beneath, in a rounded bar. It reads at a glance and never changes
      shape, which is what "one locked-in footer" actually requires.
   2. MENUS ARE CENTRED CARDS, not a sheet hanging off the dock. A menu gets
      the middle of the screen and the room dims behind it. Every menu opens
      the same way, in the same place, at the same size.

   The orb colours are the category palette doing its legend job again:
   Overview teal, Content rose, Connect green. They identify a destination,
   not a state — the ACTIVE one is marked by its lit plate, not by hue.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── THE DOCK ──────────────────────────────────────────────────────────── */
#sxr-hub {
  width: auto;
  border-radius: 999px;
  padding: 0;
}
#sxr-hub .hub-rail { gap: 4px; padding: 10px 12px; }
#sxr-hub .hub-rail .grow { display: none; }

.hub-btn.hub-primary {
  flex-direction: column;
  gap: 7px;
  padding: 10px 22px 8px;
  min-width: 112px;
  border-radius: 18px;
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
}
.hub-btn.hub-primary .ic {
  width: 46px; height: 46px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 38%,
    color-mix(in srgb, var(--orb) 55%, transparent) 0%,
    color-mix(in srgb, var(--orb) 22%, transparent) 55%,
    transparent 78%);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--orb) 45%, transparent),
              0 0 18px color-mix(in srgb, var(--orb) 22%, transparent);
  color: color-mix(in srgb, var(--orb) 88%, white);
}
.hub-btn.hub-primary .ic svg { width: 20px; height: 20px; stroke-width: 1.7; }

/* one hue per destination — the legend, not a state */
.hub-btn.hub-primary:nth-child(1) { --orb: var(--cat-tours); }
.hub-btn.hub-primary:nth-child(2) { --orb: var(--cat-photos); }
.hub-btn.hub-primary:nth-child(3) { --orb: var(--cat-area); }

/* ACTIVE is the lit plate behind the orb, so state stays legible to anyone
   who cannot separate the three hues */
.hub-btn.hub-primary.on {
  background: rgba(255, 255, 255, 0.07);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--orb) 32%, transparent);
  color: var(--ink-primary);
}
.hub-btn.hub-primary.on .ic {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--orb) 75%, transparent),
              0 0 26px color-mix(in srgb, var(--orb) 34%, transparent);
}
@media (hover: hover) and (pointer: fine) {
  .hub-btn.hub-primary:hover { color: var(--ink-primary); background: rgba(255,255,255,0.05); }
}

/* ══ NO PLATE BEHIND THE ORB (Michael, 2026-08-12) ══════════════════════════
   "the icons have a box inside the border that's bigger than doc itself. Can
   you remove or make it a circle slightly larger than icon."

   He is describing .hub-btn: a 70×79 rounded rectangle at 14px radius wrapping
   a 46px circle wrapping a 20px icon. When active it also drew a 1px ring
   around that whole rectangle, so the "box" read as a second, larger frame
   inside the dock's own border.

   ⚠️ THE PLATE HAD A REASON, AND THE REASON EXPIRED. Its own comment above says
   "ACTIVE is the lit plate behind the orb, so state stays legible to anyone who
   cannot separate the three hues" — written when each orb carried a different
   --cat-* hue and telling them apart was the problem. HUB.md records the dock
   leaving that legend on 2026-08-10: the orbs are brushed chrome now and the
   ACTIVE one is the only coloured thing in the dock. The orb already carries
   state on its own, so the plate is a leftover solving a problem that is gone.

   The circle keeps everything: .ic is 46px around a 20px icon, with the bronze
   ring and glow on .on/:hover. That IS "a circle slightly larger than the icon".

   ⚠️ SCOPED TO .hub-primary ON PURPOSE. Plain .hub-btn is also used for list
   rows inside the modals, where a full-width plate is the correct affordance —
   nulling it globally would flatten those too.

   ⚠️ AND PLACED HERE, NOT APPENDED. Five rules paint this background across the
   file (lines ~368, ~370, ~1286, and the two just above). At equal specificity
   the last one wins, so this sits immediately after the last of them. HUB.md
   logs "Home's active ring" as one of three times appending to the end of this
   file failed to win — matching the selector and the position is the fix. */
.hub-btn.hub-primary,
.hub-btn.hub-primary.on {
  background: transparent;
  box-shadow: none;
}
@media (hover: hover) and (pointer: fine) {
  .hub-btn.hub-primary:hover { background: transparent; }
}

/* ── THE MODAL ─────────────────────────────────────────────────────────── */
#sxr-modal {
  position: fixed; inset: 0;
  z-index: 48;
  display: grid; place-items: center;
  padding: 4vh 3vw 128px;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-chrome) var(--ease-out);
}
#sxr-modal.open { opacity: 1; pointer-events: auto; }

.modal-scrim {
  position: absolute; inset: 0;
  background: rgba(6, 5, 4, 0.55);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.modal-card {
  position: relative;
  width: min(1240px, 100%);
  max-height: 100%;
  display: flex; flex-direction: column;
  border-radius: 20px;
  background: var(--glass),
              linear-gradient(180deg, rgba(16, 15, 14, 0.94) 0%, rgba(10, 9, 8, 0.97) 100%);
  box-shadow: var(--edge), var(--lift), 0 0 0 1px rgba(255, 255, 255, 0.06);
  backdrop-filter: var(--blur-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  transform: translateY(14px) scale(0.985);
  transition: transform var(--dur-sheet) var(--ease-out);
}
#sxr-modal.open .modal-card { transform: none; }

.modal-x {
  position: absolute; top: 18px; right: 18px;
  z-index: 3;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border: 0; border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.10);
  color: var(--ink-secondary);
  font-size: 0.85rem;
  cursor: pointer;
  transition: background var(--dur-press) var(--ease-out), color var(--dur-press) var(--ease-out);
}
.modal-x:hover { background: rgba(255, 255, 255, 0.12); color: var(--ink-primary); }

.modal-body {
  padding: 30px 34px 26px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
}
.modal-body::-webkit-scrollbar { width: 9px; }
.modal-body::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.16); border-radius: 99px; }

/* ── THE MENU HEADER — the part Michael called out by name ─────────────── */
.hub-head {
  display: block;
  position: static;
  background: none;
  margin: 0 0 22px;
  padding: 0 44px 20px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.hub-head .t:first-child {
  display: block;
  font-size: 0.625rem;
  letter-spacing: 0.22em;
  color: var(--cat-tours);
  margin-bottom: 10px;
}
/* the second .t is the space's name — a quiet right-hand credit, not a title */
.hub-head .t:last-child:not(:first-child) {
  display: block;
  font-size: 0.8125rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-tertiary);
  margin-top: 6px;
}
/* the big line, drawn from the kicker's sibling text via a data attribute */
.hub-head[data-title]::after {
  content: attr(data-title);
  display: block;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(1.5rem, 2.4vw, 2.1rem);
  letter-spacing: -0.02em;
  line-height: 1.05;
  color: var(--ink-primary);
}

/* the grid gets room to breathe in a card this size */
.hub-grid { gap: 14px; }
.connect-grid, .info-grid { gap: 12px; }
.connect-card { min-height: 132px; padding: 16px 18px 15px; }

/* the sheet-era rules that no longer apply */
.sxr-dead-hub-pad, .sxr-dead-hub-sheet, .sxr-dead-hub-clip { all: unset; }
#sxr-hub-peek { display: none; }

@media (max-width: 900px) {
  #sxr-modal { padding: 2vh 2vw 116px; }
  .modal-body { padding: 22px 18px 20px; }
  .modal-card { border-radius: 16px; }
  .hub-btn.hub-primary { min-width: 0; padding: 8px 12px 6px; }
  .hub-btn.hub-primary .ic { width: 38px; height: 38px; }
  .hub-btn.hub-primary > span:not(.ic) { display: block; font-size: 0.6rem; }
}

/* header order: kicker → HEADLINE → sub. The ::after title is appended last
   in the DOM, so without an explicit order the count line jumped above the
   headline it was supposed to qualify. */
.hub-head {
  display: flex;
  flex-direction: column;
}
.hub-head .t:first-child { order: 1; }
.hub-head[data-title]::after { order: 2; margin-top: 2px; }
.hub-head .t:last-child:not(:first-child) { order: 3; margin-top: 10px; }

/* THE DOCK STAYS LIT AND LIVE ABOVE THE SCRIM. It is the one thing that must
   never dim: you switch menus by pressing another orb, and a dock behind the
   overlay would mean closing one menu to open the next. */
#sxr-hub { z-index: 49; }
#sxr-sono-launch { z-index: 49; }

/* ═══════════════════════════════════════════════════════════════════════════
   14 · THE HOME SCREEN  (Michael, 2026-08-06)

   "This is how I want our home page to look yet our version makes me cycle
   through 3 different sections to land on overview and it's just dull and
   not engaging."

   The fault was structural — three screens doing one job — and section 14 of
   showcase-template.html merges them. This is the look: one composition, all
   of it above the fold, over the live space.

   TYPE IS THE OTHER HALF OF WHY IT READ AS DULL. The headline was set in the
   display SERIF, which is quiet and editorial. The reference is a wide, tight
   grotesque at a size that owns the screen. The serif keeps its place on menu
   headlines, where a quiet voice is right; the home screen is not that.
   ═════════════════════════════════════════════════════════════════════════ */

#sxr-guide .guide-in {
  width: min(1180px, 92vw);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* THE HEADLINE — the thing that makes it feel like an experience */
#sxr-guide .guide-title {
  font-family: var(--font-screen);
  font-size: clamp(2.6rem, 8.2vw, 7rem);
  font-weight: 700;
  letter-spacing: -0.045em;
  line-height: 0.92;
  margin: 0 0 10px;
  color: #fff;
  text-shadow: 0 2px 40px rgba(0, 0, 0, 0.55), 0 1px 6px rgba(0, 0, 0, 0.4);
  animation: hubRise 0.9s var(--ease-out) 0.06s both;
}
#sxr-guide .guide-sub {
  font-family: var(--font-screen);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  max-width: none;
  margin: 0 0 14px;
}
#sxr-guide .guide-lede {
  font-size: clamp(0.95rem, 1.5vw, 1.18rem);
  line-height: 1.5;
  font-weight: 400;
  color: var(--ink-primary);
  max-width: 62ch;
  margin: 0 0 6px;
  text-shadow: 0 1px 20px rgba(0, 0, 0, 0.5);
  animation: hubRise 0.9s var(--ease-out) 0.14s both;
}
#sxr-guide .guide-lede[hidden] { display: none; }

/* SONO — centred, large, and the thing your eye lands on after the headline */
.guide-sono {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  margin: 30px 0 4px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  color: inherit;
  animation: hubRise 0.9s var(--ease-out) 0.2s both;
}
.sono-halo.lg {
  width: 168px; height: 168px;
  margin: 0 0 -26px;
  z-index: 1;
}
.sono-halo.lg .bloom { inset: -54px; }
.sono-halo.lg .mark { inset: 30px; width: calc(100% - 60px); height: calc(100% - 60px); }
/* the orb reads as an object: a lit disc, not an outline on nothing */
.sono-halo.lg::before {
  content: '';
  position: absolute; inset: 0; border-radius: 50%;
  background: radial-gradient(circle at 50% 34%,
    rgba(28, 26, 24, 0.94) 0%, rgba(12, 11, 10, 0.97) 62%, rgba(9, 8, 7, 0.99) 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.14), 0 18px 60px rgba(0,0,0,0.55);
}
.guide-sono .sono-cap {
  position: relative; z-index: 2;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 8px 20px 9px;
  border-radius: 999px;
  background: var(--glass), linear-gradient(180deg, rgba(14,13,12,0.92), rgba(9,8,7,0.96));
  box-shadow: var(--edge), 0 10px 30px rgba(0,0,0,0.5);
  backdrop-filter: var(--blur-panel);
  -webkit-backdrop-filter: var(--blur-panel);
}
.guide-sono .who {
  font-family: var(--font-screen);
  font-size: 0.5625rem; font-weight: 500;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--accent);
}
.guide-sono .cta { font-size: 0.95rem; font-weight: 600; letter-spacing: -0.01em; color: var(--ink-primary); }
@media (hover: hover) and (pointer: fine) {
  .guide-sono:hover .sono-cap { box-shadow: var(--edge), 0 0 0 1px var(--accent-dim), 0 12px 34px rgba(0,0,0,0.55); }
}

.guide-ask {
  font-family: var(--font-screen);
  font-size: 0.5625rem; font-weight: 500;
  letter-spacing: 0.26em; text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 26px 0 14px;
  animation: hubRise 0.8s var(--ease-out) 0.3s both;
}

/* THE PATHS — a ROW, all visible at once. They were a stack, which pushed the
   last option under the fold and made the choice feel like a list to read
   rather than a set to pick from. */
#sxr-guide .guide-paths {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  grid-auto-flow: column;
  gap: 12px;
  width: 100%;
  max-width: none;
}
#sxr-guide .guide-path {
  grid-template-columns: auto 1fr auto;
  padding: 16px 18px;
  border-radius: 16px;
  /* each path wears its own hue, the legend again */
  background: var(--glass),
    linear-gradient(150deg,
      color-mix(in srgb, var(--pc) 16%, rgba(14,13,12,0.92)) 0%,
      rgba(10, 9, 8, 0.94) 62%);
  box-shadow: var(--edge), var(--lift), inset 0 0 0 1px color-mix(in srgb, var(--pc) 26%, transparent);
}
#sxr-guide .guide-path .chip {
  background: color-mix(in srgb, var(--pc) 20%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--pc) 55%, transparent);
  color: color-mix(in srgb, var(--pc) 90%, white);
}
#sxr-guide .guide-path .n {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--ink-primary);
}
#sxr-guide .guide-path h3 {
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--ink-tertiary);
  margin: 3px 0 0;
  text-transform: none;
}
#sxr-guide .guide-path p { display: none; }
#sxr-guide .guide-path .go { color: color-mix(in srgb, var(--pc) 85%, white); }
@media (hover: hover) and (pointer: fine) {
  #sxr-guide .guide-path:hover {
    box-shadow: var(--edge), var(--lift),
                inset 0 0 0 1px color-mix(in srgb, var(--pc) 60%, transparent),
                0 0 40px color-mix(in srgb, var(--pc) 20%, transparent);
  }
}
[data-go="explore"] { --pc: var(--cat-photos); }
[data-go="play"]    { --pc: var(--cat-tours); }
[data-go="avatar"]  { --pc: var(--cat-splats); }
[data-go="live"]    { --pc: var(--cat-area); }

/* the compact set inside the Sono menu keeps the stacked form */
.guide-paths.compact { grid-auto-flow: row; grid-template-columns: 1fr; }

/* HOME IS A PLACE YOU CAN RETURN TO. The greeting used to be dismissed for
   good; the Overview orb brings it back, so it is a destination like any
   other rather than a door that closes behind you. */
body.sxr-hub.sxr-guide-open .hub-btn.hub-primary:nth-child(1) {
  background: rgba(255, 255, 255, 0.07);
  color: var(--ink-primary);
}
body.sxr-hub.sxr-guide-open .hub-btn.hub-primary:nth-child(1) .ic {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--orb) 75%, transparent),
              0 0 26px color-mix(in srgb, var(--orb) 34%, transparent);
}

@media (max-width: 1000px) {
  #sxr-guide .guide-paths { grid-auto-flow: row; grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  #sxr-guide .guide-paths { grid-template-columns: 1fr; }
  .sono-halo.lg { width: 120px; height: 120px; margin-bottom: -22px; }
  .guide-ask { margin: 18px 0 10px; }
}

/* THE DOCK STAYS ON THE HOME SCREEN. It used to hide behind the greeting,
   which is what made home feel like a door rather than a destination — you
   could not see where else to go from the one screen everyone starts on.
   Home is Overview now, so the Overview orb is simply lit. */
body.sxr-guide-open #sxr-hub { opacity: 1; transform: translateX(-50%); pointer-events: auto; }
/* the rail launcher would be a second Sono on a screen that already has a
   large one — one guide, one door */
body.sxr-guide-open #sxr-sono-launch { opacity: 0; pointer-events: none; }
body.sxr-guide-open #sxr-powered { opacity: 1; }


/* ═══ LAYER ORDER, stated once so it stops drifting ════════════════════════
   The modal used to sit BELOW the home screen, so opening Property · Contact
   from home rendered it behind the hero and nothing appeared to happen. The
   corner launchers and the dock must also outrank the modal, because you
   switch menus from them while one is open.

     41  room scrim          50  home screen (the guide)
     45  —                   52  the modal
     47  Sono launcher       54  dock, logo, Audio Hub launcher
   ══════════════════════════════════════════════════════════════════════ */
#sxr-modal { z-index: 52; }
#sxr-hub, #sxr-sono-launch, #sxr-audio-launch { z-index: 54; }
body.sxr-hub .sxr-brand-slot { z-index: 54; }

/* the adopted Audio Hub is a child of the modal card now — pin the reset to
   the card so nothing it brings with it can escape again */
#sxr-modal .hub-adopt { max-height: none; overflow: visible; }
#sxr-modal .hub-adopt > * {
  position: static !important;
  inset: auto !important;
  transform: none !important;
  width: auto !important;
  height: auto !important;
  max-width: none !important;
  max-height: none !important;
}

/* THE ADOPTED PANEL BRINGS ITS OWN TITLE. The modal head already says
   "SONO XR · AUDIO HUB / Immersive 3D Audio", so the panel's copy of the same
   two lines printed straight underneath it. Its standfirst stays — that line
   is the only one of the three the modal head does not already carry. */
#sxr-modal .hub-adopt .sxa-kicker,
#sxr-modal .hub-adopt .sxa-head h3 { display: none !important; }
#sxr-modal .hub-adopt .sxa-head { margin-bottom: var(--s-3); }
#sxr-modal .hub-adopt .sxa-head p { margin-top: 0; }

/* THE AUDIO HUB STOPS FLICKERING. Two rules were fighting over it: one hid it
   on the home screen, another showed it whenever the body went `sono-idle`.
   The idle class toggles with the mouse, so the launcher blinked in and out
   as you moved. It belongs on the home screen anyway — the reference has it
   there — so the home-screen hide goes and nothing toggles it any more. */
body.sxr-guide-open #sxr-audio-launch { opacity: 1; pointer-events: auto; }
body.sxr-hub.sono-idle #sxr-audio-launch,
body.sxr-hub.sxr-guide-open.sono-idle #sxr-audio-launch { opacity: 1; }
/* it still stands down for the presenter, which owns the whole frame */
body.sxr-presenting #sxr-audio-launch { opacity: 0 !important; pointer-events: none; }

/* ═══════════════════════════════════════════════════════════════════════════
   15 · THE FOOTER ROW AND CINEMA MODE  (2026-08-06)
   ═════════════════════════════════════════════════════════════════════════ */

.hub-foot {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--s-3);
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.hf-note {
  font-family: var(--font-screen);
  font-size: 0.625rem; font-weight: 500;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ink-tertiary);
}
.hf-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.hf-chip {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 14px 8px 8px;
  border: 0; border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.035);
  color: var(--ink-secondary);
  font-family: var(--font-screen);
  font-size: 0.8rem; font-weight: 500;
  cursor: pointer;
  transition: background var(--dur-press) var(--ease-out), color var(--dur-press) var(--ease-out);
}
.hf-chip:hover { background: rgba(255, 255, 255, 0.08); color: var(--ink-primary); }
/* the state block IS the readout — it prints the live value, never a label */
.hf-chip .st {
  min-width: 42px;
  padding: 4px 7px;
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.10);
  font-size: 0.5625rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-tertiary);
  text-align: center;
}
.hf-chip.is-on { color: var(--ink-primary); }
.hf-chip.is-on .st {
  background: var(--accent-dim);
  box-shadow: inset 0 0 0 1px var(--accent-glow);
  color: var(--accent);
}

/* ── CINEMA MODE ───────────────────────────────────────────────────────────
   The engine's rule (showcase-stage.css) points at .showcase-hud-header,
   .sxr-brand-slot and .sxr-lead-pill — a header that no longer exists, a pill
   that is already hidden, and a logo js/showcase-hub.js re-homes to <body>.
   So it hid almost nothing. This is the same feature, aimed at the chrome
   that is actually on screen. */
body.sxr-hub.sxr-cinema #sxr-hub,
body.sxr-hub.sxr-cinema #sxr-audio-launch,
body.sxr-hub.sxr-cinema #sxr-sono-launch,
body.sxr-hub.sxr-cinema #sxr-powered,
body.sxr-hub.sxr-cinema .sono-slate,
body.sxr-hub.sxr-cinema .sxr-brand-slot {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-chrome) var(--ease-out);
}

/* THE WAY BACK. Hiding every control with no route out is a trap. The dock
   returns when the pointer enters the bottom strip, the way a video player
   behaves — and Escape leaves cinema outright. */
body.sxr-hub.sxr-cinema.sxr-cinema-peek #sxr-hub {
  opacity: 1;
  pointer-events: auto;
}
/* on touch there is no pointer to move into a strip, so cinema keeps the dock */
@media (hover: none) {
  body.sxr-hub.sxr-cinema #sxr-hub { opacity: 1; pointer-events: auto; }
}

@media (max-width: 900px) {
  .hub-foot { flex-direction: column; align-items: flex-start; }
  .hf-chip { padding: 7px 11px 7px 7px; font-size: 0.75rem; }
  .hf-chip .st { min-width: 36px; }
}

/* CINEMA IS A MODE THE VISITOR CHOSE, so it outranks every ambient
   visibility rule — the idle fade, the home-screen exceptions, all of it.
   Without this the Audio Hub launcher survived cinema, because
   `body.sxr-hub.sxr-guide-open.sono-idle` is a longer selector than the
   cinema rule and simply won. !important is the right tool for a mode.
   The home screen goes too: cinema means the space and nothing else. */
body.sxr-hub.sxr-cinema #sxr-hub,
body.sxr-hub.sxr-cinema #sxr-audio-launch,
body.sxr-hub.sxr-cinema #sxr-sono-launch,
body.sxr-hub.sxr-cinema #sxr-powered,
body.sxr-hub.sxr-cinema .sono-slate,
body.sxr-hub.sxr-cinema .sxr-brand-slot,
body.sxr-hub.sxr-cinema #sxr-guide {
  opacity: 0 !important;
  pointer-events: none !important;
}
body.sxr-hub.sxr-cinema.sxr-cinema-peek #sxr-hub {
  opacity: 1 !important;
  pointer-events: auto !important;
}
@media (hover: none) {
  body.sxr-hub.sxr-cinema #sxr-hub { opacity: 1 !important; pointer-events: auto !important; }
}

/* THE CARD WAS OVERFLOWING ITS OWN BOX. #sxr-modal reserves 128px at the
   bottom for the dock, but a grid item's default `min-height: auto` lets its
   content push it past `max-height: 100%` — so a tall media grid grew the card
   straight down through the dock, and the footer row ended up underneath it.
   min-height:0 lets the cap bind; the body then scrolls, which is what it is
   for. The reserve is sized off the dock (105px + its 22px offset + air). */
#sxr-modal { padding-bottom: 152px; }
.modal-card { min-height: 0; }
.modal-body { min-height: 0; flex: 1 1 auto; }
@media (max-width: 900px) { #sxr-modal { padding-bottom: 132px; } }

/* THE FOOTER ROW IS PINNED. It lives inside the scrolling body, so with a
   full media grid above it it simply scrolled out of reach — the controls
   were there and unclickable, which is the same failure as the logo. Sticky
   to the bottom of the scroll box: always in reach, never a floating bar. */
.hub-foot {
  position: sticky;
  bottom: -26px;               /* cancels .modal-body's bottom padding */
  z-index: 2;
  margin-bottom: -26px;
  padding-bottom: 26px;
  background: linear-gradient(0deg,
    rgba(11, 10, 9, 0.98) 62%, rgba(11, 10, 9, 0.92) 82%, rgba(11, 10, 9, 0) 100%);
}

/* ═══ 16 · MASTER VOLUME AND SOUNDSTAGE PROFILES ══════════════════════════
   Both are real: the slider drives graph.master, a GainNode at the end of the
   chain, and each profile is a combination of the five effects that already
   exist. Nothing here plays audio the showcase does not carry. ══════════ */

#sxr-modal .hub-adopt .sxa-vol {
  display: flex; align-items: center; gap: 14px;
  margin-top: 10px;
  padding: 12px 15px;
  border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.035);
  --cat: var(--cat-tours);
}
#sxr-modal .hub-adopt .sxa-vl { flex: 0 0 auto; }
#sxr-modal .hub-adopt .sxa-vl b { display: block; font-size: 0.86rem; font-weight: 600; }
#sxr-modal .hub-adopt .sxa-vl i {
  display: block; font-style: normal;
  font-size: 0.72rem; color: var(--ink-tertiary); margin-top: 1px;
}
#sxr-modal .hub-adopt .sxa-vol.is-off { opacity: 0.45; }

/* the slider is styled, not replaced — a real <input type=range> keeps the
   keyboard and the screen-reader behaviour the browser already gives it */
#sxr-modal .hub-adopt .sxa-slider {
  flex: 1 1 auto;
  -webkit-appearance: none; appearance: none;
  height: 5px; border-radius: 99px;
  background: linear-gradient(90deg,
    var(--cat-tours) 0%, var(--cat-tours) var(--fill, 80%),
    rgba(255,255,255,0.14) var(--fill, 80%), rgba(255,255,255,0.14) 100%);
  cursor: pointer;
}
#sxr-modal .hub-adopt .sxa-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 17px; height: 17px; border-radius: 50%;
  background: var(--ink-primary);
  box-shadow: 0 2px 8px rgba(0,0,0,0.55), 0 0 0 1px rgba(0,0,0,0.35);
  cursor: grab;
}
#sxr-modal .hub-adopt .sxa-slider::-moz-range-thumb {
  width: 17px; height: 17px; border: 0; border-radius: 50%;
  background: var(--ink-primary);
  box-shadow: 0 2px 8px rgba(0,0,0,0.55);
}
#sxr-modal .hub-adopt .sxa-vpct {
  flex: 0 0 auto; min-width: 46px; text-align: right;
  font-family: var(--font-screen);
  font-size: 0.82rem; font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--cat-tours);
}

#sxr-modal .hub-adopt .sxa-prof-l {
  font-family: var(--font-screen);
  font-size: 0.5625rem; font-weight: 500;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: var(--s-3) 0 8px;
}
#sxr-modal .hub-adopt .sxa-profs {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 6px;
}
#sxr-modal .hub-adopt .sxa-prof {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 14px;
  border: 0; border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.035);
  color: var(--ink-primary);
  text-align: left; cursor: pointer;
  --cat: var(--cat-splats);
  transition: background var(--dur-press) var(--ease-out),
              box-shadow var(--dur-press) var(--ease-out);
}
#sxr-modal .hub-adopt .sxa-prof:hover { background: rgba(255, 255, 255, 0.07); }
#sxr-modal .hub-adopt .sxa-pl { flex: 1; min-width: 0; }
#sxr-modal .hub-adopt .sxa-pl b { display: block; font-size: 0.84rem; font-weight: 600; }
#sxr-modal .hub-adopt .sxa-pl i {
  display: block; font-style: normal;
  font-size: 0.71rem; line-height: 1.35; color: var(--ink-tertiary); margin-top: 1px;
}
/* the tick is a READOUT — .on is set from currentProfile(), which reads the
   graph back, so pressing a single effect afterwards honestly clears it */
#sxr-modal .hub-adopt .sxa-pk {
  flex: 0 0 auto; opacity: 0;
  color: var(--accent); font-size: 0.9rem;
  transition: opacity var(--dur-press) var(--ease-out);
}
#sxr-modal .hub-adopt .sxa-prof.on {
  background: rgba(255, 255, 255, 0.07);
  box-shadow: inset 0 0 0 1px var(--accent-dim);
}
#sxr-modal .hub-adopt .sxa-prof.on .sxa-pk { opacity: 1; }

@media (max-width: 900px) {
  #sxr-modal .hub-adopt .sxa-vol { flex-wrap: wrap; }
  #sxr-modal .hub-adopt .sxa-slider { order: 3; flex-basis: 100%; }
}

/* The card must clear the CORNER LAUNCHERS, not just the dock. The logo and
   the Audio Hub sit at y≈26–85, and the modal's own close button lands in the
   same band — so the X was underneath the Audio Hub panel and unclickable. */
#sxr-modal { padding-top: 106px; }
@media (max-width: 900px) { #sxr-modal { padding-top: 74px; } }

/* ═══ 17 · THE GALLERY STRIP IS NAVIGATION, NOT CHROME  (2026-08-07) ═══════
   Michael: "there's no way to browse photos/videos in gallery to pick which
   to view." Two separate causes, both real:

   1. sono-showcase.css fades .sxr-gal-strip out with the rest of the chrome
      on `sono-idle` — which toggles the moment the pointer stops. So the ONLY
      control for stepping through a gallery vanished while he was looking at
      the photo it belongs to. The idle fade is right for controls you are not
      using; it is wrong for the thing that navigates the content.
   2. The strip sat at bottom:46px, running the full width — directly beneath
      the dock, which is centred at bottom:22px and ~105px tall. Even visible,
      its middle third was covered.

   Both fixed here: exempt from the fade, and lifted clear of the dock. ══ */
body.sxr-hub .sxr-gal-strip,
body.sxr-hub .sxr-gal-title,
body.sxr-hub .sxr-scenes {
  bottom: 140px !important;
}
body.sxr-hub .sxr-gal-title { bottom: 232px !important; }
/* ⚠️ .sxr-scenes is TOP-anchored in showcase-stage.css (top:12px). Adding a
   bottom without releasing the top pins BOTH edges, and the pill row stretches
   into a full-height slab down the middle of the panorama. Release it. */
body.sxr-hub .sxr-scenes { top: auto !important; bottom: 156px !important; }
body.sxr-hub.sono-idle .sxr-gal-strip,
body.sxr-hub.sono-idle .sxr-gal-title,
body.sxr-hub.sono-idle .sxr-scenes {
  opacity: 1;
  pointer-events: auto;
}
/* cinema mode still clears it — that is an explicit request for just the space */
body.sxr-hub.sxr-cinema .sxr-gal-strip,
body.sxr-hub.sxr-cinema .sxr-gal-title,
body.sxr-hub.sxr-cinema .sxr-scenes {
  opacity: 0 !important;
  pointer-events: none !important;
}

/* ── 18. 360 — the sphere, and the honest fallback ──────────────────────
   renderPano measures the image: 2:1 is an equirectangular panorama and gets
   a real three.js inverted sphere; anything else is a flat photo and keeps
   the pan, labelled "drag to pan" so it claims nothing it cannot do.
   A scene list that mixes the two flips between them, so the canvas has to
   get out of the way rather than sit on top showing the last 360.
   !important because the renderer sets display:block INLINE on its own canvas
   (three.js canvases are inline elements otherwise and leave a baseline gap),
   and an inline style outranks any class rule. ── */
.sxr-pano-stage.is-flat > canvas { display: none !important; }

/* ── 19. THIRD-PARTY EMBEDS KEEP THEIR OWN CONTROLS ────────────────────
   Michael, 2026-08-07: "the Matterport 3D tour menu is cut-off (at bottom
   of our dock). That's most important part of their or Treedis tours."

   Full bleed is right for a photo. It is wrong for an embed that carries
   its OWN bottom-anchored chrome — Matterport's floorplan / dollhouse /
   measure bar, Treedis's controls, Google's map buttons. Those sit at the
   bottom of THEIR viewport, which is exactly where our dock floats, and we
   cannot restyle inside someone else's iframe. So the iframe stops short
   and their bar rides above our dock instead of underneath it.

   :has() rather than a mode list, so an embed added later is covered the
   day it lands. The band it leaves is the stage's own dark — it reads as
   letterboxing, not as a gap.

   Cinema mode takes the dock away, so it gives the embed the whole viewport
   back. That is now the way to see a tour truly full bleed. ── */
body.sxr-hub .sxr-frame:has(iframe) { bottom: 140px; }
body.sxr-hub .sxr-frame:has(iframe) .sxr-frame-label { bottom: 14px; }
body.sxr-hub.sxr-cinema .sxr-frame:has(iframe) { bottom: 0; }
/* Paint the band. Without this the establishing view behind the stage shows
   through the strip the iframe gave up, and the tour appears to sit on a
   slice of some other room. Solid dark reads as letterboxing. */
body.sxr-hub .sxr-stage-inner > [data-mode]:has(> .sxr-frame > iframe) {
  background: var(--ground-00, #05070a);
}

/* ── 20. FEATURES BROUGHT BACK (Michael, 2026-08-07) ────────────────────
   Four things he missed from the pre-HUB build. Three of them still
   existed and had only stopped being reachable; this is mostly about
   putting the doors back where he expects them. ── */

/* 20a. FULL SCREEN on the dock. A toggle, not a destination — so no
   coloured orb, no pip. It reads as a utility sitting after the three,
   divided from them by a hairline. The RETURN is the engine's own
   always-visible "Exit Full Screen" pill; this label flips too, so the way
   out is stated in both places. */
#sxr-hub .hub-rail .hub-util {
  position: relative;
  /* same stacked geometry as the three, so the dock reads as ONE row —
     matched to .hub-btn.hub-primary above, just narrower */
  flex-direction: column;
  gap: 7px;
  padding: 10px 18px 8px;
  margin-left: 10px;
  min-width: 92px;
  border-radius: 18px;
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
  opacity: 0.66;
}
/* the hairline is what says "utility, not destination" — no orb hue, so
   nothing here reads as a fourth place to go */
#sxr-hub .hub-rail .hub-util::before {
  content: ''; position: absolute; left: -5px; top: 50%;
  width: 1px; height: 40px; transform: translateY(-50%);
  background: var(--edge, rgba(255, 255, 255, 0.13));
}
#sxr-hub .hub-rail .hub-util .ic {
  display: grid; place-items: center;
  width: 46px; height: 46px; border-radius: 50%;
  flex: 0 0 auto;
  background: rgba(255, 255, 255, 0.04);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.10);
  color: var(--ink-secondary);
  transition: box-shadow var(--dur-press, .18s) var(--ease-out, ease),
              color var(--dur-press, .18s) var(--ease-out, ease);
}
#sxr-hub .hub-rail .hub-util .ic svg { width: 20px; height: 20px; stroke-width: 1.7; }
#sxr-hub .hub-rail .hub-util:hover { opacity: 1; color: var(--ink-primary); }
/* ON = you are IN full screen, and the label already says "Exit" */
#sxr-hub .hub-rail .hub-util.on { opacity: 1; color: var(--ink-primary); }
#sxr-hub .hub-rail .hub-util.on .ic {
  box-shadow: inset 0 0 0 1px var(--accent-glow), 0 0 22px var(--accent-dim);
  color: var(--accent);
}
@media (max-width: 900px) {
  #sxr-hub .hub-rail .hub-util { min-width: 0; padding: 8px 12px 6px; margin-left: 6px; }
  #sxr-hub .hub-rail .hub-util .ic { width: 38px; height: 38px; }
}

/* 20b. The logo menu's ACTIONS. The card above states what the space IS;
   this states where you can GO. Same rows the old title-card dropdown had. */
/* ── GO TO: TWO COLUMNS  (2026-08-09) ──────────────────────────────────
   Michael: "the menu needs slight adjustment … on doc it's too big."

   Measured before changing anything: the panel is byte-identical whichever
   way you open it — 462px, same top, same bottom, from the mark and from the
   dock alike. What actually changed was the row count. Adding "Back to the
   start" made seven, the card hit its height cap, and .modal-body started
   scrolling. A panel that scrolls reads as "too big" even when its frame has
   not moved a pixel.

   The fix is not smaller text, it is the wasted axis. Each row was spanning
   the full 1240px card to carry a 200-character label — all the pressure was
   vertical while half the width sat empty. Two columns turns seven rows into
   four rows of height and the card fits again with room to spare.

   One column under 720px, where a two-up row would be too narrow to read. */
.info-acts {
  margin-top: 18px;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 7px;
  align-content: start;
}
@media (max-width: 720px) { .info-acts { grid-template-columns: 1fr; } }
.info-acts .ia-t {
  grid-column: 1 / -1;
  font-size: 0.66rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-tertiary, rgba(255, 255, 255, 0.42));
  margin: 0 0 2px;
}
.info-acts .info-act {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 11px 13px;
  background: var(--glass, rgba(255, 255, 255, 0.04));
  border: 1px solid var(--edge, rgba(255, 255, 255, 0.1));
  border-radius: var(--radius-control, 10px);
  color: inherit; cursor: pointer; text-align: left;
  transition: border-color 0.18s ease, background 0.18s ease;
}
.info-acts .info-act:hover {
  border-color: var(--accent);
  background: var(--lift, rgba(255, 255, 255, 0.07));
}
.info-acts .info-act i { width: 17px; height: 17px; flex: 0 0 auto; opacity: 0.8; }
.info-acts .info-act span { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.info-acts .info-act b { font-size: 0.86rem; font-weight: 600; }
.info-acts .info-act small {
  font-size: 0.72rem; color: var(--ink-secondary, rgba(255, 255, 255, 0.55));
}

/* 20c. SUB-NAV — the siblings of whatever is on the stage. Sits above the
   dock, where his screenshot had it. Same 140px shelf the gallery strip and
   scene pills use, so the three never stack on each other. Hidden in cinema
   mode with the rest of the chrome, and exempt from the idle fade because
   it is navigation, not decoration. */
#sxr-subnav {
  position: fixed; left: 50%; bottom: 156px;
  transform: translateX(-50%);
  display: none; gap: 4px; padding: 4px;
  background: var(--glass-solid, rgba(14, 14, 16, 0.86));
  backdrop-filter: blur(var(--blur-panel, 18px));
  -webkit-backdrop-filter: blur(var(--blur-panel, 18px));
  border: 1px solid var(--edge, rgba(255, 255, 255, 0.12));
  border-radius: var(--radius-panel, 14px);
  z-index: 53;
}
#sxr-subnav.on { display: flex; }
#sxr-subnav button {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 14px; border: 0; border-radius: var(--radius-control, 10px);
  background: none; color: var(--ink-secondary, rgba(255, 255, 255, 0.6));
  font: inherit; font-size: 0.8rem; cursor: pointer; white-space: nowrap;
  transition: color 0.18s ease, background 0.18s ease;
}
#sxr-subnav button i { width: 15px; height: 15px; }
#sxr-subnav button:hover { color: var(--ink, #fff); background: var(--lift, rgba(255, 255, 255, 0.06)); }
#sxr-subnav button.on { color: var(--ink, #fff); background: var(--lift, rgba(255, 255, 255, 0.09)); }
body.sono-idle #sxr-subnav.on { opacity: 1; pointer-events: auto; }
body.sxr-cinema #sxr-subnav { display: none !important; }
/* the guide is a full-screen destination — sub-nav belongs to the stage */
body.sxr-guide-open #sxr-subnav { display: none !important; }

@media (max-width: 900px) {
  #sxr-subnav { bottom: 132px; }
  #sxr-subnav button span { display: none; }
  #sxr-subnav button { padding: 9px 12px; }
}

/* Both the sub-nav and the 360 scene pills claim the 156px shelf, and a
   panorama in a multi-mode 360 group shows BOTH. Scenes step up when the
   sub-nav is present: group first, then the scene within it. */
body:has(#sxr-subnav.on) .sxr-scenes { bottom: 214px !important; }

/* ── 21. TOUCH KIOSK — on the chrome that is actually on screen ─────────
   Michael, 2026-08-08: "if I'm using my tablet/iPad and/or touchscreen
   monitor would I be able to touch menus etc."

   Touch already worked — every control is tappable and the base targets are
   viable (85px dock buttons, 203x156 tiles). What did NOT work was the
   Touch Kiosk surface itself: measured at 1024x768, the dock was IDENTICAL
   in Web, Kiosk and XR. Every kiosk rule in css/showcase-stage.css aims at
   .sxr-dock / .sxr-rail-btn / .sxr-grp-menu / .sxr-settings-btn — legacy
   chrome the HUB hides. It was scaling up a dock nobody can see. Exactly the
   bug cinema mode had, for exactly the same reason.

   Sizes are bumped rather than transform:scale()'d — scaling blurs text and
   leaves hit areas fighting the visual edges. A kiosk is read at arm's
   length, so this is about comfort and confidence, not basic reachability. */

/* the dock */
body.sxr-hub.sxr-kiosk #sxr-hub .hub-rail { gap: 8px; padding: 14px 18px; }
body.sxr-hub.sxr-kiosk .hub-btn.hub-primary {
  min-width: 138px; padding: 14px 26px 11px; font-size: 0.8rem; gap: 9px;
}
body.sxr-hub.sxr-kiosk .hub-btn.hub-primary .ic { width: 58px; height: 58px; }
body.sxr-hub.sxr-kiosk .hub-btn.hub-primary .ic svg { width: 25px; height: 25px; }
body.sxr-hub.sxr-kiosk #sxr-hub .hub-rail .hub-util {
  min-width: 116px; padding: 14px 22px 11px; font-size: 0.8rem;
}
body.sxr-hub.sxr-kiosk #sxr-hub .hub-rail .hub-util .ic { width: 58px; height: 58px; }

/* the content grid */
body.sxr-hub.sxr-kiosk .hub-tile { flex-basis: 232px; }
body.sxr-hub.sxr-kiosk .hub-tile .nm { font-size: 1rem; }
body.sxr-hub.sxr-kiosk .hub-tile .k { font-size: 0.68rem; }

/* connect, the logo menu, the footer chips */
body.sxr-hub.sxr-kiosk .connect-card { min-height: 140px; padding: 17px 19px 16px; }
body.sxr-hub.sxr-kiosk .connect-card strong { font-size: 1rem; }
body.sxr-hub.sxr-kiosk .info-acts .info-act { padding: 15px 17px; }
body.sxr-hub.sxr-kiosk .info-acts .info-act b { font-size: 0.95rem; }
body.sxr-hub.sxr-kiosk .hf-chip { padding: 12px 18px 12px 12px; font-size: 0.88rem; }

/* the home screen and the sub-nav */
body.sxr-hub.sxr-kiosk .guide-path { padding: 21px 24px; }
body.sxr-hub.sxr-kiosk #sxr-subnav button { padding: 13px 20px; font-size: 0.88rem; }
body.sxr-hub.sxr-kiosk #sxr-subnav button i { width: 18px; height: 18px; }

/* the modal has to give the taller dock its room back, or the card grows
   down underneath it — the min-height:0 lesson from HUB.md §4 */
body.sxr-hub.sxr-kiosk #sxr-modal { padding-bottom: 186px; }
body.sxr-hub.sxr-kiosk .sxr-gal-strip,
body.sxr-hub.sxr-kiosk .sxr-scenes { bottom: 174px !important; }
body.sxr-hub.sxr-kiosk .sxr-gal-title { bottom: 266px !important; }
body.sxr-hub.sxr-kiosk #sxr-subnav { bottom: 190px; }
body.sxr-hub.sxr-kiosk .sxr-frame:has(iframe) { bottom: 174px; }

/* The home screen centres in the WHOLE viewport while the dock floats over
   the bottom, so on a short screen the three paths slide underneath it —
   visible the moment you open this on an iPad in landscape (768 tall), and
   worse with the taller kiosk dock. Reserve the dock's footprint.

   Gated on height so the locked 1440x900 composition does not shift: at
   desktop height it already clears, and Michael signed that layout off. */
@media (max-height: 860px) {
  body.sxr-hub #sxr-guide { padding-bottom: 132px; }

  /* Reserving the dock's room is only half of it. The headline is sized off
     viewport WIDTH — clamp(2.6rem, 8.2vw, 7rem) — so on a 1024x768 iPad it
     renders enormous against a short screen, and pushing the block up just
     drove the title into the logo. Scale the composition to the height it
     actually has: smaller type, a smaller orb, tighter gaps. Nothing is
     dropped, so the screen still says everything it says on a desktop. */
  #sxr-guide .guide-title { font-size: clamp(2rem, 5.6vh, 3.4rem); margin-bottom: 6px; }
  #sxr-guide .guide-lede  { font-size: clamp(0.88rem, 1.9vh, 1.02rem); }
  .guide-status { margin-bottom: 14px; }
  .guide-sono { margin: 16px 0 2px; }
  .sono-halo.lg { width: 116px; height: 116px; margin-bottom: -20px; }
  .sono-halo.lg .bloom { inset: -36px; }
  #sxr-guide .guide-paths { gap: 8px; }
  #sxr-guide .guide-path { padding: 12px 16px; }
}

/* the kiosk dock is taller again, so it needs more room than the web one */
body.sxr-hub.sxr-kiosk #sxr-guide { padding-bottom: 176px; }
@media (max-height: 860px) {
  body.sxr-hub.sxr-kiosk .guide-sono { margin: 10px 0 2px; }
  body.sxr-hub.sxr-kiosk .sono-halo.lg { width: 96px; height: 96px; margin-bottom: -16px; }
  body.sxr-hub.sxr-kiosk #sxr-guide .guide-title { font-size: clamp(1.9rem, 4.8vh, 2.9rem); }
}

/* ── 22. THE CATEGORY PLATE ─────────────────────────────────────────────
   A tile with no art was a small icon floating on flat grey — it read as
   broken rather than as empty. Every tile already carries --cat (the legend
   colour, section 1035), so an art-less tile can be a designed plate in its
   own category's hue instead of a hole.

   This is a FALLBACK, not decoration: js/showcase-hub.js prefers the
   showcase's own media, then cfg.posters[mode]. Real art always wins.

   ⚠️ It must never make an EMPTY category look stocked. The plate is
   deliberately quiet, and .is-off drops it further and keeps the — badge,
   so "we have none of this" still reads at a glance. */
.hub-tile .frame {
  background:
    radial-gradient(120% 100% at 50% 0%,
      color-mix(in srgb, var(--cat, #8C99A6) 17%, transparent) 0%,
      transparent 68%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--cat, #8C99A6) 7%, var(--ground-02)) 0%,
      var(--ground-02) 74%);
}
/* the icon becomes the plate's subject rather than a lost 17px glyph */
.hub-tile .frame .ico {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  color: color-mix(in srgb, var(--cat, #8C99A6) 72%, transparent);
}
.hub-tile .frame .ico i,
.hub-tile .frame .ico svg { width: 34px; height: 34px; stroke-width: 1.25; }
/* a hairline in the category hue, so the plate has an edge like real art */
.hub-tile:not(.is-off) .frame {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cat, #8C99A6) 26%, transparent),
              0 6px 18px rgba(0, 0, 0, 0.45);
}
/* EMPTY stays visibly empty */
.hub-tile.is-off .frame { background: var(--ground-02); }
.hub-tile.is-off .frame .ico { color: rgba(255, 255, 255, 0.16); }
.hub-tile.is-off .frame .ico i,
.hub-tile.is-off .frame .ico svg { width: 26px; height: 26px; }

@media (max-width: 900px) {
  .hub-tile .frame .ico i, .hub-tile .frame .ico svg { width: 28px; height: 28px; }
}

/* ── 23. DOCUMENTS ──────────────────────────────────────────────────────
   The one category you READ. A list, not a gallery — a brochure has no
   useful thumbnail, and a wall of grey PDF icons is worse than a title.
   The kind badge does the work a thumbnail would. */
/* ⚠️ --glass is a linear-gradient LAYER, not a colour. `background: var(--glass)`
   on its own leaves a near-transparent row, and these sit over live media —
   the first build put unreadable text straight onto the room. Reading modules
   in this engine sit on a SOLID (var(--ground-02)); so does this. */
.sxr-docs {
  position: absolute; inset: 0;
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 8px;
  padding: 84px 6vw 168px;
}
/* the room dims behind a module you are meant to READ. On the element
   itself, not a ::before at z-index:-1 — that painted behind the stage and
   only the strip under the rows appeared to dim. */
.sxr-docs { background: linear-gradient(180deg, rgba(6,5,4,0.74) 0%, rgba(6,5,4,0.88) 100%); }
.sxr-docs > .sxr-doc { max-width: 860px; width: 100%; margin: 0 auto; }
.sxr-doc {
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: 14px;
  padding: 15px 17px;
  border: 1px solid var(--edge, rgba(255, 255, 255, 0.1));
  border-radius: var(--radius-control, 10px);
  background: var(--ground-02);
  color: var(--ink-primary); text-align: left; cursor: pointer;
  transition: border-color .18s ease, background .18s ease;
}
.sxr-doc:hover { border-color: var(--cat-docs); background: var(--lift, rgba(255,255,255,0.07)); }
.sxr-doc .dk {
  display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: 9px;
  background: color-mix(in srgb, var(--cat-docs) 16%, transparent);
  color: var(--cat-docs);
}
.sxr-doc .dk i, .sxr-doc .dk svg { width: 19px; height: 19px; }
.sxr-doc .dt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sxr-doc .dt b { font-size: 0.95rem; font-weight: 600; }
.sxr-doc .dt small { font-size: 0.76rem; color: var(--ink-secondary); }
.sxr-doc .db {
  font-family: var(--font-screen);
  font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 5px 9px; border-radius: 6px;
  color: var(--cat-docs);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cat-docs) 34%, transparent);
}
/* the PDF opens ON the stage — leaving the showcase to read a brochure is
   how you lose the visit */
.sxr-doc-view {
  position: absolute; inset: 0; z-index: 3;
  display: none; background: var(--ground-00, #0b0a09);
}
.sxr-doc-view.on { display: block; }
.sxr-doc-view iframe { position: absolute; inset: 0 0 128px; width: 100%; height: calc(100% - 128px); border: 0; }
/* top: 84px, not 14px — the Audio Hub launcher owns the top-right corner at
   z-54 and the close button landed underneath it. Same shelf the engine's
   back pill and Exit Full Screen use. */
.sxr-doc-view .dv-x {
  position: absolute; top: 84px; right: 16px; z-index: 2;
  width: 38px; height: 38px; border-radius: 50%;
  border: 1px solid var(--edge, rgba(255,255,255,0.14));
  background: rgba(10, 10, 12, 0.82); color: var(--ink-primary);
  font-size: 1.2rem; line-height: 1; cursor: pointer;
}
.sxr-doc-view .dv-x:hover { border-color: var(--cat-docs); color: var(--cat-docs); }
@media (max-width: 900px) {
  .sxr-docs { padding: 74px 5vw 150px; }
  .sxr-doc .db { display: none; }
}

/* ── 24. THE OVERVIEW, STRIPPED (Michael, 2026-08-08) ───────────────────
   "keep our SONO SPATIAL GUIDE avatar but lose the options below. That will
   give the screen a cleaner interface and all options clearly viewable."

   Home is now three things: the name of the space, Sono, and the dock. The
   three paths were a second menu competing with the dock for the same job —
   they still exist, one click into Sono, and the dock carries everything.

   Hidden, not deleted. js/showcase-hub.js still BUILDS the paths, and the
   Sono panel renders the same list from the same data — scoping this to
   #sxr-guide leaves the compact copy inside the modal untouched. */
body.sxr-hub #sxr-guide .guide-status,
body.sxr-hub #sxr-guide .guide-sub,
body.sxr-hub #sxr-guide .guide-lede,
body.sxr-hub #sxr-guide .guide-ask,
body.sxr-hub #sxr-guide .guide-paths { display: none !important; }

/* the bottom-left slate. Everything it said — the name, the city, the year —
   is either in the title above it or in the logo menu. */
body.sxr-hub .sono-slate { display: none !important; }

/* With the paths gone the block is short, so it no longer needs the dock's
   room reserved on a short screen (section 21) — centre it properly again. */
@media (max-height: 860px) {
  body.sxr-hub #sxr-guide { padding-bottom: 40px; }
  body.sxr-hub.sxr-kiosk #sxr-guide { padding-bottom: 60px; }
}

/* ── 25. YOU CAN SEE THE ROOM BEHIND AN OPEN MENU ───────────────────────
   Michael: "when you click on Contact or Audio Hub could you lighten
   background so you can see background behind?"

   The card was never the problem — .modal-card is already glass, with
   blur(26px) and a light gradient. What sat UNDER it was #sxr-guide::before,
   a radial scrim at rgba(6,5,4,0.86) in the exact centre of the screen, put
   there to make the home copy legible. With a menu on top of it, it just
   read as an opaque black plate.

   That copy is now mostly gone (section 24), and a menu brings its own
   contrast, so the scrim stands down while one is open. */
body.sxr-hub.sxr-modal-open #sxr-guide::before { opacity: 0.34; }
body.sxr-hub.sxr-modal-open .sxr-room-scrim { opacity: 0.55; }
#sxr-guide::before, body.sxr-hub .sxr-room-scrim {
  transition: opacity var(--dur-chrome, .3s) var(--ease-out, ease);
}

/* ── 26. THE MENUS ARE ACTUALLY GLASS NOW ───────────────────────────────
   Michael, 2026-08-08: "on all the menus can you make it so the background
   is lighter to let background be viewable (glass effect)."

   .modal-card already carried backdrop-filter: blur(26px) saturate(1.35) —
   and it was doing nothing you could see, because the layer painted ON TOP
   of it was linear-gradient(rgba(16,15,14,0.94) → rgba(10,9,8,0.97)). At 94%
   opaque there is nothing left for a blur to show through. It was a glass
   panel with a wall behind it.

   Dropped to the mid-60s so the blur has something to do. Kept a real tint
   rather than going near-transparent: this card holds body copy over live
   video, and legibility is not negotiable — the blur plus saturate is what
   makes text readable, not the opacity alone.

   The pinned footer row and the documents list get the same treatment; both
   were near-opaque for the same reason. */
.modal-card {
  background: var(--glass),
              linear-gradient(180deg, rgba(16, 15, 14, 0.66) 0%, rgba(10, 9, 8, 0.74) 100%);
  backdrop-filter: blur(30px) saturate(1.4);
  -webkit-backdrop-filter: blur(30px) saturate(1.4);
}
.hub-foot {
  background: linear-gradient(0deg,
    rgba(11, 10, 9, 0.80) 62%, rgba(11, 10, 9, 0.62) 82%, rgba(11, 10, 9, 0) 100%);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
}
.sxr-docs {
  background: linear-gradient(180deg, rgba(6, 5, 4, 0.52) 0%, rgba(6, 5, 4, 0.68) 100%);
  backdrop-filter: blur(22px) saturate(1.3);
  -webkit-backdrop-filter: blur(22px) saturate(1.3);
}
/* the rows inside sit on the glass, so they lift rather than block */
.sxr-doc { background: rgba(255, 255, 255, 0.055); }
.sxr-doc:hover { background: rgba(255, 255, 255, 0.1); }

/* ── 27. THE ICON PLATE IS THE DESIGN, NOT A GAP ────────────────────────
   Michael, 2026-08-08: "Maybe we could just skip image and use an ICON with
   TEXT???"

   Yes — for the categories where no honest image exists. A Matterport tour,
   a splat viewer and a Google area map are third-party embeds; there is no
   frame on our side to pull. Stock art in that slot would show a building
   that is not the client's, which is the Malibu mistake in picture form.

   So section 22's fallback is promoted to a deliberate treatment: the icon
   is the subject, at a size that holds the frame, over its category's hue.
   The tile still reads icon + kicker + name — that IS the icon-with-text he
   asked for, with the text where it has always been. */
.hub-tile .frame .ico i,
.hub-tile .frame .ico svg { width: 42px; height: 42px; stroke-width: 1.1; }
.hub-tile .frame .ico {
  /* a soft pool of the category colour behind the glyph, so the plate has a
     centre of gravity instead of a lone mark floating on a panel */
  background: radial-gradient(46% 58% at 50% 50%,
    color-mix(in srgb, var(--cat, #8C99A6) 13%, transparent) 0%, transparent 72%);
}
.hub-tile:not(.is-off):hover .frame .ico {
  color: color-mix(in srgb, var(--cat, #8C99A6) 95%, white);
}
/* an empty category still has to look empty */
.hub-tile.is-off .frame .ico { background: none; }
.hub-tile.is-off .frame .ico i,
.hub-tile.is-off .frame .ico svg { width: 30px; height: 30px; }

@media (max-width: 900px) {
  .hub-tile .frame .ico i, .hub-tile .frame .ico svg { width: 34px; height: 34px; }
}

/* ── 28. PRESENTED BY, AND THE FULL LOCKUP ──────────────────────────────
   Michael, 2026-08-08. Two branding fixes.

   1. "Presented by ABC CLIENT" under the title. Quiet — it is an
      attribution, not a second headline. Section 24 hid the sub-line and
      the lede; this is deliberately NOT one of them, it is the one piece of
      branding he asked to add back.

   2. The mark is the full SXR / SONO XR lockup now, not the monogram.
      "The monogram logo doesn't work, too close to SEX" — and he is right;
      the S-X-R glyphs run together at small sizes. A two-line lockup needs
      more height than the 46px a single row wanted. */
#sxr-guide .guide-by {
  font-family: var(--font-screen);
  /* Michael, 2026-08-08: "it needs to be BOLD WHITE like TITLE." Two passes
     of nudging opacity did not fix it, because the problem was never the
     colour — it was 11px of heavily letter-spaced 600 over live media. Type
     that thin does not hold against a bright room at any opacity. Same
     weight and same white as the headline, at a size that subordinates it. */
  font-size: clamp(0.95rem, 1.45vw, 1.2rem);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* ⚠️ NOT --ink-tertiary. This line sits over live media with no scrim
     behind it (section 24 removed the copy the guide scrim existed for), and
     at 50% white it vanished completely against a bright room. Quiet is the
     intent; invisible is not. */
  color: #fff;
  margin: 14px 0 0;
  /* ⚠️ A TEXT SHADOW IS NOT ENOUGH. Three passes went into weight, size and
     opacity and none of them fixed it, because the failure is white type on
     a LIGHT background — this showcase's room has bright walls and the line
     sits directly on them with no scrim (section 24 removed the copy the
     guide scrim existed for). Contrast, not weight, was always the problem.
     So it gets a plate, the same move "Choose with Sono" already uses: the
     line carries its own background and is legible on any frame of any
     space. */
  display: inline-block;
  padding: 7px 16px;
  border-radius: 999px;
  background: rgba(10, 9, 8, 0.62);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
}
#sxr-guide .guide-by[hidden] { display: none; }

/* the lockup carries two lines of type, so it needs the height to stay
   legible — the 46px in section 12 was sized for the monogram alone */
body.sxr-hub .sxr-brand-slot img { height: 62px !important; }
@media (max-width: 900px) { body.sxr-hub .sxr-brand-slot img { height: 40px !important; } }
/* ⚠️ this used to force 0.66rem and silently undid the size bump above on
   any window under 860px tall — which is most browser windows. Scale it,
   do not shrink it back to the value the bump was fixing. */
@media (max-height: 860px) { #sxr-guide .guide-by { font-size: 0.9rem; margin-top: 10px; } }

/* the launcher pill now tells the truth, so it needs an ON state to tell it in */
#sxr-audio-launch .st.is-on {
  color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent-glow);
  background: var(--accent-dim);
}

/* ── 29. CHANNELS AND CHAPTERS ──────────────────────────────────────────
   Michael, 2026-08-08: a channel list of background music, and narration
   that reads the space like an audiobook. Both are a named list of audio
   sources, so both get the same row. Rendered only at two or more — one
   track is a track, not a channel list. */
#sxr-modal .sxa-list-l,
.sxa-list-l {
  font-family: var(--font-screen);
  font-size: 0.62rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--ink-tertiary, rgba(255, 255, 255, 0.45));
  margin: 18px 0 8px;
}
.sxa-list { display: flex; flex-direction: column; gap: 5px; }
.sxa-row {
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: 12px;
  padding: 11px 14px;
  border: 1px solid transparent;
  border-radius: var(--radius-control, 10px);
  background: rgba(255, 255, 255, 0.04);
  color: var(--ink-secondary); font: inherit; font-size: 0.86rem;
  text-align: left; cursor: pointer;
  transition: background .16s ease, color .16s ease, border-color .16s ease;
}
.sxa-row:hover { background: rgba(255, 255, 255, 0.08); color: var(--ink-primary); }
.sxa-row .rn {
  font-family: var(--font-screen); font-size: 0.62rem; letter-spacing: 0.1em;
  color: var(--ink-tertiary, rgba(255, 255, 255, 0.4));
}
.sxa-row .rt { font-weight: 600; }
.sxa-row .rm {
  font-family: var(--font-screen); font-size: 0.6rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-tertiary, rgba(255,255,255,0.42));
  padding: 4px 8px; border-radius: 5px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}
.sxa-row.on {
  color: var(--ink-primary);
  background: var(--accent-dim);
  border-color: var(--accent-glow);
}
.sxa-row.on .rn, .sxa-row.on .rm { color: var(--accent); }
/* a chapter that is actually reading right now, not merely selected */
.sxa-row.playing .rn::after {
  content: ''; display: inline-block; margin-left: 7px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent);
  animation: sxaPulse 1.6s ease-in-out infinite;
}
@keyframes sxaPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* ── 30. HERO ACTIONS ───────────────────────────────────────────────────
   The client's own buttons on the entry screen — Michael's original premise
   and the one piece of it that had never been built. Section 24 stripped
   OUR three generic paths from here; these are not those. They are whatever
   this showcase configured, and a showcase that configured none shows none.

   Same plate logic as .guide-by: these sit on live media with no scrim, so
   they carry their own background rather than trusting the frame behind
   them to be dark. */
.hero-acts {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 10px; margin: 22px 0 0;
}
.hero-acts[hidden] { display: none; }
.hero-act {
  display: inline-flex; align-items: center; gap: 11px;
  padding: 13px 20px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(10, 9, 8, 0.62);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  color: #fff; font: inherit; text-align: left; cursor: pointer;
  animation: hubRise 0.9s var(--ease-out) calc(0.26s + var(--i) * 0.05s) both;
  transition: border-color .18s ease, background .18s ease, transform .18s ease;
}
.hero-act:hover { border-color: var(--accent); background: rgba(14, 13, 12, 0.78); transform: translateY(-1px); }
.hero-act i { width: 17px; height: 17px; flex: 0 0 auto; opacity: .85; }
.hero-act span { display: flex; flex-direction: column; gap: 1px; }
.hero-act b { font-size: 0.92rem; font-weight: 650; letter-spacing: -0.005em; }
.hero-act small { font-size: 0.74rem; color: rgba(255, 255, 255, 0.6); }
/* the one the client wants pressed */
.hero-act.is-primary {
  background: var(--accent);
  border-color: transparent;
  color: #0b0a09;
}
.hero-act.is-primary small { color: rgba(0, 0, 0, 0.6); }
.hero-act.is-primary:hover { background: var(--accent); filter: brightness(1.07); }

@media (max-width: 900px) {
  .hero-acts { flex-direction: column; align-items: stretch; }
  .hero-act { justify-content: center; }
  .hero-act small { display: none; }
}
@media (max-height: 860px) {
  .hero-acts { margin-top: 16px; gap: 8px; }
  .hero-act { padding: 11px 17px; }
}

/* ── 31. THE SOUND CHIP IS A BUTTON ─────────────────────────────────────
   Michael, 2026-08-09. Sound was playing with no findable way to stop it:
   the chip said ON and could not be clicked, and Mute lived inside a panel
   you had to know to open. Now the chip mutes and the label opens the hub —
   two real buttons in one container, so both are keyboard reachable. */
#sxr-audio-launch .st {
  border: 0; font: inherit; cursor: pointer;
  transition: background var(--dur-press) var(--ease-out),
              color var(--dur-press) var(--ease-out),
              box-shadow var(--dur-press) var(--ease-out);
}
#sxr-audio-launch .st:hover {
  color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent-glow);
}
#sxr-audio-launch .st.is-on:hover {
  /* already lit — hovering offers the OFF, so warn rather than encourage */
  color: var(--cat-videos, #D9705E);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cat-videos, #D9705E) 55%, transparent);
}
#sxr-audio-launch .tx-btn {
  display: flex; align-items: center; gap: 12px;
  padding: 0; border: 0; background: none; color: inherit;
  font: inherit; text-align: left; cursor: pointer;
}
#sxr-audio-launch .st:focus-visible,
#sxr-audio-launch .tx-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ═══ 32 · THE AGENT CARD — the person, not the company  (2026-08-09) ══════
   Michael: a showcase was carrying the client's brand but never their FACE.
   "They should have very own branding and area like on Zillow etc."

   Zillow's agent card works because it breaks the grid: everything else on
   the page is property data in equal cells, and the person is a distinct
   band with a photo. Copying the LAYOUT LOGIC, not the look — this card
   spans every column of .info-grid so it reads as its own area rather than
   a fourth data cell. Property and Location stay side by side above it. */
.info-block.agent-card {
  padding: 16px 18px 15px;
  background: rgba(255, 255, 255, 0.05);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.055);
}

.agent-card .ag-row { display: flex; align-items: center; gap: 15px; }

.agent-card .ag-photo {
  flex: 0 0 auto;
  width: 62px; height: 62px;
  border-radius: 50%;
  background-color: rgba(255, 255, 255, 0.06);
  background-size: cover;
  background-position: center;
  /* the accent ring is the only place a client's colour touches their own
     portrait — enough to feel deliberate, not enough to tint the face */
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.10), 0 0 0 3px var(--accent-dim, rgba(255,255,255,0.05));
}
/* NO STOCK SILHOUETTE. A generic person-icon asserts that someone was
   photographed; initials read as "not supplied", which is the truth. */
.agent-card .ag-photo.is-initials {
  display: grid; place-items: center;
}
.agent-card .ag-photo.is-initials span {
  font-family: var(--font-screen);
  font-size: 1.05rem; font-weight: 500; letter-spacing: 0.06em;
  color: var(--ink-tertiary);
}

.agent-card .ag-id { min-width: 0; }
.agent-card .ag-name {
  font-size: 1rem; font-weight: 600; color: var(--ink-primary);
  line-height: 1.25;
}
.agent-card .ag-sub {
  margin-top: 3px;
  font-size: 0.8rem; color: var(--ink-secondary);
}
.agent-card .ag-line {
  margin-top: 2px;
  font-size: 0.76rem; color: var(--ink-tertiary);
  overflow-wrap: anywhere;
}

.agent-card .ag-acts {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-top: 14px;
}
.agent-card .ag-act {
  padding: 8px 15px;
  border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  font-family: var(--font-screen);
  font-size: 0.625rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-secondary);
  text-decoration: none;
  transition: color var(--dur-press) var(--ease-out),
              box-shadow var(--dur-press) var(--ease-out);
}
.agent-card .ag-act:hover {
  color: var(--ink-primary);
  box-shadow: inset 0 0 0 1px var(--accent-dim);
}

@media (max-width: 700px) {
  .agent-card .ag-photo { width: 52px; height: 52px; }
  .agent-card .ag-acts .ag-act { flex: 1 1 auto; text-align: center; }
}

/* the "Agent & Contact" row scrolls to the card above rather than opening a
   second surface for the same three facts. When the card is already in view
   that scroll is invisible, so the card acknowledges the click itself. */
@keyframes sxr-called {
  0%   { box-shadow: inset 0 0 0 1px var(--accent), 0 0 0 0 var(--accent-dim); }
  100% { box-shadow: inset 0 0 0 1px rgba(255,255,255,0.055), 0 0 0 14px transparent; }
}
.info-block.agent-card.is-called { animation: sxr-called 1100ms var(--ease-out); }
@media (prefers-reduced-motion: reduce) {
  .info-block.agent-card.is-called { animation: none; box-shadow: inset 0 0 0 1px var(--accent); }
}

/* ═══ 33 · CORNER SWAP, ICON LAUNCHER, HERO LOCKUP  (2026-08-09) ═══════════
   Michael, on the chrome: Full Screen off the dock and into the corner where
   the credit was, the credit to the opposite corner, and the Audio Hub
   button reduced to an icon.

   The through-line: the dock should hold DESTINATIONS and nothing else.
   Overview, Media and Connect are places you go. Full Screen is a property
   of the window, and it was wearing the same clothes as the three. Now the
   dock is three of one kind, and the two window-level toggles — sound and
   screen — sit in opposite corners as matching icon controls. */

/* the two corner toggles are one component in two places */
#sxr-fs-corner,
#sxr-audio-launch {
  position: fixed;
  z-index: 46;
  border: 0;
  border-radius: 999px;
  background: var(--glass), linear-gradient(180deg, rgba(12,11,10,0.72) 0%, rgba(9,8,7,0.80) 100%);
  box-shadow: var(--edge);
  backdrop-filter: var(--blur-panel);
  -webkit-backdrop-filter: var(--blur-panel);
  color: var(--ink-secondary);
  cursor: pointer;
  display: grid; place-items: center;
  width: 46px; height: 46px;
  padding: 0;
  transition: color var(--dur-press) var(--ease-out),
              box-shadow var(--dur-press) var(--ease-out);
}
#sxr-fs-corner { right: 28px; bottom: 26px; }
#sxr-audio-launch { top: 26px; right: 28px; gap: 0; }

/* ⚠️ ABOVE THE GREETING (z 54, matching #sxr-hub and the client mark).
   Both of these inherited z-index 46 from the old launcher, and #sxr-guide is
   50 — so on the entry screen they were painted UNDER a full-screen overlay:
   plainly visible, completely unclickable. The Audio Hub launcher had shipped
   that way, and it went unnoticed because every test of it drove the button
   with .click(), which bypasses hit-testing entirely. A programmatic click
   proves a handler is bound; it says nothing about whether a person can
   reach the control. Use elementFromPoint for that. */
#sxr-fs-corner, #sxr-audio-launch { z-index: 54; }

#sxr-fs-corner:hover,
#sxr-audio-launch:hover { color: var(--ink-primary); box-shadow: var(--edge), 0 0 0 1px var(--accent-dim); }
#sxr-fs-corner:focus-visible,
#sxr-audio-launch .tx-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

#sxr-fs-corner svg,
#sxr-audio-launch .tx-btn svg {
  width: 19px; height: 19px;
  stroke-width: 1.7;
  display: block;
}
/* the label is for screen readers and the tooltip, not for the eye */
#sxr-fs-corner [data-hub-fs-label] {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
#sxr-audio-launch .tx-btn {
  width: 100%; height: 100%;
  display: grid; place-items: center;
  padding: 0; border: 0; background: none; color: inherit; cursor: pointer;
}
/* the sound control says whether sound is ON without adding a second control:
   the icon itself carries the accent when something is playing */
#sxr-audio-launch.is-playing { color: var(--accent); }

/* ⚠️ NO GUIDE-OPEN FADE HERE. The first cut hid this while Sono had the
   frame — "comes back when the visit starts". It never came back: body keeps
   `sxr-guide-open` for the whole visit, it is not a transient state. The
   button was 46x46 at the right coordinates with visibility:visible and
   opacity:0 forever, which is why it inspected as present and was invisible.
   (#sxr-powered carries the same fade and survives only because a later rule
   re-asserts opacity:1 on it — worth knowing before copying that pattern.)

   It should not fade regardless: full screen is a window control, and the
   entry screen is exactly where someone is most likely to want it. */

/* ── THE HERO LOCKUP ────────────────────────────────────────────────────
   The client's mark standing in for the title and the presented-by line. */
.guide-lockup { margin: 0 auto 6px; max-width: min(420px, 76vw); }
.guide-lockup img {
  display: block; width: 100%; height: auto;
  filter: drop-shadow(0 6px 30px rgba(0, 0, 0, 0.55));
}
@media (max-width: 900px) { .guide-lockup { max-width: min(320px, 74vw); } }
@media (max-width: 600px) { .guide-lockup { max-width: 72vw; } }

/* ═══ 34 · SONO, ALIVE  (2026-08-09) ══════════════════════════════════════
   Michael: "any way to give SONO GUIDE some motion or more interactive
   elements? It's truly unique and large part of platform … should feel like
   some effort went into visual".

   The restraint that already governs this mark still applies — DESIGN.md's
   SpaceX-not-Tron rule, and the command-center note that mission control is
   QUIET, not neon. So the aim is not MORE motion. Before this, Sono had two
   loops that ran forever (breathe, spin) and one that resolved (the sweep,
   which stops when the link is up). Everything that never resolves reads as
   decoration after ten seconds, and nothing at all responded to the person
   looking at it — which is why it felt flat despite already moving.

   What is added is RESPONSE, not decoration:
     · the orb leans toward the pointer — the single clearest signal that a
       thing is aware of you, and it costs one transform
     · a slow float, so it is suspended rather than pinned
     · the visor catches a light every so often — a blink, not a strobe:
       long gaps, short event. Continuous shimmer would be neon.
     · hover and press are felt, not just coloured
   All of it is transform and opacity, all of it dies under
   prefers-reduced-motion, and none of it claims anything. */

.guide-sono {
  /* set by js/showcase-hub.js — the pointer lean, in px */
  --sono-x: 0px;
  --sono-y: 0px;
}
.guide-sono .sono-halo.lg {
  transform: translate3d(var(--sono-x), var(--sono-y), 0);
  transition: transform 420ms cubic-bezier(.22,.61,.36,1);
  will-change: transform;
}
/* suspended, not pinned. Deliberately slow and tiny — at 7s and 5px it
   reads as "floating"; any faster and it reads as "loading". */
@keyframes sonoFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}
.guide-sono .sono-halo.lg .mark { animation: sonoFloat 7s ease-in-out infinite; }

/* THE BLINK. 92% of the cycle is nothing at all, which is the whole point —
   a highlight that is always travelling is a screensaver. */
@keyframes sonoScan {
  0%, 88%  { opacity: 0; transform: translateX(-120%) skewX(-18deg); }
  92%      { opacity: 0.5; }
  100%     { opacity: 0; transform: translateX(120%) skewX(-18deg); }
}
.guide-sono .sono-halo.lg::after {
  content: '';
  position: absolute; inset: 0; border-radius: 50%;
  overflow: hidden; pointer-events: none; z-index: 2;
  background: linear-gradient(100deg, transparent 42%,
    color-mix(in srgb, var(--accent) 40%, white) 50%, transparent 58%);
  mix-blend-mode: screen;
  animation: sonoScan 9s ease-in-out infinite;
}

@media (hover: hover) and (pointer: fine) {
  /* the orb answers before the caption does */
  .guide-sono:hover .sono-halo.lg .bloom { animation-duration: 2.6s; opacity: 1; }
  .guide-sono:hover .sono-halo.lg .ring::after { animation-duration: 3.2s; opacity: 1; }
  .guide-sono:hover .sono-halo.lg .mark { color: #fff; }
}
.guide-sono:active .sono-halo.lg { transform: translate3d(var(--sono-x), var(--sono-y), 0) scale(0.972); }

/* the three paths arrive in sequence rather than all at once — the eye gets
   to read them as a set of choices instead of a block appearing */
#sxr-guide .guide-paths .guide-path { animation: hubRise 0.7s var(--ease-out) both; }
#sxr-guide .guide-paths .guide-path:nth-child(1) { animation-delay: 0.42s; }
#sxr-guide .guide-paths .guide-path:nth-child(2) { animation-delay: 0.52s; }
#sxr-guide .guide-paths .guide-path:nth-child(3) { animation-delay: 0.62s; }
#sxr-guide .guide-paths .guide-path:nth-child(4) { animation-delay: 0.72s; }

@media (prefers-reduced-motion: reduce) {
  .guide-sono .sono-halo.lg,
  .guide-sono .sono-halo.lg .mark,
  .guide-sono .sono-halo.lg::after,
  #sxr-guide .guide-paths .guide-path { animation: none !important; transition: none !important; }
  .guide-sono .sono-halo.lg { transform: none !important; }
}

/* ═══ 35 · THE CHROME-GLASS TOGGLES  (2026-08-09) ═════════════════════════
   Michael: "the audio icon needs to be easier to view/click, could we do
   like a more modern/futuristic SPEAKER or something. It's important it
   looks cool, maybe Chrome glass."

   ⚠️ NOTE AGAINST DESIGN.md, deliberately. The constitution says SpaceX-not-
   Tron: near-monochrome, restrained, no ornament. A machined bezel is
   ornament, and this is the one place in the showcase that now carries any.
   It earns the exception on the grounds Michael gave — legibility first. A
   flat 46px stroke icon sitting on top of live photography has no consistent
   background to contrast against, so it half-disappeared over bright media.
   A bezel gives the glyph its own physical edge, which reads at any size and
   over anything. Kept to two corner controls; if it starts spreading into
   panels, that is the drift DESIGN.md is there to prevent.

   Both corners change together. They are one component in two places, and
   chroming one while the other stayed flat would just look like a bug. */

#sxr-fs-corner,
#sxr-audio-launch {
  width: 54px; height: 54px;
  /* THE BEZEL. A conic gradient is the cheap, sharp way to fake a turned
     metal ring: light catches at four points around the circumference the
     way it does on a real machined edge. No image, no blur pass. */
  background:
    conic-gradient(from 218deg,
      #4a4f56 0deg, #b9c0c8 42deg, #6b7178 88deg, #e6eaee 134deg,
      #767c84 186deg, #cad0d7 238deg, #5b6067 292deg, #a8aeb6 338deg, #4a4f56 360deg);
  padding: 2px;                       /* the bezel's own thickness */
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.30) inset,
    0 10px 26px rgba(0, 0, 0, 0.55),
    0 2px 6px rgba(0, 0, 0, 0.45);
  color: rgba(255, 255, 255, 0.92);
  backdrop-filter: none;              /* the glass disc below does this */
  -webkit-backdrop-filter: none;
}

/* THE GLASS. Sits inside the bezel and holds the icon: a dark, slightly
   domed disc with one specular sweep across the upper left. */
#sxr-fs-corner::before,
#sxr-audio-launch::before {
  content: '';
  position: absolute; inset: 2px;
  border-radius: 50%;
  background:
    radial-gradient(120% 100% at 32% 22%,
      rgba(255, 255, 255, 0.26) 0%, rgba(255, 255, 255, 0.06) 26%, transparent 52%),
    linear-gradient(168deg, rgba(46, 49, 54, 0.96) 0%, rgba(16, 17, 19, 0.98) 62%, rgba(9, 10, 11, 1) 100%);
  box-shadow: inset 0 0 14px rgba(0, 0, 0, 0.75);
  pointer-events: none;
}
/* the glyph rides above the glass */
#sxr-fs-corner svg,
#sxr-audio-launch .tx-btn svg {
  position: relative; z-index: 1;
  width: 23px; height: 22px;
  stroke-width: 1.9;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.9));
}
#sxr-audio-launch .tx-btn { position: relative; z-index: 1; border-radius: 50%; }

#sxr-fs-corner:hover,
#sxr-audio-launch:hover {
  color: #fff;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.40) inset,
    0 0 0 1px var(--accent-dim),
    0 12px 30px rgba(0, 0, 0, 0.6);
}
#sxr-fs-corner:active { transform: scale(0.965); }
#sxr-audio-launch:active { transform: scale(0.965); }

/* PLAYING. The faint outer arc is the tell — it takes the accent and the fan
   pulses, so sound-on is legible from across the room without a second
   control or a word of text. */
#sxr-audio-launch .tx-btn svg .w3 { opacity: 0.42; }
#sxr-audio-launch.is-playing { color: var(--accent); }
#sxr-audio-launch.is-playing .tx-btn svg .w3 { opacity: 1; }
@keyframes sxrWave {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 1; }
}
#sxr-audio-launch.is-playing .w2 { animation: sxrWave 1.9s ease-in-out infinite; }
#sxr-audio-launch.is-playing .w3 { animation: sxrWave 1.9s ease-in-out infinite 0.28s; }

@media (prefers-reduced-motion: reduce) {
  #sxr-audio-launch.is-playing .w2,
  #sxr-audio-launch.is-playing .w3 { animation: none; }
}
@media (max-width: 700px) {
  #sxr-fs-corner, #sxr-audio-launch { width: 48px; height: 48px; }
  #sxr-fs-corner svg, #sxr-audio-launch .tx-btn svg { width: 21px; height: 20px; }
}

/* ═══ 36 · THE PANEL FITS AGAIN  (2026-08-09) ═════════════════════════════
   Michael: "on doc it's too big". Measured first — the panel is identical
   whichever way it opens (462px, same top, same bottom, from the mark and
   from the dock alike), so the route was never the variable. Row seven
   pushed the content past the card's cap and .modal-body began scrolling,
   which put the entire GO TO list below the fold. A menu you cannot see is
   the thing that reads as "too big".

   Two structural causes, neither of them the row:

   1 · THE CARD RESERVED 258px OF EMPTY CHROME on a 720px viewport — 106px at
       the top and 152px at the bottom, 36% of the screen gone before any
       content. The 152 clears the dock and is honest. The 106 exists only so
       the card's close button clears the corner launchers, and at
       width:min(1240px,…) the card runs the full width of the screen and
       does sit under them. Narrow the card and the reason evaporates.
       Applied only from 1260px up, which is the point at which a 1080px card
       genuinely clears a 54px launcher inset 28px from each edge. Below that
       the old padding stands, because there the collision is real.

   2 · THE HEADER WAS 158px OF TITLE for a panel whose job is the list under
       it. Display type earns its size on the entry screen, not on a utility
       menu opened from a dock. */

@media (min-width: 1260px) {
  .modal-card { width: min(1080px, 100%); }
  #sxr-modal { padding-top: 44px; }
}

/* the header, proportionate to what it introduces */
.hub-panel .hub-head { margin-bottom: 14px; padding-bottom: 13px; }
.hub-panel .hub-head[data-title]::after { font-size: 1.55rem; }
.hub-panel .hub-head .t { font-size: 0.6rem; }

/* the contact actions sit beside the person, not under them, which reclaims
   a whole row without shrinking anything */
@media (min-width: 720px) {
  .info-block.agent-card { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
  .agent-card .ib-t { flex: 1 0 100%; }
  .agent-card .ag-row { flex: 1 1 auto; }
  .agent-card .ag-acts { margin-top: 0; flex: 0 0 auto; }
}

/* the last 14px. At 1440x900 the card came out 704 tall against 704 of room
   and still showed a scrollbar — overflowing by exactly its own bottom
   padding plus a gap. Trimming those two lands it flush, so the common
   desktop case never scrolls at all. */
.modal-body { padding-bottom: 18px; }
.info-acts { margin-top: 12px; }

/* An odd number of rows leaves the last one alone in the left column, which
   reads as a mistake rather than a list. When it lands orphaned it spans
   instead. (.ia-t is child 1, so an odd-numbered ROW is an even child.)
   With an even count nothing matches and the grid is left alone. */
.info-acts .info-act:last-child:nth-child(even) { grid-column: 1 / -1; }

/* ═══ 37 · THE DOCK, AS A DOCK  (2026-08-09) ══════════════════════════════
   Michael: "the DOC is too crowded in Showcase. It also has a box glow inside
   doc and looks bad in tablet. Can we do more of a traditional apple doc?"

   Both complaints are the same element. Each item carried its own rounded
   plate — 112px minimum width, 22px of side padding, and on the active one a
   lit background plus an inset ring. Three plates inside a fourth container
   is a box in a box, and at tablet width the outer pill shrinks toward the
   plates until the nesting is all you see.

   A macOS dock has no per-item chrome at all. The icons sit directly on the
   dock surface, and the only state marker is a small dot under the running
   app. That is what this does:

     · the plate is gone — no background, no ring, no min-width
     · ACTIVE IS A DOT beneath the icon, in that destination's hue
     · hover lifts and brightens the orb rather than drawing a box behind it
     · labels stay, quieter and tighter. Apple can drop labels because a
       Finder icon is learned; a grid glyph meaning "Media" is not, and this
       dock has three items rather than thirty. Crowding was the plates.

   ⚠️ The lit plate was deliberate — the comment above it says state has to
   stay legible "to anyone who cannot separate the three hues". The dot keeps
   that promise: it is position and presence, not colour alone, so it still
   reads without hue discrimination. */

.hub-btn.hub-primary {
  min-width: 0;
  padding: 8px 14px 6px;
  border-radius: 14px;
  gap: 6px;
}

/* no plate, in any state */
.hub-btn.hub-primary.on,
.hub-btn.hub-primary:hover {
  background: none;
  box-shadow: none;
}
.hub-btn.hub-primary.on { color: var(--ink-primary); }

/* the orb reacts instead of the box behind it */
.hub-btn.hub-primary .ic {
  transition: transform var(--dur-press) var(--ease-out),
              box-shadow var(--dur-press) var(--ease-out),
              background var(--dur-press) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .hub-btn.hub-primary:hover { color: var(--ink-primary); }
  .hub-btn.hub-primary:hover .ic { transform: translateY(-2px) scale(1.06); }
}
.hub-btn.hub-primary:active .ic { transform: translateY(0) scale(0.97); }
.hub-btn.hub-primary.on .ic {
  background: radial-gradient(circle at 50% 38%,
    color-mix(in srgb, var(--orb) 75%, transparent) 0%,
    color-mix(in srgb, var(--orb) 32%, transparent) 55%,
    transparent 78%);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--orb) 80%, transparent),
              0 0 22px color-mix(in srgb, var(--orb) 34%, transparent);
}

/* THE RUNNING DOT — the whole state marker */
.hub-btn.hub-primary::after {
  content: '';
  position: absolute; left: 50%; bottom: 1px;
  width: 4px; height: 4px; border-radius: 50%;
  transform: translateX(-50%) scale(0);
  background: var(--orb);
  box-shadow: 0 0 8px color-mix(in srgb, var(--orb) 70%, transparent);
  transition: transform var(--dur-press) var(--ease-out);
}
.hub-btn.hub-primary.on::after { transform: translateX(-50%) scale(1); }
@media (prefers-reduced-motion: reduce) {
  .hub-btn.hub-primary .ic,
  .hub-btn.hub-primary::after { transition: none; }
}

/* the label, quieter — it is a caption on the icon, not a button of its own */
.hub-btn.hub-primary > span:not(.ic) {
  font-size: 0.64rem;
  letter-spacing: 0.06em;
  line-height: 1;
  padding-bottom: 3px;
}

/* the rail closes up now that the items carry no padding of their own */
#sxr-hub .hub-rail { gap: 2px; padding: 7px 8px; }

/* TABLET — where he saw it worst, and it was worse than crowding.
   ⚠️ THE ICONS WERE NOT RENDERING AT ALL BELOW 900px. Section 10 hides every
   span in a dock button on small screens — `.hub-btn > span:not(.sono-chip):
   not(.hub-pip) { display: none }` — which was written when the dock was
   text-led and the spans WERE labels. The orb is a <span class="ic">, so it
   was caught by the same rule, and the later block that re-shows the label
   (`> span:not(.ic)`) deliberately excludes it. It set the orb's width and
   height without ever restoring its display, so every tablet and phone got a
   text-only dock while the CSS looked like it was sizing an icon.
   Restoring display here rather than editing section 10, because that rule is
   still correct for the utility buttons it was written for. */
@media (max-width: 900px) {
  .hub-btn.hub-primary { padding: 7px 11px 5px; }
  /* ⚠️ ID-SCOPED DELIBERATELY. The rule being overridden is
     `.hub-btn > span:not(.sono-chip):not(.hub-pip)` — three classes AND an
     element selector, (0,3,1). A plain `.hub-btn.hub-primary .ic` is (0,3,0)
     and loses, which is why the first attempt at this fix changed nothing.
     #sxr-hub takes it to (1,3,1) and settles it. */
  #sxr-hub .hub-btn.hub-primary > span.ic { display: grid; width: 42px; height: 42px; }
  #sxr-hub .hub-btn.hub-primary > span.ic svg { width: 19px; height: 19px; }
  #sxr-hub .hub-rail { gap: 0; padding: 6px 7px; }
}

/* The last plate: a "start here" highlight sat on Overview while the greeting
   is up (section 14). Same box-in-a-box Michael objected to, so it speaks the
   dock's new language instead — the orb lights and the dot appears, which is
   exactly what "this is where you are" now looks like everywhere else.
   Same selector as the rule it overrides, later in the file, so it wins on
   source order rather than by inflating specificity. */
body.sxr-hub.sxr-guide-open .hub-btn.hub-primary:nth-child(1) {
  background: none;
  color: var(--ink-primary);
}
/* ⚠️ NO START-HERE DOT, AND THE REASON MATTERS. The first attempt pinned a
   dot to Overview while `sxr-guide-open` was set — and that class is NEVER
   removed; it is on the body for the whole visit. The dot therefore stuck to
   Overview permanently and stopped following the panel you opened. Exactly
   the trap that hid the Full Screen button earlier today, from exactly the
   same class.

   So the dot means one thing only: THIS PANEL IS OPEN. closeModal() already
   clears `.on` from every dock button, so closing a menu clears the dot with
   it. No panel open, no dot — which is also more honest than pointing at
   Overview while the visitor is looking at the stage. The greeting is its own
   start-here cue and it occupies the middle of the screen. */

/* ═══ 38 · LOGO WITHOUT A BOX · FOUR IN THE DOCK  (2026-08-09) ═════════════
   Michael: "get rid of drop down menu at top left (LOGO) just logo no
   background… no matter where a client is on showcase the logo is always
   there, but on home page you get larger logo w/contact info and SONO GUIDE."

   The mark had grown a pill, a hover ring and a chevron because it was a
   control. It isn't one any more — Property in the dock carries the property,
   location and contact card it used to open. So it goes back to what section 2
   of this file originally specified before the drop-down was added:
   "NO BOX — Michael, 2026-08-06". The entry screen keeps the big lockup and
   Sono; this is the persistent small mark for the rest of the visit. */
body.sxr-hub .sxr-brand-slot {
  padding: 0 !important;
  background: none;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  pointer-events: none;
}
body.sxr-hub .sxr-brand-slot:hover { box-shadow: none; }
/* the chevron went with the menu it advertised */
body.sxr-hub .sxr-brand-slot::after { content: none; }

/* THE FOURTH ORB. Hues are assigned by :nth-child, and adding Home shifted
   every position by one — without this the new fourth item inherits no --orb
   at all and renders colourless. Home takes the cyan the old first item had. */
/* --cat-overview is literally commented "teal — the way back" in the tokens,
   which is exactly what Home is. Property takes amber: mint (--cat-docs) and
   green (--cat-area) are #6FBF8A and #86B36B, near-identical side by side, and
   the four orbs sit in a row where they have to be told apart at a glance. */
.hub-btn.hub-primary:nth-child(1) { --orb: var(--cat-overview); }
.hub-btn.hub-primary:nth-child(2) { --orb: var(--cat-aerial); }
.hub-btn.hub-primary:nth-child(3) { --orb: var(--cat-photos); }
.hub-btn.hub-primary:nth-child(4) { --orb: var(--cat-area); }

/* four items need slightly less air than three did */
.hub-btn.hub-primary { padding: 8px 12px 6px; }
@media (max-width: 900px) {
  .hub-btn.hub-primary { padding: 7px 9px 5px; }
}

/* ═══ 39 · THE AUDIO HUB, TIGHTENED  (2026-08-09) ══════════════════════════
   Michael: "the AUDIO menu needs to be adjusted, too large."

   Measured: 1180px of content in a 524px card — 656px of overflow, more than
   twice the panel. It is the biggest menu in the product by some distance,
   and it grew that way one honest feature at a time: three status cards, four
   channels, three chapters, a volume row, four soundstage profiles and four
   effect toggles.

   Nothing is removed. Everything here is real and Michael has called the Audio
   Hub the feature that "cements SONO XR value" — cutting controls to win
   height would be solving the wrong problem. What is cut is padding: the
   repeated row is used eleven times, so every pixel off it comes back eleven
   times over. ~1180 → ~840, which fits a normal desktop card without
   scrolling and roughly halves the scroll on a short laptop. */

/* the three status cards: 13px padding and a 0.9rem title for what is a
   status line with one verb under it */
#sxr-modal .hub-adopt .sxa-cards { gap: 6px; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
#sxr-modal .hub-adopt .sxa-card { padding: 9px 12px; }
#sxr-modal .hub-adopt .sxa-card b { font-size: 0.82rem; }
#sxr-modal .hub-adopt .sxa-card i { font-size: 0.7rem; }

/* the row — eleven instances, so this is where the height actually is */
.sxa-row { padding: 7px 12px; font-size: 0.82rem; gap: 10px; }
.sxa-list { gap: 4px; }
#sxr-modal .sxa-list-l, .sxa-list-l { margin: 12px 0 6px; }

/* profiles and effects */
#sxr-modal .hub-adopt .sxa-fxb { padding: 8px 11px; gap: 9px; }
#sxr-modal .hub-adopt .sxa-fx { gap: 5px; margin-top: 8px; }
#sxr-modal .hub-adopt .sxa-vol { padding: 9px 12px; }

/* the panel header earns less room here than on a content menu — this one is
   a settings surface, not a destination */
.hub-panel[data-p="audio"] .hub-head { margin-bottom: 10px; padding-bottom: 10px; }
.hub-panel[data-p="audio"] .hub-head[data-title]::after { font-size: 1.3rem; }

/* Second pass — the first cut only took 123px because it trimmed the row and
   left the two tallest blocks alone. Measured again rather than estimating
   twice: .sxa-cards was still 147 and .sxa-profs 144, together more than a
   third of the panel.

   The profiles wrap to two rows at minmax(250px) inside this card; at 220px
   they sit two-up on desktop and the block halves. The status cards carry a
   chip, a title, a sub-line and a verb — the sub-line is the one that can
   lose its own line of leading. */
#sxr-modal .hub-adopt .sxa-profs { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 5px; }
#sxr-modal .hub-adopt .sxa-prof { padding: 8px 12px; gap: 10px; }
#sxr-modal .hub-adopt .sxa-prof i,
#sxr-modal .hub-adopt .sxa-prof small { line-height: 1.35; }
#sxr-modal .hub-adopt .sxa-cards { grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); }
#sxr-modal .hub-adopt .sxa-card i { margin-top: 1px; line-height: 1.3; }
#sxr-modal .hub-adopt .sxa-act { margin-top: 8px; }

/* ── TWO COLUMNS, NOTHING REMOVED ───────────────────────────────────────
   Padding got the panel from 1180 to 992 and then stopped paying: what is
   left is eleven real controls, and shaving them further would start costing
   legibility for maybe 40px.

   The height was never the problem — the SHAPE was. The card is 1080px wide
   and the Audio Hub was using a single 500px column of it, so it ran twice as
   tall as it needed to while half the card sat empty. Two columns halves the
   height with every control intact.

   Multi-column rather than grid because the panel's contents are ADOPTED from
   js/showcase-audio.js — the HUB moves that markup in wholesale and does not
   own its structure. `column-count` needs no knowledge of what the children
   are or how many there will be, so adding a fourth soundstage profile later
   cannot break the layout. `break-inside: avoid` keeps a control from being
   split down the middle of the fold. */
@media (min-width: 900px) {
  .hub-panel[data-p="audio"] .hub-adopt > * {
    column-count: 2;
    column-gap: 18px;
  }
  .hub-panel[data-p="audio"] .hub-adopt > * > * {
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
  }
  /* the status cards read as one row across the top rather than being split
     between the two columns */
  .hub-panel[data-p="audio"] .hub-adopt .sxa-cards { column-span: all; margin-bottom: 12px; }
}

/* ═══ 40 · HOME LOSES ITS CAPTION  (Michael, 2026-08-10) ═══════════════════
   "delete Home under icon, it doesn't need name."

   The dock is a COLUMN flex per button — orb on top, caption under it — and
   the rail centred its items. So dropping one caption made that button ~21px
   shorter than its three neighbours, and centring pushed its orb DOWN out of
   the row. Four orbs that no longer sit on one line is a worse problem than
   the caption ever was.

   `stretch` gives every button the height of the tallest, and starting the
   content at the top puts all four orbs on one line whether or not a caption
   follows. Home simply has empty space beneath its orb — and because the
   plate is what shows `.on`, the active state also stays the same size and
   shape across all four.

   Written this way rather than as a padding fudge on Home specifically, so
   the next caption that gets dropped needs no new rule. */
#sxr-hub .hub-rail { align-items: stretch; }
.hub-btn.hub-primary { justify-content: flex-start; }

/* ═══ 41 · CHROME, NOT COLOUR  (Michael, 2026-08-10) ═══════════════════════
   "instead of colors its brushed chrome … use same chrome icons you added
   yesterday for showcase dock/sono guide."

   The reference is the two corner toggles (#sxr-fs-corner, #sxr-audio-launch):
   neutral glass, a hairline edge, NO hue. They already read as the most
   expensive things on screen, which is the whole argument — DESIGN.md calls
   SONO XR "near-monochrome" and rules that cyan is the only accent.

   ⚠️ THIS RETIRES THE --cat-* ORB LEGEND, which HUB.md §2 documents as "one
   hue per destination — the legend, not a state". That was a real idea and it
   is being deliberately traded: four hues in a row is the loudest thing in a
   dock that sits over someone else's branded space, and white-label means the
   frame should defer to the client's colours rather than add four of its own.
   The destinations stay separable by ICON and LABEL, which is how the corner
   toggles have always worked. HUB.md updated in the same commit.

   ⚠️ AND IT MAKES STATE MORE LEGIBLE, not less. Previously the resting orbs
   were coloured and the ACTIVE one was also coloured, just brighter — hue was
   doing two jobs. Now rest is chrome and active is cyan, so "you are here" is
   the only colour on the dock. */
/* ⚠️ NEUTRAL, NOT WARM (Michael, 2026-08-10): "there's still a brown/beige
   color … would rather it be silver or gradient black."
   The cast was not imagined and it was not the icons — it is the whole ink
   palette. sono-tokens.css defines --ink-primary #EDE8E0 and both --ink-
   secondary/-tertiary from it: R237 G232 B224, red above green above blue,
   i.e. warm cream. The orb fill inherited the same bias at rgba(12,11,10).
   At 46px with a lit edge that reads as brushed BRASS, not chrome.

   Fixed LOCALLY rather than by editing the tokens: those warm inks are the
   showcase's whole typography and panel palette, and cooling them globally
   would restyle every surface in the product to fix four orbs. Silver and a
   neutral black gradient are declared here, for these controls only. */
.hub-btn.hub-primary .ic {
  background:
    linear-gradient(180deg, rgba(255,255,255,0.13) 0%, rgba(255,255,255,0.035) 45%, rgba(0,0,0,0.20) 100%),
    linear-gradient(180deg, #1a1d21 0%, #0a0c0e 100%);
  /* ⚠️ MATCH THE MATERIAL, NOT THE ELEVATION. The corner toggles compute to a
     0.30 top edge PLUS two lift shadows — they float free over the space, so
     they cast. These orbs sit INSIDE the dock panel, which already casts its
     own; giving them the lift too would read as objects hovering inside a
     card. So the lit edge is matched at 0.30 and the drop shadows are not. */
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.30),
              inset 0 -1px 0 rgba(0,0,0,0.42);
  color: #c3ccd6;          /* silver — neutral, not the warm --ink-secondary */
}
/* hue is gone; state is not */
.hub-btn.hub-primary.on .ic,
.hub-btn.hub-primary:hover .ic {
  color: var(--accent);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.30), inset 0 -1px 0 rgba(0,0,0,0.42),
              0 0 22px color-mix(in srgb, var(--accent) 22%, transparent);
}
.hub-btn.hub-primary.on {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
}

/* the Sono guide orb — same material, so the guide and the dock read as one
   family rather than the guide being the one coloured thing on the screen */
#sxr-sono-launch .orb {
  background:
    linear-gradient(180deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0.035) 45%, rgba(0,0,0,0.20) 100%),
    linear-gradient(180deg, #1a1d21 0%, #0a0c0e 100%);
  color: #d4dce4;          /* silver, a touch brighter — Sono leads the screen */
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.30), inset 0 -1px 0 rgba(0,0,0,0.42);
}

/* ── the Sono halo was the last warm thing on the entry screen ─────────────
   Measured, not guessed: .sono-halo::before is a radial of rgba(28,26,24) →
   rgba(9,8,7) — red above green above blue at every stop, i.e. warm brown —
   and ::after sweeps a highlight of srgb(0.876 0.821 0.736), which is gold.
   Together they are what read as "brown/beige" on the biggest control on the
   home screen. Same fix as the dock: neutral fill, silver highlight. The
   sweep's geometry and timing are untouched — only its colour. */
.sono-halo::before,
.guide-sono span::before {
  background: radial-gradient(circle at 50% 34%,
    rgba(30, 33, 38, 0.94) 0%, rgba(14, 16, 19, 0.97) 62%, rgba(8, 9, 11, 0.99) 100%);
}
.sono-halo::after,
.guide-sono span::after {
  background: linear-gradient(100deg,
    rgba(0,0,0,0) 42%, rgba(226, 234, 243, 0.85) 50%, rgba(0,0,0,0) 58%);
}

/* ── Home's ring was the last of the old legend ────────────────────────────
   Michael: "the HOME icon on doc has neon border that suppose should look
   bronze." Correct — it was still teal. The dock went chrome, but this rule
   sits at specificity (0,5,1):

     body.sxr-hub.sxr-guide-open .hub-btn.hub-primary:nth-child(1) .ic

   against (0,3,1) for the general active-state rule, so appending a chrome
   version further down the file did nothing — CSS weighs specificity before
   order, and this one outranks it wherever it is written. It has to be
   matched selector-for-selector, which is why this looks so verbose.

   --orb was the per-destination hue; --accent is the showcase's one state
   colour, #B08D57 antique brass. Home lights bronze now, like every other
   active state, and the legend is fully retired. */
body.sxr-hub.sxr-guide-open .hub-btn.hub-primary:nth-child(1) .ic {
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.30),
              inset 0 -1px 0 rgba(0,0,0,0.42),
              0 0 26px color-mix(in srgb, var(--accent) 30%, transparent);
  color: var(--accent);
}
/* ⚠️ THE RING MOVED TO THE CIRCLE (Michael, 2026-08-12): "the icons have a box
   inside the border that's bigger than doc itself… make it a circle slightly
   larger than icon."

   This drew a 1px bronze ring around the whole 70×79 BUTTON — a rounded
   rectangle at 14px radius, sitting inside the dock's own rounded border, so
   Home read as a box within a box. The ring it should have been is one line
   above, on .ic: a 46px circle around a 20px icon, already bronze and already
   glowing when active. That circle now carries the state alone.

   Removed here rather than overridden further down, because this selector is
   (0,5,1) — the one HUB.md records as having beaten three appended fixes. An
   override would have had to repeat all five compound parts to win; deleting
   the declaration at its source is simpler and cannot drift. */

/* ═══ 42 · THE MENUS JOIN THE DOCK  (Michael, 2026-08-10) ══════════════════
   "the chrome/bronze theme is actually growing on me … but needs to be
   consistent throughout site."

   The dock went chrome earlier today; the MENUS did not, so opening one
   still produced the full spectrum — a cyan kicker and eleven category
   labels in rose, red, teal, indigo, violet, amber, green, mint and orchid.
   Half the interface had moved and half had not, which reads as unfinished
   rather than as a legend.

   Traced rather than guessed at, because both were being set by a LATER
   rule that overrode the neutral one already present:
     .hub-head .t:first-child { color: var(--cat-tours) }   ← the kicker
     .hub-tile .k { color: var(--cat, var(--ink-tertiary)) } ← tile labels

   ⚠️ THIS COMPLETES THE RETIREMENT OF THE --cat-* LEGEND as a *visible*
   device. HUB.md §2 already records the dock leaving it. The tokens stay
   defined and the [data-cat] hooks stay wired — a category still knows what
   it is, and a future feature can colour by it again — but nothing paints
   with it now. Kicker takes the one accent; tiles are neutral and are told
   apart by icon, label and poster, exactly as the dock is. */
.hub-head .t:first-child { color: var(--accent); }
.hub-tile .k { color: var(--ink-tertiary); }

/* the tile edge and hover lit up in the category hue too */
.hub-tile .frame { border-color: rgba(255, 255, 255, 0.10); }
@media (hover: hover) and (pointer: fine) {
  .hub-tile:hover .frame {
    border-color: color-mix(in srgb, var(--accent) 45%, transparent);
    box-shadow: 0 0 26px color-mix(in srgb, var(--accent) 18%, transparent);
  }
}
/* an active/selected tile is state, so it keeps the accent — the only
   colour in the panel, same rule the dock now follows */
.hub-tile.on .frame,
.hub-tile[aria-current="true"] .frame {
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
}

/* ── and the surfaces, not just the text ──────────────────────────────────
   The kicker and labels went neutral above, but the category hue was ALSO
   painting two surfaces, which is why the panel still looked like a paint
   chart. Traced by reading computed values rather than the source, because
   neither is a border:

     .hub-tile .frame  — a radial-gradient BACKGROUND tinted with --cat, plus
                         a 1px --cat inset in its box-shadow
     the active dock .ic — the same radial, tinted with --orb

   Setting border-color did nothing for either. These replace the tints. */
.hub-tile .frame {
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(255,255,255,0.055) 0%, rgba(0,0,0,0) 68%),
    linear-gradient(180deg, #16191d 0%, #0b0d0f 100%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.09), 0 6px 18px rgba(0,0,0,0.45);
}
@media (hover: hover) and (pointer: fine) {
  .hub-tile:hover .frame {
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent),
                0 6px 22px rgba(0,0,0,0.5);
  }
}
/* the ACTIVE dock orb — state, so it takes the accent rather than the
   destination's old hue */
.hub-btn.hub-primary.on .ic {
  background:
    radial-gradient(circle at 50% 38%,
      color-mix(in srgb, var(--accent) 42%, transparent) 0%,
      color-mix(in srgb, var(--accent) 16%, transparent) 55%,
      transparent 78%),
    linear-gradient(180deg, #1a1d21 0%, #0a0c0e 100%);
}

/* ⚠️ AND ONE MORE SPECIFICITY MISS, the same shape as Home's ring. The tint
   that survived is on `.hub-tile:not(.is-off) .frame` — (0,3,0) — which
   outranks the `.hub-tile .frame` override above it at (0,2,0). It showed up
   as exactly 8 of 11 tiles still tinted: the three that are `.is-off` were
   never matched by it. Matched selector-for-selector here. */
.hub-tile:not(.is-off) .frame {
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.09), 0 6px 18px rgba(0,0,0,0.45);
}
@media (hover: hover) and (pointer: fine) {
  .hub-tile:not(.is-off):hover .frame {
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent),
                0 6px 22px rgba(0,0,0,0.5);
  }
}

/* the 2px cap on each tile was the last of it — HUB.md called this the
   legend's home ("chips, kickers and 2px edges"). Neutral now, so a tile is
   told apart by its poster, icon and label like every other control. */
.hub-tile .frame::after { background: rgba(255, 255, 255, 0.13); }
.hub-tile:not(.is-off):hover .frame::after {
  background: color-mix(in srgb, var(--accent) 70%, transparent);
}

/* ═══ 43 · CONNECT GOES CHROME, AND MEDIA BECOMES TEXT  (2026-08-10) ═══════

   (a) CONNECT still wore the legend. `.connect-card .tag` and `.act` were
   drawing from --cat, so the panel came up in teal, blue, violet, amber and
   green while the dock and the media grid had already gone neutral. Same
   treatment as everything else: labels neutral, the ONE accent reserved for
   the action, so the eye lands on what you can actually press.

   (b) MEDIA LOSES ITS POSTERS. Michael: "maybe our Media section should be
   more like Connect (no photos, just text) … most are galleries that have
   various images, so this way it's more about organizing/loading media type."

   He is right, and the reason is structural rather than aesthetic: one poster
   cannot represent a GALLERY. It shows the first image and implies the tile
   IS that image, when the tile is really a container of many. Two tiles were
   literally showing the same still, because Photos and Videos happen to open
   on the same space. A text card says what the row is — a kind of media and
   how much of it — which is the actual decision being made here.

   ⚠️ DONE WITH `display: contents` RATHER THAN NEW MARKUP. The tile is built
   by js/showcase-hub.js as .frame(.img/.scrim/.cnt) + .meta(.k/.nm). Letting
   .frame dissolve promotes its children into the tile's own grid, so the
   count can sit beside the label without touching the builder — and the HUB
   stays an adapter, which is HUB.md §1's whole rule. `order` puts them back
   in reading order, since .cnt precedes .meta in the DOM. */
.connect-card .tag { color: var(--ink-tertiary); border-color: rgba(255,255,255,0.16); }
.connect-card .act { color: var(--accent); }

/* ── media tiles become text cards ── */
.hub-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1180px) { .hub-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 860px)  { .hub-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .hub-grid { grid-template-columns: 1fr; } }

.hub-tile {
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  gap: 3px;
  min-height: 116px;
  padding: 13px 15px 12px;
  border-radius: var(--radius-control);
  background: rgba(255, 255, 255, 0.035);
  text-align: left;
}
.hub-tile .frame { display: contents; }
.hub-tile .img, .hub-tile .scrim, .hub-tile .ico { display: none; }

.hub-tile .meta { display: contents; }
.hub-tile .k  { order: 1; justify-self: start; color: var(--ink-tertiary); }
.hub-tile .nm { order: 2; font-size: 0.95rem; font-weight: 600; color: var(--ink-primary); }
/* the count becomes the action line, the way Connect's .act reads */
.hub-tile .cnt {
  order: 4; position: static; align-self: end; justify-self: start;
  font-family: var(--font-screen); font-size: 0.625rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent); background: none; border: 0; padding: 0;
  width: auto; height: auto; min-width: 0; border-radius: 0; box-shadow: none;
}
.hub-tile .cnt::after { content: ' in this showcase'; letter-spacing: 0.06em; }

/* a category with nothing in it says so, rather than showing a dark rectangle */
.hub-tile.is-off { opacity: 1; background: rgba(255,255,255,0.018); }
.hub-tile.is-off .nm { color: var(--ink-secondary); }
.hub-tile.is-off::after {
  content: 'Not in this showcase';
  order: 4; align-self: end;
  font-family: var(--font-screen); font-size: 0.625rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-tertiary);
}
@media (hover: hover) and (pointer: fine) {
  .hub-tile:not(.is-off):hover { background: rgba(255,255,255,0.06); }
}

/* ⚠️ THE SAME SPECIFICITY MISS, A THIRD TIME TODAY. `.hub-tile .frame .ico`
   is (0,3,0) and beat the `.hub-tile .ico` rule above at (0,2,0), so the
   category glyphs kept rendering inside the new text cards. Worth stating
   plainly for whoever reads this file next: in sono-hub.css, appending a rule
   at the end is NOT how you win. Match the selector you are replacing.
   The `.frame` prefix still works even though .frame is display:contents —
   that removes the BOX, not the element, so it still matches selectors. */
.hub-tile .frame .ico { display: none; }

/* an empty category already says "Not in this showcase" via ::after — the
   count line would otherwise render "— IN THIS SHOWCASE" directly above it */
.hub-tile.is-off .cnt { display: none; }
