/* =============================================================
   Arctop Design System -- core tokens
   colors_and_type.css

   Dark-first system tuned for BCI / cognition interfaces.
   Warm-neutral charcoals (not pure black) for cognitive calm.
   Two signal colors -- synapse violet + signal mint -- used
   sparingly to mark live neural activity.

   Synced 2026-08-04 from the claude.ai/design "Arctop" design
   system project (dist/arctop-tokens.css); the canonical bundle
   lives at the hegemon monorepo root under design/. Tokens marked
   "backend extension" are ours, not the bundle's -- keep them when
   re-syncing. The font src paths target the backend's /fonts/
   static route; if the PP Mori TTFs are absent the --font-sans
   fallback chain (system fonts) renders gracefully.
   ============================================================= */

@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&display=swap');

@font-face {
  font-family: 'PP Mori';
  src: url('/fonts/PPMori-Regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'PP Mori';
  src: url('/fonts/PPMori-SemiBold.ttf') format('truetype');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Dark is the default theme; /js/theme.js resolves the user's choice (or the
     OS setting) and stamps data-theme="light" on <html> to swap the block of
     overrides further down. Because that script always resolves to a concrete
     value there is deliberately no prefers-color-scheme block here. */
  color-scheme: dark;

  /* ===== Color -- neutrals (warm charcoals) ===== */
  --color-bg:           #0E0F12;   /* primary canvas */
  --color-surface-1:    #16181D;   /* raised panel */
  --color-surface-2:    #1D2026;   /* card */
  --color-surface-3:    #262A33;   /* input / hover */
  --color-surface-4:    #2F343E;   /* pressed / selected */
  --color-hairline:     rgba(255, 255, 255, 0.08);
  --color-hairline-strong: rgba(255, 255, 255, 0.16);

  /* ===== Color -- foreground ===== */
  --color-fg-1:         #F2F1ED;   /* primary text -- warm off-white */
  --color-fg-2:         #C5C6CC;   /* secondary text */
  --color-fg-3:         #8A8D96;   /* tertiary / labels */
  --color-fg-4:         #5F636D;   /* disabled / placeholder */
  --color-fg-inverse:   #0E0F12;   /* text on light surfaces */

  /* ===== Color -- on-accent =====
     Text / icons sitting on a SOLID accent or state fill. Not fg-1
     or fg-inverse: those flip with the theme, this one does not.
     (Signal fills are the exception -- pale mint wants dark type in
     dark mode, so text on a signal fill stays fg-inverse.) */
  --color-on-accent:    #F2F1ED;

  /* ===== Color -- signal (brand accents) ===== */
  --color-synapse:      #7B61FF;   /* primary -- violet ("synapse") */
  --color-synapse-hi:   #9E8AFF;
  --color-synapse-lo:   #5A3FE0;
  --color-synapse-wash: rgba(123, 97, 255, 0.14);
  --color-synapse-hover:   #9E8AFF;                  /* solid-fill hover */
  --color-synapse-line:    rgba(123, 97, 255, 0.35); /* 1px ring on wash */
  --color-synapse-on-wash: #9E8AFF;                  /* text on a wash */

  --color-signal:       #5BE0C9;   /* secondary -- calm mint */
  --color-signal-hi:    #8FECDB;
  --color-signal-lo:    #2EB89E;
  --color-signal-wash:  rgba(91, 224, 201, 0.12);
  /* Backend extension: the bundle ships hover tokens only for synapse
     and error, but our primary buttons fill with signal. Built by the
     bundle's own rule -- hover lightens (-hi) in dark, darkens (-lo)
     in light. */
  --color-signal-hover: #8FECDB;

  /* ===== Color -- operator state ===== */
  --state-idle:        #5F636D;
  --state-connecting:  #7B61FF;
  --state-streaming:   #3DDC97;
  --state-warning:     #FFB547;
  --state-error:       #FF6B6B;
  --state-unknown:     #8A8D96;

  --state-idle-wash:       rgba(95, 99, 109, 0.16);
  --state-connecting-wash: rgba(123, 97, 255, 0.16);
  --state-streaming-wash:  rgba(61, 220, 151, 0.14);
  --state-warning-wash:    rgba(255, 181, 71, 0.14);
  --state-error-wash:      rgba(255, 107, 107, 0.14);

  /* 1px rings for state chips / outline buttons on those washes */
  --state-idle-line:       rgba(95, 99, 109, 0.32);
  --state-connecting-line: rgba(123, 97, 255, 0.32);
  --state-streaming-line:  rgba(61, 220, 151, 0.32);
  --state-warning-line:    rgba(255, 181, 71, 0.30);
  --state-error-line:      rgba(255, 107, 107, 0.32);
  --state-error-hover:     #FF8585;   /* solid destructive hover */
  /* Backend extension: hover fill over the error wash. */
  --state-error-wash-hi:   rgba(255, 107, 107, 0.22);

  /* ===== Color -- semantic (cognitive states) ===== */
  --color-focus:        #5BE0C9;
  --color-calm:         #7B61FF;
  --color-engage:       #FFB547;   /* engagement / arousal */
  --color-alert:        #FF8A65;
  --color-warning:      #FFB547;
  --color-danger:       #FF6B6B;
  --color-success:      #5BE0C9;
  --color-info:         #7B61FF;

  /* ===== Color -- data viz (EEG band palette) ===== */
  --color-delta:        #5B7BFF;
  --color-theta:        #7B61FF;
  --color-alpha:        #5BE0C9;
  --color-beta:         #FFB547;
  --color-gamma:        #FF6B9D;

  /* ===== Typography -- families ===== */
  --font-sans: 'PP Mori', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'IBM Plex Mono', 'SF Mono', Menlo, Consolas, monospace;
  --font-display: 'PP Mori', -apple-system, system-ui, sans-serif;

  /* ===== Typography -- scale (1.20 minor third) ===== */
  --fs-12: 0.75rem;
  --fs-13: 0.8125rem;
  --fs-14: 0.875rem;
  --fs-15: 0.9375rem;
  --fs-16: 1rem;
  --fs-18: 1.125rem;
  --fs-20: 1.25rem;
  --fs-24: 1.5rem;
  --fs-32: 2rem;
  --fs-40: 2.5rem;
  --fs-56: 3.5rem;
  --fs-72: 4.5rem;

  /* ===== Typography -- weights ===== */
  --fw-light:    400;
  --fw-regular:  400;
  --fw-medium:   600;
  --fw-semibold: 600;
  --fw-bold:     600;

  /* ===== Typography -- leading ===== */
  --lh-tight:   1.08;
  --lh-snug:    1.25;
  --lh-normal:  1.45;
  --lh-relaxed: 1.6;

  /* ===== Typography -- tracking ===== */
  --tr-tightest: -0.04em;
  --tr-tight:    -0.02em;
  --tr-normal:   0;
  --tr-wide:     0.02em;
  --tr-eyebrow:  0.14em;

  /* ===== Spacing -- 4px base ===== */
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  20px;
  --space-6:  24px;
  --space-8:  32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;
  --space-24: 96px;

  /* ===== Radii ===== */
  --radius-xs:   3px;
  --radius-sm:   6px;
  --radius-md:   10px;
  --radius-lg:   14px;
  --radius-xl:   20px;
  --radius-2xl:  28px;
  --radius-pill: 999px;

  /* ===== Elevation / shadow ===== */
  --shadow-sm:  0 1px 0 0 rgba(255, 255, 255, 0.04) inset,
                0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-md:  0 1px 0 0 rgba(255, 255, 255, 0.05) inset,
                0 4px 16px rgba(0, 0, 0, 0.35);
  --shadow-lg:  0 1px 0 0 rgba(255, 255, 255, 0.06) inset,
                0 10px 30px rgba(0, 0, 0, 0.45),
                0 2px 8px rgba(0, 0, 0, 0.3);
  --shadow-glow-synapse: 0 0 0 1px rgba(123, 97, 255, 0.35),
                         0 0 24px rgba(123, 97, 255, 0.35);
  --shadow-glow-signal:  0 0 0 1px rgba(91, 224, 201, 0.35),
                         0 0 24px rgba(91, 224, 201, 0.25);

  /* ===== Motion ===== */
  --ease-out:   cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in:    cubic-bezier(0.55, 0, 0.68, 0.18);
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  --dur-fast:   120ms;
  --dur-base:   200ms;
  --dur-slow:   320ms;
  --dur-pulse:  1800ms;

  /* ===== Chrome -- the bits a theme flip would otherwise strand =====
     Every one of these was a hardcoded rgba(255,255,255,...) or rgba(0,0,0,...)
     somewhere in a page or component sheet. They are tokens so the light block
     below can answer them, and so a new rule has an obvious thing to reach for
     instead of hardcoding white again. */
  --color-scrollbar-thumb:       rgba(255, 255, 255, 0.12);
  --color-scrollbar-thumb-hover: rgba(255, 255, 255, 0.22);
  --color-scrim:        rgba(14, 15, 18, 0.72); /* modal backdrop (bundle token) */
  --color-scrim-soft:   rgba(14, 15, 18, 0.64); /* side-sheet backdrop (backend extension) */
  --color-strip-dim:    rgba(5, 6, 8, 0.72);   /* trimmed-away region of a filmstrip */
  --color-grid-line:    rgba(255, 255, 255, 0.05); /* chart gridlines */
  --color-grid-line-strong: rgba(255, 255, 255, 0.28); /* segment boundaries */
  --color-crosshair:    rgba(242, 241, 237, 0.32); /* hover readout line */
  --color-playhead:     #FFFFFF;
  --color-playhead-glow: rgba(242, 241, 237, 0.35);
  --color-fill-faint:   rgba(255, 255, 255, 0.04); /* sparkline area under a neutral line */
  --date-picker-filter: invert(0.55);          /* native calendar glyph */

  /* Activity heatmap ramp (empty -> busiest day). */
  --heat-0: rgba(255, 255, 255, 0.05);
  --heat-1: rgba(91, 224, 201, 0.22);
  --heat-2: rgba(91, 224, 201, 0.42);
  --heat-3: rgba(91, 224, 201, 0.68);
  --heat-4: rgba(91, 224, 201, 1);
  --heat-empty: rgba(255, 255, 255, 0.02);
  --heat-outline: rgba(255, 255, 255, 0.30);
}

