/*
  Design tokens.

  Every colour, space and radius in the application resolves through a variable
  declared here. Component CSS must never hard-code a hex value -- that is what
  makes adding a theme a matter of writing one new block rather than auditing
  every stylesheet.

  Themes are selected with data-theme on <html>. `moria` is the default and is
  declared on :root so the page is styled even before the theme script runs.
*/

:root {
  /* --- Type ------------------------------------------------------------- */
  --font-display: "Iowan Old Style", "Palatino Linotype", Palatino, Palladio,
    "URW Palladio L", "Book Antiqua", Baskerville, Georgia, serif;
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code",
    "Cascadia Code", Menlo, Consolas, monospace;

  --text-xs: 0.75rem;
  --text-sm: 0.8125rem;
  --text-base: 0.9375rem;
  --text-md: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.375rem;
  --text-2xl: 1.75rem;
  --text-3xl: 2.25rem;

  --leading-tight: 1.25;
  --leading-normal: 1.6;
  --leading-relaxed: 1.75;

  /* --- Space (4px scale) ------------------------------------------------ */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.25rem;
  --sp-6: 1.5rem;
  --sp-8: 2rem;
  --sp-10: 2.5rem;
  --sp-12: 3rem;
  --sp-16: 4rem;

  /* --- Radius & shadow -------------------------------------------------- */
  --radius-sm: 4px;
  --radius: 8px;
  --radius-md: 10px;
  --radius-lg: 12px;
  --radius-xl: 18px;
  --radius-full: 999px;

  /*
    --- Controls ----------------------------------------------------------
    Every button, input and select resolves its height from these. That is the
    whole reason things line up: a row of mixed controls has one height, not
    whatever each element's padding and font happened to add up to.
  */
  --control-h: 2.25rem;
  --control-h-sm: 1.75rem;
  --control-h-lg: 2.75rem;
  --control-px: 0.75rem;
  --control-px-sm: 0.5rem;

  /* --- Layout ----------------------------------------------------------- */
  --sidebar-width: 17.5rem;
  --inspector-width: 24rem;
  /* Wider than the inspector because the content is not prose: eighty columns
     of --font-mono do not fit in 24rem, and a terminal narrower than eighty
     re-wraps everything a program prints. */
  --terminal-width: 34rem;
  --terminal-width-min: 24rem;
  /* Wider again: a source line is longer than eighty columns once nothing is
     re-wrapping it, and this one holds prose as well. The minimum is 24rem =
     384px and must equal both `data-resize-min` in chat/_canvas.html and the
     lower bound in api/preferences.py:LAYOUT_BOUNDS -- a width outside those
     bounds is silently dropped, so the three are pinned equal by a test. */
  --canvas-width: 40rem;
  --canvas-width-min: 24rem;
  --thread-max-width: 48rem;
  --header-height: 3.5rem;

  /* What `--header-height` does at the top of the shell, this does at the
     bottom. The sidebar's footer and the composer sit either side of the same
     vertical line and both are content-sized, so without this they end at
     different heights -- and neither can be made to match the other by
     accident: the footer's height depends on which entries the reader's
     permissions allow, and the composer's on how much they have typed.

     This began as the fix for a broken *line*: both carried a top border, and
     the two met the sidebar's edge at different heights. The borders are gone
     now -- an edge that content scrolls under reads better undrawn than drawn
     and aligned -- and the token stays, because the two ends of the shell
     sitting at different heights is visible without any border to prove it.

     A calc of the pieces the footer is actually built from -- four rows at
     `--control-h`, the gaps between them, and its own padding -- so it stays
     true if those tokens move. Applied as a `min-height` to both: it holds the
     footer at full height even for somebody who sees fewer entries, and lifts
     the composer to meet it. A composer that grows past this as somebody types
     is expected; the input is getting bigger, and nothing is pretending the
     sidebar should follow it. */
  --footer-height: calc(4 * var(--control-h) + 3 * var(--sp-1) + 2 * var(--sp-2));

  /* The terminal's own type. xterm holds this as a number rather than reading
     it from CSS, so terminal.js parses it back out -- it must stay a plain
     pixel value. */
  --terminal-font-size: 13px;

  /*
    --- Stacking ----------------------------------------------------------
    These were four bare numbers scattered across two stylesheets, which is
    fine until something new has to sit between two of them and nobody can
    say what is already there.
  */
  --z-raised: 5;
  --z-handle: 10;
  --z-dropdown: 30;
  --z-panel: 40;
  --z-overlay: 50;
  --z-toast: 60;

  --transition-fast: 120ms ease;
  --transition: 200ms ease;

  /* The focus treatment, written once. Three components spelled it out. It
     resolves --accent-soft at the point of use, so it follows the theme even
     though it is declared above them. */
  --ring: 0 0 0 3px var(--accent-soft);
}

