/* loghq design tokens — the single source for the app surface.
 *
 * Linked once from config/stx.ts `app.head.link`, so every page gets it without
 * repeating it. Ten app views used to declare their own byte-similar copy of
 * this block: 90 custom-property declarations in four subtly diverging
 * variants, which is how the dashboard's `--mono` stack ended up one entry
 * longer than everyone else's.
 *
 * It is NOT in config/crosswind.ts `preflights`, which is where the styling
 * guide says design tokens belong. Verified against the installed build:
 * `preflights` is spread into the generator config and then never emitted —
 * toCSS() writes the built-in reset only. A probe token placed there appears
 * nowhere in the served CSS on any route.
 *
 * These names are the contract config/crosswind.ts maps its semantic utilities
 * onto — `bg-canvas`, `bg-panel`, `border-line`, `text-ink`, `text-muted`,
 * `text-subtle`, `text-accent`. Renaming one here silently breaks the matching
 * utility everywhere, because an undefined var() resolves to the property's
 * initial value rather than erroring.
 *
 * Marketing pages additionally load /marketing.css, which arrives after this
 * file and defines a richer set of its own (--surface, --accent-line, --r, …).
 * --accent-soft moved the other way, out of marketing.css and into here, the
 * day the compare tables needed a `bg-accent-soft` utility: a utility backed
 * by a token that only marketing pages declare is dead everywhere else and
 * fails silently. marketing.css still consumes it (.live-dot, .cell.tint,
 * .cell.feature) and resolves it from this file.
 * Where the two overlap the values agree; where they differ, marketing.css
 * wins on marketing pages, which is the intent.
 *
 * Both the media query and the [data-theme] attribute are covered, and that
 * pairing is deliberate: the attribute is what the pre-paint theme script and
 * the theme store write, and it has to beat the OS preference so an explicit
 * choice survives.
 */

:root {
  /* The one declaration that reaches the parts of the page we do not draw.
   *
   * Native UI — the select popup, scrollbars, the date and colour pickers, form
   * control backgrounds, the spellcheck underline, the caret — is painted by the
   * browser from `color-scheme`, not from our custom properties. Undeclared it
   * defaults to `light`, so on a dark loghq the project dropdown opened as a
   * white popup on a #090b11 page and the scrollbars stayed light. No amount of
   * token work reaches those pixels; this is the only thing that does.
   *
   * It is declared on all four blocks below and not once on `:root` with both
   * values, because `color-scheme: light dark` means "either is fine, follow the
   * OS" — which is wrong the moment someone picks a theme explicitly. The
   * attribute blocks have to be able to override the media query, exactly like
   * every colour here. */
  color-scheme: light;
  --bg: #fbfbfd;
  --panel: #ffffff;
  --border: rgba(15, 23, 42, 0.09);
  --text: #0b0f19;
  --text-2: #4b5565;
  /* --text-3 was #97a1b2, which measured 2.52:1 against --bg and 2.61:1
   * against --panel. WCAG AA wants 4.5:1 for body text, so every "subtle"
   * label in the app failed it — 73 elements in light and 82 in dark, measured
   * in a real browser across seven pages. It is the same token in both themes
   * and it failed in both (dark was #667085, 3.73:1 on --panel).
   *
   * #6b7280 gives 4.83:1 on --panel and 4.68:1 on --bg; the dark #79839a gives
   * 4.88:1 and 5.18:1. Both are the smallest step that clears AA on the worse
   * of the two surfaces, so "subtle" still reads as subtle. */
  --text-3: #6b7280;
  /* Emerald, not the rose this shipped with.
   *
   * The old --accent was #e11d48, which is byte-for-byte --sev-error below. In
   * a product whose whole surface is log severity, the brand colour WAS the
   * error colour: every button, link and focus ring carried the one hue the
   * severity scale reserves for something being wrong.
   *
   * Green collides with --ok instead, which is the milder trade. --ok is a UI
   * success state, not a severity: nothing in the sev-* scale is green, so the
   * severity reading stays unambiguous. Brand-as-healthy misleads nobody;
   * brand-as-error did.
   *
   * #047857 rather than the more obvious #059669: emerald-600 measures 3.65:1
   * on --bg, under the 4.5 floor this file holds everything else to. This one
   * measures 5.31:1 on --bg and 5.48:1 on --panel, which is also an improvement
   * on the rose it replaces (4.54:1, passing by 0.04). */
  --accent: #047857;
  --accent-soft: rgba(4, 120, 87, 0.08);
  /* Ink for text sitting ON a saturated fill — the accent, --warn, and the
   * Slack/Discord brand colours.
   *
   * It cannot be a hardcoded #fff, which is what the app markup and
   * app-chrome.css assumed. In dark mode the fills get LIGHTER, not darker:
   * --accent is #34d399 and the dashboard's indigo is #818cf8, where white
   * measures 1.92:1 and 2.98:1. The dark value here measures 9.83:1 and 6.34:1
   * on those same fills, and 6.83:1 on the dark --warn.
   *
   * marketing.css has had this token, correctly themed, since the marketing
   * pages were built; it just never reached the app surface, because that
   * sheet only loads on marketing routes. Declared here it covers both, and
   * marketing.css no longer declares its own copy — see the .reveal note there
   * for what two copies of one token cost. */
  --accent-ink: #ffffff;
  --ok: #16a34a;
  --warn: #b91c1c;
  --sans: 'Space Grotesk', ui-sans-serif, system-ui, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Log severity. These were six hex literals inside a levelColor() switch in
   * dashboard.stx, with two of them repeated in the stats array — so the same
   * "warning amber" was written twice and could drift.
   *
   * They were also a single set used on BOTH themes. The dark values below are
   * exactly what shipped before, so dark is unchanged; light gets darker,
   * higher-contrast variants, because #fb7185 on a #fbfbfd background is not a
   * readable label. */
  --sev-critical: #be123c;
  --sev-error: #e11d48;
  --sev-warning: #b45309;
  --sev-notice: #0369a1;
  --sev-info: #1d4ed8;
  --sev-debug: #64748b;
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --bg: #090b11;
    --panel: #10131c;
    --border: rgba(148, 163, 184, 0.12);
    --text: #f3f5f9;
    --text-2: #b3bccb;
    --text-3: #79839a;
    --accent: #34d399;
    --accent-soft: rgba(52, 211, 153, 0.1);
    --accent-ink: #04140d;
    --ok: #4ade80;
    --warn: #f87171;
  --sev-critical: #f43f5e;
  --sev-error: #fb7185;
  --sev-warning: #f59e0b;
  --sev-notice: #38bdf8;
  --sev-info: #60a5fa;
  --sev-debug: #94a3b8;
  }
}

