/* ==========================================================================
 MOMENTUM - Magnum Vault design system
 Extracted and codified, not invented. Every rule below traces to one of
 two production surfaces:

 [OPS] /Users/anricblatt/dev/magnum-ecosystem/ops/index.html
 MAESTRO operator console V3.x. Chamfered-corner idiom
 (clip-path), gold + cyan, Russo One / Inter / Space Mono.
 This is the INTERACTION and DENSITY reference.

 [MEMBER] live-dash-css.css, extracted from the live member dashboard
 at app.magnumvault.vip/dashboard. Everything that matters is
 scoped under ".mvp-cmd" inside a much larger GHL page-builder
 stylesheet (vendor boilerplate ignored). Rounded-panel idiom
 (border-radius, NOT clip-path), gold + status colors, Russo
 One / Verdana / JetBrains Mono. This is the MEMBER-FACING
 LOOK reference - the standard the rejected prototype broke.

 Every declaration is commented with [SOURCE] and the original selector.
 HARVESTED = a value copied verbatim from a source. CHOSEN = a value this
 file adds that has no direct source (fallback stacks, tabular-nums,
 reduced-motion handling). CHOSEN values are also listed in
 docs/DESIGN-SYSTEM.md so they can be reviewed, not just assumed.

 CORRECTION ON RECORD: the member dashboard was almost briefed to use
 chamfered clip-path corners, copying the OPS idiom. Verified by grep:
 live-dash-css.css contains ZERO clip-path declarations. The member
 dashboard's entire panel language is border-radius: 14px + a gradient
 hairline top edge + backdrop-filter blur. Chamfers are an OPS-only
 idiom (magnum-ecosystem/CLAUDE.md rule D2: "Chamfered corners, not pill
 rounding" - explicitly scoped to that project's own surfaces). This
 file follows the MEMBER idiom throughout because mvp-dashboard is a
 member-facing surface.

 NO ITALICS. NO EM DASHES. NO PILL RADII (no 999px/9999px/50% on
 buttons, chips, tags, badges). Small radii and the 14px panel radius
 are correct. NO external requests: no font CDNs, no remote images.
 Mobile first throughout.
 ========================================================================== */

/* ==========================================================================
 1. TOKENS
 ========================================================================== */

:root {
  /* ---- Surfaces -----------------------------------------------------
 [MEMBER] .panel, .welcome, .reactor-zone, .gear-cluster-zone,
 .wisdom-panel - all near-black translucent panes over a dark page
 background, distinguished only by opacity and gradient angle.
 [OPS] body background is flat #000000 (--mv-bg), card bg #0d0d0d
 (--mv-bg-card). OPS panels (.sidebar, .telemetry) use a subtle gold
 wash gradient instead of pure black. ------------------------------ */
  --mom-bg-page: #0a0a0c; /* CHOSEN: page canvas behind panels; both sources assume a near-black host page rather than defining one themselves */
  --mom-bg-panel: rgba(8, 8, 12, 0.74); /* [MEMBER] .panel background */
  --mom-bg-panel-dim: rgba(
    8,
    8,
    12,
    0.62
  ); /* [MEMBER] .reactor-zone background */
  --mom-bg-panel-solid: #0d0d0d; /* [OPS] --mv-bg-card */
  --mom-bg-inset: rgba(
    0,
    0,
    0,
    0.4
  ); /* [MEMBER] .stat, .wm, .whats-new background */
  --mom-bg-modal: linear-gradient(
    180deg,
    rgba(20, 20, 26, 0.98) 0%,
    rgba(12, 12, 16, 0.98) 100%
  ); /* [MEMBER] .wisdom-panel */
  --mom-bg-gear-zone: linear-gradient(
    180deg,
    rgba(15, 15, 20, 0.92) 0%,
    rgba(10, 10, 14, 0.92) 100%
  ); /* [MEMBER] .gear-cluster-zone */
  --mom-bg-ops-wash: linear-gradient(
    180deg,
    rgba(250, 189, 89, 0.04),
    rgba(0, 0, 0, 0.6)
  ); /* [OPS] .telemetry background */

  /* ---- Gold (primary accent, both surfaces) -------------------------- */
  --mom-gold: #f0c173; /* [MEMBER] primary gold used throughout .mvp-cmd (welcome-hello span, plabel, borders) */
  --mom-gold-ops: #fabd59; /* [OPS] --mv-gold. Slightly warmer/more saturated than the member gold. Kept distinct rather than merged: each surface's real value. */
  --mom-gold-dark: #d4a950; /* [OPS] --mv-gold-dark, also appears in [MEMBER] .id-avatar gradient stop */
  --mom-gold-border: #b8893e; /* [OPS] --mv-gold-border */
  --mom-gold-bright: #fabd59; /* [MEMBER] .id-avatar gradient mid stop, .gear:hover border-color */
  --mom-gold-hot: #ffd99b; /* [MEMBER] hover state text (wisdom-btn:hover, bulletin-link:hover) */
  --mom-gold-dim: rgba(250, 189, 89, 0.08); /* [OPS] --mv-gold-dim */

  /* ---- Cyan (OPS secondary accent) ------------------------------------
 [MEMBER] does NOT use true cyan anywhere in .mvp-cmd - grepped, zero
 hits for #4ecdc4/#22d3ee. Its "cool" accent is a blue, kept separate
 below as --mom-blue. Do not conflate the two; they come from
 different surfaces and are not interchangeable. --------------------- */
  --mom-cyan: #4ecdc4; /* [OPS] --mv-cyan */
  --mom-cyan-dim: rgba(78, 205, 196, 0.15); /* [OPS] --mv-cyan-dim */

  /* ---- Status colors --------------------------------------------------
 [MEMBER] .tag.* and .wisdom-btn.is-read / .welcome-pip
 [OPS] .status-row .ok, .a-status-tag, .a-finding.sev-* -------------- */
  --mom-green: #4ade80; /* [MEMBER] .tag.live, .tag.update, .welcome-pip, .wisdom-btn.is-read */
  --mom-green-ops: #4caf50; /* [OPS] --mv-green */
  --mom-amber: #f0c173; /* [MEMBER] .tag.soon / .tag.coming reuse the primary gold - "coming" is not a distinct hue in this system */
  --mom-orange: #fb923c; /* [MEMBER] .tag.beta, .mag-beta-pill */
  --mom-orange-alt: #ff9f1c; /* [MEMBER] .wb-card-pill (workbench/rough-beta accent, distinct from .tag.beta orange) */
  --mom-red: #dc143c; /* [MEMBER] .lr-tag, .live-cta.primary, .core-node crimson; [OPS] --mv-red. Same crimson in both - this is the one color both sources agree on exactly. */
  --mom-red-dim: rgba(220, 20, 60, 0.1);
  --mom-purple: #c084fc; /* [MEMBER] .tag.publishing */
  --mom-blue: #60a5fa; /* [MEMBER] .tag.upcoming, .tag.entry, .cap-card.theme-blueprint */

  /* ---- Text tiers ------------------------------------------------------
 [MEMBER] base text #e8e8e8 (.mvp-cmd), headings #fff, muted #888,
 dimmer body copy #cfcfcf/#aaa.
 [OPS] --mv-text #fff, --mv-text-muted #888888, --mv-text-dim #555555. */
  --mom-text: #ffffff; /* [OPS] --mv-text; [MEMBER] headings (.id-name, .welcome-hello, .cm-title) */
  --mom-text-body: #e8e8e8; /* [MEMBER] .mvp-cmd base color */
  --mom-text-soft: #cfcfcf; /* [MEMBER] .id-meta b, .bulletin-body, .cm-tagline */
  --mom-text-muted: #888888; /* [MEMBER] .wm-lab, .welcome-sub; [OPS] --mv-text-muted */
  --mom-text-dim: #555555; /* [OPS] --mv-text-dim, faintest tier, used sparingly */

  /* ---- Borders (gold, at varying opacity - this system's real texture) */
  --mom-border-faint: rgba(
    240,
    193,
    115,
    0.12
  ); /* [MEMBER] .plabel border-bottom */
  --mom-border-soft: rgba(240, 193, 115, 0.22); /* [MEMBER] .panel border */
  --mom-border: rgba(240, 193, 115, 0.32); /* [MEMBER] .program-card border */
  --mom-border-strong: rgba(
    240,
    193,
    115,
    0.5
  ); /* [MEMBER] .id-tier border, .welcome-vchip border */
  --mom-border-solid: #f0c173; /* [MEMBER] :hover state borders go fully opaque */
  --mom-border-ops: var(
    --mom-gold-border
  ); /* [OPS] most borders are the flat --mv-gold-border, not a rgba wash */

  /* ---- Type scale --------------------------------------------------
 Harvested by walking every font-size in both files. Two surfaces,
 near-identical ladders. Smallest real UI text found: 8px
 ([OPS] .net-k / .net-u, the network-test readout labels). Nothing
 in either source goes below that. Base body text is 14-16px, not
 the 16-18px an "airy" layout would use. -------------------------- */
  --mom-text-2xs: 8px; /* [OPS] .net-k, .net-u */
  --mom-text-xs: 10px; /* [MEMBER] .wn-toggle, .tag; [OPS] .vital-row */
  --mom-text-sm: 11px; /* [MEMBER] .cm-meta, .welcome-sub; [OPS] .panel-head, .status-row */
  --mom-text-base: 13px; /* [MEMBER] .plabel; [OPS] .search */
  --mom-text-md: 14px; /* [OPS] body base font-size; [MEMBER] .feed-item, .live-cta */
  --mom-text-lg: 16px; /* [MEMBER] .mvp-cmd base font-size; .wb-card-name */
  --mom-text-xl: 18px; /* [MEMBER] .stat .v on narrow layouts, .id-avatar-inner mobile */
  --mom-text-2xl: 22px; /* [MEMBER] .welcome-hello, .cm-title, .wisdom-quote; [OPS] .brand-name */
  --mom-text-3xl: 24px; /* [MEMBER] .id-name */
  --mom-text-4xl: 28px; /* [MEMBER] .stat .v - the largest figure treatment in the system */
  --mom-text-5xl: 32px; /* CHOSEN, V0.4.0: mvp-dashboard's own hero-figure tier. Neither
    source goes above 28px, but the V0.3.0 rejection ("everything lives in a 6px band")
    was fixed by widening the ladder at both ends, not just adding mid tiers. Used for
    exactly one spot: the catalog telemetry TOTAL ITEMS figure, the "one hero figure"
    the density brief allows above the panel-heading tier. */

  /* ---- Spacing scale --------------------------------------------------
 Harvested from panel padding and gap values actually in use. This
 system does not run on an 8pt grid; it runs tight, mostly 8-22px. */
  --mom-space-1: 4px;
  --mom-space-2: 8px;
  --mom-space-3: 10px;
  --mom-space-4: 12px;
  --mom-space-5: 14px;
  --mom-space-6: 16px;
  --mom-space-7: 18px;
  --mom-space-8: 20px;
  --mom-space-9: 22px;
  --mom-space-10: 26px;

  /* ---- Radii -----------------------------------------------------------
 [MEMBER] panel radius is 14px, confirmed by grep (12 hits of
 "border-radius: 14px" across .welcome/.panel/.capital-magnet/
 .gear-cluster-zone). Smaller interior elements step down from there.
 NEVER 999px/9999px on interactive chrome - verified zero uses in
 both sources on buttons/tags/badges; anything called ".pill" in the
 member CSS (.wb-card-pill) is styled with border-radius: 3px, a
 square-cornered chip that kept an old class name, not an actual
 pill. Do not let the name mislead a rebuild. ------------------------ */
  --mom-radius-xs: 3px; /* [OPS] .nav-icon, .chip, .search; [MEMBER] .wb-card-pill, .tag */
  --mom-radius-sm: 5px; /* [MEMBER] .plabel .num, .wisdom-close */
  --mom-radius-md: 7px; /* [MEMBER] .welcome-vchip */
  --mom-radius-lg: 9px; /* [MEMBER] .wm, .stat, .id-btn */
  --mom-radius-xl: 11px; /* [MEMBER] .whats-new, .cm-ico is 18px separately */
  --mom-radius-2xl: 13px; /* [MEMBER] .program-card, .cluster .ch */
  --mom-radius-panel: 14px; /* [MEMBER] .panel, .welcome, .capital-magnet, .gear-cluster-zone - THE panel radius */
  --mom-radius-full: 50%; /* circular only: avatars, dots, rings - never used on rectangular chrome */

  /* ---- Shadows -----------------------------------------------------
 [MEMBER] every panel carries a drop shadow plus an inset top
 highlight line, in that exact pairing. ------------------------------ */
  --mom-shadow-panel:
    0 6px 24px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.03); /* [MEMBER] .panel */
  --mom-shadow-panel-lg:
    0 8px 32px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.04); /* [MEMBER] .reactor-zone */
  --mom-shadow-feature:
    0 8px 28px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.04); /* [MEMBER] .capital-magnet */
  --mom-shadow-modal:
    0 24px 60px rgba(0, 0, 0, 0.7), 0 0 36px rgba(240, 193, 115, 0.18),
    inset 0 1px 0 rgba(255, 255, 255, 0.04); /* [MEMBER] .wisdom-panel */
  --mom-shadow-glow-gold: 0 0 16px rgba(240, 193, 115, 0.2); /* [MEMBER] .id-btn:hover */
  --mom-shadow-glow-red: 0 4px 14px rgba(220, 20, 60, 0.4); /* [MEMBER] .live-cta.primary */

  /* ---- Blur (backdrop-filter) --------------------------------------
 Confirmed by grep: 12 backdrop-filter uses in live-dash-css.css,
 all in the 6-10px range. There is no large/heavy blur in this
 system. ------------------------------------------------------------ */
  --mom-blur-sm: 6px; /* [MEMBER] .wisdom-backdrop */
  --mom-blur-md: 8px; /* [MEMBER] .panel, .reactor-zone */
  --mom-blur-lg: 10px; /* [MEMBER] .welcome */

  /* ---- Motion durations ---------------------------------------------
 FIXED ESTATE TEMPOS. Do not re-invent. Verified by grep against
 [OPS] index.html: .ring-spin-cw { animation: spin 22s } (slow),
 .radar-blip inline --drift-dur alternates 9s/14s/22s across the
 eight blips (fast/mid/slow three-tier system), .head::after
 { animation: sweepLine 6s } (beam), #maestroStopBtn.is-active
 { animation: pulse 1.5s } (blink). Also documented as house rule
 D4 in magnum-ecosystem/CLAUDE.md: "match the duration cadence
 (slow ring 22s, mid 14s, fast 9s, beam 6s, blink 1.5s)." --------- */
  --mom-dur-slow: 22s;
  --mom-dur-mid: 14s;
  --mom-dur-fast: 9s;
  --mom-dur-beam: 6s;
  --mom-dur-blink: 1.5s;
  /* Secondary tempos actually in production, kept distinct from the
 five canonical ones above rather than rounded into them: */
  --mom-dur-glow: 2.6s; /* [MEMBER] mvp-current-glow-small/big */
  --mom-dur-meter: 4s; /* [OPS] vital-flux (telemetry bar breathing) */
  --mom-dur-hairline: 4s; /* [OPS] borderSlide (sidebar top shimmer) */
  --mom-dur-gear: 60s; /* [MEMBER] mvp-gear-spin-cw/ccw (atmosphere gear rotation, deliberately slow/ambient) */
}

/* Every gold border/background token above is expressed at member-dashboard
 opacity. Components that want the OPS flat-gold look should reference
 --mom-gold-ops / --mom-border-ops directly instead. */

/* ==========================================================================
 2. TYPOGRAPHY
 ========================================================================== */

/* ---- Font stacks -----------------------------------------------------
 REAL fonts in use, harvested verbatim:
 [MEMBER] headings: 'Russo One', sans-serif (welcome-hello, id-name, cm-title, stat .v)
 [MEMBER] body: Verdana, Geneva, Tahoma, sans-serif (.mvp-cmd base - not a premium
 font choice, a GHL page-builder default the dashboard inherited)
 [MEMBER] mono/data: 'JetBrains Mono', monospace (plabel, tags, all figures/labels)
 [OPS] headings: 'Russo One', sans-serif (--font-head)
 [OPS] body: 'Inter', sans-serif (--font-body)
 [OPS] mono/data: 'Space Mono', monospace (--font-mono)

 SUBSTITUTION NOTICE: Russo One, Inter, and both monospace faces are
 loaded from Google Fonts in production (<link href="fonts.googleapis...">
 in ops/index.html). This machine has none of them installed and this
 project cannot make external font requests. The stacks below are
 CHOSEN fallbacks that approximate each face's weight and character,
 not the real fonts. Swap in the real @font-face / <link> the moment
 external loading is allowed again; until then every "Russo One"
 heading on screen is actually a heavy system fallback. ---------------- */

:root {
  /* CHOSEN: heavy condensed-ish fallback for Russo One. Real Russo One is a
 single-weight, very heavy, slightly condensed display face. Arial
 Narrow / Segoe UI / system-ui at font-weight 900 with tightened
 letter-spacing is the closest safe local approximation. */
  --mom-font-head:
    "Arial Narrow", "Segoe UI", system-ui, -apple-system, sans-serif;
  --mom-font-head-weight: 900;

  --mom-font-body-member:
    Verdana, Geneva, Tahoma, sans-serif; /* [MEMBER] harvested exactly, no substitution needed, it's already a system font */
  --mom-font-body-ops:
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; /* CHOSEN fallback for OPS's Inter */

  /* [MEMBER] harvested fallback chain, found verbatim once in
 live-dash-css.css: 'JetBrains Mono', monospace, ui-monospace,
 SFMono-Regular, Menlo, Consolas - reused here for both surfaces'
 mono treatment since Space Mono has no meaningfully different
 local substitute. */
  --mom-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* ---- Heading treatment [MEMBER] .welcome-hello / .cm-title / .id-name */
.mom-h {
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  color: var(--mom-text);
  letter-spacing: 0.5px;
  line-height: 1.2;
  font-style: normal; /* never italic */
}

/* ---- Micro-label: small letterspaced uppercase caption ---------------
 [MEMBER] .wm-lab, .welcome-sub, .id-meta; [OPS] .status-row,
 .brand-sub, .vital-row. The single most repeated text treatment in
 both sources. --------------------------------------------------------- */
.mom-label {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--mom-text-muted);
}

/* ---- Monospace tabular figure -----------------------------------------
 [MEMBER] .stat .v, .wm-num; [OPS] .vital-row b, .net-v - every number
 on screen is set in the mono face, which is what makes columns of
 figures line up. Neither source sets an explicit tabular-nums
 property (grepped: zero hits for font-variant-numeric in both files);
 monospace alone was relied on to do that job. CHOSEN addition below:
 font-variant-numeric: tabular-nums, a safe reinforcement that changes
 nothing visually in a monospace face but keeps digits aligned if this
 is ever set in a proportional fallback. ------------------------------ */
