/* ============================================================================
   Refocus X — Design Pack · tokens.css
   The single source of truth for colour, radius, elevation, type, layout and
   stacking across every Refocus X product.

   USAGE — link this BEFORE any page-local <style>:
     <link rel="stylesheet" href="/design-pack/tokens.css">

   Because it loads first, a page's own :root block still wins. That is
   deliberate: an existing app can adopt this file today and nothing changes
   until it starts deleting its local overrides, one page at a time.

   Every value here is taken from real shipped code. Nothing is invented.
   The reasoning for each ruling is in DECISIONS.md.
   ============================================================================ */

:root {

  /* ── Ink (text) ──────────────────────────────────────────────────────────
     Three steps. Anything below --rx-ink-subtle is not readable on our
     surfaces, so there is no fourth step on purpose. */
  --rx-ink:         #18181b;   /* primary text; also the primary button fill */
  --rx-ink-muted:   #52525b;   /* secondary text, field labels */
  --rx-ink-subtle:  #a1a1aa;   /* hints, placeholders, disabled, timestamps */
  --rx-on-ink:      #ffffff;   /* text ON an --rx-ink fill */
  --rx-ink-hover:   #27272a;   /* hover state of an --rx-ink fill */

  /* ── Surfaces ────────────────────────────────────────────────────────────
     Named by role, not by number, because numbered scales get used
     inconsistently the moment two people touch them. */
  --rx-surface-page:    #fafaf9;   /* the page background */
  --rx-surface-card:    #ffffff;   /* raised: cards, panels, menus, table head */
  --rx-surface-sunken:  #f4f4f5;   /* tints: row hover, chip rest, inset blocks */
  --rx-border:          #e4e4e7;   /* every border and divider. One value. */
  --rx-border-strong:   #d4d4d8;   /* hover/active border ONLY. --rx-border is
                                      1.27:1 on white — a hover state that stays
                                      on it is not a hover state. Do not reach
                                      for this as a second default border. */

  /* ── Brand ───────────────────────────────────────────────────────────────
     The one pair a white-label deployment overrides at runtime. Defaults to
     the ink, so an unbranded install looks deliberate rather than unfinished.
     Set by RX.brand() from a tenant's settings; --rx-brand-on is COMPUTED
     from luminance, never assumed to be white. */
  --rx-brand:     #18181b;
  --rx-brand-on:  #ffffff;

  /* ── The Refocus X mark ──────────────────────────────────────────────────
     Our own corporate purple, sampled from the logo artwork. This is NOT
     --rx-brand: a tenant overrides --rx-brand, and must never override this
     one — its whole job is saying "Refocus X built this". Product favicons,
     app icons, the logo itself. Nothing else.
     Measured 4.70:1 on white and 3.77:1 on --rx-ink, so it may carry text on
     white and takes --rx-on-mark as a fill, but it is a mark colour, not a
     UI accent — reach for --rx-accent-ui before you reach for this. */
  --rx-mark:     #7f4fff;
  --rx-on-mark:  #ffffff;   /* 4.70:1 — text ON an --rx-mark fill */

  /* ── Accent (purple decorative, green UI+text) ───────────────────────────
     The three tiers now split by MEANING, not just by depth (D16).
     --rx-accent is brand: the sign-in headline, spinners, large decorative
     fills. It is the mark's purple and follows the brand.
     --rx-accent-ui and --rx-accent-text are STATE: switches, progress fills,
     toast-success, unread dots, "Saved", "Came online", "done". Those say
     something is good, and green is what says that. They do NOT follow the
     brand — a purple tick is just a tick.
     This is why D2's split exists at all now. Ratios against white. */
  --rx-accent:        #7f4fff;   /* 4.70:1 — BRAND: headline, spinner, fills */
  --rx-accent-hover:  #663fcc;   /* 6.67:1 — hover/active of an --rx-accent fill.
                                    RX.darkenTo-derived from the mark (v0.4.0's
                                    TEXT tier). Was the ui tier #7347e6 for a
                                    few hours — Pierre: "I can hardly see it"
                                    — so the step doubled to the next recorded
                                    rung; selftest pins a 1.35× floor so it can
                                    never quietly shrink again. Exists because
                                    the old hover swap accent→accent-ui crosses
                                    MEANINGS since D16: a brand-purple button
                                    must not hover state-green (D18). */
  --rx-accent-ui:     #16a34a;   /* 3.30:1 — STATE: switches, bars, dots */
  --rx-accent-text:   #15803d;   /* 5.02:1 — STATE: success copy on white */
  --rx-on-accent:     #ffffff;   /* 4.70:1 — text ON an --rx-accent fill */

  /* ── Status ──────────────────────────────────────────────────────────────
     Four families, one shape each (D17). Every family answers the same five
     questions, so nobody has to invent an answer for the one that is missing:

       --rx-X          the fill, and the text/border colour on white
       --rx-X-strong   the escalated step
       --rx-X-soft     the tint background for a card or badge
       --rx-X-border   the border that goes with that tint
       --rx-on-X       text ON an --rx-X fill

     Use -soft and -border together; they are contrast-checked as a set.
     --rx-danger-strong is the escalated destructive tier only (full wipes,
     unscoped deletion). See patterns/COMPONENTS.md. */
  --rx-danger:         #dc2626;
  --rx-danger-strong:  #991b1b;
  --rx-danger-soft:    #fef2f2;   /* danger-zone card background */
  --rx-danger-border:  #fecaca;   /* 4.41:1 — danger on danger-soft */
  --rx-on-danger:      #ffffff;

  --rx-warn:           #b45309;
  --rx-warn-strong:    #d97706;   /* reversible-but-serious actions */
  --rx-warn-soft:      #fff7ed;   /* 4.73:1 — warn on warn-soft */
  --rx-warn-border:    #fde68a;
  --rx-on-warn:        #ffffff;   /* 5.02:1 */

  --rx-info:           #2563eb;
  --rx-info-strong:    #1e40af;
  --rx-info-soft:      #dbeafe;   /* 7.15:1 — info-strong on info-soft */
  --rx-info-border:    #bfdbfe;
  --rx-on-info:        #ffffff;   /* 5.17:1 */

  /* Success is a STATE, and it now has its own name (D17).
     These values are --rx-accent-ui and --rx-accent-text today, deliberately.
     The point is not that they differ — it is that they are free to. D16 made
     the accent's green tiers mean "state"; a name the brand cannot reach is
     what stops the next brand move turning every tick purple, which is exactly
     what v0.4.0 did. Reference --rx-success for anything that says good/on. */
  --rx-success:        #16a34a;   /* 3.30:1 — fills, bars, dots, switches */
  --rx-success-strong: #15803d;   /* 5.02:1 — success copy on white */
  --rx-success-soft:   #f0fdf4;   /* 4.79:1 — success-strong on success-soft */
  --rx-success-border: #bbf7d0;
  --rx-on-success:     #ffffff;

  /* ── Severity fills — alerting surfaces ──────────────────────────────────
     NOT the same thing as danger/warn/info, and the difference is the whole
     reason this tier exists (D17). Those are FOREGROUND colours, chosen for
     contrast on white: button text, badge ink, borders. These are FILLS for a
     surface that is itself the alarm — PageCast's full-screen kiosk takeover,
     its preset buttons, its pulsing banner.

     Ruled by Pierre 6 Aug 2026 and shipping since. Hazard yellow carries dark
     ink, not white: 9.53:1, the best-contrasting surface in the suite, and the
     one colour a person reads as "warning" from across a room without reading
     the word. Folding these onto --rx-warn would make it a brown screen at
     5.02:1. Do not.

     A life-safety UI is the one place a colour must never be re-derived. */
  --rx-alert-critical:       #b91c1c;   /* 6.47:1 with white */
  --rx-alert-critical-pulse: #7f1d1d;   /* the trough of the pulse — 10.02:1 */
  --rx-alert-warning:        #eab308;   /* hazard yellow */
  --rx-on-alert-warning:     #1c1400;   /* 9.53:1 — dark ink, NOT white */
  --rx-alert-info:  var(--rx-info-strong);  /* #1e40af, by reference: one place */
  --rx-on-alert:             #ffffff;   /* text on critical and info fills */

  /* ── Category tints ──────────────────────────────────────────────────────
     Background + readable-on-it text, for badges that classify rather than
     alarm (event namespaces, record types). Each pair is contrast-checked;
     use them together, never mix a tint with a different ink. */
  --rx-tint-green:   #dcfce7;  --rx-tint-green-ink:  #15803d;
  --rx-tint-blue:    #e0f2fe;  --rx-tint-blue-ink:   #0369a1;
  --rx-tint-purple:  #f3e8ff;  --rx-tint-purple-ink: #7c3aed;
  --rx-tint-orange:  #ffedd5;  --rx-tint-orange-ink: #c2410c;
  --rx-tint-amber:   #fef3c7;  --rx-tint-amber-ink:  #92400e;

  /* ── Radius ──────────────────────────────────────────────────────────────
     Three steps plus a pill. */
  --rx-radius-sm:    8px;    /* buttons, inputs, chips, small menus */
  --rx-radius:      10px;    /* default: panels, inset blocks, tiles */
  --rx-radius-lg:   14px;    /* cards, modals, big containers */
  --rx-radius-pill: 999px;   /* filter chips, tags, toggles */

  /* ── Scrim ───────────────────────────────────────────────────────────────
     The dim behind an overlay. Two weights: the heavier one for modal
     overlays that take the whole screen, the lighter one for a non-modal
     side panel where the list behind must stay readable. */
  --rx-scrim:      rgba(0,0,0,.45);
  --rx-scrim-soft: rgba(0,0,0,.25);

  /* ── Elevation ───────────────────────────────────────────────────────────
     One shadow per z-band. Do not mix bands. */
  --rx-shadow-sm:     0 1px 3px rgba(0,0,0,.08);
  --rx-shadow:        0 1px 3px rgba(0,0,0,.08), 0 4px 16px rgba(0,0,0,.06);
  --rx-shadow-pop:    0 8px 24px rgba(0,0,0,.18);
  --rx-shadow-modal:  0 16px 50px rgba(0,0,0,.28);

  /* ── Type ────────────────────────────────────────────────────────────────
     Self-host these. See DECISIONS.md ruling 7. */
  --rx-font: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --rx-mono: 'DM Mono', ui-monospace, Consolas, monospace;

  /* ── Type scale ──────────────────────────────────────────────────────────
     Taken from the real distribution across a shipped 15,000-line frontend,
     not invented. Usage counts are in DECISIONS.md D12.

     13px is the body size and by far the most used — this is a dense
     information product, not a marketing site. There is deliberately NO step
     below 11px: 10px appears 22 times in the source and is below the
     comfortable reading floor. Do not add one. */
  --rx-text-xs:    11px;   /* hints, micro-labels, timestamps */
  --rx-text-sm:    12px;   /* secondary text, meta rows */
  --rx-text:       13px;   /* BODY. The default for everything. */
  --rx-text-md:    14px;   /* inputs, prominent body, switch labels */
  --rx-text-lg:    16px;   /* subheads */
  --rx-title-sm:   18px;   /* card and section titles */
  --rx-title:      22px;   /* page title — management screens */
  --rx-title-lg:   28px;   /* big numbers: stat values */
  --rx-display:    48px;   /* hero, sign-in only */

  /* ── Layout ──────────────────────────────────────────────────────────────
     Three widths, each with a job. Picking the wrong one is the most common
     layout mistake — see patterns/LAYOUT.md. */
  --rx-content-max: 1040px;   /* reading + management screens. The default. */
  --rx-form-max:     640px;   /* single-column task forms */
  --rx-canvas-max:  1200px;   /* dashboards and multi-column canvases */
  --rx-header-h:      60px;
  --rx-brand-mark:    32px;   /* the brand mark in the header — CANON (D20).
                                 Measured from FileDrop's nav.js (width="32");
                                 rx-shell applies it to brand.logo itself, so
                                 a caller's width="20" cannot shrink the tile
                                 to a dot. */
  --rx-gap:           20px;   /* grid gutter */
  --rx-toast-top:     18px;   /* toast-stack offset from the viewport top.
                                 Apps with fixed/sticky chrome raise it (even
                                 dynamically, from the header's measured
                                 height) so a toast never covers the header —
                                 PageCast's carries its master switch. */

  /* ── Focus ring ──────────────────────────────────────────────────────────
     One ring, used by every focusable control. */
  --rx-focus-ring: 0 0 0 3px rgba(24,24,27,.08);

  /* ── Stacking scale ──────────────────────────────────────────────────────
     NEVER write a raw z-index. Use one of these. The contract that explains
     why the gaps are where they are is in patterns/STACKING.md. */
  --rx-z-sticky:    10;      /* sticky table headers — clippable, in-flow */
  --rx-z-menu:      50;      /* page-local menus inside a container */
  --rx-z-header:   100;      /* the fixed app header */
  --rx-z-dropdown: 200;      /* menus belonging to the header */
  --rx-z-modal:   1000;      /* page modals */
  --rx-z-boot:    9999;      /* boot splash — must outrank page modals */
  --rx-z-popover:10000;      /* body-rendered, fixed, rect-anchored poppers */
  --rx-z-dialog: 11000;      /* shared dialog engine */
  --rx-z-toast:  12000;      /* toasts sit ABOVE dialogs, deliberately */
  --rx-z-expired:13000;      /* session-expired terminal state */
  --rx-z-blocked:99999;      /* access-denied takeover — nothing above this */
}

