/* Baseline styles. No framework: system fonts, a handful of custom properties, light and dark. */
:root {
  color-scheme: light dark;
  --bg: #f6f7f9;
  --surface: #ffffff;
  --text: #1c2330;
  --muted: #5b6676;
  --border: #d9dee6;
  --accent: #1f5fbf;
  --ok: #1a7f4b;
  --bad: #b3261e;
  --radius: 8px;

  /* School branding. These defaults are the built-in look. A school that has chosen colors gets
     a second stylesheet, /branding/theme.css, which sets these same names (and --accent) to
     values worked out from its three colors. The page colors above are repeated in
     Branding/BrandColor.swift (PagePalette), which checks contrast against them: change both. */
  --bar: var(--surface);
  --on-bar: var(--text);
  --bar-link: var(--accent);
  --bar-muted: var(--muted);
  --bar-stripe: 1px solid var(--border);
  --button-fill: var(--accent);
  --button-text: var(--surface);
  --button-edge: var(--accent);
  --highlight: var(--accent);
  --on-highlight: var(--surface);
  --highlight-edge: var(--accent);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14181f;
    --surface: #1d232d;
    --text: #e6e9ef;
    --muted: #9aa5b5;
    --border: #2f3846;
    --accent: #7fb0f5;
    --ok: #5fd08f;
    --bad: #ff8a80;
  }
}

*, *::before, *::after { box-sizing: border-box; }

/* Text size. Everything is sized in rem, so changing the root size scales text, spacing and tap
   targets together. --base is the device's starting size: a computer with a mouse starts a step
   smaller than a phone, to fit more on a screen read from further away. --scale is the person's
   own choice from the "Aa" menu (TextSize in Access/UserPreferences.swift), carried as a class
   on <body>; the CSP allows no inline styles, so it cannot be written onto <html> itself. */
html { --base: 100%; --scale: 1; font-size: calc(var(--base) * var(--scale)); }
@media (min-width: 64rem) and (pointer: fine) { html { --base: 93.75%; } }
html:has(> body.text-small) { --scale: 0.875; }
html:has(> body.text-large) { --scale: 1.125; }
html:has(> body.text-larger) { --scale: 1.25; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 1rem/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

a { color: var(--accent); }

.site-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
  padding: 0.75rem 1rem;
  color: var(--on-bar);
  background: var(--bar);
  border-bottom: var(--bar-stripe);
}

.brand { display: flex; align-items: center; gap: 0.6rem; font-weight: 700; text-decoration: none; color: var(--on-bar); }
.brand-icon { width: 2rem; height: 2rem; object-fit: contain; }
.product-mark { width: 1.25em; height: 1.25em; flex: none; vertical-align: -0.25em; margin-right: 0.3em; }
.brand .product-mark { width: 1.5rem; height: 1.5rem; margin-right: 0; }
.muted { color: var(--muted); }
.site-header .product, .site-header .muted { color: var(--bar-muted); }
.site-header .product { font-size: 0.875rem; opacity: 0.85; }

main { max-width: 60rem; margin: 0 auto; padding: 1.5rem 1rem; }

h1 { margin: 0 0 0.25rem; font-size: 1.75rem; }
h2 { margin: 0 0 0.75rem; font-size: 1.125rem; }

.card {
  margin-top: 1.5rem;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.25rem 1rem; margin: 0 0 1rem; }
dt { color: var(--muted); }
dd { margin: 0; }
.ok { color: var(--ok); }
.bad { color: var(--bad); }

button {
  padding: 0.4rem 0.9rem;
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
}
button:hover { border-color: var(--accent); }

/* A link that starts something (sign in with Google) rather than going somewhere. */
a.button, .button-look {
  display: inline-block;
  padding: 0.5rem 1rem;
  color: var(--button-text);
  background: var(--button-fill);
  /* An outline drawn inside the button, so it takes no room. Same color as the button unless
     the school's color is too close to the page's. */
  box-shadow: inset 0 0 0 1px var(--button-edge);
  border-radius: var(--radius);
  font-weight: 600;
  text-decoration: none;
}
a.button:hover { filter: brightness(1.1); }

.notice {
  margin-top: 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--bad);
  border-radius: var(--radius);
  color: var(--bad);
}

/* htmx's own indicator styles are switched off (they are injected inline, which the CSP blocks). */
.htmx-indicator { opacity: 0; transition: opacity 200ms ease-in; }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }

.site-header { align-items: center; }
.signout { display: flex; align-items: center; gap: 0.75rem; }

/* The header's links. A phone gets: school, "Aa", Menu. From 56rem the links come out of the
   menu and sit across the bar, grouped by area, with the person's own links at the far right.
   Layout.swift draws both versions; only one is ever shown. */
.wide-only { display: none !important; }
.site-header .text-size { margin-left: auto; }
/* On a phone everything stays on one row: a long school name wraps within itself instead of
   pushing the buttons down. */
@media (max-width: 55.99rem) {
  .site-header { position: relative; flex-wrap: nowrap; }
  .site-header .brand { flex: 0 1 auto; min-width: 0; }
}
/* The name beside "Sign out" and the product name only once there is plenty of room. */
.site-header .product, .you .signout .muted { display: none; }
@media (min-width: 56rem) {
  .wide-only { display: flex !important; }
  .narrow-only { display: none !important; }
}
@media (min-width: 64rem) {
  .site-header .product, .you .signout .muted { display: inline; }
}
.site-nav { align-items: center; gap: 0.25rem 0; }
.site-nav a, .you a { color: var(--bar-link); }
.nav-group { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; }
/* A thin rule between areas: everyday pages, then administration. */
.site-nav .nav-group + .nav-group { margin-left: 1rem; padding-left: 1rem; border-left: 1px solid var(--bar-muted); }
.you { align-items: center; gap: 1rem; }