/* =============================================================
   Light theme -- token overrides only

   Same variables, same semantics, so no component rule changes:
     surface-1/2  the CONTAINER layers (nav, panel, card) get lighter
                as they rise, ending at white -- the mirror of rising
                against a dark canvas. The member dashboard cards its
                tiles on surface-2 while the admin console cards on
                surface-1, so both read as lifted off the canvas.
     surface-3/4  the CONTROL layers (input, chip, button, hover,
                pressed) DARKEN instead: a filled control on light
                paper recedes rather than glows.
     *-hi / *-lo keep their LITERAL luminance meaning in both themes.
                Legibility and hover live in their own tokens:
                *-on-wash for text on a wash, *-hover for solid-fill
                hover (lightens in dark, darkens in light).
     fg-inverse "text on dark surfaces" here -- near-white, e.g. type
                on a solid signal fill (deep teal in this theme).
     on-accent  stays light in BOTH themes -- a solid violet or red
                fill wants white type whichever way the canvas goes.
   ============================================================= */

:root[data-theme="light"],
[data-theme="light"]:not(:root) {
  color-scheme: light;

  /* ===== Color -- neutrals (warm paper greys) ===== */
  --color-bg:           #F1EFE9;   /* primary canvas -- warm paper */
  --color-surface-1:    #F8F6F1;   /* raised panel -- header bar */
  --color-surface-2:    #FFFFFF;   /* card -- the only pure white */
  --color-surface-3:    #EBE8E1;   /* input / hover -- steps DOWN */
  --color-surface-4:    #DFDBD1;   /* pressed / selected */
  --color-hairline:        rgba(14, 15, 18, 0.10);
  --color-hairline-strong: rgba(14, 15, 18, 0.20);

  /* ===== Color -- foreground ===== */
  --color-fg-1:         #1A1B1F;   /* primary text -- warm charcoal */
  --color-fg-2:         #4A4D55;
  --color-fg-3:         #666A73;
  --color-fg-4:         #94989F;
  --color-fg-inverse:   #FBFAF7;   /* text on dark surfaces */

  --color-on-accent:    #FFFFFF;

  /* ===== Color -- signal (brand accents) =====
     Ramp shifts one stop darker; hover DARKENS here. */
  --color-synapse:      #5A3FE0;
  --color-synapse-hi:   #7B61FF;
  --color-synapse-lo:   #4227B8;
  --color-synapse-wash: rgba(90, 63, 224, 0.10);
  --color-synapse-hover:   #4227B8;
  --color-synapse-line:    rgba(90, 63, 224, 0.32);
  --color-synapse-on-wash: #4227B8;

  --color-signal:       #0B7A67;   /* deep mint */
  --color-signal-hi:    #2EB89E;
  --color-signal-lo:    #075F50;
  --color-signal-wash:  rgba(11, 122, 103, 0.10);
  --color-signal-hover: #075F50;   /* backend extension */

  /* ===== Color -- operator state ===== */
  --state-idle:        #6E727B;
  --state-connecting:  #5A3FE0;
  --state-streaming:   #0A8354;
  --state-warning:     #A85F00;
  --state-error:       #C0342F;
  --state-unknown:     #9A9EA7;

  --state-idle-wash:       rgba(110, 114, 123, 0.10);
  --state-connecting-wash: rgba(90, 63, 224, 0.10);
  --state-streaming-wash:  rgba(10, 131, 84, 0.10);
  --state-warning-wash:    rgba(168, 95, 0, 0.10);
  --state-error-wash:      rgba(192, 52, 47, 0.09);

  --state-idle-line:       rgba(110, 114, 123, 0.30);
  --state-connecting-line: rgba(90, 63, 224, 0.30);
  --state-streaming-line:  rgba(10, 131, 84, 0.28);
  --state-warning-line:    rgba(168, 95, 0, 0.28);
  --state-error-line:      rgba(192, 52, 47, 0.28);
  --state-error-hover:     #A32925;
  --state-error-wash-hi:   rgba(192, 52, 47, 0.18);   /* backend extension */

  /* ===== Color -- semantic (cognitive states) ===== */
  --color-focus:        #0B7A67;
  --color-calm:         #5A3FE0;
  --color-engage:       #A85F00;
  --color-alert:        #C0562B;
  --color-warning:      #A85F00;
  --color-danger:       #C0342F;
  --color-success:      #0B7A67;
  --color-info:         #5A3FE0;

  /* ===== Color -- data viz (EEG band palette) ===== */
  --color-delta:        #3355DB;
  --color-theta:        #5A3FE0;
  --color-alpha:        #0B7A67;
  --color-beta:         #A85F00;
  --color-gamma:        #C22B62;

  /* ===== Elevation / shadow =====
     No luminance headroom above white, so cards lean on the
     hairline; shadows stay soft and low-alpha. */
  --shadow-sm:  0 1px 2px rgba(18, 19, 23, 0.07);
  --shadow-md:  0 1px 2px rgba(18, 19, 23, 0.05),
                0 4px 14px rgba(18, 19, 23, 0.08);
  --shadow-lg:  0 2px 6px rgba(18, 19, 23, 0.06),
                0 10px 30px rgba(18, 19, 23, 0.12);
  --shadow-glow-synapse: 0 0 0 1px rgba(90, 63, 224, 0.35),
                         0 0 20px rgba(90, 63, 224, 0.18);
  --shadow-glow-signal:  0 0 0 1px rgba(11, 122, 103, 0.30),
                         0 0 20px rgba(11, 122, 103, 0.14);

  /* ===== Chrome (backend extensions; scrim is a bundle token) ===== */
  --color-scrollbar-thumb:       rgba(14, 15, 18, 0.18);
  --color-scrollbar-thumb-hover: rgba(14, 15, 18, 0.32);
  --color-scrim:        rgba(28, 28, 32, 0.40);
  --color-scrim-soft:   rgba(28, 28, 32, 0.34);
  --color-strip-dim:    rgba(248, 246, 241, 0.72);
  --color-grid-line:    rgba(14, 15, 18, 0.08);
  --color-grid-line-strong: rgba(14, 15, 18, 0.26);
  --color-crosshair:    rgba(26, 27, 31, 0.34);
  --color-playhead:     #1A1B1F;
  --color-playhead-glow: rgba(26, 27, 31, 0.25);
  --color-fill-faint:   rgba(14, 15, 18, 0.06);
  --date-picker-filter: none;

  --heat-0: rgba(14, 15, 18, 0.07);
  --heat-1: rgba(11, 122, 103, 0.22);
  --heat-2: rgba(11, 122, 103, 0.45);
  --heat-3: rgba(11, 122, 103, 0.72);
  --heat-4: rgba(11, 122, 103, 1);
  --heat-empty: rgba(14, 15, 18, 0.03);
  --heat-outline: rgba(14, 15, 18, 0.35);
}

