/* PsiEVO — design tokens.
 *
 * Invariant: no text token below 4.5:1 (WCAG AA) on ANY of the three pairable
 * backgrounds (--canvas, --surface, --sunken). The numbers in the comments are measured,
 * not estimated — `node tools/contrast.mjs` proves the whole matrix and fails if anyone
 * changes a colour without redoing the maths.
 *
 * Second rule: nothing is communicated by colour alone. Every state is colour + icon +
 * text.
 *
 * ── Provenance of this palette ──────────────────────────────────────────────────
 * Adopted from PsiEVO, an earlier single-file incarnation of this project, with one
 * correction: its --ink-3 (#6C7A89) measured 3.84:1 on the canvas and failed the floor.
 * Darkened 10% along the same hue to #616E7B — 4.56 / 5.22 / 4.90, visually identical.
 * Everything else passed as-is, including white-on-strong and chip-on-soft.
 *
 * The accent is aubergine, not wine. The wine (--danger) is strictly semantic: it means
 * destruction or a blocking exclusion, never brand.
 */

:root {
  color-scheme: light;
  /* ── Backgrounds ────────────────────────────────────────── */
  --canvas: #edf0f4;          /* app background: a cool blue-grey, so white cards read
                                 as paper on a desk rather than a flat white page */
  --surface: #ffffff;
  --sunken: #f6f8fa;          /* zebra rows, table headers, disabled fields */

  /* ── Text ───────────────────────────────────────────────── */
  --text-1: #131a24;          /* 15.3:1 on canvas · body and headings */
  --text-2: #3d4956;          /*  8.0:1 · labels, secondary text */
  /**
   * PsiEVO's `--ink-3`, adopted verbatim.
   *
   * ⚠ This is the one token in the file below the 4.5:1 floor: 3.84 on canvas, 4.39 on
   * surface, 4.12 on sunken. It used to be #616E7B (4.56 / 5.22 / 4.90). The two
   * products now share a palette AND a landing page, so a 10% darker grey on this side
   * is a visible seam at the handover from the public page to the app.
   *
   * Contained rather than waved through: this token carries metadata only — "há 3 dias",
   * placeholders, the hint under a field — never clinical text and never a label, and
   * the print block at the end of this file restores #616E7B, because paper shares
   * nothing with the landing page. See docs/DECISIONS.md D20 and the ACCEPTED list in
   * tools/contrast.mjs, which pins all three ratios.
   */
  --text-3: #6c7a89;

  /* ── Meaning colours ────────────────────────────────────── */
  --accent: #4c3a63;          /* 8.8:1 on canvas · 10.0:1 for white on it */
  --accent-2: #6b5288;        /* hover, focus ring, timeline dots */
  --accent-soft: #f0ebf7;

  --ok: #256e52;              /* 5.4:1 */
  --ok-soft: #e4f1eb;
  --warn: #96610a;            /* 4.6:1 */
  --warn-soft: #faf0dd;
  /* Between warn and danger. Payment status escalates through the full ramp:
     em dia → vence em ≤5 dias → vence hoje → vencido. */
  --alert: #a63a16;           /* 5.7:1 */
  --alert-soft: #fbebe4;
  --danger: #9e2020;          /* 6.9:1 */
  --danger-soft: #fae8e8;

  /* Deliberately off-palette, and used for exactly one thing: marking capability
     rather than severity ("Aplicável no app"). */
  --teal: #1f6e7b;            /* 5.1:1 */
  --teal-soft: #e6f2f4;

  /**
   * The foreground of a strong fill.
   *
   * This was `#fff` written into ten rules — a palette decision disguised as a
   * constant, and a constant that is wrong the moment the fill underneath is light.
   * White on the dark theme's accent measures 1.95:1.
   */
  --on-accent: #ffffff;

  /* ── Strokes ────────────────────────────────────────────── */
  --border: #dce3ea;
  --border-strong: #c6d0da;
  --focus: #6b5288;

  /** Behind the mobile drawer and under a modal. Ink-tinted, not black, for the same
   *  reason the shadow is. */
  --scrim: rgb(19 26 36 / 34%);

  /* ── Typography ─────────────────────────────────────────── */
  /* Serif is reserved for IDENTITY AND PLACE — the wordmark, a patient's name, a page
     title, the agenda's period label, a group heading. It never appears in body copy.
     That single restriction is most of why this reads as considered. */
  --font-serif: 'Spectral', Georgia, 'Times New Roman', serif;
  --font: 'Public Sans', system-ui, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  /* Mono carries every number, with tabular figures so columns of money and scores
     actually line up. */
  --font-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;

  --size-xs: 0.6875rem;       /* 11px — metadata only, never clinical text */
  --size-sm: 0.8125rem;       /* 13px */
  --size-md: 0.9375rem;       /* 15px — body */
  --size-lg: 1.1875rem;       /* 19px */
  --size-xl: 1.375rem;        /* 22px */
  --size-2xl: 1.625rem;       /* 26px */

  --leading-tight: 1.25;
  --leading: 1.5;

  /* ── Spacing (scale of 4) ───────────────────────────────── */
  --s1: 0.25rem;  --s2: 0.5rem;   --s3: 0.75rem;  --s4: 1rem;
  --s5: 1.5rem;   --s6: 2rem;     --s7: 3rem;     --s8: 4rem;

  /* ── Shape ──────────────────────────────────────────────── */
  /* Radius grows with the container: pills → buttons → cards → modal sheets. */
  --radius-sm: 7px;
  --radius: 10px;
  /* Ink-tinted rather than pure black, so cards sit on the canvas instead of
     hovering over it. */
  --shadow: 0 1px 2px rgb(19 26 36 / 5%), 0 4px 14px rgb(19 26 36 / 5%);
  --shadow-modal: 0 24px 70px rgb(19 26 36 / 28%);

  --layout-max: 1080px;
  --rail: 264px;
  /* The title bar's real height: two lines (eyebrow + serif title) plus padding. It was
     4rem when the bar held a single line, which left every sticky element offset by it
     sitting slightly too high. */
  --header-h: 4.5rem;
}

