/*
 * tokens.css -- design tokens for pbrs_manager
 *
 * Every colour used anywhere in the application is named here, exactly once,
 * and nowhere else. app.css, app.js and every template reach colour only
 * through var(--color-*) or var(--shadow-float). See DESIGN.md for the
 * rationale: a greenish tinted slate, not navy, with the brand green kept
 * under ten percent of the surface and never doubling as a status colour.
 *
 * Theme handling: the light palette lives on bare :root as the baseline
 * (so a no-JS render is still correct). The dark palette repeats twice by
 * necessity of the pattern: once behind a prefers-color-scheme media query
 * for a visitor who never chose, and once behind [data-theme="dark"] so an
 * explicit choice always wins over the operating system. app.js is the only
 * script that ever sets or clears the data-theme attribute, and only for an
 * explicit choice -- "system" is expressed by the attribute being absent.
 */

:root {
  /* ================= fonts ================= */
  --font-mono: 'Martian Mono', ui-monospace, 'Cascadia Mono', 'Consolas', 'SFMono-Regular', monospace;
  --font-sans: 'Familjen Grotesk', 'Segoe UI', system-ui, -apple-system, sans-serif;

  /* ================= spacing: fixed 4px base grid (DESIGN.md) ================= */
  --space-4: 0.25rem;
  --space-8: 0.5rem;
  --space-12: 0.75rem;
  --space-16: 1rem;
  --space-24: 1.5rem;
  --space-32: 2rem;
  --space-48: 3rem;
  --space-64: 4rem;

  /* ================= type scale: fixed steps (DESIGN.md) ================= */
  --text-12: 0.75rem;
  --text-13: 0.8125rem;
  --text-15: 0.9375rem;
  --text-17: 1.0625rem;
  --text-21: 1.3125rem;
  --text-27: 1.6875rem;

  /* ================= radii ================= */
  --radius-sm: 2px;
  --radius-md: 4px;
  --radius-lg: 8px;
  --radius-pill: 999px;

  /* ================= borders ================= */
  --border-width: 1px;
  --border-width-strong: 2px;

  /* ================= motion: 120-200ms, decelerating, never size/position ===== */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --duration-fast: 120ms;
  --duration-base: 200ms;

  /* ================= the edges of the screen =================
   * What the phone itself occupies: the notch or Dynamic Island and the
   * status bar at the top, the home indicator at the bottom, and the rounded
   * corners either side in landscape.
   *
   * These exist because base.html asks for `viewport-fit=cover` and tells
   * iOS the status bar is translucent, which together mean the page is drawn
   * edge to edge, *underneath* all of that. That is the right way round --
   * the alternative is two grey bands where the page should be -- but it is
   * only right if every edge of the layout then keeps out of the way, and
   * the first version of this kept out of the way at the bottom only. The
   * header sat under the clock: the brand mark behind the time, the tab
   * button behind the Dynamic Island, neither readable and neither
   * tappable.
   *
   * Named rather than written as `env()` at each site for two reasons: it is
   * one place to correct, and a value can be forced from the console to see
   * the effect on a desktop, where every inset is otherwise zero and the
   * mistake is invisible.
   */
  --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);

  /* ================= layout ================= */
  /* 36px, the height of a table row. The header used to be 48px and carried
     the same three controls in the same one line -- the twelve pixels were
     air. On a laptop screen the plan grid wins them, which is one station
     more on the screen without a single row getting shorter. */
  --header-height: 2.25rem;
  --row-height: 36px;
  /* The plan grid packs its rows tighter than a table, because a booking bar
     carries a word and a table row carries eight columns. Fifteen stations
     fit a laptop screen at this height; below it a finger cannot hit a bar. */
  --row-height-compact: 26px;

  /* Density: how much room a hit target gets.
   *
   * This used to be decided by `any-pointer: coarse` alone, and that question
   * cannot be answered by a media query. An iPad with a keyboard reports a
   * *fine* primary pointer while its screen still takes a finger, and a
   * Windows laptop with a touchscreen reports a *coarse* pointer available
   * while the person is using a mouse. Both answer every query identically,
   * so whichever query is picked is wrong on one of the two -- and it was
   * wrong on the laptop, which spent every page in the iPad layout and lost
   * a third of the plan grid to it.
   *
   * So the media query only sets the *default*, and a person can overrule it;
   * `data-density` is then present exactly as `data-theme` is, absent meaning
   * nobody chose. `0px` is not "no minimum" by accident -- it is the value
   * that makes `min-height: var(--tap)` a no-op, so the touch rules can stay
   * where they are and simply stop having an effect. */
  --tap: 0px;
  --row-min: var(--row-height-compact);
  --content-max-width: 1680px;

  /* =====================================================================
   * Colour -- light, the baseline on bare :root.
   * Greenish slate (hue 155), chroma 0.004-0.014, not blue, not grey.
   * ===================================================================== */
  color-scheme: light;

  --color-bg: oklch(0.985 0.004 155);
  --color-surface: oklch(0.965 0.005 155);
  --color-surface-2: oklch(0.935 0.006 155);
  --color-border: oklch(0.885 0.008 155);
  --color-border-strong: oklch(0.79 0.010 155);

  --color-text: oklch(0.22 0.014 155);
  --color-text-2: oklch(0.44 0.012 155);
  --color-text-3: oklch(0.56 0.010 155);

  /* Accent: the one brand colour. Active element, primary button, focus
     ring. Never a status. */
  --color-accent: oklch(0.50 0.125 152);

  /* Traffic light: separate from the accent, exclusively for device and
     connection state. "unknown" is not a colour of its own, it borrows the
     third text tier so an unread state does not compete with real ones. */
  --color-good: oklch(0.50 0.140 148);
  --color-warning: oklch(0.55 0.130 62);
  --color-critical: oklch(0.52 0.185 25);
  --color-unknown: var(--color-text-3);

  /* Sunday, on the revenue figures per day. Not a state and not an accent:
     it is a mark on the calendar, so the eye can find the week boundaries in
     a row of thirty days without any day looking more important than the
     money on it. One step of lightness toward the middle -- the same small
     step in both themes -- and a hue away from the greenish slate, so it
     reads as another colour rather than as a brighter row. Away from the
     traffic light on purpose: at hue 62 to 78 a tinted Sunday would sit in
     the warning family, and a Sunday warns about nothing. Blue only here, at
     a chroma just above the neutrals; DESIGN.md rules out navy as the ground
     tone, and one tinted row in seven is not the ground. */
  --color-sunday: oklch(0.955 0.016 250);

  /* Event palette. Identity, never state: which event a booking on the plan
     belongs to. Eight hues far enough apart to be told apart at four pixels
     wide, all at one lightness so no event looks more important than another,
     and at a chroma below the traffic light so a plan full of bookings stays
     quieter than one alarming station. The brand hue (152) is deliberately
     absent: the accent marks the active element and must not also mean "the
     Volksfest". Used only on the station plan. */
  --color-event-1: oklch(0.55 0.115 20);
  --color-event-2: oklch(0.55 0.110 60);
  --color-event-3: oklch(0.55 0.105 100);
  --color-event-4: oklch(0.55 0.100 185);
  --color-event-5: oklch(0.55 0.110 220);
  --color-event-6: oklch(0.55 0.115 260);
  --color-event-7: oklch(0.55 0.110 300);
  --color-event-8: oklch(0.55 0.115 340);

  /* The one shadow in the whole application, reserved for things that
     genuinely float above the page: open menus, short-lived tooltips.
     Nothing at rest carries a shadow. */
  --shadow-float: 0 8px 24px oklch(0.26 0.02 155 / 0.16);
}