/* A scoped theme island paints itself -- html/body read the tokens
   through the pages' own base styles, nested [data-theme] subtrees
   read them here. */
[data-theme]:not(:root):not(body) {
  background: var(--color-bg);
  color: var(--color-fg-1);
}

/* The wordmark artwork is white-on-transparent, so it needs flipping on a
   light canvas. It is flat white, so a straight invert lands on flat black. */
:root[data-theme="light"] .wordmark { filter: invert(1); }

/* =============================================================
   Semantic typography classes
   ============================================================= */

.t-display {
  font-family: var(--font-display);
  font-size: var(--fs-72);
  font-weight: 400;
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-tightest);
  color: var(--color-fg-1);
}

.t-h1 {
  font-family: var(--font-display);
  font-size: var(--fs-56);
  font-weight: 400;
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-tightest);
  color: var(--color-fg-1);
}

.t-h2 {
  font-family: var(--font-display);
  font-size: var(--fs-40);
  font-weight: 400;
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-tight);
  color: var(--color-fg-1);
}

.t-h3 {
  font-family: var(--font-sans);
  font-size: var(--fs-32);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-tight);
  color: var(--color-fg-1);
}

.t-h4 {
  font-family: var(--font-sans);
  font-size: var(--fs-24);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  color: var(--color-fg-1);
}

