/* =============================================================================
 * theme.css — global brand foundation for Quantapact
 * =============================================================================
 * Single source of truth for the navy + blue + cyan palette. Imported by
 * every public page so the theme toggle, color variables, and core nav
 * styling stay consistent across surfaces.
 *
 * Brand:
 *   - Navy bg + blue primary + cyan accent = trust foundation
 *   - Violet preserved as quantum-signature accent (logo PACT, score
 *     viz, hero pulse, MONITORED stamp)
 *   - Slate scale for text in light mode; white/slate-300/slate-400
 *     for text in dark mode
 *
 * Pages opt in by linking <link rel="stylesheet" href="/assets/theme.css">
 * in <head> and <script defer src="/assets/theme-toggle.js"></script>
 * before </body>. Existing per-page styles still cascade — this file only
 * defines the variables and core nav/toggle treatment.
 *
 * Light mode is opted into via the .theme-toggle button in the nav, which
 * adds class="light" to <html> and persists the choice to localStorage.
 * Cards/elements that should stay dark (hero animation, score viz) use
 * data-island="dark" to override the light overrides locally.
 * ============================================================================= */

:root {
  /* Default = dark theme. Light overrides are in html.light below. */
  --bg-page: #081120;             /* navy foundation */
  --bg-section: #0B1426;          /* slightly elevated section variant */
  --bg-nav: rgba(8, 17, 32, 0.78);
  --nav-border: rgba(34, 211, 238, 0.12);

  --text-primary: #ffffff;
  --text-secondary: #cbd5e1;      /* slate-300 — body copy */
  --text-tertiary: #94a3b8;       /* slate-400 — secondary */
  --text-muted: #64748b;          /* slate-500 — captions */

  /* Brand semantics */
  --accent-primary: #3b82f6;       /* blue-500 — primary brand / CTAs / links */
  --accent-secondary: #2563eb;     /* blue-600 — hover/darker */
  --accent-deep: #2563eb;
  --accent-darker: #1d4ed8;        /* blue-700 */

  /* Cyan — eyebrows, hairlines, scanner signals */
  --accent-cyan: #22d3ee;          /* cyan-400 */
  --accent-cyan-soft: #67e8f9;     /* cyan-300 */

  /* Violet — quantum-signature ONLY */
  --accent-violet: #a78bfa;        /* violet-400 */
  --accent-violet-deep: #8b5cf6;   /* violet-500 */

  --accent-gold: #fbbf24;          /* amber-400 — warnings */

  /* Grade pill colors — A through F. Canonical palette lives at
     /grade-colors.json (single source of truth for both site + extension).
     Values here mirror that JSON for fast paint (no fetch needed on the site
     since CSS deploys with the rest of the build). To change the palette:
     edit grade-colors.json + this block, redeploy. Extension picks up the
     JSON automatically on next popup open.
     SCHEMA: -dark = saturated bg for white text. -light = pastel bg for
     dark text (B and F use same value as -dark because they need white text
     in both contexts). */
  --grade-A-dark: #16a34a;          /* green-600 */
  --grade-A-light: #34d399;         /* emerald-400 — chips on white bg */
  --grade-B-dark: #8b5cf6;          /* violet-500 — distinct from navy icon + brand blue */
  --grade-B-light: #8b5cf6;         /* same as dark, needs white text */
  --grade-C-dark: #ca8a04;          /* amber-600 */
  --grade-C-light: #facc15;         /* yellow-400 */
  --grade-D-dark: #dc2626;          /* red-600 */
  --grade-D-light: #f87171;         /* red-400 */
  --grade-F-dark: #171717;          /* zinc-900 — near-black */
  --grade-F-light: #171717;         /* same as dark, needs white text */

  /* Legacy aliases — kept for any old CSS still referencing them. New code
     should use the -dark / -light variants explicitly. */
  --grade-a: var(--grade-A-light);
  --grade-b: var(--grade-B-light);
  --grade-c: var(--grade-C-light);
  --grade-d: var(--grade-D-light);
  --grade-f: var(--grade-F-light);

  /* Canonical nav container width — kept here so every page renders the
     header at the SAME max-width. Previously each page declared its own
     .nav-inner block (some 1100, some 1200, some 1280), which made the
     right edge of the nav land in different horizontal positions per
     page — visible as the header "moving" when navigating between pages.
     Pages should NOT redefine .nav-inner max-width; if a page legitimately
     needs a wider page-content area, use a wider `.page` selector, not
     a wider nav. */
  --nav-max-width: 1200px;

  /* Cards: distinctly different value than the page bg */
  --card-bg: rgba(15, 23, 42, 0.65);   /* slate-900 with transparency */
  --card-border: rgba(255, 255, 255, 0.08);
  --card-border-strong: rgba(34, 211, 238, 0.4);

  /* Surface tints — flippable in light mode */
  --surface-subtle: rgba(255, 255, 255, 0.04);
  --surface-soft: rgba(255, 255, 255, 0.06);
  --surface-medium: rgba(255, 255, 255, 0.10);
  --hairline-soft: rgba(255, 255, 255, 0.08);
  --hairline-medium: rgba(255, 255, 255, 0.14);
}