/* ── Adopting this in an app that already has its own token names? ──────────
   Do not edit this file. Link the matching alias sheet from `compat/` after
   it, and the app's existing `var(--its-own-name)` keeps working unchanged:

     <link rel="stylesheet" href="/design-pack/tokens.css">
     <link rel="stylesheet" href="/design-pack/compat/filedrop.css">

   Alias sheets are per-app and disposable — delete one when its app has
   finished migrating to the --rx-* names. Keeping them here rather than in
   this file means tokens.css stays the standard, not a pile of history.
   ============================================================================ */

/* ============================================================================
   BASE — the three rules every Refocus X app needs. Nothing else is imposed.
   ============================================================================ */

*, *::before, *::after { box-sizing: border-box; }

body {
  font-family: var(--rx-font);
  background: var(--rx-surface-page);
  color: var(--rx-ink);
  margin: 0;
  /* Long unbroken strings — filenames, ids, email addresses — must wrap
     instead of forcing a horizontal scrollbar. This one line prevents a
     whole class of layout bug. */
  overflow-wrap: anywhere;
}

/* ── Layout primitives ─────────────────────────────────────────────────────
   Wrap page content in .rx-page. Put cards in a .rx-grid. Mark a full-bleed
   cell .rx-col-span. That is the entire layout system. */
/* The page-title size travels WITH the width modifier, because in the source
   it always did — 22px on management screens, 24px on task forms, 26px on
   landing pages. That looked like three inconsistent values until the pages
   were grouped by type, at which point it was one rule. See DECISIONS.md D12. */
.rx-page   { width: 100%; max-width: var(--rx-content-max); margin: 0 auto;
             --rx-page-title: var(--rx-title); }
.rx-page--form   { max-width: var(--rx-form-max);   --rx-page-title: 24px; }
.rx-page--canvas { max-width: var(--rx-canvas-max); --rx-page-title: 26px; }

.rx-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: var(--rx-gap);
  align-items: start;
}
.rx-grid > .rx-col-span { grid-column: 1 / -1; }

@media (max-width: 720px) {
  .rx-grid { grid-template-columns: 1fr; }
}
