/*
 * prototype/style.css
 *
 * Kate/zentree.us aesthetic: deep indigo/night-sky background, warm gold
 * accents, serif display headings, generous whitespace, calm and
 * uncluttered. System font stack only -- no external fonts, no CDN,
 * nothing that touches the network at runtime.
 *
 * Theming: every color in this file flows from the custom properties
 * declared in :root below. Dark is the default (unchanged) palette; a
 * light "dawn" variant lives in the theme blocks near the bottom of this
 * file (search "THEME SYSTEM"). Two token families exist for golds:
 *   --gold / --gold-bright / --gold-dim   -- TEXT-safe (AA contrast on the
 *     current theme's ground), used for headings/labels/svg-text/borders.
 *   --gold-fill / --gold-fill-bright       -- vivid, used for self-contained
 *     fills (buttons, chips, progress bars, decorative wheel strokes) that
 *     carry their own on-fill text color rather than needing to contrast
 *     against the page background.
 * In dark mode the two families render identically (fill == text values,
 * same as before this theming pass existed); in light mode they diverge on
 * purpose -- see the THEME SYSTEM comment for why.
 */

:root {
  --ink: #eef0fb;
  --ink-dim: #b9beda;
  --ink-muted: #8d92b8;
  --ink-strong: #0a0b1a; /* always-dark ink for text sitting ON TOP of a gold fill (buttons, selection, badges) -- independent of --ink so it doesn't invert in light mode */
  --night-950: #0a0b1a;
  --night-900: #10122a;
  --night-800: #171a3a;
  --night-700: #20244a;
  --night-600: #2b2f5c;
  --gold: #d8b464;
  --gold-bright: #f0cf8a;
  --gold-dim: #9c8452;
  --gold-fill: var(--gold);
  --gold-fill-bright: var(--gold-bright);
  --gold-fill-rgb: 216, 180, 100;
  --gold-fill-bright-rgb: 240, 207, 138;
  --warm: #c9622a;
  --cool: #3d6b8a;
  --warm-rgb: 201, 98, 42;
  --cool-rgb: 61, 107, 138;
  --warm-tint: #e59a6c;
  --cool-tint: #7fb2d1;
  --fire: #c9622a;
  --earth: #5c7a4d;
  --air: #c9a227;
  --water: #3d6b8a;
  --ink-muted-rgb: 141, 146, 184;
  --ok: #7fae6a;
  --err: #e08a8a;
  --edge-shadow-rgb: 0, 0, 0;
  --overlay-a-rgb: 23, 26, 58;
  --overlay-b-rgb: 10, 11, 26;
  --serif: Georgia, Cambria, "Times New Roman", Times, serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --radius: 10px;
  --shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
  color-scheme: dark;

  /* Celestial-tarot landing scene (docs/UX-NAV.md "Landing wall + celestial
     scene", Troy 2026-07-07/08): --landing-sky-* is the ONLY landing token
     that flips between themes (see the THEME SYSTEM overrides below) -- a
     deep-twilight lift in dawn mode, never a bright band, because the
     landing is a night sky by IDENTITY in both themes. The rest of the
     --landing-* family (wordmark/tagline/frame/moon/star/water) is
     deliberately NOT re-declared in either theme override block: fixed at
     their dark-mode-equivalent values always, because they render directly
     against the landing's own always-dark scene -- unlike --gold-bright/
     --ink-muted elsewhere on the page, they must NOT deepen for light-
     mode's near-white PAGE ground, since they never sit on that ground.
     Palette reference: /home/troy/files/blue-celestial-tarot-deck.png
     (docs/design-ref-celestial-tarot.png) -- deep navy ground, cool-blue
     nebula dust, silver + pale-gold filigree linework. */
  --landing-sky-top: #0a1030;
  --landing-sky-mid: #141c3e;
  --landing-sky-bottom: #1e2a55;
  --landing-water-base: #0a0f26;
  --landing-wordmark-top: #f0d9a0;
  --landing-wordmark-bottom: #cda45c;
  --landing-wordmark-glow-rgb: 240, 217, 160;
  --landing-tagline-ink: #c7cdea;
  --landing-frame-gold: #cfa758;
  --landing-frame-gold-rgb: 207, 167, 88;
  --landing-frame-silver: #cdd6ef;
  --landing-frame-silver-rgb: 205, 214, 239;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: radial-gradient(ellipse at top, var(--night-800) 0%, var(--night-950) 60%);
  background-attachment: fixed;
  color: var(--ink);
  font-family: var(--sans);
  line-height: 1.55;
}

body { min-height: 100vh; overflow-x: hidden; }

::selection { background: var(--gold-fill); color: var(--ink-strong); }

/* ------------------------------------------------------------------ */
/* Header / footer                                                     */
/* ------------------------------------------------------------------ */

.site-header {
  position: relative; /* anchors .theme-toggle to the top-right corner */
  text-align: center;
  padding: 3rem 1.5rem 1.5rem;
}
.site-header h1 {
  font-family: var(--serif);
  font-size: 3rem;
  letter-spacing: 0.06em;
  margin: 0 0 0.4rem;
  background: linear-gradient(180deg, var(--gold-bright), var(--gold));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* "Stellaverum" (11 chars) is ~1.8x "Lumora" (6 chars) -- desktop has
     plenty of room (max-width:900px main), but the 640px/380px mobile
     rules below shrink font-size AND letter-spacing (not just font-size)
     since letter-spacing's per-character gap cost scales with word
     length too; a longer word needs both dialed back to keep the same
     physical footprint on a narrow phone. */
}
.tagline {
  color: var(--ink-muted);
  font-style: italic;
  margin: 0;
  font-size: 1rem;
}

/* ------------------------------------------------------------------ */
/* Landing wall + celestial scene (docs/UX-NAV.md "Landing wall +      */
/* celestial scene", Troy 2026-07-07/08)                                */
/*                                                                      */
/* Signed-out (API reachable) shows ONLY #landingView: a full-viewport  */
/* animated night scene (prototype/constellations.js paints            */
/* #heroCanvas) styled after the celestial-tarot reference              */
/* (docs/design-ref-celestial-tarot.png) -- deep navy ground, cool-blue */
/* milky way, silver/gold filigree constellations + frame, a luminous   */
/* crescent-moon/compass-star centerpiece, a low stylized mountain+tree */
/* silhouette mirrored in a rippling water band -- with the wordmark/   */
/* tagline/single sign-in CTA overlaid, centered. Signed-in (or the     */
/* API-unreachable fallback) shows #appView instead -- the full tab-    */
/* based app behind a slim #appTopbar carrying only the account         */
/* cluster. Toggled by prototype/account.js's updateViewMode() setting  */
/* `hidden` directly on #landingView/#appView (JS is the source of      */
/* truth for which one applies); #landingView is visible and #appView   */
/* is `hidden` in the RAW markup so a not-yet-decided page (before the  */
/* async GET /me resolves) fails toward the signed-out landing rather   */
/* than flashing the full app -- same "fail toward requiring sign-in"   */
/* direction the old per-tab auth gate used.                            */
/* ------------------------------------------------------------------ */

.landing-view {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.5rem 12vh;
  /* Sky through 65% (the HORIZON_RATIO prototype/constellations.js also
     uses for its own math, so the canvas-painted horizon glow/mountain
     silhouette lines up exactly with this gradient's own tone change),
     then a flat "water" base tone for the reflection band below it --
     the canvas draws the actual dimmed/rippled reflection ON TOP of this
     flat tone (mostly-transparent clears elsewhere let this CSS gradient
     show through, so the whole sky+water backdrop is theme-reactive for
     free with zero JS/canvas cost). */
  background: linear-gradient(180deg,
    var(--landing-sky-top) 0%,
    var(--landing-sky-mid) 46%,
    var(--landing-sky-bottom) 65%,
    var(--landing-water-base) 65%,
    var(--landing-water-base) 100%);
}
.landing-view[hidden] { display: none; } /* the [hidden]-loses-to-flex gotcha this file's own comments flag elsewhere -- .landing-view's own display:flex ties with [hidden] at equal specificity */

/* The scene canvas (prototype/constellations.js): fills #landingView
   exactly, sits BEHIND the ornate frame + wordmark/tagline/CTA content
   (z-index 0 vs .landing-frame/.landing-content above), never intercepts
   clicks/taps. */
.landing-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}

/* Ornate celestial frame (docs/design-ref-celestial-tarot.png): a thin
   double silver/gold line inset from the viewport edge, evoking the
   reference card's border, plus a small mirrored corner flourish (one
   path, reused via CSS transform mirroring so all four corners stay in
   sync from a single definition -- see the corner-tl/tr/bl/br rules
   below). Purely decorative (aria-hidden, pointer-events: none), and
   entirely static CSS/SVG -- zero per-frame animation cost. */
.landing-frame {
  position: absolute;
  inset: 14px;
  z-index: 1;
  border: 1px solid rgba(var(--landing-frame-gold-rgb), 0.55);
  pointer-events: none;
}
.landing-frame::before {
  content: '';
  position: absolute;
  inset: 7px;
  border: 1px solid rgba(var(--landing-frame-silver-rgb), 0.32);
}
.landing-frame-corner {
  position: absolute;
  width: clamp(30px, 6vw, 56px);
  height: clamp(30px, 6vw, 56px);
  overflow: visible;
}
.landing-frame-corner .flourish-arc-outer { fill: none; stroke: var(--landing-frame-gold); stroke-width: 1.3; opacity: 0.7; }
.landing-frame-corner .flourish-arc-inner { fill: none; stroke: var(--landing-frame-silver); stroke-width: 1; opacity: 0.5; }
.landing-frame-corner .flourish-spark { fill: var(--landing-frame-silver); opacity: 0.8; }
.corner-tl { top: -2px; left: -2px; }
.corner-tr { top: -2px; right: -2px; transform: scaleX(-1); }
.corner-bl { bottom: -2px; left: -2px; transform: scaleY(-1); }
.corner-br { bottom: -2px; right: -2px; transform: scale(-1, -1); }

.landing-content {
  position: relative;
  z-index: 2;
  max-width: 640px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* AA-legibility guard: the scene's constellation line-art/stars drift and
   can land directly behind the wordmark/tagline (they're randomly
   positioned/timed, on purpose -- docs/design-ref-celestial-tarot.png's
   own "constellations glow softly in/out" spec). A soft radial vignette
   behind the text block guarantees contrast regardless of what the
   canvas happens to be drawing there at any given moment, rather than
   hoping the scene never wanders into the text zone. Purely decorative
   (no pointer-events), sits behind the text (z-index -1 relative to
   .landing-content's own stacking context) but above the canvas/frame. */
.landing-content::before {
  content: '';
  position: absolute;
  inset: -2rem -3.5rem;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(6, 9, 20, 0.6) 0%, rgba(6, 9, 20, 0.32) 55%, rgba(6, 9, 20, 0) 78%);
}

/* Wordmark: larger + a subtle luminous glow (this is the brand's first
   impression) -- text-shadow works fine alongside the existing
   background-clip:text gradient fill (the shadow renders from the
   glyphs' shape regardless of what fills them). Uses the FIXED
   --landing-wordmark-* tokens (see :root's comment) rather than
   --gold-bright/--gold, which deepen for light-mode's near-white PAGE
   ground -- the landing's own ground stays dark in both themes, so the
   wordmark must stay the bright-on-dark gold in both themes too. Sits
   just below the canvas's painted moon/compass-star centerpiece (that
   glow is soft and low-contrast by design, see constellations.js, so it
   frames the wordmark rather than competing with it). */
.landing-content h1 {
  font-family: var(--serif);
  font-size: 3.5rem;
  letter-spacing: 0.06em;
  margin: 0 0 0.4rem;
  background: linear-gradient(180deg, var(--landing-wordmark-top), var(--landing-wordmark-bottom));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-shadow: 0 0 22px rgba(var(--landing-wordmark-glow-rgb), 0.55), 0 0 54px rgba(var(--landing-wordmark-glow-rgb), 0.25);
}
.landing-content .tagline {
  color: var(--landing-tagline-ink);
  font-size: 1.05rem;
  margin-top: 0.15rem;
}

/* Single consolidated sign-in CTA (Troy requirement: one auth action, not
   a Create-account/Log-in pair -- magic-link is one flow regardless of
   which door a visitor walks through). Reuses the standard gold-fill
   `button` styling untouched -- self-contained fill with its own on-fill
   text color (--ink-strong), so no landing-specific text-color token is
   needed the way the wordmark/tagline (rendered directly against the
   scene) require. */
.landing-cta {
  min-height: 48px;
  padding: 0 2.2rem;
  margin-top: 2rem;
  font-size: 1.02rem;
  letter-spacing: 0.03em;
  box-shadow: 0 6px 26px rgba(var(--gold-fill-rgb), 0.32);
}
.landing-cta:hover { box-shadow: 0 6px 26px rgba(var(--gold-fill-rgb), 0.5); }

/* ------------------------------------------------------------------ */
/* App view (signed-in, or the API-unreachable open-app fallback):     */
/* a slim topbar (brand + the account cluster) replaces the full-      */
/* viewport landing -- "drop to the normal themed app background"      */
/* (Troy's own simplest-option call) rather than running the animated   */
/* scene behind the whole app.                                         */
/* ------------------------------------------------------------------ */

.app-topbar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 1.25rem;
}
.app-topbar[hidden] { display: none; } /* .app-topbar's own display:flex would otherwise beat the UA [hidden] rule, same gotcha as .landing-view above */
.app-topbar-brand {
  font-family: var(--serif);
  font-size: 1.25rem;
  letter-spacing: 0.04em;
  background: linear-gradient(180deg, var(--gold-bright), var(--gold));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* #accountUnavailable (prototype/serve.sh's plain no-backend :8899
   preview, and by extension the APK's degraded mode): accounts can't
   work there, so #appView renders fully open (today's pre-gate
   behavior) with this one-line note instead of the landing. Compound
   selector (not just the class alone) to out-specificity .panel-note's
   own `margin-top: -0.5rem` (declared later in this file, same 0-1-0
   specificity a class-only selector here would tie with) -- see
   .panel-note's own rule if touching either. */