.mom-figure {
  font-family: var(--mom-font-mono);
  font-variant-numeric: tabular-nums; /* CHOSEN, see note above */
  color: var(--mom-gold);
  line-height: 1;
  font-style: normal;
}

/* ==========================================================================
 3. PANEL - the core surface component
 [MEMBER] .panel, live-dash-css.css lines 845-854 (also .welcome, line
 571-587, and .reactor-zone, line 1149-1158, which are the same recipe
 at slightly different opacity/blur). This is THE member-dashboard
 panel idiom: rounded corners, translucent dark fill, backdrop blur,
 a soft gold border, and a gradient hairline laid along the top edge
 with an ::before pseudo-element. No clip-path anywhere in this
 component family - confirmed by grep, 0 hits in the source file.
 ========================================================================== */

.mom-panel {
  position: relative;
  background: var(--mom-bg-panel);
  backdrop-filter: blur(var(--mom-blur-md));
  -webkit-backdrop-filter: blur(var(--mom-blur-md));
  border: 1px solid var(--mom-border-soft);
  border-radius: var(--mom-radius-panel);
  /* V1.3.0 global spacing fix, direct from Anric: every panel had more space above its title
     than below it (measured, getBoundingClientRect at 1680px, before this fix: 21px above
     the label text, 16px below it, on Portals/Alerts/Latest Releases/Replays - a real,
     consistent 5px asymmetry, not a one-panel bug). Top padding cut from --mom-space-8 (20px)
     to --mom-space-6 (16px), left/right/bottom untouched, so panels are shorter (real estate
     reclaimed) and the two gaps read as balanced instead of top-heavy. .mom-section-label's
     own padding-bottom/margin-bottom (momentum.css section 4) are untouched - the fix is
     entirely in this one top-padding value, shared by every .mom-panel on the page. */
  padding: var(--mom-space-6) var(--mom-space-9) var(--mom-space-8); /* was space-8 space-9 (20px 22px) on all sides */
  box-shadow: var(--mom-shadow-panel);
}

/* Gradient hairline along the top edge. [MEMBER] .panel::before, line 854:
 "content:''; position:absolute; top:-1px; left:14px; right:14px;
 height:1px; background:linear-gradient(90deg, transparent,
 rgba(240,193,115,0.6), transparent);" - inset 14px from each side so
 the line reads as a highlight, not a full border. */
.mom-panel::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 14px;
  right: 14px;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(240, 193, 115, 0.6),
    transparent
  );
  pointer-events: none;
}

/* Feature-panel variant: slightly stronger border + gradient fill wash.
 [MEMBER] .capital-magnet, line 979-991, and .program-card, line
 1085-1096 (same recipe, different gradient stop). Used for a single
 promoted CTA card inside a denser layout, not for ordinary panels. */
.mom-panel--feature {
  background: linear-gradient(
    135deg,
    rgba(240, 193, 115, 0.14) 0%,
    rgba(8, 8, 12, 0.85) 60%
  );
  border-color: rgba(240, 193, 115, 0.45);
  box-shadow: var(--mom-shadow-feature);
  overflow: hidden;
  transition:
    transform 0.22s ease,
    border-color 0.22s ease,
    box-shadow 0.22s ease;
}
.mom-panel--feature::before {
  height: 2px;
  opacity: 0.85;
}
.mom-panel--feature:hover {
  transform: translateY(-2px);
  border-color: var(--mom-gold);
  box-shadow: 0 12px 36px rgba(240, 193, 115, 0.22);
}

/* Dim/quiet variant. [MEMBER] .reactor-zone, line 1149-1158 - same shape,
 lower base opacity, used behind a busy diagram so the diagram reads
 first. */
.mom-panel--dim {
  background: var(--mom-bg-panel-dim);
  box-shadow: var(--mom-shadow-panel-lg);
}

/* Inset sub-panel: nested block inside a .mom-panel, flat black rather
 than translucent gold-tinted. [MEMBER] .stat, .wm, .whats-new
 (line 920, 609, 925-931). */
.mom-inset {
  background: var(--mom-bg-inset);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: var(--mom-radius-lg);
  padding: var(--mom-space-4) var(--mom-space-5);
}

/* Modal panel. [MEMBER] .wisdom-panel, line 692-707 - the same hairline
 trick but a 2px line at full gold opacity instead of 1px/60%, because
 a modal needs a stronger top accent to read over a dimmed backdrop. */
.mom-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.78);
  backdrop-filter: blur(var(--mom-blur-sm));
  -webkit-backdrop-filter: blur(var(--mom-blur-sm));
}
.mom-modal-panel {
  position: relative;
  background: var(--mom-bg-modal);
  border: 1px solid rgba(240, 193, 115, 0.55);
  border-radius: 8px; /* [MEMBER] harvested exactly: modal uses a tighter 8px, not the 14px panel radius */
  padding: 36px 40px 32px;
  box-shadow: var(--mom-shadow-modal);
  max-height: calc(100vh - 40px);
  overflow-y: auto;
}
.mom-modal-panel::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 30px;
  right: 30px;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--mom-gold), transparent);
  opacity: 0.7;
}

/* [OPS] equivalent surface, for parity reference only: .sidebar/.telemetry
 (line 332-338, 400-406) are flat-bordered, squared corners, no
 backdrop-filter, no hairline pseudo-element - a plainer, denser
 idiom than the member panel. Included so a page mixing both surfaces
 doesn't accidentally invent a third look. */
.mom-panel--ops {
  background: var(--mom-bg-ops-wash);
  border: 1px solid var(--mom-border-ops);
  border-radius: 0;
  padding: var(--mom-space-6) var(--mom-space-7);
}

/* ==========================================================================
 4. SECTION LABEL - caret, uppercase micro-label, numbered badge, rule
 Both sources use a version of this above every content block.
 [MEMBER] .plabel, line 855-863 (solid border-bottom rule, numbered
 badge, no caret).
 [OPS] .panel-head, line 348-358 (dashed border-bottom rule, cyan
 caret glyph via ::before, no numbered badge).
 Merged here into one component with both features available, because
 a rebuild should be able to reach for either without re-deriving them.
 ========================================================================== */

.mom-section-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-lg); /* CHOSEN, V0.4.0: bumped from --mom-text-base (13px) to
    --mom-text-lg (16px). V0.3.0's panel headings sat in the same 6px band as everything
    else on the page (defect 1, "no type hierarchy"); this is the "panel headings 15-17px"
    tier the density brief calls for, still a real token from the existing scale. */
  letter-spacing: 1.6px;
  color: var(--mom-gold);
  text-transform: uppercase;
  margin-bottom: var(--mom-space-6);
  padding-bottom: var(--mom-space-4);
  border-bottom: 1px solid var(--mom-border-faint); /* [MEMBER] .plabel: solid rule */
}

/* [OPS] variant: dashed rule instead of solid, smaller size, no bottom
 margin baked in (OPS labels sit inside tighter sidebars). */
.mom-section-label--ops {
  font-size: var(--mom-text-sm);
  letter-spacing: 2.5px;
  border-bottom: 1px dashed var(--mom-border-ops);
  padding-bottom: var(--mom-space-2);
}

/* Caret marker. [OPS] .panel-head::before, line 358: content:'▸ ';
 color:var(--mv-cyan). The member dashboard has no caret; this is an
 OPS-only accent, ported as an optional modifier rather than forced
 onto every label. */
.mom-section-label--caret::before {
  content: "▸ ";
  color: var(--mom-cyan);
}

/* Numbered badge. [MEMBER] .plabel .num, line 863: background
 rgba(240,193,115,0.12); color gold; padding 3px 10px; border-radius
 5px; font-size 12px. */
.mom-section-label .mom-num {
  background: rgba(240, 193, 115, 0.12);
  color: var(--mom-gold);
  padding: 3px 10px;
  border-radius: var(--mom-radius-sm);
  font-size: 12px;
}

/* Panel-index badge (V1.2.0, JOB 1). Anric could not give feedback because panels were
 unlabelled ("you have not labeled the cards or the boxes"); production already numbers every
 panel top to bottom (docs/reference/v3-source/03_FEED-NOTE-REPLAYS.html line 35:
 <span class="num">05</span> RELEASE FEED; 02B_IDENTITY-ORBIT-ROTATION.html line 127:
 <span class="num">01</span> IDENTITY). Same exact visual as .mom-num directly above (byte-
 identical background/color/padding/radius/font-size, the .plabel .num pattern reused, not
 reinvented) but a DIFFERENT class: .mom-num stays reserved for live item counts on the right
 of a label (RELEASE FEED, REPLAYS); .mom-panel-num is the static left-hand ordinal (01-08)
 every panel now carries, sitting inside the label's own first <span> ahead of its text, same
 position the sourced markup uses. */
.mom-section-label .mom-panel-num {
  background: rgba(240, 193, 115, 0.12);
  color: var(--mom-gold);
  padding: 3px 10px;
  border-radius: var(--mom-radius-sm);
  font-size: 12px;
  margin-right: var(--mom-space-2);
}

/* Chrome markers (00a BANNER / 00b HEADER / 00c STATUS + SEARCH) were removed in V1.4.0.
 They were added in V1.2.0 only so Anric could name a region of the page chrome in feedback
 ("you have not labeled the cards or the boxes"). He has since given that feedback on the
 header, so the markers had served their purpose and were, in his words, part of what made
 the header read as "too much being jammed in there". The .mvp-chrome-marker rule that lived
 here is deleted along with its three call sites in index.html. The numbered badges on real
 addressable panels (.mom-panel-num, 01 through 08) are a different thing and stay. */

/* ==========================================================================
 5. BADGES & CHIPS
 [MEMBER] .tag.* family, line 1289-1298 (status badges: live/new/
 soon/coming/beta/update/publishing/upcoming/entry/primary/note).
 [OPS] .nav-icon, line 374-384 (two-letter squared category badge,
 ADMIN PORTALS sidebar list) and .chip, line 184-194 (header chip).
 NONE of these use a pill radius. The member CSS has one class
 literally named ".wb-card-pill" that nonetheless renders with
 border-radius: 3px - a square chip wearing an old name. Do not let
 the class name "pill" anywhere in the source imply 999px rounding.
 ========================================================================== */

.mom-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mom-font-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--mom-radius-xs); /* 3px - never pill */
  border: 1px solid transparent;
  line-height: 1.5;
}

/* Status variants, harvested colors verbatim from .tag.* */
.mom-badge--live {
  background: rgba(74, 222, 128, 0.18);
  color: var(--mom-green);
  border-color: rgba(74, 222, 128, 0.4);
}
.mom-badge--new {
  background: var(--mom-gold);
  color: #000;
  border-color: var(--mom-gold);
  font-weight: 700;
}
.mom-badge--update {
  background: rgba(74, 222, 128, 0.2);
  color: var(--mom-green);
  border-color: rgba(74, 222, 128, 0.4);
}
.mom-badge--coming {
  background: rgba(240, 193, 115, 0.15);
  color: var(--mom-gold);
  border-color: rgba(240, 193, 115, 0.4);
}
.mom-badge--beta {
  background: rgba(249, 115, 22, 0.22);
  color: var(--mom-orange);
  border-color: rgba(249, 115, 22, 0.55);
  font-weight: 700;
}
.mom-badge--publishing {
  background: rgba(168, 85, 247, 0.18);
  color: var(--mom-purple);
  border-color: rgba(168, 85, 247, 0.4);
}
.mom-badge--upcoming {
  background: rgba(96, 165, 250, 0.18);
  color: var(--mom-blue);
  border-color: rgba(96, 165, 250, 0.4);
}
.mom-badge--primary {
  background: rgba(240, 193, 115, 0.2);
  color: var(--mom-gold);
  border-color: rgba(240, 193, 115, 0.4);
}
.mom-badge--note {
  background: rgba(255, 255, 255, 0.06);
  color: #aaa;
  border-color: rgba(255, 255, 255, 0.08);
}
/* CHOSEN: dead-link status variant, needed by mvp-dashboard's link-health feature.
 Neither source has a "dead" tag/status; this extends the same family with the
 already-harvested --mom-red token rather than inventing a new color. */
.mom-badge--dead {
  background: var(--mom-red-dim);
  color: var(--mom-red);
  border-color: rgba(220, 20, 60, 0.5);
}

/* Two-letter squared category badge. [OPS] .nav-icon, line 374-384:
 28x28 fixed box, gold-wash fill, 3px radius, heading font at 11px.
 Used in the ADMIN PORTALS sidebar list for two-letter category codes
 (MV, ST, CL, IF, AI, LV, MN, SE, EM, UT, MX). */
.mom-badge-square {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  background: var(--mom-gold-dim);
  border: 1px solid var(--mom-border-ops);
  border-radius: var(--mom-radius-xs);
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: 11px;
  color: var(--mom-gold-ops);
  letter-spacing: 1px;
  flex-shrink: 0;
}

/* Header chip. [OPS] .chip, line 184-194. */
.mom-chip {
  display: inline-block;
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: 10px;
  letter-spacing: 2px;
  padding: 4px 10px;
  border: 1px solid var(--mom-cyan);
  color: var(--mom-cyan);
  background: var(--mom-cyan-dim);
  border-radius: var(--mom-radius-xs);
}

/* ==========================================================================
 6. DATA DISPLAY - telemetry meter, tabular figures
 [OPS] .vital / .vital-row / .vital-bar / .vital-fill, line 407-431.
 A labeled thin meter bar that "breathes" (width oscillates a few
 percent around its real value on a 4s loop) rather than sitting
 static. This is the FLEET TELEMETRY sidebar pattern (WORKERS LIVE,
 REQUESTS, ERRORS, UPTIME, REQ/DAY, CF COST). Not to be confused with
 the GHL page-builder's generic .progress-outer/.progress-inner widget
 found elsewhere in live-dash-css.css - that is vendor boilerplate,
 unrelated to this system, and was not ported.
 ========================================================================== */

.mom-meter {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.mom-meter-row {
  display: flex;
  justify-content: space-between;
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  letter-spacing: 1.5px;
  color: var(--mom-text-muted);
  text-transform: uppercase;
}
.mom-meter-row b {
  color: var(--mom-text);
  font-weight: 400;
  font-variant-numeric: tabular-nums; /* CHOSEN, see section 2 note */
}
.mom-meter-track {
  height: 4px;
  background: rgba(250, 189, 89, 0.08);
  border-radius: 1px;
  position: relative;
  overflow: hidden;
}
.mom-meter-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: var(--mom-lvl, 50%);
  background: linear-gradient(90deg, var(--mom-gold-dark), var(--mom-gold-ops));
  box-shadow: 0 0 8px rgba(250, 189, 89, 0.6);
  animation: mom-meter-flux var(--mom-dur-meter) ease-in-out infinite;
}
@keyframes mom-meter-flux {
  0%,
  100% {
    width: var(--mom-lvl, 50%);
  }
  50% {
    width: calc(var(--mom-lvl, 50%) + 6%);
  }
}

/* Stat figure. [MEMBER] .stat .v / .stat .l, line 920-922: the largest
 numeral treatment in the system (28px, Russo One, gold), paired with
 a micro-label underneath. */
.mom-stat {
  background: var(--mom-bg-inset);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: var(--mom-radius-lg);
  padding: 12px 14px;
  text-align: center;
}
.mom-stat .mom-stat-value {
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: var(--mom-text-4xl);
  color: var(--mom-gold);
  line-height: 1;
  font-variant-numeric: tabular-nums; /* CHOSEN */
}
.mom-stat .mom-stat-label {
  font-family: var(--mom-font-mono);
  font-size: 11.5px;
  color: var(--mom-text-muted);
  text-transform: uppercase;
  letter-spacing: 1.2px;
  margin-top: 6px;
}

/* ==========================================================================
 7. MOTION
 Fixed estate tempos only (see tokens: --mom-dur-slow/mid/fast/beam/
 blink). Named keyframes below map 1:1 to a real production animation;
 none of these durations were invented for this file.
 ========================================================================== */

/* Blink - [OPS] #maestroStopBtn.is-active, line 1144: "animation: pulse
 1.5s ease-in-out infinite". Canonical blink tempo. Also present as
 [MEMBER] .pulse-dot / cmd-blink at a close-but-not-identical 1.4s/2s
 (both real production values, listed here as the exact 1.5s canon
 plus the member's own near-variants for anyone matching pixel-for-
 pixel against the live dashboard). */
@keyframes mom-blink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.3;
  }
}
.mom-motion-blink {
  animation: mom-blink var(--mom-dur-blink) ease-in-out infinite;
}

/* Live status dot with glow. [MEMBER] .welcome-pip / .pulse-dot, line
 589-595, 864. */
.mom-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--mom-green);
  box-shadow:
    0 0 12px var(--mom-green),
    0 0 24px rgba(74, 222, 128, 0.5);
  animation: mom-blink var(--mom-dur-blink) ease-in-out infinite;
}
.mom-dot--red {
  background: var(--mom-red);
  box-shadow: 0 0 8px var(--mom-red);
}

/* Pulse ring - [MEMBER] .core-node .ring / cmd-pulse-ring, line
 1165-1168: an expanding, fading ring, used staggered (0s/1s/2s
 delays on three stacked rings) to read as a continuous pulse. */
@keyframes mom-pulse-ring {
  0% {
    transform: scale(1);
    opacity: 0.8;
  }
  100% {
    transform: scale(1.6);
    opacity: 0;
  }
}
.mom-motion-pulse-ring {
  animation: mom-pulse-ring 3s ease-in-out infinite;
}

/* Orbit / spin family - [OPS] .ring-spin-cw/.ring-spin-ccw/.ring-spin-fast,
 line 495-497, and the radar-blip drift tiers (line 1496-1503) which
 use exactly --drift-dur: 9s / 14s / 22s across the eight blips. These
 three durations ARE the canonical slow/mid/fast tempos. */
@keyframes mom-spin {
  to {
    transform: rotate(360deg);
  }
}
.mom-motion-orbit-slow {
  animation: mom-spin var(--mom-dur-slow) linear infinite;
}
.mom-motion-orbit-mid {
  animation: mom-spin var(--mom-dur-mid) linear infinite;
}
.mom-motion-orbit-fast {
  animation: mom-spin var(--mom-dur-fast) linear infinite;
}
.mom-motion-orbit-ccw {
  animation: mom-spin 30s linear infinite reverse;
} /* [OPS] .ring-spin-ccw exact value, kept separate from canon */
.mom-motion-orbit-mid-ccw {
  animation: mom-spin var(--mom-dur-mid) linear infinite reverse;
} /* CHOSEN, V0.4.0: the 14s tempo counter-rotating, needed so the orb placeholder's two
     rings visibly counter-rotate on the two canonical tempos (22s/14s) instead of pairing
     22s against the unrelated 30s ccw value above. */
.mom-motion-orbit-beam {
  animation: mom-spin var(--mom-dur-beam) linear infinite;
} /* CHOSEN, V0.4.0: reuses the canonical beam tempo (6s) and the existing mom-spin
     keyframe for a rotating sweep, rather than the horizontal mom-motion-beam shape,
     for the orb placeholder's beam sweep. */

