/*
 * attend. -- the application stylesheet: layer order, fonts, tokens, base,
 * layout and utilities. The components layer is static/css/components.css.
 *
 * Layers, lowest to highest priority: reset, tokens, base, layout, components,
 * utilities. Colours exist only as tokens ("Indigo", docs/design/visual-design.md,
 * listed in docs/design-tokens.json); every other layer refers to var(--attend-*).
 * tests/test_css_rules.py enforces: no hex/rgb/hsl colours outside the tokens
 * layer, no gradients, blur, backdrop-filter, filter or light-dark(), and shadows
 * only as the light theme's elevation tokens (none in the dark themes).
 *
 * Theme: <html data-theme="light|dark|system">, rendered by the server from the
 * user's profile (or the attend_theme cookie when signed out). "system" follows
 * prefers-color-scheme with a media query, so there is no flash on load.
 */

@layer reset, tokens, base, layout, components, utilities;

/* Bricolage Grotesque (display) and Figtree (body and UI), self-hosted
   (@fontsource-variable 5.3.0, SIL OFL 1.1; scripts/vendor_fetch.py). */

@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-display: swap;
  font-weight: 200 800;
  src: url("../vendor/fontsource-variable-bricolage-grotesque/5.3.0/bricolage-grotesque-latin-opsz-normal.ad33cbc6d232.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;
}

@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-display: swap;
  font-weight: 200 800;
  src: url("../vendor/fontsource-variable-bricolage-grotesque/5.3.0/bricolage-grotesque-latin-ext-opsz-normal.c4c7bb6a8d9b.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308,
    U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Figtree";
  font-style: normal;
  font-display: swap;
  font-weight: 300 900;
  src: url("../vendor/fontsource-variable-figtree/5.3.0/figtree-latin-wght-normal.202cfb54d4e6.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;
}

@font-face {
  font-family: "Figtree";
  font-style: normal;
  font-display: swap;
  font-weight: 300 900;
  src: url("../vendor/fontsource-variable-figtree/5.3.0/figtree-latin-ext-wght-normal.eca8d8b337ff.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308,
    U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}

/* ------------------------------------------------------------------ reset */
@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
  }

  body,
  h1,
  h2,
  h3,
  h4,
  p,
  figure,
  blockquote,
  dl,
  dd,
  ul,
  ol {
    margin: 0;
  }

  ul[role="list"],
  ol[role="list"] {
    list-style: none;
    padding: 0;
  }

  img,
  svg,
  video,
  canvas {
    display: block;
    max-width: 100%;
  }

  input,
  button,
  textarea,
  select {
    font: inherit;
    color: inherit;
  }

  button {
    cursor: pointer;
  }

  fieldset {
    margin: 0;
    padding: 0;
    border: 0;
    min-width: 0;
  }

  legend {
    padding: 0;
  }

  table {
    border-collapse: collapse;
  }

  [hidden] {
    display: none !important;
  }

  /* In the reset layer, !important outranks every later layer. */
  @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;
    }
  }
}

