/* Aluma for educators: the educator app at /educators (educators.html +
   educators.js, with the roster, student and actions sheets after this one).

   A standalone sheet, like parents.css: an educator on a phone gets one
   small light sheet, not the student app's bundles. The same shape language
   as the family app (Geist, white cards with a soft drop, pill buttons with a
   pressed lip) on a cooler ground with an ocean accent. Every token is
   namespaced --e-* so nothing here collides with the app-wide ladder or the
   ink checks in the regression gate, and calendar pieces are ec-*, never
   cal-*, so the calendar sweeps leave them alone.

   This sheet holds the shell and every component more than one of the
   educator files uses. The roster, student page and actions sheets add only
   their own pieces.

   Contrast, measured (WCAG 4.5:1 for text):
     --e-text-2 #4f5d68 is 6.0:1 on --e-surface-3 and 5.8:1 on --e-accent-soft.
     --e-text-3 #5f6c77 is 4.78:1 on --e-surface-3 and 5.39:1 on white; it is
     never set on --e-accent-soft.
     --e-accent #1f5f82 is 6.96:1 on white, and white on it the same.
     Status inks on their own soft fills: ok 5.68, warn 5.12, risk 5.67,
     streak 5.23, night 6.98, violet 6.88. The bright streak orange is only
     the flame.
     Controls (WCAG 3:1): the off switch track and the checkbox border are
     --e-icon #73808a, 4.05:1 on the card.
     Bars on --e-bar-track: known 6.17, learning 4.56, weak 4.26. */

:root {
  --e-bg: #f4f7f9;
  --e-surface: #fff;
  --e-surface-2: #fafcfd;
  --e-surface-3: #edf2f6;
  --e-text: #121c24;
  --e-text-2: #4f5d68;
  --e-text-3: #5f6c77;
  --e-icon: #73808a;
  --e-border: rgba(18, 28, 36, 0.09);
  --e-border-strong: rgba(18, 28, 36, 0.14);
  --e-hair: rgba(18, 28, 36, 0.07);
  --e-accent: #1f5f82;
  --e-accent-hover: #1a5272;
  --e-accent-lip: #123f58;
  --e-accent-soft: #e4eef5;
  --e-accent-soft-ink: #174d68;
  --e-ok-ink: #1f6a3a;
  --e-ok-soft: #e2f2e6;
  --e-warn-ink: #8a5a00;
  --e-warn-soft: #f7eed6;
  --e-risk-ink: #a43243;
  --e-risk-soft: #f8e8e6;
  --e-risk-hover: #8e2837;
  --e-risk-lip: #74202d;
  --e-streak: #d9730d;
  --e-streak-ink: #9a4d07;
  --e-streak-soft: #f8ecd9;
  --e-night-ink: #0d5963;
  --e-night-soft: #def4f3;
  --e-violet-ink: #553aa6;
  --e-violet-soft: #ece8fa;
  --e-bar-track: #e9eef2;
  --e-bar-known: #1f5f82;
  --e-bar-learning: #8a6508;
  --e-bar-weak: #c2453f;
  --e-ring: 0 0 0 4px rgba(31, 95, 130, 0.16);
  --e-shadow: 0 0 0 1px rgba(18, 28, 36, 0.04), 0 22px 44px -22px rgba(18, 28, 36, 0.24);
  --e-shadow-sm: 0 0 0 1px rgba(18, 28, 36, 0.04), 0 10px 24px -16px rgba(18, 28, 36, 0.22);
  --e-shadow-hover: 0 0 0 1px rgba(18, 28, 36, 0.05), 0 30px 54px -24px rgba(18, 28, 36, 0.30);
  --e-shadow-pop: 0 0 0 1px rgba(18, 28, 36, 0.05), 0 32px 72px -24px rgba(18, 28, 36, 0.36);
  --e-radius-card: 28px;
  --e-radius-hero: 36px;
  --e-radius-input: 18px;
  --e-radius-pill: 999px;
  --e-font: 'Geist', Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --e-mono: 'Geist Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  --e-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --e-max: 1120px;
  --e-gutter: 16px;
}

@media (min-width: 640px) {
  :root { --e-gutter: 24px; }
}

/* The student's own colours, for the read-only student view only (E3 puts
   this class on its wrapper). Measured: the accent 6.02:1 on white, the
   kicker ink 6.59:1 on its soft. */
.edu-sv {
  --e-accent: #1b6f5f;
  --e-accent-hover: #175f51;
  --e-accent-lip: #114b40;
  --e-accent-soft: #e3f5ee;
  --e-accent-soft-ink: #15604f;
  --e-ring: 0 0 0 4px rgba(27, 111, 95, 0.16);
  --e-bar-known: #1b6f5f;
}

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

[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: 84px;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: var(--e-bg);
  background-image:
    radial-gradient(1100px 520px at 50% -260px, rgba(176, 208, 232, 0.55), rgba(176, 208, 232, 0) 72%),
    linear-gradient(180deg, #eaf0f5 0, rgba(244, 247, 249, 0) 440px);
  background-repeat: no-repeat;
  color: var(--e-text);
  font-family: var(--e-font);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

html.modal-open { overflow: hidden; }

h1, h2, h3, h4, p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
button { -webkit-tap-highlight-color: transparent; }
a { color: var(--e-accent); }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--e-accent);
  outline-offset: 3px;
}

[tabindex="-1"]:focus { outline: none; }

.sr-only {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 12px;
  top: -80px;
  z-index: 200;
  padding: 10px 16px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface);
  color: var(--e-text);
  font-weight: 650;
  text-decoration: none;
  box-shadow: var(--e-shadow);
}
.skip-link:focus { top: 12px; }

.ico {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
}
.ico svg { width: 100%; height: 100%; }
.ico-sm { width: 16px; height: 16px; }

.num { font-variant-numeric: tabular-nums; }
.unit { font-size: 0.55em; font-weight: 620; color: var(--e-text-2); margin-left: 1px; letter-spacing: 0; }

/* ── top bar ──────────────────────────────────────────────────────────── */
.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background-color 0.2s var(--e-ease), border-color 0.2s var(--e-ease);
}
/* Scrolled, the bar is the page's own ground, solid: a see-through bar
   ghosted the status and the tiles scrolling under it. */
.topbar.is-scrolled {
  background: var(--e-bg);
  border-bottom-color: var(--e-hair);
  box-shadow: 0 6px 16px -14px rgba(18, 28, 36, 0.35);
}

.topbar-inner {
  max-width: var(--e-max);
  height: 66px;
  margin: 0 auto;
  padding: 0 var(--e-gutter);
  display: flex;
  align-items: center;
  gap: 12px;
}

.brand {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 10px;
  color: var(--e-text);
  text-decoration: none;
  border-radius: 12px;
}
/* The student app's lockup, as the family app draws it: the mark in a 42px
   slot (its artwork is half the PNG) beside "Aluma" at 15px. */
.brand-logo { width: 42px; height: 42px; object-fit: contain; }
.brand-word { font-size: 15px; font-weight: 700; letter-spacing: -0.02em; }

/* Says whose app this is once signed in. A phone has no room for it.
   Sentence case, like every other chip in the three apps: it was the only
   small-caps label anywhere, and read as a page title rather than a tag. */
.topbar-tag {
  flex: none;
  padding: 2px 11px 3px;
  border-radius: var(--e-radius-pill);
  background: var(--e-accent-soft);
  color: var(--e-accent-soft-ink);
  font-size: 13px;
  font-weight: 650;
  letter-spacing: 0;
  white-space: nowrap;
}
@media (max-width: 459px) {
  .topbar-tag { display: none; }
}

.topbar-end {
  position: relative;
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

.acct-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 4px 12px 4px 4px;
  border: 0;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface);
  box-shadow: var(--e-shadow-sm);
  cursor: pointer;
  transition: box-shadow 0.18s var(--e-ease);
}
.acct-btn:hover { box-shadow: var(--e-shadow); }
.acct-btn .avatar { width: 34px; height: 34px; font-size: 14px; }
.acct-name {
  max-width: 140px;
  font-size: 14px;
  font-weight: 640;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.acct-btn .ico { width: 16px; height: 16px; color: var(--e-icon); transition: transform 0.18s var(--e-ease); }
.acct-btn[aria-expanded="true"] .ico { transform: rotate(180deg); }

.acct-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 10px);
  z-index: 60;
  width: min(320px, calc(100vw - 32px));
  padding: 8px;
  border-radius: 24px;
  background: var(--e-surface);
  box-shadow: var(--e-shadow-pop);
  animation: e-pop 0.18s var(--e-ease);
}
/* Who is signed in: face and name, then the role on one quiet line, in a
   soft panel so the menu's head reads apart from its items. */
.acct-menu-head {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  margin-bottom: 6px;
  border-radius: 18px;
  background: var(--e-surface-2);
  box-shadow: inset 0 0 0 1px var(--e-hair);
}
.acct-menu-who { display: flex; align-items: center; gap: 12px; min-width: 0; }
.acct-menu-id { min-width: 0; }
.acct-menu-name { font-weight: 720; font-size: 15px; letter-spacing: -0.01em; line-height: 1.3; overflow-wrap: anywhere; }
.acct-menu-email { color: var(--e-text-2); font-size: 13px; line-height: 1.35; overflow-wrap: anywhere; }
.acct-menu-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; }
.acct-menu-sub { min-width: 0; color: var(--e-text-2); font-size: 13px; font-weight: 560; line-height: 1.4; overflow-wrap: anywhere; }
.acct-role {
  display: inline-block;
  flex: none;
  padding: 2px 10px;
  border-radius: var(--e-radius-pill);
  background: var(--e-accent-soft);
  color: var(--e-accent-soft-ink);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.5;
}
.acct-menu-sep { height: 1px; margin: 6px 10px; background: var(--e-hair); }
.acct-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 0 14px;
  border: 0;
  border-radius: 16px;
  background: none;
  color: var(--e-text);
  font-size: 15px;
  font-weight: 580;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.acct-menu-item:hover { background: var(--e-surface-3); }
