/* tokens.css — the design system's vocabulary. One authority on colour,
 * type, space and elevation for both browser apps.
 *
 * Oracle: the product's own three-valued outcome (PASS / FAIL / UNDETERMINED,
 * CLAUDE.md §1.4) and the closed campaign status set (specification §24).
 * Every status below is keyed by the wire value of its enum member, so the
 * stylesheet cannot drift into inventing a status the domain does not have.
 * The palette's oracle is `site/styles.css` — the storefront at mailgun.cz,
 * whose brand scale is the owner's own ruling («цвета те же», measured from
 * lifegoal.cz).
 *
 * Direction — one product, two doors. A firm that clicks through from the
 * storefront to the cabinet must arrive at the SAME product: same blue, same
 * neutrals, same corner radii, same air. Colour carries meaning only —
 * brand blue on the action to take, red on a refusal, amber on "cannot
 * tell", grey on everything else. Nothing is coloured to be pretty.
 * Prose is sans; anything the system ASSERTS (counts, rates, ids, DNS
 * records, status names) is mono with tabular figures, because this is a
 * product about numbers that must reconcile. No web font is loaded: a
 * strict CSP and the no-new-dependencies rule mean the personality comes
 * from treatment, not from a downloaded face.
 *
 * Why the values moved on 2026-08-11 (lane U): the previous revision was a
 * manila-paper/indigo palette inherited from the marketing product this
 * tree was re-based away from. It was not wrong, it was someone else's —
 * and it made app.mailgun.cz look like a different company from
 * mailgun.cz. The owner's instruction was «только вид кабинета простой и из
 * 2026 года, а не 1998». Every token NAME below is unchanged, so
 * components.css, shell.css and the admin console keep working; only the
 * values moved onto the storefront's scale.
 *
 * 🔴 Do not add a second palette anywhere. `site/styles.css` declares the
 * brand scale for the standalone storefront (it ships without this file);
 * this file declares it for both applications. Two files, one ruling — a
 * third place that names a hex is a defect.
 *
 * Update together on every touch: components.css consumes every custom
 * property named here; apps/front/static/shell.css and
 * apps/admin/.../static consume the same names; templates/shared_ui/
 * _macros.html emits the class names keyed to the status and outcome
 * values below; `site/styles.css` holds the same brand scale and must not
 * disagree with it.
 */