/* Light is the DEFAULT, and it is NOT inferred from the operating system — see D21.
 *
 * In a consulting room the screen is read up close, in ambient light, and that remains
 * the right default. This file briefly honoured `prefers-color-scheme` instead, which
 * made the OS decide; the clinician using it asked for the opposite: light unless they
 * themselves turn dark on. So the dark palette hangs off `data-theme`, written by
 * public/theme-boot.js from localStorage, and ONLY the exact string 'dark' selects it.
 * tests/tokens.test.ts fails if `prefers-color-scheme` reappears in styles/. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ── Dark theme — chosen, never inferred ──────────────────────────────────────────
 *
 * `:where()` here is load-bearing, not sugar. It contributes ZERO specificity, so this
 * selector measures (0,1,0) — exactly what a bare `:root` measures, which is what lets
 * the print block below win on source order alone. Write `:root[data-theme='dark']` and
 * it becomes (0,2,0): it beats the print block in ANY order, a clinician who sets the
 * vault up at night prints an invisible recovery code, and the "print block is last"
 * test stays green while the protection is gone. tests/tokens.test.ts asserts the
 * WEIGHT of every palette rule, not its position.
 *
 * The severity ramp (--warn / --alert / --danger and their tints), --focus, --on-accent,
 * --scrim and both shadows are derived here rather than copied: PsiEVO has no dark app
 * to copy from, and its landing page has no payment status to colour.
 *
 * Every number in a comment is measured by tools/contrast.mjs against all THREE dark
 * backgrounds, in the order canvas / surface / sunken. The tool layers this block over
 * `:root` exactly as the cascade does, so a token omitted here is not "inherited
 * silently": it is checked in its LIGHT value against the dark canvas and fails. The
 * light --warn #96610a on #12161c measures 3.47:1. That is the guard rail.
 */