.acct-menu-item:focus-visible { outline-offset: 0; background: var(--e-surface-3); }
.acct-menu-item .ico { color: var(--e-icon); }
.acct-menu-item.is-danger,
.acct-menu-item.is-danger .ico { color: var(--e-risk-ink); }
.acct-menu-item.is-danger:hover { background: var(--e-risk-soft); }

/* ── page shell ───────────────────────────────────────────────────────── */
.page {
  flex: 1 0 auto;
  width: 100%;
  max-width: var(--e-max);
  margin: 0 auto;
  padding: 20px var(--e-gutter) 64px;
}
@media (min-width: 900px) {
  .page { padding-top: 36px; }
}

.footer-inner {
  max-width: var(--e-max);
  margin: 0 auto;
  padding: 18px var(--e-gutter) calc(28px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 24px;
}
.footer-promise { color: var(--e-text-2); font-size: 13px; }
.footer-links { display: flex; flex-wrap: wrap; gap: 4px 18px; }
@media (min-width: 700px) {
  .footer-links { margin-left: auto; }
}
.footer-links a {
  color: var(--e-text-2);
  font-size: 13px;
  font-weight: 580;
  text-decoration: none;
  border-radius: 6px;
}
.footer-links a:hover { color: var(--e-text); text-decoration: underline; }
/* A phone: the promise, then the links in two balanced rows (Privacy, Terms,
   Help; then the two other apps), never one link orphaned on a line. The
   ::after is a full-width break ordered between the third link and the
   fourth. */
@media (max-width: 559px) {
  .footer-inner { flex-direction: column; justify-content: center; text-align: center; gap: 10px; }
  .footer-links { justify-content: center; gap: 2px 20px; }
  .footer-links::after { content: ""; flex-basis: 100%; height: 0; order: 1; }
  .footer-links a:nth-child(n+4) { order: 2; }
}

.noscript {
  max-width: 520px;
  margin: 24px auto;
  padding: 18px 22px;
  border-radius: var(--e-radius-card);
  background: var(--e-surface);
  box-shadow: var(--e-shadow);
  text-align: center;
}

/* ── loading skeleton ─────────────────────────────────────────────────── */
.boot { display: flex; flex-direction: column; gap: 14px; }
/* The roster's shape while anything loads: four tiles, then cards. */
.boot-tiles {
  display: grid;
  gap: 12px;
  margin-top: 12px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 720px) {
  .boot-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
}
.boot-grid {
  display: grid;
  gap: 20px;
  margin-top: 14px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
}
.sk {
  display: block;
  border-radius: 14px;
  background: linear-gradient(90deg, #e0e8ef 0%, #ecf1f5 45%, #e0e8ef 90%);
  background-size: 220% 100%;
  animation: e-shimmer 1.4s linear infinite;
}
.sk-line { height: 14px; }
.sk-title { height: 36px; width: min(420px, 80%); border-radius: 16px; }
.sk-tile { height: 104px; border-radius: 24px; }
.sk-card { height: 300px; border-radius: var(--e-radius-card); }
/* stands in for a section heading */
.sk-h2 { height: 26px; width: 130px; margin-top: 26px; border-radius: 13px; }
.sk-row { height: 56px; border-radius: 18px; }
.sk-w30 { width: 30%; max-width: 180px; }
.sk-w60 { width: 60%; max-width: 360px; }

/* ── type ─────────────────────────────────────────────────────────────── */
.display {
  font-size: clamp(38px, 8.4vw, 58px);
  line-height: 1.02;
  font-weight: 800;
  letter-spacing: -0.05em;
}
.h1 {
  font-size: clamp(28px, 5.6vw, 38px);
  line-height: 1.1;
  font-weight: 780;
  letter-spacing: -0.04em;
}
.h2 {
  font-size: 21px;
  line-height: 1.25;
  font-weight: 750;
  letter-spacing: -0.025em;
}
.lede { color: var(--e-text-2); font-size: 17px; line-height: 1.5; overflow-wrap: anywhere; }
.muted { color: var(--e-text-2); }
.tiny { font-size: 13px; }

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  margin-bottom: 16px;
}

.mini-label {
  color: var(--e-text-2);
  font-size: 13px;
  font-weight: 650;
  letter-spacing: 0;
}

/* ── cards ────────────────────────────────────────────────────────────── */
.card {
  background: var(--e-surface);
  border-radius: var(--e-radius-card);
  box-shadow: var(--e-shadow);
}
.card-pad { padding: 22px; }
@media (min-width: 640px) {
  .card-pad { padding: 26px; }
}

.icon-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 20px;
  background: var(--e-accent-soft);
  color: var(--e-accent);
}
.icon-badge .ico { width: 26px; height: 26px; }
.icon-badge.is-danger { background: var(--e-risk-soft); color: var(--e-risk-ink); }
.icon-badge.is-warm { background: var(--e-streak-soft); color: var(--e-streak-ink); }
.icon-badge.is-night { background: var(--e-night-soft); color: var(--e-night-ink); }

/* ── buttons: pills with a pressed lip ────────────────────────────────── */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  padding: 0 22px;
  border: 1px solid transparent;
  border-radius: var(--e-radius-pill);
  background: transparent;
  font-size: 15px;
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.18s var(--e-ease), box-shadow 0.12s var(--e-ease),
    color 0.18s var(--e-ease), transform 0.12s var(--e-ease);
}
.btn[disabled],
.btn[aria-disabled="true"] { cursor: not-allowed; opacity: 0.6; }
.btn .ico { width: 18px; height: 18px; }

.btn-primary {
  background: var(--e-accent);
  color: #fff;
  box-shadow: 0 3px 0 var(--e-accent-lip);
}
.btn-primary:hover:not([disabled]):not([aria-disabled="true"]) { background: var(--e-accent-hover); }
.btn-primary:active:not([disabled]):not([aria-disabled="true"]) { transform: translateY(2px); box-shadow: 0 1px 0 var(--e-accent-lip); }

.btn-secondary {
  background: #fff;
  border-color: var(--e-border);
  color: var(--e-text);
  box-shadow: 0 3px 0 rgba(18, 28, 36, 0.10);
}
.btn-secondary:hover:not([disabled]):not([aria-disabled="true"]) { background: var(--e-surface-2); }
.btn-secondary:active:not([disabled]):not([aria-disabled="true"]) { transform: translateY(2px); box-shadow: 0 1px 0 rgba(18, 28, 36, 0.10); }

.btn-soft {
  background: var(--e-accent-soft);
  color: var(--e-accent-soft-ink);
  box-shadow: 0 3px 0 rgba(23, 77, 104, 0.16);
}
.btn-soft:hover:not([disabled]):not([aria-disabled="true"]) { background: #d8e6f0; }
.btn-soft:active:not([disabled]):not([aria-disabled="true"]) { transform: translateY(2px); box-shadow: 0 1px 0 rgba(23, 77, 104, 0.16); }

.btn-quiet { min-height: 40px; padding: 0 14px; color: var(--e-accent); }
.btn-quiet:hover:not([disabled]):not([aria-disabled="true"]) { background: var(--e-accent-soft); }
.btn-quiet.is-danger { color: var(--e-risk-ink); }
.btn-quiet.is-danger:hover:not([disabled]):not([aria-disabled="true"]) { background: var(--e-risk-soft); }
.btn-secondary.is-danger { color: var(--e-risk-ink); }
.btn-secondary.is-danger:hover:not([disabled]):not([aria-disabled="true"]) { background: var(--e-risk-soft); }

.btn-danger { background: var(--e-risk-ink); color: #fff; box-shadow: 0 3px 0 var(--e-risk-lip); }
.btn-danger:hover:not([disabled]) { background: var(--e-risk-hover); }
.btn-danger:active:not([disabled]) { transform: translateY(2px); box-shadow: 0 1px 0 var(--e-risk-lip); }

.btn-sm { min-height: 38px; padding: 0 16px; font-size: 14px; }
.btn-lg { min-height: 54px; padding: 0 26px; font-size: 16px; }
.btn-block { width: 100%; }

.btn.is-busy { color: transparent !important; }
.btn.is-busy .ico { visibility: hidden; }
.btn.is-busy::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.95);
  border-right-color: transparent;
  animation: e-spin 0.7s linear infinite;
}
.btn-secondary.is-busy::after,
.btn-quiet.is-busy::after,
.btn-soft.is-busy::after {
  border-color: var(--e-text-2);
  border-right-color: transparent;
}

.link-btn {
  display: inline;
  padding: 2px 0;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--e-accent);
  font-size: inherit;
  font-weight: 640;
  text-decoration: underline;
  text-decoration-color: rgba(31, 95, 130, 0.3);
  text-underline-offset: 3px;
  cursor: pointer;
}
.link-btn:hover { text-decoration-color: currentColor; }
.link-btn.is-quiet { color: var(--e-text-2); text-decoration-color: rgba(79, 93, 104, 0.4); font-weight: 580; }
.link-btn[disabled] { cursor: default; text-decoration: none; color: var(--e-text-2); }