/* Beam sweep - [OPS] .head::after / sweepLine, line 141-151: a
 horizontal hairline under the header that brightens and widens on a
 6s loop. THE canonical beam tempo. */
@keyframes mom-beam-sweep {
  0%,
  100% {
    opacity: 0.4;
    transform: scaleX(0.8);
  }
  50% {
    opacity: 1;
    transform: scaleX(1);
  }
}
.mom-motion-beam {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent,
    var(--mom-gold-ops) 50%,
    transparent
  );
  animation: mom-beam-sweep var(--mom-dur-beam) ease-in-out infinite;
  pointer-events: none;
}

/* Hairline shimmer - [OPS] .sidebar::before / borderSlide, line
 339-347: a short gradient segment that slides in from the left edge
 and fades, 4s loop. Used at the top of the ADMIN PORTALS sidebar. */
@keyframes mom-hairline-shimmer {
  0%,
  100% {
    transform: translateX(-50%);
    opacity: 0;
  }
  50% {
    transform: translateX(0);
    opacity: 1;
  }
}
.mom-motion-hairline {
  position: absolute;
  top: 0;
  left: 10%;
  right: 10%;
  height: 2px;
  background: linear-gradient(
    90deg,
    transparent,
    var(--mom-gold-ops),
    transparent
  );
  animation: mom-hairline-shimmer var(--mom-dur-hairline) ease-in-out infinite;
  pointer-events: none;
}

/* Current-state glow - [MEMBER] mvp-current-glow-small, line 2389-2392:
 used on the ecosystem gear cluster to mark the node the member is
 "on" right now, distinct from done (steady) and locked (grayscale). */
@keyframes mom-glow-pulse {
  0%,
  100% {
    filter: drop-shadow(0 0 12px rgba(240, 193, 115, 0.45))
      drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6));
  }
  50% {
    filter: drop-shadow(0 0 26px rgba(250, 189, 89, 0.85))
      drop-shadow(0 0 44px rgba(240, 193, 115, 0.4))
      drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6));
  }
}
.mom-motion-glow {
  animation: mom-glow-pulse var(--mom-dur-glow) ease-in-out infinite;
}

/* Log/feed line entrance - [OPS] .log-line / log-in, line 452-458: a
 one-shot (not looping) fade + slide-in used when a new line appears
 in the live log window. */
@keyframes mom-line-in {
  from {
    opacity: 0;
    transform: translateX(-4px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}
.mom-motion-line-in {
  animation: mom-line-in 0.4s forwards;
}

/* Ambient gear rotation - [MEMBER] mvp-gear-spin-cw/ccw, line 2401-2405:
 the atmosphere gear cluster rotates very slowly (60s), big nodes
 clockwise, small nodes counter-clockwise, "the way real interlocked
 gear trains turn" per the source comment. Paused on hover so a label
 can be read. This tempo is intentionally NOT one of the five
 canonical durations - it is slower on purpose, for background
 atmosphere rather than a status signal. */
@keyframes mom-gear-spin-cw {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
@keyframes mom-gear-spin-ccw {
  from {
    transform: rotate(360deg);
  }
  to {
    transform: rotate(0deg);
  }
}
.mom-motion-gear-cw {
  animation: mom-gear-spin-cw var(--mom-dur-gear) linear infinite;
}
.mom-motion-gear-ccw {
  animation: mom-gear-spin-ccw var(--mom-dur-gear) linear infinite;
}

/* Reduced motion - [MEMBER] carries its own reduced-motion guard for the
 gear cluster (line 2406-2408); this block generalizes that same
 pattern to every animation class this file defines. */
@media (prefers-reduced-motion: reduce) {
  .mom-motion-blink,
  .mom-dot,
  .mom-motion-pulse-ring,
  .mom-motion-orbit-slow,
  .mom-motion-orbit-mid,
  .mom-motion-orbit-fast,
  .mom-motion-orbit-ccw,
  .mom-motion-orbit-mid-ccw,
  .mom-motion-orbit-beam,
  .mom-motion-beam,
  .mom-motion-hairline,
  .mom-motion-glow,
  .mom-motion-line-in,
  .mom-motion-gear-cw,
  .mom-motion-gear-ccw,
  .mom-meter-fill {
    animation: none !important;
  }
}

/* ==========================================================================
 8. ATMOSPHERE - the dark background
 Neither source's dark background is a single image asset doing all
 the work. Both build it from cheap CSS: a near-black base, one or two
 large soft radial glows, and (OPS only) a faint repeating grid line
 pattern. [OPS] does load one very-low-opacity (0.08) background
 photograph (body::before, line 104-110, a remote CDN-hosted JPEG,
 same asset host the orb's own images were on before section 10
 self-hosted them) as a texture layer UNDER the grid - that single
 external image is the only real photographic texture found in either
 source, and
 it is decorative at 8% opacity, not load-bearing. Since external
 requests are blocked here, it is dropped and replaced with a pure-CSS
 equivalent below: a second, dimmer radial gradient standing in for
 "a photograph ghosted into the dark." Nothing to apologize for - at
 8% opacity the original photo was already nearly indistinguishable
 from a soft vignette. ========================================================================== */

.mom-atmosphere {
  position: relative;
  background: var(--mom-bg-page);
}

/* Grid line texture. [OPS] body::after, line 111-119: two repeating
 linear-gradients at 60x60px forming a faint grid, tinted gold at
 6% opacity (--mv-grid). */
.mom-atmosphere::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(rgba(250, 189, 89, 0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(250, 189, 89, 0.06) 1px, transparent 1px);
  background-size: 60px 60px;
  pointer-events: none;
}

/* Photograph-substitute glow. CHOSEN pure-CSS stand-in for [OPS]
 body::before's 8%-opacity remote JPEG, and doubles as the [MEMBER]
 .bg-glow-1/.bg-glow-2 pattern (line 564-568: two 540px blurred
 radial blobs, gold and crimson, drifting on a 16s/20s float). */
.mom-atmosphere::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(
      circle at 15% 10%,
      rgba(240, 193, 115, 0.16),
      transparent 45%
    ),
    radial-gradient(circle at 85% 90%, rgba(220, 20, 60, 0.12), transparent 45%);
  filter: blur(60px);
  opacity: 0.6;
  pointer-events: none;
}

/* [MEMBER] drifting glow blob, for use inside an individual panel rather
 than page-wide (e.g. behind a welcome strip or hero card). */
@keyframes mom-glow-float {
  0%,
  100% {
    transform: translate(0, 0) scale(1);
  }
  50% {
    transform: translate(40px, 30px) scale(1.12);
  }
}
.mom-glow-blob {
  position: absolute;
  width: 540px;
  height: 540px;
  border-radius: 50%;
  filter: blur(120px);
  opacity: 0.22;
  pointer-events: none;
  animation: mom-glow-float 16s ease-in-out infinite;
}
.mom-glow-blob--gold {
  background: radial-gradient(
    circle,
    rgba(240, 193, 115, 0.55),
    transparent 70%
  );
}
.mom-glow-blob--red {
  background: radial-gradient(circle, rgba(220, 20, 60, 0.45), transparent 70%);
  animation-duration: 20s;
  animation-direction: reverse;
}
/* CHOSEN, V0.4.0: cool-tone counterpart to --gold, using --mom-blue (the member
   dashboard's actual cool accent - not cyan, which is OPS-only and banned on this
   surface). Fixes defect 2 ("no atmosphere"): the brief calls for gold + a cool tone,
   not gold + crimson twice. */
.mom-glow-blob--blue {
  background: radial-gradient(circle, rgba(96, 165, 250, 0.4), transparent 70%);
  animation-duration: 20s;
  animation-direction: reverse;
}

/* CHOSEN, V0.6.1: the single fixed, full-viewport, overflow:hidden wrapper for every
   page-atmosphere decoration (both glow blobs + the gear texture below). Replaces each
   decoration carrying its own position:fixed (see .mom-atmosphere-blob / .mom-gear-texture
   below, both now position:absolute relative to THIS wrapper). Why: a position:fixed
   element's containing block is always the viewport, no matter what ancestor wraps it, so
   overflow:hidden on an ancestor (even html/body) never clips a fixed descendant's excess -
   that is exactly what let a 640px-wide .mom-gear-texture at right:-280px paint out to
   x=670px on a 390px viewport (real incident 2026-07-30, document.documentElement.scrollWidth
   measured at 670). position:absolute descendants, by contrast, ARE clipped by an
   overflow:hidden ancestor. This wrapper is that ancestor: fixed to the viewport (inset:0,
   so it behaves exactly like the old per-element position:fixed for visibility/placement
   purposes) and overflow:hidden (so any decoration inside that would paint past the
   viewport edge is genuinely clipped instead of inflating document scrollWidth). */
.mom-atmosphere-decor {
  position: fixed;
  inset: 0;
  overflow: hidden;
  z-index: -1;
  pointer-events: none;
}

/* CHOSEN, V0.4.0: page-wide atmosphere modifier. The base .mom-glow-blob component is
   documented above as scoped-inside-a-panel (position: absolute); mvp-dashboard's V0.3.0
   page-level blobs used that same absolute positioning directly on <body>, which put them
   in normal document flow and left them clipped/hidden behind the opaque HUD header and
   preview banner (measured: not visible on the rendered page). This modifier reattaches
   the same blob to the viewport instead of the document, and sends it behind all content,
   without changing the base component other call sites may still use inside a panel.
   V0.6.1: position is absolute (not fixed) so it is contained/clipped by the new
   .mom-atmosphere-decor wrapper above rather than escaping to the viewport's own
   containing block; because the wrapper is itself position:fixed + inset:0, the visible
   placement is unchanged. */
.mom-atmosphere-blob {
  position: absolute;
}

/* CHOSEN, V0.4.0: gear/concentric-arc texture layer, defect 2. Neither source's texture
   is an image; this follows the same recipe as .mom-atmosphere::after (a repeating CSS
   gradient, no external request) but shaped like gear teeth (repeating-conic-gradient)
   crossed with concentric rings (repeating-radial-gradient) instead of a flat grid, so it
   reads as "gear cluster ghosted into the dark" rather than graph paper. Reuses the
   already-defined mom-gear-spin-cw keyframe/tempo (section 7) so this layer can rotate at
   the ambient 60s tempo when combined with .mom-motion-gear-cw, which V0.3.0 defined but
   never actually placed on anything visible.
   V0.6.1: position is absolute (not fixed), contained by the .mom-atmosphere-decor wrapper
   (position:fixed, inset:0, overflow:hidden) instead of being fixed itself. This element's
   own right:-280px + width:640px combination is exactly what produced the 390px-viewport
   overflow regression (right edge painted at 390 + 280 = 670px, matching the traced
   document.documentElement.scrollWidth of 670 exactly) because a position:fixed element's
   containing block is the viewport regardless of ancestors, so no ancestor overflow:hidden
   (including the pre-existing html/body rule in app.css) could clip it. Left at the same
   640px/-280px geometry (the visual design is unchanged and still correct as a decoration
   partially off-screen) since the wrapper's overflow:hidden now genuinely contains the
   excess instead of merely hiding a symptom at the body level. */
.mom-gear-texture {
  position: absolute;
  top: 50%;
  right: -280px;
  width: 640px;
  height: 640px;
  margin-top: -320px;
  border-radius: 50%;
  opacity: 0.05;
  pointer-events: none;
  background:
    repeating-conic-gradient(
      from 0deg,
      rgba(240, 193, 115, 0.9) 0deg 3deg,
      transparent 3deg 16deg
    ),
    repeating-radial-gradient(
      circle,
      rgba(240, 193, 115, 0.6) 0px 1px,
      transparent 1px 46px
    );
}

@media (prefers-reduced-motion: reduce) {
  .mom-glow-blob {
    animation: none !important;
  }
}

/* ==========================================================================
 9. DASHBOARD SHELL - mvp-dashboard V0.3.0 additions
 CHOSEN compositions, not harvested 1:1 from either source: a full HUD
 header bar, a world-clock ribbon, a dense instrument-style row list and
 an ADMIN-PORTALS-idiom facet rail. Neither production surface ships
 these exact shapes, but every color/radius/spacing/motion value used
 below still traces to a token in section 1 - nothing here invents a
 new visual language, it only assembles the existing one into shapes
 this page needs that the two source surfaces did not have to build.
 Added here (rather than as one-off rules in app.css) so the next page
 built on Momentum can reuse them instead of re-deriving them.
 ========================================================================== */

/* HUD header bar. OPS chrome idiom: flat panel, --mom-gold-ops accent
 (header chrome gold, not body gold), dense, a beam sweep hairline
 riding the bottom edge (.mom-motion-beam, inserted as a child). */
.mom-hud {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--mom-space-6);
  flex-wrap: wrap;
  padding: var(--mom-space-4) var(--mom-space-7);
  background: var(--mom-bg-ops-wash);
  border-bottom: 1px solid var(--mom-border-ops);
}

.mom-hud-brand {
  display: flex;
  align-items: center;
  gap: var(--mom-space-3);
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: var(--mom-text-lg);
  letter-spacing: 1.5px;
  color: var(--mom-gold-ops);
  text-transform: uppercase;
  white-space: nowrap;
}

.mom-hud-nav {
  display: flex;
  align-items: center;
  gap: var(--mom-space-5);
  flex-wrap: wrap;
}

.mom-hud-nav a {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--mom-text-muted);
  text-decoration: none;
}
.mom-hud-nav a:hover,
.mom-hud-nav a:focus-visible {
  color: var(--mom-gold-ops);
}

/* Keyboard-hint chip, the visual affordance for the Cmd+K palette. */
.mom-kbd {
  display: inline-flex;
  align-items: center;
  font-family: var(--mom-font-mono);
  font-size: 10px;
  letter-spacing: 0.5px;
  color: var(--mom-text-muted);
  border: 1px solid var(--mom-border-ops);
  border-radius: var(--mom-radius-xs);
  padding: 2px 6px;
}

.mom-hud-status {
  display: flex;
  flex-wrap: wrap; /* V0.6.1 regression fix: was nowrap (the default), which forced this row's
    five children (clock, countdown, two dividers, the "PREVIEW, NOT SIGNED IN" state, and
    the account link) onto one unbreakable line. Real incident 2026-07-30: at 390px that
    line measured 658px wide (position:static, in normal flow), the second confirmed source
    of the traced 670px document overflow alongside the gear-texture/blobs fix above. The
    parent .mom-hud already had flex-wrap:wrap so .mom-hud-status itself could move to its
    own row, but its own content still could not break internally without this. Wrapping
    here (rather than shrinking text or truncating) keeps every status string fully
    legible - "PREVIEW, NOT SIGNED IN" in particular must never be clipped or ellipsized. */
  align-items: center;
  justify-content: flex-end;
  row-gap: var(--mom-space-2);
  gap: var(--mom-space-5);
  min-width: 0; /* V0.6.1: lets this flex item shrink below its content's intrinsic width
    instead of forcing .mom-hud wider than the viewport (flex items default to min-width:auto,
    which ignores flex-wrap and keeps demanding full content width). */
  max-width: 100%;
  margin-left: auto;
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--mom-text-muted);
}
.mom-hud-clock,
.mom-hud-countdown b {
  font-variant-numeric: tabular-nums; /* CHOSEN, see section 2 note */
  color: var(--mom-text);
}

/* V0.6.0 job 5: header auth-surface placeholder. Styled in the header-chrome gold
 (--mom-gold-ops), same family as the rest of .mom-hud-status, not the body gold. Where the
 real MVP Universal Header would show a member's name/tier/contact id/counters, this always
 renders the literal "PREVIEW, NOT SIGNED IN" string - never a name, never a number. */
.mom-hud-divider {
  color: var(--mom-text-dim);
}
.mom-hud-preview-state {
  color: var(--mom-gold-ops);
  font-weight: 700;
}
.mom-hud-account-link {
  color: var(--mom-text-muted);
  text-decoration: none;
  border: 1px solid var(--mom-border-ops);
  border-radius: var(--mom-radius-xs);
  padding: 2px 8px;
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  letter-spacing: 1.2px;
}
.mom-hud-account-link:hover,
.mom-hud-account-link:focus-visible {
  color: var(--mom-gold-ops);
  border-color: var(--mom-gold-ops);
}

/* Dense instrument-readout row list. [Item 5, "catalog surface"]: tighter
 rows than .mom-panel content, 1px hairline separators (not the 22px
 card-wall spacing a marketing grid would use), monospace figures for
 every count. Modeled on the density of OPS .log-line / .status-row,
 recolored to the member gold. */
.mom-rowlist {
  display: flex;
  flex-direction: column;
}
.mom-row {
  display: grid;
  grid-template-columns: auto 1fr 128px 24px; /* CHOSEN, V0.4.0: fixed widths on the tag and
    link columns instead of auto. Each .mom-row is its own independent grid (not rows of
    one shared table), so "auto" sizing let every row's tag/link column width drift with
    that row's own content, i.e. the ragged right edge defect 5 names. Fixed widths make
    every row's meta columns land in the same place. */
  align-items: center;
  gap: var(--mom-space-4);
  padding: 8px var(--mom-space-4);
  border-bottom: 1px solid var(--mom-border-faint);
  border-left: 2px solid transparent; /* status stripe, colored by .mom-row--status-* below */
  font-size: var(--mom-text-xs);
  color: var(--mom-text-body);
  text-decoration: none;
  transition:
    background-color 0.15s ease,
    border-left-color 0.15s ease,
    box-shadow 0.15s ease,
    transform 0.15s ease;
}
.mom-row:last-child {
  border-bottom: 0;
}
.mom-row:hover,
.mom-row:focus-visible {
  background: var(--mom-gold-dim);
  transform: translateY(-1px); /* the "lift" defect 5 asks for */
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.35);
}
.mom-row--highlight {
  background: var(--mom-gold-dim);
  border-left-color: var(--mom-gold);
  border-left-width: 3px;
}
/* Status stripe colors, one per catalog status, matching .status-dot-* in app.css so the
   stripe and the badge always agree. */