#accountUnavailable.app-unavailable-note {
  max-width: 480px;
  margin: 0 auto 1.25rem;
  padding: 0 1.25rem;
  text-align: center;
}

/* Signed-in header cluster (docs/UX-NAV.md): a compact initial-letter
   chip + Sign out, both 44px-tall tap targets, both themes, plus the
   theme toggle -- all three read as one matched group at the topbar's
   right edge. account.js's updateAccountCluster() shows/hides the
   chip/sign-out pair; the toggle always renders (signed out, only the
   toggle shows, at the row's right edge -- same as before this round). */
.header-account-cluster {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin-left: auto;
}

/* Sun/moon theme toggle (prototype/theme.js): cycles light<->dark, stamps
   data-theme on <html>, persisted to localStorage. 44px circle for a
   comfortable tap target at every width -- positioning comes from the
   .header-account-cluster flex parent above. */
.theme-toggle {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-radius: 50%;
  color: var(--ink);
  font-size: 1.15rem;
  line-height: 1;
  padding: 0;
  cursor: pointer;
  flex: none;
  transition: border-color 0.15s ease, transform 0.1s ease;
}
.theme-toggle:hover { border-color: var(--gold-dim); }
.theme-toggle:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; }
.theme-toggle:active { transform: translateY(1px); }

/* Signed-in header cluster (docs/UX-NAV.md): compact initial-letter chip +
   Sign out, both 44px-tall tap targets, both themes -- account.js's
   updateAccountCluster() shows/hides the pair together and fills the
   chip's letter/title/aria-label. */
.header-account-chip {
  width: 44px;
  height: 44px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-radius: 50%;
  color: var(--gold-bright);
  font-family: var(--serif);
  font-size: 1.05rem;
  font-weight: 700;
  cursor: pointer;
  transition: border-color 0.15s ease, transform 0.1s ease;
}
.header-account-chip:hover { border-color: var(--gold-dim); }
.header-account-chip:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; }
.header-account-chip:active { transform: translateY(1px); }

.header-signout-btn {
  min-height: 44px;
  padding: 0 0.9rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-radius: 22px;
  color: var(--ink);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color 0.15s ease, transform 0.1s ease;
}
.header-signout-btn:hover { border-color: var(--gold-dim); }
.header-signout-btn:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; }
.header-signout-btn:active { transform: translateY(1px); }

/* Standard clip-based screen-reader-only utility (#headerSignOutStatus) --
   present in the accessibility tree/announced by AT, never visible/never
   affects layout. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.site-footer {
  text-align: center;
  color: var(--ink-muted);
  font-size: 0.8rem;
  padding: 2rem 1rem 3rem;
}
.footer-links { margin: 0.5rem 0 0; }
.footer-links a {
  color: var(--gold-dim);
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}
.footer-links a:hover, .footer-links a:focus-visible { color: var(--gold-bright); }

/* prototype/terms.html's own prose (Terms & Conditions body text) --
   living HERE in the shared stylesheet, not a per-page inline <style>
   block, on purpose: server/src/security-headers.js's CSP (style-src
   'self') would silently drop an inline block (same gap phone.html/
   numbers-preview.html's pre-existing inline <style> tags already have,
   not fixed here -- out of scope, flagged in this round's report instead).
   Link color matches .footer-links a's gold-dotted convention exactly. */
.legal-page a {
  color: var(--gold-dim);
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}
.legal-page a:hover, .legal-page a:focus-visible { color: var(--gold-bright); }
.legal-page h3 { font-family: var(--serif); color: var(--gold-bright); margin: 1.4rem 0 0.5rem; font-size: 1.1rem; }
.legal-page p { color: var(--ink-dim); line-height: 1.6; margin: 0 0 0.9rem; }
.legal-back { margin-top: 1.5rem; }

main {
  max-width: 900px;
  margin: 0 auto;
  padding: 0 1.25rem 4rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
/* This rule's own display:flex is an AUTHOR-origin rule, which beats the
   UA stylesheet's [hidden] rule regardless of specificity (origin outranks
   specificity in the cascade) -- same gotcha as .hero-cta-row/.dev-tier-
   toggle/.app-topbar/.landing-view elsewhere in this file, just easy to
   miss here because `main`'s own selector has LOWER specificity than
   [hidden] (which is why a plain type selector felt safe -- it isn't;
   origin wins before specificity is ever compared). */
main[hidden] { display: none; }

/* ------------------------------------------------------------------ */
/* Panels                                                               */
/* ------------------------------------------------------------------ */

.panel {
  background: linear-gradient(180deg, var(--night-800), var(--night-900));
  border: 1px solid var(--night-600);
  border-radius: var(--radius);
  padding: 1.5rem 1.5rem 1.75rem;
  box-shadow: var(--shadow);
}

.panel h2 {
  font-family: var(--serif);
  font-size: 1.5rem;
  color: var(--gold-bright);
  margin: 0 0 1.1rem;
  letter-spacing: 0.02em;
}

.panel-note { color: var(--ink-muted); font-size: 0.85rem; margin-top: -0.5rem; }

/* ------------------------------------------------------------------ */
/* Form                                                                 */
/* ------------------------------------------------------------------ */

.field { margin-bottom: 1.1rem; }
.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}
.field-row .field { margin-bottom: 0; }

label {
  display: block;
  font-size: 0.85rem;
  color: var(--ink-dim);
  margin-bottom: 0.35rem;
}
.checkbox-field label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
}
.checkbox-field input { width: auto; }

.fieldset-label {
  display: block;
  font-size: 0.85rem;
  color: var(--ink-dim);
  margin-bottom: 0.5rem;
}
.radio-row {
  display: flex;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.radio-row label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--ink);
  font-size: 0.9rem;
}
.radio-row input { width: auto; }

input[type="text"], input[type="date"], input[type="time"], input[type="number"], select, textarea {
  width: 100%;
  padding: 0.55rem 0.7rem;
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-radius: 6px;
  color: var(--ink);
  font-size: 0.95rem;
  font-family: var(--sans);
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--gold-dim);
  box-shadow: 0 0 0 3px rgba(var(--gold-fill-rgb), 0.15);
}
input::placeholder, textarea::placeholder { color: var(--ink-muted); }

.field-divider { border: none; border-top: 1px solid var(--night-600); margin: 1.5rem 0; }

.submit-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.5rem;
}
button {
  background: linear-gradient(180deg, var(--gold-fill-bright), var(--gold-fill));
  color: var(--ink-strong);
  border: none;
  border-radius: 8px;
  padding: 0.7rem 1.6rem;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  letter-spacing: 0.02em;
  transition: transform 0.1s ease, box-shadow 0.15s ease;
}
button:hover { box-shadow: 0 4px 16px rgba(var(--gold-fill-rgb), 0.35); }
button:active { transform: translateY(1px); }

.compute-status { color: var(--ink-muted); font-size: 0.85rem; }
.compute-status.compute-error { color: var(--err); }

/* ------------------------------------------------------------------ */
/* Banners                                                              */
/* ------------------------------------------------------------------ */

.banner {
  background: rgba(var(--gold-fill-rgb), 0.1);
  border: 1px solid var(--gold-dim);
  border-radius: 8px;
  padding: 0.85rem 1rem;
  color: var(--gold-bright);
  font-size: 0.9rem;
  margin-bottom: 1rem;
}
.banner.account-banner-error {
  background: transparent;
  border-color: var(--err);
  color: var(--err);
}

/* ------------------------------------------------------------------ */
/* Sign-in modal (docs/UX-NAV.md "Sign-in presentation", prototype/      */
/* account.js) -- replaces the old always-open top-of-page Account      */
/* panel. Same overlay/dialog-card pattern as .reveal-overlay/           */
/* .reveal-modal further down this file (centered card over a dim       */
/* backdrop, own border/shadow) -- kept visually consistent with that    */
/* existing modal rather than inventing a third look.                   */
/* ------------------------------------------------------------------ */

.signin-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: radial-gradient(ellipse at center, rgba(var(--overlay-a-rgb), 0.92) 0%, rgba(var(--overlay-b-rgb), 0.96) 100%);
}
.signin-modal-overlay[hidden] { display: none; }

.signin-modal {
  position: relative;
  width: 100%;
  max-width: 420px;
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
  background: linear-gradient(180deg, var(--night-800), var(--night-900));
  border: 1px solid var(--gold-dim);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 2rem 1.75rem 1.75rem;
  outline: none; /* focus is conveyed by the card's own border/shadow, same convention as .reveal-modal */
}
.signin-modal-heading {
  font-family: var(--serif);
  font-size: 1.4rem;
  color: var(--gold-bright);
  letter-spacing: 0.02em;
  margin: 0 1.75rem 1.25rem 0;
}
.signin-modal-close {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  box-shadow: none;
  color: var(--ink-muted);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.signin-modal-close:hover, .signin-modal-close:focus-visible { color: var(--gold-bright); }
.signin-modal-close:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; }

/* ------------------------------------------------------------------ */
/* Compact Account panel (signed-in only, docs/UX-NAV.md "Account panel */
/* removal + relocation") -- plain .panel/h2 styling already supplies    */
/* the card background + serif gold heading, no extra rule needed for   */
/* either; #accountCompactHeading is a script-focus target (tabindex=   */
/* "-1") for the header chip's click.                                   */
/* ------------------------------------------------------------------ */

#accountCompactHeading:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 4px; }

/* "or" divider between the email flow and the social sign-in buttons.
   Hidden entirely (not just its social buttons) when no provider is
   enabled -- account.js -- so there's never a dangling divider with
   nothing rendered under it. */
.auth-divider[hidden] { display: none; }
.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.1rem 0;
  color: var(--ink-muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.auth-divider::before, .auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--night-600);
}

.social-auth-buttons { display: flex; flex-direction: column; gap: 0.6rem; margin-bottom: 1.1rem; }

/* Brand-recognizable buttons -- deliberately NOT theme-tokened (Google's
   button stays white, Facebook's stays #1877F2, in both night and dawn
   mode), same as any other embedded brand mark would. */
.social-auth-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  width: 100%;
  min-height: 44px;
  padding: 0 1rem;
  border-radius: var(--radius);
  font-size: 0.95rem;
  font-weight: 600;
  font-family: var(--sans);
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: filter 0.15s ease, transform 0.1s ease;
}
.social-auth-btn:active { transform: translateY(1px); }
/* The class's display:flex would otherwise override the HTML hidden
   attribute (UA display:none loses to author styles) — dead Google/
   Facebook buttons rendered for everyone until providers get creds. */
.social-auth-btn[hidden] { display: none; }
.social-auth-icon { flex: none; }

.social-auth-btn-google {
  background: #ffffff;
  color: #3c4043;
  border: 1px solid #dadce0;
}
.social-auth-btn-google:hover { background: #f7f8f8; }
.social-auth-btn-google:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; }

.social-auth-btn-facebook {
  background: #1877f2;
  color: #ffffff;
  border: 1px solid #1877f2;
}
.social-auth-btn-facebook:hover { filter: brightness(1.06); }
.social-auth-btn-facebook:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; }

/* ------------------------------------------------------------------ */
/* Sign up / Log in toggle + magic-link secondary option (Troy           */
/* requirement, 2026-07-06, prototype/account.js's setAuthMode()/         */
/* setMagicLinkSectionVisible()) -- segmented-control look mirrors        */
/* .breathe-pattern-btn/.active exactly (plain night surface, gold-tinted */
/* active state), NOT the vivid gold-gradient CTA every plain <button>    */
/* gets by default -- this is a mode toggle, not a primary action.        */
/* ------------------------------------------------------------------ */

.auth-mode-toggle {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
}
.auth-mode-btn {
  flex: 1 1 0;
  background: var(--night-700);
  color: var(--ink-dim);
  border: 1px solid var(--night-600);
  border-radius: 8px;
  padding: 0.6rem 0.5rem;
  font-size: 0.9rem;
  font-weight: 600;
  min-height: 44px;
  box-shadow: none;
  text-align: center;
}
.auth-mode-btn:hover { border-color: var(--gold-dim); color: var(--ink); box-shadow: none; }
.auth-mode-btn.active {
  background: rgba(var(--gold-fill-rgb), 0.16);
  border-color: var(--gold-dim);
  color: var(--gold-bright);
}

.signin-modal-secondary-toggle { text-align: center; margin: 1rem 0 0; }

