/* =============================================================================
 *  GroundDeck - design tokens
 *
 *  The house style lives HERE and nowhere else. A component references a token;
 *  it never writes a literal colour, a raw pixel font size, or its own shadow.
 *  If you need a value that is not in this file, add it here first.
 *
 *  Three scopes, in this order, and all three are required:
 *    :root                                the complete light palette
 *    @media (prefers-color-scheme: dark)  the OS setting
 *    :root[data-theme="dark"|"light"]     the in-app toggle, which wins
 *
 *  Never give a colour its only definition inside a media query: a token that
 *  exists only in dark mode is undefined in light mode, and the browser falls
 *  back to transparent rather than to something sensible.
 *
 *  Sources:
 *    - mark, ink, paper, blue    housestyle/GroundDeck Logo.dc.html
 *    - chart palette             computed and validated - see CLAUDE-Frontend.md.
 *      The raw house accents FAIL colour-blind separation as a chart series
 *      set; the --chart-* values below are the re-stepped, validated
 *      replacements. Do not substitute the raw accents into a chart.
 *
 *  Sizing. GroundDeck is used on wide and ultrawide monitors, on a 1440-wide
 *  laptop lid and on a phone, so the type scale starts at a 16px body and
 *  every size, space, control height and layout width is in rem. That is not a
 *  style preference: the text-size setting (lib/theme.tsx stamps
 *  data-text-size on <html>, global.css turns it into the root font size)
 *  scales the whole product only because nothing here is in px. Radii and
 *  hairlines stay in px - they do not need to grow with the text.
 *
 *  The body does not move. --text-2xs through --text-base are the same at
 *  every width and in every band; what came down a notch in the 2026-09
 *  responsive work is the LOUD half of the scale (--text-md and up), the four
 *  control heights and the module header, because "everything is enormous" was
 *  never a complaint about a table cell. The Appearance setting is the way
 *  back: at "extra large" the loud steps land within a pixel and a half of the
 *  sizes this file shipped before, which is what that choice now means.
 *
 *  Frame and canvas. The product has two visual layers that must never be
 *  confused, and most of the colour families below exist to keep them apart:
 *
 *    --chrome-*    the app's OWN frame - the left sidebar and the top bar. Dark
 *                  ink in BOTH themes (in dark mode it is the darkest layer of
 *                  all), light text, a bright-blue active bar.
 *    --canvas-*    the rounded panel the open module renders into, inset in
 *                  the chrome on --surface-page. A light panel inside a dark
 *                  frame is what makes "app" versus "module" obvious.
 *    --module-*    three levels INSIDE the canvas that must not look alike:
 *    --toolbar-*   the module header (identity, primary actions), the
 *                  module's own light side panel, and the slim tinted filter
 *                  strip. None of them may borrow a --chrome-* value.
 *
 *  Every text pairing in the new families was computed, not eyeballed: body
 *  text 4.5:1 or better, glyphs, bars and field boundaries 3:1 or better, in
 *  both themes. The numbers are in the comment beside each family.
 *
 *  Responsive. This file has a second half. After the theme scopes comes a
 *  short stack of override blocks, one per viewport band, that re-state SIZES
 *  ONLY - never a colour, for exactly the reason given above. :root holds the
 *  desktop values and every block below it narrows them. The ladder those
 *  blocks use is written out above the Layout family, together with the reason
 *  a breakpoint can never be a token, and mirrored once for JS in
 *  lib/breakpoints.ts.
 * ========================================================================== */