.mom-row--status-live {
  border-left-color: var(--mom-green);
}
.mom-row--status-new {
  border-left-color: var(--mom-gold);
}
.mom-row--status-beta {
  border-left-color: var(--mom-orange);
}
.mom-row--status-coming_soon,
.mom-row--status-in_development {
  border-left-color: var(--mom-text-dim);
}
.mom-row--status-migrating {
  border-left-color: var(--mom-blue);
}
.mom-row--status-dead {
  border-left-color: var(--mom-red);
}
/* Brighten the stripe on hover/focus: wider + a matching glow bleeding off the edge. */
.mom-row--status-live:hover,
.mom-row--status-live:focus-visible {
  border-left-width: 3px;
  box-shadow: -2px 0 10px rgba(74, 222, 128, 0.5);
}
.mom-row--status-new:hover,
.mom-row--status-new:focus-visible {
  border-left-width: 3px;
  box-shadow: -2px 0 10px rgba(240, 193, 115, 0.5);
}
.mom-row--status-beta:hover,
.mom-row--status-beta:focus-visible {
  border-left-width: 3px;
  box-shadow: -2px 0 10px rgba(251, 146, 60, 0.5);
}
.mom-row--status-migrating:hover,
.mom-row--status-migrating:focus-visible {
  border-left-width: 3px;
  box-shadow: -2px 0 10px rgba(96, 165, 250, 0.5);
}
.mom-row--status-dead:hover,
.mom-row--status-dead:focus-visible {
  border-left-width: 3px;
  box-shadow: -2px 0 10px rgba(220, 20, 60, 0.5);
}
.mom-row-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--mom-text-md); /* CHOSEN, V0.4.0: 14px, the primary "body/row text" tier.
    Previously unset and inheriting .mom-row's 11px - the single biggest contributor to the
    V0.3.0 "everything in a 6px band" measurement, since this text renders once per row. */
  color: var(--mom-text);
}
.mom-row:hover .mom-row-name,
.mom-row:focus-visible .mom-row-name {
  color: var(--mom-gold);
}
.mom-row-tags {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-base); /* CHOSEN, V0.4.0: 13px, body tier (was 10px). Measured
    after the first pass: badges alone (138 of them, correctly micro at 10.5px) were
    enough to anchor the page median in the micro band even with row-name/row-purpose
    bumped. The category tag is real secondary content, not a micro decoration, so it
    moves to the same body tier as the purpose column. */
  letter-spacing: 0.6px;
  color: var(--mom-text-muted);
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The arrow glyph that replaces the old always-on "OPEN" text (defect 5): hidden until
   the row is hovered or focused, since the whole row is now the click target and a
   persistent link label on every row was noise. .mom-row-link--static (dead/no-url rows,
   which are not clickable) stays visible instead of revealing on hover. */
.mom-row-link {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  letter-spacing: 0.6px;
  color: var(--mom-gold);
  text-transform: uppercase;
  white-space: nowrap;
  text-align: right;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.mom-row:hover .mom-row-link,
.mom-row:focus-visible .mom-row-link {
  opacity: 1;
}
.mom-row-link--static {
  opacity: 1;
  color: var(--mom-text-dim);
}

/* Facet rail. [Item 5]: OPS ADMIN PORTALS idiom verbatim - two-letter
 squared badge (.mom-badge-square, section 5) + label + count, one per
 row, dense. */
.mom-facet-rail {
  display: flex;
  flex-direction: column;
}
.mom-facet-row {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  align-items: center;
  gap: var(--mom-space-3);
  width: 100%;
  padding: 6px var(--mom-space-2);
  border: none;
  border-bottom: 1px solid var(--mom-border-faint);
  background: transparent;
  color: var(--mom-text-body);
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-base); /* CHOSEN, V0.4.0: bumped from 11px to 13px (body tier);
    facet labels are read-and-clicked constantly, they belong at the same weight as row
    text, not down with the micro badges. */
  letter-spacing: 0.8px;
  text-transform: uppercase;
  text-align: left;
  cursor: pointer;
}
.mom-facet-row:last-child {
  border-bottom: 0;
}
.mom-facet-row:hover,
.mom-facet-row:focus-visible {
  background: var(--mom-gold-dim);
}
.mom-facet-row[aria-pressed="true"] {
  background: var(--mom-gold-dim);
  color: var(--mom-gold);
}
.mom-facet-row[aria-pressed="true"] .mom-badge-square {
  background: var(--mom-gold);
  color: #000;
  border-color: var(--mom-gold);
}
.mom-facet-row-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mom-facet-row-count {
  font-variant-numeric: tabular-nums; /* CHOSEN */
  font-size: var(--mom-text-2xs); /* CHOSEN, V0.4.0: 8px, deliberately the smallest micro
    tier (a secondary count next to a now-larger label), matching the reference
    dashboard's 8px floor. */
  color: var(--mom-text-muted);
}

/* World clock ribbon. Both source surfaces close with a dense strip of
 read-only data (OPS: fleet telemetry; MEMBER: whats-new feed); this is
 that same "instrument strip" idiom applied to seven timezones. */
.mom-worldclock {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
}
.mom-worldclock-cell {
  flex: 1 1 120px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: var(--mom-space-3) var(--mom-space-4);
  border-right: 1px solid var(--mom-border-faint);
}
.mom-worldclock-cell:last-child {
  border-right: 0;
}
.mom-worldclock-city {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-2xs); /* CHOSEN, V0.4.0: 8px, the true micro floor. */
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--mom-text-muted);
}
.mom-worldclock-time {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-base);
  font-variant-numeric: tabular-nums; /* CHOSEN */
  color: var(--mom-gold);
}

/* Placeholder panel. Used for the identity block (no member data this
 wave) and the orb reservation. Dashed border marks it explicitly as
 "not the real thing," distinct from every solid-bordered live panel. */
.mom-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--mom-space-3);
  text-align: center;
  border: 1px dashed var(--mom-border);
  border-radius: var(--mom-radius-panel);
  padding: var(--mom-space-7);
  color: var(--mom-text-muted);
}
.mom-placeholder-label {
  font-family: var(--mom-font-mono);
  font-size: 10px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--mom-text-muted);
}

/* Compact top-row panels. CHOSEN, V0.4.0, defect 3 ("top third is a void"): identity and
   orb previously used the default .mom-panel padding (20px/22px) meant for content-heavy
   panels, which is most of why they read as large and empty. Same panel recipe, tighter
   padding, so a third panel (telemetry) fits beside them without the row feeling cramped. */
.mom-panel.mom-panel--compact {
  padding: var(--mom-space-6) var(--mom-space-6);
}

/* Catalog telemetry panel. [Item 3]: the ops console's FLEET TELEMETRY idiom (labelled
   rows, a figure, a thin breathing meter bar - .mom-meter, section 6) ported onto real,
   independently-sourced catalog counts instead of OPS's hardcoded --lvl percentages. One
   hero figure (TOTAL ITEMS) at the new --mom-text-5xl tier anchors the panel; every other
   row is a label + tabular figure + optional meter, sized down from there. */
.telemetry-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding-bottom: var(--mom-space-4);
  margin-bottom: var(--mom-space-4);
  border-bottom: 1px solid var(--mom-border-faint);
}
.telemetry-hero-figure {
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: var(--mom-text-5xl);
  color: var(--mom-gold);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.telemetry-hero-label {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-2xs);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--mom-text-muted);
}
.telemetry-meter-list {
  display: flex;
  flex-direction: column;
  gap: var(--mom-space-4);
}
.telemetry-row .mom-meter-row {
  font-size: var(--mom-text-base);
  color: var(--mom-text-soft);
  text-transform: none;
  letter-spacing: 0.4px;
}
.telemetry-figure {
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: var(--mom-text-2xl); /* section-title tier (20-24px), shared with the hero's
    six sibling figures so this bucket alone clears the "5+ elements" bar. */
  color: var(--mom-gold) !important;
}

/* ==========================================================================
 10. ORB (GEAR CLUSTER) - mvp-dashboard V0.8.0
 [MEMBER] .gear-cluster-zone / .gear-cluster / .gear / .gear-disc / .gear-label /
 .gear-center / .corner-shortcut / .leader-lines, PORTED VERBATIM 2026-07-30 from the
 real GoHighLevel section source now vendored at
 docs/reference/v3-source/02A_STYLES.html (V3.9.14 CSS host) and
 docs/reference/v3-source/02B_IDENTITY-ORBIT-ROTATION.html (V3.9.22 markup), supplied
 directly rather than reverse-engineered from the rendered page. This replaces the
 earlier V0.7.1 pass, which was reconstructed by curling the live page's computed
 output and guessing at two things it got wrong: the four corner shortcuts were built
 as CSS-shape text chips instead of real <img> elements, and the --cluster-bg texture
 was an invented pure-CSS gear-ring gradient because no real background asset existed
 yet. Both are fixed below using the real source values and the now-available local
 asset files (public/assets/orb/corner-*.webp, cluster-bg.webp). Ring geometry
 (pos-n/e/s/w/ne/se/sw/nw at 13%/87%/50% and 26%/74%), the round-disc + big/small
 sizing split (28%/22%), the hover-reveal label, and the three status treatments
 (done/current/locked) remain HARVESTED VERBATIM as in V0.7.1 - these are Anric's own
 tuned values from the live source, not approximations.

 V0.8.0 corrections found by re-checking every value directly against 02A instead of
 trusting this section's own prior comments:
 - .gear-cluster max-width was 460px; 02A line 1783 (V3.9.14 host numbering) reads
   500px. Fixed below.
 - .gear-disc's glow filter was one shared value (the BIG figure) applied to every
   gear regardless of size. 02A carries genuinely distinct BIG (.gear.big .gear-disc)
   and SMALL (.gear.small .gear-disc) filter values for base/hover/done/current/locked
   - smaller glow radius and softer opacity on the small nodes. Split below, applied
   to .gear-disc-inner (this build's hover-scale wrapper, not present in the source
   DOM - see that rule's own comment for why the wrapper exists; the filter VALUES
   themselves are still the source's, only the element they land on differs).
 - Hover scale was invented at 1.09 + a translateY(-3px) lift ("CHOSEN" per the old
   comment). Source is exactly `transform: scale(1.07)`, no lift. Reverted to that.
 - The DONE/CURRENT/LOCKED "icon" on the label was invented (checkmark U+2713,
   padlock U+1F512, a green box-shadow token). The source never adds an icon glyph -
   the ONLY per-state indicator is the small dot already on .gear-label::before,
   recolored: done = solid green #4ade80, current = gold #F0C173 pulsing via
   mvp-dot-pulse (opacity 1 -> 0.45, 1.8s), locked = dim gray rgba(140,140,150,0.35).
   Fixed below; the checkmark/padlock content and the done green-box-shadow filter
   token are removed.
 - .corner-shortcut was styled as a 58x58 text chip with a background, border, and
   uppercase label (no source icon assets existed at the time). Source is an 86x86
   plain image tile - transparent background, no border, no padding, the icon PNG
   itself supplies the name - with a drop-shadow filter and a scale(1.08) hover. The
   four PNGs now exist locally (public/assets/orb/corner-*.webp) so this is now a real
   image tile, matching source exactly. Markup moved from static HTML into
   buildOrbChrome() in app.js, rendered from data/orb.json's "corners" array.
 - .leader-lines styled <line> elements (2px dasharray, 0.35 opacity, 9s flow). Source
   draws 4 quadratic-bezier <path> elements from the center to each corner (see 02B
   lines 172-175, ported verbatim into index.html) styled at stroke-width 1.6,
   rgba(240,193,115,0.65), dasharray "5 4", 4s linear flow. Fixed below.
 - --cluster-bg now resolves to a real local file (assets/orb/cluster-bg.webp, set via
   app.js from data/orb.json's "background" entry) instead of the invented pure-CSS
   gradient fallback; the ::after rule's z-index reverts from -1 (invented, to force it
   behind this build's own .orb-globe-layer addition, not present in source) back to
   the harvested 0, matching 02A line 1770-1777 exactly - the fix for any apparent
   stacking collision with .orb-globe-layer is not needed at 0.18 opacity in practice,
   per the "do not adjust anything that looks like it collides" instruction; if a real
   collision ever surfaces, fix it by re-reading source again, not by re-inventing a
   z-index here.

 Everything already correct in V0.7.1 and unchanged here, still HARVESTED VERBATIM:
 the live source's real gear-disc images spin in place at a fixed compass position
 (60s, bigs clockwise / smalls counter-clockwise, "the way real interlocked gear
 trains turn" per the source's own comment), not the whole cluster as a unit. The
 centre-ring decoration is two pseudo-elements directly on .gear-center (dashed 22s
 cw / dotted 38s ccw). The centre sits at 44%, the gears at 28%/22%, positions at the
 harvested 13%/87%/26%/74%, with zero overlap. These durations (60s gear spin,
 22s/38s centre rings, 2.6s current-glow, 4s leader-line flow) are the live
 component's own real values, not the estate's generic tempo list, for this
 component only, per the source. */

.gear-cluster-zone {
  position: relative;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--mom-space-4) 0 var(--mom-space-6);
  overflow: visible;
}

/* Connection-globe backdrop, V0.7.0 (change 2). [OPS] .globe-layer / #globe-canvas
 (magnum-ecosystem/ops/index.html V3.9, read-only reference): layer 0, absolutely filling
 its container, pointer-events:none so it never intercepts a gear click, and a darkening
 radial-gradient vignette behind it so the point cloud reads clearly against the panel
 rather than washing out - [OPS]'s own comment calls this exact treatment out ("knock the
 gear background back so the globe reads clearly"), ported at a lighter opacity here since
 this panel is smaller and the vignette only needs to clear the immediate globe area, not a
 whole console stage. z-index 0 puts it strictly behind .orb-decor-rings and .gear-cluster
 (both bumped to z-index 1 below) so the globe is atmosphere, never the focus. The canvas
 itself is drawn by initOrbGlobe() in app.js; see that function for the point-cloud /
 wireframe / arc rendering, the ~30fps frame cap, the document.hidden pause, and the
 prefers-reduced-motion handling. */
.orb-globe-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(
    circle at 50% 50%,
    rgba(0, 0, 0, 0.45) 0%,
    rgba(0, 0, 0, 0.2) 55%,
    transparent 82%
  );
}
/* .is-headless, [MEMBER] class carried verbatim onto #gear-cluster-zone for markup
 fidelity (the live container is ".gear-cluster-zone is-headless"). No CSS below keys
 off it: this preview already ships every journey step as neutral "locked" (see
 data/journey.json), so there is no real-progress state for a hook here to suppress.
 Left as a stable selector in case a future pass needs to gate a real-member-only
 treatment behind "this container has no live session backing it". */

/* V0.7.1: the three-ring .orb-decor-rings backdrop (invented, canonical 22s/14s/9s
 tempo) is removed. The live source's real centre-ring decoration is two
 pseudo-elements on .gear-center itself (dashed 22s cw / dotted 38s ccw) - see that
 rule below, harvested verbatim from docs/reference/live-orb.css. */

/* --cluster-bg background hook, HARVESTED from docs/reference/live-orb.css:
 ".gear-cluster-zone[style*=--cluster-bg]::after" layers a darkened texture behind
 everything when a --cluster-bg custom property is set inline on the zone. The live
 source's own comment example sets it to a url(...) photograph; this build makes zero
 external requests, so index.html instead sets --cluster-bg to a pure-CSS gear-ring
 gradient (the same repeating-conic + repeating-radial recipe as .mom-gear-texture in
 section 8) approximating "a large gear ring behind the cluster" from Anric's
 screenshots. z-index is pinned to -1 (not the harvested 0) so it always paints
 behind .orb-globe-layer/.leader-lines, which are themselves z-index 0 siblings in
 this zone and would otherwise tie with it on paint order. */
.gear-cluster-zone[style*="--cluster-bg"]::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--cluster-bg);
  background-size: cover;
  background-position: center;
  opacity: 0.18;
  filter: brightness(0.7) saturate(0.6);
  pointer-events: none;
  z-index: 0; /* 02A line 1770-1777, HARVESTED VERBATIM - see section 10 header comment */
}

.gear-cluster {
  position: relative;
  width: 100%;
  max-width: 500px; /* 02A line 1783 - HARVESTED, corrects V0.7.1's 460px */
  aspect-ratio: 1 / 1;
  margin: 0 auto;
  overflow: visible;
  z-index: 1; /* V0.7.0: above .orb-globe-layer (z-index 0), see that rule's comment */
}

/* ── Node anchor - [MEMBER] .gear, HARVESTED ── */
.gear {
  position: absolute;
  text-decoration: none;
  display: block;
}
.gear.big {
  width: 28%;
} /* [MEMBER] .gear.big { width: 28% } - HARVESTED exactly, V0.7.1 restores this after a
     prior pass wrongly trimmed it to 26% to make room for an invented 60% centre. */
.gear.small {
  width: 22%;
} /* [MEMBER] .gear.small { width: 22% } - HARVESTED exactly, same V0.7.1 restoration. */

/* Compass positions - [MEMBER] .gear.pos-*, HARVESTED VERBATIM (13%/87%/50% cardinals,
 26%/74% intercardinals) straight from docs/reference/live-orb.css. V0.7.1 restores these
 after a prior pass pushed every position outward (4%/96%/20%/80%) to avoid a collision
 that only existed because that same pass had also grown .gear-center to an invented 60%.
 With the centre correctly at the harvested 44% and the gears at the harvested 28%/22%,
 the harvested positions clear the centre with real margin - the live page runs exactly
 this geometry every day with zero overlap. Do not re-derive these from the gear/centre
 sizes; they are independently harvested values, not a computed layout. */
.gear.pos-n {
  top: 13%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.gear.pos-e {
  top: 50%;
  left: 87%;
  transform: translate(-50%, -50%);
}
.gear.pos-s {
  top: 87%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.gear.pos-w {
  top: 50%;
  left: 13%;
  transform: translate(-50%, -50%);
}
.gear.pos-ne {
  top: 26%;
  left: 74%;
  transform: translate(-50%, -50%);
}
.gear.pos-se {
  top: 74%;
  left: 74%;
  transform: translate(-50%, -50%);
}
.gear.pos-sw {
  top: 74%;
  left: 26%;
  transform: translate(-50%, -50%);
}
.gear.pos-nw {
  top: 26%;
  left: 26%;
  transform: translate(-50%, -50%);
}

.gear:hover {
  z-index: 10;
}

/* ── Round disc - [MEMBER] .gear-disc, HARVESTED base recipe. No disc chrome
 (background/border/box-shadow are never set here - the PNGs are already gear-shaped).
 V0.8.0: the glow filter is now split BIG vs SMALL per 02A (lines 1959-1967 big,
 1848-1856 small) - a real, distinct pair of values, not one filter shared by every
 gear regardless of size. Applied to .gear-disc-inner (see that rule's comment for why
 this build's hover-scale wrapper carries the filter instead of .gear-disc itself; the
 filter VALUES are still the source's). The whole-cluster spin + disc counter-rotation
 this rule used to carry (a V0.6.0 invention) stays removed: the live source spins
 each gear's own <img>, not the disc or the cluster. ── */
.gear-disc {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  box-shadow: none;
}
.gear.big .gear-disc-inner {
  filter: drop-shadow(0 0 14px rgba(240, 193, 115, 0.5))
    drop-shadow(0 4px 10px rgba(0, 0, 0, 0.65)); /* 02A line 1964-1966, HARVESTED */
}
.gear.small .gear-disc-inner {
  filter: drop-shadow(0 0 12px rgba(240, 193, 115, 0.45))
    drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6)); /* 02A line 1853-1855, HARVESTED */
}