/* The phone's menu: a button in the bar and a panel that drops under it. */
.site-menu { flex: none; margin: 0; }
/* An iPhone-style badge: a red circle (a pill past 9) with a thin white ring, the number centred. */
.count-badge { display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box; min-width: 1.5em; height: 1.5em; margin-left: 0.3em; padding: 0 0.4em; border-radius: 999px; border: 1.5px solid #fff; font-size: 0.75em; font-weight: 700; line-height: 1; vertical-align: 0.15em; color: #fff; background: #ff3b30; box-shadow: 0 0 0 0.5px rgb(0 0 0 / 0.15); font-variant-numeric: tabular-nums; }
/* Approvals waiting (emails from teachers): the school's own color, so they read apart from tasks. */
.count-badge.approval { background: #1f5fbf; }
.site-menu > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 2.75rem;
  padding: 0 0.75rem;
  color: var(--bar-link);
  border: 1px solid var(--bar-link);
  border-radius: var(--radius);
  list-style: none;
}
.site-menu > summary::-webkit-details-marker { display: none; }
.site-menu[open] > summary { margin-bottom: 0; color: var(--button-text); background: var(--button-fill); border-color: var(--button-edge); }
.menu-icon { font-size: 1.125rem; line-height: 1; }
.site-menu-panel {
  /* Hangs from the header (the nearest positioned box), so it lines up with the page edge. */
  position: absolute;
  top: calc(100% + 0.25rem);
  right: 0.5rem;
  z-index: 2;
  width: min(20rem, calc(100% - 1rem));
  padding: 0.25rem 0;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 4px 16px rgb(0 0 0 / 0.18);
}
.site-menu-panel .nav-group { flex-direction: column; gap: 0; padding: 0.25rem 0; }
.site-menu-panel .nav-group + .nav-group, .site-menu-panel .signout { border-top: 1px solid var(--border); }
.site-menu-panel a { display: flex; align-items: center; min-height: 2.75rem; padding: 0 1rem; color: var(--accent); text-decoration: none; }
.site-menu-panel a:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.site-menu-panel .signout { justify-content: space-between; padding: 0.5rem 1rem; }
.site-menu-panel .signout .muted { overflow-wrap: anywhere; }
.site-menu-panel button { min-height: 2.75rem; }

table { width: 100%; border-collapse: collapse; }
th, td { padding: 0.4rem 0.6rem; text-align: left; border-bottom: 1px solid var(--border); vertical-align: top; }
th { color: var(--muted); font-weight: 600; }
tbody tr:last-child td { border-bottom: none; }
td form { margin: 0; }
ul { margin: 0; padding-left: 1.25rem; }

/* ---- Slice 2D: admin screens, viewers, error pages ---- */

.small { font-size: 0.875rem; }
.nowrap { white-space: nowrap; }
.hint { display: block; color: var(--muted); font-size: 0.875rem; font-weight: 400; }

.flash {
  margin-top: 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--ok);
  border-radius: var(--radius);
  color: var(--ok);
}
/* Stays in view while the page scrolls: the failed click may be well below the top. */
#flash { position: sticky; top: 0.5rem; z-index: 1; }
#flash:empty { display: none; }
#flash .notice { margin-top: 0; background: var(--surface); box-shadow: 0 2px 8px rgb(0 0 0 / 0.15); }

/* The second row of links, under the header, on admin screens. */
.subnav { display: flex; flex-wrap: wrap; gap: 0.25rem 0.5rem; margin: 0.75rem 0 0; }
.subnav a { padding: 0.3rem 0.7rem; border-radius: var(--radius); text-decoration: none; }
.subnav a:hover { background: var(--surface); }
.subnav a[aria-current="page"] { background: var(--button-fill); color: var(--button-text); box-shadow: inset 0 0 0 1px var(--button-edge); font-weight: 600; }
.subnav.tabs { margin-top: 1rem; }

/* A grouped menu down the left with the chosen page beside it (SideMenu.swift). On a phone the
   area's first page (.at-home) is the menu; every other page hides it and shows "← Admin". */