/* Light theme — pure white-ish page, slate text scale, blue accents.
   Quantum-violet stays violet (just darker for AA contrast).
   Hero/result cards stay dark via [data-island="dark"] override below. */
html.light {
  --bg-page: #f8fafc;             /* slate-50 */
  --bg-section: #ffffff;
  --bg-nav: rgba(248, 250, 252, 0.85);
  --nav-border: rgba(15, 23, 42, 0.08);

  --text-primary: #0f172a;        /* slate-900 */
  --text-secondary: #334155;      /* slate-700 */
  --text-tertiary: #64748b;       /* slate-500 */
  --text-muted: #94a3b8;          /* slate-400 */

  /* Light-mode accent palette (per GPT feedback): emphasis tone is darker /
     less saturated than dark mode so it doesn't "buzz" on a white background
     (was reading as fintech/AI startup blue, not security-infrastructure blue).
     CTA backgrounds still use --accent-deep, which stays bright enough to feel
     like a button. Hierarchy from quietest → loudest:
       primary  (inline emphasis, eyebrows, nav-active) = blue-800
       deep     (CTA button bg)                          = blue-700
       darker   (hover state)                            = blue-900 */
  --accent-primary: #1e40af;      /* blue-800 — authoritative emphasis tone */
  --accent-secondary: #1e3a8a;    /* blue-900 — hover/darker */
  --accent-deep: #1d4ed8;         /* blue-700 — CTA background (still has presence) */
  --accent-darker: #1e3a8a;       /* blue-900 — CTA hover */

  --accent-cyan: #0e7490;         /* cyan-700 — darker for contrast */
  --accent-cyan-soft: #0891b2;    /* cyan-600 */

  --accent-violet: #7c3aed;       /* violet-600 */
  --accent-violet-deep: #6d28d9;  /* violet-700 */

  --accent-gold: #b45309;         /* amber-700 */

  --card-bg: #ffffff;
  --card-border: rgba(15, 23, 42, 0.08);
  --card-border-strong: rgba(37, 99, 235, 0.4);

  --surface-subtle: rgba(15, 23, 42, 0.04);
  --surface-soft: rgba(15, 23, 42, 0.06);
  --surface-medium: rgba(15, 23, 42, 0.08);
  --hairline-soft: rgba(15, 23, 42, 0.08);
  --hairline-medium: rgba(15, 23, 42, 0.12);
}

/* Dark islands — opt out of light-mode overrides (preserves brand visuals
   on hero animation, score viz cards, etc.) */
html.light [data-island="dark"] {
  --bg-page: #081120;
  --bg-section: #0B1426;
  --text-primary: #ffffff;
  --text-secondary: #cbd5e1;
  --text-tertiary: #94a3b8;
  --text-muted: #64748b;
  --accent-primary: #3b82f6;
  --accent-cyan: #22d3ee;
  --accent-cyan-soft: #67e8f9;
  --accent-violet: #a78bfa;
  --accent-violet-deep: #8b5cf6;
  --accent-gold: #fbbf24;
  --card-bg: rgba(15, 23, 42, 0.65);
  --card-border: rgba(255, 255, 255, 0.08);
  --surface-subtle: rgba(255, 255, 255, 0.04);
  --surface-soft: rgba(255, 255, 255, 0.06);
  --surface-medium: rgba(255, 255, 255, 0.10);
  --hairline-soft: rgba(255, 255, 255, 0.08);
  --hairline-medium: rgba(255, 255, 255, 0.14);
  color: #ffffff;
}