:root {
  color-scheme: light;

  /* -- Brand constants (identical in both themes) ------------------------- */
  --gd-mark-ground: #0e1116;
  --gd-mark-bracket: #56a8f5;
  --gd-mark-square: #fbfaf8;
  /* The mark's corner on its own 64-unit grid. A brand constant, deliberately
   * NOT --radius-xl: the generic radii grow with the overhaul, the mark must
   * not change shape because a card did. */
  --gd-mark-radius: 14px;

  /* -- Surfaces ----------------------------------------------------------- */
  --surface-page: #f5f4f1;
  --surface-1: #fbfaf8;
  --surface-2: #efeeea;
  --surface-3: #e6e4de;
  --surface-inset: #f2f8fe;
  --surface-inverse: #0e1116;
  --surface-overlay: rgb(14 17 22 / 0.45);

  /* -- Ink ---------------------------------------------------------------- */
  --text-primary: #0e1116;
  --text-secondary: #40454d;
  --text-muted: #5a5f67;
  --text-faint: #7d838b;
  --text-inverse: #fbfaf8;
  /* Two inks for "on the accent", because they sit on two different things:
   *   --text-on-accent       a tick or glyph on a USER-CHOSEN colour swatch
   *                          (colour pickers). White in both themes - the
   *                          swatch colours are data, not the accent steps.
   *   --text-on-accent-fill  the label on a SOLID accent fill: a primary
   *                          button, an accent count pill, a checked box.
   *                          White here (4.6:1 on --accent, 5.5:1 on hover,
   *                          6.9:1 pressed); a dark ink in both dark blocks,
   *                          where the fill is the lighter dark-theme blue. */
  --text-on-accent: #ffffff;
  --text-on-accent-fill: #ffffff;

  /* -- Lines -------------------------------------------------------------- */
  --border-subtle: #e4e2dc;
  --border-default: #cfddea;
  --border-strong: #adc2d6;
  --border-inverse: #2a3038;

  /* -- Accent (the house blue) -------------------------------------------- */
  --accent: #1878cc;
  --accent-hover: #146bb8;
  --accent-active: #115c9e;
  --accent-bright: #56a8f5;
  --accent-tint: #f2f8fe;
  --accent-tint-strong: #dbecfd;
  --accent-ring: rgb(24 120 204 / 0.35);
  /* The accent as TEXT: links, an accent badge's word, a selected tab's
   * count. --accent above is a fill and line colour, and at 4.4:1 on a card
   * it is not AA for small text; this is one step deeper. 4.5:1 or better on
   * every surface, tint and hover ground (worst: 4.7:1 on --module-item-hover;
   * 5.7:1 on a card, 5.4:1 on the page, 5.0:1 on --accent-tint-strong). The
   * hover step goes deeper still (7.4:1 on a card). */
  --accent-text: #1265b0;
  --accent-text-hover: #0d5494;

  /* -- Status. Always paired with an icon or a word, never colour alone. --- */
  --status-good: #0e8837;
  --status-good-tint: #e9fceb;
  --status-good-border: #a8e3b8;
  --status-warn: #a56500;
  --status-warn-tint: #fff4e8;
  --status-warn-border: #f0cf9a;
  --status-critical: #c04945;
  --status-critical-tint: #fff2f1;
  --status-critical-border: #f2b8b5;
  --status-info: #1878cc;
  --status-info-tint: #eff7ff;
  --status-info-border: #b7d8f5;
  --status-neutral: #5a5f67;
  --status-neutral-tint: #efeeea;
  --status-neutral-border: #d6d4ce;

  /* -- Chrome: the app's own frame (sidebar + top bar). Dark in BOTH themes.
   * Light-theme ratios: text 15.7:1 on the ground, 13.3:1 on hover, 11.1:1 on
   * the active fill; muted 8.7 / 7.4 / 6.2; faint 5.8:1 (section labels are
   * real text, so faint is still AA); the active bar 6.9:1 on the ground and
   * 4.8:1 against its own fill; badge count 7.5:1; the search field's border
   * 3.1:1 against the frame (WCAG 1.4.11); focus ring 8.6:1.
   * One step lighter than --gd-mark-ground, so the ink mark's tile still
   * reads on it. */
  --chrome-bg: #151b23;
  --chrome-bg-raised: #212933;
  --chrome-bg-active: #1d3752;
  --chrome-border: #2b343f;
  --chrome-text: #f2f4f7;
  --chrome-text-muted: #b1b9c4;
  --chrome-text-faint: #8d96a2;
  --chrome-accent: #56a8f5;
  --chrome-badge-bg: #56a8f5;
  --chrome-badge-text: #07111c;
  --chrome-input-bg: #1e252e;
  --chrome-input-border: #5f6a78;
  --chrome-focus-ring: #7bbcf8;

  /* -- Canvas: the rounded panel a module renders into, inset in the chrome.
   * In light mode the paper panel on the ink frame needs no hairline; the
   * token still exists (transparent) so a 1px border keeps the same box in
   * both themes. */
  --canvas-border: transparent;
  --canvas-shadow: 0 0 0 1px rgb(0 0 0 / 0.3), 0 4px 16px rgb(0 0 0 / 0.25);

  /* -- Module: the three levels inside the canvas.
   *   header    --module-header-*   identity + primary actions (paper)
   *   side panel --module-rail-* / --module-item-*   a LIGHT panel, nothing
   *             like the dark chrome sidebar; the active item is an accent
   *             tint with deep-blue text (6.3:1)
   *   toolbar   --toolbar-*         a cool tinted strip for filters, visibly
   *             subordinate to the header (secondary text 8.3:1, muted 5.5:1)
   * --module-icon-*: the header's icon tile, glyph 4.8:1 on its ground. */
  --module-header-bg: #fbfaf8;
  --module-header-border: #e1dfd8;
  --module-rail-bg: #efeee9;
  --module-rail-border: #dfddd6;
  --module-item-hover: #e5e3dd;
  --module-item-active-bg: #d9ebfc;
  --module-item-active-text: #0d5494;
  --module-icon-bg: #dbecfd;
  --module-icon-fg: #1567b0;
  --toolbar-bg: #e9eef4;
  --toolbar-border: #d2dbe5;

  /* -- Mail direction. Received, sent and team-only notes must be told apart
   * at a glance - and never by colour alone: the mail views pair these with
   * the arrow-down-left / arrow-up-right / note glyphs, the words "Received",
   * "Sent", "Internal note", and alignment. Inbound is a calm steel, NOT a
   * status colour (a customer writing to us is not a warning). Outbound is the
   * house blue one step deeper than --accent, so the label stays AA on its
   * tint. Ink on its own tint: inbound 6.0:1, outbound 4.8:1, note 6.0:1; body
   * text 16.6:1 or better, muted meta 5.6:1 or better on every tint. */
  --mail-inbound: #4a5d70;
  --mail-inbound-tint: #eef1f5;
  --mail-inbound-border: #c3cdd8;
  --mail-outbound: #146bb8;
  --mail-outbound-tint: #e8f2fd;
  --mail-outbound-border: #a6cbf0;
  --mail-note: #835200;
  --mail-note-tint: #fff4dc;
  --mail-note-border: #ecc983;

  /* -- CRM: the lead tag and the Claude family. The two colours the sales
   * CRM adds, each an ink, its tint and an outline. LEAD is the one type
   * tag that is neither neutral (Prospect) nor good (Client): a purple that
   * is no status. CLAUDE is a terracotta that marks what Claude did or will
   * do - its buttons, "Waiting for Claude", "via Claude", the queue badge -
   * and nothing else. Never colour alone: the tag carries its word, a Claude
   * mark its sparkle glyph and its words. Measured in light: lead ink 5.7:1
   * on its tint and 6.2:1 on a card, its outline 3.4:1 on the tint, 3.7:1 on
   * a card, 3.5:1 on the page; Claude ink 5.0:1 on its tint and 5.3:1 on a
   * card, its outline 3.3:1 / 3.6:1 / 3.4:1. */
  --crm-lead: #6b46c1;
  --crm-lead-tint: #f4f0fc;
  --crm-lead-border: #8f72d0;
  --crm-claude: #a64f27;
  --crm-claude-tint: #fbf1ea;
  --crm-claude-border: #c26f43;

  /* -- File kinds: the colour of a file-type glyph. Always drawn WITH the
   * glyph and the type word (never colour alone). Every one is 4.5:1 or
   * better on --surface-1 and 4.0:1 or better on a 14% tint of itself, so an
   * icon tile may tint its ground from the same token. */
  --file-pdf: #c0362f;
  --file-image: #0b7a86;
  --file-doc: #1d63c4;
  --file-sheet: #17783a;
  --file-slides: #b05406;
  --file-archive: #7c5a36;
  --file-media: #8a44b8;
  --file-code: #4a50bd;
  --file-text: #44576b;
  --file-other: #686e77;

  /* -- Drop zones: where a dragged file may land. The (dashed) border is 4.1:1
   * on the zone and on the canvas. */
  --dropzone-bg: #eaf3fd;
  --dropzone-border: #1878cc;

  /* -- Code: the text viewer and editor of Files (pages/files/viewer/). The
   * syntax inks are 4.5:1 or better on --code-bg in both themes, and none is
   * the only thing that says what a token is - the text itself does; colour
   * only helps the eye. The line numbers are 4.5:1 on their gutter.
   *   --code-selection / --code-line  the selection and the caret's line,
   *                  thin tints laid under the text.
   *   --preview-paper  the ground an HTML file is rendered on: white in BOTH
   *                  themes, because a web page is drawn for white unless it
   *                  says otherwise - dark mode must not invent a design.
   *   --checker-*    the two squares behind a picture's transparent parts. */
  --code-bg: #fbfaf8;
  --code-text: #0e1116;
  --code-gutter-bg: #f1efeb;
  --code-gutter-text: #5f6670;
  --code-keyword: #b3261e;
  --code-string: #0a3069;
  --code-number: #0550ae;
  --code-comment: #5f6670;
  --code-function: #7a3fc9;
  --code-type: #8c4a00;
  --code-tag: #116329;
  --code-attr: #0550ae;
  --code-punctuation: #40454d;
  --code-heading: #0a3069;
  --code-inserted: #116329;
  --code-deleted: #9c1c24;
  --code-selection: rgb(24 120 204 / 0.24);
  --code-line: rgb(14 17 22 / 0.04);
  --preview-paper: #ffffff;
  --checker-a: #ffffff;
  --checker-b: #e6e4de;

  /* -- Chart: categorical. Fixed order, NEVER cycled. Cap 6, then "Other".
   * Validated (adjacent pairlist) on surface #FBFAF8: lightness band, chroma
   * floor, normal-vision separation and 3:1 contrast all pass. The worst
   * adjacent colour-blind separation sits in the 6-8 band, which is legal ONLY
   * with secondary encoding - so any chart using 5+ slots MUST also carry a
   * legend and direct labels or 2px gaps between fills. Hard requirement.
   * Bars, stacked bars, lines and areas only; not scatter (all-pairs fails).
   */
  --chart-1: #0065b2;
  --chart-2: #8c5500;
  --chart-3: #7847a8;
  --chart-4: #01782e;
  --chart-5: #e16760;
  --chart-6: #4758b9;
  --chart-other: #7d838b;

  /* -- Chart: sequential (magnitude). One hue, light to dark. ------------- */
  --chart-seq-1: #89b5e6;
  --chart-seq-2: #65a0de;
  --chart-seq-3: #3f8ad4;
  --chart-seq-4: #1572c3;
  --chart-seq-5: #00599f;

  /* -- Chart: chrome ------------------------------------------------------ */
  --chart-surface: var(--surface-1);
  --chart-grid: #e4e2dc;
  --chart-axis: #c4c2bc;
  --chart-gap: var(--surface-1); /* the 2px spacer between adjacent fills */

  /* -- Elevation ---------------------------------------------------------- */
  --shadow-xs: 0 1px 2px rgb(14 17 22 / 0.05);
  --shadow-sm: 0 1px 3px rgb(14 17 22 / 0.08), 0 1px 2px rgb(14 17 22 / 0.04);
  --shadow-md: 0 4px 12px rgb(14 17 22 / 0.09), 0 1px 3px rgb(14 17 22 / 0.05);
  --shadow-lg: 0 12px 32px rgb(14 17 22 / 0.13), 0 2px 8px rgb(14 17 22 / 0.06);
  --shadow-drag: 0 16px 36px rgb(14 17 22 / 0.22);

  /* -- Typography --------------------------------------------------------- */
  --font-sans: "Archivo", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
    Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo,
    Consolas, "Liberation Mono", monospace;

  /* px values are at the standard text size; the setting scales them all.
   *
   * The first four are FROZEN: they are the same number at every band, and
   * --text-base stays 1rem on purpose. Every Input, Select, Textarea,
   * SearchInput and DateRangeControl computes its size from it, and below 16px
   * mobile Safari zooms the page when a field takes focus and never zooms back
   * - and index.html deliberately carries no maximum-scale to stop it, because
   * that would cost pinch zoom. --text-xs is frozen for a second reason:
   * components/charts/useChartFontSize.ts mirrors it by hand as
   * CHART_LABEL_REM, and every chart's axis margins and label strides derive
   * from that one number.
   *
   * The five loud steps DO move, in the MD and SM blocks at the end of the
   * file. They are the module titles, stat values and hero numbers - the sizes
   * that make a phone feel shouted at and a laptop feel cramped. */
  --text-2xs: 0.75rem;    /* 12px - tiny counters inside pills, nothing else */
  --text-xs: 0.8125rem;   /* 13px - captions, meta */
  --text-sm: 0.875rem;    /* 14px - secondary lines, table headers */
  --text-base: 1rem;      /* 16px - body, table cells, inputs: THE default */
  --text-md: 1.0625rem;   /* 17px - list primary lines, card titles */
  --text-lg: 1.1875rem;   /* 19px - section headings, dialog titles */
  --text-xl: 1.4375rem;   /* 23px - module titles */
  --text-2xl: 1.75rem;    /* 28px - stat values, big titles */
  --text-3xl: 2.25rem;    /* 36px - hero numbers */

  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  --weight-extrabold: 800; /* big titles and hero numbers only */

  --leading-tight: 1.2;
  --leading-snug: 1.35;
  --leading-normal: 1.55;
  --leading-relaxed: 1.7; /* long-form reading: points on a timeline, mail bodies */

  --tracking-tight: -0.011em;
  --tracking-wide: 0.02em;
  --tracking-caps: 0.06em;

  /* -- Space. A 4px base; use the step, not the pixel. The ladder is complete
   * rather than trimmed to what is in use today: a rung nobody has reached
   * for yet (14, 20, 24) is what keeps the next big gap on the grid instead
   * of being invented in a component. -------------------------------------- */
  --space-0: 0;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-7: 1.75rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;
  --space-14: 3.5rem;
  --space-16: 4rem;
  --space-20: 5rem;
  --space-24: 6rem;

  /* -- Control heights. Buttons, inputs, selects, segmented controls and
   * tabs size from these, never from a px height: sm pairs with --text-sm,
   * md (the default) with --text-base, lg with --text-md.
   *
   * One notch down from what the product shipped before, which is most of the
   * "clumsy buttons" fix and costs no component edit at all: every `size` prop
   * in the kit resolves through these four names. There is deliberately no
   * width step-up - touch is a POINTER question, never a width one, so the
   * floors live in the (pointer: coarse) block at the end of the file. A
   * 1440px touch laptop needs 44px targets and a 700px mouse-driven window
   * does not. */
  --control-sm: 1.875rem;  /* 30px */
  --control-md: 2.25rem;   /* 36px */
  --control-lg: 2.75rem;   /* 44px */
  --control-xl: 3.25rem;   /* 52px */

  /* -- Touch. The minimum hit area on a coarse pointer, for anything that is
   * not a --control-* sized control: a page number, a chip's clear button, a
   * switch track, a checkbox, a sort button, a colour swatch. Nothing in this
   * file expressed one before - --control-sm, the smallest thing available,
   * is 14px short. Applied as a min-width / min-height floor inside ONE
   * @media (pointer: coarse) block per file, with the PAINTED glyph kept at
   * its own size: grow the box with padding or an ::after inset, never by
   * growing the ink, or a chip strip gets taller on a mouse-driven screen. */
  --tap-min: 2.75rem;                 /* 44px */

  /* -- Chrome rows. Three different heights sit on one top bar today
   * (GlobalSearch .trigger 2.75rem, ChromeButton --control-md, TimerWidget
   * .running and UserMenu .trigger 3rem) and nothing lines up. Two names, and
   * every chrome control reads one of them. */
  --chrome-row: 2.5rem;               /* 40px - nav rows, search trigger, chrome buttons */
  --chrome-pill: 2.75rem;             /* 44px - the timer pill, the user menu trigger */

  /* -- Row heights. Table .td hard-codes 3.25rem and Skeleton .cell keeps a
   * second copy of the same literal; FileTable uses 3.75rem, AttachmentsPanel
   * 4rem, FileGrid .folder 4.5rem and FilePickerDialog 3.5rem - six
   * hand-picked heights for one idea, which cannot move together. */
  --row-height: 3rem;                 /* 48px, down from 3.25rem */
  --row-height-dense: var(--control-md);
  --row-height-tall: 3.5rem;          /* a row carrying a thumbnail or a tile */

  /* -- Icon boxes, for CSS that sizes a glyph or the square it sits in. The
   * <Icon> component takes the same four named sizes; --icon-2xs is CSS only,
   * for the glyphs that are part of another control rather than an icon in
   * their own right - a sort caret, a crumb separator, a chevron in a small
   * Select, a checkbox tick. There is no --icon-xs, and there must not be: at
   * 1.125rem it would be --icon-md under a second name, which is how a scale
   * rots. --icon-sm holds at 16px because it sits inside --text-2xs pills. */
  --icon-2xs: 0.875rem;  /* 14px - a glyph inside another control */
  --icon-sm: 1rem;       /* 16px - dense rows, inside pills */
  --icon-md: 1.125rem;   /* 18px - beside body text; the Icon default */
  --icon-lg: 1.5rem;     /* 24px - large buttons, section headers */
  --icon-xl: 1.75rem;    /* 28px - tiles, empty states */

  /* -- Radii. px on purpose: a corner does not need to grow with the text. - */
  --radius-xs: 4px;
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --radius-xl: 18px;
  --radius-pill: 999px;

  /* =========================================================================
   *  BREAKPOINTS - written down here, but NOT tokens, and they cannot be.
   *
   *  CSS does not resolve var() inside an @media or @container condition:
   *  `@media (max-width: var(--bp-md))` silently never matches. There is no
   *  @custom-media either - nothing PostCSS is installed (no config file
   *  anywhere in the repo, no lightningcss, no browserslist), so it would be
   *  emitted verbatim and every query using it would be dead CSS. So the
   *  ladder is literal px, written out in full in each file, recorded once
   *  here and mirrored once in client/src/lib/breakpoints.ts for the four
   *  useMediaQuery call sites.
   *
   *  px is DELIBERATE. A px media query does not scale with the root font
   *  size, so the frame switches at a real device width whatever
   *  data-text-size says. Never rewrite one in em. And never write a width
   *  query in rem: rem in a MEDIA condition resolves against the browser's
   *  INITIAL font size, not the root size global.css sets, so it silently
   *  ignores the text-size preference altogether.
   *
   *    XS   (max-width:  599px)  phone portrait
   *    SM   (max-width:  767px)  phone landscape - the sidebar is a drawer
   *    MD   (max-width: 1023px)  tablet - the sidebar is a rail, module rail folds
   *    LG   (max-width: 1279px)  small laptop
   *    XL   (max-width: 1599px)  laptop / a 1440-wide window
   *    2XL   (base CSS)          desktop 1600-2399 - the sidebar is EXPANDED
   *    UW   (min-width: 2400px)  1440p and ultrawide
   *    (pointer: coarse)         touch - NOT a width. Last block in this file,
   *                              so its control-height floors beat SM and XS.
   *
   *  Inside the canvas nothing uses these. A component's own box is what
   *  matters there, because the canvas width depends on the chrome sidebar's
   *  collapsed state, which is a per-browser localStorage preference: at a
   *  1280px window the content column is 1152px with a rail and 972px with an
   *  expanded sidebar, and no viewport query can see that 180px.
   *
   *  Use @container, with one of the four names - gd-module (the module band),
   *  gd-content (the page's content column), gd-surface (any box inside it),
   *  gd-pane (a pane that owns its height) - and one of the seven rungs:
   *
   *    24rem  32rem  44rem  56rem  76rem  96rem  120rem
   *
   *  Container-query rem DOES resolve against the root font size, so those
   *  rungs follow the text-size setting - which is correct, and is the exact
   *  opposite of media-query rem. Never write an anonymous @container: all
   *  four names are global (CSS Modules does not scope container-name) and an
   *  anonymous query binds to whichever container happens to be nearest.
   *  Adding an eighth rung or a fifth name is a bug: widen an existing one.
   * ======================================================================= */

  /* -- Layout ------------------------------------------------------------- */

  /* -- Safe areas. index.html has always shipped viewport-fit=cover, and until
   * the 2026-09 overhaul nothing in the client read it back: on a notched
   * phone the nav drawer, the top bar's controls and the canvas edge ran
   * under the cutout in landscape and under the home indicator in portrait.
   * These are LENGTHS, not type or colour, so env() is safe here even though
   * nothing else in this file is a browser function.
   * They are consumed only where a box meets a window edge: --safe-right and
   * --safe-left in Shell .canvas's margins, the phone's PhoneBar and Toast
   * .viewport; --safe-bottom in Shell .canvas's block-end margin and in the
   * boxes that sit on the bottom edge - the sidebar drawer, Drawer, Modal and
   * Toast; --safe-top only on a phone, where there is no top bar, so the
   * canvas's top margin, the PhoneBar and the sidebar drawer meet the status
   * bar themselves (non-zero there in a standalone or fullscreen window). */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);

  /* -- The frame ---------------------------------------------------------- */
  --sidebar-width: 15.5rem;            /* the chrome sidebar, expanded */
  --sidebar-width-collapsed: 4.25rem;  /* the chrome sidebar as a rail */
  /* -- The chrome sidebar as an off-canvas drawer, below 768px. The rem arm
   * still follows the text-size preference; the vw arm is a WIDTH, not type,
   * so it is legal here and does not touch the type chain. The scrim behind
   * it is NOT a new token: it is --surface-overlay, which is already the
   * Modal backdrop and already the right ink in both themes. */
  --sidebar-drawer-width: min(var(--sidebar-width), 86vw);
  --topbar-height: 3.5rem;             /* the chrome top bar */
  --canvas-gap: 0.625rem;              /* chrome visible around the canvas */
  --canvas-radius: 1rem;               /* the canvas's corner */
  --module-header-height: 4.5rem;      /* ModuleHeader minimum, without tabs */
  --module-sidebar-width: 17rem;       /* a module's own left panel; it steps
                                        * down twice below 1600 and leaves the
                                        * flow altogether below 1024 */
  /* -- The inline padding of the module header row and the content column.
   * Was three literals inside ModuleLayout's own width queries; it lives here
   * so a page can reason about the gutter without reading ModuleLayout's
   * stylesheet, and so ModuleLayout carries two fewer queries. */
  --module-gutter: var(--space-8);

  /* -- Page measures ------------------------------------------------------ */
  /* The cap on a default (not `fill`) ModuleLayout page without a module
   * sidebar, header row included (beside a sidebar nothing is capped).
   * 120rem = 1920px, and the UW band widens it to 132rem = 2112px. The old
   * 160rem never engaged: at 2560 the content column is about 2238px, UNDER
   * the cap, so a 1440p monitor was not capped at all and a feed row ran the
   * whole screen. This number and the dashboard's container rungs are ONE
   * decision - tightening it here without re-pitching those makes them
   * unreachable, and dead CSS does not warn. Pages that read as one column
   * (a profile, a form) set a narrower cap of their own inside it. */
  --content-max: 120rem;
  --reading-max: 50rem;                /* one column of long text */
  --document-max: 72rem;               /* a page that reads as one document */
  --pane-max: 72rem;                   /* a pane that caps itself beside an
                                        * uncapped sibling */
  --preview-max: 100rem;               /* a window ON a file, not the screen */
  --reading-max-mono: 100ch;           /* a code column - characters, not rem */

  /* -- Mail measures, derived rather than chosen, so a mail body is never a
   * wider measure than a long text column. --mail-gutter is ONE inherited inline
   * padding, set on .workspace and read by ReadingPane .head, Conversation
   * .lane, MessageItem .content/.paper and InlineComposer - replacing the
   * padding literals that six email files each redeclare in their own block. */
  --mail-list-width: 21rem;            /* the slim list beside an open conversation */
  --mail-card-max: calc(var(--reading-max) + 2 * var(--space-8));   /* ~54rem */
  --mail-lane-max: 64rem;
  --mail-gutter: var(--space-6);

  /* -- Overlays. The margin every PORTALLED box clamps itself by, and the two
   * maxima it clamps, so DropdownMenu, Tooltip, ChartTooltip, Toast and Modal
   * all write one clamp from one number:
   *   max-width: min(var(--menu-max), calc(100dvw - 2 * var(--overlay-inset)))
   * 100dvw, not 100vw: on Windows - this team's platform - 100vw includes the
   * classic scrollbar, so calc(100vw - 32px) can exceed the visible width and
   * push a horizontal scrollbar onto the page. */
  --overlay-inset: var(--space-4);
  --menu-max: 24rem;
  --menu-min: 14rem;
  --tooltip-max: 20rem;
  --drawer-sm: 28rem;
  --drawer-md: 40rem;
  --drawer-lg: 52rem;
  --drawer-xl: 60rem;
  --modal-sm: 30rem;
  --modal-md: 42rem;
  --modal-lg: 56rem;
  --modal-xl: 70rem;

  /* -- Grid tracks. ALWAYS written as
   *   repeat(auto-fill, minmax(min(100%, var(--x-min)), 1fr))
   * so the overflow guard travels with the value. A bare minmax(Nrem, 1fr) is
   * a HARD minimum: the track cannot shrink below it, so it overflows its
   * container - and because ModuleLayout .content is overflow-x: hidden, the
   * overflow is not even scrollable. It is silently clipped, which is why a
   * screenshot at 360px looks plausible while content is being lost. */
  --grid-track-min: 15rem;            /* the generic smallest readable card */
  --folder-card-min: 16rem;
  --file-card-min: 13.5rem;           /* FileGrid AND its skeleton, one number */
  --document-card-min: 13rem;         /* a Documents home card: the first page, drawn small */
  --template-tile-min: 11rem;         /* a template in the library or the New document dialog */
  --project-card-min: 21rem;
  --scrum-tile-min: 22rem;
  --claude-card-min: 22rem;           /* a Claude session card: prompt, progress, activity */
  --claude-panel-width: 28rem;        /* the session view's panel: agents, progress, files changed, model */
  --board-column-min: 18rem;
  --board-column-max: 28rem;          /* was 40rem: a 612px kanban column on a
                                       * 3440 screen is not glanceable */
  --board-carry-width: 15rem;         /* a card carried on a one-column board:
                                       * narrower than its column, so it stays
                                       * on screen under a finger at the edge
                                       * (scrum/carry.ts) */
  --card-list-min: 3rem;              /* BoardColumn .list, was a literal 6rem -
                                       * the direct cause of the quick-add form
                                       * being clipped on a phone */

  /* -- Stat tiles. A floor AND a ceiling. StatRow's auto-fit track has a floor
   * but no ceiling, so four tiles stretch to ~545px at 2560 and ~800px at
   * 3440, past StatTile's own value step. --stat-track is what the grid reads
   * and --stat-min is what a caller may override: StatRow.tsx sets --stat-min
   * as an INLINE STYLE, which beats the class-rule override in its own
   * stylesheet and makes that override dead for every caller that passes the
   * prop. The indirection is required, not decorative. */
  --stat-min: 15rem;
  --stat-max: 22rem;                  /* 24rem in the UW band */
  --stat-track: var(--stat-min);

  /* -- Chart geometry. An svg is drawn at an exact pixel height and its
   * viewBox is in the same units, so a chart cannot read a CSS length: handed
   * a rem it would SCALE - stretched type, half-pixel gridlines - rather than
   * be given more room. These four rungs are therefore MIRRORED BY HAND in
   * components/charts/ChartFrame.tsx (CHART_HEIGHT_REM, CHART_MIN_PLOT_REM),
   * which turns them into pixels from the root size, and pages/dashboard/
   * TrendBand.tsx keeps its own 20rem band the same way. Move a rung here and
   * move the constant in the same change, the way --text-xs and
   * CHART_LABEL_REM are already paired. They sit here rather than beside the
   * --chart-* colours because they are lengths: the theme scopes below
   * re-state colour and nothing else, and these must not be copied into
   * them. */
  --chart-height-sm: 14rem;
  --chart-height-md: 17.5rem;
  --chart-height-lg: 24rem;
  --chart-min-plot: 22rem;            /* below this MEASURED plot width a chart
                                       * drops its end labels; at <= 0 it falls
                                       * back to the table twin it already owns */

  /* -- Repeated literals, named once -------------------------------------- */
  --status-disc: 2.25rem;             /* the tinted disc beside one sentence:
                                       * three identical 2.5rem copies in
                                       * InlineError, ErrorBoundary, ConfirmDialog */
  --control-inset: 0.375rem;          /* SegmentedControl's four
                                       * `calc(var(--control-*) - 6px)` and
                                       * ChartFrame's fifth, all in px, so at
                                       * data-text-size="xlarge" the trough grows
                                       * 12.5% and the inset does not */
  --count-pill-h: 1.375rem;
  --count-pill-min-w: 1.5rem;
  --track-tile: 7rem;                 /* DayView .track and .dayLink, two 7.5rem
                                       * copies that drift independently */
  --severity-col: 7rem;               /* AttentionPanel, three literals for one
                                       * mostly-blank column */
  --feed-client-col: 12rem;           /* ActivityFeed and AttentionPanel, the same
                                       * 13rem written twice */
  --card-min-height: 13rem;           /* ProjectCard, was 15rem */
  --crm-table-min: 60rem;             /* one number for a table's scroll floor AND
                                       * the container width at which its card form
                                       * takes over, so the two cannot disagree */
  --crm-prospect-col-min: 8rem;       /* the room one column of the Prospects & leads
                                       * list is given (the company takes two, the
                                       * selection box one): its scroll floor and the
                                       * width at which it sheds a column. Mirrored as
                                       * COLUMN_MIN_REM in pages/crm/prospects/columns.ts */

  /* -- Motion. Respect prefers-reduced-motion in the component. ----------- */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --duration-fast: 110ms;
  --duration-base: 180ms;
  --duration-slow: 280ms;

  /* -- Z-index. One ladder; never write a literal z-index. ----------------
   * Everything that portals lands as a child of <body>, so nothing about
   * being "inside" a dialog helps it: siblings are ordered by this ladder
   * alone. --z-dropdown is for a floating box in the page, under the chrome's
   * overlays; --z-popover is the SAME box when it is opened from inside a
   * Drawer or a Modal, which is now an ordinary case rather than a rare one
   * (a dialog is a sheet on a phone and a toolbar folds into a Drawer, so the
   * pickers went with them). A menu at --z-dropdown opened from a sheet
   * renders behind it. --z-toast stays at the top of the ladder: a toast
   * reports what just happened and must cover a popover, never the reverse. */
  --z-base: 0;
  --z-sticky: 10;
  --z-dropdown: 100;
  --z-drag: 200;
  --z-overlay: 300;
  --z-modal: 310;
  --z-popover: 320;
  --z-toast: 400;
}