/*
  ---------------------------------------------------------------------------
  MORIA (default, dark)

  Deep stone and lamplight: the halls under the mountain. Surfaces are cool and
  near-neutral so the mallorn and mithril accents carry all the colour.
  ---------------------------------------------------------------------------
*/
:root,
:root[data-theme="moria"] {
  color-scheme: dark;

  --bg: #101317;
  --bg-sunken: #0B0E11;
  --surface: #171B21;
  --surface-raised: #1E242B;
  --surface-hover: #232A32;
  --surface-active: #2A323B;

  --border: #2A313A;
  --border-strong: #3A434E;

  --ink: #E4E8EC;
  --ink-muted: #A2ADB8;
  --ink-faint: #7A848F;
  --ink-inverse: #0B0E11;

  /* Mithril: the cool primary, used for focus and interactive accents. */
  --accent: #8FB3CC;
  --accent-hover: #A9C6DA;
  --accent-ink: #0B0E11;
  --accent-soft: rgba(143, 179, 204, 0.14);

  /*
    Mallorn: the brand accent, and the assistant's mark. A yellow-leaning leaf
    green, so it stays warm against the mithril blue rather than turning the
    palette into two cool accents that compete.
  */
  --leaf: #9BCC5A;
  --leaf-hover: #B1DD74;
  --leaf-soft: rgba(155, 204, 90, 0.14);

  /* Ember: destructive actions and errors. */
  --danger: #E2795A;
  --danger-hover: #EC8E72;
  --danger-soft: rgba(226, 121, 90, 0.14);

  /*
    Success leans teal rather than leaf. Two greens a hue apart read as one
    colour rendered inconsistently -- an unread dot beside a brand badge has to
    be tellable from it at a glance.
  */
  --success: #5FBFA0;
  --success-soft: rgba(95, 191, 160, 0.14);
  --warning: #DFAE58;
  --warning-soft: rgba(223, 174, 88, 0.14);

  --bubble-user: #232B34;
  --bubble-assistant: transparent;
  --code-bg: #0C0F13;
  --code-border: #262D36;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
  --shadow-lg: 0 12px 34px rgba(0, 0, 0, 0.55);

  --scrim: rgba(6, 8, 10, 0.66);

  /*
    The sixteen ANSI colours, for the terminal panel.

    A shell picks its own colours -- `ls --color`, a git diff, htop's meters --
    and until these existed it got xterm's defaults, which are a different
    palette from this one and read as a foreign window pasted into the
    application. Where a slot has an obvious counterpart above it takes it, so
    an error in the terminal is the same ember as an error anywhere else.

    "black" is not #000: it is what a program picks for a dim background or
    faint text, and on a near-black surface a true black is invisible.
  */
  --ansi-black: #1A1F26;
  --ansi-red: #E2795A;
  --ansi-green: #9BCC5A;
  --ansi-yellow: #DFAE58;
  --ansi-blue: #8FB3CC;
  --ansi-magenta: #C08FCC;
  --ansi-cyan: #5FBFA0;
  --ansi-white: #C6CDD4;
  --ansi-bright-black: #4A535E;
  --ansi-bright-red: #EC8E72;
  --ansi-bright-green: #B1DD74;
  --ansi-bright-yellow: #EFC87A;
  --ansi-bright-blue: #A9C6DA;
  --ansi-bright-magenta: #D4A9DE;
  --ansi-bright-cyan: #7FD4BA;
  --ansi-bright-white: #E4E8EC;
}