:root {
  color-scheme: light dark;

  /* ── Paper and ink. Neutrals are the storefront's grey scale. ───── */
  --ui-paper: #f9fafb;
  --ui-surface: #ffffff;
  --ui-surface-sunk: #f4f4f5;
  --ui-rule: #e5e7eb;
  --ui-rule-strong: #d1d5db;
  --ui-ink: #111827;
  --ui-ink-soft: #4b5563;
  --ui-ink-faint: #6b7280;
  --ui-ink-inverse: #ffffff;

  /* The stage a rendered email sits on (`.ui-stage-frame`, `.ui-stage-frame
   * iframe` in components.css). It is deliberately an alias of the sunk
   * surface rather than a fifth neutral: the preview must read as a hole
   * cut in the page, and the customer's own HTML — which brings its own
   * background — is what the eye should land on, never our frame. Written
   * as `var(--ui-surface-sunk)` and not as the hex, so the dark block below
   * needs no second copy: a custom property that references another one is
   * resolved where it is USED, so it follows the theme by itself. */
  --ui-preview: var(--ui-surface-sunk);

  /* ── Stamp: the single accent, and it is the storefront's brand blue.
   *    Primary action, focus ring and "you are here" wear it. Nothing
   *    decorative does. ─────────────────────────────────────────────── */
  --ui-stamp: #2563eb;
  --ui-stamp-deep: #1d4ed8;
  --ui-stamp-wash: #eff6ff;
  --ui-stamp-rule: #bfdbfe;

  /* ── Campaign status system. Keyed by enum wire value (§24).
   *    Muted on purpose: a status classifies, it does not alarm. ───── */
  --ui-status-draft-ink: #4b5563;
  --ui-status-draft-wash: #f4f4f5;
  --ui-status-moderation_pending-ink: #b45309;
  --ui-status-moderation_pending-wash: #fef3c7;
  --ui-status-scheduled-ink: #1d4ed8;
  --ui-status-scheduled-wash: #eff6ff;
  --ui-status-preparing-ink: #0e7490;
  --ui-status-preparing-wash: #ecfeff;
  --ui-status-sending-ink: #0f766e;
  --ui-status-sending-wash: #f0fdfa;
  --ui-status-paused-ink: #475569;
  --ui-status-paused-wash: #f1f5f9;
  --ui-status-sent-ink: #15803d;
  --ui-status-sent-wash: #f0fdf4;
  --ui-status-cancelled-ink: #6b7280;
  --ui-status-cancelled-wash: #f4f4f5;
  --ui-status-failed-ink: #b91c1c;
  --ui-status-failed-wash: #fef2f2;

  /* ── The three-valued outcome. The signature of this system.
   *    UNDETERMINED is neither pass nor fail and must never be able to
   *    read as either — it carries a hatch, so it survives greyscale,
   *    colour-blindness and a bad monitor. ─────────────────────────── */
  --ui-outcome-pass-ink: #15803d;
  --ui-outcome-pass-wash: #f0fdf4;
  --ui-outcome-fail-ink: #b91c1c;
  --ui-outcome-fail-wash: #fef2f2;
  --ui-outcome-undetermined-ink: #b45309;
  --ui-outcome-undetermined-wash: #fffbeb;
  --ui-outcome-undetermined-hatch: rgba(180, 83, 9, 0.18);

  /* ── Exclusion reasons (§47). One hue family, stepped, so the
   *    reconciliation bar reads as one quantity cut into parts rather
   *    than as five competing alerts. ──────────────────────────────── */
  --ui-reason-eligible: #2563eb;
  --ui-reason-unsubscribed: #6366f1;
  --ui-reason-suppressed: #8b5cf6;
  --ui-reason-frequency_capped: #a855f7;
  --ui-reason-duplicate: #c084fc;
  --ui-reason-internal: #d8b4fe;

  /* ── Type. The storefront's stacks, and a base that is actually
   *    readable: 16px body, not the 15px of a dense back office. ──── */
  --ui-font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --ui-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  --ui-size-display: 1.875rem;
  --ui-size-title: 1.25rem;
  --ui-size-subtitle: 1.0625rem;
  --ui-size-body: 1rem;
  --ui-size-small: 0.875rem;
  --ui-size-micro: 0.75rem;

  --ui-weight-regular: 400;
  --ui-weight-medium: 500;
  --ui-weight-strong: 650;

  --ui-lh-tight: 1.2;
  --ui-lh-snug: 1.4;
  --ui-lh: 1.6;

  --ui-track-display: -0.02em;
  --ui-track-micro: 0.06em;

  /* ── Space. 4px base; the scale is the only permitted set of gaps. ─ */
  --ui-s1: 0.25rem;
  --ui-s2: 0.5rem;
  --ui-s3: 0.75rem;
  --ui-s4: 1rem;
  --ui-s5: 1.5rem;
  --ui-s6: 2rem;
  --ui-s7: 3rem;
  --ui-s8: 4rem;

  /* ── Control height. Exactly two rungs, because a form has exactly two
   *    kinds of control: the one a finger aims at, and the one that rides
   *    inside a dense row (segmented control, step rail, small button).
   *
   *    Why 44px and not "whatever the text makes": measured on 2026-08-15,
   *    `.ui-input` computes to 43.6px from its own box (8px padding twice,
   *    16px × 1.6 line, 1px border twice) while `.ui-btn` beside it — same
   *    padding, 14px text — computes to 24.4px. The button was not styled
   *    to be short; it simply had no floor, so the pair misaligned by 19px
   *    and the button fell under every touch-target guidance there is.
   *    2.75rem is the height the input ALREADY has, so the token codifies
   *    the measured reality instead of shrinking the field to meet it, it
   *    is what apps/front/static/auth.css independently chose for the same
   *    controls on the registration screen, and it is the 44px pointer
   *    target of WCAG 2.5.5. The small rung is 2.25rem = 36px: on the 4px
   *    base like everything else, comfortably over the 24px minimum of
   *    WCAG 2.5.8, and — the point of a token — actually taller than the
   *    35.6px its content would produce, so it governs rather than
   *    decorates. ─────────────────────────────────────────────────────── */
  --ui-control: 2.75rem;
  --ui-control-small: 2.25rem;

  /* ── Shape. The storefront's ladder: 6 / 8 / 12, and a pill. ────── */
  --ui-r1: 6px;
  --ui-r2: 8px;
  --ui-r3: 12px;
  --ui-r-round: 999px;

  /* ── Elevation. Exactly two levels exist, and both are almost
   *    nothing: separation is done with air and a hairline, not with a
   *    drop shadow. ─────────────────────────────────────────────────── */
  --ui-shadow: 0 1px 2px rgba(17, 24, 39, 0.06);
  --ui-shadow-pop: 0 12px 32px rgba(17, 24, 39, 0.12), 0 2px 6px rgba(17, 24, 39, 0.06);

  /* The scrim behind a modal (`.ui-modal`). It is the ink at 45%, not a
   * neutral grey and not black: the dimmed page must stay legible enough
   * to show WHAT the dialogue is about, while being unmistakably out of
   * reach. Undeclared until 2026-08-15, which made the backdrop fully
   * transparent and a modal look like a card that had landed on the page
   * by accident. */
  --ui-overlay: rgba(17, 24, 39, 0.45);

  /* ── Motion. Short, and always opt-out-able. ───────────────────── */
  --ui-motion-quick: 110ms;
  --ui-motion-settle: 220ms;
  --ui-ease: cubic-bezier(0.2, 0, 0.2, 1);

  /* ── Measure. `--ui-measure-page` matches the storefront's `--page`. ─ */
  --ui-measure-prose: 68ch;
  --ui-measure-page: 1152px;
  --ui-measure-form: 640px;
}