/* =============================================================================
 *  Dark - the OS setting.
 *  The :not([data-theme="light"]) guard lets an explicit light choice in the
 *  app beat OS dark.
 *
 *  The surfaces are one step LIGHTER than before the 2026-09 overhaul, and the
 *  chrome is the darkest layer of all: frame #07090c, canvas #14181e, cards
 *  #1a1f26, plus a --canvas-border hairline. Without the lift the canvas and
 *  the frame melt into one black. Borders, --text-faint and the neutral tint
 *  moved up with the surfaces so their ratios hold (muted text 5.4:1 on a
 *  card, faint 3.8:1).
 *
 *  The accent. The dark fill (#3f97ee and its lighter hover/pressed steps) is
 *  too light for white: 3.1 / 2.5 / 2.2:1. So --text-on-accent-fill turns to
 *  the chrome's badge ink here: 6.2 / 7.5 / 8.7:1 on the three steps. Accent
 *  TEXT is the bright house blue, --accent-text 6.6:1 on a card and 5.2:1 or
 *  better on every dark surface and tint (worst --accent-tint-strong), its
 *  hover 9.0:1. --text-on-accent stays white: it is for ticks on
 *  user-chosen swatches, which do not follow the theme.
 *
 *  Charts: no --chart-* value changed. --chart-surface follows --surface-1, so
 *  the dark set now paints on #1a1f26; every categorical slot was re-checked
 *  for 3:1 against it (worst: --chart-other 3.36:1, --chart-5 3.67:1).
 * ========================================================================== */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --surface-page: #14181e;
    --surface-1: #1a1f26;
    --surface-2: #20262e;
    --surface-3: #28303a;
    --surface-inset: #16212c;
    --surface-inverse: #fbfaf8;
    --surface-overlay: rgb(0 0 0 / 0.6);

    --text-primary: #f2f3f5;
    --text-secondary: #bcc2ca;
    --text-muted: #8e949c;
    --text-faint: #737a82;
    --text-inverse: #0e1116;
    --text-on-accent: #ffffff;
    --text-on-accent-fill: #07111c;

    --border-subtle: #252c35;
    --border-default: #323a45;
    --border-strong: #454e5a;
    --border-inverse: #cfddea;

    --accent: #3f97ee;
    --accent-hover: #56a8f5;
    --accent-active: #6fb5f7;
    --accent-bright: #7bbcf8;
    --accent-tint: #10243a;
    --accent-tint-strong: #143253;
    --accent-ring: rgb(86 168 245 / 0.45);
    --accent-text: #56a8f5;
    --accent-text-hover: #8cc4fa;

    --status-good: #5fba6f;
    --status-good-tint: #10261a;
    --status-good-border: #2c5c38;
    --status-warn: #db9031;
    --status-warn-tint: #2a1e0c;
    --status-warn-border: #5e451a;
    --status-critical: #ed7d76;
    --status-critical-tint: #2c1516;
    --status-critical-border: #63302f;
    --status-info: #56a8f5;
    --status-info-tint: #102436;
    --status-info-border: #2a4a68;
    --status-neutral: #8e949c;
    --status-neutral-tint: #232931;
    --status-neutral-border: #3a424d;

    /* Chrome: the darkest layer. Text 18.1:1, muted 9.2:1 (6.6:1 on the
     * active fill), faint 5.8:1, field border 3.2:1, badge 7.5:1. */
    --chrome-bg: #07090c;
    --chrome-bg-raised: #151a21;
    --chrome-bg-active: #132b44;
    --chrome-border: #1c222a;
    --chrome-text: #f2f4f7;
    --chrome-text-muted: #a8b1bd;
    --chrome-text-faint: #838c98;
    --chrome-accent: #56a8f5;
    --chrome-badge-bg: #56a8f5;
    --chrome-badge-text: #07111c;
    --chrome-input-bg: #12171d;
    --chrome-input-border: #58636f;
    --chrome-focus-ring: #7bbcf8;

    --canvas-border: #2c343f;
    --canvas-shadow: 0 0 0 1px rgb(0 0 0 / 0.6);

    /* Module: active item 8.0:1, icon tile glyph 7.0:1, toolbar muted 5.0:1. */
    --module-header-bg: #1a1f26;
    --module-header-border: #2b323c;
    --module-rail-bg: #191d24;
    --module-rail-border: #2a313a;
    --module-item-hover: #232a33;
    --module-item-active-bg: #163250;
    --module-item-active-text: #a3d0fb;
    --module-icon-bg: #163250;
    --module-icon-fg: #8cc4fa;
    --toolbar-bg: #1e252e;
    --toolbar-border: #303945;

    /* Mail: ink on its tint 7.4 / 7.6 / 8.0:1; muted meta 5.0:1 or better. */
    --mail-inbound: #a3b3c4;
    --mail-inbound-tint: #1c232c;
    --mail-inbound-border: #3b4756;
    --mail-outbound: #7dbcf8;
    --mail-outbound-tint: #12263b;
    --mail-outbound-border: #28537c;
    --mail-note: #e9ac52;
    --mail-note-tint: #29200f;
    --mail-note-border: #634b1c;

    /* CRM lead and Claude: lead ink 7.1:1 on its tint and 7.2:1 on a card,
       outline 3.4:1 on both; Claude ink 7.4:1 / 7.3:1, outline 3.7:1 / 3.6:1. */
    --crm-lead: #b69cf5;
    --crm-lead-tint: #221a36;
    --crm-lead-border: #7a62b8;
    --crm-claude: #e89a72;
    --crm-claude-tint: #2a1a12;
    --crm-claude-border: #a8653f;

    /* File kinds: 6.3:1 or better on a card, 4.6:1 or better on an 18% tint. */
    --file-pdf: #f4847c;
    --file-image: #48c3cf;
    --file-doc: #71acf6;
    --file-sheet: #5cc57d;
    --file-slides: #f2a45f;
    --file-archive: #cfa77d;
    --file-media: #c790f1;
    --file-code: #9da5f6;
    --file-text: #a9b7c7;
    --file-other: #9ba1a9;

    --dropzone-bg: #10263d;
    --dropzone-border: #56a8f5;

    --code-bg: #161b21;
    --code-text: #f2f3f5;
    --code-gutter-bg: #1d232a;
    --code-gutter-text: #8e949c;
    --code-keyword: #ff7b72;
    --code-string: #a5d6ff;
    --code-number: #79c0ff;
    --code-comment: #8e949c;
    --code-function: #d2a8ff;
    --code-type: #ffa657;
    --code-tag: #7ee787;
    --code-attr: #79c0ff;
    --code-punctuation: #bcc2ca;
    --code-heading: #79c0ff;
    --code-inserted: #7ee787;
    --code-deleted: #ffa198;
    --code-selection: rgb(86 168 245 / 0.32);
    --code-line: rgb(255 255 255 / 0.04);
    --preview-paper: #ffffff;
    --checker-a: #2a3038;
    --checker-b: #20262e;

    /* Dark steps of the SAME six hues, re-stepped for the dark surface and
     * validated as a set against #0E1116 (and re-checked for 3:1 against the
     * lifted dark --surface-1, #1a1f26, on 2026-09-19 - every slot passes, the
     * lowest being --chart-other at 3.36:1) - not an automatic flip of the light
     * values. Same rules: fixed order, cap 6, legend + labels at 5 or more. */
    --chart-1: #3f97ee;
    --chart-2: #c68333;
    --chart-3: #aa78de;
    --chart-4: #43ad5a;
    --chart-5: #c74f4b;
    --chart-6: #748bf1;
    --chart-other: #6b7178;

    --chart-seq-1: #8bc3ff;
    --chart-seq-2: #51a3f5;
    --chart-seq-3: #2783d8;
    --chart-seq-4: #0365b1;
    --chart-seq-5: #004984;

    --chart-surface: var(--surface-1);
    --chart-grid: #232a33;
    --chart-axis: #333b45;
    --chart-gap: var(--surface-1);

    --shadow-xs: 0 1px 2px rgb(0 0 0 / 0.4);
    --shadow-sm: 0 1px 3px rgb(0 0 0 / 0.5), 0 1px 2px rgb(0 0 0 / 0.3);
    --shadow-md: 0 4px 12px rgb(0 0 0 / 0.55), 0 1px 3px rgb(0 0 0 / 0.35);
    --shadow-lg: 0 12px 32px rgb(0 0 0 / 0.6), 0 2px 8px rgb(0 0 0 / 0.4);
    --shadow-drag: 0 16px 36px rgb(0 0 0 / 0.7);
  }
}