.actions { display: flex; flex-wrap: wrap; gap: 12px; }
.action-stack { display: flex; flex-direction: column; gap: 12px; }

/* ── forms ────────────────────────────────────────────────────────────── */
.form { display: flex; flex-direction: column; gap: 16px; }
.field { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.label,
.field > label,
.label-row > label { font-size: 14px; font-weight: 640; color: var(--e-text); }
.label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.label-row .link-btn { font-size: 13px; }
.label-opt { margin-left: 6px; color: var(--e-text-2); font-size: 12px; font-weight: 600; }

.input,
.textarea {
  width: 100%;
  min-height: 52px;
  padding: 13px 16px;
  border: 1px solid var(--e-border-strong);
  border-radius: var(--e-radius-input);
  background: var(--e-surface-2);
  color: var(--e-text);
  font-size: 16px;
  line-height: 1.3;
  transition: border-color 0.15s var(--e-ease), box-shadow 0.15s var(--e-ease), background-color 0.15s var(--e-ease);
}
.textarea { min-height: 110px; line-height: 1.45; resize: vertical; }
.input::placeholder,
.textarea::placeholder { color: var(--e-text-3); opacity: 1; }
.input:hover,
.textarea:hover { border-color: rgba(18, 28, 36, 0.24); }
.input:focus,
.textarea:focus { outline: none; border-color: var(--e-accent); box-shadow: var(--e-ring); background-color: #fff; }
.input[readonly] { background-color: var(--e-surface-3); color: var(--e-text-2); border-color: transparent; }
.input[aria-invalid="true"],
.textarea[aria-invalid="true"] { border-color: var(--e-risk-ink); }
.input[aria-invalid="true"]:focus,
.textarea[aria-invalid="true"]:focus { box-shadow: 0 0 0 4px rgba(164, 50, 67, 0.14); }
.input:disabled,
.textarea:disabled { cursor: not-allowed; opacity: 0.6; }

/* A select drawn like the inputs, with the chevron every Aluma app draws:
   12px, stroke 2, in --e-text-3, its ink 16px inside the border (it was an
   18px icon whose ink sat 20.7px in), with 38px so no option runs under it.
   The roster's sort uses this one too, rather than an icon of its own.
   A list box ([multiple], or a [size] over 1) opens nothing and keeps no
   arrow; :where() holds this at select.input's own (0,1,1). */
select.input:where(:not([multiple]):is(:not([size]), [size="0"], [size="1"])) {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%235f6c77' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px 12px;
  text-overflow: ellipsis;
  cursor: pointer;
}
select.input:disabled { cursor: not-allowed; }

/* The calendar and clock glyphs: on the chevron's 16px line, in the muted
   ink instead of full black. A date field keeps 14px on that side for it;
   the hour field (.ac-hour) already pads 12px. */
input.input:is([type="date"], [type="datetime-local"]) { padding-right: 14px; }
.input::-webkit-calendar-picker-indicator { margin-inline-end: -2px; opacity: 0.62; cursor: pointer; }
.input::-webkit-calendar-picker-indicator:hover { opacity: 1; }
/* A search field's clear button: the browser drew a blue X; this is the
   app's muted one, 16px in like the chevron. */
.input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  /* Into the 16px padding, so the X's ink ends 16px from the edge. */
  margin: 0 -4px 0 0;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%2373808a' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M3 3l6 6M9 3l-6 6'/%3E%3C/svg%3E") center / 12px 12px no-repeat;
  cursor: pointer;
}

.input-lock { position: relative; }
.input-lock .input { padding-right: 46px; }
.input-lock .ico {
  position: absolute;
  right: 16px;
  top: 50%;
  width: 18px;
  height: 18px;
  margin-top: -9px;
  color: var(--e-icon);
  pointer-events: none;
}

.pw-wrap { position: relative; }
.pw-wrap .input { padding-right: 54px; }
.pw-toggle {
  position: absolute;
  right: 6px;
  top: 50%;
  width: 40px;
  height: 40px;
  margin-top: -20px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--e-radius-pill);
  background: none;
  color: var(--e-icon);
  cursor: pointer;
}
.pw-toggle:hover { color: var(--e-text); background: var(--e-surface-3); }
.pw-toggle[aria-pressed="true"] { color: var(--e-accent); }

.field-hint { color: var(--e-text-2); font-size: 13px; }
/* An error says what the hint said: one of them. */
.field:has(> .field-error:not([hidden])) > .field-hint { display: none; }
.field-error { color: var(--e-risk-ink); font-size: 13px; font-weight: 600; }
.field-foot { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.counter { margin-left: auto; color: var(--e-text-2); font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.counter.is-over { color: var(--e-risk-ink); }

.form-error {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 12px 14px;
  border-radius: 18px;
  background: var(--e-risk-soft);
  color: var(--e-risk-ink);
  font-size: 14px;
  font-weight: 580;
  line-height: 1.45;
}
.form-error .ico { width: 18px; height: 18px; margin-top: 1px; }
.form-error .link-btn,
.form-error a { color: var(--e-risk-ink); }
/* A question, not a failure: an invite for a parent account waits at this
   address. The way to that sign-up and "I'm an educator" under it. */
.form-error.is-ask { background: var(--e-warn-soft); color: var(--e-warn-ink); }
.form-error .steer-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.form-error.is-ask .btn { color: var(--e-text); }

.checks { display: flex; flex-direction: column; gap: 12px; }
.check {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  gap: 11px;
  align-items: start;
  font-size: 14px;
  line-height: 1.45;
  cursor: pointer;
}
.check input {
  appearance: none;
  -webkit-appearance: none;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  margin: 0;
  border: 1.5px solid var(--e-icon);
  border-radius: 7px;
  background: #fff;
  cursor: pointer;
  transition: background-color 0.15s var(--e-ease), border-color 0.15s var(--e-ease);
}
.check input:checked { background: var(--e-accent); border-color: var(--e-accent); }
.check input:checked::after {
  content: "";
  width: 6px;
  height: 11px;
  border: solid #fff;
  border-width: 0 2.4px 2.4px 0;
  transform: translateY(-1px) rotate(45deg);
}
.check input[aria-invalid="true"] { border-color: var(--e-risk-ink); }
.check-sub { display: block; color: var(--e-text-2); font-size: 13px; }
.check-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface-3);
  color: var(--e-text-2);
  font-size: 12px;
  font-weight: 640;
  vertical-align: 1px;
}

.fieldset-reset { border: 0; margin: 0; padding: 0; min-width: 0; display: flex; flex-direction: column; gap: 12px; }

/* ── tabs (a pill segmented control) ──────────────────────────────────── */
.tabs {
  display: flex;
  gap: 4px;
  padding: 5px;
  margin-bottom: 26px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface-3);
}
.tab {
  flex: 1 1 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 42px;
  padding: 8px 12px;
  border: 0;
  border-radius: var(--e-radius-pill);
  background: none;
  color: var(--e-text-2);
  font-size: 14px;
  font-weight: 650;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.18s var(--e-ease), color 0.18s var(--e-ease), box-shadow 0.18s var(--e-ease);
}
.tab:hover { color: var(--e-text); }
.tab[aria-selected="true"] {
  background: #fff;
  color: var(--e-text);
  box-shadow: 0 0 0 1px rgba(18, 28, 36, 0.04), 0 2px 6px rgba(18, 28, 36, 0.10);
}
/* A count reads as a pill, never a bare number: white on the grey track,
   grey on the white selected tab. */
.tab-count {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface);
  box-shadow: inset 0 0 0 1px var(--e-hair);
  color: var(--e-text-2);
  font-size: 11px;
  font-weight: 740;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.tab[aria-selected="true"] .tab-count { background: var(--e-surface-3); box-shadow: none; color: var(--e-text); }
/* The sign-in card's two tabs on a 320px phone: each takes half and may
   wrap, so "Create educator account" stays inside the card. */
@media (max-width: 359px) {
  .auth-card .tabs .tab { flex: 1 1 0; min-width: 0; padding-inline: 8px; white-space: normal; text-align: center; }
}

/* a segmented control built from radio inputs */
.seg { display: flex; gap: 2px; padding: 4px; border-radius: var(--e-radius-pill); background: var(--e-surface-3); }
.seg-opt { position: relative; flex: 1 1 0; min-width: 0; display: flex; }
.seg-opt input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.seg-opt label {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 38px;
  padding: 6px 8px;
  border-radius: var(--e-radius-pill);
  color: var(--e-text-2);
  font-size: 14px;
  font-weight: 650;
  text-align: center;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.18s var(--e-ease), color 0.18s var(--e-ease), box-shadow 0.18s var(--e-ease);
}
.seg-opt input:checked + label {
  background: #fff;
  color: var(--e-text);
  box-shadow: 0 0 0 1px rgba(18, 28, 36, 0.04), 0 2px 6px rgba(18, 28, 36, 0.10);
}
.seg-opt input:hover:not(:checked) + label { color: var(--e-text); }
.seg-opt input:focus-visible + label { outline: 2px solid var(--e-accent); outline-offset: 1px; }
.seg-opt input:disabled + label { cursor: not-allowed; opacity: 0.6; }

