/* Design tokens.
 *
 * Every value the UI needs is declared once here as a CSS custom property.
 * Components never hardcode a colour, size or radius -- they reference a token.
 * That is what makes a theme swap a change in one file instead of a hundred.
 *
 * Theming works in three layers, and the order matters:
 *   1. :root                        -- the light palette, always defined.
 *   2. prefers-color-scheme: dark   -- follows the operating system setting.
 *   3. [data-theme="..."]           -- an explicit user choice, wins over both.
 */

/* One variable font, served from this project rather than from a font CDN.
 *
 * The app is installable and meant to work offline, and a face fetched from
 * someone else's domain is the one asset that cannot: the first hallway with
 * no signal falls back to the system font and the whole layout shifts. It is
 * also one less third party watching students load a page.
 *
 * Only the latin subset ships (27 KB): every accent Spanish uses -- á é í ó ú
 * ü ñ ¿ ¡ -- lives inside U+0000-00FF, so the extended subsets would be
 * weight nobody here renders.
 *
 * `swap` because a blank page is worse than a moment of the fallback, and one
 * variable file covers 400-800 without a request per weight. The fallback
 * stack is real, not decorative -- it is what renders during that moment.
 */
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/plus-jakarta-sans-latin.7660bd9909fb.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Typography */
  --font-sans: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto,
    sans-serif;

  /* The whole scale came down a step.
   *
   * The old one put the interface at 16px and headings at 28-36px, which is a
   * reading scale, not an application one -- a screen fitted four rows where
   * it had room for eight, and every list needed scrolling to be read. The
   * reference this project set itself is Figma's interface, and the products
   * in that family agree closely on the numbers: Linear ships 13px controls in
   * 32px-tall buttons, Vercel's Geist puts UI text at 14px.
   *
   * So: `md` (14px) is the interface default and what `body` inherits, `sm`
   * (13px) is for controls and secondary rows, and `lg` (16px) is the *reading*
   * size that prose opts back up into -- legal pages, a review's comment, a
   * page's lead paragraph. Density where the app is an instrument, room where
   * it is something to read.
   */
  --font-size-2xs: 0.6875rem; /* 11px -- uppercase labels only */
  --font-size-xs: 0.75rem; /*    12px -- hints, badges */
  --font-size-sm: 0.8125rem; /*  13px -- controls, secondary rows */
  --font-size-md: 0.875rem; /*   14px -- the interface default */
  --font-size-lg: 1rem; /*       16px -- prose, h3 */
  --font-size-xl: 1.125rem; /*   18px -- h2 */
  --font-size-2xl: 1.375rem; /*  22px -- h1 */
  --font-size-3xl: 1.75rem; /*   28px -- h1 on a wide screen */
  --font-size-4xl: 2.25rem; /*   36px -- the landing page, and one stat */

  --line-height-tight: 1.25;
  --line-height-normal: 1.5;
  /* Prose only. At 16px a 1.5 rhythm reads cramped over a paragraph, and
   * these are the screens somebody actually reads rather than scans. */
  --line-height-prose: 1.65;

  /* Headings tighten as they grow: at display sizes the default spacing
   * reads as gaps between letters rather than one word. Labels do the
   * opposite -- uppercase at 11px needs the room to stay readable. */
  --tracking-tight: -0.021em;
  --tracking-label: 0.07em;

  --font-weight-normal: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;

  /* Spacing scale. Multiples of 4px keep vertical rhythm predictable.
   * `--space-0` is the half step the density pass needed: the gap between an
   * icon and its label, the padding inside a pill. */
  --space-0: 0.125rem;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;

  /* Radii. Tighter than they were (8/16 -> 6/12): a large radius at a large
   * size reads as soft, and at this size it reads as blurry. */
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 12px;
  --radius-full: 999px;

  /* Layout */
  --content-max-width: 46rem;
  /* Prose caps on the measure rather than on the container: 46rem of 16px
   * text is about ninety characters a line, and the eye loses the start of
   * the next one past roughly seventy. */
  --prose-max-width: 68ch;
  --nav-height: 3.25rem;
  --control-height: 2.25rem;
  --sidebar-width: 14rem;

  /* Motion.
   *
   * Two curves, and the difference between them is the whole feel. `--ease-out`
   * is for something arriving -- it starts fast and settles, which is what
   * makes a panel feel placed rather than dragged. `--ease-spring` overshoots
   * slightly and is for something the student just did, where a hint of
   * physical response reads as the app answering.
   *
   * Every duration here is honoured only when the user has not asked for
   * reduced motion; base.css switches them all off in one place.
   */
  --transition-fast: 120ms;
  --transition-base: 200ms;
  --transition-slow: 420ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* Semantic colours -- light theme.
   *
   * Named by role, not by hue: "surface", not "white". A component asking for
   * --color-surface keeps working when the theme repaints it dark.
   *
   * The neutrals are not grey. Every one of them carries a little of the
   * logo's violet, sampled from the file itself -- the mark runs #5030b0
   * through #9040d0 to #f090b0 -- so the page reads as one palette instead of
   * a coloured button sitting on a stock grey page. It is a couple of points
   * of saturation, invisible named and obvious side by side.
   *
   * Every pair below is checked against WCAG 2.1 AA by
   * tests/test_design_tokens.py, which is why these are the values and not
   * the ones that looked right.
   */
  --color-bg: #fbfaff;
  --color-surface: #f3f1fb;
  --color-surface-raised: #ffffff;
  --color-border: #e3ddf2;
  /* Separators may stay quiet, but anything that outlines a control has to
   * clear 3:1 -- the box IS what tells you where to type. */
  --color-border-strong: #79718f;
  --color-text: #17132a;
  --color-text-muted: #585070;
  --color-text-inverted: #ffffff;

  --color-accent: #6d28d9;
  --color-accent-hover: #5b21b6;
  --color-accent-subtle: #ece3fc;

  /* The far end of the logo's gradient. Decoration only -- it appears in
   * `--gradient-brand` and nowhere as text, because at the lightness that
   * makes the gradient work it cannot clear 4.5:1 on any surface here. */
  --color-brand-pink: #d946a6;
  --gradient-brand: linear-gradient(
    135deg,
    var(--color-accent) 0%,
    var(--color-brand-pink) 100%
  );

  --color-danger: #b3261e;
  --color-danger-subtle: #fdecea;
  --color-danger-solid: #b3261e;
  --color-on-danger: #ffffff;
  --color-success: #1c6b3c;
  --color-success-subtle: #e6f4ec;

  --color-focus-ring: #6d28d9;

  /* Shadows tinted with the same violet rather than with black. Black on a
   * warm-white page reads as dirt; the tint reads as depth. */

  /* One colour per materia, so a week is readable at a glance rather than
   * readable by reading. Assigned from the materia's own code and
   * overridable per student -- see schedule/palette.py for why eight, and
   * why derived from the code rather than the primary key.
   *
   * Every ink/fill pair below clears 4.5:1, and every ink clears 4.5:1 on
   * the page as well, because these are used as text on a tinted box and as
   * a plain label beside one. tests/test_design_tokens.py checks all
   * sixteen pairs in both themes.
   */
  --color-materia-violeta: #6d28d9;
  --color-materia-violeta-fill: #efe7fd;
  --color-materia-azul: #1d4ed8;
  --color-materia-azul-fill: #e3ecfd;
  --color-materia-verde: #136c34;
  --color-materia-verde-fill: #e2f5e9;
  --color-materia-ambar: #8a5a00;
  --color-materia-ambar-fill: #fbf0d9;
  --color-materia-rosa: #be185d;
  --color-materia-rosa-fill: #fde8f1;
  --color-materia-cyan: #0e7490;
  --color-materia-cyan-fill: #dff2f7;
  --color-materia-naranja: #9a3412;
  --color-materia-naranja-fill: #fdeae1;
  --color-materia-lila: #7e22ce;
  --color-materia-lila-fill: #f6e8fd;

  --shadow-sm: 0 1px 2px rgb(23 19 42 / 6%);
  --shadow-md: 0 4px 14px -2px rgb(23 19 42 / 10%);
  --shadow-lg: 0 12px 32px -8px rgb(23 19 42 / 16%);
}