/* =============================================================================
 *  Dark - the in-app toggle. Same values, higher specificity, so the toggle
 *  wins in both directions regardless of the OS setting. Keep this block a
 *  line-for-line copy of the one above.
 * ========================================================================== */

:root[data-theme="dark"] {
  color-scheme: dark;

  --surface-page: #14181e;
  --surface-1: #1a1f26;
  --surface-2: #20262e;
  --surface-3: #28303a;
  --surface-inset: #16212c;
  --surface-inverse: #fbfaf8;
  --surface-overlay: rgb(0 0 0 / 0.6);

  --text-primary: #f2f3f5;
  --text-secondary: #bcc2ca;
  --text-muted: #8e949c;
  --text-faint: #737a82;
  --text-inverse: #0e1116;
  --text-on-accent: #ffffff;
  --text-on-accent-fill: #07111c;

  --border-subtle: #252c35;
  --border-default: #323a45;
  --border-strong: #454e5a;
  --border-inverse: #cfddea;

  --accent: #3f97ee;
  --accent-hover: #56a8f5;
  --accent-active: #6fb5f7;
  --accent-bright: #7bbcf8;
  --accent-tint: #10243a;
  --accent-tint-strong: #143253;
  --accent-ring: rgb(86 168 245 / 0.45);
  --accent-text: #56a8f5;
  --accent-text-hover: #8cc4fa;

  --status-good: #5fba6f;
  --status-good-tint: #10261a;
  --status-good-border: #2c5c38;
  --status-warn: #db9031;
  --status-warn-tint: #2a1e0c;
  --status-warn-border: #5e451a;
  --status-critical: #ed7d76;
  --status-critical-tint: #2c1516;
  --status-critical-border: #63302f;
  --status-info: #56a8f5;
  --status-info-tint: #102436;
  --status-info-border: #2a4a68;
  --status-neutral: #8e949c;
  --status-neutral-tint: #232931;
  --status-neutral-border: #3a424d;

  --chrome-bg: #07090c;
  --chrome-bg-raised: #151a21;
  --chrome-bg-active: #132b44;
  --chrome-border: #1c222a;
  --chrome-text: #f2f4f7;
  --chrome-text-muted: #a8b1bd;
  --chrome-text-faint: #838c98;
  --chrome-accent: #56a8f5;
  --chrome-badge-bg: #56a8f5;
  --chrome-badge-text: #07111c;
  --chrome-input-bg: #12171d;
  --chrome-input-border: #58636f;
  --chrome-focus-ring: #7bbcf8;

  --canvas-border: #2c343f;
  --canvas-shadow: 0 0 0 1px rgb(0 0 0 / 0.6);

  --module-header-bg: #1a1f26;
  --module-header-border: #2b323c;
  --module-rail-bg: #191d24;
  --module-rail-border: #2a313a;
  --module-item-hover: #232a33;
  --module-item-active-bg: #163250;
  --module-item-active-text: #a3d0fb;
  --module-icon-bg: #163250;
  --module-icon-fg: #8cc4fa;
  --toolbar-bg: #1e252e;
  --toolbar-border: #303945;

  --mail-inbound: #a3b3c4;
  --mail-inbound-tint: #1c232c;
  --mail-inbound-border: #3b4756;
  --mail-outbound: #7dbcf8;
  --mail-outbound-tint: #12263b;
  --mail-outbound-border: #28537c;
  --mail-note: #e9ac52;
  --mail-note-tint: #29200f;
  --mail-note-border: #634b1c;

  --crm-lead: #b69cf5;
  --crm-lead-tint: #221a36;
  --crm-lead-border: #7a62b8;
  --crm-claude: #e89a72;
  --crm-claude-tint: #2a1a12;
  --crm-claude-border: #a8653f;

  --file-pdf: #f4847c;
  --file-image: #48c3cf;
  --file-doc: #71acf6;
  --file-sheet: #5cc57d;
  --file-slides: #f2a45f;
  --file-archive: #cfa77d;
  --file-media: #c790f1;
  --file-code: #9da5f6;
  --file-text: #a9b7c7;
  --file-other: #9ba1a9;

  --dropzone-bg: #10263d;
  --dropzone-border: #56a8f5;

  --code-bg: #161b21;
  --code-text: #f2f3f5;
  --code-gutter-bg: #1d232a;
  --code-gutter-text: #8e949c;
  --code-keyword: #ff7b72;
  --code-string: #a5d6ff;
  --code-number: #79c0ff;
  --code-comment: #8e949c;
  --code-function: #d2a8ff;
  --code-type: #ffa657;
  --code-tag: #7ee787;
  --code-attr: #79c0ff;
  --code-punctuation: #bcc2ca;
  --code-heading: #79c0ff;
  --code-inserted: #7ee787;
  --code-deleted: #ffa198;
  --code-selection: rgb(86 168 245 / 0.32);
  --code-line: rgb(255 255 255 / 0.04);
  --preview-paper: #ffffff;
  --checker-a: #2a3038;
  --checker-b: #20262e;

  --chart-1: #3f97ee;
  --chart-2: #c68333;
  --chart-3: #aa78de;
  --chart-4: #43ad5a;
  --chart-5: #c74f4b;
  --chart-6: #748bf1;
  --chart-other: #6b7178;

  --chart-seq-1: #8bc3ff;
  --chart-seq-2: #51a3f5;
  --chart-seq-3: #2783d8;
  --chart-seq-4: #0365b1;
  --chart-seq-5: #004984;

  --chart-surface: var(--surface-1);
  --chart-grid: #232a33;
  --chart-axis: #333b45;
  --chart-gap: var(--surface-1);

  --shadow-xs: 0 1px 2px rgb(0 0 0 / 0.4);
  --shadow-sm: 0 1px 3px rgb(0 0 0 / 0.5), 0 1px 2px rgb(0 0 0 / 0.3);
  --shadow-md: 0 4px 12px rgb(0 0 0 / 0.55), 0 1px 3px rgb(0 0 0 / 0.35);
  --shadow-lg: 0 12px 32px rgb(0 0 0 / 0.6), 0 2px 8px rgb(0 0 0 / 0.4);
  --shadow-drag: 0 16px 36px rgb(0 0 0 / 0.7);
}