/* ----------------------------------------------------------------- tokens */
@layer tokens {
  /* Light: also the default before/without a data-theme attribute. */
  :root {
    color-scheme: light;
    /* Surfaces, borders and text */
    --attend-ground: #f5f6f8;
    --attend-surface: #ffffff;
    --attend-raised: #ffffff;
    --attend-border: #e3e5ea;
    --attend-border-strong: #878d99;
    --attend-field: #f1f3f6;
    --attend-field-edge: #e3e5ea;
    --attend-field-edge-hover: #c9cdd6;
    --attend-field-halo: #dfe4fb;
    --attend-scrollbar: #c3c8d1;
    --attend-text-strong: #15171c;
    --attend-text: #343842;
    --attend-text-muted: #636976;
    --attend-row-hover: #f8f9fb;
    /* Brand: primary button, active navigation, links, focus, progress */
    --attend-brand: #4a5bd0;
    --attend-brand-hover: #3c4cb8;
    --attend-on-brand: #ffffff;
    --attend-brand-tint: #eef1fd;
    --attend-brand-text: #3445b4;
    --attend-link-hover: #3c4cb8;
    --attend-accent: #4a5bd0;
    --attend-focus: #4a5bd0;
    --attend-hero: #eef1fd;
    --attend-hero-border: #dce2f8;
    /* Status families: tint, text (also the icon), solid fill */
    --attend-positive-bg: #edf0fc;
    --attend-positive-text: #3b4aa8;
    --attend-positive-solid: #4a5bd0;
    --attend-warning-bg: #f4eff8;
    --attend-warning-text: #6a4a86;
    --attend-danger-bg: #f9eef1;
    --attend-danger-text: #983d55;
    --attend-danger-solid: #983d55;
    --attend-info-bg: #ebf1f8;
    --attend-info-text: #3e5f86;
    --attend-inactive-bg: #eff0f4;
    --attend-inactive-text: #575a6a;
    /* Sidebar: deep ink in both themes */
    --attend-sidebar: #1b1e27;
    --attend-sidebar-border: #1b1e27;
    --attend-sidebar-text: #d9dce4;
    --attend-sidebar-strong: #f4f5f8;
    --attend-sidebar-muted: #a0a6b4;
    --attend-sidebar-hover: #252935;
    --attend-sidebar-active: #2e3344;
    --attend-sidebar-accent: #afbaff;
    --attend-sidebar-on-accent: #1b1e27;
    --attend-sidebar-scrollbar: #3b4150;
    /* Icons inside native controls (scripts/build_ui_icons.py) */
    --attend-icon-chevron: url("../img/ui/chevron-down-light.949ded7433bb.svg");
    --attend-icon-calendar: url("../img/ui/calendar-light.a4f21720a55a.svg");
    --attend-icon-clock: url("../img/ui/clock-light.86df85830f1a.svg");
    --attend-icon-clear: url("../img/ui/x-light.1a4c3831af32.svg");
    --attend-icon-check: url("../img/ui/check-light.b298186ba4dc.svg");
    --attend-icon-dot: url("../img/ui/dot-light.55d95ce78c3a.svg");
    /* Elevation (light theme only) */
    --attend-shadow-card: 0 1px 2px rgba(21, 23, 28, 0.04), 0 4px 16px rgba(21, 23, 28, 0.05);
    --attend-shadow-raised: 0 12px 40px rgba(21, 23, 28, 0.14), 0 2px 6px rgba(21, 23, 28, 0.06);
    --attend-shadow-primary: 0 4px 12px -6px rgba(74, 91, 208, 0.35);
    --attend-shadow-bar: 0 -4px 20px rgba(21, 23, 28, 0.05);
  }

  :root[data-theme="dark"] {
    color-scheme: dark;
    /* Surfaces, borders and text */
    --attend-ground: #0f1114;
    --attend-surface: #171a1f;
    --attend-raised: #20242b;
    --attend-border: #2c313a;
    --attend-border-strong: #6f7684;
    --attend-field: #20242b;
    --attend-field-edge: #2c313a;
    --attend-field-edge-hover: #3a404b;
    --attend-field-halo: #2a3366;
    --attend-scrollbar: #3f4552;
    --attend-text-strong: #f4f5f7;
    --attend-text: #d6d9df;
    --attend-text-muted: #9ca2ae;
    --attend-row-hover: #1c1f25;
    /* Brand: primary button, active navigation, links, focus, progress */
    --attend-brand: #5064d6;
    --attend-brand-hover: #4357c8;
    --attend-on-brand: #ffffff;
    --attend-brand-tint: #1e2446;
    --attend-brand-text: #afbaff;
    --attend-link-hover: #afbaff;
    --attend-accent: #afbaff;
    --attend-focus: #afbaff;
    --attend-hero: #1a1f38;
    --attend-hero-border: #2b3263;
    /* Status families: tint, text (also the icon), solid fill */
    --attend-positive-bg: #1e2446;
    --attend-positive-text: #b3bdff;
    --attend-positive-solid: #5064d6;
    --attend-warning-bg: #2a2136;
    --attend-warning-text: #d3b8ec;
    --attend-danger-bg: #351a21;
    --attend-danger-text: #f2a3b6;
    --attend-danger-solid: #a8475f;
    --attend-info-bg: #172434;
    --attend-info-text: #a8c5e8;
    --attend-inactive-bg: #252830;
    --attend-inactive-text: #b8bdc8;
    /* Sidebar: deep ink in both themes */
    --attend-sidebar: #13151b;
    --attend-sidebar-border: #252933;
    --attend-sidebar-text: #d9dce4;
    --attend-sidebar-strong: #f4f5f8;
    --attend-sidebar-muted: #a0a6b4;
    --attend-sidebar-hover: #1c1f27;
    --attend-sidebar-active: #262b38;
    --attend-sidebar-accent: #afbaff;
    --attend-sidebar-on-accent: #13151b;
    --attend-sidebar-scrollbar: #2e3344;
    /* Icons inside native controls (scripts/build_ui_icons.py) */
    --attend-icon-chevron: url("../img/ui/chevron-down-dark.66661755876e.svg");
    --attend-icon-calendar: url("../img/ui/calendar-dark.c405087f5759.svg");
    --attend-icon-clock: url("../img/ui/clock-dark.ab2f31ff728d.svg");
    --attend-icon-clear: url("../img/ui/x-dark.84334cbb7188.svg");
    --attend-icon-check: url("../img/ui/check-dark.92d02908a2bb.svg");
    --attend-icon-dot: url("../img/ui/dot-dark.d3c341f9dd9e.svg");
    /* Elevation (none in the dark themes) */
    --attend-shadow-card: none;
    --attend-shadow-raised: none;
    --attend-shadow-primary: none;
    --attend-shadow-bar: none;
  }

  @media (prefers-color-scheme: dark) {
    :root[data-theme="system"] {
      color-scheme: dark;
      /* Surfaces, borders and text */
      --attend-ground: #0f1114;
      --attend-surface: #171a1f;
      --attend-raised: #20242b;
      --attend-border: #2c313a;
      --attend-border-strong: #6f7684;
      --attend-field: #20242b;
      --attend-field-edge: #2c313a;
      --attend-field-edge-hover: #3a404b;
      --attend-field-halo: #2a3366;
      --attend-scrollbar: #3f4552;
      --attend-text-strong: #f4f5f7;
      --attend-text: #d6d9df;
      --attend-text-muted: #9ca2ae;
      --attend-row-hover: #1c1f25;
      /* Brand: primary button, active navigation, links, focus, progress */
      --attend-brand: #5064d6;
      --attend-brand-hover: #4357c8;
      --attend-on-brand: #ffffff;
      --attend-brand-tint: #1e2446;
      --attend-brand-text: #afbaff;
      --attend-link-hover: #afbaff;
      --attend-accent: #afbaff;
      --attend-focus: #afbaff;
      --attend-hero: #1a1f38;
      --attend-hero-border: #2b3263;
      /* Status families: tint, text (also the icon), solid fill */
      --attend-positive-bg: #1e2446;
      --attend-positive-text: #b3bdff;
      --attend-positive-solid: #5064d6;
      --attend-warning-bg: #2a2136;
      --attend-warning-text: #d3b8ec;
      --attend-danger-bg: #351a21;
      --attend-danger-text: #f2a3b6;
      --attend-danger-solid: #a8475f;
      --attend-info-bg: #172434;
      --attend-info-text: #a8c5e8;
      --attend-inactive-bg: #252830;
      --attend-inactive-text: #b8bdc8;
      /* Sidebar: deep ink in both themes */
      --attend-sidebar: #13151b;
      --attend-sidebar-border: #252933;
      --attend-sidebar-text: #d9dce4;
      --attend-sidebar-strong: #f4f5f8;
      --attend-sidebar-muted: #a0a6b4;
      --attend-sidebar-hover: #1c1f27;
      --attend-sidebar-active: #262b38;
      --attend-sidebar-accent: #afbaff;
      --attend-sidebar-on-accent: #13151b;
      --attend-sidebar-scrollbar: #2e3344;
      /* Icons inside native controls (scripts/build_ui_icons.py) */
      --attend-icon-chevron: url("../img/ui/chevron-down-dark.66661755876e.svg");
      --attend-icon-calendar: url("../img/ui/calendar-dark.c405087f5759.svg");
      --attend-icon-clock: url("../img/ui/clock-dark.ab2f31ff728d.svg");
      --attend-icon-clear: url("../img/ui/x-dark.84334cbb7188.svg");
      --attend-icon-check: url("../img/ui/check-dark.92d02908a2bb.svg");
      --attend-icon-dot: url("../img/ui/dot-dark.d3c341f9dd9e.svg");
      /* Elevation (none in the dark themes) */
      --attend-shadow-card: none;
      --attend-shadow-raised: none;
      --attend-shadow-primary: none;
      --attend-shadow-bar: none;
    }
  }

  :root {
    /* Earlier names the components still use (docs/design-tokens.json "alias"):
       each is one of the Indigo tokens above. Later redesign steps move the
       components onto the new names. */
    --attend-surface-subtle: var(--attend-ground);
    --attend-surface-hover: var(--attend-brand-tint);
    --attend-text-secondary: var(--attend-text-muted);
    --attend-edge: var(--attend-border);
    --attend-control-edge: var(--attend-border-strong);
    --attend-action: var(--attend-brand);
    --attend-action-text: var(--attend-on-brand);
    --attend-accent-line: var(--attend-focus);
    --attend-accent-soft: var(--attend-brand-tint);
    --attend-avatar-bg: var(--attend-ground);
    --attend-positive: var(--attend-positive-text);
    --attend-warning: var(--attend-warning-text);
    --attend-danger: var(--attend-danger-text);
    --attend-inactive: var(--attend-inactive-text);

    /* Projection is white/black in both themes (UI_UX §4 Projection). */
    --attend-projection-bg: #ffffff;
    --attend-projection-fg: #000000;

    --font-display: "Bricolage Grotesque", "Figtree", system-ui, sans-serif;
    --font-body: "Figtree", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-mono: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

    /* Type scale (visual-design.md §3): phone values here, desktop from 768px. */
    --text-display: 34px;
    --leading-display: 38px;
    --weight-display: 800;
    --tracking-display: -0.025em;
    --text-h1: 26px;
    --leading-h1: 32px;
    --weight-h1: 750;
    --tracking-h1: -0.02em;
    --text-h2: 19px;
    --leading-h2: 26px;
    --weight-h2: 700;
    --tracking-h2: -0.01em;
    --text-body: 16px;
    --leading-body: 24px;
    --weight-body: 450;
    --tracking-body: 0;
    --text-small: 14px;
    --leading-small: 20px;
    --weight-small: 500;
    --tracking-small: 0;
    --text-label: 12px;
    --leading-label: 16px;
    --weight-label: 700;
    --tracking-label: 0.08em;
    --text-button: 16px;
    --leading-button: 20px;
    --weight-button: 650;
    --tracking-button: 0;
    --text-num-xl: 64px;
    --leading-num-xl: 64px;
    --weight-num-xl: 800;
    --tracking-num-xl: -0.03em;
    --text-num-m: 20px;
    --leading-num-m: 24px;
    --weight-num-m: 700;
    --tracking-num-m: 0;
    --text-input-mobile: 16px;
    --text-meta: var(--text-label);
    --text-support: var(--text-small);
    --text-section: var(--text-h2);
    --text-course: var(--text-h1);
    --text-page: var(--text-h1);
    --weight-book: 450;
    --weight-medium: 500;
    --weight-semi: 550;
    --weight-bold: 600;
    --weight-strong: 650; /* names and course titles */

    /* Spacing: 4px base (visual-design.md §5) */
    --space-05: 2px;
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-8: 32px;
    --space-10: 40px;
    --space-14: 56px;
    --space-18: 72px;

    /* Radii (visual-design.md §4) */
    --r-xs: 8px;
    --r-sm: 12px;
    --r-md: 16px;
    --r-lg: 22px;
    --r-xl: 28px;
    --r-pill: 999px;
    --radius-control: var(--r-sm);
    --radius-panel: var(--r-md);
    --touch: 44px;

    --sidebar-w: 248px;
    --rail-w: 56px;
    --topbar-h: 56px;
    --bottom-nav-h: 64px;
    --content-max: 1440px;
    --narrow-max: 720px;
    /* Running text stays readable while pages fill the width. */
    --measure: 72ch;
  }

  @media (min-width: 768px) {
    :root {
      --text-display: 40px;
      --leading-display: 44px;
      --text-h1: 28px;
      --leading-h1: 34px;
      --text-h2: 18px;
      --leading-h2: 24px;
      --text-body: 15px;
      --leading-body: 22px;
      --text-small: 13px;
      --leading-small: 18px;
      --text-label: 12px;
      --leading-label: 16px;
      --text-button: 15px;
      --leading-button: 20px;
      --text-num-xl: 72px;
      --leading-num-xl: 72px;
      --text-num-m: 20px;
      --leading-num-m: 24px;
    }
  }
}