/* Nobody chose a density: a coarse pointer anywhere is the better guess,
   because getting it wrong on a tablet costs a missed tap and getting it
   wrong on a laptop costs only space. The switch in the header settles it
   either way. */
@media (any-pointer: coarse) {
  :root:not([data-density]) {
    --tap: 2.75rem;
    --row-min: 2.75rem;
  }
}

:root[data-density="touch"] {
  --tap: 2.75rem;
  --row-min: 2.75rem;
}

:root[data-density="compact"] {
  --tap: 0px;
  --row-min: var(--row-height-compact);
}

/* Nobody chose yet: follow the operating system, unless it was overruled
   by an explicit light choice (handled by staying on the :root baseline
   above, since this rule only fires for prefers-color-scheme: dark). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --color-bg: oklch(0.17 0.010 155);
    --color-surface: oklch(0.215 0.011 155);
    --color-surface-2: oklch(0.262 0.012 155);
    --color-border: oklch(0.315 0.012 155);
    --color-border-strong: oklch(0.42 0.014 155);

    --color-text: oklch(0.955 0.006 155);
    --color-text-2: oklch(0.79 0.010 155);
    --color-text-3: oklch(0.64 0.012 155);

    --color-accent: oklch(0.72 0.115 152);

    --color-good: oklch(0.76 0.145 148);
    --color-warning: oklch(0.80 0.130 78);
    --color-critical: oklch(0.68 0.175 22);
    --color-unknown: var(--color-text-3);

    /* The same hue, measured against the dark ground instead of the light
       one, but a wider step: 0.035 read clearly on white and almost
       vanished on the dark ground, checked side by side in the browser
       on 2026-09-05. Lightness differences compress as the ground gets
       darker, so matching the number would not have matched the effect. */
    --color-sunday: oklch(0.225 0.022 250);

    /* The event palette, lifted for the dark surface. Same eight hues, same
       order, so an event keeps its colour when the theme changes. */
    --color-event-1: oklch(0.75 0.105 20);
    --color-event-2: oklch(0.75 0.100 60);
    --color-event-3: oklch(0.75 0.095 100);
    --color-event-4: oklch(0.75 0.090 185);
    --color-event-5: oklch(0.75 0.100 220);
    --color-event-6: oklch(0.75 0.105 260);
    --color-event-7: oklch(0.75 0.100 300);
    --color-event-8: oklch(0.75 0.105 340);

    --shadow-float: 0 8px 24px oklch(0 0 0 / 0.45);
  }
}

