/* Tinkerex design system: the layer order, the tokens, the fonts and the base rules.
   Components live in components.css (one definition each), the shell in shell.css, page families
   under pages/, and the third-party skins in vendor-overrides.css — the ONE unlayered sheet.
   THE @layer STATEMENT MUST STAY THE FIRST RULE OF THIS FILE, AND THIS FILE THE FIRST OUR LAYOUT
   LINKS: the first @layer statement the browser meets fixes the order for the whole document.
   test/css_hygiene_test.rb guards both. Spec: docs/superpowers/specs/2026-09-03-web-shell-and-design-system-design.md */
@layer tokens, base, components, shell, pages;

@layer tokens {
  /* PAPER IS THE DEFAULT (2026-10-08 redesign): an absent or junk theme cookie renders
     data-theme="light", and :root carries the complete Paper set. Graphite redefines the SAME names
     below; a colour declared only inside a theme block never applies in the other state
     (design-system.md rule 4). Token names are the pre-redesign names on purpose, so every page
     sheet keeps working. Values: the Tinkerex Design System artifact. */
  :root {
    color-scheme: light;
    --bg: #f4f2ed;
    --surface: #fcfbf8;
    --raised: #ebe8e1;
    --line: #dcd8cf;
    --line-strong: #878276;
    --fg: #1b1c19;
    --fg-muted: #5b5c55;
    /* Text tokens (fg-muted, accent-text, ok, warn, danger, info) clear 4.5:1 on BOTH --bg and
       --surface: the rail and page heads sit on --bg, panels on --surface. */
    /* THE ACCENT IS SPENT ONCE PER SCREEN: the one primary action, and the destination it lands
       in. Selection, tabs, links and the active nav item are INK (--fg), never accent. */
    --accent: #c2410c;
    --accent-ink: #ffffff;
    --accent-text: #ad3a0b;
    --accent-soft: rgba(194, 65, 12, .12);
    --ok: #206b3a;
    --ok-soft: rgba(36, 115, 63, .14);
    --warn: #835500;
    --warn-soft: rgba(138, 90, 0, .14);
    --danger: #b42318;
    --danger-soft: rgba(180, 35, 24, .14);
    --info: #1c5d99;
    --info-soft: rgba(28, 93, 153, .14);
    --focus: var(--accent);
    --shadow-float: 0 8px 24px rgba(27, 28, 25, .14), 0 1px 2px rgba(27, 28, 25, .10);
    --shadow-toggle: 0 1px 2px rgba(27, 28, 25, .12);
    --scrim: rgba(27, 28, 25, .45);
    /* A user's PHOTO is not a render: it sits on a neutral matte, never on the paper card. */
    --photo-stage: #ebe8e1;
    /* EVERY THUMBNAIL IS INK ON PAPER (design-system.md rule 5, 2026-10-21). A render keeps a
       pure-white ground (ours, LightBurn's own, a PDF page) and is MULTIPLIED onto this card, so
       white becomes the card and ink stays ink. The card is light in BOTH themes because the ink is
       often the FILE's own colour (a black or blue laser layer), which reads on no dark ground; in
       Graphite it is a dimmed paper rather than a white square. --thumb-ink is what the app paints
       on the card itself (the type label, the spinner, the failed mark) and flips with it. The
       Measure stage is this card too: it draws the same file colours. */
    --thumb-stage: #fcfbf8;
    --thumb-ink: #5b5c55;
    /* ONE MESH MATERIAL for the STL and F3D thumbnailers (Maker::Previews::Palette::MATERIAL, which
       test/services/maker/previews/palette_test.rb holds equal to this) and the 3D viewer's
       default colour. Deliberately NOT redefined per theme: it clears 3:1 on the viewer's --raised
       in Paper (3.44) and Graphite (3.61) and on the card in both (4.18, 3.58), so a live theme
       switch has only the viewer's background to repaint. */
    --model-material: #7f7b73;
    /* Syntax colours for the code viewers (lib/codemirror_theme.js's HighlightStyle). Every one
       clears 4.5:1 on --surface in its theme; --code-comment is the lowest, 5.13:1 in both. */
    --code-keyword: #9a3412;
    --code-string: #2f6b2a;
    --code-number: #8a5300;
    --code-comment: #6b6c64;
    --code-function: #1c5d99;
    --code-type: #6d3fa0;
    --code-property: #7c4a12;
    --code-meta: #5b5c55;
    --code-invalid: #b42318;
    /* Aliases for rules migrated from the pre-2026-09 sheets. New rules use the names above. */
    --muted: var(--fg-muted);
    --soft: var(--raised);
    --font-ui: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
    --space-7: 48px;
    --space-8: 64px;
    --radius-control: 3px;
    --radius-panel: 5px;
    --radius-float: 8px;
    --radius-pill: 999px;
    --control-height: 32px;
    --control-height-small: 26px;
    --row-height: 44px;
    --container-max: 1360px;
    --prose-measure: 72ch;
    --rail-width: 216px;
    --rail-collapsed-width: 56px;
    --topbar-height: 48px;
    --tabbar-height: 56px;
  }
  :root[data-theme="dark"] {
    color-scheme: dark;
    --bg: #141512;
    --surface: #1b1c19;
    --raised: #252622;
    --line: #32332d;
    --line-strong: #74756c;
    --fg: #ecebe5;
    --fg-muted: #a3a299;
    --accent: #ff7a33;
    --accent-ink: #1d0c02;
    --accent-text: #ff8f52;
    --accent-soft: rgba(255, 122, 51, .14);
    --ok: #56c27f;
    --ok-soft: rgba(86, 194, 127, .14);
    --warn: #e3a73d;
    --warn-soft: rgba(227, 167, 61, .14);
    --danger: #f36c5c;
    --danger-soft: rgba(243, 108, 92, .14);
    --info: #6fb2f0;
    --info-soft: rgba(111, 178, 240, .14);
    --shadow-float: 0 10px 30px rgba(0, 0, 0, .55), 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-toggle: 0 1px 2px rgba(0, 0, 0, .5);
    --scrim: rgba(0, 0, 0, .6);
    --photo-stage: #0e0f0c;
    /* The dimmed paper (rule 5): 9.6:1 against Graphite's page where white was ~19:1, so a grid
       of tiles no longer out-shines the text; black ink 11:1, a LightBurn blue layer 6.7:1 on it. */
    --thumb-stage: #bfbbb2;
    --thumb-ink: #3d3e38;
    --code-keyword: #ff9a66;
    --code-string: #93c97c;
    --code-number: #e3b45a;
    --code-comment: #8e8d85;
    --code-function: #82b9ee;
    --code-type: #c4a5ee;
    --code-property: #dfb98c;
    --code-meta: #a3a299;
    --code-invalid: #f36c5c;
  }
  @media (prefers-color-scheme: dark) {
    :root[data-theme="system"] {
      color-scheme: dark;
      --bg: #141512;
      --surface: #1b1c19;
      --raised: #252622;
      --line: #32332d;
      --line-strong: #74756c;
      --fg: #ecebe5;
      --fg-muted: #a3a299;
      --accent: #ff7a33;
      --accent-ink: #1d0c02;
      --accent-text: #ff8f52;
      --accent-soft: rgba(255, 122, 51, .14);
      --ok: #56c27f;
      --ok-soft: rgba(86, 194, 127, .14);
      --warn: #e3a73d;
      --warn-soft: rgba(227, 167, 61, .14);
      --danger: #f36c5c;
      --danger-soft: rgba(243, 108, 92, .14);
      --info: #6fb2f0;
      --info-soft: rgba(111, 178, 240, .14);
      --shadow-float: 0 10px 30px rgba(0, 0, 0, .55), 0 1px 2px rgba(0, 0, 0, .4);
      --shadow-toggle: 0 1px 2px rgba(0, 0, 0, .5);
      --scrim: rgba(0, 0, 0, .6);
      --photo-stage: #0e0f0c;
      /* The dimmed paper (rule 5): 9.6:1 against Graphite's page where white was ~19:1, so a grid
         of tiles no longer out-shines the text; black ink 11:1, a LightBurn blue layer 6.7:1 on it. */
      --thumb-stage: #bfbbb2;
      --thumb-ink: #3d3e38;
      --code-keyword: #ff9a66;
      --code-string: #93c97c;
      --code-number: #e3b45a;
      --code-comment: #8e8d85;
      --code-function: #82b9ee;
      --code-type: #c4a5ee;
      --code-property: #dfb98c;
      --code-meta: #a3a299;
      --code-invalid: #f36c5c;
    }
  }
}