/* ------------------------------------------------------------------- base */
@layer base {
  html {
    background: var(--attend-ground);
  }

  /* Scrollbars (owner, 2026-11-01): thin, a token thumb on a clear track.
     scrollbar-color inherits; the ink sidebar sets its own (shell.css). */
  :root {
    scrollbar-color: var(--attend-scrollbar) transparent;
  }

  * {
    scrollbar-width: thin;
  }

  /* The same for browsers without scrollbar-color (Safari). Chromium and
     Firefox use the two properties above and ignore these. */
  ::-webkit-scrollbar {
    inline-size: 8px;
    block-size: 8px;
  }

  ::-webkit-scrollbar-track {
    background-color: transparent;
  }

  ::-webkit-scrollbar-thumb {
    border: 2px solid transparent;
    border-radius: var(--r-pill);
    background-clip: padding-box;
    background-color: var(--attend-scrollbar);
  }

  ::selection {
    background-color: var(--attend-brand-tint);
    color: var(--attend-text-strong);
  }

  body {
    min-height: 100vh;
    min-height: 100dvh;
    background: var(--attend-ground);
    color: var(--attend-text);
    font-family: var(--font-body);
    font-size: var(--text-body);
    font-weight: var(--weight-body);
    line-height: var(--leading-body);
  }

  h1,
  h2,
  h3,
  h4 {
    color: var(--attend-text-strong);
    line-height: 1.25;
    text-wrap: balance;
  }

  /* Display text: Bricolage Grotesque grows chunkier at large sizes (§1). */
  h1,
  h2,
  .brand__mark,
  .course-card__title,
  .round-panel__count,
  .presenter__code {
    font-family: var(--font-display);
    font-optical-sizing: auto;
  }

  h1 {
    font-size: var(--text-h1);
    font-weight: var(--weight-h1);
    letter-spacing: var(--tracking-h1);
    line-height: var(--leading-h1);
  }

  h2 {
    font-size: var(--text-h2);
    font-weight: var(--weight-h2);
    letter-spacing: var(--tracking-h2);
    line-height: var(--leading-h2);
  }

  h3 {
    font-size: var(--text-body);
    font-weight: var(--weight-strong);
  }

  p {
    text-wrap: pretty;
  }

  /* Times, countdowns, counts and IDs keep their width (§1). */
  time,
  output,
  .tabular,
  .num,
  .count-badge,
  .round-panel__count,
  .round-card__tally,
  .presenter__countdown,
  .presenter__code,
  .day-row__time,
  .roster-student__id,
  .activation-code {
    font-variant-numeric: tabular-nums;
  }

  /* --brand-text in both themes: --brand is too dim as text on dark (3.43:1). */
  a {
    color: var(--attend-brand-text);
    text-underline-offset: 2px;
  }

  a:hover {
    color: var(--attend-link-hover);
  }

  code,
  kbd {
    font-family: var(--font-mono);
    font-size: 0.92em;
  }

  /* The design's ring (3px gap, 2px ring) as an outline: also in forced colours. */
  :focus-visible {
    outline: 2px solid var(--attend-focus);
    outline-offset: 3px;
  }

  ::placeholder {
    color: var(--attend-text-muted);
    opacity: 1;
  }

  /* Inputs (visual-design.md §7): 48px on phones, 40px from 768px. Soft
     filled (owner, 2026-11-01): a tinted fill and a hairline edge that
     darkens on hover; the label and the fill identify the field. */
  input:not([type="checkbox"], [type="radio"]),
  select,
  textarea {
    width: 100%;
    min-height: 48px;
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--attend-field-edge);
    border-radius: var(--r-sm);
    background-color: var(--attend-field);
    color: var(--attend-text-strong);
    caret-color: var(--attend-focus);
    transition:
      border-color 120ms ease,
      background-color 120ms ease;
  }

  input:not([type="checkbox"], [type="radio"]):hover,
  select:hover,
  textarea:hover {
    border-color: var(--attend-field-edge-hover);
  }

  /* Focus: the surface, the focus edge and a soft halo. The halo is an
     outline at the edge (not a shadow), so it shows in the dark themes and
     in forced colours too. */
  input:not([type="checkbox"], [type="radio"]):focus-visible,
  select:focus-visible,
  textarea:focus-visible {
    background-color: var(--attend-surface);
    border-color: var(--attend-focus);
    outline: 3px solid var(--attend-field-halo);
    outline-offset: 0;
  }

  input[type="search"] {
    border-radius: var(--r-pill);
  }

  /* Compact for a mouse only; a touched tablet keeps 48px fields. */
  @media (min-width: 768px) and (pointer: fine) {
    input:not([type="checkbox"], [type="radio"]),
    select {
      min-height: 40px;
    }
  }

  /* Native parts drawn in the Indigo look (owner, 2026-11-01). The pop-up
     calendar, a select's open list and the date format stay the browser's;
     color-scheme gives them the theme. Icons: --attend-icon-* tokens. */
  select {
    appearance: none;
    padding-inline-end: var(--space-8);
    background-image: var(--attend-icon-chevron);
    background-repeat: no-repeat;
    background-position: right var(--space-3) center;
    background-size: 16px;
  }

  select[multiple],
  select[size]:not([size="1"]) {
    padding-inline-end: var(--space-4);
    background-image: none;
  }

  ::-webkit-calendar-picker-indicator {
    inline-size: 18px;
    block-size: 18px;
    padding: 0;
    color: transparent;
    background-image: var(--attend-icon-calendar);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 18px;
    opacity: 1;
    cursor: pointer;
  }

  input[type="time"]::-webkit-calendar-picker-indicator {
    background-image: var(--attend-icon-clock);
  }

  ::-webkit-search-cancel-button {
    appearance: none;
    inline-size: 16px;
    block-size: 16px;
    background-image: var(--attend-icon-clear);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 16px;
    cursor: pointer;
  }

  /* No spinners: the value is typed (arrow keys still step it). */
  input[type="number"] {
    appearance: textfield;
  }

  ::-webkit-inner-spin-button,
  ::-webkit-outer-spin-button {
    appearance: none;
    margin: 0;
  }

  textarea {
    resize: vertical;
  }

  /* Chrome paints its autofill colour with !important; holding that
     transition keeps the field's own fill, and the text keeps its colour. */
  input:autofill {
    -webkit-text-fill-color: var(--attend-text-strong);
    transition: background-color 600000s 0s, color 600000s 0s;
  }

  input[type="checkbox"],
  input[type="radio"] {
    flex: none;
    inline-size: 18px;
    block-size: 18px;
    margin: 0;
    appearance: none;
    border: 1.5px solid var(--attend-border-strong);
    background-color: var(--attend-surface);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 14px;
    vertical-align: middle;
    cursor: pointer;
  }

  input[type="checkbox"] {
    border-radius: var(--r-xs);
  }

  input[type="radio"] {
    border-radius: 50%;
  }

  input[type="checkbox"]:hover,
  input[type="radio"]:hover {
    border-color: var(--attend-accent);
  }

  /* --accent: indigo in light, lavender in dark (brand would be too dim
     for a small indicator there); the mark is ink on lavender. */
  input[type="checkbox"]:checked,
  input[type="radio"]:checked {
    border-color: var(--attend-accent);
    background-color: var(--attend-accent);
  }

  input[type="checkbox"]:checked {
    background-image: var(--attend-icon-check);
  }

  input[type="radio"]:checked {
    background-image: var(--attend-icon-dot);
  }

  input[type="checkbox"]:disabled,
  input[type="radio"]:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  input[aria-invalid="true"],
  select[aria-invalid="true"],
  textarea[aria-invalid="true"] {
    border-color: var(--attend-danger-text);
    outline: 3px solid var(--attend-danger-bg);
    outline-offset: 0;
  }

  /* iOS zooms into inputs smaller than 16px. */
  @media (max-width: 767px) {
    input,
    select,
    textarea {
      font-size: var(--text-input-mobile);
    }
  }

  /* Disclosure headings are touch targets too (44px, UI_UX §2). A simple
     chevron replaces the browser's triangle: it points down when closed and
     up when open. */
  summary {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--touch);
    min-inline-size: var(--touch);
    padding-block: var(--space-3);
    list-style: none;
    cursor: pointer;
  }

  summary::-webkit-details-marker {
    display: none;
  }

  summary::after {
    content: "";
    flex: none;
    inline-size: 8px;
    block-size: 8px;
    margin-block-start: -4px;
    border-inline-end: 2px solid currentColor;
    border-block-end: 2px solid currentColor;
    transform: rotate(45deg);
  }

  details[open] > summary::after {
    margin-block-start: 4px;
    transform: rotate(-135deg);
  }

  hr {
    border: 0;
    border-top: 1px solid var(--attend-edge);
    margin: var(--space-6) 0;
  }
}