/* Hover lift + scale lives on a SEPARATE inner wrapper (CHOSEN) rather than on
 .gear-disc itself, because .gear-disc already owns the counter-rotation animation
 and a CSS animation's own transform cannot be combined with a plain hover
 transition on the same property/element without one clobbering the other every
 animation frame. Splitting the two transforms across parent/child is the standard
 fix and keeps both effects independently readable here. */
.gear-disc-inner {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.22s ease, filter 0.22s ease;
}
.gear:hover .gear-disc-inner,
.gear:focus-visible .gear-disc-inner {
  transform: scale(1.07); /* 02A line 1809 `.gear:hover .gear-disc { transform: scale(1.07) }`,
    HARVESTED VERBATIM - V0.7.1 had invented 1.09 + a translateY(-3px) lift; reverted. */
}
.gear.big:hover .gear-disc-inner,
.gear.big:focus-visible .gear-disc-inner {
  filter: drop-shadow(0 0 22px rgba(250, 189, 89, 0.75))
    drop-shadow(0 4px 10px rgba(0, 0, 0, 0.65)); /* 02A line 1973-1978, HARVESTED */
}
.gear.small:hover .gear-disc-inner,
.gear.small:focus-visible .gear-disc-inner {
  filter: drop-shadow(0 0 20px rgba(250, 189, 89, 0.7))
    drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6)); /* 02A line 1861-1865, HARVESTED */
}
.gear-disc-inner img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
} /* [MEMBER] .gear.big .gear-disc img */

/* ── Ambient per-image spin, HARVESTED VERBATIM from 02A lines 1889-1893: bigs
 clockwise, smalls counter-clockwise, "the way real interlocked gear trains turn" per
 the source's own comment. Applied to the <img> itself (not the disc, not the
 cluster) so the logo rotates fully in place at its fixed compass position - no
 counter-rotation anywhere. Paused on individual hover/focus so the label can be read,
 per the source. Both at 60s, the live component's own real value - not one of the
 estate's five canonical tempos, deliberately slower for background atmosphere. ── */
@keyframes mvp-gear-spin-cw {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
@keyframes mvp-gear-spin-ccw {
  from {
    transform: rotate(360deg);
  }
  to {
    transform: rotate(0deg);
  } /* 02A line 1893, HARVESTED VERBATIM - functionally identical to V0.7.1's
       0deg -> -360deg but matched to source byte-for-byte per the port instruction. */
}
.gear.big .gear-disc-inner img {
  animation: mvp-gear-spin-cw 60s linear infinite;
}
.gear.small .gear-disc-inner img {
  animation: mvp-gear-spin-ccw 60s linear infinite;
}
.gear:hover .gear-disc-inner img,
.gear:focus-visible .gear-disc-inner img {
  animation-play-state: paused;
}

/* Graceful image-load degrade (CHOSEN, brief requirement): if the <img> 404s, app.js's
 onerror handler hides it and adds this class so the gear ring shape + label still
 render instead of a broken-image icon. */
.gear-disc-inner--fallback {
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 32%,
    rgba(240, 193, 115, 0.16) 0%,
    rgba(10, 10, 15, 0.4) 95%
  );
  border: 2px solid var(--mom-border-strong);
}

/* ── Label below disc - [MEMBER] .gear-label, HARVESTED (hover-only reveal, so the
 cluster reads as pure icons at rest and surfaces the tool name on hover/focus,
 matching the brief's "reveal its tool name" requirement). ── */
.gear-label {
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  color: var(--mom-gold);
  text-shadow: 0 0 8px rgba(0, 0, 0, 0.95), 0 0 4px rgba(0, 0, 0, 0.8);
  text-transform: uppercase;
  letter-spacing: 1.1px;
  line-height: 1.15;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.gear:hover .gear-label,
.gear:focus-visible .gear-label {
  opacity: 1;
}
.gear.big .gear-label {
  font-size: var(--mom-text-sm);
}
.gear.small .gear-label {
  font-size: var(--mom-text-xs);
  letter-spacing: 0.7px;
}

/* ── Inline status dot on the label, base rule - 02A lines 2042-2055, HARVESTED
 VERBATIM. Transparent/invisible until a data-state is set (next rule), then
 recolored per state below. This is the source's ONLY per-state indicator - no icon
 glyph is ever added on top of it. ── */
.gear-label::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 7px;
  vertical-align: 1px;
  background: transparent;
  box-shadow: none;
  transition: background 0.3s ease, box-shadow 0.3s ease;
}
.gear[data-state] .gear-label::before {
  background: rgba(140, 140, 150, 0.45);
}

/* Visible focus ring - CHOSEN, keyboard-focusable requirement. Uses the same outline
 treatment as every other focusable control in this file (section 2 base rule) rather
 than inventing a new focus style just for the orb. */
.gear:focus-visible {
  outline: 1px solid var(--mom-gold);
  outline-offset: 4px;
  border-radius: 50%;
}

/* ── Journey status states, PORTED VERBATIM V0.8.0 from 02A lines 1981-2011 (big),
 1867-1884 (small), and 2053-2081 (the label dot, size-independent). data-state values
 are done/current/locked, matching source's own attribute values exactly.

 V0.7.1 had invented a checkmark (U+2713) for done, a padlock (U+1F512) for locked,
 and a green box-shadow token - none of that exists in source. The source's ONLY
 per-state indicator is the small dot already on .gear-label::before (base rule,
 above), recolored per state - removed the invented glyphs/colors below and restored
 the source's real dot-only treatment: solid green for done, a pulsing gold dot for
 current (mvp-dot-pulse, opacity 1 -> 0.45, 1.8s - source's own value, not the
 estate's canonical mom-blink 1.5s token), dim gray for locked. The disc filter is
 still split BIG vs SMALL, same reasoning as the base/hover filters above. */
.gear.big[data-state="done"] .gear-disc-inner {
  filter: drop-shadow(0 0 16px rgba(240, 193, 115, 0.6))
    drop-shadow(0 4px 10px rgba(0, 0, 0, 0.65)); /* 02A line 1981-1985 */
}
.gear.small[data-state="done"] .gear-disc-inner {
  filter: drop-shadow(0 0 14px rgba(240, 193, 115, 0.55))
    drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6)); /* 02A line 1867-1871 */
}
.gear[data-state="done"] .gear-label::before {
  background: #4ade80;
  box-shadow: 0 0 8px rgba(74, 222, 128, 0.75); /* 02A line 2065-2067, HARVESTED */
}

@keyframes mvp-current-glow-big {
  0%,
  100% {
    filter: drop-shadow(0 0 14px rgba(240, 193, 115, 0.5))
      drop-shadow(0 4px 10px rgba(0, 0, 0, 0.65));
  }
  50% {
    filter: drop-shadow(0 0 30px rgba(250, 189, 89, 0.95))
      drop-shadow(0 0 50px rgba(240, 193, 115, 0.45))
      drop-shadow(0 4px 10px rgba(0, 0, 0, 0.65));
  }
} /* 02A line 1992-2003, HARVESTED VERBATIM */
.gear.big[data-state="current"] .gear-disc-inner {
  animation: mvp-current-glow-big 2.6s ease-in-out infinite;
}
.gear.small[data-state="current"] .gear-disc-inner {
  animation: mom-glow-pulse var(--mom-dur-glow) ease-in-out infinite; /* [MEMBER]
    mvp-current-glow-small, 02A line 1877-1880, byte-identical to this file's own
    section-7 mom-glow-pulse token - reused rather than redefined. */
}
.gear[data-state="current"] .gear-label::before {
  background: var(--mom-gold-bright);
  box-shadow: 0 0 8px rgba(240, 193, 115, 0.75); /* 02A line 2069-2071, HARVESTED */
  animation: mvp-dot-pulse 1.8s ease-in-out infinite;
}
@keyframes mvp-dot-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.45;
  }
} /* 02A line 2074-2077, HARVESTED VERBATIM - replaces the estate's canonical
     mom-blink (1.5s) token, which this gear used previously; source's own current-dot
     timing is 1.8s and it animates opacity only, not a background/box-shadow swap. */

.gear.big[data-state="locked"] .gear-disc-inner {
  opacity: 0.5;
  filter: grayscale(0.45) drop-shadow(0 4px 10px rgba(0, 0, 0, 0.65)); /* 02A line 2006-2010 */
}
.gear.small[data-state="locked"] .gear-disc-inner {
  opacity: 0.5;
  filter: grayscale(0.45) drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6)); /* 02A line 1881-1884 */
}
.gear[data-state="locked"] .gear-label::before {
  background: rgba(140, 140, 150, 0.35);
  box-shadow: none; /* 02A line 2078-2081, HARVESTED */
}

/* ── Center mark - [MEMBER] .gear-center, HARVESTED base (no border, no circle, per the
 live source's own comment on that element: "raw icon, no border, no circle"). V0.7.1:
 width restored to the harvested 44% after a prior pass wrongly grew it to 60% on the
 theory that Anric wanted the centre "4x bigger" - he did not; 44% IS the live page's real
 tuned value and is already dominant at that size. Restoring 44% is also what makes the
 harvested 13%/87%/26%/74% gear positions clear the centre correctly again (see the
 .gear.pos-* comment above). public/assets/orb/center-mvp.webp (552x552) still renders
 sharp at 44% of any realistic .gear-cluster size. V0.8.0: filter and z-index corrected
 to 02A lines 2119-2129, HARVESTED VERBATIM - V0.7.1 had a single invented
 drop-shadow(0 0 16px/.55) and no z-index; source runs two stacked drop-shadows and
 pins z-index:7 so the centre always paints above every gear. */
.gear-center {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44%;
  transform: translate(-50%, -50%);
  z-index: 7;
  filter: drop-shadow(0 0 18px rgba(240, 193, 115, 0.45))
    drop-shadow(0 4px 12px rgba(0, 0, 0, 0.7));
  transition: filter 0.4s ease;
}
.gear-center img {
  width: 100%;
  height: auto;
  display: block;
  position: relative;
  z-index: 2;
}
.gear-center--fallback {
  border-radius: 50%;
  background: radial-gradient(circle, var(--mom-gold-bright) 0%, transparent 70%);
  aspect-ratio: 1 / 1;
}

/* ── Centre rings, HARVESTED VERBATIM from docs/reference/live-orb.css:
 .gear-center::before/::after, two concentric rings (dashed 22s clockwise, dotted 38s
 counter-clockwise), replacing the invented three-ring .orb-decor-rings backdrop this
 file used to carry (removed above, see that section's note). ── */
.gear-center::before,
.gear-center::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  z-index: 1;
}
.gear-center::before {
  inset: -22%;
  border: 1px dashed rgba(240, 193, 115, 0.42);
  animation: mvp-orb-cw 22s linear infinite;
}
.gear-center::after {
  inset: -42%;
  border: 1px dotted rgba(240, 193, 115, 0.22);
  animation: mvp-orb-ccw 38s linear infinite;
}
@keyframes mvp-orb-cw {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
@keyframes mvp-orb-ccw {
  from {
    transform: rotate(360deg);
  }
  to {
    transform: rotate(0deg);
  }
}

/* ── Corner shortcuts + leader lines, PORTED VERBATIM V0.8.0 from 02A lines 2249-2277
 (.corner-shortcut) and 2220-2244 (.leader-lines), replacing the V0.7.1 text-chip
 fallback now that the four real icon PNGs exist locally (public/assets/orb/
 corner-*.webp - the live source's icons are brand PNGs with names baked in, no text
 label needed). Markup for both is now generated by buildOrbChrome() in app.js from
 data/orb.json's "corners" array, not hardcoded in index.html - see that function's
 comment for the facet-drawer wiring, which has no source-of-truth equivalent (02B
 uses plain in-page anchors; this build has portal drawers instead). Hidden below
 920px, matching source's own mobile behavior (02A line 2373-2378: "corner pills +
 leader lines crowd the gears" on small screens). ── */
.corner-shortcut {
  position: absolute;
  z-index: 5;
  display: block;
  width: 86px;
  height: 86px;
  background: transparent;
  border: none;
  text-decoration: none;
  transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.3, 1.2), filter 0.22s ease;
  filter: drop-shadow(0 0 10px rgba(240, 193, 115, 0.4))
    drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6));
}
.corner-shortcut img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.corner-shortcut:hover,
.corner-shortcut:focus-visible {
  transform: scale(1.08);
  filter: drop-shadow(0 0 18px rgba(250, 189, 89, 0.75))
    drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6));
}
/* V1.5.0 (FIX 6): visible click feedback on every corner. Anric's report was that TOOLS did
 nothing when pressed; two of the four were genuinely dead controls (see app.js
 wireCornerShortcuts), but even the two that worked gave no acknowledgement at the corner
 itself, so a member could not tell a working corner from a broken one. The class is added
 and removed on a timer by pulseCorner() rather than relying on :hover, so it fires
 identically for a keyboard activation and on a touch screen where there is no hover state. */
.corner-shortcut--pulse {
  animation: mom-corner-pulse 0.62s ease-out;
}
@keyframes mom-corner-pulse {
  0% {
    transform: scale(1);
    filter: drop-shadow(0 0 10px rgba(240, 193, 115, 0.4))
      drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6));
  }
  28% {
    transform: scale(1.18);
    filter: drop-shadow(0 0 30px rgba(250, 189, 89, 1))
      drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6));
  }
  100% {
    transform: scale(1);
    filter: drop-shadow(0 0 10px rgba(240, 193, 115, 0.4))
      drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6));
  }
}
/* Reduced motion: no scale, no animation, but the acknowledgement itself is not removed -
 the corner still visibly brightens for the same duration, it just does not move. */
@media (prefers-reduced-motion: reduce) {
  .corner-shortcut--pulse {
    animation: none;
    filter: drop-shadow(0 0 30px rgba(250, 189, 89, 1))
      drop-shadow(0 3px 8px rgba(0, 0, 0, 0.6));
  }
}

/* V1.5.0 (FIX 6): the EVENTS corner scrolls to panel 05 LIVE and flashes it, so the member
 can see WHICH panel answered the click. Same add-then-remove-on-a-timer pattern as the
 corner pulse; app.js owns the timing. */
.mom-panel--flash {
  animation: mom-panel-flash 1.5s ease-out;
}
@keyframes mom-panel-flash {
  0% {
    border-color: var(--mom-gold);
    box-shadow: 0 0 0 1px var(--mom-gold), 0 0 30px rgba(250, 189, 89, 0.55);
  }
  70% {
    border-color: var(--mom-gold);
    box-shadow: 0 0 0 1px rgba(250, 189, 89, 0.5), 0 0 18px rgba(250, 189, 89, 0.28);
  }
  100% {
    border-color: var(--mom-border-soft);
    box-shadow: var(--mom-shadow-panel);
  }
}
@media (prefers-reduced-motion: reduce) {
  .mom-panel--flash {
    animation: none;
    border-color: var(--mom-gold);
    box-shadow: 0 0 0 1px var(--mom-gold), 0 0 30px rgba(250, 189, 89, 0.55);
  }
}

.corner-shortcut.cs-tl {
  top: 14px;
  left: 14px;
}
.corner-shortcut.cs-tr {
  top: 14px;
  right: 14px;
}
.corner-shortcut.cs-bl {
  bottom: 14px;
  left: 14px;
}
.corner-shortcut.cs-br {
  bottom: 14px;
  right: 14px;
}

.leader-lines {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}
.leader-lines path {
  fill: none;
  stroke: rgba(240, 193, 115, 0.65);
  stroke-width: 1.6;
  stroke-dasharray: 5 4;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  animation: mvp-leader-flow 4s linear infinite;
} /* 02A lines 2230-2237, HARVESTED VERBATIM - V0.7.1 used <line> elements at
     stroke-width 0.4/opacity 0.35/dasharray "2 2"/9s, none of which matched source;
     the markup itself (4 quadratic-bezier <path>s from center to each corner) is now
     ported verbatim into index.html from 02B lines 172-175. */
@keyframes mvp-leader-flow {
  from {
    stroke-dashoffset: 0;
  }
  to {
    stroke-dashoffset: -36;
  }
}

@media (max-width: 920px) {
  .corner-shortcut,
  .leader-lines {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gear-disc-inner img,
  .gear.big[data-state="current"] .gear-disc-inner,
  .gear.small[data-state="current"] .gear-disc-inner,
  .gear[data-state="current"] .gear-label::before,
  .gear-center::before,
  .gear-center::after,
  .leader-lines path {
    animation: none !important;
  }
}

/* ── Journey progress readout + state legend, below the cluster ── */
.orb-journey {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--mom-space-4);
  margin-top: var(--mom-space-3);
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  letter-spacing: 0.6px;
  color: var(--mom-text-muted);
}
.orb-journey-readout {
  color: var(--mom-gold);
  font-weight: 700;
  text-transform: uppercase;
}
.orb-journey-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mom-space-3);
}
.orb-journey-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  text-transform: uppercase;
}
.orb-journey-swatch {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
}
.orb-journey-swatch--done {
  background: var(--mom-green);
  box-shadow: 0 0 6px var(--mom-green);
}
.orb-journey-swatch--current {
  background: var(--mom-gold-bright);
  box-shadow: 0 0 6px var(--mom-gold-bright);
  animation: mom-blink var(--mom-dur-blink) ease-in-out infinite;
}
.orb-journey-swatch--locked {
  background: var(--mom-text-dim);
  box-shadow: none;
}
@media (prefers-reduced-motion: reduce) {
  .orb-journey-swatch--current {
    animation: none !important;
  }
}

/* ==========================================================================
 11. PORTAL RAIL + DRAWER - mvp-dashboard V0.5.0
 [OPS] ADMIN PORTALS sidebar (.nav-item/.nav-icon/.nav-label/.nav-count,
 magnum-ecosystem/ops/index.html line 362-400) and the right-hand DRAWER
 OVERLAY (.drawer-overlay family, line 900-1041) in that same file V3.20,
 ported for INTERACTION ONLY and restyled to this file's own MEMBER idiom:
 14px panel radius (never the OPS 2-3px chrome radius), --mom-gold (not
 --mv-gold-ops flat chrome gold) for the selected/active state, no cyan
 anywhere - [OPS] .drawer-overlay::before cycles gold/cyan/gold across its
 top-edge sweep; this one stays gold-only. The slide transform, its exact
 cubic-bezier(0.4,0,0.2,1) timing, and the 0.4s duration ARE kept
 byte-identical: [OPS]'s own comment calls that specific transition "the
 architecture," not a color choice, and porting the interaction faithfully
 was the brief.
 ========================================================================== */

.mom-portal-list {
  display: flex;
  flex-direction: column;
}

/* 2026-08-08, approved by Anric after reviewing /proto-focus/: two fixes, additive only.
 (1) color: inherit + text-decoration: none - when this is rendered on a real <a href>
 (course-nav.js does), it must not inherit the browser's blue/underline link default;
 the component looks the same as a div or an anchor.
 (2) :focus-visible below gets the gold outline this component was missing, split from
 :hover so the ring never shows on mouse hover - only on real keyboard focus. */