/* =============================================================================
 * Hero starfield + shooting stars + atmospheric bloom (dark-mode only)
 * =============================================================================
 * Direct port of the sister project's pattern (validated as the canonical
 * working version):
 *   1. StarField — 140 dot spans with random pos / size / drift / twinkle,
 *      injected by inline JS at the bottom of index.html (vanilla port of
 *      the React component).
 *   2. ShootingStars — fire-once-on-load streak that travels up-left
 *      across the hero (NOT infinite — one elegant pass per page load).
 *   3. HeroGlow — 3 huge blurred radial-gradient blobs ("GridGlow"
 *      equivalent), drifting slowly via CSS animation.
 *   4. mask-hero-effects — bottom-fade mask so decorations don't compete
 *      with the form / content lower in the hero.
 *
 * Light mode: html.light .theme-dark-only { display: none; } hides the
 * whole decoration stack. Dark mode = decorated; light mode = clean.
 *
 * Respects `prefers-reduced-motion: reduce` — animations off, dots stay static.
 * ============================================================================= */

[data-starfield] { position: relative; isolation: isolate; }
[data-starfield] > .hero-inner { position: relative; z-index: 3; }

/* Whole-decoration toggle. Each layer carries .theme-dark-only individually
   so it hides in light mode without needing a positioned wrapper. */
html.light .theme-dark-only { display: none; }

/* StarField host: positioned directly inside .hero (no wrapper).
   Fills the hero; mask fades the bottom so stars don't bleed through the form. */
.starfield-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 0;
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 65%, transparent 100%);
          mask-image: linear-gradient(to bottom, black 0%, black 65%, transparent 100%);
}

/* Individual star — cyan-200 dot. The spawner adds an inline box-shadow
   only on stars > 2px (matches sister project exactly — small dots stay
   subtle, larger ones get a glow halo). */