@layer base {
  /* Self-hosted (app/assets/fonts, SIL OFL 1.1): the CSP's font_src is :self, and a self-hosted
     app does not call Google. Propshaft rewrites these url()s to the fingerprinted paths. */
  @font-face {
    font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400 600; font-display: swap;
    src: url("/assets/ibm-plex-sans-578a0041.woff2") format("woff2");
  }
  @font-face {
    font-family: "IBM Plex Sans"; font-style: italic; font-weight: 400; font-display: swap;
    src: url("/assets/ibm-plex-sans-italic-8be8f653.woff2") format("woff2");
  }
  @font-face {
    font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap;
    src: url("/assets/ibm-plex-mono-400-d13bb5b4.woff2") format("woff2");
  }
  @font-face {
    font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap;
    src: url("/assets/ibm-plex-mono-500-deab9c2b.woff2") format("woff2");
  }
  /* KiCanvas's icon font (Apache-2.0, LICENSE-material-symbols.txt), written by bin/build-kicanvas,
     which also deletes the Google Fonts <link> upstream injected; its labels inherit --font-ui. This
     document-level rule reaches the viewer's shadow DOM, and nothing else names the family, so the
     492 KB file is fetched only on a page where a KiCanvas icon renders. `block`, not `swap`: an icon
     is a ligature, so a fallback face would spell its NAME ("zoom_in") until the font arrived.
     Rename the family and every icon reads as a word: the build checks the bundle still asks for it. */
  @font-face {
    font-family: "Material Symbols Outlined"; font-style: normal; font-weight: 400; font-display: block;
    src: url("/assets/material-symbols-outlined-336eeb42.woff2") format("woff2");
  }

  *, *::before, *::after { box-sizing: border-box; }
  /* THE `hidden` ATTRIBUTE IS AUTHORITATIVE, HERE AND ONCE (F-S34-2). Any author `display` outranks
     the user agent's `[hidden] { display: none }`, so a component that hid with the attribute had to
     restate it: seven restatements in three sheets, each added after a shipped bug, and the page
     editor's YAML drawer, which had none, rendered while hidden. `!important` in this EARLY layer is
     the cascade's own tool for it: important declarations reverse the layer order, so this beats
     every component, shell, page and unlayered vendor rule, and no component restates `[hidden]`.
     `until-found` keeps the user agent's behaviour, which find-in-page reveals. css_hygiene_test.rb
     refuses a `[hidden]` rule anywhere else; shell_test.rb probes it in a browser. */
  [hidden]:not([hidden="until-found"]) { display: none !important; }
  html { -webkit-text-size-adjust: 100%; }
  body {
    margin: 0; background: var(--bg); color: var(--fg);
    font: 400 14px/21px var(--font-ui);
  }
  /* Links are INK, not accent (the accent is spent on the one action). Navigation links (file
     names, crumbs) underline on hover; links inside prose are always underlined (.prose a). */
  a { color: var(--fg); text-decoration: none; }
  a:hover { text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
  h1, h2, h3 { margin: 0; text-wrap: balance; }
  h1 { font-size: 20px; line-height: 26px; font-weight: 600; letter-spacing: -0.01em; }
  h2 { font-size: 16px; line-height: 22px; font-weight: 600; }
  h3 { font-size: 14px; line-height: 20px; font-weight: 600; }
  p { margin: 0 0 8px; }
  code, kbd, pre, .mono { font-family: var(--font-mono); font-size: 13px; line-height: 20px; }
  .muted { color: var(--fg-muted); }
  .num { text-align: right; font-family: var(--font-mono); font-size: 12px; font-variant-numeric: tabular-nums; }
  .prose { max-width: var(--prose-measure); font-size: 15px; line-height: 24px; }
  .prose h2 { margin: var(--space-5) 0 var(--space-2); }
  .prose a { text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
  .prose a:hover { text-decoration-color: var(--fg); }
  :focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
  ::selection { background: var(--accent-soft); }
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  }
}