.chip-radios { display: flex; flex-wrap: wrap; gap: 8px; }
.chip-radio { position: relative; display: flex; }
.chip-radio input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.chip-radio label {
  display: inline-flex;
  align-items: center;
  min-height: 38px;
  padding: 6px 16px;
  border-radius: 19px;
  background: var(--e-surface-3);
  color: var(--e-text);
  font-size: 14px;
  font-weight: 640;
  line-height: 1.3;
  cursor: pointer;
  transition: background-color 0.18s var(--e-ease), color 0.18s var(--e-ease);
}
.chip-radio input:checked + label { background: var(--e-accent); color: #fff; }
.chip-radio input:focus-visible + label { outline: 2px solid var(--e-accent); outline-offset: 2px; }
.chip-radio input:disabled + label { cursor: not-allowed; opacity: 0.6; }

/* ── signed-out landing ───────────────────────────────────────────────── */
.auth {
  display: grid;
  gap: 24px;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "hero" "card" "more";
}
.auth-hero { grid-area: hero; display: flex; flex-direction: column; gap: 12px; }
.auth-hero .lede { font-size: 18px; max-width: 36ch; text-wrap: pretty; }
/* The landing's headline runs a little looser than .display once there is
   room: at 800 weight -0.05em ran the letters of "educators" together. */
@media (min-width: 640px) {
  .auth-hero .display { letter-spacing: -0.035em; }
}
.auth-card { grid-area: card; padding: 22px; border-radius: var(--e-radius-hero); }
.auth-more { grid-area: more; display: flex; flex-direction: column; gap: 16px; }
/* Invited educators start from their email; one quiet line says so. */
.auth-helper { margin-top: 18px; text-align: center; font-size: 14px; line-height: 1.5; color: var(--e-text-2); text-wrap: balance; }
/* The other two doors, as the family app draws its own: the question in the
   quiet ink, the way through as the link. */
.auth-other {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--e-hair);
  color: var(--e-text-2);
  font-size: 14px;
  text-align: center;
  text-wrap: balance;
}
.auth-other a {
  font-weight: 650;
  text-decoration-color: rgba(31, 95, 130, 0.35);
  text-underline-offset: 3px;
  border-radius: 4px;
}
.auth-other a:hover { text-decoration-color: currentColor; }
@media (min-width: 640px) {
  .auth-card { padding: 32px; }
}
/* Two columns: the headline's top lines up with the card's, and the sample
   fills the column under it, so the two sides end near the same line. */
@media (min-width: 920px) {
  .auth {
    grid-template-columns: minmax(0, 1fr) minmax(380px, 440px);
    grid-template-areas: "hero card" "more card";
    grid-template-rows: auto 1fr;
    column-gap: 64px;
    row-gap: 28px;
    align-items: start;
  }
  .auth-card { position: sticky; top: 96px; }
}
/* One column: the sign-in card is as wide as every other signed-out card. */
@media (max-width: 919px) {
  .auth { max-width: 480px; margin: 0 auto; }
}

/* The sample card fills its column. It is a picture, so its Open and Remind
   buttons stay off it. From 500px wide it splits under the counts (the weak
   spot and the week on the left, what's next on the right), so it reads as
   a card rather than a tall strip. */
.preview { width: 100%; margin: 0; container: preview / inline-size; }
.preview .rs-card-foot { display: none; }
@container preview (min-width: 500px) {
  .preview .rs-card {
    display: grid;
    grid-template-areas:
      "head head"
      "why why"
      "counts counts"
      "weak next"
      "study next";
    grid-template-rows: auto auto auto auto 1fr;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    column-gap: 24px;
    row-gap: 14px;
    align-items: start;
  }
  .preview .rs-head { grid-area: head; }
  .preview .rs-reasons { grid-area: why; }
  .preview .rs-counts { grid-area: counts; }
  .preview .rs-weakest { grid-area: weak; }
  .preview .rs-study { grid-area: study; }
  .preview .rs-next { grid-area: next; align-self: stretch; padding: 0 0 0 24px; border-top: 0; border-left: 1px solid var(--e-hair); }
}
/* The sample roster card when the roster view isn't there to draw it. */
.preview-card { display: flex; flex-direction: column; gap: 16px; padding: 24px; }
.pv-head { display: flex; align-items: center; gap: 14px; min-width: 0; }
.pv-id { flex: 1; min-width: 0; }
.pv-name { font-size: 19px; font-weight: 760; letter-spacing: -0.025em; line-height: 1.2; }
.pv-meta { margin-top: 2px; color: var(--e-text-2); font-size: 14px; }
.pv-reasons { color: var(--e-text-2); font-size: 14px; font-weight: 600; }
.pv-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.pv-label { color: var(--e-text-2); font-size: 14px; font-weight: 600; }
.pv-value { font-size: 22px; font-weight: 760; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }

.privacy-line {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--e-text-2);
  font-size: 14px;
  font-weight: 580;
}
.privacy-line .ico { width: 18px; height: 18px; color: var(--e-accent); }

/* ── narrow centered cards (invites, the code step, results) ──────────── */
.narrow { max-width: 480px; margin: 0 auto; }
.narrow.is-wide { max-width: 540px; }
.center-card {
  padding: 32px 22px;
  border-radius: var(--e-radius-hero);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
@media (min-width: 640px) {
  .center-card { padding: 40px 40px 36px; }
}
.center-card .lede { font-size: 16px; text-wrap: pretty; }
/* A headline that wraps does it evenly ("This invite is for / a student"). */
.center-card .h1,
.invite-head .h1 { text-wrap: balance; }
.center-card .actions { justify-content: center; margin-top: 6px; }
@media (max-width: 559px) {
  .center-card .actions { align-self: stretch; }
  .center-card .actions .btn { flex: 1 1 100%; }
}
.center-card .form { width: 100%; text-align: left; }
.center-foot { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 18px; font-size: 14px; color: var(--e-text-2); }
/* One quiet door under a result: the question plain, the way through linked. */
.center-door { margin-top: 2px; color: var(--e-text-2); font-size: 14px; }
.center-door a { font-weight: 650; text-decoration-color: rgba(31, 95, 130, 0.35); text-underline-offset: 3px; border-radius: 4px; }

.code-input {
  text-align: center;
  font-family: var(--e-mono);
  font-size: 28px;
  font-weight: 600;
  letter-spacing: 0.22em;
  min-height: 64px;
}
.code-input::placeholder { letter-spacing: 0.22em; }

.note {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 13px 15px;
  border-radius: 18px;
  background: var(--e-surface-2);
  color: var(--e-text-2);
  font-size: 14px;
  text-align: left;
}
.note .ico { width: 18px; height: 18px; margin-top: 2px; color: var(--e-icon); }
.note > span { min-width: 0; overflow-wrap: anywhere; }
.note strong { color: var(--e-text); }
.note.is-warm { background: var(--e-warn-soft); color: var(--e-warn-ink); }
.note.is-warm .ico { color: var(--e-warn-ink); }
.note.is-ok { background: var(--e-accent-soft); color: var(--e-accent-soft-ink); }
.note.is-ok .ico { color: var(--e-accent); }
.scope-note .ico { color: var(--e-accent); }

/* The promise in two lines that scan: what is shared, then what never is. */
.scope-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 15px 16px;
  border-radius: 20px;
  background: var(--e-surface-2);
  box-shadow: inset 0 0 0 1px var(--e-hair);
  text-align: left;
}
.scope-title { color: var(--e-text); font-size: 14px; font-weight: 700; }
.scope-row {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr);
  gap: 10px;
  align-items: start;
  color: var(--e-text-2);
  font-size: 14px;
  line-height: 1.5;
}
.scope-row .ico { width: 18px; height: 18px; margin-top: 2px; color: var(--e-accent); }
.scope-row > span { min-width: 0; overflow-wrap: anywhere; }
.scope-row.is-never { color: var(--e-text); font-weight: 600; }
.scope-row.is-never .ico { color: var(--e-ok-ink); }

/* A permission the student turned off: who did, and who can turn it on. */
.perm-note {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 13px 15px;
  border-radius: 18px;
  background: var(--e-warn-soft);
  color: var(--e-warn-ink);
  font-size: 14px;
  font-weight: 560;
  line-height: 1.45;
  text-align: left;
}
.perm-note .ico { width: 18px; height: 18px; margin-top: 1px; color: var(--e-warn-ink); }
.perm-note > span { min-width: 0; overflow-wrap: anywhere; }

.invite-card { padding: 30px 22px 24px; border-radius: var(--e-radius-hero); }
@media (min-width: 640px) {
  .invite-card { padding: 40px 40px 30px; }
}
.invite-head { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; margin-bottom: 26px; }
.invite-head .lede { font-size: 16px; }
.invite-head .from-email { margin-top: -6px; }
.from-email { color: var(--e-text-2); font-size: 14px; font-weight: 600; overflow-wrap: break-word; }
.invite-card .scope-note,
.invite-card .scope-list { margin: -8px 0 22px; }
.invite-card .perm-note,
.invite-card > .note { margin: -8px 0 20px; }
.invite-decline { margin-top: 22px; text-align: center; font-size: 14px; color: var(--e-text-2); }
/* A student account at an educator invite's address: the ways out. */
.invite-mistake { margin: -10px 0 18px; font-size: 15px; color: var(--e-text-2); }
.invite-other { margin-top: 16px; text-align: center; font-size: 14px; }