.t-h5 {
  font-family: var(--font-sans);
  font-size: var(--fs-20);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  color: var(--color-fg-1);
}

.t-body-lg {
  font-family: var(--font-sans);
  font-size: var(--fs-18);
  font-weight: var(--fw-regular);
  line-height: var(--lh-relaxed);
  color: var(--color-fg-2);
}

.t-body {
  font-family: var(--font-sans);
  font-size: var(--fs-16);
  font-weight: var(--fw-regular);
  line-height: var(--lh-normal);
  color: var(--color-fg-2);
}

.t-ui {
  font-family: var(--font-sans);
  font-size: var(--fs-15);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  color: var(--color-fg-1);
}

.t-caption {
  font-family: var(--font-sans);
  font-size: var(--fs-13);
  font-weight: var(--fw-regular);
  line-height: var(--lh-normal);
  color: var(--color-fg-3);
}

.t-eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-12);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--color-fg-3);
}

.t-mono {
  font-family: var(--font-mono);
  font-size: var(--fs-13);
  font-weight: var(--fw-regular);
  color: var(--color-fg-2);
  font-feature-settings: 'tnum' on;
}

.t-mono-data {
  font-family: var(--font-mono);
  font-size: var(--fs-14);
  font-weight: var(--fw-medium);
  color: var(--color-signal);
  font-feature-settings: 'tnum' on;
  letter-spacing: var(--tr-tight);
}