/* Follows the OS setting, but only when the user has not chosen explicitly.
 * The :not() guard is what lets an explicit light choice survive a dark OS. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-bg: #0d0a15;
    --color-surface: #151021;
    --color-surface-raised: #1b1530;
    --color-border: #2b2340;
    --color-border-strong: #7a7196;
    --color-text: #ece9f7;
    --color-text-muted: #9f97b8;
    --color-text-inverted: #0d0a15;

    --color-accent: #b794f6;
    --color-accent-hover: #c9aefb;
    --color-accent-subtle: #241b3d;

    --color-brand-pink: #f0a0c8;
    --gradient-brand: linear-gradient(
      135deg,
      var(--color-accent) 0%,
      var(--color-brand-pink) 100%
    );

    --color-danger: #f2b8b5;
    --color-danger-subtle: #3a1a18;
    --color-danger-solid: #c0392b;
    --color-on-danger: #ffffff;
    --color-success: #8fd6a8;
    --color-success-subtle: #14301f;

    --color-focus-ring: #b794f6;


    /* One colour per materia, so a week is readable at a glance rather than
     * readable by reading. Assigned from the materia's own code and
     * overridable per student -- see schedule/palette.py for why eight, and
     * why derived from the code rather than the primary key.
     *
     * Every ink/fill pair below clears 4.5:1, and every ink clears 4.5:1 on
     * the page as well, because these are used as text on a tinted box and as
     * a plain label beside one. tests/test_design_tokens.py checks all
     * sixteen pairs in both themes.
     */
    --color-materia-violeta: #c4a6f9;
    --color-materia-violeta-fill: #251c3d;
    --color-materia-azul: #9dc0fb;
    --color-materia-azul-fill: #16203a;
    --color-materia-verde: #8ed9a8;
    --color-materia-verde-fill: #12291c;
    --color-materia-ambar: #e8c579;
    --color-materia-ambar-fill: #2b2210;
    --color-materia-rosa: #f5a5c6;
    --color-materia-rosa-fill: #331526;
    --color-materia-cyan: #8ed2e6;
    --color-materia-cyan-fill: #0f272f;
    --color-materia-naranja: #f2ab86;
    --color-materia-naranja-fill: #301a11;
    --color-materia-lila: #dba9f7;
    --color-materia-lila-fill: #2a1739;

    --shadow-sm: 0 1px 2px rgb(0 0 0 / 45%);
    --shadow-md: 0 4px 14px -2px rgb(0 0 0 / 55%);
    --shadow-lg: 0 12px 32px -8px rgb(0 0 0 / 65%);
  }
}