/* The explicit light choice. Light is the :root default, so this only has to
 * restore color-scheme when the OS is dark and the viewer picked light. The
 * chrome needs nothing here: its tokens are dark in :root already. (Native
 * scrollbars and form controls follow color-scheme, not these tokens, so the
 * chrome's own elements declare `color-scheme: dark` to stay dark in both
 * themes.) */
:root[data-theme="light"] {
  color-scheme: light;
}

/* =============================================================================
 *  APPENDED by the shell (components/shell/Mark.tsx).
 *
 *  The --gd-mark-* constants at the top of this file describe ONE of the two
 *  marks in housestyle/GroundDeck Logo.dc.html: ink ground, bright blue
 *  bracket, off-white square. That file draws a second one - paper ground with
 *  a hairline edge, the deep house blue bracket and an ink square - and the
 *  brief asks the mark to invert to it in dark mode.
 *
 *  Inverting needs values that FOLLOW the theme, which the constants above
 *  deliberately do not, so the four tokens below are the same two marks written
 *  as a theme-aware set. Nothing above this line is changed. All four scopes
 *  are present, exactly as the header of this file requires.
 *
 *  These follow the theme, so they are for a mark on a THEMED surface (sign-in,
 *  splash, no-access). A mark on the chrome - dark in both themes - uses the
 *  fixed constants at the top of the file instead.
 * ========================================================================== */