/* ── avatars ──────────────────────────────────────────────────────────── */
.avatar {
  --av-bg: #dfeaf2;
  --av-ink: #1d4f6b;
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--av-bg);
  color: var(--av-ink);
  font-size: 16px;
  font-weight: 740;
  letter-spacing: -0.02em;
  line-height: 1;
  user-select: none;
}
.avatar-sm { width: 32px; height: 32px; font-size: 13px; }
.avatar-lg { width: 52px; height: 52px; font-size: 20px; }
.avatar-xl { width: 68px; height: 68px; font-size: 27px; }

/* ── small shared pieces ──────────────────────────────────────────────── */
.badges { display: flex; flex-wrap: wrap; gap: 8px; }
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface-3);
  color: var(--e-text-2);
  font-size: 13px;
  font-weight: 650;
}
.badge .ico { width: 15px; height: 15px; }
.badge.is-night { background: var(--e-night-soft); color: var(--e-night-ink); }
.badge.is-accent { background: var(--e-accent-soft); color: var(--e-accent-soft-ink); }

.tag {
  display: inline-block;
  padding: 1px 9px;
  margin-left: 6px;
  border-radius: var(--e-radius-pill);
  background: var(--e-accent-soft);
  color: var(--e-accent-soft-ink);
  font-size: 12px;
  font-weight: 680;
  vertical-align: 1px;
}
.tag.is-muted { background: var(--e-surface-3); color: var(--e-text-2); }

.due {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 11px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface-3);
  color: var(--e-text-2);
  font-size: 13px;
  font-weight: 650;
  white-space: nowrap;
}
.due.is-today { background: var(--e-warn-soft); color: var(--e-warn-ink); }
.due.is-done,
.due.is-soon { background: var(--e-accent-soft); color: var(--e-accent-soft-ink); }
.due.is-late { background: var(--e-risk-soft); color: var(--e-risk-ink); }

/* the kinds of work worth planning around: a test, a quiz, an essay */
.kind-badge {
  display: inline-flex;
  flex: none;
  align-items: center;
  padding: 1px 7px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface-3);
  color: var(--e-text-2);
  font-size: 11px;
  font-weight: 720;
  line-height: 1.5;
  white-space: nowrap;
}
.kind-badge.is-test { background: var(--e-streak-soft); color: var(--e-streak-ink); }
.kind-badge.is-quiz { background: var(--e-warn-soft); color: var(--e-warn-ink); }
.kind-badge.is-project { background: var(--e-night-soft); color: var(--e-night-ink); }
.kind-badge.is-essay { background: var(--e-violet-soft); color: var(--e-violet-ink); }
.kind-badge.is-lab { background: var(--e-accent-soft); color: var(--e-accent-soft-ink); }

.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 6px 12px 6px 10px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface-3);
  color: var(--e-text-2);
  font-size: 13px;
  font-weight: 620;
  white-space: nowrap;
}
.chip .ico { width: 14px; height: 14px; color: var(--e-icon); }
.chip.is-accent { background: var(--e-accent-soft); color: var(--e-accent-soft-ink); }
.chip.is-accent .ico { color: var(--e-accent); }
.chip.is-mastered { background: var(--e-violet-soft); color: var(--e-violet-ink); }
.chip.is-mastered .ico { color: var(--e-violet-ink); }
.chip.is-night { background: var(--e-night-soft); color: var(--e-night-ink); }
.chip.is-night .ico { color: var(--e-night-ink); }
.chip.is-risk { background: var(--e-risk-soft); color: var(--e-risk-ink); }
.chip.is-risk .ico { color: var(--e-risk-ink); }
.chip.is-off { background: var(--e-surface-3); color: var(--e-text-2); }

/* On track, needs attention, at risk: always a word, never colour alone. */
.status-chip {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 6px;
  padding: 5px 12px 5px 9px;
  border-radius: var(--e-radius-pill);
  background: var(--e-ok-soft);
  color: var(--e-ok-ink);
  font-size: 13px;
  font-weight: 650;
  line-height: 1.3;
  white-space: nowrap;
}
.status-chip .ico { width: 14px; height: 14px; }
.status-chip.is-on_track { background: var(--e-ok-soft); color: var(--e-ok-ink); }
.status-chip.is-attention { background: var(--e-warn-soft); color: var(--e-warn-ink); }
.status-chip.is-at_risk { background: var(--e-risk-soft); color: var(--e-risk-ink); }
.status-chip.is-sm { padding: 3px 10px 3px 8px; font-size: 12px; }
.status-chip.is-sm .ico { width: 13px; height: 13px; }

/* A topic's score: a bar on its track, the number beside it. */
.pct-bar { display: flex; align-items: center; gap: 10px; min-width: 0; }
.pct-track {
  position: relative;
  flex: 1 1 auto;
  min-width: 48px;
  height: 8px;
  border-radius: var(--e-radius-pill);
  background: var(--e-bar-track);
  overflow: hidden;
}
.pct-fill {
  display: block;
  height: 100%;
  min-width: 4px;
  border-radius: inherit;
  background: var(--e-bar-known);
}
.pct-fill.is-known { background: var(--e-bar-known); }
.pct-fill.is-learning { background: var(--e-bar-learning); }
.pct-fill.is-weak { background: var(--e-bar-weak); }
.pct-fill.is-zero { min-width: 0; }
.pct-num {
  flex: none;
  min-width: 38px;
  text-align: right;
  color: var(--e-text);
  /* Geist's own figures, tabular: the mono face slashes its zeros, which
     next to a score reads as "none". */
  font-size: 13px;
  font-weight: 680;
  font-variant-numeric: tabular-nums;
}

.streak {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 5px;
  padding: 6px 12px 6px 9px;
  border-radius: var(--e-radius-pill);
  background: var(--e-streak-soft);
  color: var(--e-streak-ink);
  font-size: 15px;
  font-weight: 740;
}
.streak .ico { width: 18px; height: 18px; color: var(--e-streak); }
.streak.is-zero { background: var(--e-surface-3); color: var(--e-text-2); }
.streak.is-zero .ico { color: var(--e-icon); }

.counts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 4px;
  padding: 14px 4px;
  border-radius: 22px;
  background: var(--e-surface-2);
}
.count { display: flex; flex-direction: column; align-items: center; gap: 3px; text-align: center; min-width: 0; }
.count-num { font-size: 25px; font-weight: 760; letter-spacing: -0.03em; line-height: 1.1; font-variant-numeric: tabular-nums; }
.count-label { color: var(--e-text-2); font-size: 12px; font-weight: 600; line-height: 1.25; }
.count.is-risk .count-num,
.count.is-risk .count-label { color: var(--e-risk-ink); }
.count.is-warn .count-num,
.count.is-warn .count-label { color: var(--e-warn-ink); }
.count.is-zero .count-num { color: var(--e-text-3); }

.stats-card { padding: 22px 24px; }
.stats { display: grid; gap: 20px 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 720px) {
  .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.stat { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.stat-label { display: flex; align-items: center; gap: 7px; color: var(--e-text-2); font-size: 13px; font-weight: 640; }
.stat-label .ico { width: 16px; height: 16px; color: var(--e-icon); }
.stat-label .ico.is-warm { color: var(--e-streak); }
.stat-value { font-size: 30px; font-weight: 780; letter-spacing: -0.035em; line-height: 1.15; font-variant-numeric: tabular-nums; }
.stat-sub { color: var(--e-text-3); font-size: 13px; font-weight: 560; }
.stat.is-risk .stat-value { color: var(--e-risk-ink); }
.stat.is-warn .stat-value { color: var(--e-warn-ink); }

.progress { flex: 1; height: 8px; border-radius: var(--e-radius-pill); background: var(--e-bar-track); overflow: hidden; }
.progress span { display: block; height: 100%; border-radius: inherit; background: var(--e-accent); }
.goal-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  align-items: center;
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--e-hair);
}
@media (min-width: 720px) {
  .goal-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); gap: 28px; }
}
.goal-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.goal-title { font-size: 17px; font-weight: 720; letter-spacing: -0.015em; overflow-wrap: anywhere; }
.goal-bar { display: flex; align-items: center; gap: 12px; }
.goal-pct { flex: none; min-width: 44px; text-align: right; font-size: 15px; font-weight: 720; font-variant-numeric: tabular-nums; }

.back-btn { align-self: flex-start; margin-left: -12px; }

.banner {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 16px 20px;
  border-radius: 22px;
  background: var(--e-surface);
  box-shadow: var(--e-shadow-sm);
  font-size: 15px;
}
.banner .ico { color: var(--e-accent); }

/* ── rows (lists inside a card) ───────────────────────────────────────── */
.list-card { padding: 4px 0; }
.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  min-height: 60px;
  padding: 12px 24px;
  border-top: 1px solid var(--e-hair);
}
.row:first-child { border-top: 0; }
.row > .avatar { flex: none; }
.row-main { min-width: 0; flex: 1 1 110px; }
.row-title { font-size: 15px; font-weight: 640; overflow-wrap: break-word; word-break: break-word; }
.row-sub { color: var(--e-text-2); font-size: 13px; overflow-wrap: break-word; }
.row-actions { display: flex; flex: none; gap: 6px; margin-left: auto; }
.row-buttons { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 24px 16px; }