/* Explicit user choice. Repeated so the toggle wins in both directions:
 * dark-on-a-light-OS needs this block, light-on-a-dark-OS needs the guard above. */
:root[data-theme="dark"] {
  --color-bg: #0d0a15;
  --color-surface: #151021;
  --color-surface-raised: #1b1530;
  --color-border: #2b2340;
  --color-border-strong: #7a7196;
  --color-text: #ece9f7;
  --color-text-muted: #9f97b8;
  --color-text-inverted: #0d0a15;

  --color-accent: #b794f6;
  --color-accent-hover: #c9aefb;
  --color-accent-subtle: #241b3d;

  --color-brand-pink: #f0a0c8;
  --gradient-brand: linear-gradient(
    135deg,
    var(--color-accent) 0%,
    var(--color-brand-pink) 100%
  );

  --color-danger: #f2b8b5;
  --color-danger-subtle: #3a1a18;
  --color-danger-solid: #c0392b;
  --color-on-danger: #ffffff;
  --color-success: #8fd6a8;
  --color-success-subtle: #14301f;

  --color-focus-ring: #b794f6;


  /* One colour per materia, so a week is readable at a glance rather than
   * readable by reading. Assigned from the materia's own code and
   * overridable per student -- see schedule/palette.py for why eight, and
   * why derived from the code rather than the primary key.
   *
   * Every ink/fill pair below clears 4.5:1, and every ink clears 4.5:1 on
   * the page as well, because these are used as text on a tinted box and as
   * a plain label beside one. tests/test_design_tokens.py checks all
   * sixteen pairs in both themes.
   */
  --color-materia-violeta: #c4a6f9;
  --color-materia-violeta-fill: #251c3d;
  --color-materia-azul: #9dc0fb;
  --color-materia-azul-fill: #16203a;
  --color-materia-verde: #8ed9a8;
  --color-materia-verde-fill: #12291c;
  --color-materia-ambar: #e8c579;
  --color-materia-ambar-fill: #2b2210;
  --color-materia-rosa: #f5a5c6;
  --color-materia-rosa-fill: #331526;
  --color-materia-cyan: #8ed2e6;
  --color-materia-cyan-fill: #0f272f;
  --color-materia-naranja: #f2ab86;
  --color-materia-naranja-fill: #301a11;
  --color-materia-lila: #dba9f7;
  --color-materia-lila-fill: #2a1739;

  --shadow-sm: 0 1px 2px rgb(0 0 0 / 45%);
  --shadow-md: 0 4px 14px -2px rgb(0 0 0 / 55%);
  --shadow-lg: 0 12px 32px -8px rgb(0 0 0 / 65%);
}