.side-menu-search { margin: 0 0 0.75rem; max-width: none; }
.side-menu-heading { margin: 1rem 0 0.25rem; padding: 0 0.6rem; font-size: 0.8125rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.side-menu-heading:first-child { margin-top: 0; }
.side-menu ul { margin: 0; padding: 0; list-style: none; }
.side-menu li a { display: flex; gap: 0.6rem; align-items: flex-start; padding: 0.5rem 0.6rem; border-radius: var(--radius); text-decoration: none; color: var(--text); }
.side-menu li a:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.side-menu li a[aria-current="page"] { background: color-mix(in srgb, var(--accent) 14%, transparent); box-shadow: inset 3px 0 0 var(--accent); }
.menu-item-icon { flex: none; width: 1.25rem; height: 1.25rem; margin-top: 0.1rem; color: var(--accent); }
.side-menu-title { display: block; font-weight: 600; color: var(--accent); }
.side-menu-text { display: block; margin-top: 0.1rem; font-size: 0.875rem; line-height: 1.35; color: var(--muted); }
.side-layout:not(.at-home) > .side-menu { display: none; }
.glance { margin: 0; }
/* The Find box (SiteSearch.swift): in the header its list drops down under it once something is
   typed; in the phone's menu it opens in place. */
.site-find { position: relative; margin: 0; }
.site-find input[type="search"] { width: 100%; }
.site-find-slot { flex: 1 1 12rem; max-width: 18rem; margin-left: auto; }
.site-find-slot + .text-size { margin-left: 0; }
.site-find-panel { display: none; }
.site-find:not([data-closed]):has(input:not(:placeholder-shown)) .site-find-panel { display: block; }
.site-find-header .site-find-panel {
  position: absolute; top: calc(100% + 0.25rem); right: 0; z-index: 3;
  width: min(26rem, 90vw); max-height: 70vh; overflow-y: auto; padding: 0.5rem;
  color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: 0 4px 16px rgb(0 0 0 / 0.18);
}
.site-find-menu { padding: 0.5rem 1rem; border-bottom: 1px solid var(--border); }
.site-find-menu .site-find-panel { margin-top: 0.5rem; }
.find-list { margin: 0 0 0.5rem; padding: 0; list-style: none; }
.find-list a, .site-menu-panel .find-list a { display: flex; gap: 0.6rem; align-items: flex-start; min-height: 0; padding: 0.45rem 0.5rem; border-radius: var(--radius); text-decoration: none; color: var(--text); }
.find-list a:hover, .find-list a:focus-visible { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.find-page-results { margin-top: 1rem; }
.find-page-results.side-menu { position: static; max-height: none; border-right: 0; }
/* On a phone the overview's own heading goes above the menu. */
.side-layout.at-home { display: flex; flex-direction: column-reverse; justify-content: flex-end; }
.side-layout.at-home > .side-detail { margin-bottom: 1rem; }
@media (min-width: 56rem) {
  /* The menu takes a fixed column pinned to the left edge, and the page all that is left: a wider
     window widens the page, not the gap beside the menu (Eric, 2026-10-27). */
  main:has(> .side-layout), main.wide:has(> .side-layout) { max-width: none; margin: 0; }
  .side-layout, .side-layout.at-home { display: grid; grid-template-columns: 17rem minmax(0, 1fr); gap: 2rem; align-items: start; }
  .side-layout:not(.at-home) > .side-menu { display: block; }
  .side-layout.at-home > .side-detail { margin-bottom: 0; }
  /* It scrolls on its own, which clips anything drawn past its edge: the 4px of padding (taken
     back by the margin, so nothing moves) leaves room for the 3px focus ring. */
  .side-menu { position: sticky; top: 1rem; max-height: calc(100vh - 2rem); overflow-y: auto; margin: -4px 0 0 -4px; padding: 4px 0.5rem 4px 4px; border-right: 1px solid var(--border); }
}

/* Forms: one field per line, label above. */
.stack > * + * { margin-top: 1rem; }
.stack p { margin-bottom: 0; }
.field { display: block; max-width: 32rem; }
.field-label { display: block; margin-bottom: 0.25rem; font-weight: 600; }
input[type="text"], input[type="email"], input[type="tel"], input[type="date"], input[type="datetime-local"], input[type="search"], select, textarea {
  width: 100%;
  min-height: 2.5rem;
  padding: 0.4rem 0.6rem;
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
textarea { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.875rem; }
.field:has(textarea) { max-width: none; }
input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
input:disabled, select:disabled { color: var(--muted); background: var(--bg); }

.checklist { margin: 1rem 0 0; padding: 0; border: none; }
.checklist legend { padding: 0; margin-bottom: 0.25rem; font-weight: 600; }
.checklist label { display: flex; gap: 0.6rem; align-items: baseline; padding: 0.3rem 0; }
.checklist input { flex: none; }

.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
button.primary { color: var(--button-text); background: var(--button-fill); border-color: var(--button-edge); font-weight: 600; }
button.danger { color: var(--bad); border-color: var(--bad); }
button.primary:hover, button.danger:hover { filter: brightness(1.1); }

details { margin-top: 1rem; }
summary { cursor: pointer; color: var(--accent); font-weight: 600; }
details[open] > summary { margin-bottom: 1rem; }

.filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 1rem; }
.filters .field { flex: 1 1 12rem; max-width: 20rem; }
.inline-fields { display: flex; flex-wrap: wrap; gap: 1rem; }
.inline-fields .field { flex: 1 1 7rem; max-width: 9rem; }

/* The permissions matrix: one row per rule, a select per action. */
/* Relative too, or a visually hidden heading past the card's edge widens the whole page. */
.card:has(> table) { position: relative; overflow-x: auto; }
main:has(.matrix) { max-width: 84rem; }
.matrix select { min-width: 5.5rem; }
.matrix select[name="scope"] { min-width: 11rem; }
.matrix input[name="notes"] { min-width: 9rem; }
.matrix td { vertical-align: middle; }
.matrix select.state-full { border-color: var(--ok); }
.matrix select.state-none { color: var(--muted); }
.matrix tr.unsaved td { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.matrix tr.unsaved button { color: var(--button-text); background: var(--button-fill); border-color: var(--button-edge); }
.row-actions { white-space: nowrap; }
.row-actions form { display: inline; }
.saved { margin-left: 0.5rem; color: var(--ok); font-weight: 600; animation: fade-away 4s forwards; }
@keyframes fade-away { 0%, 60% { opacity: 1; } 100% { opacity: 0; } }

.old { color: var(--muted); text-decoration: line-through; }
.pager { display: flex; gap: 1.5rem; margin: 1rem 0 0; }
.pager:empty { display: none; }

.error-page { max-width: 36rem; margin: 3rem auto; }
.error-page h1 { margin-bottom: 0.75rem; }
.error-page .actions { margin: 1.5rem 0; }
pre { overflow-x: auto; padding: 0.75rem; background: var(--bg); border-radius: var(--radius); font-size: 0.8125rem; white-space: pre-wrap; }

/* ---- Phase 2.5: school branding ---- */

.badge {
  display: inline-block;
  padding: 0.1rem 0.6rem;
  color: var(--on-highlight);
  background: var(--highlight);
  box-shadow: inset 0 0 0 1px var(--highlight-edge);
  border-radius: 999px;
  font-size: 0.875rem;
  font-weight: 600;
}

.after-form { margin-top: 1rem; }
.advice { margin: 1rem 0 0; padding: 0.6rem 1rem; border: 1px solid var(--border); border-radius: var(--radius); color: var(--muted); }

.color-field { display: flex; gap: 0.5rem; align-items: center; }
.color-field input[type="color"] { flex: none; width: 3rem; height: 2.75rem; padding: 0.15rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); cursor: pointer; }
.color-field input[type="text"] { max-width: 9rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
input[type="file"] { max-width: 100%; font: inherit; }

.icon-current { display: flex; align-items: center; gap: 1rem; }
.icon-current img { flex: none; object-fit: contain; border: 1px solid var(--border); border-radius: var(--radius); }

/* The two mock pages on the branding screen. Each box is given a whole set of page and brand
   colors by /admin/branding/preview.css, so one shows light mode and the other dark whatever
   the device itself is set to. */
.preview-pair { margin-top: 1rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; }
.brand-preview { margin: 0; }
.preview-page { overflow: hidden; color: var(--text); background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); }
.preview-body { margin: 0.75rem; padding: 0.75rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
.preview-body p { margin: 0; }
.preview-body p + p { margin-top: 0.75rem; }
.preview-link { text-decoration: underline; }
table.contrast { margin-top: 1rem; font-size: 0.875rem; }


/* Sign-in and the account page are phone screens: buttons a thumb can hit (44px and up). */
button.tall { min-height: 2.75rem; padding: 0.5rem 1.1rem; }
button:disabled { opacity: 0.55; cursor: default; }
.passkey .notice { margin-bottom: 0; }
[hidden] { display: none !important; }
.passkey-list { list-style: none; margin: 0; padding: 0; }
.passkey-list li { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem 1rem; padding: 0.75rem 0; border-top: 1px solid var(--border); }
.passkey-list li > div { flex: 1 1 14rem; }
.passkey-list form { margin: 0; }


/* ---- Sign in as another user ---- */

/* The bar across every page while an administrator is signed in as someone else. Its colors are
   fixed, not the school's: it has to look like a warning under any branding, light or dark.
   Black on this amber is 11:1. A phone screen (it rides on parents' pages), so it stacks. */
.acting-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  margin: 0;
  padding: 0.6rem 1rem;
  color: #1c1300;
  background: #ffc940;
  border-bottom: 2px solid #1c1300;
}
.acting-bar p { flex: 1 1 16rem; margin: 0; }
.acting-bar button { flex: none; color: #1c1300; background: #fff8e1; border-color: #1c1300; font-weight: 600; }

/* A server whose email is held or redirected (MailGuard): staff should know a family wasn't emailed. */
.test-server-bar {
  margin: 0;
  padding: 0.4rem 1rem;
  font-size: 0.9rem;
  color: #fff;
  background: #8a1c1c;
}

/* A copy of real data (CopyNotice). Not the test-server red, so the two read as two facts. */
.copy-of-real-data-bar {
  margin: 0;
  padding: 0.4rem 1rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: #1a1a1a;
  background: #f2c94c;
}
@media print {
  .copy-of-real-data-bar { background: none; border: 1px solid; }
}


/* ---- Phase 4D: the directory. A phone screen first ---- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* A link that looks like a quiet button: big enough for a thumb, not as loud as a primary one. */
a.button-quiet {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  text-decoration: none;
  overflow-wrap: anywhere;
}
a.button-quiet:hover { border-color: var(--accent); }

/* Search box full width on a phone; the filters wrap under it. */
.directory-search { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.75rem 1rem; margin-top: 1rem; }
.directory-search .search-field { flex: 1 1 100%; max-width: none; }
.directory-search input[type="search"] { min-height: 2.75rem; font-size: 1.0625rem; }
.directory-search .grade-field { flex: 1 1 10rem; max-width: 14rem; }
.directory-search select { min-height: 2.75rem; }
@media (min-width: 40rem) {
  .directory-search .search-field { flex: 1 1 18rem; }
}
/* While htmx is on the page the list follows the typing, so the button is only a fallback. It
   stays for keyboards and for anyone whose browser has scripts off. */
.search-button { flex: none; }

/* Everyone / Students / Staff as one row of joined buttons. */
.segmented { display: inline-flex; margin: 0; padding: 0; border: none; }
.segmented label { position: relative; }
.segmented input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.segmented span {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--border);
  background: var(--surface);
}
.segmented label + label span { border-left: none; }
.segmented label:first-of-type span { border-radius: var(--radius) 0 0 var(--radius); }
.segmented label:last-of-type span { border-radius: 0 var(--radius) var(--radius) 0; }
.segmented input:checked + span { color: var(--button-text); background: var(--button-fill); border-color: var(--button-edge); font-weight: 600; }
.segmented input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }

.result-count { margin: 1.25rem 0 0; color: var(--muted); }

/* One row per person, the whole row a link. Name above details on a phone, side by side when
   there is room. */
.directory-list { list-style: none; margin-top: 0.5rem; padding: 0; overflow: hidden; }
.directory-list li + li { border-top: 1px solid var(--border); }
.directory-list a {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 3.25rem;
  padding: 0.6rem 1rem;
  color: var(--text);
  text-decoration: none;
}
.directory-list a:hover, .directory-list a:focus-visible { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.directory-list .who { font-weight: 600; color: var(--accent); }
.directory-list .what { color: var(--muted); font-size: 0.9375rem; }
@media (min-width: 40rem) {
  .directory-list a { flex-direction: row; align-items: center; justify-content: space-between; gap: 1rem; }
  .directory-list .what { text-align: right; }
}

.directory-pager { align-items: center; justify-content: space-between; gap: 0.75rem; }

/* The person page. */
.crumbs { margin: 0 0 0.5rem; }
.person-summary { margin: 0; }
.card h3 { margin: 1.25rem 0 0.5rem; font-size: 1rem; }
.contact-list { list-style: none; margin: 0; padding: 0; }
.contact-list li + li { margin-top: 0.75rem; padding-top: 0.75rem; border-top: 1px solid var(--border); }
.contact-name { margin: 0; }
.contact-links { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.4rem 0 0; }
.home address { font-style: normal; }
.home p { margin: 0.5rem 0 0; }
.plain-list { list-style: none; margin: 0; padding: 0; }
.plain-list li + li { margin-top: 0.4rem; }
.contact-note { margin: 0.35rem 0 0; }
.list-heading { margin: 1.5rem 0 0; font-size: 1rem; color: var(--muted); }


/* ---- Phase 5: student support. Logging an event is a phone task ---- */

input[type="time"], input[type="number"] {
  width: 100%;
  min-height: 2.75rem;
  padding: 0.4rem 0.6rem;
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.event-form select, .event-form input[type="date"] { min-height: 2.75rem; }
/* Notes are prose, not the monospace of pasted CSV. */
.note-field textarea { font-family: inherit; font-size: 1rem; }
.when-fields .field { flex: 1 1 8rem; max-width: 12rem; }
.tier-choice { display: flex; flex-wrap: wrap; align-items: center; }
.tier-choice legend { width: 100%; }
.share-list { margin: 0.25rem 0 0.5rem 1.75rem; }
.share-list label { padding: 0.2rem 0; }

.support-list { list-style: none; margin: 0; padding: 0; }
.support-list > li { padding: 0.9rem 0; border-top: 1px solid var(--border); }
.support-list > li:first-child { border-top: none; padding-top: 0.25rem; }
.support-list p { margin: 0.25rem 0 0; }
.support-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.6rem; }
.support-note { white-space: pre-wrap; overflow-wrap: anywhere; }
/* Tiers 2 and 3 stand out by their outline; the text keeps the page's own contrast. */
.badge.tier-2, .badge.tier-3 { color: var(--text); background: transparent; }
.badge.tier-2 { box-shadow: inset 0 0 0 2px var(--accent); }
.badge.tier-3 { box-shadow: inset 0 0 0 2px var(--bad); }
.row-links { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem; }

/* The timeline's filters (TimelineFilterBar). While any is on, a banner in the accent color says
   so, with the count and one chip per filter that drops just that one, so a shortened timeline
   never reads as the whole record. The form itself folds away, which on a phone costs one line. */
.filter-banner {
  margin: 0.75rem 0 0.5rem;
  padding: 0.75rem 1rem;
  border: 2px solid var(--accent);
  border-left-width: 6px;
  border-radius: var(--radius);
  background: var(--bg);
}
.filter-banner p { margin: 0; }
.filter-count { font-size: 1.0625rem; }
.filter-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.6rem 0; padding: 0; list-style: none; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  padding: 0.3rem 0.5rem 0.3rem 0.9rem;
  color: var(--on-highlight);
  background: var(--highlight);
  box-shadow: inset 0 0 0 1px var(--highlight-edge);
  border-radius: 999px;
  font-weight: 600;
  text-decoration: none;
  overflow-wrap: anywhere;
}
.chip:hover { filter: brightness(1.1); }
.chip-x { display: inline-flex; align-items: center; justify-content: center; width: 1.5rem; height: 1.5rem; border-radius: 50%; background: rgb(0 0 0 / 0.18); font-size: 1.125rem; line-height: 1; }
.clear-filters { font-weight: 600; }
.timeline-filters { margin: 0.75rem 0 1rem; }
.timeline-filters .filters { margin-bottom: 0.5rem; }
.timeline-filters input[type="search"] { width: 100%; }
@media (max-width: 40rem) {
  .timeline-filters .filters .field { flex: 1 1 100%; max-width: none; }
  .timeline-filters select { width: 100%; }
}
@media (min-width: 64rem) and (pointer: fine) {
  .chip { min-height: 2.25rem; }
}
.inline-details { margin-top: 0.5rem; }
.inline-details > summary { font-weight: 400; color: var(--bad); }
.documents li { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.75rem; margin-top: 0.5rem; }
.snsp-line { margin: 0 0 0.25rem; }
table.totals { max-width: 24rem; margin-bottom: 0.5rem; }
table.totals td + td, table.totals th + th { text-align: right; }
table.sizes td + td, table.sizes th + th { text-align: right; font-variant-numeric: tabular-nums; }


/* ---- Text size menu and denser desktop screens ---- */

/* The "Aa" button in the header and the little menu that drops from it. */
.text-size { position: relative; margin: 0; }
.text-size > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0 0.5rem;
  color: var(--bar-link);
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-weight: 700;
  list-style: none;
}
.text-size > summary::-webkit-details-marker { display: none; }
.text-size > summary:hover, .text-size[open] > summary { border-color: var(--bar-link); }
.text-size[open] > summary { margin-bottom: 0; }
.text-size-menu {
  position: absolute;
  right: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 11rem;
  margin-top: 0.25rem;
  padding: 0.5rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 4px 16px rgb(0 0 0 / 0.18);
}
.text-size-menu .hint { margin: 0 0 0.25rem 0.25rem; }
.text-size-menu button { min-height: 2.75rem; text-align: left; border-color: transparent; }
.text-size-menu button:hover { border-color: var(--border); }
.text-size-menu button[aria-pressed="true"] { color: var(--button-text); background: var(--button-fill); border-color: var(--button-edge); font-weight: 600; }
/* Each choice drawn at roughly the size it gives. */
.text-size-menu .size-small { font-size: 0.875rem; }
.text-size-menu .size-large { font-size: 1.125rem; }
.text-size-menu .size-larger { font-size: 1.25rem; }