/* ----------------------------------------------------------------- layout */
@layer layout {
  .skip-link {
    position: absolute;
    left: var(--space-3);
    top: var(--space-3);
    z-index: 100;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-control);
    background: var(--attend-action);
    color: var(--attend-action-text);
    font-weight: var(--weight-medium);
    transform: translateY(-200%);
  }

  .skip-link:focus {
    transform: none;
  }

  /* App shell: sidebar >=1024px, 56px rail 768-1023px, top bar + bottom nav <768px. */
  .shell {
    min-height: 100vh;
    min-height: 100dvh;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }

  .sidebar {
    display: none;
  }

  .topbar {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    min-height: var(--topbar-h);
    padding: var(--space-1) var(--space-2) var(--space-1) var(--space-4);
    padding-top: max(var(--space-1), env(safe-area-inset-top));
    border-bottom: 1px solid var(--attend-edge);
    background: var(--attend-surface);
  }

  .main {
    min-width: 0;
    padding: var(--space-6) var(--space-4);
    padding-left: max(var(--space-4), env(safe-area-inset-left));
    padding-right: max(var(--space-4), env(safe-area-inset-right));
  }

  .shell--bottom-nav .main {
    padding-bottom: calc(var(--bottom-nav-h) + var(--space-6) + env(safe-area-inset-bottom));
  }

  .main:focus {
    outline: none;
  }

  /* Pages use the width of wide screens, centered (owner, 2026-11-02, after
     the Rad360 reference); running text keeps --measure, forms --narrow-max. */
  .content {
    max-width: var(--content-max);
    margin-inline: auto;
  }

  .content > p {
    max-width: var(--measure);
  }

  /* Flow: blocks in the content column and in a plain section are spaced, so
     a paragraph never sits on the next form, table or heading. Zero
     specificity in this layer: a component's own margin wins, and sibling
     margins collapse, so a larger gap is never added to. Sections that are
     flex or grid containers space their children with gap instead. */
  :where(.content, section.section:not(.stack, .form, .roster-card)) > :where(* + *) {
    margin-block-start: var(--space-4);
  }

  .content--narrow {
    max-width: var(--narrow-max);
    margin-inline: auto;
  }

  .main-foot {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
    margin-top: var(--space-8);
    padding-top: var(--space-4);
    border-top: 1px solid var(--attend-edge);
    font-size: var(--text-meta);
    color: var(--attend-text-secondary);
  }

  /* One line: a long university name ends in an ellipsis (the full name is
     its title) instead of wrapping the footer. */
  .main-foot__org {
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  @media (min-width: 768px) {
    .shell {
      grid-template-columns: var(--rail-w) minmax(0, 1fr);
      grid-template-rows: minmax(0, 1fr);
    }

    .topbar,
    .bottom-nav {
      display: none;
    }

    .shell--bottom-nav .main {
      padding-bottom: var(--space-8);
    }

    .sidebar {
      position: sticky;
      top: 0;
      height: 100vh;
      height: 100dvh;
      overflow-y: auto;
      display: flex;
      flex-direction: column;
      gap: var(--space-4);
      padding: var(--space-6) var(--space-1) var(--space-4);
      border-right: 1px solid var(--attend-edge);
      /* A card-white column on the ground until the ink sidebar lands. */
      background: var(--attend-surface);
    }

    .main {
      padding: var(--space-8) var(--space-8) var(--space-6);
    }
  }

  @media (min-width: 1024px) {
    .shell {
      grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
    }

    .shell[data-nav="collapsed"] {
      grid-template-columns: var(--rail-w) minmax(0, 1fr);
    }

    .shell:not([data-nav="collapsed"]) .sidebar {
      padding-inline: var(--space-3);
    }

    .main {
      padding: 28px var(--space-8) var(--space-6);
    }
  }

  /* Bare pages: sign-in, verification, errors. */
  .bare {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    padding: var(--space-8) var(--space-4) var(--space-6);
  }

  .bare__inner {
    width: 100%;
    max-width: 400px;
    margin: auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
  }

  .bare__foot {
    margin-top: var(--space-8);
    text-align: center;
  }
}

/* -------------------------------------------------------------- utilities */
@layer utilities {
  .visually-hidden {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip-path: inset(50%) !important;
    white-space: nowrap !important;
    border: 0 !important;
  }

  .muted {
    color: var(--attend-text-secondary);
  }

  .small {
    font-size: var(--text-small);
  }

  .stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
  }

  .stack--tight {
    gap: var(--space-2);
  }

  .mt-0 {
    margin-top: 0;
  }

  .tabular {
    font-variant-numeric: tabular-nums;
  }

  .only-mobile {
    display: none;
  }

  @media (max-width: 767px) {
    .only-mobile {
      display: revert;
    }

    .hide-mobile {
      display: none;
    }
  }
}