/* ── settings rows and switches ───────────────────────────────────────── */
.set-card { overflow: hidden; }
.set-row { display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; gap: 14px; align-items: center; padding: 16px 20px; }
.set-row + .set-row,
.set-block + .set-row,
.set-row + .set-block,
.set-block + .set-block { border-top: 1px solid var(--e-hair); }
.set-ico {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 13px;
  background: var(--e-accent-soft);
  color: var(--e-accent);
}
.set-ico .ico { width: 19px; height: 19px; }
.set-ico.is-warm { background: var(--e-streak-soft); color: var(--e-streak-ink); }
.set-ico.is-night { background: var(--e-night-soft); color: var(--e-night-ink); }
.set-ico.is-violet { background: var(--e-violet-soft); color: var(--e-violet-ink); }
.set-ico.is-warn { background: var(--e-warn-soft); color: var(--e-warn-ink); }
.set-text { min-width: 0; }
.set-name { display: block; font-size: 15px; font-weight: 680; }
label.set-name { cursor: pointer; }
.set-desc { display: block; color: var(--e-text-2); font-size: 13px; line-height: 1.4; margin-top: 1px; }
.set-end { display: inline-flex; align-items: center; gap: 10px; }
.set-block { padding: 14px 20px 18px; display: flex; flex-direction: column; gap: 12px; }
.set-block.is-inset { padding-left: 72px; }
.set-block > * { max-width: 360px; }
@media (max-width: 420px) {
  .set-block.is-inset { padding-left: 20px; }
}

.switch {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  flex: none;
  width: 51px;
  height: 31px;
  margin: 0;
  border: 0;
  border-radius: var(--e-radius-pill);
  /* Off is a quiet track with a hairline ring, so it never reads louder than
     on; the blue is spent only on the switch that is doing something. */
  background: rgba(18, 28, 36, 0.14);
  box-shadow: inset 0 0 0 1px var(--e-border-strong);
  cursor: pointer;
  transition: background-color 0.22s var(--e-ease), box-shadow 0.22s var(--e-ease);
}
.switch::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 27px;
  height: 27px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2), 0 0 0 0.5px rgba(0, 0, 0, 0.05);
  transition: transform 0.22s var(--e-ease);
}
.switch:checked { background: var(--e-accent); box-shadow: none; }
.switch:checked::before { transform: translateX(20px); }
.switch:disabled { cursor: not-allowed; opacity: 0.5; }
.switch.is-busy { cursor: progress; opacity: 0.7; }

/* ── tables (the roster's list mode) ──────────────────────────────────── */
.table-wrap {
  /* Holds the sr-only text inside its cells, so it can't widen the page. */
  position: relative;
  width: 100%;
  overflow-x: auto;
  border-radius: var(--e-radius-card);
  background: var(--e-surface);
  box-shadow: var(--e-shadow);
}
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.table th,
.table td {
  padding: 12px 14px;
  border-top: 1px solid var(--e-hair);
  text-align: left;
  vertical-align: middle;
}
.table thead th {
  border-top: 0;
  color: var(--e-text-2);
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}
.table td.is-num,
.table th.is-num { text-align: right; font-variant-numeric: tabular-nums; }
.th-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 32px;
  padding: 0 4px;
  margin: 0 -4px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.th-btn:hover { color: var(--e-text); }
.th-btn .ico { width: 14px; height: 14px; color: var(--e-icon); }
th[aria-sort="ascending"] .th-btn,
th[aria-sort="descending"] .th-btn { color: var(--e-text); }

/* ── the calendar: due work and planned sittings, a week at a time ────── */
.ec-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 16px; }
.ec-title-wrap { display: flex; align-items: center; gap: 12px; min-width: 0; }
.ec-ico {
  display: grid;
  flex: none;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 14px;
  background: var(--e-accent-soft);
  color: var(--e-accent);
}
.ec-range { min-height: 21px; color: var(--e-text-2); font-size: 14px; font-weight: 600; }
.ec-nav { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.ec-step {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--e-border);
  border-radius: 50%;
  background: #fff;
  color: var(--e-text);
  cursor: pointer;
  box-shadow: 0 2px 0 rgba(18, 28, 36, 0.08);
  transition: background-color 0.18s var(--e-ease), transform 0.12s var(--e-ease);
}
.ec-step:hover:not([disabled]) { background: var(--e-surface-2); }
.ec-step:active:not([disabled]) { transform: translateY(1px); }
.ec-step[disabled] { cursor: not-allowed; opacity: 0.5; }
.ec-home { min-height: 44px; }
/* On this week already: it stays focusable (focus doesn't jump), just quiet. */
.ec-home[aria-disabled="true"] { background: var(--e-surface-3); border-color: transparent; color: var(--e-text-2); box-shadow: none; cursor: default; opacity: 1; }
.ec-home[aria-disabled="true"]:active { transform: none; }

.ec-body { min-width: 0; }
/* Phones and tablets: one day under the next, the date at the side. */
.ec-week { display: flex; flex-direction: column; gap: 8px; }
.ec-day {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  min-width: 0;
  padding: 12px;
  border-radius: 20px;
  background: var(--e-surface-2);
  box-shadow: inset 0 0 0 1px var(--e-hair);
}
.ec-day.is-today { background: var(--e-accent-soft); box-shadow: inset 0 0 0 1.5px rgba(31, 95, 130, 0.32); }
.ec-week.is-loading .ec-day { min-height: 64px; box-shadow: none; }
.ec-date { display: flex; flex-direction: column; align-items: center; gap: 1px; font-size: inherit; text-align: center; }
.ec-wd { color: var(--e-text-2); font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
.ec-dn { font-size: 22px; font-weight: 760; letter-spacing: -0.03em; line-height: 1.1; font-variant-numeric: tabular-nums; }
.ec-day.is-past .ec-dn { color: var(--e-text-2); }
.ec-today {
  margin-top: 3px;
  padding: 1px 8px;
  border-radius: var(--e-radius-pill);
  background: var(--e-accent);
  color: #fff;
  font-size: 11px;
  font-weight: 720;
}
/* A free day is one quiet line. */
.ec-day.is-empty { align-items: center; padding-top: 7px; padding-bottom: 7px; background: transparent; }
.ec-day.is-empty.is-today { background: var(--e-accent-soft); }
.ec-day.is-empty .ec-date { flex-direction: row; justify-content: center; align-items: baseline; gap: 5px; }
.ec-day.is-empty .ec-dn { font-size: 16px; }
.ec-day.is-empty .ec-today { margin: 0; }
/* An empty today on the stacked list: "Thu 8" on one line and the Today
   chip under it, inside the date column, so it never runs into "Free". */
.ec-day.is-empty.is-today .ec-date {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: center;
  align-items: baseline;
  gap: 2px 5px;
}
.ec-day.is-empty.is-today .ec-today { grid-column: 1 / -1; justify-self: center; }
/* A day gone by: no card of its own, and what it held a little quieter,
   except work still late, which is the point of looking back. Quieter by
   its ground (no white card, only a hairline), never by fading the text:
   12px grey meta at 85% fell to the edge of AA. */
.ec-day.is-past { background: transparent; }
.ec-day.is-past .ec-item:not(.is-late) { background: transparent; box-shadow: 0 0 0 1px var(--e-hair); }
.ec-day-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ec-free { color: var(--e-text-2); font-size: 14px; }
.ec-list { display: flex; flex-direction: column; gap: 6px; }
.ec-item {
  min-width: 0;
  padding: 8px 10px 8px 12px;
  border-radius: 14px;
  background: #fff;
  box-shadow: inset 3px 0 0 var(--e-accent), 0 0 0 1px var(--e-hair);
}
.ec-item.is-late { background: var(--e-risk-soft); box-shadow: inset 3px 0 0 var(--e-risk-ink); }
.ec-item.is-late .ec-title { color: var(--e-risk-ink); }
.ec-item.is-done { background: transparent; box-shadow: 0 0 0 1px var(--e-hair); }
.ec-item.is-done .ec-title { color: var(--e-text-2); text-decoration: line-through; text-decoration-color: rgba(79, 93, 104, 0.5); }
/* A planned sitting is the student's own plan: outlined, not a deadline. */
.ec-item.is-plan { padding-left: 10px; background: rgba(255, 255, 255, 0.6); box-shadow: inset 0 0 0 1.5px rgba(13, 89, 99, 0.26); }
.ec-item.is-plan.is-done { box-shadow: inset 0 0 0 1px var(--e-hair); }
.ec-item.is-plan.is-done .ec-title { color: var(--e-text-2); }
/* The educator's own sitting: a thin accent rule on its left. */
.ec-item.is-plan.is-educator { box-shadow: inset 3px 0 0 var(--e-accent), inset 0 0 0 1.5px rgba(31, 95, 130, 0.26); }
.ec-plan-line { color: var(--e-night-ink); font-size: 12px; font-weight: 720; line-height: 1.4; }
.ec-plan-line .ico { display: inline-flex; margin-right: 4px; vertical-align: -3px; color: var(--e-night-ink); }
.ec-session {
  display: inline-block;
  padding: 0 6px;
  border-radius: var(--e-radius-pill);
  background: var(--e-night-soft);
  color: var(--e-night-ink);
  font-size: 11px;
  font-weight: 720;
  white-space: nowrap;
}
.ec-title { font-size: 14px; font-weight: 650; line-height: 1.3; overflow-wrap: anywhere; }
/* Another educator's to-do: whose it is, never what it says. */
.ec-title.is-private { color: var(--e-text-2); font-style: italic; font-weight: 560; }
.ec-tick { display: inline-flex; margin-right: 4px; vertical-align: -3px; color: var(--e-accent); }
.ec-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 3px 6px; margin-top: 3px; color: var(--e-text-2); font-size: 12px; font-weight: 560; }
.ec-class { min-width: 0; overflow-wrap: anywhere; }
.ec-flag { color: var(--e-risk-ink); font-weight: 720; }
.ec-by {
  display: inline-flex;
  max-width: 100%;
  margin: 6px 4px 0 0;
  padding: 1px 8px;
  border-radius: var(--e-radius-pill);
  background: var(--e-accent-soft);
  color: var(--e-accent-soft-ink);
  font-size: 11px;
  font-weight: 700;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.ec-mins { color: var(--e-text-2); font-size: 12px; font-weight: 680; }
.ec-more { color: var(--e-text-2); font-size: 12px; font-weight: 680; }
/* Move and Remove show on every sitting, so an educator sees at a glance
   that a sitting can be moved. On a mouse they rest as quiet words and fill
   in when the pointer or focus is on the sitting; on touch they are plain
   buttons. */
.ec-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.ec-actions .btn-sm,
.ec-add.btn-sm { min-height: 32px; padding: 0 10px; font-size: 13px; }
.ec-actions .btn .ico,
.ec-add .ico { width: 15px; height: 15px; }
@media (hover: hover) and (pointer: fine) {
  .ec-actions .btn-soft { background: transparent; box-shadow: none; color: var(--e-accent); }
  .ec-actions .btn-quiet.is-danger { color: var(--e-text-2); }
  .ec-item:hover .ec-actions .btn-soft,
  .ec-item:focus-within .ec-actions .btn-soft { background: var(--e-accent-soft); color: var(--e-accent-soft-ink); }
  .ec-item:hover .ec-actions .btn-quiet.is-danger,
  .ec-item:focus-within .ec-actions .btn-quiet.is-danger { color: var(--e-risk-ink); }
}
/* Add sitting: a dashed slot at the foot of the day, the place a sitting
   would go. */
.ec-add {
  align-self: stretch;
  border: 1.5px dashed rgba(31, 95, 130, 0.30);
  border-radius: 14px;
  background: transparent;
}
.ec-add:hover:not([disabled]):not([aria-disabled="true"]) { border-color: rgba(31, 95, 130, 0.5); background: var(--e-accent-soft); }
.ec-foot { color: var(--e-text-2); font-size: 14px; font-weight: 600; }
/* A week on its way: the last one stays, dimmed, instead of a skeleton. */
.ec-body,
.ec-foot { transition: opacity 0.18s var(--e-ease); }
.ec-body.is-stale,
.ec-foot.is-stale { opacity: 0.45; pointer-events: none; }
.ec-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 28px 16px;
  border-radius: 22px;
  background: var(--e-surface-2);
  color: var(--e-text-2);
  font-size: 15px;
  font-weight: 580;
  text-align: center;
}
.ec-empty-ico {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 16px;
  background: var(--e-surface-3);
  color: var(--e-icon);
}
.ec-error {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 12px;
  padding: 14px 16px;
  border-radius: 18px;
  background: var(--e-risk-soft);
  color: var(--e-risk-ink);
  font-size: 14px;
  font-weight: 580;
}
.ec-error p { flex: 1 1 160px; min-width: 0; }
.ec-error .ico { width: 18px; height: 18px; }