/* A computer with a mouse: tighter spacing and a wider page, so a list or table shows more rows
   and columns at once. Phones and tablets keep the roomier, thumb-sized layout. */
@media (min-width: 64rem) and (pointer: fine) {
  body { line-height: 1.45; }
  main { max-width: 72rem; padding: 1.25rem 1.5rem; }
  /* Tables and long lists (PageWidth.wide in Layout.swift) use more of a big screen. */
  main.wide { max-width: 100rem; }
  .site-header { padding: 0.5rem 1.5rem; }
  .card { margin-top: 1rem; padding: 0.875rem 1rem; }
  .tiles { margin-top: 1rem; gap: 0.75rem; }
  .stack > * + * { margin-top: 0.75rem; }
  th, td { padding: 0.3rem 0.6rem; }
  .directory-list a { min-height: 2.5rem; padding: 0.4rem 1rem; }
  .support-list > li { padding: 0.6rem 0; }
  a.button-quiet, .segmented span, .directory-search input[type="search"], .directory-search select,
  input[type="time"], input[type="number"], .event-form select, .event-form input[type="date"],
  button.tall, .text-size > summary, .text-size-menu button { min-height: 2.25rem; }
  input[type="text"], input[type="email"], input[type="date"], input[type="search"], select { min-height: 2.25rem; }
}