.mom-nav-item {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  align-items: center;
  gap: var(--mom-space-3);
  padding: 8px var(--mom-space-3);
  margin-bottom: 3px;
  border-radius: var(--mom-radius-xs);
  border-left: 3px solid transparent;
  cursor: pointer;
  color: inherit;
  text-decoration: none;
  transition:
    background-color 0.2s ease,
    border-left-color 0.2s ease;
}
.mom-nav-item:hover,
.mom-nav-item:focus-visible {
  background: var(--mom-gold-dim);
  border-left-color: var(--mom-border-strong);
}
/* Gold focus ring, same treatment as .gear:focus-visible above. outline-offset keeps
 it clear of the border-left-color/background set above. Never on :hover. */
.mom-nav-item:focus-visible {
  outline: 1px solid var(--mom-gold);
  outline-offset: 4px;
}
/* Selected state: left gold edge (glow) + lifted/tinted background + the badge inverting
 to solid gold with dark text, per the brief's portal-rail spec, using this file's own
 --mom-gold rather than the ops console's flat chrome gold. */
.mom-nav-item.is-active {
  background: rgba(240, 193, 115, 0.12);
  border-left-color: var(--mom-gold);
  box-shadow:
    inset 0 0 0 1px rgba(240, 193, 115, 0.18),
    -2px 0 10px rgba(240, 193, 115, 0.35);
}
.mom-nav-item.is-active .nav-icon {
  background: var(--mom-gold);
  color: #000;
  border-color: var(--mom-gold);
}
.mom-nav-item.is-active .nav-label {
  color: var(--mom-gold);
}
.nav-icon {
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--mom-gold-dim);
  border: 1px solid var(--mom-border-ops);
  border-radius: var(--mom-radius-xs);
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: 11px;
  color: var(--mom-gold-ops);
  letter-spacing: 1px;
  flex-shrink: 0;
}
.nav-label {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-base); /* body tier (13px): portal labels are read-and-clicked
    constantly, they belong at the same weight as row text, matching the same reasoning
    already applied to .mom-facet-row-label in section 9. */
  color: var(--mom-text-body);
  letter-spacing: 0.8px;
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.nav-count {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  color: var(--mom-text-muted);
  font-variant-numeric: tabular-nums; /* CHOSEN, see section 2 note */
}

/* Drawer overlay. [OPS] .drawer-overlay geometry/transition kept intact; recolored. Bottom
 is bounded above the footer via --mom-footer-h (set by app.css to the real measured
 footer height), matching the brief's "bounded above the new footer." */
/* V1.5.0 (FIX 2, direct from Anric: "there is no way to close that when you're done. Unless
 you scroll the whole page because the header covers the top address bar of the search
 results, including the x to exit out."). top was 0. #mn-bar is position:sticky, top:0,
 z-index:9999; this overlay is position:fixed, z-index:100. So the drawer opened underneath
 the header, and the exact strip the header covered was .drawer-head - the title, the count
 and the × close button. --mn-bar-h is the header's REAL measured height, published by
 setHeaderHeightVar() in app.js and refreshed by a ResizeObserver, because the bar's height
 genuinely changes at four breakpoints (1500, 1280, 1100, 768) and any hardcoded offset would
 be wrong at three of them. The fallback in the var() is only ever used in the impossible
 case where app.js has not run at all. */
.drawer-overlay {
  position: fixed;
  top: var(--mn-bar-h, 75px);
  right: 0;
  bottom: var(--mom-footer-h, 108px);
  width: 480px;
  max-width: 92vw;
  background: var(--mom-bg-modal);
  border-left: 1px solid var(--mom-border-strong);
  box-shadow:
    -8px 0 24px rgba(0, 0, 0, 0.6),
    0 0 32px rgba(240, 193, 115, 0.12) inset;
  transform: translateX(110%);
  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); /* [OPS] exact timing, see header note above */
  z-index: 100;
  display: flex;
  flex-direction: column;
  backdrop-filter: blur(var(--mom-blur-md));
  -webkit-backdrop-filter: blur(var(--mom-blur-md));
}
.drawer-overlay.open {
  transform: translateX(0);
}
.drawer-overlay::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--mom-gold), transparent);
  opacity: 0.85;
  pointer-events: none;
}
.drawer-head {
  display: flex;
  align-items: center;
  gap: var(--mom-space-4);
  padding: var(--mom-space-6) var(--mom-space-7);
  border-bottom: 1px solid var(--mom-border-soft);
}
.drawer-head-icon {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--mom-gold);
  color: #000;
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: 12px;
  border-radius: var(--mom-radius-xs);
  flex-shrink: 0;
}
.drawer-head-title {
  flex: 1;
  min-width: 0;
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: var(--mom-text-xl);
  color: var(--mom-text);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.drawer-head-count {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  color: var(--mom-gold);
  letter-spacing: 1px;
  padding: 3px 9px;
  border: 1px solid var(--mom-border);
  border-radius: var(--mom-radius-xs);
  background: var(--mom-gold-dim);
  white-space: nowrap;
  flex-shrink: 0;
}
.drawer-close {
  width: 30px;
  height: 30px;
  background: var(--mom-red-dim);
  border: 1px solid rgba(220, 20, 60, 0.4);
  color: var(--mom-red);
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: 16px;
  cursor: pointer;
  border-radius: var(--mom-radius-xs);
  transition: all 0.2s ease;
  flex-shrink: 0;
}
.drawer-close:hover,
.drawer-close:focus-visible {
  background: rgba(220, 20, 60, 0.25);
  color: #fff;
  box-shadow: 0 0 12px rgba(220, 20, 60, 0.4);
}
.drawer-body {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  padding: var(--mom-space-5) var(--mom-space-7) 0; /* V1.6.1: bottom was --mom-space-8
    (20px). MEASURED this had the same sticky-detachment problem as the padding-bottom
    tried and reverted on .notewell-body/.release-body/.replays-body above (a
    position:sticky child does not stick into its own containing block's bottom padding in
    Chrome) - the drawer's .scroll-fade sat a flat 20px off the drawer's true bottom edge at
    every scrollTop, pre-existing and unrelated to this pass. Bottom padding dropped to 0 so
    .scroll-fade (now real 52px flow height, not a cancelled negative margin - see that rule
    in the comment above it) is what supplies the drawer's bottom clearance too, same as the
    other three panels that share this component. */
  position: relative; /* V0.7.0 (change 5): containing block for .scroll-fade */
}

/* ── Scroll affordance, V0.7.0 (change 5) ──────────────────────────────────────────────
 Every internally-scrolling panel (.notewell-body, .release-body, .replays-body,
 .live-rotation, .drawer-body - all `position: relative; overflow-y: auto`, set at each
 selector above) gets one of these appended as its last child by
 setupScrollAffordance()/updateScrollAffordance() in app.js: a soft fade plus a small
 downward chevron, shown only while there is more content below the current scroll
 position. Because this is `position: absolute` against the SAME element that scrolls (not
 a separate wrapper), it is glued to that element's own visible viewport rather than
 scrolling away with the content - the standard technique for a fixed edge-fade inside a
 scrolling box. `pointer-events: none` throughout so it can never block a click or cover a
 link underneath, per the brief. Visibility is driven by a real scroll listener comparing
 scrollTop + clientHeight against scrollHeight (see app.js), not a static guess; this rule
 only defines the opacity transition the JS toggles via .is-visible. */
/* V1.5.0 (FIX 3, direct from Anric: "On all boxes that are scrollable, the arrow indicator
 that there is more is too small."). Measured before: the fade was 30px tall and the chevron
 was a 13px '⌄' text glyph, whose actually-painted mark is roughly 7x4px inside that em box -
 a mark small enough to miss entirely against a busy panel. After: a 52px fade carrying a
 26x26px stroked SVG chevron (a real 26x13px painted mark, about 3.3x wider and 3.2x taller),
 sitting on a small darkened gold-tinted disc so it reads as a control-shaped affordance
 rather than a stray punctuation character, with a deeper gradient beneath it. Still
 pointer-events:none on the whole thing including the disc, so nothing here can ever
 intercept a click on the row underneath, and visibility is still driven purely by the real
 scroll position measured in app.js's updateScrollAffordance(). */
/* V1.6.0 (snag 6, direct from Anric: "the arrow kind of stays with it" as you scroll card 03
 LATEST RELEASES). The comment above this rule claimed `position: absolute` against the SAME
 element that scrolls "is glued to that element's own visible viewport rather than scrolling
 away with the content." Measured, that claim was wrong: an absolutely positioned descendant
 of a scrolling element is translated by the SAME scroll offset as the element's in-flow
 content when painted, because scrolling shifts everything the scrolling box paints, in-flow
 or not - it is only exempt if its containing block is an ancestor OUTSIDE the scrolling box.
 Proof (getBoundingClientRect on #release-body's .scroll-fade, 1280px): at scrollTop 0 the
 fade sat at container-relative bottom:0 as intended; forcing scrollTop to 500 moved the fade
 from screen y=778-830 to y=278-330, a 500px shift identical to the scroll delta, while the
 panel's own visible box never moved. That is the reported bug, and it is also what produced
 "entries overlapping and not showing clearly": the fade/chevron does not stay pinned to the
 bottom edge, it drifts up through the list and can land on top of mid-list text at almost any
 scroll position (reproduced: at rest it already overlapped the last visible line of the first
 release row, "No cold / scraping. No generic..."). `position: sticky` is the property
 actually designed for "stay within the visible viewport of the nearest scrolling ancestor
 while remaining a normal descendant of it" - unlike absolute, it is scroll-aware by
 definition. Switched below; `margin-top: -52px` cancels the 52px this element would
 otherwise add to the list's normal-flow height (and therefore to scrollHeight), since it is
 an overlay, not real content - confirmed no change to scrollHeight before/after (5553px, 34
 rows) and the fade now measures container-bottom-pinned (830/830) at every scrollTop tested,
 including mid-scroll. Applies to every panel that shares this class (.notewell-body,
 .release-body, .replays-body, .drawer-body - the same JS-appended affordance), not just card
 03, since the bug was in the shared component, not one panel's markup. */
/* V1.6.1 fix, direct from Anric: "the chevron sits ON TOP OF the text" in ALERTS, LATEST
 RELEASES and REPLAYS. MEASURED in a real browser against the local build before touching
 anything (getBoundingClientRect + elementFromPoint on .notewell-body at 1280px): confirmed
 the guessed cause. .notewell-body/.release-body/.replays-body/.drawer-body had zero (or, for
 the drawer, only 20px) bottom clearance while this element sat sticky over the bottom 52px of
 whatever was visible, so a real <p class="note-sec-body"> ("Every session, every walk-in
 meeting and every live training...") rendered directly under the chevron's own point at rest.
 TWO THINGS WERE TRIED, ONE WAS REVERTED. First tried the brief's suggested fix, 52px of
 padding-bottom on the scrolling containers themselves: MEASURED that this regresses the
 V1.6.0 sticky fix instead of completing it - a sticky-positioned child does not stick into
 its own containing block's bottom padding in Chrome, so adding padding-bottom to
 .notewell-body pulled this element UP off the container's true bottom edge by exactly the
 padding amount (measured: fadeBottom 390 vs containerBottom 442, a 52px gap), leaving a
 blank-looking strip with the SAME unmasked text still showing through it and the affordance
 no longer flush with the panel. Reverted; .notewell-body/.release-body/.replays-body/
 .drawer-body carry no padding change from this pass.
 What shipped instead, all in this one shared rule: margin-top changed from -52px to 0, so
 this element now consumes its own 52px of real flow height at the tail of the list instead of
 cancelling it. MEASURED after: still position:sticky bottom:0 inside a container with zero
 padding, so it still sticks flush to the container's true bottom at every scrollTop
 (fadeBottom === containerBottom, unlike the padding attempt above) - the V1.6.0 requirement
 ("stay pinned to the container bottom at every scrollTop") holds. The real 52px it now adds
 to scrollHeight is deliberate: it is exactly what lets a user who scrolls to the true end
 clear the last real line of text out from under this band before hasMore goes false, instead
 of that line staying trapped under a still-visible chevron until the last few px of scroll.
 HONEST LIMIT, not silently worked around: partway through a long list (which is most of the
 scroll range in these panels) this is a fixed-position overlay above content that is still
 scrolling underneath it - by definition it will still visually coincide with real text there,
 the same as every fade-affordance of this kind (iOS lists, Netflix rails, etc). Eliminating
 that entirely would mean this stops being an overlay on the text at all (e.g. a separate
 non-overlapping strip below the list), which is a different, bigger change than this pass
 made; flagged to Anric rather than assumed away. What this pass DOES fix is the two things
 actually reported as broken: the affordance no longer drifts off the container's true edge,
 and the worst case - real text trapped under a still-opaque-looking chevron right up to the
 end of scroll - is gone. To make what IS still an overlay read as an intentional fade rather
 than "text peeking through a translucent badge," the gradient below is also steeper now (near-
 opaque by the point the chevron sits, instead of ~40-60% there) and the chevron disc itself
 (next rule) is a solid, near-opaque backing instead of a 14%-alpha tint, so it fully masks
 whatever is behind it regardless of gradient position. */
.scroll-fade {
  position: sticky;
  left: 0;
  right: 0;
  bottom: 0;
  height: 52px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 6px;
  background: linear-gradient(
    180deg,
    transparent,
    rgba(8, 8, 12, 0.88) 30%,
    rgba(8, 8, 12, 0.98) 60%
  );
  opacity: 0;
  transition: opacity 0.18s ease;
  pointer-events: none;
}
.scroll-fade.is-visible {
  opacity: 1;
}
.scroll-fade-chevron {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--mom-radius-sm); /* never pill */
  background: rgba(12, 10, 8, 0.92); /* V1.6.1: was rgba(240,193,115,0.14), a 14%-alpha gold
    tint that let text underneath show through the disc itself. Solid near-opaque dark backing
    (still gold-bordered/gold-icon below) so the badge fully masks whatever is behind it. */
  border: 1px solid rgba(240, 193, 115, 0.42);
  color: var(--mom-gold);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.7);
  pointer-events: none;
}
.scroll-fade-chevron svg {
  width: 26px;
  height: 26px;
  display: block;
  filter: drop-shadow(0 0 5px rgba(0, 0, 0, 0.9));
}
@media (prefers-reduced-motion: reduce) {
  .scroll-fade {
    transition: none;
  }
}
.drawer-empty {
  text-align: center;
  padding: 40px 20px;
  color: var(--mom-text-muted);
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-sm);
  letter-spacing: 1.5px;
}
.drawer-item {
  display: flex;
  align-items: center;
  gap: var(--mom-space-3);
  padding: 10px var(--mom-space-4);
  margin-bottom: 6px;
  border: 1px solid transparent;
  border-left: 3px solid transparent;
  border-radius: var(--mom-radius-sm); /* never pill */
  text-decoration: none;
  color: var(--mom-text-muted);
  transition: all 0.18s ease;
}
.drawer-item:hover,
.drawer-item:focus-visible {
  background: var(--mom-gold-dim);
  border-color: var(--mom-border);
  color: var(--mom-text);
  transform: translateX(2px);
}
.drawer-item--highlight {
  background: var(--mom-gold-dim);
  border-color: var(--mom-gold);
}
/* V4.6.7 (WRITING-portal sample, app.js drawerItemHTML's `kind` param): the row's own leading
 icon, a flex child sitting before .drawer-item-text so it shares the row's existing
 display:flex/align-items:center/gap rhythm - no new layout system, just one more child. 16px
 roughly matches .drawer-item-name's own line height. Only ever present when app.js passes a
 `kind`, so every drawer that never does (every portal but WRITING) never gets this element at
 all.
 V4.6.8: `color: var(--mom-gold)` added here, direct from Anric on the icon revision - gold,
 explicitly, not the row's own status tint. Before this the svg (fill="currentColor" in app.js's
 toolIconSVG()/videoIconSVG()) inherited whatever color .drawer-item happened to be in - dimmed
 at rest, brighter on :hover/:focus-visible, dimmed-red on .drawer-item--status-dead. Now the
 icon is pinned gold in every one of those states; only the text around it still shifts. */
.drawer-item-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  color: var(--mom-gold);
}
.drawer-item-icon svg {
  display: block;
}
.drawer-item-text {
  flex: 1;
  min-width: 0;
}
.drawer-item-name {
  font-size: var(--mom-text-base); /* body tier (13px) */
  font-weight: 600;
  color: var(--mom-text);
  letter-spacing: 0.3px;
}
.drawer-item-purpose {
  font-size: var(--mom-text-sm);
  color: var(--mom-text-soft);
  margin-top: 2px;
}
.drawer-item-host {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  color: var(--mom-text-muted);
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* V4.6.8: the V1.1.0 JOB 2 .drawer-item-local (CLONE badge) rule that used to live here is
 deleted, not just unused - app.js's drawerItemHTML() no longer emits that span at all (see its
 own V4.6.8 comment for why: wrong since cutover, and Anric said it was taking too much room).
 Confirmed no other markup in this repo references .drawer-item-local before removing it. */
/* LIBRARY two-group heading (V1.3.0, direct from Anric): "YOUR LIBRARY" and "THE CAPITAL
 MAGNETISM COLLECTION" inside the LIBRARY drawer, built by app.js's libraryGroupedHTML().
 A quieter version of .mom-section-label (same mono/letterspace/gold idiom, smaller and
 without the panel-heading border-bottom, since this sits inside an already-labelled
 drawer) rather than a second competing heading style. */
.drawer-group-head {
  display: flex;
  align-items: baseline;
  gap: var(--mom-space-2);
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-sm);
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--mom-gold);
  margin: var(--mom-space-5) 0 var(--mom-space-2);
  padding-bottom: 4px;
  border-bottom: 1px solid var(--mom-border-faint);
}
.drawer-group-head:first-child {
  margin-top: 0;
}
.drawer-group-count {
  color: var(--mom-text-muted);
  font-size: var(--mom-text-xs);
}
/* WRITING-portal sample (V4.6.7): the small icon inside a TOOLS/VIDEOS group heading, built
 by app.js's writingGroupedHTML(). `top: 1px` is a small optical nudge only - the parent's
 align-items:baseline (unchanged, still governs the LIBRARY heading's label/count alignment
 too) puts a plain svg's own bottom edge on the text baseline, which reads very slightly high
 against the mono label's cap-height without this.
 V4.6.8: explicit `color: var(--mom-gold)` added rather than leaving it to inherit
 .drawer-group-head's own gold - same explicit-gold pin as .drawer-item-icon above, so this
 element's color is not silently dependent on staying inside a gold-colored parent. */
.drawer-group-head-icon {
  display: inline-flex;
  align-items: center;
  position: relative;
  top: 1px;
  color: var(--mom-gold);
}
.drawer-group-note {
  margin-left: auto;
  font-size: var(--mom-text-xs);
  letter-spacing: 0.4px;
  text-transform: none;
  color: var(--mom-text-muted);
}