.account-profiles-list { list-style: none; margin: 0; padding: 0; }
.account-profile-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.9rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--night-600);
}
.account-profile-row:last-child { border-bottom: none; }
.account-profile-load { margin-top: 0; font-size: 0.95rem; text-align: left; }
.account-profile-email { padding: 0.4rem 0.8rem; font-size: 0.85rem; }
.account-profile-status { font-size: 0.8rem; }

/* ------------------------------------------------------------------ */
/* Chart wheel                                                         */
/* ------------------------------------------------------------------ */

.wheel-container {
  display: flex;
  justify-content: center;
}
.chart-wheel {
  width: 100%;
  max-width: 560px;
  height: auto;
}

/* .zodiac-wedge's stroke is deliberately --ink-strong (always dark), not a
   surface token -- it's the hairline separating adjacent colored wedges,
   and needs to read as a dark divider against pastel wedge fills in BOTH
   themes, not fade into a near-white --night-950 in light mode. */
.zodiac-wedge { stroke: var(--ink-strong); stroke-width: 1; opacity: 0.28; }
/* fill: for the SVG <path> wedges. background-color: for the legend swatch
   <span>s that reuse these same classes on an HTML element (SVG `fill`
   has no effect on a box element, and `background-color` has no effect on
   an SVG shape, so declaring both here safely serves either use site). */
.zodiac-fire { fill: var(--fire); background-color: var(--fire); }
.zodiac-earth { fill: var(--earth); background-color: var(--earth); }
.zodiac-air { fill: var(--air); background-color: var(--air); }
.zodiac-water { fill: var(--water); background-color: var(--water); }
.zodiac-inner-edge { fill: none; stroke: var(--night-600); stroke-width: 1.5; }
.zodiac-glyph { fill: var(--ink); font-size: 17px; font-family: var(--serif); }

.house-cusp-line { stroke: var(--night-600); stroke-width: 1; }
.house-cusp-angle { stroke: var(--gold-fill); stroke-width: 2; }
.house-number { fill: var(--ink-muted); font-size: 12px; }
.angle-axis { stroke: var(--gold-fill); stroke-width: 1.5; stroke-dasharray: 4 3; opacity: 0.7; }
.angle-label { fill: var(--gold-bright); font-size: 12px; font-weight: 700; letter-spacing: 0.05em; }

.aspect-line.aspect-warm { stroke: var(--warm); stroke-width: 1.4; }
.aspect-line.aspect-cool { stroke: var(--cool); stroke-width: 1.4; }
.aspect-line.aspect-neutral { stroke: var(--ink-muted); stroke-width: 1; }

.planet-leader { stroke: var(--ink-muted); stroke-width: 0.75; stroke-dasharray: 2 2; }
.planet-marker circle { fill: var(--night-900); stroke: var(--gold-fill); stroke-width: 1.25; }
.planet-glyph { fill: var(--gold-bright); font-size: 15px; font-family: var(--serif); }
.retrograde-mark { fill: var(--err); font-size: 10px; }

.wheel-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  justify-content: center;
  margin-top: 1rem;
  font-size: 0.8rem;
  color: var(--ink-muted);
}
.legend-item { display: inline-flex; align-items: center; gap: 0.4rem; }
.legend-swatch { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
.legend-line { width: 18px; height: 2px; display: inline-block; }
.legend-line.aspect-warm { background: var(--warm); }
.legend-line.aspect-cool { background: var(--cool); }

/* ------------------------------------------------------------------ */
/* Tables                                                               */
/* ------------------------------------------------------------------ */

/* Pure-CSS "scroll shadow" edge fades: two opaque local-attached strips mask
   the fixed (scroll-attached) shadow gradients wherever the table hasn't
   actually scrolled away from that edge yet, so a shadow only shows on the
   side there's more content to reveal -- no JS overflow-detection needed,
   and a table that doesn't overflow at all shows no shadow on either side. */
.table-scroll {
  overflow-x: auto;
  background-color: transparent;
  background-image:
    linear-gradient(to right, var(--night-800), var(--night-800)),
    linear-gradient(to left, var(--night-800), var(--night-800)),
    linear-gradient(to right, rgba(var(--edge-shadow-rgb), 0.45), rgba(var(--edge-shadow-rgb), 0)),
    linear-gradient(to left, rgba(var(--edge-shadow-rgb), 0.45), rgba(var(--edge-shadow-rgb), 0));
  background-repeat: no-repeat;
  background-position: left, right, left, right;
  background-size: 24px 100%, 24px 100%, 16px 100%, 16px 100%;
  background-attachment: local, local, scroll, scroll;
}
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
table.mini-table { margin: 0.75rem 0; }
th, td {
  text-align: left;
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--night-600);
  white-space: nowrap;
}
th { color: var(--gold-dim); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; }
tbody tr:hover { background: rgba(var(--gold-fill-rgb), 0.05); }
.glyph-cell .glyph { color: var(--gold-bright); font-size: 1.05rem; }

.dignity-strong { color: var(--gold-bright); }
.dignity-weak { color: var(--ink-muted); }

.aspect-chip {
  padding: 0.15rem 0.5rem;
  border-radius: 12px;
  font-size: 0.82rem;
  white-space: nowrap;
}
.aspect-chip.aspect-warm { background: rgba(var(--warm-rgb), 0.18); color: var(--warm-tint); }
.aspect-chip.aspect-cool { background: rgba(var(--cool-rgb), 0.22); color: var(--cool-tint); }
.aspect-chip.aspect-neutral { background: rgba(var(--ink-muted-rgb), 0.18); color: var(--ink-muted); }

.strength-bar {
  display: inline-block;
  width: 60px;
  height: 6px;
  border-radius: 3px;
  background: var(--night-600);
  position: relative;
  vertical-align: middle;
  margin-right: 0.4rem;
}
.strength-fill { position: absolute; inset: 0; width: var(--pct); background: var(--gold-fill); border-radius: 3px; }

/* ------------------------------------------------------------------ */
/* Positions -- stacked cards (phone-width alternative to the table)   */
/*                                                                      */
/* Both #positionsTable's .table-scroll and #positionsCards are always */
/* rendered into the DOM by app.js; only one is ever visible at a time, */
/* toggled purely by this media query (see the <640px block below) --  */
/* this sidesteps needing a resize listener to swap markup, matching   */
/* glossary.js's own "just re-decide on next open" philosophy rather   */
/* than trying to keep JS and CSS breakpoints in a live handshake.     */
/* ------------------------------------------------------------------ */

.positions-cards { display: none; }

.position-card {
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-radius: var(--radius);
  padding: 0.9rem 1rem 1rem;
  margin-bottom: 0.75rem;
}
.position-card:last-child { margin-bottom: 0; }
.position-card-header {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-family: var(--serif);
  font-size: 1.1rem;
  color: var(--gold-bright);
  margin: 0 0 0.6rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--night-600);
}
.position-card-header .glyph { font-size: 1.15rem; }
.position-card-rows {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 0.9rem;
  row-gap: 0.5rem;
  margin: 0;
}
.position-card-label {
  color: var(--gold-dim);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  align-self: center;
}
.position-card-value { margin: 0; text-align: right; align-self: center; }

/* ------------------------------------------------------------------ */
/* Reading sections (natal / daily / cultural)                         */
/* ------------------------------------------------------------------ */

.reading-section { margin-bottom: 1.4rem; }
.reading-section:last-child { margin-bottom: 0; }
.reading-section h3 {
  font-family: var(--serif);
  color: var(--gold);
  font-size: 1.15rem;
  margin: 0 0 0.5rem;
}
.reading-section p { margin: 0 0 0.6rem; color: var(--ink); }
.reading-section p:last-child { margin-bottom: 0; }
.reading-section .sub-line { color: var(--ink-dim); font-size: 0.9rem; }
.reading-section .explainer { color: var(--ink-dim); font-size: 0.88rem; font-style: italic; }
.reading-section .muted { color: var(--ink-muted); font-size: 0.85rem; }

/* Element/modality balance chips (prototype/glossary.js triggers) */
.balance-chip-row { margin-bottom: 0.9rem; }
.balance-chip-group {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
.balance-chip-group:last-child { margin-bottom: 0; }
.balance-chip {
  padding: 0.2rem 0.6rem;
  border-radius: 12px;
  font-size: 0.85rem;
  background: rgba(var(--ink-muted-rgb), 0.14);
  color: var(--ink);
}
.balance-chip.dignity-strong { background: rgba(var(--gold-fill-rgb), 0.18); color: var(--gold-bright); }
.balance-chip.dignity-weak { background: rgba(var(--ink-muted-rgb), 0.1); color: var(--ink-muted); }

/* Bold headline value that's also a glossary trigger (e.g. "Metal Monkey"
   in the Chinese Zodiac panel) -- keeps the same visual weight a plain
   <strong> tag had before it became clickable. */
.headline-trigger { font-weight: 700; font-size: 1.05rem; }

.daily-opener, .daily-closer {
  font-family: var(--serif);
  font-style: italic;
  color: var(--gold-bright);
  font-size: 1.05rem;
}

.pending-badge {
  display: inline-block;
  font-size: 0.7rem;
  color: var(--ink-strong);
  background: var(--gold-fill);
  padding: 0.05rem 0.4rem;
  border-radius: 8px;
  margin-left: 0.4rem;
  vertical-align: middle;
}

.why-expander { margin-top: 1.25rem; }
.why-expander summary {
  cursor: pointer;
  color: var(--gold);
  font-size: 0.9rem;
}

.debug-panel { padding: 0.75rem 1.5rem; }
.debug-panel summary { cursor: pointer; color: var(--ink-muted); font-size: 0.85rem; }
.debug-panel ul { color: var(--ink-muted); font-size: 0.8rem; columns: 2; font-family: monospace; }

/* ------------------------------------------------------------------ */
/* Human Design panel (prototype/humandesign-panel.js, HUMANDESIGN.md UI)*/
/* ------------------------------------------------------------------ */

/* Headline stat row: Type/Strategy/Authority/Profile/Definition, each a
   label + a glossary-chip trigger (dl/dt/dd for structure without a table,
   same semantic pattern as .position-card-rows). */
.hd-headline-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.4rem;
  margin: 0 0 1.3rem;
}
.hd-headline-label {
  color: var(--gold-dim);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0 0 0.25rem;
  width: 100%;
}
.hd-headline-value { margin: 0 1.4rem 0 0; }
.hd-chip {
  background: rgba(var(--gold-fill-rgb), 0.14);
  color: var(--gold-bright);
  font-weight: 600;
}
.hd-chip:hover, .hd-chip:focus-visible { filter: brightness(1.15); }

/* Bodygraph SVG: hand-drawn 9-center chart (viewBox 360x520), scales down to
   fit its panel, never grows past ~420px per the UI brief. */
.hd-bodygraph-wrap { display: flex; justify-content: center; margin-bottom: 1.1rem; }
.hd-bodygraph { width: 100%; max-width: 400px; height: auto; }

.hd-center-shape { stroke: var(--night-600); stroke-width: 1.5; }
.hd-center-shape-defined { fill: var(--gold-fill); fill-opacity: 0.55; stroke: var(--gold-fill); }
.hd-center-shape-undefined { fill: var(--night-800); fill-opacity: 0.5; }
.hd-center-name {
  fill: var(--ink-muted);
  font-size: 8px;
  font-family: var(--sans);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  pointer-events: none;
}

/* Gate-number labels ringed around each center. Colored per which moment
   activated that gate this chart -- classic HD convention is black
   (personality) / red (design); mapped to theme-safe --ink / --warm-tint
   tokens (both already AA-verified as TEXT colors against the panel
   background, per the dawn-theme pass), with --gold-bright marking a gate
   activated by BOTH moments and a dim --ink-muted for a gate this chart
   never activates at all. */
.hd-gate-label { font-size: 9px; font-family: var(--sans); pointer-events: none; }
.hd-gate-label-personality { fill: var(--ink); font-weight: 700; }
.hd-gate-label-design { fill: var(--warm-tint); font-weight: 700; }
.hd-gate-label-both { fill: var(--gold-bright); font-weight: 700; }
.hd-gate-label-inactive { fill: var(--ink-muted); opacity: 0.55; }

/* Channel bars between center shapes: personality-only / design-only get
   the classic dashed ink/red-leaning treatment, a channel defined by BOTH
   moments (the common case) gets a heavier solid gold bar ("both=solid" per
   the UI brief), and the channels that AREN'T defined this chart are drawn
   first (so defined bars paint over them) as a faint background line. */
.hd-channel-personality { stroke: var(--ink); stroke-width: 2; stroke-dasharray: 5 3; opacity: 0.9; }
.hd-channel-design { stroke: var(--warm); stroke-width: 2; stroke-dasharray: 5 3; opacity: 0.9; }
.hd-channel-both { stroke: var(--gold-fill); stroke-width: 3; opacity: 0.95; }
.hd-channel-undefined { stroke: var(--ink-muted); stroke-width: 1; opacity: 0.2; }

.hd-activations-heading {
  font-family: var(--serif);
  color: var(--gold);
  font-size: 1.15rem;
  margin: 0 0 0.6rem;
}
.hd-activations-table th { text-align: center; }
.hd-activations-table td { text-align: center; white-space: nowrap; }