/* ---- Record pages: folding sections and two columns ---- */

/* The header band (name, summary) across the full width, then the cards. */
.record-header { margin-bottom: 1.5rem; }
.record-cards > .card { margin-top: 0; margin-bottom: 1.5rem; }

/* A section's heading is its fold button: the whole heading row is the target, with a chevron
   that points down when open and right when folded. */
details.record-section { margin-top: 0; }
.record-section > summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  margin: -0.5rem 0;
  color: var(--text);
  list-style: none;
}
.record-section > summary::-webkit-details-marker { display: none; }
.record-section > summary::before {
  content: "";
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  margin: 0 0.25rem;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(-45deg);
  transition: transform 120ms ease-out;
}
.record-section[open] > summary::before { transform: rotate(45deg); }
.record-section > summary h2 { margin: 0; }
.record-section > summary:hover h2 { color: var(--accent); }
.record-section[open] > summary { margin-bottom: 0.25rem; }
.section-body > :last-child { margin-bottom: 0; }

/* "Arrange sections" sits at the far end of the crumbs row; its panel lists the record's
   sections with thumb-sized up and down buttons. A button that can't move is dimmed. */
.record-header .crumbs { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem 1rem; }
.arrange-link { font-size: 0.875rem; }
.record-actions { display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; }
.history-list > li + li { margin-top: 0.75rem; padding-top: 0.75rem; border-top: 1px solid var(--border); }
.arrange-sections > h2 { margin-top: 0; }
.arrange-list { list-style: none; margin: 0.75rem 0; padding: 0; }
.arrange-list > li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.25rem 0;
  border-top: 1px solid var(--border);
}
.arrange-list > li:first-child { border-top: 0; }
.arrange-title { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.arrange-move { min-width: 2.75rem; min-height: 2.75rem; padding: 0.25rem; }
.arrange-move:disabled { opacity: 0.35; cursor: default; }
.arrange-move:disabled:hover { border-color: var(--border); }

/* From 75rem (1200px) a record page is wider and its cards run down two columns, the first
   filling before the second. The page scrolls as one; a card never splits across columns. */
@media (min-width: 75rem) {
  main.record { max-width: 90rem; }
  .record-cards { columns: 2; column-gap: 1rem; }
  .record-cards > .card { break-inside: avoid; margin-bottom: 1rem; }
}
/* A student's support page (SupportRecordPage): the same cards, but in two fixed columns, the
   working cards on the left and the timeline on the right, so opening a section never moves a
   card to the other side. A page with only one of the two uses the full width. */
.support-layout .card { margin-top: 0; margin-bottom: 1.5rem; }
@media (min-width: 75rem) {
  .record-cards.support-layout { columns: auto; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 1rem; align-items: start; }
  .support-layout .card { margin-bottom: 1rem; }
  .support-layout > :only-child { grid-column: 1 / -1; }
  .support-side:only-child { columns: 2; column-gap: 1rem; }
  .support-side:only-child > .card { break-inside: avoid; }
}
@media (min-width: 64rem) and (pointer: fine) {
  .record-header { margin-bottom: 1rem; }
  .record-cards > .card, .support-layout .card { margin-bottom: 1rem; }
  .record-section > summary { min-height: 2.25rem; margin: -0.375rem 0; }
  .arrange-move { min-width: 2.25rem; min-height: 2.25rem; }
}

/* ---- Sign-in pages (SignInHero in SignInPage.swift) ----
   One narrow column, centered: on a phone it is the whole width, on a computer it stays the size
   of a phone's so the page reads as one short form. The OneFold mark is large, with the school's
   icon as a round badge over its empty lower-right corner, the way an app icon wears a count. */
.signin { max-width: 26rem; margin: 0 auto; }
.signin > h1 { margin-top: 1.25rem; font-size: 1.375rem; text-align: center; }
.signin-hero { padding-top: 0.75rem; text-align: center; }
.signin-logo { position: relative; display: inline-block; width: 6.5rem; height: 6.5rem; }
.signin-mark { display: block; width: 100%; height: 100%; }
/* A white disc whatever the page's colors: school icons are mostly drawn for a white page. The
   ring in the page's own background color cuts it cleanly out of the mark beneath. */
.school-badge {
  position: absolute;
  right: -0.5rem;
  bottom: -0.3rem;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0.2rem;
  object-fit: contain;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 0 0 0.2rem var(--bg), 0 0.1rem 0.4rem 0.2rem rgb(0 0 0 / 0.18);
}
.signin-wordmark { display: block; width: 11rem; height: auto; margin: 0.6rem auto 0; }
.signin-school { margin: 0.35rem 0 0; color: var(--muted); font-weight: 600; }
@media (min-width: 40rem) {
  .signin-hero { padding-top: 1.5rem; }
  .signin-logo { width: 8rem; height: 8rem; }
  .school-badge { width: 3.25rem; height: 3.25rem; }
  .signin-wordmark { width: 13rem; }
}

/* Help articles (Plan-Help Manual). The text is rendered from Markdown by HelpMarkdown, so these
   style plain elements inside it rather than classes. */
.help-article { max-width: 42rem; line-height: 1.55; }
.help-article h2 { margin-top: 1.75rem; }
.help-article h3 { margin: 1.25rem 0 0.5rem; font-size: 1rem; }
.help-article p, .help-article ul, .help-article ol { margin: 0 0 0.75rem; }
.help-article li + li { margin-top: 0.25rem; }
.help-article blockquote { margin: 1rem 0; padding: 0.75rem 1rem; border-left: 4px solid var(--border); }
.help-article blockquote.callout { background: var(--bg); border-left-color: var(--accent); border-radius: var(--radius); }
.help-article blockquote > :last-child { margin-bottom: 0; }
.help-article table { margin: 0 0 1rem; }
.help-results { list-style: none; padding: 0; margin-top: 1rem; }
.help-results li + li { margin-top: 0.75rem; }
.help-results p { margin: 0.125rem 0 0; }

/* "Who gets it?" (AudienceViews.swift): a nested list of tick boxes, each level indented, with a
   count beside each that opens the names under it. */
.audience-tree { margin: 1rem 0 0; padding: 0; border: none; }
.audience-tree legend { padding: 0; margin-bottom: 0.25rem; font-weight: 600; }
.audience-tree ul { list-style: none; margin: 0; padding: 0; }
.audience-tree ul ul { margin-left: 0.6rem; padding-left: 1rem; border-left: 1px solid var(--border); }
.audience-tree .hint { display: block; margin: 0 0 0.25rem 1.9rem; }
.tree-row { display: flex; flex-wrap: wrap; gap: 0.25rem 0.75rem; align-items: baseline; }
.tree-row label { display: flex; gap: 0.6rem; align-items: baseline; padding: 0.3rem 0; }
.tree-count summary { cursor: pointer; font-size: 0.875rem; color: var(--muted); }
.tree-count[open] { flex-basis: 100%; margin: 0 0 0.5rem 1.9rem; }

/* ---- Next year's roster: stacked on a phone, a grid on a computer ---- */

.roster-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.75rem 1rem; }
.roster-filters .field { flex: 1 1 10rem; max-width: 16rem; margin: 0; }
.roster-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr)); gap: 1rem; align-items: start; }
.roster-grid > .card { margin-top: 0; }
.roster-grade h2 { margin-bottom: 0; }
.roster-count { margin: 0 0 0.5rem; font-weight: 600; }
.roster-list { margin: 0.5rem 0 0; padding-left: 1.25rem; }
ul.roster-list { padding-left: 0; list-style: none; }
.roster-list li { padding: 0.3rem 0; }
.roster-tags { display: inline-flex; flex-wrap: wrap; gap: 0.25rem; }
.tag { padding: 0 0.4rem; color: var(--muted); border: 1px solid var(--border); border-radius: var(--radius); font-size: 0.8125rem; }
.tag-attention { color: var(--bad); border-color: var(--bad); }
/* Tables, a record's versions: the one shown. */
.tag-current { color: var(--text); border-color: var(--accent); font-weight: 600; }
a.tag { text-decoration: none; }
/* Tables, compare: rows that differ. */
.compare-table tr.differs th, .compare-table tr.differs td { background: color-mix(in srgb, var(--highlight) 14%, transparent); }
.compare-table td { overflow-wrap: anywhere; }
/* The parts that differ, darker: struck through on the left, underlined on the right (not colour alone). */
.compare-table del.diff-out, .compare-table ins.diff-in { background: color-mix(in srgb, var(--highlight) 38%, transparent); border-radius: 2px; color: inherit; }
.compare-table del.diff-out { text-decoration: line-through; }
.compare-table ins.diff-in { text-decoration: underline; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 30rem) {
  .tuition-totals .rate { display: none; }
  .tuition-totals th, .tuition-totals td { padding-left: 0.3rem; padding-right: 0.3rem; }
}