:root {
  --gd-mark-face: #0e1116;
  --gd-mark-face-edge: transparent;
  --gd-mark-face-bracket: #56a8f5;
  --gd-mark-face-square: #fbfaf8;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --gd-mark-face: #f2f8fe;
    --gd-mark-face-edge: #cfddea;
    --gd-mark-face-bracket: #1878cc;
    --gd-mark-face-square: #0e1116;
  }
}

:root[data-theme="dark"] {
  --gd-mark-face: #f2f8fe;
  --gd-mark-face-edge: #cfddea;
  --gd-mark-face-bracket: #1878cc;
  --gd-mark-face-square: #0e1116;
}

:root[data-theme="light"] {
  --gd-mark-face: #0e1116;
  --gd-mark-face-edge: transparent;
  --gd-mark-face-bracket: #56a8f5;
  --gd-mark-face-square: #fbfaf8;
}

/* =============================================================================
 *  RESPONSIVE OVERRIDES
 *
 *  Sizing only - never a colour. A colour redefined inside a width query is
 *  undefined OUTSIDE it and, for a colour, var() with no fallback resolves to
 *  transparent, which would break the three-scope light / OS-dark /
 *  toggle-dark structure above. Every token here has its :root baseline in
 *  that block; a token defined only in here is undefined everywhere else.
 *
 *  Order matters: UW first (disjoint), then the max-width rungs widest to
 *  narrowest so a narrower one wins, then (pointer: coarse) LAST so the touch
 *  floors beat SM and XS. The ladder itself is documented at the top of the
 *  Layout family.
 * ========================================================================== */