.hd-explainer { margin-bottom: 1.2rem; }
.hd-explainer summary {
  cursor: pointer;
  color: var(--gold);
  font-size: 0.95rem;
}
.hd-explainer p { margin: 0.6rem 0 0; color: var(--ink-dim); font-size: 0.9rem; line-height: 1.5; }

/* ------------------------------------------------------------------ */
/* v1.1 "Advanced" section (Variable arrows, circuitry bars, hanging       */
/* gates, split bridges) + first-class gate popovers everywhere a gate    */
/* number appears (Gil's mid-task addition). Tokens only, both themes.    */
/* ------------------------------------------------------------------ */

.hd-arrow { font-size: 11px; font-family: var(--sans); font-weight: 700; }
.hd-arrow-personality { fill: var(--ink); }
.hd-arrow-design { fill: var(--warm-tint); }

.hd-advanced { margin-top: 1.4rem; }
.hd-advanced > summary {
  cursor: pointer;
  color: var(--gold);
  font-family: var(--serif);
  font-size: 1.15rem;
  margin-bottom: 0.4rem;
}
.hd-advanced-inner { padding-top: 0.6rem; }
.hd-advanced-inner h3 {
  font-family: var(--serif);
  color: var(--gold);
  font-size: 1.05rem;
  margin: 1.2rem 0 0.6rem;
}
.hd-advanced-inner h3:first-of-type { margin-top: 0.4rem; }

.hd-caveat-banner { font-size: 0.85rem; }

.hd-variable-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.8rem;
  margin-bottom: 0.4rem;
}
.hd-variable-card {
  background: rgba(var(--gold-fill-rgb), 0.06);
  border: 1px solid var(--night-600);
  border-radius: var(--radius);
  padding: 0.8rem 0.9rem;
}
.hd-variable-card-title { margin: 0 0 0.3rem; font-family: var(--serif); color: var(--gold-bright); font-size: 1rem; }
.hd-variable-card-meta { margin: 0 0 0.4rem; color: var(--ink-muted); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; }
.hd-variable-card p:last-child { margin-bottom: 0; font-size: 0.9rem; }
.hd-variable-disclaimer { color: var(--ink-muted); font-size: 0.78rem; font-style: italic; margin-top: 0.5rem; }

.hd-circuit-groups { display: flex; flex-direction: column; gap: 0.7rem; margin-bottom: 0.8rem; }
.hd-circuit-bar-row-dominant .hd-circuit-bar-label { color: var(--gold-bright); }
.hd-circuit-bar-label { display: block; font-size: 0.85rem; font-weight: 600; margin-bottom: 0.25rem; }
.hd-circuit-bar-track {
  display: block;
  height: 8px;
  border-radius: 4px;
  background: var(--night-700);
  overflow: hidden;
}
.hd-circuit-bar-fill { display: block; height: 100%; background: var(--gold-fill); border-radius: 4px; }
.hd-circuit-bar-row-dominant .hd-circuit-bar-fill { background: var(--gold-fill-bright); }
.hd-circuit-bar-text { margin: 0.3rem 0 0; color: var(--ink-dim); font-size: 0.85rem; }
.hd-circuit-sub-list { margin: 0; padding-left: 1.2rem; color: var(--ink-dim); font-size: 0.88rem; }
.hd-circuit-sub-list li { margin-bottom: 0.35rem; }

.hd-hanging-gates { margin: 0; padding-left: 1.2rem; color: var(--ink-dim); font-size: 0.9rem; }
.hd-hanging-gates li { margin-bottom: 0.4rem; }

.hd-bridge-line ul { margin: 0.4rem 0 0; padding-left: 1.2rem; color: var(--ink-dim); font-size: 0.9rem; }
.hd-bridge-line li { margin-bottom: 0.3rem; }

/* Gate glossary triggers (Gil's addition): every gate number anywhere in
   this panel opens the same popover. Inline text-variant triggers inherit
   .glossary-trigger-text's dotted underline (glossary.js) -- no extra
   styling needed there beyond a touch-friendly tap target. */
.hd-gate-trigger { padding: 0.1em 0; }

/* SVG-embedded gate hit targets: a foreignObject-hosted real <button>
   layered on top of the (pointer-events:none, decorative) visible gate
   number text. Invisible but clickable/focusable -- see
   humandesign-panel.js's buildGateHitTarget(). */
.hd-gate-hit { overflow: visible; }
.hd-gate-hit-wrap { width: 100%; height: 100%; }
.hd-gate-hit-btn {
  width: 100%;
  height: 100%;
  display: block;
  background: transparent;
  border: none;
  padding: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.hd-gate-hit-btn:focus-visible {
  opacity: 1;
  outline: 2px solid var(--gold-bright);
  outline-offset: 1px;
  border-radius: 3px;
  background: rgba(var(--gold-fill-rgb), 0.25);
}

/* "Your Gates" section (Gil's addition): distinct activated gates grouped
   by defined center, personality/design/both badges. */
.hd-gate-group { margin-bottom: 1rem; }
.hd-gate-group:last-child { margin-bottom: 0; }
.hd-gate-group-title {
  margin: 0 0 0.4rem;
  color: var(--gold-dim);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.hd-gate-group-list { list-style: none; margin: 0; padding: 0; }
.hd-gate-row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--night-700);
}
.hd-gate-row:last-child { border-bottom: none; }
.hd-gate-row p { margin: 0; flex-basis: 100%; color: var(--ink-dim); font-size: 0.88rem; }
.hd-gate-badge { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-muted); }
.hd-gate-badge-both { color: var(--gold-bright); font-weight: 600; }
.hd-gate-chip { font-weight: 700; }
.hd-gate-chip-both { color: var(--gold-bright); }
.hd-gate-row-both { background: rgba(var(--gold-fill-rgb), 0.06); border-radius: 6px; padding-left: 0.5rem; padding-right: 0.5rem; }
.hd-your-gates-summary { margin: 0 0 0.8rem; }

/* ------------------------------------------------------------------ */
/* Compatibility (synastry) panel (prototype/synastry-panel.js,          */
/* SYNASTRY.md "UI")                                                    */
/* ------------------------------------------------------------------ */

.syn-subheading {
  font-family: var(--serif);
  color: var(--gold);
  font-size: 1.15rem;
  margin: 0 0 0.8rem;
}

.syn-category-bars { margin-bottom: 0.4rem; }
.syn-bar-row {
  display: grid;
  grid-template-columns: 8.5rem 1fr 2.5rem;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: 0.15rem;
}
.syn-bar-row-overall { margin-top: 0.6rem; padding-top: 0.6rem; border-top: 1px solid var(--night-600); }
.syn-bar-label { color: var(--ink-dim); font-size: 0.88rem; }
.syn-bar-row-overall .syn-bar-label { color: var(--gold); font-weight: 700; }
.syn-bar-track {
  position: relative;
  height: 10px;
  border-radius: 6px;
  background: var(--night-600);
  overflow: hidden;
}
/* Category bars use the same warm gold-fill as every other "quality" meter
   in this app (Positions table's strength bar, etc). Friction is
   deliberately NOT alarm-red (SYNASTRY.md: "reported honestly ... not
   hidden: some friction is engagement") -- a neutral cool tone instead,
   same --cool token the trine/sextile aspect lines already use, so a high
   friction score reads as "notable" rather than "bad". */
.syn-bar-fill { position: absolute; inset: 0; height: 100%; background: var(--gold-fill); border-radius: 6px; }
.syn-bar-fill-friction { background: var(--cool); }
.syn-bar-value { text-align: right; color: var(--ink); font-variant-numeric: tabular-nums; font-size: 0.9rem; }
.syn-bar-band-text { margin: 0.2rem 0 0.9rem; color: var(--ink-dim); font-size: 0.88rem; }

.syn-overall-text { color: var(--ink); margin: 0 0 1.3rem; }

.syn-top-aspects {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 0.8rem;
}
.syn-aspect-card {
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-left: 3px solid var(--ink-muted);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
}
.syn-aspect-card.aspect-warm { border-left-color: var(--warm); }
.syn-aspect-card.aspect-cool { border-left-color: var(--cool); }
.syn-aspect-card-title { margin: 0 0 0.3rem; color: var(--gold-bright); font-weight: 700; font-size: 0.98rem; }
.syn-aspect-card-meta { margin: 0 0 0.5rem; color: var(--ink-muted); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; }
.syn-aspect-card-text { margin: 0; color: var(--ink); font-size: 0.92rem; }

.syn-sort-btn { font-size: 0.78rem; color: var(--gold-dim); }
.syn-sort-btn:hover, .syn-sort-btn:focus-visible { color: var(--gold-bright); }

.syn-overlay-direction { margin-bottom: 1.1rem; }
.syn-overlay-direction:last-child { margin-bottom: 0; }
.syn-overlay-direction h4 { color: var(--gold); font-family: var(--serif); font-size: 1rem; margin: 0 0 0.5rem; }
.syn-overlay-direction p { margin: 0 0 0.5rem; color: var(--ink); font-size: 0.92rem; }

/* ------------------------------------------------------------------ */
/* Natal free-tier teaser (docs/TIERS.md v2 Chart Ruler section)        */
/* ------------------------------------------------------------------ */

.natal-teaser-section { margin-bottom: 0.6rem; }
.teaser-fade {
  position: relative;
  color: var(--ink);
  -webkit-mask-image: linear-gradient(180deg, #000 40%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 40%, transparent 100%);
  padding-bottom: 0.4rem;
}

/* ------------------------------------------------------------------ */
/* Tiers v2 lock card (prototype/tiers.js buildLockCard) -- one shared    */
/* component every gated panel (natal/daily/HD/synastry) uses.          */
/* ------------------------------------------------------------------ */

.tier-lock-card {
  display: flex;
  gap: 0.9rem;
  align-items: flex-start;
  background: linear-gradient(160deg, rgba(var(--gold-fill-rgb), 0.12), transparent 70%), var(--night-700);
  border: 1px solid var(--gold-dim);
  border-radius: var(--radius);
  padding: 1.1rem 1.2rem;
  margin-top: 0.4rem;
}
.tier-lock-icon {
  flex: none;
  font-size: 1.4rem;
  color: var(--gold-bright);
  line-height: 1;
  margin-top: 0.1rem;
}
.tier-lock-body { min-width: 0; }
.tier-lock-title { margin: 0 0 0.3rem; color: var(--gold-bright); font-weight: 700; font-family: var(--serif); font-size: 1.05rem; }
.tier-lock-text { margin: 0 0 0.5rem; color: var(--ink); font-size: 0.92rem; }
.tier-lock-coming-soon {
  margin: 0;
  color: var(--gold-dim);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ------------------------------------------------------------------ */
/* Plan selection screen (Troy requirement, 2026-07-06, shown right      */
/* after a new account is created -- prototype/onboarding.js). Wider     */
/* than the plain .signin-modal (which stays at max-width:420px for the  */
/* sign-in forms) since this holds two side-by-side cards; the featured  */
/* (paid) card reuses .tier-lock-card's gold-tinted-gradient/gold-dim-   */
/* border recipe so the two premium surfaces in this app read as the     */
/* same visual language.                                                 */
/* ------------------------------------------------------------------ */

.plan-selection-card {
  position: relative;
  width: 100%;
  max-width: 760px;
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
  background: linear-gradient(180deg, var(--night-800), var(--night-900));
  border: 1px solid var(--gold-dim);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 2rem 1.75rem 1.75rem;
  outline: none;
}
.plan-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.25rem;
  margin-top: 1.25rem;
}
@media (max-width: 640px) {
  .plan-cards { grid-template-columns: 1fr; }
}
.plan-card {
  display: flex;
  flex-direction: column;
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-radius: var(--radius);
  padding: 1.25rem 1.3rem;
}
.plan-card h3 {
  font-family: var(--serif);
  color: var(--gold-bright);
  font-size: 1.2rem;
  margin: 0 0 0.6rem;
}
.plan-card-featured {
  background: linear-gradient(160deg, rgba(var(--gold-fill-rgb), 0.14), transparent 70%), var(--night-700);
  border-color: var(--gold-dim);
}
.plan-price { color: var(--gold-bright); font-weight: 700; font-size: 1rem; margin: 0 0 0.8rem; }
.plan-feature-list {
  list-style: none;
  margin: 0 0 1.1rem;
  padding: 0;
  flex: 1;
}
.plan-feature-list li {
  color: var(--ink-dim);
  font-size: 0.88rem;
  line-height: 1.5;
  padding-left: 1.2rem;
  position: relative;
  margin-bottom: 0.4rem;
}
.plan-feature-list li::before {
  content: '✦';
  position: absolute;
  left: 0;
  color: var(--gold-dim);
  font-size: 0.75rem;
}
.plan-card .submit-row { margin-top: auto; flex-direction: column; align-items: stretch; gap: 0.5rem; }
.plan-card .submit-row button { width: 100%; }

/* ------------------------------------------------------------------ */
/* Beta / discount code redemption (Troy requirement, 2026-07-06,       */
/* prototype/onboarding.js's wireBetaCodeRedeem()) -- one set of rules   */
/* shared verbatim by #accountBetaCodeWrap (account panel, right next    */
/* to "Upgrade your plan" -- the REQUIRED spot) and #planBetaCodeWrap    */
/* (plan-selection screen, a third small option under the two cards).    */
/* Classes only, no inline style="..." anywhere -- this app's CSP        */
/* (style-src 'self') silently drops inline styles, so every bit of      */
/* layout here has to be a real stylesheet rule.                        */
/* ------------------------------------------------------------------ */

.plan-selection-beta-code {
  margin-top: 1.25rem;
  text-align: center;
}
.plan-selection-beta-code .link-button { margin-top: 0; }

.beta-code-form { margin-top: 0.6rem; }
.beta-code-row {
  display: flex;
  gap: 0.6rem;
  align-items: center;
}
.beta-code-row .beta-code-input { flex: 1; }
.beta-code-row button { padding: 0.55rem 1.1rem; white-space: nowrap; }
.beta-code-form .compute-status { display: block; margin-top: 0.5rem; }
/* Same [hidden]-losing-to-author-styles gotcha as .profile-switcher-make-
   primary/.social-auth-btn elsewhere in this file -- .account-beta-code-
   toggle also carries .link-button, whose own `display: inline-block`
   would otherwise beat the UA [hidden] rule when onboarding.js's
   wireBetaCodeRedeem() sets toggleBtn.hidden = true on reveal. */
.account-beta-code-toggle[hidden] { display: none; }

/* ------------------------------------------------------------------ */
/* Dev preview tier toggle (prototype/tiers.js) -- fixed corner,        */
/* deliberately devtool-styled (monospace, dashed border) so it never   */
/* reads as a real product control. Bottom-left: opposite corner from   */
/* the topbar's account cluster, to avoid crowding it. #appView-only    */
/* (docs/UX-NAV.md "Landing wall + celestial scene"): account.js's       */
/* updateViewMode() hides this together with the rest of #appView while  */
/* #landingView is showing.                                              */
/* ------------------------------------------------------------------ */

.dev-tier-toggle {
  position: fixed;
  left: 0.9rem;
  bottom: 0.9rem;
  z-index: 500;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: var(--night-900);
  border: 1px dashed var(--ink-muted);
  border-radius: 8px;
  padding: 0.35rem 0.45rem;
  box-shadow: var(--shadow);
}
/* This class's own display:flex would otherwise beat the UA [hidden]
   rule (author normal-priority styles win over UA styles regardless of
   source order) -- same gotcha as .hero-cta-row/.profile-switcher/
   .social-auth-btn elsewhere in this file. */
.dev-tier-toggle[hidden] { display: none; }
.dev-tier-toggle-label {
  font-family: monospace;
  font-size: 0.68rem;
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0 0.2rem;
}
.dev-tier-toggle-btn {
  font-family: monospace;
  font-size: 0.7rem;
  padding: 0.25rem 0.55rem;
  border-radius: 5px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--ink-muted);
  box-shadow: none;
}
.dev-tier-toggle-btn:hover { box-shadow: none; color: var(--ink); }
.dev-tier-toggle-btn.active {
  background: rgba(var(--gold-fill-rgb), 0.16);
  border-color: var(--gold-dim);
  color: var(--gold-bright);
}