/*
  ---------------------------------------------------------------------------
  SHIRE (light)

  Parchment, ink and moss: warm, low-contrast, easy to read for a long time.
  Backgrounds are deliberately off-white -- pure #FFF next to the leaf accent
  reads as clinical rather than as paper.
  ---------------------------------------------------------------------------
*/
/*
  `[data-base="shire"]` as well as `[data-theme="shire"]`, and that second
  selector is what makes a custom theme possible. A custom theme is a handful of
  token overrides served from /branding.css; everything it does not set has to
  come from somewhere, and Moria's block above matches bare `:root` so it always
  applies. Without this a custom *light* theme would be four light colours on
  near-black surfaces. `<html>` carries both attributes -- see base.html and
  app.js:applyTheme.
*/
:root[data-theme="shire"],
:root[data-base="shire"] {
  color-scheme: light;

  --bg: #F6F1E4;
  --bg-sunken: #EDE6D4;
  --surface: #FDFBF5;
  --surface-raised: #FFFFFF;
  --surface-hover: #F1EADA;
  --surface-active: #E7DEC9;

  --border: #DED3BB;
  --border-strong: #C6B896;

  --ink: #2C2419;
  --ink-muted: #6A5C48;
  --ink-faint: #756A55;
  --ink-inverse: #FDFBF5;

  /* Hobbit-door blue-green: the cool primary. */
  --accent: #3E6B7A;
  --accent-hover: #325867;
  --accent-ink: #FDFBF5;
  --accent-soft: rgba(62, 107, 122, 0.12);

  /* The same mallorn, darkened until it holds its own as text on parchment. */
  --leaf: #4C7A22;
  --leaf-hover: #3C6318;
  --leaf-soft: rgba(76, 122, 34, 0.13);

  --danger: #A6432B;
  --danger-hover: #8C3722;
  --danger-soft: rgba(166, 67, 43, 0.11);

  --success: #2C7360;
  --success-soft: rgba(44, 115, 96, 0.12);
  --warning: #98701A;
  --warning-soft: rgba(152, 112, 26, 0.13);

  --bubble-user: #EDE4CF;
  --bubble-assistant: transparent;
  --code-bg: #F2EBD9;
  --code-border: #DED3BB;

  --shadow-sm: 0 1px 2px rgba(72, 58, 34, 0.09);
  --shadow: 0 4px 14px rgba(72, 58, 34, 0.11);
  --shadow-lg: 0 12px 34px rgba(72, 58, 34, 0.16);

  --scrim: rgba(44, 36, 25, 0.4);

  /*
    The same sixteen on parchment, and the reason they are not the Moria set
    lightened: every one of these has to be readable as *text* on #F2EBD9, so
    the whole palette is darkened rather than brightened. "bright" therefore
    means more emphatic here, not lighter -- a light terminal theme that made
    bright yellow actually bright would render it invisible.
  */
  --ansi-black: #2C2419;
  --ansi-red: #A6432B;
  --ansi-green: #4C7A22;
  --ansi-yellow: #98701A;
  --ansi-blue: #3E6B7A;
  --ansi-magenta: #7A3E6B;
  --ansi-cyan: #2C7360;
  --ansi-white: #6A5C48;
  --ansi-bright-black: #94856D;
  --ansi-bright-red: #8C3722;
  --ansi-bright-green: #3C6318;
  --ansi-bright-yellow: #7A5A14;
  --ansi-bright-blue: #325867;
  --ansi-bright-magenta: #63325A;
  --ansi-bright-cyan: #235C4D;
  --ansi-bright-white: #453A2A;
}

/* Respect a stated preference for reduced motion everywhere, at once. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