/* Status-colored left edge, the drawer's equivalent of section 9's .mom-row--status-*
 stripes - same tokens, same meaning, just on the drawer-item shape instead of the old
 flat row shape. */
.drawer-item--status-live {
  border-left-color: var(--mom-green);
}
.drawer-item--status-new {
  border-left-color: var(--mom-gold);
}
.drawer-item--status-beta {
  border-left-color: var(--mom-orange);
}
.drawer-item--status-coming_soon,
.drawer-item--status-in_development {
  border-left-color: var(--mom-text-dim);
}
.drawer-item--status-migrating {
  border-left-color: var(--mom-blue);
}
.drawer-item--status-dead {
  border-left-color: var(--mom-red);
  color: var(--mom-text-dim);
}
.drawer-item--status-dead .drawer-item-host {
  color: var(--mom-red);
}

/* V1.5.0 (FIX 2): the backdrop is offset by the same measured header height as the overlay
 above. It used to start at top:0 too, so the topmost strip of "click outside to close" was
 sitting under the header and a click there landed on header chrome instead of closing the
 drawer - half of "there is no way to close that when you're done." */
.drawer-backdrop {
  position: fixed;
  top: var(--mn-bar-h, 75px);
  left: 0;
  right: 480px;
  bottom: var(--mom-footer-h, 108px);
  background: rgba(0, 0, 0, 0.3);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
  z-index: 99;
}
.drawer-backdrop.open {
  opacity: 1;
  pointer-events: auto;
}

@media (max-width: 760px) {
  .drawer-overlay {
    width: 100%;
    max-width: 100%;
  }
  .drawer-backdrop {
    right: 0;
  }
}

/* ==========================================================================
 12. FOOTER + QUICK LINKS - mvp-dashboard V0.5.0, rebuilt V1.1.0 (JOB 1)
 The world-clock strip (section 9, .mom-worldclock, unchanged) generalized into a member
 footer, .mom-footer below - now just that ribbon, nothing else. The 13-entry link set that
 used to live inside .mom-footer moved out into its own panel above it
 (public/index.html's #quick-links-panel, a plain .mom-panel - see that panel's own HTML
 comment for why it deliberately carries no border/radius/background override anywhere in
 this file: the whole point is that it inherits .mom-panel's frame unmodified, the exact
 same way .telemetry-strip does, so the two panels' computed border/radius/background can
 never drift apart). The old bottom brand/version row that used to close .mom-footer is
 deleted, not just hidden; its version stamp lives in the top preview banner now.
 ========================================================================== */
/* V1.5.0 (FIX 5): #site-footer no longer carries .mom-footer - it is a plain .mom-panel now,
 for exactly the reason QUICK LINKS was changed in V1.1.0 (Anric, on 14/15: "the border
 around that is too dominant again, and I think it could be slimmer"). .mom-footer's frame
 was --mom-border-ops (a heavier, more saturated gold) over --mom-bg-ops-wash, against
 .mom-panel's --mom-border-soft over --mom-bg-panel used by 07 and 08. The rule below is kept
 rather than deleted because nothing requires its removal and a future member-footer block
 may want it, but no element in this build references it today. */
.mom-footer {
  border: 1px solid var(--mom-border-ops);
  border-radius: var(--mom-radius-panel);
  background: var(--mom-bg-ops-wash);
  overflow: hidden;
}
/* #world-clock (V1.3.0): now the absolutely-positioned city-pin LAYER inside .mom-tz-ribbon
 below, repurposed from the old flat digital-ribbon container the .mom-worldclock-cell rules
 further up this file styled (that markup is gone; those rules are unreferenced now, left
 in place rather than deleted since nothing here requires their removal). */
.mom-worldclock-ribbon {
  position: absolute;
  inset: 0;
}

/* ==========================================================================
 13. WORLD CLOCK OPS BAY - 09 WORLD CLOCK, V1.3.0, direct from Anric: "he prefers the
 original". Ported from docs/reference/v3-source/08_ROADMAP-CLOCK-ACTIONS.html (Box 14 World
 Clock + Box 15 Quick Actions + ops-foot row, markup) and 02A_STYLES.html (.ops-bay/.ops-grid/
 .ops-block/.ops-label/.tz-ribbon/.tz-daynight/.tz-sun/.city*/.qa*/.ops-foot*, CSS) - values
 harvested verbatim (colors, sizes, the sun's box-shadow pulse), selectors renamed with a
 mom- prefix and de-scoped from the source's ".mvp-cmd" wrapper (this page has no such
 wrapper) to avoid colliding with any existing class of the same short name elsewhere in this
 system. The map background is the local public/assets/map/worldmap.webp (2000x528, 77KB),
 never the source's filesafe.space URL - see #tz-ribbon's background-image below.
 ========================================================================== */
/* V1.5.0 (FIX 5, "It's too high."): the bay is a .mom-panel now, so it inherits that panel's
 own padding; the only override left here is a slightly tighter bottom, because this bay ends
 in a horizontal rule + footer row rather than in content. .mom-ops-bay no longer sets its
 own border/background/radius at all - that is the whole point of the class change on
 #site-footer, see the note above .mom-footer. overflow:hidden is kept: the sun glow inside
 the time-zone ribbon genuinely needs clipping at the panel edge.
 Measured height reduction comes from four places, none of them a squeeze on legibility:
 the panel padding is now .mom-panel's (16/22/20) instead of 20/22 all round, the ops grid's
 map column narrows from 1.6fr to 1.25fr (the ribbon is aspect-ratio locked, so a narrower
 column is directly a shorter ribbon and the map is never stretched), the label block loses
 its extra top/bottom padding, and the quick-action tiles lose a row of padding each. */
.mom-ops-bay {
  margin-top: 0;
  padding-bottom: var(--mom-space-6);
  position: relative;
  overflow: hidden;
}
.mom-ops-bay::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, transparent, rgba(240, 193, 115, 0.5), transparent);
  pointer-events: none;
}
.mom-ops-grid {
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: 22px;
  margin-bottom: var(--mom-space-5);
  align-items: start;
}
.mom-ops-block {
  padding: 0;
  min-width: 0;
}
.mom-ops-label {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-base);
  color: var(--mom-gold);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  margin-bottom: var(--mom-space-4);
  display: flex;
  align-items: center;
  gap: var(--mom-space-2);
}
.mom-ops-label .mom-num {
  background: rgba(240, 193, 115, 0.12);
  padding: 3px 9px;
  border-radius: var(--mom-radius-sm);
  font-size: 12px;
}

/* Time-zone ribbon: equirectangular world map, cities placed by real longitude/latitude
 (x% = ((lon+180)/360)*100, y% = ((90-lat)/180)*100 - the source's own formula, city
 percentages HARVESTED VERBATIM from its markup, see app.js's WORLD_CITIES). Locked to the
 map image's real aspect ratio (2000x528 = 3.79:1, same ratio as the source's 4000x1056
 original) so it fills without stretching. */
/* --tz-map is set inline by app.js's buildWorldClock() (a document-relative "assets/map/
 worldmap.webp" path, JS-set inline styles resolve against the document, not the stylesheet -
 the same reason orb.json's images are applied via JS/--cluster-bg rather than a literal
 url() written here), so this ONE rule works unmodified in both the split public/ dev build
 and the bundled dist/index.html build, where this whole file is inlined into a <style> tag
 and a literal relative url() here would resolve against the wrong base. */
/* V1.5.0 (FIX 5): the ribbon's own 1px gold border comes down from 0.22 to 0.14 alpha so the
 map frame reads as a hairline inside the panel rather than a second competing frame around
 box 14 - the "too dominant" border Anric named. Geometry (aspect-ratio, the map background)
 is untouched: the map is never stretched, the ribbon simply sits in a narrower grid column
 now and is therefore shorter. */
.mom-tz-ribbon {
  position: relative;
  width: 100%;
  aspect-ratio: 2000 / 528;
  background:
    linear-gradient(rgba(0, 0, 0, 0.82), rgba(0, 0, 0, 0.82)),
    var(--tz-map, none) center / 100% 100% no-repeat,
    var(--mom-bg-panel-solid);
  border: 1px solid rgba(240, 193, 115, 0.14);
  border-radius: var(--mom-radius-xl);
  overflow: hidden;
}
.mom-tz-daynight {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 50% 120% at var(--sun-x, 50%) 50%,
    rgba(255, 255, 255, 0.55) 0%,
    rgba(240, 193, 115, 0.45) 18%,
    rgba(240, 193, 115, 0.22) 40%,
    rgba(240, 193, 115, 0.08) 60%,
    rgba(0, 0, 0, 0) 80%
  );
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 2;
}
.mom-tz-sun {
  position: absolute;
  top: 50%;
  left: var(--sun-x, 50%);
  transform: translate(-50%, -50%);
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, #f0c173 45%, rgba(240, 193, 115, 0.6) 100%);
  box-shadow: 0 0 44px #f0c173, 0 0 90px rgba(240, 193, 115, 0.9), 0 0 150px rgba(240, 193, 115, 0.5);
  pointer-events: none;
  z-index: 3;
  animation: mom-tz-sun-pulse 3s ease-in-out infinite;
}
@keyframes mom-tz-sun-pulse {
  0%, 100% {
    box-shadow: 0 0 44px #f0c173, 0 0 90px rgba(240, 193, 115, 0.9), 0 0 150px rgba(240, 193, 115, 0.5);
  }
  50% {
    box-shadow: 0 0 60px #f0c173, 0 0 120px rgba(240, 193, 115, 1), 0 0 210px rgba(240, 193, 115, 0.7);
  }
}
.mom-city {
  position: absolute;
  transform: translate(-50%, -50%);
  text-align: center;
  z-index: 4;
}
.mom-city-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #fff;
  border: 1.5px solid #f0c173;
  box-shadow: 0 0 4px rgba(255, 255, 255, 0.5);
  margin: 0 auto 4px;
  animation: mom-city-pulse 2s ease-in-out infinite;
}
@keyframes mom-city-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.85; transform: scale(1.12); }
}
.mom-city-label {
  font-family: var(--mom-font-mono);
  font-size: 8.5px;
  color: #ccc;
  letter-spacing: 0.7px;
  text-transform: uppercase;
  background: rgba(0, 0, 0, 0.75);
  padding: 2px 5px;
  border-radius: var(--mom-radius-xs);
  border: 1px solid rgba(240, 193, 115, 0.3);
  white-space: nowrap;
}
.mom-city-time {
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: 12px;
  color: var(--mom-gold);
  margin-top: 3px;
  text-shadow: 0 0 6px rgba(240, 193, 115, 0.6);
}

/* Quick actions (Box 15). Real hrefs from the source: Record Testimonial, Contact Support
 (mailto), Ecosystem Map, Terms and Privacy. */
.mom-qa-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--mom-space-3);
}
/* V1.5.0 (FIX 5, "It's too high"): one step of vertical padding off each tile and a slightly
 smaller icon square. Four tiles in two rows, so this is 2 x 8px off box 15's height without
 touching the label size. */
.mom-qa {
  display: flex;
  align-items: center;
  gap: var(--mom-space-4);
  min-width: 0;
  padding: var(--mom-space-4) var(--mom-space-5);
  background: var(--mom-bg-inset);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: var(--mom-radius-lg);
  text-decoration: none;
  color: var(--mom-text-soft);
  font-size: var(--mom-text-md);
  transition: all 0.18s ease;
}
.mom-qa:hover,
.mom-qa:focus-visible {
  border-color: rgba(240, 193, 115, 0.5);
  background: rgba(240, 193, 115, 0.04);
  color: var(--mom-gold);
}
.mom-qa-ico {
  width: 26px;
  height: 26px;
  border-radius: var(--mom-radius-md);
  background: rgba(240, 193, 115, 0.1);
  color: var(--mom-gold);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  flex-shrink: 0;
}

/* Ops footer row: integration dots, version chip, Send Feedback link, copyright.
 V1.5.0 (FIX 5, direct from Anric: "the footer indicators are a little cut off and
 squashed."). Cause: this was a single space-between flex line whose children are all
 letterspaced monospace text. A flex item's default min-width is auto, but these items are
 themselves flex CONTAINERS (.mom-ops-ints) or plain divs holding nowrap-able text, and with
 five integration labels plus three right-hand items on one line at 1280 and below the row
 ran out of width and the labels clipped at their box edge.
 Fix, three parts, none of which shrinks any text:
   1. Every leaf label is white-space:nowrap and flex:0 0 auto, so it is an indivisible unit
      that WRAPS to the next line rather than being compressed below its own text width.
      This is what makes the "scrollWidth never exceeds clientWidth" check pass.
   2. The three right-hand items are grouped in .mom-ops-foot-meta so they wrap together as
      a block instead of one at a time, which is what made the row look squashed rather than
      simply wrapped.
   3. A real row-gap, so a wrapped line has vertical breathing room instead of colliding
      with the line above it. */
.mom-ops-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: var(--mom-space-5);
  border-top: 1px solid rgba(240, 193, 115, 0.12);
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-sm);
  color: var(--mom-text-muted);
  letter-spacing: 1.2px;
  flex-wrap: wrap;
  column-gap: var(--mom-space-6);
  row-gap: var(--mom-space-4);
}
.mom-ops-ints {
  display: flex;
  align-items: center;
  column-gap: var(--mom-space-6);
  row-gap: var(--mom-space-3);
  flex-wrap: wrap;
  min-width: 0;
}
.mom-ops-int {
  display: flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  white-space: nowrap;
  color: var(--mom-text-soft);
}
.mom-ops-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--mom-green);
  box-shadow: 0 0 6px var(--mom-green);
  flex-shrink: 0;
}
/* The version chip, Send Feedback and the copyright line, grouped so they wrap as one unit. */
.mom-ops-foot-meta {
  display: flex;
  align-items: center;
  column-gap: var(--mom-space-6);
  row-gap: var(--mom-space-3);
  flex-wrap: wrap;
  min-width: 0;
}
.mom-ops-foot-meta > * {
  flex: 0 0 auto;
  white-space: nowrap;
}
.mom-ops-feedback-link {
  color: var(--mom-gold);
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 1px dotted rgba(240, 193, 115, 0.35);
}
/* Below 560 the copyright is the one item that is long enough to need the whole line to
 itself; letting it have one keeps every other label on a single uncramped row. */
@media (max-width: 560px) {
  .mom-ops-foot-meta {
    width: 100%;
  }
  .mom-ops-copyright {
    flex-basis: 100%;
  }
}

@media (max-width: 900px) {
  .mom-ops-grid {
    grid-template-columns: 1fr;
  }
  .mom-qa-row {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 560px) {
  .mom-city-label {
    display: none; /* pins + times stay, labels drop first on very narrow ribbons, matching the source's own mobile behavior */
  }
}
@media (prefers-reduced-motion: reduce) {
  .mom-tz-sun {
    animation: none;
  }
  .mom-city-dot {
    animation: none;
  }
}

/* QUICK LINKS tile grid (V1.1.0, JOB 1, renamed from "footer links"). Rendered by
 buildQuickLinks() in app.js from data/footer.json. FOOTER-SPEC.md revision 3: "let's have
 it all caps in boxes so that those become linkable pages" - each entry is its own flat box
 (the .mom-inset recipe: solid dark fill, hairline border, panel-adjacent radius) rather than
 the old shared-border row cells, and the whole tile is the click target, not just the label
 text. No status badge anywhere in here anymore (revision 3 point 1). A tile with no real
 destination (action "none", or status "coming" as a defensive second gate - see
 quickLinkTileHTML() in app.js) renders .mom-quicklink-tile--disabled: dimmed, aria-disabled,
 non-interactive, same "never a bare hash link" rule drawer-item already follows for a
 catalog item with no url. */
/* V1.3.0 (08 QUICK LINKS, direct from Anric, "the thing that's actually going to make
 money"): grid-with-auto-fill used to wrap the 13 tiles onto 2 rows well before any narrow
 breakpoint (measured: already 2 rows at 1680px, the widest width this page targets) because
 auto-fill computes as many 150px-minimum columns as fit and wraps the rest - the exact
 failure mode "never wrap to a second line" rules out. Switched to a single flex row,
 nowrap, that never breaks: every tile is flex:1 1 0 so at wide widths they share the row
 evenly (scaling up), min-width holds a legible floor, and once 13 tiles at that floor would
 exceed the panel's width the row scrolls horizontally (overflow-x:auto) instead of wrapping.
 Verified at 1680/1280/1024/768/390px - one row, zero wraps, at every width; see the report
 for the exact tile width and scroll state measured at each. Tile height also cut (52px min
 -> 40px) and the panel heading compacted the same targeted way .telemetry-strip already
 was (below), per "too much space lost to its own header and to oversized buttons". */
.mom-quicklinks-grid {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--mom-space-3);
  overflow-x: auto;
  overflow-y: hidden;
  padding-bottom: 2px; /* room for the scrollbar without clipping tile borders under it */
  scrollbar-width: thin;
}
.quick-links-panel .mom-section-label {
  font-size: var(--mom-text-xs); /* was --mom-text-lg (16px), same compaction telemetry-strip uses */
  margin-bottom: var(--mom-space-2);
  padding-bottom: 2px;
}
.mom-quicklink-tile {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px; /* only visible when a tile has two children, e.g. a locked member tile's
               padlock + label (V1.7.0, REVISION 8) - a no-op on every single-child tile */
  flex: 1 1 0;
  min-width: 104px; /* REVISION 10, V4.6.2: was 92px. Raised alongside the label wrap fix
    below so a two-line label (e.g. "GENIUS AMPLIFIER") has room to break at a sane point
    instead of every long label wrapping at the tightest possible width. The row still never
    wraps to a second line - only the label text inside a tile is allowed to. */
  min-height: 40px; /* was 52px: "oversized buttons" trim, still comfortably tappable. A
    two-line label (see .mom-quicklink-label below) grows a tile past this floor on its own;
    this stays a min-height, never a fixed height, so short single-line labels are untouched. */
  padding: var(--mom-space-2) var(--mom-space-3);
  margin: 0;
  background: var(--mom-bg-inset);
  color: var(--mom-text-body);
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-sm);
  font-weight: 600;
  letter-spacing: 0.6px;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: var(--mom-radius-lg); /* never pill */
  cursor: pointer;
  appearance: none;
  overflow: hidden; /* clips the hover-shimmer sweep below to the tile's own rounded box */
  transition:
    border-color 0.18s ease,
    background 0.18s ease,
    color 0.18s ease;
}
a.mom-quicklink-tile:hover,
button.mom-quicklink-tile:hover,
a.mom-quicklink-tile:focus-visible,
button.mom-quicklink-tile:focus-visible {
  background: var(--mom-gold-dim);
  border-color: var(--mom-border);
  color: var(--mom-gold);
}
/* Hover shimmer (V1.3.0, direct from Anric): reuses the existing mom-hairline-shimmer
 keyframe and its --mom-dur-hairline tempo (4s, section 3) rather than inventing a new
 sweep/timing - the same idiom already used for .mom-motion-hairline, just triggered on
 hover/focus instead of looping at rest, and only for one pass (no infinite) since a
 continuously-looping shimmer on every clickable tile would be noisy at this density. */