/* ------------------------------------------------------------------ */
/* Feedback button + modal (Troy requirement, 2026-07-06: "a Feedback     */
/* button in the main chart area at the bottom left"). Fixed corner,     */
/* same left edge as .dev-tier-toggle above it -- both are fixed         */
/* bottom-left and both are #appView-only (account.js's updateViewMode() */
/* hides this together with #devTierToggle/#tabBar/main while            */
/* #landingView is showing), so this one is stacked ABOVE                */
/* .dev-tier-toggle with enough clearance that neither ever overlaps the */
/* other -- see the matching lift in the mobile block further down,      */
/* which additionally has to clear the fixed bottom tab bar.             */
/* ------------------------------------------------------------------ */

.feedback-fab-btn {
  position: fixed;
  left: 0.9rem;
  bottom: calc(0.9rem + 2.75rem);
  z-index: 500;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: var(--night-800);
  border: 1px solid var(--night-600);
  border-radius: 22px;
  padding: 0.5rem 0.9rem;
  color: var(--ink-muted);
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: var(--shadow);
  transition: border-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
}
/* This class's own display:inline-flex would otherwise beat the UA
   [hidden] rule -- same gotcha as .dev-tier-toggle/.social-auth-btn
   elsewhere in this file. */
.feedback-fab-btn[hidden] { display: none; }
.feedback-fab-btn:hover { border-color: var(--gold-dim); color: var(--ink); }
.feedback-fab-btn:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; }
.feedback-fab-btn:active { transform: translateY(1px); }
.feedback-fab-icon { flex: none; }

.feedback-modal-intro { margin-top: -0.3rem; margin-bottom: 1.1rem; }

/* Live "N / max" character counters (prototype/feedback.js) sit inline
   with each field's label rather than under the input, so they read as
   one glance instead of a second line to scan. */