.t-metric {
  font-family: var(--font-display);
  font-size: var(--fs-72);
  font-weight: 400;
  line-height: 1;
  letter-spacing: var(--tr-tightest);
  font-feature-settings: 'tnum' on;
  color: var(--color-fg-1);
}

/* =============================================================
   Base resets / page defaults
   ============================================================= */

::selection {
  background: var(--color-synapse-wash);
  color: var(--color-fg-1);
}

/* =============================================================
   Theme toggle -- the Light / Dark segmented control

   Markup is generated by /js/theme.js into any [data-theme-toggle]
   element, so a page opts in with one empty tag and no page script.
   ============================================================= */

.theme-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: none;
  padding: 2px;
  border-radius: var(--radius-pill);
  background: var(--color-surface-2);
  border: 1px solid var(--color-hairline);
  line-height: 0;
}

/* The moving pill behind the selected segment. Sized to one segment and
   translated by whole segments, so the three stops line up exactly. */
.theme-toggle::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 26px;
  height: 22px;
  border-radius: var(--radius-pill);
  background: var(--color-surface-4);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-base) var(--ease-spring);
}
.theme-toggle[data-mode="light"]::before { transform: translateX(0); }
.theme-toggle[data-mode="dark"]::before  { transform: translateX(26px); }

.theme-toggle button {
  position: relative;
  z-index: 1;
  width: 26px;
  height: 22px;
  padding: 0;
  border: none;
  background: none;
  border-radius: var(--radius-pill);
  display: grid;
  place-items: center;
  color: var(--color-fg-4);
  cursor: pointer;
  transition: color var(--dur-fast);
}
.theme-toggle button:hover { color: var(--color-fg-2); }
.theme-toggle button[aria-checked="true"] { color: var(--color-fg-1); }
.theme-toggle button svg { display: block; }
/* Same focus language the console uses everywhere else. */
.theme-toggle button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 1px var(--color-synapse), 0 0 0 4px var(--color-synapse-wash);
}