/* -- UW. A 1440p or ultrawide monitor. The cap widens so the extra width is
 * not all margin: at 2560 a 132rem column is 2112px, not 1920px. */
@media (min-width: 2400px) {
  :root {
    --content-max: 132rem;
    --stat-max: 24rem;
  }
}

/* -- XL. A laptop or a 1440-wide window. The chrome sidebar is a rail from
 * here down, so the module rail and the gutter can both give a step back. */
@media (max-width: 1599px) {
  :root {
    --module-sidebar-width: 15rem;
    --module-gutter: var(--space-6);
  }
}

/* -- LG. A small laptop. */
@media (max-width: 1279px) {
  :root {
    --module-sidebar-width: 13.5rem;
    --mail-list-width: 19rem;
  }
}

/* -- MD. A tablet. The module rail leaves the flow (ModuleLayout), the loud
 * type steps come down, and the canvas inset tightens - but never to zero:
 * the light panel floating on the dark frame IS the chrome/canvas idea. */
@media (max-width: 1023px) {
  :root {
    --text-xl: 1.3125rem;
    --text-2xl: 1.5rem;
    --text-3xl: 1.875rem;

    --canvas-gap: 0.5rem;
    --canvas-radius: 0.875rem;
    --module-header-height: 4rem;
    --module-gutter: var(--space-5);
  }
}