.field-label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}
.field-label-row label { margin-bottom: 0; }
.field-char-count {
  font-size: 0.75rem;
  color: var(--ink-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.feedback-message-input {
  min-height: 7rem;
  resize: vertical;
  line-height: 1.4;
}

/* ------------------------------------------------------------------ */
/* Responsive                                                          */
/* ------------------------------------------------------------------ */

@media (max-width: 640px) {
  .site-header h1 { font-size: 2rem; letter-spacing: 0.03em; }
  .field-row { grid-template-columns: 1fr; }
  .panel { padding: 1.1rem 1rem 1.4rem; }
  .debug-panel ul { columns: 1; }

  /* Landing scene: tighter frame inset + smaller wordmark/CTA so the
     card-frame doesn't eat too much of a narrow viewport, full-width CTA
     for an easier tap target. */
  .landing-view { padding: 1.5rem 1.25rem 10vh; }
  .landing-frame { inset: 8px; }
  .landing-frame::before { inset: 5px; }
  .landing-content h1 { font-size: 2.25rem; letter-spacing: 0.03em; }
  .landing-cta { width: 100%; }

  /* Sign-in modal: full-height-friendly on a short phone viewport (same
     max-height/overflow already set at all widths, just tighter padding). */
  .signin-modal { padding: 1.5rem 1.25rem 1.25rem; }

  /* Positions: swap the wide table for stacked cards -- the table crops
     at the right edge with no scroll affordance at phone width (House/
     Speed/Dignity columns run off-screen), and a per-planet card reads
     far better here than a horizontally-scrolling table would. */
  #positions-panel .table-scroll { display: none; }
  .positions-cards { display: block; }

  /* Tap targets: buttons and selects should be >=44px tall at phone
     width (WCAG 2.5.5 AAA). .glossary-trigger and .link-button are
     inline text-style triggers/links (dotted-underline, sit inside
     table cells/cards/prose), not block button controls, so they're
     exempt the same way an inline hyperlink would be -- ballooning them
     to 44px would wreck the table/card/prose layout they live in. */
  button:not(.glossary-trigger):not(.link-button),
  select {
    min-height: 44px;
  }
  .place-lookup button { padding: 0.7rem 0.9rem; }
  /* .link-button stays visually small (it's a plain-text disclosure
     link) but still gets a >=44px tall tappable box via padding, so it
     doesn't fail the tap-target check just because it looks like text. */
  .link-button { padding: 0.5rem 0; min-height: 44px; display: inline-flex; align-items: center; }
  /* Glossary popover close (x): the popover renders as a full-width
     centered sheet at this width (glossary.js's own <640 breakpoint),
     so its close button is a real primary control here -- give it a
     proper tap box without growing the visible glyph. */
  .glossary-close { min-width: 44px; min-height: 44px; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
  .glossary-popover-title { margin-right: 2.75rem; }

  /* Corner controls tighten their gap on a narrow topbar. */
  .header-account-cluster { gap: 0.35rem; }
  .app-topbar { padding: 0.65rem 0.85rem; }
  .app-topbar-brand { font-size: 1.05rem; }
  /* "Sign out" label would crowd a 44px chip + 44px toggle in ~40vw of
     remaining width on a phone -- shrink to an icon-free but tighter pill
     rather than truncating the word. */
  .header-signout-btn { font-size: 0.72rem; padding: 0 0.6rem; }

  /* Dev tier toggle: shrink + tighten so it doesn't eat too much of a
     narrow viewport's bottom edge. */
  .dev-tier-toggle { left: 0.5rem; bottom: 0.5rem; gap: 0.2rem; padding: 0.25rem 0.3rem; }
  .dev-tier-toggle-label { display: none; } /* "DEV Preview" text -- the two buttons alone are enough context at this width */
  .dev-tier-toggle-btn { font-size: 0.65rem; padding: 0.3rem 0.45rem; }

  /* Category bars: label column shrinks so the track gets room to read on
     a narrow screen (same "give the meaningful visual more space" idea as
     the Positions table -> card swap above). */
  .syn-bar-row { grid-template-columns: 6rem 1fr 2.2rem; gap: 0.4rem; }
  .syn-top-aspects { grid-template-columns: 1fr; }
}

/* Narrow phones (iPhone SE/older Android, ~320-380px): "Stellaverum" at
   the 640px breakpoint's 2rem/0.03em still runs close to the edge once
   the .site-header's 1.5rem side padding is subtracted from a 320-360px
   viewport, so shrink once more specifically for the wordmark -- nothing
   else in the 640px block needs a second tier at this width. */
@media (max-width: 380px) {
  .site-header h1 { font-size: 1.65rem; letter-spacing: 0.01em; }
}

/* ------------------------------------------------------------------ */
/* THEME SYSTEM -- dark (default, above) + light "dawn" variant        */
/*                                                                      */
/* Dark is the unchanged :root palette declared at the top of this     */
/* file. Light is an on-brand "dawn" reading of the same identity, NOT */
/* the cream/tan/orange-brown variant Troy rejected on 2026-07-04      */
/* (commit f0badd0) -- ground is cool lavender-white, ink is deep      */
/* indigo, gold is deepened for text-on-light contrast (kept vivid for */
/* fills/chips/borders via the --gold-fill* tokens), element/aspect    */
/* hues keep the same identity, lightened for the light ground.        */
/*                                                                      */
/* Applied three ways so both no-JS (pure OS preference) and the JS    */
/* toggle (prototype/theme.js, stamps data-theme on <html>, persisted  */
/* in localStorage) work correctly:                                    */
/*   1. @media (prefers-color-scheme: light) -- OS-level default when  */
/*      no explicit choice has been made (or JS never ran).            */
/*   2. :root[data-theme="light"] -- explicit toggle-to-light.         */
/*   3. :root[data-theme="dark"] -- explicit toggle-to-dark. Needed so */
/*      the toggle can force dark even when the OS prefers light: this */
/*      selector's extra attribute bumps its specificity above the    */
/*      plain `:root` used inside the media query, so it wins          */
/*      regardless of source order. Its values are simply the same     */
/*      dark values already declared at the top of this file.          */
/* Keep all three token blocks (media-light, explicit-light, explicit- */
/* dark-force) in sync by eye -- there's no preprocessor here to share  */
/* them structurally.                                                  */
/* ------------------------------------------------------------------ */

@media (prefers-color-scheme: light) {
  :root {
    --ink: #232345;
    --ink-dim: #45446b;
    --ink-muted: #6b6a92;
    --ink-strong: #201f3d;
    --night-950: #f4f3fb;
    --night-900: #f2f0fb;
    --night-800: #fdfcff;
    --night-700: #ece9f7;
    --night-600: #cfc9ea;
    --gold: #7d5f17;
    --gold-bright: #6b5012;
    --gold-dim: #8a6a1a;
    --gold-fill: #c9962e;
    --gold-fill-bright: #e0b84a;
    --gold-fill-rgb: 201, 150, 46;
    --gold-fill-bright-rgb: 224, 184, 74;
    --warm: #d9773c;
    --cool: #4f87a8;
    --warm-rgb: 217, 119, 60;
    --cool-rgb: 79, 135, 168;
    --warm-tint: #9c4a1a;
    --cool-tint: #285a78;
    /* --fire/--earth/--air/--water stay the SAME hex as dark mode on
       purpose: the zodiac wedges render at a fixed opacity:0.28 over the
       ground color (see .zodiac-wedge), and that blend alone naturally
       pastelizes any fixed hue far more against a light ground than a
       dark one -- verified these already land as soft, distinct pastels
       (peach/sage/pale-yellow/pale-blue) against the dawn ground. */
    --ink-muted-rgb: 107, 106, 146;
    --ok: #2e7d32;
    --err: #c62828;
    --edge-shadow-rgb: 35, 35, 69;
    --shadow: 0 8px 30px rgba(35, 35, 69, 0.12);
    color-scheme: light;
    /* The landing scene stays a night sky by identity (docs/UX-NAV.md
       "Landing wall + celestial scene") -- a lifted DEEP TWILIGHT indigo,
       not the bright near-white the page ground itself flips to above.
       Still clearly dark enough for the silver/gold filigree constellations
       to read brightly against it. */
    --landing-sky-top: #2e3868;
    --landing-sky-mid: #3a437c;
    --landing-sky-bottom: #4b5490;
  }
}

:root[data-theme="light"] {
  --ink: #232345;
  --ink-dim: #45446b;
  --ink-muted: #6b6a92;
  --ink-strong: #201f3d;
  --night-950: #f4f3fb;
  --night-900: #f2f0fb;
  --night-800: #fdfcff;
  --night-700: #ece9f7;
  --night-600: #cfc9ea;
  --gold: #7d5f17;
  --gold-bright: #6b5012;
  --gold-dim: #8a6a1a;
  --gold-fill: #c9962e;
  --gold-fill-bright: #e0b84a;
  --gold-fill-rgb: 201, 150, 46;
  --gold-fill-bright-rgb: 224, 184, 74;
  --warm: #d9773c;
  --cool: #4f87a8;
  --warm-rgb: 217, 119, 60;
  --cool-rgb: 79, 135, 168;
  --warm-tint: #9c4a1a;
  --cool-tint: #285a78;
  --ink-muted-rgb: 107, 106, 146;
  --ok: #2e7d32;
  --err: #c62828;
  --edge-shadow-rgb: 35, 35, 69;
  --shadow: 0 8px 30px rgba(35, 35, 69, 0.12);
  color-scheme: light;
  /* Same lifted-twilight landing-scene override as the media-query block
     above -- kept in sync by eye, per this file's own THEME SYSTEM comment. */
  --landing-sky-top: #2e3868;
  --landing-sky-mid: #3a437c;
  --landing-sky-bottom: #4b5490;
}

:root[data-theme="dark"] {
  --ink: #eef0fb;
  --ink-dim: #b9beda;
  --ink-muted: #8d92b8;
  --ink-strong: #0a0b1a;
  --night-950: #0a0b1a;
  --night-900: #10122a;
  --night-800: #171a3a;
  --night-700: #20244a;
  --night-600: #2b2f5c;
  --gold: #d8b464;
  --gold-bright: #f0cf8a;
  --gold-dim: #9c8452;
  --gold-fill: #d8b464;
  --gold-fill-bright: #f0cf8a;
  --gold-fill-rgb: 216, 180, 100;
  --gold-fill-bright-rgb: 240, 207, 138;
  --warm: #c9622a;
  --cool: #3d6b8a;
  --warm-rgb: 201, 98, 42;
  --cool-rgb: 61, 107, 138;
  --warm-tint: #e59a6c;
  --cool-tint: #7fb2d1;
  --ink-muted-rgb: 141, 146, 184;
  --ok: #7fae6a;
  --err: #e08a8a;
  --edge-shadow-rgb: 0, 0, 0;
  --shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
  color-scheme: dark;
  /* Explicit toggle-to-dark forces the landing scene back to its base
     :root values (same reasoning as every other token in this block --
     specificity bump over the plain :root the media query uses, so it
     wins regardless of OS preference or source order). */
  --landing-sky-top: #0a1030;
  --landing-sky-mid: #141c3e;
  --landing-sky-bottom: #1e2a55;
}

/* Birthplace lookup */
.place-lookup { display: flex; gap: 0.5rem; }
.place-lookup input { flex: 1; min-width: 0; }
.place-lookup button { white-space: nowrap; padding: 0.4rem 0.9rem; }
.hint { display: block; margin-top: 0.3rem; opacity: 0.75; font-size: 0.8rem; line-height: 1.35; }

/* Auto-derived timezone (tzresolve.js) + its manual-override disclosure */
.tz-derived {
  font-size: 0.85rem;
  color: var(--gold-bright);
  line-height: 1.4;
  padding: 0.5rem 0.7rem;
  background: rgba(var(--gold-fill-rgb), 0.08);
  border: 1px solid var(--gold-dim);
  border-radius: 8px;
}
.tz-derived.tz-manual { color: var(--ink-dim); background: transparent; border-style: dashed; }
.tz-derived.tz-error { color: var(--err); border-color: var(--err); }

.tz-advanced { margin: -0.4rem 0 1.1rem; }
.tz-advanced summary {
  cursor: pointer;
  color: var(--ink-muted);
  font-size: 0.8rem;
  margin-bottom: 0.6rem;
}
.tz-advanced[open] summary { color: var(--gold); }

.link-button {
  display: inline-block;
  margin-top: 0.5rem;
  background: none;
  border: none;
  padding: 0;
  color: var(--gold);
  font-size: 0.8rem;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.link-button:hover { color: var(--gold-bright); }

select:disabled { opacity: 0.5; cursor: not-allowed; }

/* Guided birthplace lookup (country -> region -> city) result indicator */
.geocode-result {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.95rem;
  font-weight: 600;
  margin-bottom: 0.35rem;
}
.geocode-result-icon { font-size: 1.15rem; line-height: 1; }
.geocode-result.geocode-ok { color: var(--ok); }
.geocode-result.geocode-fail { color: var(--err); }

/* ------------------------------------------------------------------ */
/* Glossary popovers (prototype/glossary.js)                          */
/* ------------------------------------------------------------------ */

.glossary-trigger {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.glossary-trigger-text {
  text-decoration: underline dotted;
  text-underline-offset: 3px;
  text-decoration-color: var(--gold-dim);
}
.glossary-trigger-text:hover, .glossary-trigger-text:focus-visible {
  color: var(--gold-bright);
  text-decoration-color: var(--gold-bright);
}
.glossary-trigger-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}
.glossary-trigger-chip:hover, .glossary-trigger-chip:focus-visible { filter: brightness(1.2); }
.glossary-info-icon { font-size: 0.78em; opacity: 0.85; }
.glossary-trigger:focus-visible {
  outline: 2px solid var(--gold-bright);
  outline-offset: 2px;
  border-radius: 3px;
}

.glossary-popover {
  max-width: 340px;
  width: min(340px, calc(100vw - 2rem));
  background: linear-gradient(180deg, var(--night-800), var(--night-900));
  border: 1px solid var(--gold-dim);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 1.1rem 1.2rem 1.2rem;
  outline: none;
  text-align: left;
  white-space: normal;
}
.glossary-popover-anchored { position: fixed; z-index: 1101; }
.glossary-popover-title {
  font-family: var(--serif);
  color: var(--gold-bright);
  font-size: 1.1rem;
  margin: 0 1.6rem 0.6rem 0;
}
.glossary-popover-rundown { color: var(--ink); font-size: 0.9rem; margin: 0 0 0.7rem; line-height: 1.5; white-space: normal; }
.glossary-popover-example {
  color: var(--ink-dim);
  font-size: 0.87rem;
  font-style: italic;
  margin: 0;
  line-height: 1.5;
  border-top: 1px solid var(--night-600);
  padding-top: 0.6rem;
  white-space: normal;
}
.glossary-close {
  position: absolute;
  top: 0.5rem;
  right: 0.6rem;
  background: none;
  border: none;
  color: var(--ink-muted);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.1rem 0.4rem;
}
.glossary-close:hover, .glossary-close:focus-visible { color: var(--gold-bright); }

/* Narrow screens: centered modal card over a dim backdrop instead of an
   anchored popover (anchoring near a small table cell stops making sense on
   a phone-width viewport). */
.glossary-overlay {
  position: fixed;
  inset: 0;
  z-index: 1100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: radial-gradient(ellipse at center, rgba(var(--overlay-a-rgb), 0.88) 0%, rgba(var(--overlay-b-rgb), 0.94) 100%);
}
.glossary-popover-sheet { position: relative; }

/* ------------------------------------------------------------------ */
/* "Generating Chart" reveal modal (prototype/reveal.js)               */
/* ------------------------------------------------------------------ */

.reveal-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: radial-gradient(ellipse at center, rgba(var(--overlay-a-rgb), 0.92) 0%, rgba(var(--overlay-b-rgb), 0.96) 100%);
}

.reveal-modal {
  width: 100%;
  max-width: 440px;
  background: var(--night-800);
  border: 1px solid var(--gold-dim);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 2rem 1.75rem 1.75rem;
  text-align: center;
  outline: none; /* focus is visually conveyed by the modal border/shadow, not a default ring on this container */
}

.reveal-title {
  font-family: var(--serif);
  font-size: 1.35rem;
  color: var(--gold-bright);
  letter-spacing: 0.03em;
  margin: 0 0 1.25rem;
}

.reveal-constellation {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  margin-bottom: 1.4rem;
}
.reveal-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--night-600);
  transition: background-color 0.35s ease, box-shadow 0.35s ease;
}
.reveal-dot-active { background: var(--gold-fill); }
.reveal-dot-current {
  background: var(--gold-fill-bright);
  box-shadow: 0 0 9px 2px rgba(var(--gold-fill-bright-rgb), 0.65);
  animation: reveal-twinkle 1.15s ease-in-out infinite;
}

.reveal-message {
  color: var(--ink);
  font-size: 1rem;
  line-height: 1.5;
  min-height: 2.9em; /* reserves space so stage-to-stage text length changes never shift the modal's height */
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 0 1.3rem;
}

.reveal-progress {
  height: 6px;
  background: var(--night-600);
  border-radius: 4px;
  overflow: hidden;
  margin-bottom: 1.3rem;
}
.reveal-progress-bar {
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--gold-fill), var(--gold-fill-bright));
  transition: width 0.4s ease;
}

.reveal-skip { margin-top: 0; }

@keyframes reveal-twinkle {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.65; transform: scale(1.3); }
}

@media (prefers-reduced-motion: reduce) {
  .reveal-dot-current { animation: none; }
  .reveal-progress-bar, .reveal-dot { transition: none; }
}

/* ------------------------------------------------------------------ */
/* Tab navigation (prototype/tabs.js, docs/UX-NAV.md)                   */
/*                                                                      */
/* One shared markup (<nav id="tabBar">, built entirely by tabs.js) for */
/* both layouts -- desktop gets the default rules below (a plain row   */
/* under the header); the <640px media query further down repositions  */
/* this SAME element to a fixed bottom bar. No separate mobile markup. */
/* ------------------------------------------------------------------ */

:root {
  --tabbar-mobile-height: 60px;
}

.tab-bar { padding: 0.5rem 1rem; }
.tab-bar-list {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 900px;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.3rem;
}
.tab-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  background: none;
  border: none;
  border-radius: 8px;
  padding: 0.5rem 0.9rem;
  min-width: 64px;
  color: var(--ink-muted);
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: none;
  transition: color 0.15s ease, background-color 0.15s ease;
}
.tab-item:hover { color: var(--ink); box-shadow: none; }
.tab-item:active { transform: none; }
.tab-item:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; }
.tab-item .tab-icon-svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  display: block;
}
.tab-item .tab-label { white-space: nowrap; }
.tab-item.active {
  color: var(--gold-bright);
  background: rgba(var(--gold-fill-rgb), 0.14);
}

/* "Edit birth details" affordance -- visible from any tab once a chart
   exists (docs/UX-NAV.md), sits in the header area above the tab content. */
.chart-toolbar {
  max-width: 900px;
  margin: 0 auto 0.25rem;
  padding: 0 1.25rem;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.6rem 1rem;
}
.chart-toolbar .link-button { margin-top: 0; }

/* Profile switcher (docs/UX-NAV.md "Primary chart + profile switcher"):
   compact, sits to the left of "Edit birth details" in the same toolbar
   row -- prototype/account.js shows/hides + populates it. */
.profile-switcher {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-right: auto;
}
/* Same gotcha as .social-auth-btn[hidden] above: this class's own
   display:flex would otherwise beat the UA [hidden] rule (author
   normal-priority styles win over UA styles regardless of source order),
   rendering the switcher even while account.js has set `hidden`. */
.profile-switcher[hidden] { display: none; }
.profile-switcher-select {
  width: auto;
  max-width: 220px;
  padding: 0.4rem 0.6rem;
  font-size: 0.85rem;
}
.profile-switcher-make-primary { margin-top: 0; font-size: 0.8rem; white-space: nowrap; }
/* Same [hidden]-losing-to-author-styles gotcha as .profile-switcher and
   .social-auth-btn above -- this button also carries .link-button, whose
   own `display: inline-block` would otherwise beat the UA [hidden] rule
   when account.js sets btn.hidden = true (no primary-eligible profile
   selected / switcher not shown at all). */
.profile-switcher-make-primary[hidden] { display: none; }

/* Star toggle on each saved-chart row (docs/UX-NAV.md: "star button in the
   account panel's saved list") -- filled/gold + inert once a row IS the
   primary, an outline star (actionable) otherwise. 44px-ish tap target
   matches the rest of this panel's controls. */
.account-profile-star {
  background: none;
  border: none;
  padding: 0.2rem 0.3rem;
  min-height: 32px;
  min-width: 32px;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--ink-muted);
  cursor: pointer;
}
.account-profile-star:hover { color: var(--gold-bright); }
.account-profile-star:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; }
.account-profile-star.is-primary { color: var(--gold-bright); cursor: default; }
.account-profile-star:disabled { cursor: default; }

/* Delete (✕) on each saved-chart row (docs: "Delete a chart", Troy
   2026-07-06) -- same quiet/inert-until-hovered treatment as the star
   toggle above, but reads as destructive (var(--err)) on hover/focus
   rather than gold, so it doesn't look like just another neutral action. */
.account-profile-delete {
  background: none;
  border: none;
  padding: 0.2rem 0.3rem;
  min-height: 32px;
  min-width: 32px;
  font-size: 0.95rem;
  line-height: 1;
  color: var(--ink-muted);
  cursor: pointer;
  margin-left: auto;
}
.account-profile-delete:hover { color: var(--err); }
.account-profile-delete:focus-visible { outline: 2px solid var(--err); outline-offset: 2px; }