/* An explicit choice always wins over the operating system, in both
   directions -- this block only ever applies when app.js set data-theme
   itself after the visitor picked "Dark" in the theme switch. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --color-bg: oklch(0.17 0.010 155);
  --color-surface: oklch(0.215 0.011 155);
  --color-surface-2: oklch(0.262 0.012 155);
  --color-border: oklch(0.315 0.012 155);
  --color-border-strong: oklch(0.42 0.014 155);

  --color-text: oklch(0.955 0.006 155);
  --color-text-2: oklch(0.79 0.010 155);
  --color-text-3: oklch(0.64 0.012 155);

  --color-accent: oklch(0.72 0.115 152);

  --color-good: oklch(0.76 0.145 148);
  --color-warning: oklch(0.80 0.130 78);
  --color-critical: oklch(0.68 0.175 22);
  --color-unknown: var(--color-text-3);

  /* The same hue, measured against the dark ground instead of the light
     one, but a wider step: 0.035 read clearly on white and almost
     vanished on the dark ground, checked side by side in the browser
     on 2026-09-05. Lightness differences compress as the ground gets
     darker, so matching the number would not have matched the effect. */
  --color-sunday: oklch(0.225 0.022 250);

  /* The event palette, lifted for the dark surface. Same eight hues, same
     order, so an event keeps its colour when the theme changes. */
  --color-event-1: oklch(0.75 0.105 20);
  --color-event-2: oklch(0.75 0.100 60);
  --color-event-3: oklch(0.75 0.095 100);
  --color-event-4: oklch(0.75 0.090 185);
  --color-event-5: oklch(0.75 0.100 220);
  --color-event-6: oklch(0.75 0.105 260);
  --color-event-7: oklch(0.75 0.100 300);
  --color-event-8: oklch(0.75 0.105 340);

  --shadow-float: 0 8px 24px oklch(0 0 0 / 0.45);
}