:root:where([data-theme='dark']) {
  color-scheme: dark;

  /* ── Surfaces ──
   *
   * Retuned as an APP palette. The first version was PsiEVO's `site.css` dark block
   * verbatim — but that is a LANDING PAGE palette (large type, few surfaces, generous
   * space, strong shadows), and PsiEVO has no dark app to have copied instead. This app
   * is the opposite: hairline grids, 11px mono metadata, tables, three near-identical
   * surfaces. It measured fine pair by pair and read badly, reported as "esse tema
   * escuro está ruim de ler".
   *
   * Two things changed, and both are about the same fact: SHADOW IS INVISIBLE ON
   * NEAR-BLACK. You cannot darken black, so the elevation cue this design leans on
   * simply stops existing in the dark, and every panel boundary falls to a 1px hairline.
   *
   *   1. The canvas is lifted, which puts --text-1 at 12.75:1 instead of 15.57:1. Dark
   *      UI guidance (Material, Apple HIG) warns against pure white on pure black for
   *      exactly this: light strokes bloom on dark grounds, and worse at small sizes.
   *   2. The surface steps widened (ΔL* 4.9/4.2 → 5.4/4.9) and the borders got real
   *      weight (border-on-canvas ΔL* 14.2 → 18.1), because in the dark they are the
   *      boundary rather than a decoration on top of one.
   *
   * D20's own note said --border-strong sat under the 3:1 non-text floor and that this
   * was tolerable while "the hairlines are decorative separators, never the sole carrier
   * of a boundary — if that stops being true, this is where you look". In the dark it
   * stopped being true. This is that look.
   */
  --canvas: #161b23;
  --surface: #1f2630;
  --sunken: #28303c;
  --text-1: #d7dee7;         /* 12.75 · 11.24 ·  9.82 */
  --text-2: #aeb9c8;         /*  8.70 ·  7.67 ·  6.70 */
  /* No waiver needed on this side: the parity argument is about the light theme the
     landing page shares, and a dark --text-3 clears the floor on its own. */
  --text-3: #93a0af;         /*  6.49 ·  5.72 ·  5.00 */

  --accent: #c3b0df;         /*  8.72 ·  7.69 ·  6.71 */
  --accent-2: #a78fcb;       /*  6.12 ·  5.39 ·  4.71 */
  --accent-soft: #2a2338;

  --ok: #7fc7a4;             /*  8.73 ·  7.70 ·  6.72 */
  --ok-soft: #182e25;
  --warn: #d9a93f;           /*  7.98 ·  7.04 ·  6.14 */
  --warn-soft: #2d2513;
  --alert: #e9a685;          /*  8.44 ·  7.44 ·  6.50 */
  --alert-soft: #321d15;
  --danger: #ec9a9a;         /*  7.95 ·  7.01 ·  6.12 */
  --danger-soft: #371a1a;

  --teal: #7fc5cf;           /*  8.88 ·  7.83 ·  6.84 */
  --teal-soft: #192a2e;
  --border: #39434f;
  --border-strong: #4d5866;
  --focus: #a78fcb;          /*  6.12 ·  5.39 ·  4.71 */

  /* White on every strong fill above measures 1.95–2.82:1. This is the ink that
     replaces it, which is the whole reason --on-accent exists. */
  --on-accent: #171320;

  --shadow: 0 1px 2px rgb(0 0 0 / 30%), 0 10px 30px rgb(0 0 0 / 35%);
  --shadow-modal: 0 24px 70px rgb(0 0 0 / 55%);
  --scrim: rgb(0 0 0 / 55%);
}


/* ── Paper forces the light palette ───────────────────────────────────────────────
 *
 * The recovery sheet is printed FROM THE APP DOCUMENT — firstRun.ts calls
 * `window.print()` — so it inherits whatever theme the screen is in. Without this, a
 * clinician who sets up the vault at night prints #EAEEF3 onto white paper: 1.17:1,
 * which is a blank sheet with an invisible recovery code on it. That sheet is the only
 * thing standing between a forgotten password and five years of records.
 *
 * ⚠ THIS BLOCK MUST STAY LAST, AND EVERY PALETTE RULE MUST WEIGH WHAT `:root` WEIGHS.
 * `@media print` alone is not enough: printing from a dark session matches the dark
 * theme AND print, and with equal specificity the winner is source order. Order is only
 * half of it — a heavier theme selector wins from anywhere, which is why the dark block
 * above wraps its attribute in `:where()`. tests/tokens.test.ts pins weight, order and
 * the absence of !important, because all three decide the winner together.
 *
 * Documents — chart, session, protocol result — are unaffected either way: render.ts
 * writes a standalone document into a new window with its own hard-coded PRINT_CSS and no
 * link to this file. It was already immune and stays that way.
 */
@media print {
  :root {
    color-scheme: light;
    --canvas: #ffffff;
    --surface: #ffffff;
    --sunken: #ffffff;

    --text-1: #131a24;
    --text-2: #3d4956;
    /* #616E7B, not #6C7A89. The parity argument is about a screen the clinician and the
       landing page share; paper shares nothing, so it gets the accessible grey back. This
       is why the waiver in contrast.mjs names its theme. */
    --text-3: #616e7b;

    --accent: #4c3a63;
    --accent-2: #6b5288;
    --accent-soft: #f0ebf7;
    --focus: #6b5288;
    --on-accent: #ffffff;

    --ok: #256e52;
    --ok-soft: #e4f1eb;
    --warn: #96610a;
    --warn-soft: #faf0dd;
    --alert: #a63a16;
    --alert-soft: #fbebe4;
    --danger: #9e2020;
    --danger-soft: #fae8e8;
    --teal: #1f6e7b;
    --teal-soft: #e6f2f4;

    --border: #dce3ea;
    --border-strong: #c6d0da;

    --shadow: none;
    --shadow-modal: none;
    --scrim: transparent;
  }
}