.account-make-primary-field { margin: -0.4rem 0 1.1rem; }
.account-make-primary-field label { font-size: 0.85rem; }

/* "+ Add another chart" (Troy 2026-07-06 round) -- sits between Save and
   the saved-charts list, same submit-row rhythm as Save's own field above
   it. #accountAddAnotherMsg reuses the plain .panel-note treatment
   #accountProfilesEmpty already has (a quiet, non-alarming note, not an
   error -- reaching the cap is an expected, common state, not a failure). */
.account-add-another-row { margin-top: -0.3rem; }

/* Shared "compute a chart first" note for chart-dependent tabs
   (Daily/Human Design/Traditions/Compatibility) before any chart exists.
   The old per-tab account gate (#authGateCard) is gone -- docs/UX-NAV.md
   "Landing wall + celestial scene": the account wall now lives one level
   up, at #landingView vs #appView, so #appView's tabs only ever need this
   hasChart-based note, never an auth-based one. */
#tabLockedNote { text-align: center; }
#tabLockedNote .panel-note { margin-top: 0; }

@media (max-width: 640px) {
  .tab-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 600;
    background: var(--night-900);
    border-top: 1px solid var(--night-600);
    padding: 0.3rem 0.4rem calc(0.3rem + env(safe-area-inset-bottom));
    box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.25);
  }
  .tab-bar-list { justify-content: space-around; flex-wrap: nowrap; gap: 0; }
  .tab-item { flex: 1 1 0; min-width: 0; min-height: 44px; padding: 0.35rem 0.2rem; font-size: 0.62rem; }
  .tab-item .tab-icon-svg { width: 21px; height: 21px; }

  /* The fixed bottom tab bar covers the last ~60-70px of content
     otherwise -- reserve that space so nothing sits underneath it,
     unread and untappable. */
  main { padding-bottom: calc(var(--tabbar-mobile-height) + env(safe-area-inset-bottom) + 1.25rem); }

  .chart-toolbar { padding: 0 1rem; }
}

/* Narrowest phones: 6 tabs' full labels start crowding a <=360px bottom
   bar -- drop to icon-only rather than wrapping to a second row or
   scrolling (both would be worse for a bottom nav). */
@media (max-width: 360px) {
  .tab-item .tab-label { display: none; }
  .tab-item { padding: 0.5rem 0.2rem; }
}

/* Dev preview tier toggle (docs/TIERS.md, .dev-tier-toggle declared far
   above): on mobile it now shares the bottom edge with the fixed tab bar
   -- lift it to sit just above the tab bar instead of overlapping it.
   Deliberately a SEPARATE, LATER media block (not edited into the
   existing one) -- same specificity, later source order wins, so this is
   the one that actually applies; see that block's own comment. */
@media (max-width: 640px) {
  .dev-tier-toggle { bottom: calc(var(--tabbar-mobile-height) + env(safe-area-inset-bottom) + 0.6rem); }
}

/* Feedback button (.feedback-fab-btn declared far above): same "share the
   bottom edge with the fixed tab bar" problem as .dev-tier-toggle just
   above -- plus it also has to clear THAT toggle, since both are fixed
   bottom-left in the app view at once today. Lifts it above the tab bar by
   the same amount, plus .dev-tier-toggle's own approximate mobile height
   and gap. Left edge matches .dev-tier-toggle's own mobile left offset so
   the two read as one aligned stack, not two unrelated corner controls. */
@media (max-width: 640px) {
  .feedback-fab-btn {
    left: 0.5rem;
    bottom: calc(var(--tabbar-mobile-height) + env(safe-area-inset-bottom) + 0.6rem + 2.4rem);
  }
}

/* ------------------------------------------------------------------ */
/* Just Breathe (prototype/breathe.js, docs/UX-NAV.md "Just Breathe")   */
/*                                                                      */
/* Color reuse, deliberate: inhale reuses the SAME --cool/--cool-tint   */
/* tokens the trine/sextile aspect lines already use ("cool" = calm),   */
/* exhale reuses --warm/--warm-tint (already this app's "warm release,  */
/* not alarm" color -- --warm-tint is ALSO already documented above as  */
/* AA-verified TEXT-safe in both themes, see the Human Design gate-     */
/* label comment), hold reuses --gold-fill/--gold-bright. No new hues   */
/* introduced for this feature -- the app's existing warm/cool          */
/* temperature language maps onto inhale/exhale almost exactly, and     */
/* it's already proven accessible in both themes.                      */
/* ------------------------------------------------------------------ */

.breathe-pattern-row, .breathe-length-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.breathe-pattern-btn, .breathe-length-btn {
  background: var(--night-700);
  color: var(--ink-dim);
  border: 1px solid var(--night-600);
  border-radius: 8px;
  padding: 0.5rem 0.9rem;
  font-size: 0.85rem;
  font-weight: 600;
  min-height: 44px;
  box-shadow: none;
  text-align: center;
  line-height: 1.3;
}
.breathe-pattern-btn small { display: block; font-weight: 400; opacity: 0.75; font-size: 0.75em; }
.breathe-pattern-btn:hover, .breathe-length-btn:hover { border-color: var(--gold-dim); color: var(--ink); box-shadow: none; }
.breathe-pattern-btn.active, .breathe-length-btn.active {
  background: rgba(var(--gold-fill-rgb), 0.16);
  border-color: var(--gold-dim);
  color: var(--gold-bright);
}
.breathe-custom-row .field { margin-bottom: 0.8rem; }

.breathe-visual {
  position: relative;
  width: 100%;
  max-width: 280px;
  aspect-ratio: 1 / 1;
  margin: 1rem auto 1.25rem;
}
.breathe-progress-ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.breathe-progress-track { fill: none; stroke: var(--night-600); stroke-width: 5; }
.breathe-progress-fill {
  fill: none;
  stroke: var(--gold-fill);
  stroke-width: 5;
  stroke-linecap: round;
  transition: stroke-dashoffset 0.3s linear;
}
.breathe-circle-wrap { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.breathe-circle {
  width: 62%;
  height: 62%;
  border-radius: 50%;
  transform: scale(0.55);
  background: rgba(var(--cool-rgb), 0.28);
  border: 2px solid var(--cool-tint);
  box-shadow: 0 0 26px 4px rgba(var(--cool-rgb), 0.35);
}
.breathe-circle.phase-inhale {
  background: rgba(var(--cool-rgb), 0.28);
  border-color: var(--cool-tint);
  box-shadow: 0 0 26px 4px rgba(var(--cool-rgb), 0.35);
}
.breathe-circle.phase-exhale {
  background: rgba(var(--warm-rgb), 0.26);
  border-color: var(--warm-tint);
  box-shadow: 0 0 26px 4px rgba(var(--warm-rgb), 0.32);
}
.breathe-circle.phase-hold {
  background: rgba(var(--gold-fill-rgb), 0.22);
  border-color: var(--gold-bright);
  box-shadow: 0 0 22px 4px rgba(var(--gold-fill-rgb), 0.4);
}
.breathe-circle.phase-hold.pulse { animation: breathe-hold-pulse 1.6s ease-in-out infinite; }
@keyframes breathe-hold-pulse {
  0%, 100% { box-shadow: 0 0 18px 3px rgba(var(--gold-fill-rgb), 0.3); }
  50% { box-shadow: 0 0 30px 8px rgba(var(--gold-fill-rgb), 0.55); }
}

/* Reduced-motion fallback: a row of plain dots, one per second of the
   current phase, filled left-to-right -- no scale/pulse animation
   anywhere in this state. prototype/breathe.js hides/shows this vs
   .breathe-circle-wrap based on prefers-reduced-motion (live-updated). */
.breathe-stepped {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  flex-wrap: wrap;
  padding: 0 1.5rem;
}
.breathe-step { width: 14px; height: 14px; border-radius: 50%; background: var(--night-600); }
.breathe-step.filled.phase-inhale { background: var(--cool-tint); }
.breathe-step.filled.phase-exhale { background: var(--warm-tint); }
.breathe-step.filled.phase-hold { background: var(--gold-bright); }

.breathe-text-wrap {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  pointer-events: none;
}
.breathe-phase-word {
  font-family: var(--serif);
  font-size: 1.25rem;
  margin: 0 0 0.3rem;
  color: var(--ink);
}
/* Text-safe reuse: --cool-tint/--warm-tint/--gold-bright are all already
   verified AA-on-ground tokens in both themes (see this section's header
   comment) -- reused here directly as phase-word colors. */
.breathe-phase-word.phase-inhale { color: var(--cool-tint); }
.breathe-phase-word.phase-exhale { color: var(--warm-tint); }
.breathe-phase-word.phase-hold { color: var(--gold-bright); }
.breathe-countdown {
  font-variant-numeric: tabular-nums;
  font-size: 1.8rem;
  font-weight: 700;
  color: var(--ink);
  margin: 0;
  min-height: 1.2em; /* reserves space for the blank countdown shown during the prep/"Get ready" state */
}

.breathe-session-note { text-align: center; color: var(--ink-muted); font-size: 0.85rem; margin: 0 0 1rem; }
.breathe-controls { justify-content: center; }
.breathe-complete { text-align: center; }
.breathe-complete-text { color: var(--gold-bright); font-family: var(--serif); font-size: 1.1rem; margin: 0 0 1rem; }

/* Belt-and-suspenders: even if prototype/breathe.js's own JS-driven
   prefers-reduced-motion detection somehow ran before this stylesheet
   applied, no scale/pulse animation can render here regardless. */
@media (prefers-reduced-motion: reduce) {
  .breathe-circle { transition: none !important; animation: none !important; }
}

/* ------------------------------------------------------------------ */
/* UI round 3b: Map (astrocartography) / Numbers (numerology) /        */
/* Compatibility inner sub-tabs / Daily quote card. All theme via the   */
/* same var(--...) tokens as the rest of this file EXCEPT the map's own */
/* fixed dark surface (documented in prototype/acg-panel.js's own       */
/* header -- the validated per-body color palette assumes a constant    */
/* dark ground regardless of the site's light/dark toggle).             */
/* ------------------------------------------------------------------ */

/* ---- Map (prototype/acg-panel.js) ---- */

.acg-map-wrap { margin-bottom: 1rem; }
.acg-map-container {
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--night-600);
  line-height: 0; /* kills the inline-svg baseline gap */
}
.acg-map-svg { display: block; width: 100%; height: auto; }
/* Fixed dark navy regardless of theme -- see acg-panel.js's palette note. */
.acg-map-svg-bg { background: #0a0b1a; }

.acg-coastline { stroke: #4a5480; stroke-width: 0.6; opacity: 0.85; }

.acg-line-visible { stroke-width: 1.1; opacity: 0.92; }
.acg-line-solid { stroke-dasharray: none; }
.acg-line-dashed { stroke-dasharray: 2.2 1.6; }
.acg-line-hit { stroke: transparent; stroke-width: 9; cursor: pointer; pointer-events: stroke; }
.acg-body-group.acg-body-hidden { display: none; }

.acg-birthplace circle:first-child { fill: var(--gold-fill-bright); }
.acg-birthplace-ring { stroke: var(--gold-fill-bright); stroke-width: 0.9; opacity: 0.8; }

.acg-map-silhouette .acg-map-svg { filter: blur(2.5px); opacity: 0.55; }

/* Injected off-screen trigger button (see acg-panel.js's "synthetic
   trigger" note) -- never seen or interacted with directly. */
.acg-map-trigger-hidden { width: 1px; height: 1px; padding: 0; margin: 0; border: 0; opacity: 0; pointer-events: none; overflow: hidden; position: fixed; }

/* ---- Best Places to Live (ASTROCARTOGRAPHY.md v1.1, acg-panel.js) ---- */

/* City dots: visible circle has no pointer events of its own (same
   visible/hit split as .acg-line-visible/.acg-line-hit above) -- only the
   larger transparent .acg-city-dot-hit circle actually receives clicks. */
.acg-city-dot-visible { pointer-events: none; stroke: rgba(10, 11, 26, 0.55); stroke-width: 0.3; }
.acg-city-dot-hit { fill: transparent; cursor: pointer; }

.acg-gradient-legend-wrap { margin: 0 0 1.1rem; }
.acg-gradient-legend { display: flex; flex-direction: column; gap: 0.35rem; }
/* Inline SVG now (acg-panel.js buildGradientLegend -- CSP note in that
   file's header: an HTML div's `background: linear-gradient(...)` set from
   JS is just as blocked by style-src as an inline `style=` attribute, so
   this renders through an SVG <linearGradient>'s stop-color attributes
   instead). display:block + width:100% is the SAME override
   .acg-map-svg needs and for the same reason -- SVG defaults to inline
   layout at its own intrinsic size, it does not stretch to fill a flex
   column on its own. */
.acg-gradient-bar {
  display: block;
  width: 100%;
  height: 12px;
  border-radius: 6px;
  border: 1px solid var(--night-600);
}
.acg-gradient-labels {
  display: flex;
  justify-content: space-between;
  color: var(--ink-muted);
  font-size: 0.78rem;
}

.acg-headlines-wrap { display: grid; gap: 1.3rem; margin: 0 0 1.2rem; }
/* Each list is its own card with a top accent border in the SAME
   validated score-ramp colors scoreToColor()/city dots already use
   (#5ee38b best / #bb6955 challenging / #c0312d worst -- acg-panel.js's
   SCORE_COLOR_STOPS) so best/challenging/worst are unmistakable from the
   card itself, independent of column order or position relative to the
   gradient bar (Troy's 2026-07-06 "make best-vs-worst unmistakable"
   layout note). */
.acg-headline-group {
  min-width: 0;
  background: var(--night-800);
  border: 1px solid var(--night-600);
  border-top: 3px solid var(--night-600);
  border-radius: 10px;
  padding: 0.9rem 1rem 1rem;
}
.acg-headline-group-best { border-top-color: #5ee38b; }
.acg-headline-group-challenging { border-top-color: #bb6955; }
.acg-headline-group-worst { border-top-color: #c0312d; }
.acg-headline-title {
  font-family: var(--serif);
  font-size: 1.05rem;
  color: var(--gold-bright);
  margin: 0 0 0.5rem;
}
.acg-headline-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.acg-headline-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-radius: 8px;
  padding: 0.55rem 0.8rem;
}
.acg-headline-trigger { text-align: left; }
.acg-headline-score { color: var(--ink-muted); font-size: 0.85rem; font-variant-numeric: tabular-nums; flex: none; }

@media (min-width: 640px) {
  .acg-headlines-wrap { grid-template-columns: repeat(3, 1fr); }
}

/* Place-search's own scored result (buildSearchScoringPanel) -- an INLINE
   band+why panel, not a popover, sitting above the existing proximity
   list in the same #acgSearchResults container. */
.acg-search-scoring-panel {
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-left-width: 4px;
  border-left-color: var(--gold-fill-bright);
  border-radius: 8px;
  padding: 0.8rem 1rem;
  margin-bottom: 1rem;
}
.acg-search-scoring-panel p { margin: 0.4rem 0 0; color: var(--ink-dim); font-size: 0.9rem; }
.acg-search-scoring-why { font-size: 0.85rem; }

/* Pulsing color-coded square marker at a searched place (CSS keyframe;
   prefers-reduced-motion swaps it for a static ring -- both shapes always
   exist in the DOM, see acg-panel.js's buildSearchMarker). transform-box:
   fill-box lets the percentage-based default transform-origin (center)
   apply to the shape's OWN bounding box rather than the whole SVG
   viewport, so it scales in place instead of drifting toward a corner. */
.acg-search-marker-square {
  transform-box: fill-box;
  transform-origin: center;
  stroke: rgba(10, 11, 26, 0.6);
  stroke-width: 0.4;
}
.acg-search-marker-ring {
  display: none;
  fill: none;
  stroke-width: 1.2;
}
@media (prefers-reduced-motion: no-preference) {
  .acg-search-marker-square { animation: acg-search-pulse 1.6s ease-in-out infinite; }
}
@media (prefers-reduced-motion: reduce) {
  .acg-search-marker-square { display: none; }
  .acg-search-marker-ring { display: block; }
}
@keyframes acg-search-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.45; transform: scale(1.7); }
}

/* Map + its own key as one visual unit (layout rework 2026-07-06 -- see
   index.html's acg-panel comment). A subtle card boundary around BOTH the
   map and its legend/gradient reads as "these belong together," which is
   what stops the gradient bar from feeling like a stray element sitting
   over whatever happens to come next in the DOM. */
.acg-map-card {
  background: var(--night-800);
  border: 1px solid var(--night-600);
  border-radius: 10px;
  padding: 1rem;
  margin-bottom: 1.2rem;
}
.acg-map-card .acg-map-wrap { margin-bottom: 0; }
.acg-map-key { margin-top: 0.9rem; padding-top: 0.9rem; border-top: 1px solid var(--night-600); }
.acg-map-key .acg-legend-row { margin-top: 0; }
.acg-map-key .acg-gradient-legend-wrap { margin: 0.7rem 0 0; }

/* Section headings shared by the headlines + search sections below the map
   card (layout rework 2026-07-06) -- gives each block its own clearly
   labeled identity instead of an unlabeled stack of divs. */
.acg-section-heading {
  font-family: var(--serif);
  font-size: 1.15rem;
  color: var(--gold-bright);
  margin: 0 0 0.35rem;
}
.acg-section-sub { color: var(--ink-dim); font-size: 0.88rem; margin: 0 0 0.9rem; max-width: 60ch; }
.acg-headlines-section { margin-bottom: 1.4rem; }
.acg-search-section { margin-bottom: 1rem; }

/* "Your map, decoded" (mapPersonal composition) -- moved directly under
   the map card (2026-07-06, was previously the LAST thing on the tab,
   past the search box, easy to miss) and restyled with a gold accent
   border + a small "personal" badge so it reads as the most
   YOU-specific section on the page, not a quiet footnote. */
.acg-your-map-wrap { margin-bottom: 1.4rem; }
.acg-your-map {
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-left: 4px solid var(--gold-fill-bright);
  border-radius: 8px;
  padding: 1.1rem 1.3rem;
}
.acg-your-map-title {
  font-family: var(--serif);
  font-size: 1.2rem;
  color: var(--gold-bright);
  margin: 0 0 0.6rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.acg-your-map-badge {
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-strong);
  background: var(--gold-fill-bright);
  border-radius: 999px;
  padding: 0.18rem 0.55rem;
}
.acg-your-map-lead { color: var(--ink-dim); font-style: italic; margin: 0 0 0.8rem; font-size: 0.9rem; }
.acg-your-map-lines { display: flex; flex-direction: column; gap: 0.6rem; }
.acg-your-map-lines p { margin: 0; color: var(--ink); font-size: 0.9rem; line-height: 1.5; }

.acg-legend-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.9rem 0; }
.acg-legend-btn {
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: var(--night-700); border: 1px solid var(--night-600);
  border-radius: 999px; padding: 0.35rem 0.7rem; font-size: 0.82rem; color: var(--ink);
  min-height: 36px;
}
.acg-legend-btn:hover { box-shadow: none; border-color: var(--gold-dim); }
.acg-legend-btn-off { opacity: 0.45; }
.acg-legend-swatch { width: 12px; height: 12px; border-radius: 50%; flex: none; display: inline-block; }
.acg-legend-label { white-space: nowrap; }

.acg-search-row.place-lookup { margin-top: 0.5rem; }
.acg-search-status { margin: 0.4rem 0 0.8rem; }
.acg-proximity-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.7rem; }
.acg-proximity-item {
  background: var(--night-700); border: 1px solid var(--night-600); border-left-width: 4px;
  border-radius: 8px; padding: 0.7rem 0.9rem;
}
.acg-proximity-item.acg-band-on { border-left-color: var(--gold-fill-bright); }
.acg-proximity-item.acg-band-glow { border-left-color: var(--gold-dim); }
.acg-proximity-item.acg-band-faint { border-left-color: var(--night-600); }
.acg-proximity-head { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.3rem; }
.acg-proximity-head strong { color: var(--gold-bright); }
.acg-proximity-km { margin-left: auto; color: var(--ink-muted); font-size: 0.82rem; font-variant-numeric: tabular-nums; }
.acg-proximity-item p { margin: 0; color: var(--ink-dim); font-size: 0.9rem; }

