/*
 * Design tokens. Components read these custom properties only; they never
 * hard-code colours, radii, spacing, type sizes or durations.
 *
 * Themes: night (graphite, the approved "Calm" sketch) and day (light, cool
 * neutrals). The theme is set on <html data-theme="day|night"> by
 * src/ui/theme.js. Every text token reaches at least 4.5:1 on every surface
 * it is used on; dev/components.html measures and lists the ratios.
 *
 * Elevation is expressed by surface lightness, not by borders: surface-0 is
 * the page, surface-1 cards and sheets, surface-2 controls and tiles inside
 * cards, surface-3 pressed states, tracks and the topmost fills.
 */
@layer tokens, base, layout, components, pages;

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../../assets/fonts/InterVariable.woff2") format("woff2");
}

@layer tokens {
  /*
   * A zoom scope (`data-zoom-scope`): a box laid out at the standard display size inside a page at another step.
   * A custom property is worked out where it is declared and inherited as that value, so the tokens worked out
   * from the step's zoom (the touch targets, the smallest type, the safe-area insets) would come into it as the
   * page has them; it declares them again, at --display-zoom 1. The on-screen keyboard, drawn at 1 at every step
   * (components.css): at "Kleiner" it inherited the page's 54 px touch target, its keys were 54 px on the screen
   * and the keyboard 376 px high, 360 at the other steps (review 2026-10-11). And Today's grid, while runs-card.js
   * measures what standard shows (pages.css).
   */
  :root,
  [data-zoom-scope] {
    /* Typeface */
    --font-sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans",
      "Helvetica Neue", "Liberation Sans", "DejaVu Sans", Arial, sans-serif;

    /*
     * Display size (Settings › This device; Stephan 2026-10-11): the whole page zooms by this factor
     * (base.css, the mechanism and why). 1 is standard; the steps set it below. Everything that is
     * measured in physical pixels on the device, not in the page's CSS pixels, divides by it: the touch
     * targets, the smallest type, the viewport units.
     */
    --display-zoom: 1;

    /* Spacing: 4 px grid */
    --space-0: 0;
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-7: 28px;
    --space-8: 32px;
    --space-10: 40px;
    --space-12: 48px;
    --space-16: 64px;

    /* Radius scale and semantic aliases */
    --radius-xs: 4px;
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 14px;
    --radius-xl: 16px;
    --radius-2xl: 20px;
    --radius-3xl: 24px;
    --radius-full: 999px;
    --radius-card: var(--radius-2xl);
    --radius-control: var(--radius-lg);
    --radius-tile: var(--radius-xl);
    --radius-row: var(--radius-md);
    --radius-sheet: var(--radius-3xl);

    /*
     * Type scale. Tracking is size-specific: small text opens up slightly,
     * large text tightens. Line height shrinks relative to size as text grows.
     */
    /* The 13 px sizes keep 12 physical px at the smaller step (Stephan 2026-10-11: its smallest captions not under
       12 px): 13.33 px there at 90 %, 13 px at the other steps. */
    --type-caption-size: max(13px, calc(12px / var(--display-zoom)));
    --type-caption-line: 18px;
    --type-caption-tracking: 0.01em;
    --type-body-size: 15px;
    --type-body-line: 20px;
    --type-body-tracking: 0.005em;
    --type-headline-size: 17px;
    --type-headline-line: 22px;
    --type-headline-tracking: -0.006em;
    --type-title3-size: 20px;
    --type-title3-line: 26px;
    --type-title3-tracking: -0.012em;
    --type-title2-size: 24px;
    --type-title2-line: 30px;
    --type-title2-tracking: -0.016em;
    --type-title1-size: 34px;
    --type-title1-line: 40px;
    --type-title1-tracking: -0.024em;
    --type-display-s-size: 32px;
    --type-display-s-line: 38px;
    --type-display-s-tracking: -0.022em;
    --type-display-m-size: 44px;
    --type-display-m-line: 50px;
    --type-display-m-tracking: -0.03em;
    --type-display-l-size: 56px;
    --type-display-l-line: 56px;
    --type-display-l-tracking: -0.035em;
    /* The navigation's labels, fixed at the 13 px floor on every device: on the kiosk's 100 px rail
       "Einstellungen" at 14 px would end at x = 96, under the alarm frame's 5 px edge (type audit
       2026-10-10); at 13 px it ends at 93. */
    --type-nav-size: max(13px, calc(12px / var(--display-zoom)));
    /* The phone's caption on every device, for small words in a box the kiosk's half step outgrows
       (measured 2026-10-10 at 1024 × 600). The cistern gauge's words and its 24 hours' causes, like a
       chart's labels: at 14 px "Nach dem Gießen morgen" broke into two lines, the causes ("Regen
       +290 L", "Nachspeisung +204 L", "Entnahme −89 L") took three lines instead of two, and the
       cistern card ran 3 px past its column. The zone wizard's summary rows: at 14 px "Größter
       Wasserspeicher 24 mm · Versickerung 2 mm am Tag" took two lines and the last row ended 15 px
       under the footer, where every row must be in view. */
    --type-caption-compact-size: max(13px, calc(12px / var(--display-zoom)));
    --type-caption-compact-line: 18px;
    /* The phone's headline on every device, for a headline in a box the kiosk's half step outgrows
       (measured 2026-10-10 at 1024 × 600): a zone tile's name ("Rasen Terrasse" broke into two lines
       beside its symbol, where it keeps one without it; Stephan's rule of 2026-10-08), the History
       cistern card's three figures ("about 2,345 L" cut in its 112 px), the old engine's rain row on
       Today ("7 Tage 11,9 mm" past the weather card's edge). */
    --type-headline-compact-size: 17px;
    --type-headline-compact-line: 22px;
    /* Sizes of one place each, kept here so no rule hard-codes a size (they do not follow the kiosk's
       half step below): the hero number's unit ("5.780 L", "18,2 °C"), the stepper's value, the
       on-screen keyboard's keys. */
    --type-hero-unit-size: 28px;
    --type-stepper-size: 40px;
    --type-stepper-line: 46px;
    --type-key-size: 22px;
    --type-key-line: 28px;

    --weight-light: 300;
    --weight-regular: 400;
    --weight-medium: 500;
    --weight-semibold: 600;

    /* Motion. Springs (src/ui/motion/spring.js) have no duration; these are
       for the few CSS transitions that remain. */
    --duration-press: 100ms;
    --duration-fast: 150ms;
    --duration-base: 200ms;
    --duration-slow: 300ms;
    --duration-reduced: 180ms;
    --duration-theme: 600ms;
    --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
    --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
    --press-scale: 0.97;

    /* Shell geometry */
    --header-height: 56px;
    /* "Einstellungen" (85 px at 13 px) stays clear of the alarm frame's 5 px edge (review 2026-10-09). */
    --rail-width: 100px;
    --tabbar-height: 64px;
    --sheet-max-width: 640px;
    /* Touch targets are a finger's size on the glass, whatever the display size: --touch-floor in physical
       px (44 on phones, 48 on the kiosk), --touch-min the CSS px that make it at the step's zoom, whole px
       up (54 px for the kiosk's 48 at 90 %, 48.6 on the screen: a target of 53.3 measured 47 px where the
       finger's reach is counted in whole px; at a larger step it stays, and grows with the page). A control
       is never lower than a touch target. */
    --touch-floor: 44px;
    --touch-min: round(up, calc(var(--touch-floor) / min(1, var(--display-zoom))), 1px);
    --control-height: max(48px, var(--touch-min));
    --control-height-lg: 56px;
    /* The safe-area insets (a phone's home indicator, its island, its rounded corners) are the device's, in
       physical px. env() is zoomed like any length (base.css), so the rules take these, divided by the step's zoom:
       at the smaller step the tab bar kept 30.6 px of a 34 px inset and its items reached into it (review
       2026-10-11). The on-screen keyboard is zoomed back to 1 and keeps env(). */
    --safe-top: calc(env(safe-area-inset-top, 0px) / var(--display-zoom));
    --safe-right: calc(env(safe-area-inset-right, 0px) / var(--display-zoom));
    --safe-bottom: calc(env(safe-area-inset-bottom, 0px) / var(--display-zoom));
    --safe-left: calc(env(safe-area-inset-left, 0px) / var(--display-zoom));

    /* Page backgrounds of both themes, independent of the active theme.
       The theme cross-fade (src/ui/theme.js) dips to the target colour. */
    --page-night: #0b0c0e;
    --page-day: #eef1f4;
    /* The display sleep's black screen (ui/shell/sleep-screen.js): black in both themes, like the backlight off. */
    --sleep-screen: #000;
    /* The red frame of an open alarm (ui/shell/alarm-frame.js): a thin edge, its glow fades this far inward. */
    --alarm-edge-width: 5px;
    --alarm-glow-blur: 36px;

    /* Layers */
    --z-content: 1;
    --z-nav: 15;
    --z-scrim: 40;
    --z-sheet: 41;
    --z-keyboard: 45;
    --z-header: 50;
    --z-toast: 60;
    --z-alarm: 80;
    --z-theme-fade: 90;
    --z-sleep: 100;
  }

  /*
   * The kiosk is read from 50 to 70 cm, about twice a phone's distance: its small text a half step up
   * (type audit 2026-10-10, Stephan's decision). By visual angle the 13 px captions read like 8 to 10 pt
   * on an iPhone, under Apple's smallest style; body 15 px like 9.7 to 13.5 pt, 16 px like 10.3 to
   * 14.4 pt. The line heights stay, so no row gets taller and Today still fits 1024 × 600 without
   * scrolling (the e2e suite's no-scroll and clip checks); the audit measured a full Apple step (body
   * 17/22) at 27 px past the cistern card's column with "Bestätigen" cut. Left out of the step, where
   * it did not fit (measured): the navigation's labels (--type-nav-size), small words in tight boxes
   * (the cistern gauge, the zone wizard's summary rows; --type-caption-compact-size) and headlines in
   * tight boxes (zone tiles, the History cistern card's figures, the old engine's rain row;
   * --type-headline-compact-size). Undo: delete the three sizes.
   */
  :root[data-device="kiosk"],
  :root[data-device="kiosk"] [data-zoom-scope] {
    --touch-floor: 48px;
    --type-caption-size: max(14px, calc(12px / var(--display-zoom)));
    --type-body-size: 16px;
    --type-headline-size: 18px;
  }

  /*
   * The display size's steps (ui/device.js sets data-display-size; base.css zooms <html> by the factor).
   * Measured 2026-10-11 at 1024 × 600 and 390 × 844 (tests/e2e/display-size.e2e.mjs): at 90 % the kiosk
   * gets the room of a 1138 × 667 screen, Today holds without scrolling, the 13 px captions stay at
   * 12 physical px (their floor above), the touch targets at 48 / 44 physical px (--touch-min). At 110 %
   * the kiosk lays out like a 931 px tablet (the breakpoints are container queries on <html>, base.css),
   * so Today scrolls in two columns and nothing is cut. Standard is 1.
   */
  :root[data-display-size="smaller"] {
    --display-zoom: 0.9;
  }

  :root[data-display-size="larger"] {
    --display-zoom: 1.1;
  }

  /* The page's zoom, as <html> is drawn (base.css), also inside a zoom scope, where --display-zoom is 1 (above):
     the on-screen keyboard's way back to 1, Today's grid measured as at standard. */
  :root {
    --page-zoom: var(--display-zoom);
  }

  /* ------------------------------------------------------------------
     Night: graphite surfaces, one water-blue accent
     ------------------------------------------------------------------ */
  :root,
  :root[data-theme="night"] {
    color-scheme: dark;

    --surface-0: var(--page-night);
    --surface-1: #15171a;
    --surface-2: #1f2226;
    --surface-3: #2a2e33;
    --surface-elevated: #2a2e33;
    --surface-warning: #211d16;
    --surface-warning-strong: #2e281d;
    --surface-warning-hover: #3a3224;

    --text-primary: #eceef1;
    --text-secondary: #afb5bd;
    --text-tertiary: #959ca6;
    --text-inverse: #0b0c0e;

    --accent: #6db5f2;
    --accent-hover: #82c0f5;
    --selection: color-mix(in srgb, var(--accent) 30%, transparent);
    /* A place a link led to (ui/reveal.js): a tint over it that fades out once. */
    --reveal-tint: color-mix(in srgb, var(--accent) 22%, transparent);
    --on-accent: #05111d;

    --success: #68ca9d;
    --warning: #ecb862;
    --danger: #f27d74;
    --danger-hover: #f5928a;
    --on-danger: #1d0806;
    --on-warning: #1d1405;
    --info: #8fc1f1;
    --on-info: #05111d;

    /* Stop in the header (Stephan 2026-10-06, F4): red with white text,
       at least 4.5:1 in both themes (tests/theme-tokens.test.js). */
    --stop-fill: #c8302a;
    --stop-fill-pressed: #a8231e;
    --on-stop: #ffffff;

    /* The alarm frame (Stephan 2026-10-09): the Stop's red, not the light --danger, so it does not glare on
       the dark kiosk at night; the glow about a third of it (tests/theme-tokens.test.js). */
    --alarm-edge: var(--stop-fill);
    --alarm-glow: color-mix(in srgb, var(--stop-fill) 34%, transparent);

    /* Toasts by kind (F1): done and info, warning, error. */
    --toast-info: #1d3146;
    --toast-warning: #42361e;
    --toast-error: #4a2320;

    --water: #215a8e;
    --water-use: #3a78b0;
    --water-edge: #93c9f6;
    --water-reserve: #173b5e;

    /* A zone's kind (T6): the plant in a calm square, the watering mark with a fine ring.
       Ink at least 3:1 on its fill (tests/theme-tokens.test.js). */
    --kind-fill: var(--surface-2);
    --kind-ink: var(--text-secondary);
    --kind-mark-ring: #3a3f46;
    --kind-mark-ink: var(--text-secondary);

    --hairline: rgba(255, 255, 255, 0.07);
    --border-strong: transparent;
    --grabber: #4a5058;
    --fill-hover: var(--surface-3);
    --fill-thumb: var(--surface-3);
    --switch-track-off: #3a3f46;
    --switch-thumb: #eceef1;
    --focus-ring: var(--accent);

    /* On-screen keyboard: the board, letter keys, special keys */
    --keyboard-bg: #1f2226;
    --key-fill: #33373d;
    --key-special: #26292e;

    --scrim: #000;
    --scrim-opacity: 0.62;

    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-2: 0 10px 30px rgba(0, 0, 0, 0.45);
    --shadow-3: 0 18px 48px rgba(0, 0, 0, 0.55);
    --shadow-sheet: 0 -12px 40px rgba(0, 0, 0, 0.5);
    --shadow-thumb: none;
    --card-edge: none;
  }

  /* ------------------------------------------------------------------
     Day: light, calm, slightly cool neutrals; accent darkened for contrast
     ------------------------------------------------------------------ */
  :root[data-theme="day"] {
    color-scheme: light;

    --surface-0: var(--page-day);
    --surface-1: #ffffff;
    --surface-2: #e7eaee;
    --surface-3: #d9dee4;
    --surface-elevated: #ffffff;
    --surface-warning: #fbf3e3;
    --surface-warning-strong: #f2e4c6;
    --surface-warning-hover: #eadab4;

    --text-primary: #14181d;
    --text-secondary: #454d57;
    --text-tertiary: #57606b;
    --text-inverse: #ffffff;

    --accent: #0b62a6;
    --accent-hover: #0a5694;
    --selection: color-mix(in srgb, var(--accent) 30%, transparent);
    /* A place a link led to (ui/reveal.js): a tint over it that fades out once. */
    --reveal-tint: color-mix(in srgb, var(--accent) 22%, transparent);
    --on-accent: #ffffff;

    --success: #176a40;
    --warning: #8a5300;
    --danger: #b42318;
    --danger-hover: #9c1e14;
    --on-danger: #ffffff;
    --on-warning: #ffffff;
    --info: #1f5b94;
    --on-info: #ffffff;

    --stop-fill: #b42318;
    --stop-fill-pressed: #9c1e14;
    --on-stop: #ffffff;

    --alarm-edge: var(--stop-fill);
    --alarm-glow: color-mix(in srgb, var(--stop-fill) 30%, transparent);

    --toast-info: #dceaf7;
    --toast-warning: #f8e9c8;
    --toast-error: #f9dedb;

    --water: #a9cfee;
    --water-use: #6fa9dc;
    --water-edge: #0b62a6;
    --water-reserve: #d3e5f5;

    --kind-fill: var(--surface-2);
    --kind-ink: var(--text-secondary);
    --kind-mark-ring: #c3cad3;
    --kind-mark-ink: var(--text-secondary);

    --hairline: rgba(16, 24, 40, 0.1);
    --border-strong: transparent;
    --grabber: #b3bac3;
    --fill-hover: var(--surface-3);
    --fill-thumb: #ffffff;
    --switch-track-off: #cdd3db;
    --switch-thumb: #ffffff;
    --focus-ring: var(--accent);

    --keyboard-bg: #d9dee4;
    --key-fill: #ffffff;
    --key-special: #c3cad3;

    --scrim: #000;
    --scrim-opacity: 0.32;

    --shadow-1: 0 1px 2px rgba(16, 24, 40, 0.08);
    --shadow-2: 0 8px 24px rgba(16, 24, 40, 0.12);
    --shadow-3: 0 16px 40px rgba(16, 24, 40, 0.16);
    --shadow-sheet: 0 -8px 32px rgba(16, 24, 40, 0.14);
    --shadow-thumb: 0 1px 3px rgba(16, 24, 40, 0.18);
    --card-edge: 0 0 0 1px rgba(16, 24, 40, 0.04), 0 1px 2px rgba(16, 24, 40, 0.05);
  }

  /* More contrast: stronger secondary text, visible borders on surfaces. */
  @media (prefers-contrast: more) {
    :root,
    :root[data-theme="night"] {
      --text-secondary: #ccd1d7;
      --text-tertiary: #b9bfc7;
      --hairline: rgba(255, 255, 255, 0.32);
      --border-strong: rgba(255, 255, 255, 0.5);
      --grabber: #9aa1aa;
      --switch-track-off: #5a616a;
    }

    :root[data-theme="day"] {
      --text-secondary: #2f353d;
      --text-tertiary: #3c434c;
      --hairline: rgba(16, 24, 40, 0.36);
      --border-strong: rgba(16, 24, 40, 0.55);
      --grabber: #6b737d;
      --switch-track-off: #9aa2ad;
    }
  }
}