/* -- SM. A phone in landscape, or a small tablet in portrait. The chrome
 * sidebar is an off-canvas drawer from here down and overlays are full-bleed
 * sheets, so the frame gives back most of its width. */
@media (max-width: 767px) {
  :root {
    --text-md: 1rem;
    --text-lg: 1.125rem;
    --text-xl: 1.1875rem;
    --text-2xl: 1.375rem;
    --text-3xl: 1.625rem;

    --topbar-height: 3.25rem;
    --canvas-gap: 0.25rem;
    --canvas-radius: 0.75rem;
    --module-header-height: 3.5rem;
    --module-gutter: var(--space-4);
    --mail-gutter: var(--space-3);
  }
}

/* -- XS. A phone in portrait. Nothing in the scale moves again here; XS is
 * where SHAPES change (one pane, cards instead of tables, a sheet instead of
 * a dialog) and those live in the components, not in this file. Kept as an
 * empty, documented rung rather than deleted, so the next size that genuinely
 * needs a phone-portrait value has an obvious home.
 *
 * @media (max-width: 599px) { :root { } }
 */

/* -- Touch. NOT a width, and therefore last: a 1440px touch laptop needs 44px
 * targets and a 700px mouse-driven window does not, so this must beat the SM
 * and XS blocks above rather than being folded into them. It floors the
 * control heights; --tap-min floors everything that is not a control, inside
 * one (pointer: coarse) block per component, and there it grows the HIT BOX,
 * never the painted glyph. */
@media (pointer: coarse) {
  :root {
    --control-sm: 2.25rem;
    --control-md: 2.75rem;
    --control-lg: 3rem;
    --control-xl: 3.5rem;
  }
}