/* Cross-fade the palette on a theme flip. /js/theme.js adds this for one
   beat and removes it, so nothing outside the switch animates its colors. */
.theme-anim, .theme-anim *, .theme-anim *::before, .theme-anim *::after {
  transition: background-color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out) !important;
}
/* ...except the switch's own thumb, which is the thing being watched during
   the flip. The rule above would otherwise drop its transform transition for
   exactly the beat it needs to slide. */
.theme-anim .theme-toggle::before {
  transition: transform var(--dur-base) var(--ease-spring),
              background-color var(--dur-base) var(--ease-out) !important;
}

@media (prefers-reduced-motion: reduce) {
  .theme-toggle::before { transition: none; }
  .theme-anim, .theme-anim *, .theme-anim *::before, .theme-anim *::after { transition: none !important; }
}

/* =============================================================
   Keyframes -- shared signal animations
   ============================================================= */

@keyframes arctop-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}

@keyframes arctop-ring {
  0%   { transform: scale(0.95); opacity: 0.5; }
  100% { transform: scale(1.6);  opacity: 0; }
}

@keyframes arctop-breathe {
  0%, 100% { transform: scale(1);    opacity: 0.85; }
  50%      { transform: scale(1.25); opacity: 1; }
}

@keyframes arctop-spin {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