/* Wide: the week as seven columns, Monday first. */
@media (min-width: 920px) {
  .ec-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }
  .ec-day,
  .ec-day.is-empty {
    display: flex;
    flex-direction: column;
    gap: 10px;
    align-items: stretch;
    min-height: 168px;
    padding: 10px 8px 12px;
    background: var(--e-surface-2);
  }
  .ec-week.is-loading .ec-day { min-height: 168px; }
  .ec-date,
  .ec-day.is-empty .ec-date {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: flex-start;
    align-items: baseline;
    gap: 2px 6px;
    padding: 0 4px 8px;
    border-bottom: 1px solid var(--e-hair);
  }
  .ec-dn,
  .ec-day.is-empty .ec-dn { font-size: 20px; }
  .ec-today,
  .ec-day.is-empty .ec-today { margin: 0 0 0 auto; }
  /* The stacked list's own layout for an empty today (the chip under the
     date) undone: in a column it reads like every other day, "THU 8" with
     the Today chip at the end. */
  .ec-day.is-empty.is-today .ec-date { display: flex; justify-content: flex-start; gap: 2px 6px; }
  .ec-day.is-empty.is-today .ec-today { grid-column: auto; justify-self: auto; margin: 0 0 0 auto; }
  .ec-day.is-today .ec-date { border-bottom-color: rgba(31, 95, 130, 0.22); }
  .ec-free { padding: 0 4px; font-size: 13px; }
  .ec-item { padding: 7px 8px 7px 10px; }
  .ec-item.is-plan { padding-left: 8px; }
  .ec-title { font-size: 13px; }
  .ec-mins,
  .ec-more { padding: 0 4px; }
  .ec-day.is-past { background: transparent; }
  /* In a seventh of the page, Move and Remove are words only (each keeps
     its full name for a screen reader). At 1280 both fit on one line and
     share it; in a narrower column they stack, each the column's width,
     rather than wrap ragged. */
  .ec-actions { gap: 3px; margin-top: 6px; }
  .ec-actions .btn-sm { flex: 1 1 auto; justify-content: flex-start; min-height: 28px; padding: 0 4px; font-size: 12px; }
  .ec-actions .btn .ico { display: none; }
  .ec-add.btn-sm { padding: 0 6px; }
}

/* ── the invite-students modal ────────────────────────────────────────── */
.inv-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.inv-chips:empty { display: none; }
.inv-chips .chip { white-space: normal; overflow-wrap: anywhere; }
.inv-chips .chip.is-bad { background: var(--e-risk-soft); color: var(--e-risk-ink); }
.inv-chips .chip.is-bad .ico { color: var(--e-risk-ink); }
/* "Not an email" is part of the chip: its address, then the word, split by
   a thin rule in the chip's own ink rather than a middle dot. */
.inv-chip-why {
  flex: none;
  margin-left: 2px;
  padding-left: 8px;
  border-left: 1px solid rgba(164, 50, 67, 0.3);
  font-weight: 720;
}
.inv-count { color: var(--e-text-2); font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
/* The outcome first, in its colour: every invite went, some did, none did. */
.inv-summary {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 13px 15px;
  border-radius: 18px;
  background: var(--e-surface-2);
  color: var(--e-text);
  font-size: 15px;
  font-weight: 650;
  line-height: 1.45;
}
.inv-summary .ico { width: 20px; height: 20px; margin-top: 0; }
.inv-summary > span { min-width: 0; }
.inv-summary.is-ok { background: var(--e-ok-soft); color: var(--e-ok-ink); }
.inv-summary.is-part { background: var(--e-warn-soft); color: var(--e-warn-ink); }
.inv-summary.is-bad { background: var(--e-risk-soft); color: var(--e-risk-ink); }
/* One soft row per address, tinted by how it went. */
.inv-results { display: flex; flex-direction: column; gap: 6px; }
.inv-result {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  gap: 1px 10px;
  align-items: start;
  padding: 11px 14px;
  border-radius: 16px;
  background: var(--e-ok-soft);
}
.inv-result > .ico { width: 18px; height: 18px; margin-top: 2px; color: var(--e-ok-ink); }
.inv-result[data-ok="0"] { background: var(--e-risk-soft); }
.inv-result[data-ok="0"] > .ico { color: var(--e-risk-ink); }
.inv-result[data-sent="0"] { background: var(--e-warn-soft); }
.inv-result[data-sent="0"] > .ico { color: var(--e-warn-ink); }
.inv-result-email { color: var(--e-text); font-size: 15px; font-weight: 640; overflow-wrap: anywhere; }
.inv-result-note { grid-column: 2; color: var(--e-ok-ink); font-size: 13px; font-weight: 600; }
.inv-result[data-ok="0"] .inv-result-note { color: var(--e-risk-ink); }
.inv-result[data-sent="0"] .inv-result-note { color: var(--e-warn-ink); }
/* Emailed within the hour already: the invite out answers, good news. */
.inv-result.is-recent { background: var(--e-ok-soft); }
.inv-result.is-recent > .ico,
.inv-result.is-recent .inv-result-note { color: var(--e-ok-ink); }

/* ── the educator profile modal ───────────────────────────────────────── */
.prof-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px 14px;
  min-width: 0;
  padding: 14px;
  border-radius: 22px;
  background: var(--e-surface-2);
  box-shadow: inset 0 0 0 1px var(--e-hair);
}
.prof-id { min-width: 0; }
/* A phone: the count goes under the name, lined up with it. */
@media (max-width: 479px) {
  .prof-head { grid-template-columns: auto minmax(0, 1fr); }
  .prof-count { grid-column: 2; justify-self: start; }
}
.prof-name { color: var(--e-text); font-size: 17px; font-weight: 720; letter-spacing: -0.015em; overflow-wrap: anywhere; }
.prof-sub { color: var(--e-text-2); font-size: 14px; overflow-wrap: anywhere; }
.prof-count {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 6px;
  padding: 5px 12px 5px 10px;
  border-radius: var(--e-radius-pill);
  background: var(--e-accent-soft);
  color: var(--e-accent-soft-ink);
  font-size: 13px;
  font-weight: 680;
  font-variant-numeric: tabular-nums;
}
.prof-count .ico { width: 15px; height: 15px; }
/* What a reminder and a planner item will say, as they type: two rows. */
.prof-says {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 9px 12px;
  border-radius: 14px;
  background: var(--e-night-soft);
  color: var(--e-night-ink);
  font-size: 13px;
  font-weight: 560;
  overflow-wrap: anywhere;
}
.prof-says-row { display: flex; align-items: center; gap: 8px; }
.prof-says .ico { flex: none; width: 16px; height: 16px; }
.prof-says strong { font-weight: 720; }
.modal-loading { display: flex; flex-direction: column; gap: 12px; }