/* ---- Numbers (prototype/numbers-panel.js) ---- */

.numbers-lifepath-card { margin-bottom: 1.3rem; }
.numbers-lifepath-head { display: flex; align-items: center; gap: 1.1rem; margin-bottom: 0.6rem; }
.numbers-lifepath-numeral { font-family: var(--serif); font-size: 3.4rem; color: var(--gold-bright); line-height: 1; }
.numbers-lifepath-label { font-size: 1.05rem; }
.numbers-reading { color: var(--ink); margin: 0; }

.numbers-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 0.8rem; margin: 0.8rem 0 1.2rem; }
.numbers-grid-personal { grid-template-columns: repeat(3, 1fr); margin: 0.8rem 0; }
.numbers-card { background: var(--night-700); border: 1px solid var(--night-600); border-radius: 10px; padding: 0.7rem 0.9rem; text-align: center; }
.numbers-card-head { font-size: 0.82rem; color: var(--gold-dim); margin-bottom: 0.3rem; }
.numbers-card-numeral { font-family: var(--serif); font-size: 1.9rem; color: var(--gold-bright); }
.numbers-card-tag { display: block; color: var(--ink-muted); font-size: 0.72rem; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 0.2rem; }
.numbers-readings .reading-section h3 { display: flex; }
.caveat { font-style: italic; }

/* ---- Compatibility inner sub-tabs (prototype/synastry-panel.js) ---- */

.syn-subtab-bar { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.8rem 0 1.2rem; border-bottom: 1px solid var(--night-600); padding-bottom: 0.8rem; }
.syn-subtab-btn {
  background: transparent; border: 1px solid var(--night-600); color: var(--ink-dim);
  border-radius: 999px; padding: 0.45rem 0.9rem; font-size: 0.88rem; min-height: 40px;
}
.syn-subtab-btn:hover { box-shadow: none; border-color: var(--gold-dim); color: var(--ink); }
.syn-subtab-btn.active { background: var(--gold-fill); border-color: var(--gold-fill); color: var(--ink-strong); font-weight: 600; }
.syn-subtab-panel[hidden] { display: none; }
.syn-hd-channel-list { margin: 0.4rem 0 0; padding-left: 1.2rem; color: var(--ink-dim); }
.syn-hd-channel-list li { margin-bottom: 0.3rem; }
.syn-hd-new-centers { color: var(--gold); font-size: 0.92rem; }
.syn-num-pair-line { font-weight: 600; color: var(--ink); }

/* ---- Daily quote card (prototype/quote-card.js) ---- */

.daily-quote-card {
  margin-top: 1.3rem;
  padding: 1.1rem 1.3rem;
  border-left: 3px solid var(--gold-fill);
  background: rgba(var(--gold-fill-rgb), 0.06);
  border-radius: 0 8px 8px 0;
}
.daily-quote-text {
  margin: 0 0 0.5rem;
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.05rem;
  color: var(--ink);
  line-height: 1.5;
}
.daily-quote-attribution { margin: 0; color: var(--gold-dim); font-size: 0.85rem; text-align: right; }
.daily-quote-source { font-style: italic; }

@media (max-width: 640px) {
  .numbers-lifepath-numeral { font-size: 2.6rem; }
  .numbers-grid-personal { grid-template-columns: repeat(3, 1fr); gap: 0.5rem; }
  .acg-legend-btn { font-size: 0.76rem; padding: 0.3rem 0.55rem; }
}

/* ------------------------------------------------------------------ */
/* Glossary tab (prototype/glossary-tab.js) -- a plain browsable          */
/* reference: title+rundown pairs, no popovers (it IS the reference).    */
/* ------------------------------------------------------------------ */

.glossary-intro-text { color: var(--ink-dim); font-size: 0.95rem; margin: 0 0 1.1rem; }

.glossary-search-row { margin-bottom: 0.4rem; }
.glossary-search-input {
  width: 100%;
  padding: 0.65rem 0.9rem;
  background: var(--night-700);
  border: 1px solid var(--night-600);
  border-radius: 8px;
  color: var(--ink);
  font-size: 1rem;
  font-family: var(--sans);
}
.glossary-search-input:focus {
  outline: none;
  border-color: var(--gold-dim);
  box-shadow: 0 0 0 3px rgba(var(--gold-fill-rgb), 0.15);
}
.glossary-search-input::placeholder { color: var(--ink-muted); }
.glossary-search-count { margin: 0.4rem 0 0; color: var(--ink-muted); font-size: 0.85rem; }

.glossary-section { margin-top: 1.6rem; }
.glossary-section:first-of-type { margin-top: 1.2rem; }
.glossary-section[hidden] { display: none; }
.glossary-section-title {
  font-family: var(--serif);
  font-size: 1.15rem;
  color: var(--gold-bright);
  margin: 0 0 0.7rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--night-600);
  letter-spacing: 0.01em;
}
.glossary-section-entries { display: grid; gap: 0.9rem; }
.glossary-entry[hidden] { display: none; }
.glossary-entry-title { margin: 0 0 0.25rem; color: var(--ink); font-size: 0.98rem; font-weight: 700; }
.glossary-entry-rundown { margin: 0; color: var(--ink-dim); font-size: 0.9rem; line-height: 1.5; }

@media (max-width: 640px) {
  .glossary-section-entries { gap: 0.75rem; }
}

/* Landing logo (Troy 2026-07-06): the celestial emblem above the wordmark,
   and the short "what we offer" line under the sign-in CTA. */
.landing-logo {
  display: block;
  width: clamp(96px, 15vw, 168px);
  height: auto;
  margin: 0 auto 0.6rem;
  filter: drop-shadow(0 0 18px rgba(150, 196, 246, 0.28));
}
.landing-offer {
  color: var(--landing-tagline-ink);
  opacity: 0.9;
  font-size: 0.95rem;
  line-height: 1.5;
  max-width: 30rem;
  margin: 1rem auto 0;
}
@media (max-width: 640px) {
  .landing-logo { width: clamp(84px, 26vw, 132px); }
  .landing-offer { font-size: 0.9rem; max-width: 22rem; }
}