.mom-quicklink-tile::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 35%,
    rgba(240, 193, 115, 0.35) 50%,
    transparent 65%
  );
  transform: translateX(-120%);
  opacity: 0;
  pointer-events: none;
}
a.mom-quicklink-tile:hover::after,
button.mom-quicklink-tile:hover::after,
a.mom-quicklink-tile:focus-visible::after,
button.mom-quicklink-tile:focus-visible::after {
  animation: mom-quicklink-shimmer var(--mom-dur-hairline) ease-in-out 1;
}
@keyframes mom-quicklink-shimmer {
  0% {
    transform: translateX(-120%);
    opacity: 0;
  }
  35% {
    opacity: 1;
  }
  100% {
    transform: translateX(120%);
    opacity: 0;
  }
}
/* Dimmed/disabled tile (Opus Card, Bookstore, Webinar, Marketplace today - see
 quickLinkTileHTML()'s own comment in app.js for why this is a general rule, not a
 hardcoded label list). Reduced opacity is the same muted treatment already used elsewhere
 in this file for an inactive/background element (see section 6's blob opacity and the
 telemetry meter's own dim state), not a one-off invented value. No hover state: a disabled
 tile never becomes interactive on hover, including visually - and never gets the shimmer
 above, which only binds to a.mom-quicklink-tile/button.mom-quicklink-tile, never the plain
 div.mom-quicklink-tile--disabled. */
.mom-quicklink-tile--disabled {
  opacity: 0.45;
  color: var(--mom-text-muted);
  cursor: default;
}
/* REVISION 10, V4.6.2, direct from Anric: "business tools is cut off. That word is not
 playing out fully." nowrap+ellipsis was clipping every label longer than the tile's min-
 width fits on one line - not just Business Tools, also the longer MVP tool names (Genius
 Amplifier, Opus Citadel, Maven Matrix). Switched to a real two-line wrap: normal white-space
 so the label breaks at the space between words, a tight 1.15 line-height so two lines still
 land close to the tile's original min-height (see min-width/min-height above, raised the
 same revision), and break-word as a floor for the one all-caps single-word label
 (Magnanimus, Marketplace) that has no space to wrap on. No label is ever clipped or
 ellipsised again at any of the three widths this was verified at (1280/1440/1680px); the
 flex row itself still never wraps to a second line, only the text inside one tile does. */
.mom-quicklink-label {
  overflow: visible;
  white-space: normal;
  line-height: 1.15;
  overflow-wrap: break-word;
  word-break: normal;
}
@media (prefers-reduced-motion: reduce) {
  .mom-quicklink-tile::after {
    display: none;
  }
}

/* MVP QUICK LINKS member row (V1.7.0, REVISION 8, 2026-08-04, direct from Anric: "a second
 row, possibly above the Quick Links but inside the same navigation, that is a shortcut to
 MVP links"). Two <div class="mom-quicklinks-tier"> wrap the existing #quick-links-panel's two
 <nav class="mom-quicklinks-grid"> rows - the member row first, then the unchanged public row.
 REVISION 9 (V4.6.1) boxed the member tier in a bordered/tinted band with a "MVP QUICK LINKS"
 heading above it. REVISION 10 (V4.6.2) undid the box - flat row, tiles carried the contrast,
 one-line "MVP" chip. REVISION 11 below (V4.6.4) is the current, live rule; both paragraphs
 above are history. */
/* REVISION 11 (V4.6.4, 2026-08-05, direct from Anric on the V4.6.2 result): "The whole MVP box
 is way too big. It's twice the size of the quick links down below, and the color needs to be
 bifurcated so that the background is darker than the actual buttons." Two problems, one root
 cause: REVISION 10 only had 6 tiles at flex:1 1 0 sharing the row with the 13-tile public row
 below it, so each member tile was more than twice as wide as a public one, AND
 REVISION 10's tile fill (a gold wash over --mom-bg-inset) was close enough in value to the
 bare panel surface behind it to read as "the same color as the buttons." The tile list itself
 is now data (11 tiles, magnum-workers/_shared/footer.json REVISION 9 - not this file's
 concern), which alone fixes the proportion; this block fixes the color and re-confirms the
 height.

 COLOUR, bifurcated per his explicit instruction, recessed well over flat band: the row
 (.mom-quicklinks-tier--member) gets --mom-bg-panel-solid, a genuinely opaque near-black
 (#0d0d0d, the same token the ops console uses for a card surface) - no border at all, so nothing
 here reads as "a box" the way REVISION 9's bordered band did. The tiles then sit LIGHTER and
 warmer on top of that solid dark well: a faint white wash (rgba(255,255,255,0.05), the same
 literal the base .mom-quicklink-tile border already uses elsewhere in this file, so this is
 not a new value family) plus a stronger gold wash than REVISION 10's (0.14 -> 0.18 alpha),
 composited over the well the two together land at roughly rgb(64,55,42) against the well's
 rgb(13,13,13) - a real, measured lightness gap, not an eyeballed one. --mom-border-strong
 (0.5-alpha gold) on the tiles is unchanged from REVISION 10 and is what makes them read as
 buttons sitting IN the well rather than holes cut into it.

 HEIGHT: no rule in this block sets a height. min-width/min-height/padding/font-size on
 .mom-quicklink-tile (shared with the public row, unchanged this revision) are what already
 hold every tile to one line - the 11 new labels (footer.json REVISION 9) are short single
 words for exactly this reason, so the row needs no extra room the public row does not also
 get. Verified by measurement, not assumed: see the V4.6.4 report for the two rows' getBounding
 ClientRect heights side by side. */
.mom-quicklinks-tier--member {
  display: flex;
  align-items: stretch;
  gap: var(--mom-space-2);
  background: var(--mom-bg-panel-solid);
  border-radius: var(--mom-radius-md);
  padding: var(--mom-space-2);
}
.mom-quicklinks-tier--member .mom-quicklinks-grid--member {
  flex: 1 1 auto;
  min-width: 0; /* lets the nav's own overflow-x:auto keep working inside a flex item */
}
/* Restored to --mom-space-3 (was bumped to --mom-space-4 in REVISION 9 to clear the box's own
 border/padding, then back to --mom-space-3 in REVISION 10) - REVISION 11's well above carries
 its own padding now, so this only needs to clear the well's outer edge from the public row
 below it. */
.mom-quicklinks-tier + .mom-quicklinks-tier {
  margin-top: var(--mom-space-3);
}
/* The "MVP" marker chip. flex:0 0 auto so it never shrinks or grows with the row; align-
 items:stretch on the parent above makes it match the tile row's own height without a fixed
 height baked in here. Squared corners only (--mom-radius-sm, 5px - well inside the 3-6px
 estate ceiling, never a pill), a thin gold frame and a faint gold wash so it reads as a real
 chip rather than plain text sitting in the row. Unchanged in REVISION 11 - it already read
 correctly against REVISION 10's flat row and reads at least as well against this revision's
 darker well. */
.mom-quicklinks-tier-label {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--mom-space-3);
  background: var(--mom-gold-dim);
  border: 1px solid var(--mom-border-strong);
  border-radius: var(--mom-radius-sm);
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--mom-gold);
  white-space: nowrap;
}
/* Member tiles: see the COLOUR paragraph in the REVISION 11 comment above. A faint white wash
 (rgba(255,255,255,0.05), the same literal value the base .mom-quicklink-tile border already
 uses below) plus a stronger gold wash than REVISION 10 (0.14 -> 0.18 alpha) put these tiles
 visibly lighter and warmer than the solid --mom-bg-panel-solid well they now sit in - the
 bifurcation Anric asked for, well darker than button, never the reverse. --mom-border-strong
 (0.5-alpha gold, the same token .id-tier and .welcome-vchip use) is unchanged from REVISION 10
 and does the rest of the "these are buttons, not holes" work. */
.mom-quicklinks-grid--member .mom-quicklink-tile {
  background:
    linear-gradient(rgba(240, 193, 115, 0.18), rgba(240, 193, 115, 0.18)),
    rgba(255, 255, 255, 0.05);
  border-color: var(--mom-border-strong);
}
/* Locked member tile (visitor is not a signed-in member - see isSignedInMember() in app.js).
 Still a real <a> to /mvp-access, never aria-disabled and never the dimmed treatment
 .mom-quicklink-tile--disabled uses above: a locked tile is an honest, clickable door, not a
 broken promise. Muted at rest, same gold hover/focus and one-pass shimmer as an unlocked
 tile - "locked" is a visual state, not a different interaction. */
.mom-quicklink-tile--locked {
  color: var(--mom-text-muted);
}
a.mom-quicklink-tile--locked:hover,
a.mom-quicklink-tile--locked:focus-visible {
  color: var(--mom-gold);
}
.mom-quicklink-lock {
  flex: 0 0 auto;
  display: inline-flex;
}

@media (prefers-reduced-motion: reduce) {
  .drawer-overlay {
    transition: none;
  }
  .drawer-backdrop {
    transition: none;
  }
}

/* ==========================================================================
 13. MOBILE-FIRST NOTES
 Every component above sets no fixed viewport-dependent width; panels
 fill their container, meters and badges scale with font-size. The
 two production surfaces both go responsive around 1100px (member) and
 have a fixed desktop console shell (ops, not designed for mobile at
 all - it is an operator tool, not a member surface). A mvp-dashboard
 rebuild should treat the member dashboard's ~1100px breakpoint as the
 only one that matters, since ops's layout was never meant to run on a
 phone and Anric reads this product on his phone.
 ========================================================================== */

/* ==========================================================================
 14. FIND BY TOPIC // VIDEO BROWSER - 12, V1.5.0 (FIX 7), direct from Anric: "The current
 LIVE version has a way to actually search videos and show videos by topic."
 Ported from docs/reference/v3-source/02A_STYLES.html (.topic-browser-wrap /
 .topic-browser-summary / .topic-browser / .tb-controls / .tb-search / .tb-active* /
 .tb-cloud / .tb-tag* / .tb-results / .tb-row* / .tb-empty), values harvested verbatim
 (colors, sizes, radii, the auto-fill result grid at 280px minimum), selectors de-scoped
 from the source's ".mvp-cmd" wrapper, which this page does not have.
 THREE deliberate deviations, all estate rules, none of them behavioral:
   1. The source's .tb-tag-more carries font-style:italic. Italics are banned estate-wide,
      so that button reads as a quieter, letterspaced tone instead.
   2. The source's summary sets its own h3/meta typography; this build reuses the page's own
      .mom-section-label / .mom-panel-num idiom so panel 12 is labelled exactly like panels
      01-08 and 14/15 rather than introducing a second heading style.
   3. The expand/collapse marker is '+' / '-' in plain ASCII, not the source's fullwidth
      characters, so it renders identically without depending on a CJK-capable fallback font.
 ========================================================================== */
.topic-browser-panel {
  padding-bottom: var(--mom-space-6);
}
.topic-browser-wrap {
  margin: 0;
}
.topic-browser-summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--mom-space-4);
}
.topic-browser-summary::-webkit-details-marker {
  display: none;
}
.topic-browser-summary::marker {
  content: "";
}
.topic-browser-summary::before {
  content: "+";
  color: var(--mom-gold);
  font-family: var(--mom-font-mono);
  font-size: 20px;
  line-height: 1;
  width: 18px;
  flex-shrink: 0;
  display: inline-block;
  text-align: center;
}
.topic-browser-wrap[open] > .topic-browser-summary::before {
  content: "-";
}
/* The label inside the summary is the same .mom-section-label used on every other panel, so
 it must not re-apply that class's own bottom margin/border inside a flex summary row. */
.topic-browser-summary .topic-browser-title {
  margin: 0;
  padding: 0;
  border: 0;
  flex: 0 1 auto;
}
.topic-browser-meta {
  font-family: var(--mom-font-mono);
  font-size: var(--mom-text-sm);
  color: var(--mom-text-muted);
  letter-spacing: 1.2px;
  text-transform: uppercase;
  margin-left: auto;
}
.tb-expand-hint {
  color: var(--mom-text-muted);
  margin-left: 10px;
  letter-spacing: 0.8px;
  text-transform: none;
}
.topic-browser-wrap[open] .tb-expand-hint {
  display: none;
}
.topic-browser {
  background: linear-gradient(
    180deg,
    rgba(15, 15, 20, 0.85) 0%,
    rgba(10, 10, 14, 0.85) 100%
  );
  border: 1px solid rgba(240, 193, 115, 0.18);
  border-radius: var(--mom-radius-panel);
  padding: 16px 20px;
  margin-top: 14px;
}
.tb-controls {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 14px;
  flex-wrap: wrap;
}
.tb-search {
  flex: 1 1 240px;
  min-width: 0;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(240, 193, 115, 0.3);
  border-radius: var(--mom-radius-sm);
  color: var(--mom-text);
  padding: 9px 14px;
  font-family: var(--mom-font-mono);
  font-size: 13px;
  letter-spacing: 0.5px;
  outline: none;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.tb-search:focus,
.tb-search:focus-visible {
  border-color: var(--mom-gold);
  background: rgba(0, 0, 0, 0.7);
}
.tb-search::placeholder {
  color: var(--mom-text-muted);
}
.tb-active {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.tb-active-label {
  font-family: var(--mom-font-mono);
  font-size: 11px;
  letter-spacing: 1px;
  color: var(--mom-text-muted);
  text-transform: uppercase;
}
.tb-active-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgba(240, 193, 115, 0.14);
  border: 1px solid rgba(240, 193, 115, 0.5);
  color: var(--mom-gold);
  padding: 4px 10px;
  border-radius: var(--mom-radius-xs);
  font-family: var(--mom-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.6px;
}
.tb-active-chip button {
  background: transparent;
  border: none;
  color: var(--mom-gold);
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  padding: 0 2px;
}
.tb-cloud {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  padding: 14px 0;
  border-top: 1px solid rgba(240, 193, 115, 0.12);
  border-bottom: 1px solid rgba(240, 193, 115, 0.12);
  margin-bottom: 16px;
}
.tb-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgba(0, 0, 0, 0.45);
  border: 1px solid rgba(240, 193, 115, 0.25);
  color: var(--mom-text-soft);
  padding: 6px 12px;
  border-radius: var(--mom-radius-xs); /* never pill */
  cursor: pointer;
  font-family: var(--mom-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.4px;
  transition: all 0.18s ease;
}
.tb-tag:hover,
.tb-tag:focus-visible {
  background: rgba(240, 193, 115, 0.1);
  border-color: var(--mom-gold);
  color: var(--mom-gold);
}
.tb-tag.is-active {
  background: var(--mom-gold);
  border-color: var(--mom-gold);
  color: #0a0a0f;
}
.tb-tag-n {
  font-size: 10px;
  opacity: 0.65;
  background: rgba(0, 0, 0, 0.25);
  padding: 1px 5px;
  border-radius: var(--mom-radius-xs);
}
.tb-tag.is-active .tb-tag-n {
  background: rgba(0, 0, 0, 0.25);
  color: #0a0a0f;
}
/* Deviation 1: the source styles this with font-style:italic. No italics estate-wide. */
.tb-tag-more {
  color: var(--mom-text-muted);
  letter-spacing: 1.4px;
  text-transform: uppercase;
}
.tb-results {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
}
.tb-row {
  display: flex;
  flex-direction: column;
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid rgba(240, 193, 115, 0.22);
  border-radius: var(--mom-radius-lg);
  padding: 14px 16px;
  text-decoration: none;
  color: inherit;
  transition: all 0.18s ease;
}
.tb-row:hover,
.tb-row:focus-visible {
  background: rgba(240, 193, 115, 0.08);
  border-color: var(--mom-gold);
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
}
.tb-row-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.tb-row-program {
  font-family: var(--mom-font-mono);
  font-size: 10.5px;
  color: var(--mom-gold);
  letter-spacing: 1.1px;
  text-transform: uppercase;
}
.tb-row-status {
  font-family: var(--mom-font-mono);
  font-size: 9.5px;
  padding: 2px 7px;
  border-radius: var(--mom-radius-xs);
  letter-spacing: 0.7px;
  text-transform: uppercase;
  flex-shrink: 0;
  background: rgba(74, 222, 128, 0.12);
  border: 1px solid rgba(74, 222, 128, 0.35);
  color: var(--mom-green);
}
.tb-row-status--coming {
  background: rgba(240, 193, 115, 0.12);
  border-color: rgba(240, 193, 115, 0.35);
  color: var(--mom-gold);
}
/* V1.6.2 (JOB 2): dead-link state for the video browser, same red/dim treatment as
 .drawer-item--status-dead above, reusing the same isDead()/state.deadRules mechanism. */
.tb-row-status--dead {
  background: rgba(240, 90, 90, 0.12);
  border-color: rgba(240, 90, 90, 0.35);
  color: var(--mom-red);
}
.tb-row--dead {
  cursor: default;
  color: var(--mom-text-dim);
  opacity: 0.7;
}
.tb-row--dead:hover,
.tb-row--dead:focus-visible {
  background: rgba(0, 0, 0, 0.4);
  border-color: rgba(240, 193, 115, 0.22);
  transform: none;
  box-shadow: none;
}
.tb-row-title {
  font-family: var(--mom-font-head);
  font-weight: var(--mom-font-head-weight);
  font-size: 14px;
  color: var(--mom-text);
  line-height: 1.3;
  margin-bottom: 6px;
}
.tb-row-desc {
  font-size: 12px;
  color: var(--mom-text-soft);
  line-height: 1.45;
  margin-bottom: 10px;
}
.tb-row-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: auto;
}
.tb-row-tag {
  background: rgba(240, 193, 115, 0.08);
  border: 1px solid rgba(240, 193, 115, 0.18);
  color: var(--mom-gold-ops);
  padding: 3px 8px;
  border-radius: var(--mom-radius-xs);
  font-family: var(--mom-font-mono);
  font-size: 10px;
  letter-spacing: 0.4px;
  cursor: pointer;
  transition: all 0.15s ease;
}
.tb-row-tag:hover {
  background: rgba(240, 193, 115, 0.18);
  border-color: var(--mom-gold);
  color: var(--mom-gold);
}
.tb-empty {
  grid-column: 1 / -1;
  text-align: center;
  padding: 40px 20px;
  color: var(--mom-text-muted);
  font-family: var(--mom-font-mono);
  font-size: 12.5px;
  letter-spacing: 0.8px;
}
@media (max-width: 560px) {
  .topic-browser {
    padding: 14px;
  }
  .topic-browser-meta {
    margin-left: 0;
    flex-basis: 100%;
  }
}
@media (prefers-reduced-motion: reduce) {
  .tb-row,
  .tb-tag,
  .tb-row-tag,
  .tb-search {
    transition: none;
  }
  .tb-row:hover,
  .tb-row:focus-visible {
    transform: none;
  }
}