/* ── modal ────────────────────────────────────────────────────────────── */
/* A bottom sheet on phones, a centred card from 560px. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  /* 16px: the phone gutter every page in the three apps keeps. */
  padding: 16px;
  background: rgba(18, 28, 36, 0.36);
  animation: e-fade 0.18s var(--e-ease);
}
@media (min-width: 560px) {
  .modal-backdrop { align-items: center; padding: 24px; }
}
.modal {
  width: 100%;
  max-width: 480px;
  max-height: calc(100vh - 24px);
  overflow: auto;
  overscroll-behavior: contain;
  padding: 24px 22px 22px;
  border-radius: 32px;
  background: var(--e-surface);
  box-shadow: var(--e-shadow-pop);
  animation: e-rise 0.24s var(--e-ease);
}
.modal.is-wide { max-width: 560px; }
@supports (max-height: 100dvh) {
  .modal { max-height: calc(100dvh - 24px); }
}
@media (min-width: 640px) {
  .modal { padding: 30px 30px 26px; }
}
.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.modal-title { font-size: 21px; font-weight: 760; letter-spacing: -0.025em; line-height: 1.25; overflow-wrap: anywhere; }
.modal-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  margin: -6px -8px 0 0;
  border: 0;
  border-radius: var(--e-radius-pill);
  background: none;
  color: var(--e-icon);
  cursor: pointer;
}
.modal-close:hover { background: var(--e-surface-3); color: var(--e-text); }
.modal-body { display: flex; flex-direction: column; gap: 16px; color: var(--e-text-2); font-size: 15px; }
.modal-body .form,
.modal-body .field { color: var(--e-text); }
.modal-actions { display: flex; flex-wrap: wrap-reverse; justify-content: flex-end; gap: 10px; margin-top: 24px; }
@media (max-width: 479px) {
  .modal-actions .btn { flex: 1 1 140px; }
}
/* The head and the buttons stay put and the body scrolls between them. A
   tall form on a phone (the profile: four fields and two notes) put Cancel
   and Save under the sheet's bottom edge until you scrolled to them. The
   body's 4px inset keeps a focused field's ring inside its own scroll box,
   and its last 14px fade, so a form that runs on under the buttons softens
   out instead of ending on a hard cut. */
.modal { display: flex; flex-direction: column; overflow: hidden; }
.modal-head, .modal-actions { flex: none; }
.modal-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
  padding: 4px 4px 14px;
  margin: -4px -4px -14px;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), transparent);
}

/* ── toasts ───────────────────────────────────────────────────────────── */
.toasts {
  position: fixed;
  left: 50%;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  z-index: 150;
  width: min(440px, calc(100vw - 32px));
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}
/* While a modal or phone sheet is open, toasts move up out of its way. */
html.modal-open .toasts { top: calc(12px + env(safe-area-inset-top, 0px)); bottom: auto; }
.toast {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px 10px 10px;
  border-radius: 24px;
  background: var(--e-surface);
  color: var(--e-text);
  font-size: 14px;
  font-weight: 560;
  line-height: 1.4;
  box-shadow: var(--e-shadow-pop);
  pointer-events: auto;
  animation: e-toast 0.24s var(--e-ease);
  /* As wide as what it says: a short toast is a short pill. */
  max-width: 100%;
}
.toast-text { padding-right: 6px; }
/* The icon in a soft round, in the toast's tone: ok, error, or plain news. */
.toast-ico {
  display: grid;
  flex: none;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--e-accent-soft);
  color: var(--e-accent);
}
.toast .ico { width: 18px; height: 18px; }
.toast.is-ok .toast-ico { background: var(--e-ok-soft); color: var(--e-ok-ink); }
.toast.is-error .toast-ico { background: var(--e-risk-soft); color: var(--e-risk-ink); }
.toast.is-error .ico { color: var(--e-risk-ink); }
.toast-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.toast-action {
  flex: none;
  padding: 6px 12px;
  border: 0;
  border-radius: var(--e-radius-pill);
  background: var(--e-accent-soft);
  color: var(--e-accent-soft-ink);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}
.toast-action:hover { background: #d8e6f0; }
.toast.is-leaving { opacity: 0; transform: translateY(6px); transition: opacity 0.2s var(--e-ease), transform 0.2s var(--e-ease); }

/* ── small utilities ──────────────────────────────────────────────────── */
.minw0 { min-width: 0; }
.nowrap { white-space: nowrap; }
.mt-12 { margin-top: 12px; }

/* ── motion ───────────────────────────────────────────────────────────── */
.view-in { animation: e-view 0.22s var(--e-ease); }

@keyframes e-shimmer { from { background-position: 120% 0; } to { background-position: -120% 0; } }
@keyframes e-spin { to { transform: rotate(360deg); } }
@keyframes e-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes e-rise { from { opacity: 0; transform: translateY(16px) scale(0.99); } to { opacity: 1; transform: none; } }
@keyframes e-pop { from { opacity: 0; transform: translateY(-4px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes e-toast { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes e-view { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* Less motion means none: no transition, no animation, no lift or slide. */
@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;
  }
  .btn:active,
  .ec-step:active,
  .toast.is-leaving { transform: none !important; }
  .ec-body,
  .ec-foot { transition: none; }
}

/* ── touch: 44px targets ──────────────────────────────────────────────── */
@media (pointer: coarse) {
  .btn-sm,
  .btn-quiet,
  .seg-opt label,
  .chip-radio label,
  .th-btn,
  .ec-actions .btn-sm,
  .ec-add.btn-sm { min-height: 44px; }
  .tab { min-height: 44px; }
  .modal-close { width: 44px; height: 44px; }
  /* The optional news box, and the doors to the other two apps: a link in
     a sentence grows its tap area with padding, which leaves the line (and
     a wrapped question above it) where it was. */
  .check { min-height: 44px; align-content: center; }
  .auth-other a,
  .center-door a { padding-block: 13px; }
  .link-btn { padding: 10px 0; }
  .footer-links a { display: inline-flex; align-items: center; min-height: 44px; }
  /* A tap anywhere on a setting's row flips its switch (the label covers it). */
  .set-row { position: relative; }
  .set-row label.set-name::after { content: ""; position: absolute; inset: 0; }
}

/* ── Announcements (core/announcements.py) ────────────────────────────────
   The one popup an admin queued for this educator (educators.js
   maybeAnnouncement): the parent app's block in this app's tokens.
   Centred at every width, unlike the other dialogs, which sit at the
   bottom of a phone; its tile carries the category's glyph. */
.modal-backdrop.is-announce { align-items: center; }
.modal.announce-modal { position: relative; max-width: 520px; text-align: center; }
.announce-modal .announce-art {
  width: 72px;
  height: 72px;
  margin: 2px auto 14px;
  display: grid;
  place-items: center;
  border-radius: var(--e-radius-card);
  background: var(--e-accent-soft);
  color: var(--e-accent);
}
.announce-modal .announce-art svg { width: 30px; height: 30px; display: block; }
.announce-kicker {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 720;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--e-accent-soft-ink);
}
.announce-modal .modal-head { justify-content: center; }
.announce-modal .modal-title { font-size: 30px; letter-spacing: -0.03em; line-height: 1.15; overflow-wrap: anywhere; }
.announce-modal .modal-close { position: absolute; top: 14px; right: 14px; width: 44px; height: 44px; margin: 0; }
/* pre-line: a custom announcement's blank line is a paragraph break, and
   its text is never markup, so a newline is the only way to make one. */
.announce-lede { max-width: 40ch; margin: 0 auto; font-size: 16px; line-height: 1.55; white-space: pre-line; overflow-wrap: anywhere; }
.announce-points {
  margin: 0;
  padding: 16px 18px;
  list-style: none;
  display: grid;
  gap: 10px;
  text-align: left;
  border-radius: var(--e-radius-input);
  background: var(--e-surface-2);
  box-shadow: inset 0 0 0 1px var(--e-hair);
  color: var(--e-text);
}
.announce-points li { display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: 10px; align-items: start; font-size: 15px; line-height: 1.45; overflow-wrap: anywhere; }
.announce-points .ico {
  width: 22px;
  height: 22px;
  margin-top: 1px;
  padding: 4px;
  border-radius: var(--e-radius-pill);
  background: var(--e-accent-soft);
  color: var(--e-accent);
}
.announce-modal .modal-actions { justify-content: center; }
@media (max-width: 479px) {
  .modal.announce-modal { padding-left: 20px; padding-right: 20px; }
  .announce-modal .modal-title { font-size: 26px; }
  .announce-modal .modal-actions { flex-direction: column-reverse; }
  .announce-modal .modal-actions .btn { flex: none; width: 100%; }
  .announce-modal .modal-close { top: 8px; right: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .modal.announce-modal { animation: none !important; }
}