/* Tuition setup: boxes in table rows stay wide enough to read; a phone scrolls the card sideways. */
.edit-table input[type="text"] { min-width: 6rem; }
.edit-table td:first-child input[type="text"] { min-width: 11rem; }
.edit-table select { min-width: 7.5rem; }
.rule-actions { display: inline-flex; gap: 0.25rem; vertical-align: middle; }
.condition-row { margin: 0 0 0.75rem; padding: 0.5rem 0.75rem; border: 1px solid var(--border); border-radius: var(--radius); }
.condition-row legend { padding: 0 0.25rem; font-weight: 600; }
.condition-row .hint { margin: 0.25rem 0 0; }
td.number { text-align: right; font-variant-numeric: tabular-nums; }

/* Admin, Tables: one record. A phone stacks each label over its value. */
.record-fields { margin: 0; }
.record-fields dd { overflow-wrap: anywhere; }
.record-fields .record-value { white-space: pre-wrap; }
.record-fields dd .small { margin: 0.125rem 0 0; }
@media (max-width: 40rem) {
  .record-fields { grid-template-columns: 1fr; gap: 0; }
  .record-fields dt { margin-top: 0.75rem; font-weight: 600; }
  .record-fields dt:first-child { margin-top: 0; }
}
/* Tables, raw edit: the warning stays on top of the form and the review. */
.raw-edit-warning { background: color-mix(in srgb, var(--bad) 8%, transparent); }
/* Tables list: filter buttons that wrap on a phone. The chosen one is filled, bold and ticked, not
   coloured alone. */