.star-anim {
  position: absolute;
  border-radius: 50%;
  background: #a5f3fc;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}

@keyframes star-drift {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50%      { transform: translate3d(var(--dx, 30px), var(--dy, -20px), 0); }
}
@keyframes star-drift-twinkle {
  0%, 100% { transform: translate3d(0, 0, 0); opacity: var(--o, 0.5); }
  50%      { transform: translate3d(var(--dx, 30px), var(--dy, -20px), 0); opacity: calc(var(--o, 0.5) * 0.4); }
}

/* Radar-zone star pulse: TWO brief flashes per 6.25s cycle.
   Peak 1 at cycle 0% = ring 1 sweep (the JS sets animation-delay so this
     aligns with ring 1 reaching the star's distance).
   Peak 2 at cycle 75% = ring 2 sweep — ring 2 always lags ring 1 by 1.5625s
     (25% of cycle), so in the star's frame it fires 25% before peak 1
     = at cycle 75%. This offset is constant regardless of star distance. */
/* Fade IN over 4% of cycle (~0.25s). Fade OUT over 20% of cycle (~1.25s) —
   long trail behind the ring as it sweeps past. */
@keyframes radar-pulse {
  0%   { opacity: 1; }   /* peak 1 */
  20%  { opacity: 0; }   /* faded out */
  71%  { opacity: 0; }   /* hold */
  75%  { opacity: 1; }   /* peak 2 (faded in 71→75) */
  95%  { opacity: 0; }   /* faded out */
  96%  { opacity: 0; }   /* hold */
  100% { opacity: 1; }   /* peak 1 of next cycle (faded in 96→100) */
}

/* Atmospheric blurred-radial bloom blobs (3 of them) — positioned directly
   inside .hero (no wrapper). z-index -1 so the blobs sit behind everything. */
.hero-glow-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: -1;
}
.hero-glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(48px);
  opacity: 0.5;
  animation: hero-glow-drift 26s ease-in-out infinite;
}
.hero-glow-1 {
  top: -100px;
  left: 12%;
  width: 480px;
  height: 480px;
  background: radial-gradient(closest-side, rgba(37,99,235,0.45), transparent 65%);
  animation-duration: 28s;
}
.hero-glow-2 {
  top: -40px;
  right: 8%;
  width: 440px;
  height: 440px;
  background: radial-gradient(closest-side, rgba(34,211,238,0.40), transparent 65%);
  animation-duration: 24s;
  animation-direction: reverse;
}
.hero-glow-3 {
  top: 32%;
  left: 50%;
  width: 320px;
  height: 320px;
  transform: translate(-50%, 0);
  background: radial-gradient(closest-side, rgba(99,102,241,0.42), transparent 70%);
  animation-duration: 30s;
  opacity: 0.35;
}
@keyframes hero-glow-drift {
  0%   { transform: translate3d(0, 0, 0); }
  33%  { transform: translate3d(40px, 30px, 0); }
  66%  { transform: translate3d(-30px, 20px, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

html.light .hero-glow-layer { display: none; }

/* Subtle technical grid — 48×48 lattice fading out toward the bottom of
   the hero. Direct port of sister project's .bg-grid + .mask-fade-b. */
.bg-grid {
  background-image:
    linear-gradient(to right, rgba(148, 163, 184, 0.05) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(148, 163, 184, 0.05) 1px, transparent 1px);
  background-size: 48px 48px;
}
.mask-fade-b {
  -webkit-mask-image: linear-gradient(to bottom, black 30%, transparent 100%);
          mask-image: linear-gradient(to bottom, black 30%, transparent 100%);
}

/* Electric sparks travelling ALONG the grid lines (horizontal + vertical
   only — no diagonals). Each spark is a short bright streak with a glowing
   tail; randomized durations + negative delays fire them out of phase. */
.spark-h,
.spark-v {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  z-index: 2;
}
.spark-h {
  height: 2px;
  width: 90px;
  background: linear-gradient(
    to right,
    rgba(165, 243, 252, 0) 0%,
    rgba(165, 243, 252, 0.4) 60%,
    rgba(255, 255, 255, 0.95) 100%
  );
  box-shadow: 0 0 6px rgba(34, 211, 238, 0.75),
              0 0 14px rgba(34, 211, 238, 0.35);
  border-radius: 9999px;
}
.spark-v {
  width: 2px;
  height: 70px;
  background: linear-gradient(
    to bottom,
    rgba(165, 243, 252, 0) 0%,
    rgba(165, 243, 252, 0.4) 60%,
    rgba(255, 255, 255, 0.95) 100%
  );
  box-shadow: 0 0 6px rgba(34, 211, 238, 0.75),
              0 0 14px rgba(34, 211, 238, 0.35);
  border-radius: 9999px;
}

/* Curved (comet-like) spark trajectories: each streak arcs in its
   perpendicular axis as it travels and rotates slightly to suggest motion
   along a curve. Horizontal sparks dip downward (gravity); vertical sparks
   bow sideways. translate3d for GPU compositing. */
/* Spark sweep takes 15% of cycle. Comet stays at FULL opacity from 6%
   to 14.99% — visible the entire way across the screen, then snaps to
   invisible at the same moment it exits past left:110%. No premature
   fade-out before it leaves. */
@keyframes spark-fire-h {
  0%      { left: -10%; transform: translate3d(0, 0, 0)    rotate(0deg);    opacity: 0; }
  3.6%    { transform: translate3d(0, -3px, 0)  rotate(-1deg);   opacity: 0; }
  6.0%    { transform: translate3d(0, -5px, 0)  rotate(-1.5deg); opacity: 1; }
  9.0%    { transform: translate3d(0, 2px, 0)   rotate(1deg);    opacity: 1; }
  12.0%   { transform: translate3d(0, 12px, 0)  rotate(3deg);    opacity: 1; }
  14.99%  { transform: translate3d(0, 22px, 0)  rotate(5deg);    opacity: 1; }
  15.0%   { left: 110%; transform: translate3d(0, 22px, 0) rotate(5deg); opacity: 0; }
  100%    { left: 110%; transform: translate3d(0, 22px, 0) rotate(5deg); opacity: 0; }
}
@keyframes spark-fire-v {
  0%   { top: -10%; transform: translate3d(0, 0, 0)    rotate(0deg);   opacity: 0; }
  8%   { transform: translate3d(-6px, 0, 0)  rotate(2deg);  opacity: 0; }
  14%  { transform: translate3d(-10px, 0, 0) rotate(3deg);  opacity: 1; }
  20%  { transform: translate3d(4px, 0, 0)   rotate(-2deg); opacity: 1; }
  26%  { transform: translate3d(22px, 0, 0)  rotate(-6deg); opacity: 1; }
  32%  { top: 110%; transform: translate3d(42px, 0, 0)  rotate(-10deg); opacity: 0; }
  100% { top: 110%; transform: translate3d(42px, 0, 0)  rotate(-10deg); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .star-anim,
  .hero-glow,
  .spark-h,
  .spark-v { animation: none !important; }
  .spark-h, .spark-v { display: none; }
}

/* =============================================================================
 * Nav brand mark — theme-aware logo swap
 * =============================================================================
 * The nav uses /assets/logo-mark.svg (transparent bg, blue-violet luminous
 * mark) in dark mode, and /assets/logo-mark-light.svg (darker, less
 * saturated, no glow) in light mode. Both have identical geometry — only
 * colours differ so brand recognition between modes stays intact.
 *
 * Note: /assets/favicon.svg (simplified) is still the <link rel="icon">
 * target, and /assets/logo.svg (rounded-square rich mark) is still used
 * for apple-touch-icon and marketing share PNGs. This rule only governs
 * the inline nav header mark.
 * ============================================================================= */
.brand-mark {
  display: inline-block;
  width: 43px;
  height: 43px;
  vertical-align: middle;
  margin-right: 11px;
  background: url("/assets/logo-mark.svg?v=5") center/contain no-repeat;
  flex-shrink: 0;
}
html.light .brand-mark { background-image: url("/assets/logo-mark-light.svg?v=5"); }

/* =============================================================================
 * Canonical site nav — single source of truth for nav layout + styling
 * =============================================================================
 * Pages should use the <header data-site-nav></header> placeholder and load
 * /assets/site-nav.js to inject the canonical nav HTML. These styles are
 * what the injected nav binds against.
 *
 * Previously each page defined its own .nav* CSS in an inline <style> block,
 * which led to subtle drift between pages (different max-width, different
 * .brand alignment, different padding). Now they all inherit from here.
 * ============================================================================= */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg-nav);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--nav-border);
}
.nav-inner {
  max-width: var(--nav-max-width);
  margin: 0 auto;
  padding: 14px 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.brand {
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -0.5px;
  text-decoration: none;
  color: var(--text-primary);
}
.brand .pact { color: var(--accent-primary); }
.nav-links {
  display: flex;
  gap: 24px;
  font-size: 14px;
  color: var(--text-secondary);
}
.nav-links a {
  color: inherit;
  text-decoration: none;
  transition: color 0.15s;
}
.nav-links a:hover { color: var(--text-primary); }
.nav-links a.current { color: var(--accent-primary); }
@media (max-width: 768px) {
  .nav-inner { padding: 12px 16px; gap: 8px; }
  .nav-links { display: none; }
  .brand { font-size: 16px; }
}

/* =============================================================================
 * Theme toggle button (placed in nav)
 * =============================================================================
 * Standard placement: right side of nav, before any CTA button.
 * <button type="button" class="theme-toggle" id="themeToggle"
 *         aria-label="Toggle light / dark theme" title="Toggle light / dark">
 *   <svg class="icon-moon">...</svg><svg class="icon-sun">...</svg>
 * </button>
 */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 6px;
  background: var(--card-bg);
  border: 1px solid var(--nav-border);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  padding: 0;
}
.theme-toggle:hover {
  background: var(--card-border);
  color: var(--text-primary);
  border-color: var(--card-border-strong);
}
.theme-toggle svg { width: 16px; height: 16px; }
.theme-toggle .icon-sun { display: none; }
.theme-toggle .icon-moon { display: block; }
html.light .theme-toggle .icon-sun { display: block; }
html.light .theme-toggle .icon-moon { display: none; }