/* The storefront is light-only (its own ledger says so: "the same colours"
 * cannot be honoured by inventing a dark palette lifegoal.cz does not
 * have). The cabinet is a tool people sit in all day, so it keeps a dark
 * theme — re-derived on the same blue, over slate rather than over the old
 * warm greys, so the two themes are the same product at two brightnesses. */
@media (prefers-color-scheme: dark) {
  :root {
    --ui-paper: #0b1220;
    --ui-surface: #111a2b;
    --ui-surface-sunk: #0a101c;
    --ui-rule: #1f2b40;
    --ui-rule-strong: #33415c;
    --ui-ink: #f1f5f9;
    --ui-ink-soft: #b6c2d4;
    --ui-ink-faint: #8b99ad;
    --ui-ink-inverse: #0b1220;

    --ui-stamp: #60a5fa;
    --ui-stamp-deep: #93c5fd;
    --ui-stamp-wash: #16233c;
    --ui-stamp-rule: #2c4066;

    --ui-status-draft-ink: #b6c2d4;
    --ui-status-draft-wash: #182338;
    --ui-status-moderation_pending-ink: #fbbf24;
    --ui-status-moderation_pending-wash: #2e2410;
    --ui-status-scheduled-ink: #93c5fd;
    --ui-status-scheduled-wash: #16233c;
    --ui-status-preparing-ink: #67e8f9;
    --ui-status-preparing-wash: #0d2830;
    --ui-status-sending-ink: #5eead4;
    --ui-status-sending-wash: #0c2a28;
    --ui-status-paused-ink: #a5b4c8;
    --ui-status-paused-wash: #172232;
    --ui-status-sent-ink: #6ee7a0;
    --ui-status-sent-wash: #0d2a1c;
    --ui-status-cancelled-ink: #8b99ad;
    --ui-status-cancelled-wash: #151f31;
    --ui-status-failed-ink: #fca5a5;
    --ui-status-failed-wash: #31161a;

    --ui-outcome-pass-ink: #6ee7a0;
    --ui-outcome-pass-wash: #0d2a1c;
    --ui-outcome-fail-ink: #fca5a5;
    --ui-outcome-fail-wash: #31161a;
    --ui-outcome-undetermined-ink: #fbbf24;
    --ui-outcome-undetermined-wash: #2e2410;
    --ui-outcome-undetermined-hatch: rgba(251, 191, 36, 0.22);

    --ui-reason-eligible: #60a5fa;
    --ui-reason-unsubscribed: #818cf8;
    --ui-reason-suppressed: #a78bfa;
    --ui-reason-frequency_capped: #c084fc;
    --ui-reason-duplicate: #d8b4fe;
    --ui-reason-internal: #e9d5ff;

    --ui-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
    --ui-shadow-pop: 0 12px 32px rgba(0, 0, 0, 0.6), 0 2px 6px rgba(0, 0, 0, 0.45);

    /* A 45% wash of a dark ink over an already dark page separates nothing,
     * so the scrim is re-derived here rather than inherited: deeper than
     * the darkest surface and more opaque, because in this theme the panel
     * is LIGHTER than the page and the backdrop has to give it that
     * contrast. `--ui-control*` and `--ui-preview` need no counterpart —
     * the first two are geometry, which the theme does not change, and the
     * third is an alias that follows `--ui-surface-sunk` above. */
    --ui-overlay: rgba(2, 6, 14, 0.66);
  }
}