.chips { display: flex; flex-wrap: wrap; gap: 0.375rem; margin: 0; padding: 0; border: none; }
.chips legend { width: 100%; margin-bottom: 0.25rem; }
.chips label { position: relative; }
.chips input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.chips span { display: inline-block; padding: 0.25rem 0.75rem; border: 1px solid var(--border); border-radius: 999px; font-size: 0.875rem; }
.chips input:checked + span { color: var(--button-text); background: var(--button-fill); border-color: var(--button-edge); font-weight: 600; }
.chips input:checked + span::before { content: "\2713\00a0"; }
.chips input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }
.table-search { display: grid; gap: 0.75rem; }
.table-area > summary { cursor: pointer; list-style-position: inside; }
.table-area > summary h2 { display: inline; }

/* Before and after care (Views/CarePages.swift): phone-first, big rows, one button that stays in
   reach at the bottom of the screen. */
.care-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin: 1rem 0; }
.care-big { display: block; width: 100%; padding: 1.1rem 1rem; font-size: 1.25rem; font-weight: 600; text-align: center; }
a.button.care-big.primary { color: var(--button-text); background: var(--button-fill); border-color: var(--button-edge); }
.care-links { list-style: none; padding: 0; margin: 0.5rem 0; }
.care-links li { padding: 0.35rem 0; }
.care-search { width: 100%; font-size: 1.1rem; margin: 0.5rem 0; }
.care-list { list-style: none; padding: 0; margin: 0; }
.care-list li + li { border-top: 1px solid var(--border); }
.care-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.75rem; padding: 0.8rem 0.25rem; cursor: pointer; }
.care-row input[type="checkbox"] { width: 1.6rem; height: 1.6rem; flex: none; }
.care-row .care-name { font-size: 1.1rem; flex: 1; }
.care-row:has(input:checked) { background: color-mix(in srgb, var(--accent) 12%, transparent); border-radius: var(--radius); }
.care-row:has(input:disabled) { opacity: 0.6; cursor: default; }
.care-everyone > summary { padding: 0.6rem 0; }
.care-group { padding: 0.25rem 0; border-bottom: 1px solid var(--border); }
/* A group's "who picked up?" shows once one of its students is ticked. */
.care-pickup { display: none; border: 0; margin: 0 0 0.75rem; padding: 0.5rem 0.75rem; background: var(--surface); border-radius: var(--radius); }
.care-group:has(input[name="visit"]:checked) .care-pickup { display: block; }
.care-pickup legend { font-weight: 600; padding: 0; }
.care-choice { display: flex; align-items: center; gap: 0.6rem; padding: 0.55rem 0; font-size: 1.05rem; }
.care-choice input[type="radio"] { width: 1.4rem; height: 1.4rem; }
.care-other { display: none; }
.care-pickup:has(input[value="other"]:checked) .care-other { display: block; }
.care-time { margin: 1rem 0; }
.care-report-range { align-items: flex-end; }
.care-report tr.care-report-more td { border-top: 0; }
@media print {
  .care-report-day { break-inside: avoid-page; }
  .care-report-day .card { box-shadow: none; padding: 0; overflow: visible; }
}
.care-submit { position: sticky; bottom: 0; padding: 0.75rem 0; background: var(--bg); }
.inline-form { display: inline-flex; gap: 0.4rem; align-items: center; }
@media print {
  .no-print, .site-header, .acting-bar, .test-server-bar { display: none; }
}
.badge.bad { color: var(--surface); background: var(--bad); box-shadow: none; }