:root[data-theme='dark'] {
  color-scheme: dark;
  --bg: #090b11;
  --panel: #10131c;
  --border: rgba(148, 163, 184, 0.12);
  --text: #f3f5f9;
  --text-2: #b3bccb;
  --text-3: #79839a;
  --accent: #34d399;
  --accent-soft: rgba(52, 211, 153, 0.1);
  --accent-ink: #04140d;
  --ok: #4ade80;
  --warn: #f87171;
  --sev-critical: #f43f5e;
  --sev-error: #fb7185;
  --sev-warning: #f59e0b;
  --sev-notice: #38bdf8;
  --sev-info: #60a5fa;
  --sev-debug: #94a3b8;
}

:root[data-theme='light'] {
  color-scheme: light;
  --bg: #fbfbfd;
  --panel: #ffffff;
  --border: rgba(15, 23, 42, 0.09);
  --text: #0b0f19;
  --text-2: #4b5565;
  --text-3: #6b7280;
  --accent: #047857;
  --accent-soft: rgba(4, 120, 87, 0.08);
  --accent-ink: #ffffff;
  --ok: #16a34a;
  --warn: #b91c1c;
  --sev-critical: #be123c;
  --sev-error: #e11d48;
  --sev-warning: #b45309;
  --sev-notice: #0369a1;
  --sev-info: #1d4ed8;
  --sev-debug: #64748b;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* Skeleton shimmer, from account.stx's <style> block. Utilities cannot declare
 * a @keyframes, and config/crosswind.ts `preflights` — the documented home for
 * one — is never emitted on this serve path, so it lives here.
 *
 * Named loghq-pulse rather than pulse on purpose. Crosswind emits its own
 * `@keyframes pulse` (0%,100% opacity 1; 50% opacity .5) as soon as any page
 * uses `animate-pulse`, into a sheet that is linked AFTER this file. A
 * same-named keyframe there would silently replace this one and change the
 * animation rather than error.
 */
@keyframes loghq-pulse {
  0%,
  100% {
    opacity: 0.5;
  }
  50% {
    opacity: 0.9;
  }
}

/* Theme-toggle icon swap. Three identical copies of this lived in
 * dashboard.stx, settings.stx and projects/index.stx.
 *
 * It stays CSS because no utility can express it: the selector is an
 * attribute-state descendant, `[data-t="light"] .ic-sun`, driven by the
 * `theme()` signal through `:data-t` on the button. The alternative — a
 * `:show` binding on each icon — would need `style="display:none"` on both to
 * avoid showing both icons before hydration, trading a state rule for two
 * inline styles and a hydration dependency on something that is currently
 * pure CSS.
 */
.theme-btn .ic-sun,
.theme-btn .ic-moon {
  display: none;
}

/* Keyed off the ROOT element, not off the button.
 *
 * These used to read `.theme-btn[data-t='light']`, where data-t came from a
 * `:data-t="theme()"` binding — a client signal. So before hydration the button
 * carried no data-t at all, neither rule matched, and the pair above left BOTH
 * icons display:none: the theme control rendered as an empty box on every cold
 * load. There is no server-known theme to put in a static counterpart either,
 * so the fix is not a static attribute; it is keying off something that IS set
 * before first paint.
 *
 * stx's color-mode boot script sets data-theme on the root above every
 * stylesheet (config/stx.ts app.colorMode), and useColorMode keeps it current
 * afterwards, so the correct icon paints immediately and the mirror signal the
 * binding needed is gone from all three pages. */
:root[data-theme='light'] .theme-btn .ic-sun {
  display: block;
}

:root[data-theme='dark'] .theme-btn .ic-moon {
  display: block;
}
