/* Aluma for educators: the roster home (educators_roster.js).

   Loaded after educators.css, whose tokens and shared pieces (.card, .btn,
   .stat, .status-chip, .pct-bar, .table, .th-btn) this builds on. Every
   class here starts with rs- or sits under .roster, and every colour is an
   --e-* token or a tint of one: the gate's sweeps read every sheet, and an
   app-wide token name here would trip them.

   What the page is for: who needs help, and why, in one look. So the
   header names the students behind each number, the cards lead with the
   status and the reason, and a quiet student's card stays short.

   Widths: one column of compact cards on a phone, with the search and the
   filters held under the top bar while the list scrolls; three cards a row
   at 1120px with the whole toolbar on one line. The table needs about
   1100px, so the layout toggle hides below that (the script shows cards
   there whatever was chosen). */

.roster { display: flex; flex-direction: column; gap: 28px; min-width: 0; }
@media (min-width: 640px) {
  .roster { gap: 32px; }
}
/* No students yet: one calm column, the way the family app starts. */
.roster[data-count="0"] { width: 100%; max-width: 680px; margin: 0 auto; }

/* ── greeting ─────────────────────────────────────────────────────────── */
.roster .greet { display: flex; flex-direction: column; gap: 6px; }
.roster .greet-date { color: var(--e-text-2); font-size: 14px; font-weight: 600; }
.roster .greet-sub { color: var(--e-text-2); font-size: 17px; line-height: 1.45; max-width: 60ch; }
/* With students, Invite students sits beside the greeting: on a phone in
   the date's row, wider down the right of the whole block. */
.roster .rs-greet.has-action {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "date act" "title title" "sub sub";
  align-items: center;
  column-gap: 12px;
  row-gap: 6px;
}
.rs-greet .greet-date { grid-area: date; }
.rs-greet .h1 { grid-area: title; }
.rs-greet .greet-sub { grid-area: sub; }
.rs-greet-act { grid-area: act; display: flex; justify-content: flex-end; }
@media (max-width: 699px) {
  .rs-greet-act .btn { min-height: 40px; padding: 0 16px; font-size: 14px; }
  .rs-greet-act .btn .ico { width: 16px; height: 16px; }
}
@media (min-width: 700px) {
  .roster .rs-greet.has-action { grid-template-areas: "date act" "title act" "sub act"; column-gap: 24px; }
  .rs-greet-act { align-self: end; padding-bottom: 2px; }
}
/* The numbers belong to the sentence above them: a closer step. */
.roster > .rs-greet + .rs-stats { margin-top: -8px; }
@media (min-width: 640px) {
  .roster > .rs-greet + .rs-stats { margin-top: -12px; }
}

/* ── the four numbers ─────────────────────────────────────────────────── */
.rs-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
@media (min-width: 760px) {
  .rs-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
}
.rs-stats .rs-stat {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  margin: 0;
  padding: 16px 18px 15px;
  border: 0;
  border-radius: 22px;
  background: var(--e-surface);
  box-shadow: var(--e-shadow-sm);
  color: var(--e-text);
  font: inherit;
  text-align: left;
}
.rs-stat .stat-label { display: flex; align-items: center; gap: 7px; color: var(--e-text-2); font-size: 13px; font-weight: 640; }
.rs-stat .stat-label .ico { width: 16px; height: 16px; color: var(--e-icon); }
.rs-stat .stat-value { display: block; font-size: 30px; font-weight: 780; letter-spacing: -0.035em; line-height: 1.1; font-variant-numeric: tabular-nums; }
.rs-stat .stat-sub { display: block; color: var(--e-text-2); font-size: 13px; font-weight: 600; line-height: 1.35; overflow-wrap: anywhere; }
.rs-stat .stat-sub.is-risk { color: var(--e-risk-ink); }
/* A soft wash when the number is not zero, at half strength so the four
   tiles still read as one row. Inks measured on it: risk 6.3:1, warn 5.6:1. */
.rs-stats .rs-stat.is-risk { background: #fcf4f3; background: color-mix(in srgb, var(--e-risk-soft) 50%, var(--e-surface)); }
.rs-stat.is-risk .stat-label,
.rs-stat.is-risk .stat-label .ico,
.rs-stat.is-risk .stat-value,
.rs-stat.is-risk .stat-sub { color: var(--e-risk-ink); }
.rs-stats .rs-stat.is-warn { background: #fbf7eb; background: color-mix(in srgb, var(--e-warn-soft) 50%, var(--e-surface)); }
.rs-stat.is-warn .stat-label,
.rs-stat.is-warn .stat-label .ico,
.rs-stat.is-warn .stat-value,
.rs-stat.is-warn .stat-sub { color: var(--e-warn-ink); }
/* At risk and Need attention are buttons: they show only those students. */
.rs-stats .rs-stat.is-link {
  cursor: pointer;
  transition: transform 0.18s var(--e-ease), box-shadow 0.18s var(--e-ease);
}
.rs-stats .rs-stat.is-link:hover { transform: translateY(-1px); box-shadow: var(--e-shadow); }
.rs-stats .rs-stat.is-link:focus-visible { outline: 2px solid var(--e-accent); outline-offset: 3px; }
.rs-stats .rs-stat.is-risk[aria-pressed="true"] { box-shadow: inset 0 0 0 2px var(--e-risk-ink), var(--e-shadow-sm); }
.rs-stats .rs-stat.is-warn[aria-pressed="true"] { box-shadow: inset 0 0 0 2px var(--e-warn-ink), var(--e-shadow-sm); }
.rs-stat-go { display: inline-flex; margin-left: auto; opacity: 0.7; }
@media (max-width: 479px) {
  .rs-stat-go { display: none; }
}
.rs-stat .rs-stat-go .ico { width: 16px; height: 16px; }
.rs-stat.is-link:hover .rs-stat-go,
.rs-stat.is-link[aria-pressed="true"] .rs-stat-go { opacity: 1; }

/* ── invites waiting on this educator ─────────────────────────────────── */
.rs-incoming-list { display: flex; flex-direction: column; gap: 14px; }
.rs-incoming {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 22px 20px;
  background:
    radial-gradient(90% 140% at 100% 0%, rgba(176, 208, 232, 0.42), rgba(176, 208, 232, 0) 62%),
    var(--e-surface);
  box-shadow: 0 0 0 1px rgba(31, 95, 130, 0.14), var(--e-shadow);
}
@media (min-width: 640px) {
  .rs-incoming { padding: 24px 26px; }
}
.rs-incoming-top { display: flex; align-items: center; gap: 14px; }
.rs-incoming-title { font-size: 18px; font-weight: 740; line-height: 1.3; letter-spacing: -0.02em; overflow-wrap: anywhere; }
.rs-from-email { margin-top: 2px; color: var(--e-text-2); font-size: 14px; font-weight: 600; overflow-wrap: anywhere; }
.rs-incoming-sub { margin-top: 3px; color: var(--e-accent-soft-ink); font-size: 14px; font-weight: 640; }
.rs-incoming-actions { display: flex; flex-wrap: wrap; gap: 10px; }
@media (max-width: 479px) {
  .rs-incoming-actions .btn { flex: 1 1 100%; }
}
/* Wide enough: the answer sits beside the question. */
@media (min-width: 720px) {
  .rs-incoming {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 12px 24px;
  }
  .rs-incoming > .form-error { grid-column: 1 / -1; }
}

/* ── toolbar ──────────────────────────────────────────────────────────── */
/* One line from about 1050px (search, filters, then sort and layout at
   the end); below that the sort and layout take a second line. */
/* 16px to the first card: the gap every filter row in Aluma leaves. */
.rs-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-bottom: 16px; }
.rs-tools-a { display: flex; flex: 1 1 auto; flex-wrap: wrap; align-items: center; gap: 10px; min-width: 0; }
.rs-tools-b { display: flex; flex: none; align-items: center; gap: 10px; min-width: 0; margin-left: auto; }
@media (max-width: 1099px) {
  .rs-tools-b { margin-left: 0; }
}

/* A set width, so the row's length is known and it stays one line at
   1280; the search takes whatever room is left over. */
.rs-search { position: relative; flex: 1 1 auto; width: 200px; min-width: 180px; max-width: 320px; }
/* One row: the search, the chips, the sort and the layout toggle all 44px
   tall in 14px type (the search was 48.8px in 16px type: min-height alone
   loses to .input's padding and line height). The search's magnifier and
   the sort's icon start 13px in, where the chips start their dots, and
   both fields start their text at the same 37px. */
.rs-search .ico {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 11px;
  width: 16px;
  height: 16px;
  margin: auto 0;
  color: var(--e-icon);
  pointer-events: none;
}
.rs-search .rs-search-input {
  width: 100%;
  height: 44px;
  min-height: 44px;
  padding-block: 0;
  padding-left: 36px;
  border-radius: var(--e-radius-pill);
  font-size: 14px;
}

.rs-filters { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.rs-filter {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 6px 0 12px;
  border: 1px solid var(--e-border-strong);
  border-radius: var(--e-radius-pill);
  background: var(--e-surface);
  color: var(--e-text);
  font: inherit;
  font-size: 14px;
  font-weight: 650;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.18s var(--e-ease), border-color 0.18s var(--e-ease), color 0.18s var(--e-ease);
}
.rs-filter:hover { border-color: var(--e-accent); }
.rs-filter:focus-visible { outline: 2px solid var(--e-accent); outline-offset: 2px; }
.rs-filter-n {
  min-width: 22px;
  padding: 1px 6px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface-3);
  color: var(--e-text-2);
  font-size: 12px;
  font-weight: 720;
  line-height: 1.5;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.rs-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--e-ok-ink); }
.rs-filter.is-at_risk .rs-dot { background: var(--e-risk-ink); }
.rs-filter.is-attention .rs-dot { background: var(--e-warn-ink); }
.rs-filter[aria-pressed="true"] { border-color: var(--e-accent); background: var(--e-accent); color: #fff; }
.rs-filter[aria-pressed="true"] .rs-filter-n { background: var(--e-accent-lip); color: #fff; }
.rs-filter[aria-pressed="true"] .rs-dot { box-shadow: 0 0 0 2px #fff; }
/* Nobody in the group (educators_roster.js chipIdle): readable, not pressable. */
.rs-filter:disabled { border-color: var(--e-border); color: var(--e-text-3); cursor: default; }
.rs-filter:disabled .rs-dot { opacity: 0.45; }

.rs-sort { display: flex; align-items: center; min-width: 0; }
.rs-select-wrap { position: relative; display: flex; min-width: 0; }
.rs-select-wrap .ico {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 16px;
  height: 16px;
  margin: auto 0;
  color: var(--e-text-3);
  pointer-events: none;
}
.rs-select-wrap .ico:first-child { left: 11px; color: var(--e-icon); }
/* The chevron is select.input's (educators.css), so the fill here is a
   colour only: a `background:` shorthand erased that image. */
.rs-sort .rs-sort-select {
  width: auto;
  min-width: 0;
  min-height: 44px;
  padding: 0 38px 0 36px;
  border: 1px solid var(--e-border-strong);
  border-radius: var(--e-radius-pill);
  background-color: var(--e-surface);
  color: var(--e-text);
  font: inherit;
  font-size: 14px;
  /* A field's weight, the search's, like every other select.input. */
  font-weight: 400;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
}
.rs-sort .rs-sort-select:hover,
.rs-sort .rs-sort-select:focus { border-color: var(--e-accent); }
.rs-sort .rs-sort-select:disabled { border-color: var(--e-border-strong); cursor: not-allowed; }
.rs-select-wrap:has(> select:disabled) > .ico { opacity: 0.6; }
/* A finger gets 16px type in both fields, so iOS does not zoom into them.
   After the rules above on purpose: the same (0,2,0), so only the later
   one wins. */
@media (pointer: coarse) {
  .rs-search .rs-search-input,
  .rs-sort .rs-sort-select { font-size: 16px; }
}

.rs-view {
  display: inline-flex;
  flex: none;
  gap: 2px;
  padding: 3px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface-3);
}
.rs-view-btn {
  display: inline-grid;
  place-items: center;
  width: 42px;
  height: 38px;
  padding: 0;
  border: 0;
  border-radius: var(--e-radius-pill);
  background: transparent;
  color: var(--e-text-2);
  cursor: pointer;
  transition: background-color 0.18s var(--e-ease), color 0.18s var(--e-ease);
}
.rs-view-btn .ico { width: 18px; height: 18px; }
.rs-view-btn:hover { color: var(--e-text); }
.rs-view-btn[aria-pressed="true"] { background: var(--e-surface); color: var(--e-accent); box-shadow: var(--e-shadow-sm); }
.rs-view-btn:focus-visible { outline: 2px solid var(--e-accent); outline-offset: 1px; }

/* A phone: the search and the filters stay under the top bar while the
   cards scroll (the toolbar box steps aside so the strip can stick inside
   the whole students section); the sort scrolls away beneath it. */
@media (max-width: 699px) {
  .rs-toolbar { display: contents; }
  .rs-tools-a {
    position: sticky;
    top: 66px;
    z-index: 20;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 8px;
    margin: 0 calc(-1 * var(--e-gutter));
    padding: 10px var(--e-gutter);
    background: rgba(244, 247, 249, 0.92);
    box-shadow: 0 1px 0 var(--e-hair), 0 10px 18px -16px rgba(18, 28, 36, 0.28);
    -webkit-backdrop-filter: saturate(1.4) blur(16px);
    backdrop-filter: saturate(1.4) blur(16px);
  }
  .rs-tools-b { margin: 6px 0 16px; }
  .rs-search { flex: none; width: 100%; max-width: none; min-width: 0; }
  /* The chips wrap rather than scroll: as one row the last chip ("On
     track") sat wholly past the edge with nothing peeking in, so a phone
     never learned it was there. */
  .rs-filters { flex-wrap: wrap; overflow: visible; }
  .rs-tools-b,
  .rs-sort,
  .rs-select-wrap { flex: 1 1 auto; }
  .rs-sort .rs-sort-select { width: 100%; }
}
/* Too narrow for the table (educators_roster.js WIDE): cards only, so no
   layout toggle. */
@media (max-width: 1099px) {
  .rs-view { display: none; }
}
/* A focused card or button scrolled to on a phone clears the strip too. */
@media (max-width: 699px) {
  html:has(.roster .rs-tools-a) { scroll-padding-top: 242px; }
}

/* ── the cards ────────────────────────────────────────────────────────── */
.rs-grid {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
}
@media (min-width: 1000px) {
  .rs-grid { gap: 20px; }
}
/* The card asks its own width where the status chip goes. */
.rs-card { container: rs-card / inline-size; }

.rs-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
  padding: 20px 20px 18px;
  cursor: pointer;
  transition: transform 0.2s var(--e-ease), box-shadow 0.2s var(--e-ease);
}
@media (min-width: 640px) {
  .rs-card { padding: 22px 22px 20px; }
}
.rs-card:hover { transform: translateY(-2px); box-shadow: var(--e-shadow-hover); }
/* Keyboard focus inside a card marks the whole card, so the eye finds it. */
.rs-card:has(:focus-visible) { box-shadow: 0 0 0 2px rgba(31, 95, 130, 0.38), var(--e-shadow); }
.rs-card.is-sample { cursor: default; }
.rs-card.is-sample:hover { transform: none; box-shadow: var(--e-shadow); }

/* The status sits top right on every card, so it reads down a column of
   cards. In a card a third of the page wide the name takes the full line
   under the avatar (beside the chip it broke "Diego Martinez" in two); a
   wide card keeps all three on one row. */
.rs-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "av . chip" "id id id";
  align-items: center;
  column-gap: 12px;
  row-gap: 10px;
}
.rs-head > .avatar { grid-area: av; width: 42px; height: 42px; font-size: 17px; }
.rs-id { grid-area: id; min-width: 0; }
.rs-chip-slot { grid-area: chip; justify-self: end; min-width: 0; }
/* Wide enough for one row: the avatar, the name with the status beside
   it, and the meta line free to run under the chip. */
@container rs-card (min-width: 310px) {
  .rs-head { grid-template-areas: "av name chip" "av meta meta"; row-gap: 1px; }
  .rs-id { display: contents; }
  .rs-name { grid-area: name; align-self: end; }
  .rs-meta { grid-area: meta; align-self: start; margin-top: 0; }
  .rs-head:not(:has(.rs-meta)) .rs-name { grid-row: 1 / span 2; align-self: center; }
  .rs-head > .avatar { grid-row: 1 / span 2; }
  .rs-chip-slot { grid-area: chip; }
}
/* A phone's card: the chip keeps its word and drops its icon, so a full
   name like "Diego Martinez" stays on one line beside it. */
@container rs-card (min-width: 310px) and (max-width: 379px) {
  .rs-chip-slot .status-chip { padding: 3px 10px; }
  .rs-chip-slot .status-chip .ico { display: none; }
}
@container rs-card (min-width: 420px) {
  .rs-head > .avatar { width: 46px; height: 46px; font-size: 18px; }
}
/* min-width 0 and anywhere: a long surname with no break in it
   ("Wolfeschlegelsteinhausenbergerdorff") wraps inside the card instead of
   pushing past it and the page. */
.rs-name { min-width: 0; font-size: 18px; font-weight: 760; line-height: 1.22; letter-spacing: -0.02em; }
.rs-open {
  max-width: 100%;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  overflow-wrap: anywhere;
}
.rs-open:hover { color: var(--e-accent); }
.rs-open:focus-visible { outline: 2px solid var(--e-accent); outline-offset: 2px; }
.rs-meta { margin-top: 2px; color: var(--e-text-2); font-size: 14px; line-height: 1.35; overflow-wrap: anywhere; }

/* Why: the line under the name, in the status's own ink. */
.rs-reasons { color: var(--e-text); font-size: 14px; font-weight: 650; line-height: 1.4; overflow-wrap: anywhere; }
.rs-card[data-status="at_risk"] .rs-reasons { color: var(--e-risk-ink); }
.rs-card[data-status="attention"] .rs-reasons { color: var(--e-warn-ink); }
.rs-reasons.is-ok { color: var(--e-ok-ink); }

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

.rs-weakest { display: flex; flex-direction: column; gap: 4px; }
.rs-weak-line { display: grid; grid-template-columns: minmax(0, 1fr) minmax(112px, 0.8fr); align-items: center; gap: 12px; }
.rs-topic { min-width: 0; font-size: 15px; font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Geist's own figures: the mono face draws a slashed zero, which reads as
   "none" beside a bar. */
.roster .pct-num { font-family: inherit; font-weight: 680; }

.rs-study { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-width: 0; }
.rs-study-value { min-width: 0; color: var(--e-text-2); font-size: 14px; }
.rs-study-num { color: var(--e-text); font-size: 18px; font-weight: 760; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.rs-study.is-quiet { justify-content: flex-start; gap: 7px; color: var(--e-text-2); font-size: 14px; font-weight: 600; }
.rs-study.is-quiet .ico { width: 16px; height: 16px; color: var(--e-icon); }
.rs-streak {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 5px;
  padding: 5px 11px 5px 8px;
  border-radius: var(--e-radius-pill);
  background: var(--e-streak-soft);
  color: var(--e-streak-ink);
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
}
.rs-streak .ico { width: 16px; height: 16px; color: var(--e-streak); }
.rs-streak.is-zero { background: var(--e-surface-3); color: var(--e-text-2); }
.rs-streak.is-zero .ico { color: var(--e-icon); }

.rs-next { display: flex; flex-direction: column; gap: 2px; padding-top: 12px; border-top: 1px solid var(--e-hair); }
.rs-next-list { margin: 0; padding: 0; list-style: none; }
.rs-next-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 10px;
  padding: 7px 0;
}
.rs-next-row + .rs-next-row { border-top: 1px solid var(--e-hair); }
/* Two lines before an ellipsis: "Unit 4 problem set" keeps its class. */
.rs-next-title {
  display: -webkit-box;
  min-width: 0;
  overflow: hidden;
  font-size: 14px;
  font-weight: 640;
  line-height: 1.35;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow-wrap: anywhere;
}
.rs-next-class { color: var(--e-text-2); font-weight: 560; }

.rs-card-foot { display: flex; flex-wrap: wrap; gap: 8px; margin-top: auto; padding-top: 2px; }

/* A phone: the compact card. Who, the status, why, the three numbers and
   the weak spot, then Open and Remind; the page has the rest. */
@media (max-width: 699px) {
  .rs-grid .rs-card { gap: 12px; padding: 18px 18px 16px; }
  .rs-grid .rs-study,
  .rs-grid .rs-next { display: none; }
  .rs-grid .rs-weakest { flex-direction: row; align-items: center; gap: 10px; }
  .rs-grid .rs-weakest .mini-label { flex: none; }
  .rs-grid .rs-weak-line { flex: 1 1 auto; min-width: 0; grid-template-columns: minmax(0, 1fr) 104px; gap: 10px; }
  .rs-grid .rs-name { font-size: 17px; }
  .rs-grid .rs-card-foot .btn { flex: 1 1 0; }
}
/* A narrow phone: "Weakest topic" over the topic and its bar, so the topic
   keeps its whole name ("Two-step equations", not "Tw..."). */
@media (max-width: 419px) {
  .rs-grid .rs-weakest { flex-wrap: wrap; row-gap: 4px; }
  .rs-grid .rs-weakest .mini-label { flex-basis: 100%; }
  .rs-grid .rs-weak-line { grid-template-columns: minmax(0, 1fr) 88px; }
  .rs-grid .rs-topic { white-space: normal; overflow-wrap: anywhere; }
}

/* One or two students: the rest of the row offers the next step. */
.rs-addcard {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 220px;
  padding: 28px 22px;
  border: 1.5px dashed var(--e-border-strong);
  border-radius: var(--e-radius-card);
  text-align: center;
}
.rs-addcard .icon-badge { width: 48px; height: 48px; border-radius: 16px; }
.rs-addcard .icon-badge .ico { width: 22px; height: 22px; }
.rs-add-title { font-size: 17px; font-weight: 740; letter-spacing: -0.015em; }
.rs-add-text { max-width: 30ch; color: var(--e-text-2); font-size: 14px; line-height: 1.45; }
.rs-addcard .btn { margin-top: 4px; }

/* ── the table (from 1100px) ──────────────────────────────────────────── */
.rs-table-wrap {
  /* Positioned, so the sr-only text in the far cells is clipped with the
     rest of the table instead of widening the page. */
  position: relative;
  background: var(--e-surface);
  box-shadow: var(--e-shadow-sm);
}
.rs-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.rs-table th,
.rs-table td { padding: 12px 8px; border-top: 1px solid var(--e-hair); text-align: left; vertical-align: middle; }
.rs-table thead th {
  border-top: 0;
  background: var(--e-surface-2);
  color: var(--e-text-2);
  font-size: 13px;
  font-weight: 650;
  white-space: nowrap;
}
.rs-table th:first-child,
.rs-table td:first-child { padding-left: 18px; }
.rs-table th:last-child,
.rs-table td:last-child { padding-right: 16px; }
.rs-table tbody th { font-weight: inherit; }
/* A faint band on every other row keeps a long row on its line. */
.rs-table tbody tr:nth-child(even) { background: rgba(18, 28, 36, 0.03); }
.rs-table tbody tr:hover { background: var(--e-accent-soft); }
.rs-table .th-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: -6px -8px;
  padding: 6px 8px;
  border: 0;
  border-radius: 10px;
  background: none;
  color: inherit;
  font: inherit;
  white-space: nowrap;
  cursor: pointer;
}
.rs-table .th-btn:hover { color: var(--e-text); }
.rs-table .th-btn:focus-visible { outline: 2px solid var(--e-accent); outline-offset: 0; }
.rs-table th.is-sorted { color: var(--e-text); }
/* A small triangle for the order: up runs low to high. */
.rs-sort-arrow { width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; }
.rs-sort-arrow.is-up { border-bottom: 5px solid currentColor; }
.rs-sort-arrow.is-down { border-top: 5px solid currentColor; }
/* Numbers in Geist's tabular figures, right aligned with room to breathe. */
.rs-table .rs-col-num { padding-right: 12px; padding-left: 12px; text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rs-table thead .rs-col-num { font-weight: 650; }
.rs-table thead .rs-col-num .th-btn { margin-right: -8px; }
.rs-table td.is-risk { color: var(--e-risk-ink); font-weight: 750; }
.rs-table .rs-col-student { min-width: 172px; }
.rs-cell-student { display: flex; align-items: center; gap: 10px; min-width: 0; }
.rs-table .rs-open { font-size: 15px; font-weight: 680; overflow-wrap: normal; white-space: nowrap; }
/* Long headers over short numbers ("Due this week") may take two lines,
   so the student's name keeps its one. */
.rs-table thead .rs-col-num { white-space: normal; line-height: 1.25; }
.rs-table thead .rs-col-num .th-btn { white-space: normal; text-align: right; }
.rs-cell-sub { max-width: 170px; margin-top: 2px; color: var(--e-text-2); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rs-table .rs-col-weak { max-width: 200px; padding-left: 16px; }
.rs-cell-weak { display: flex; flex-direction: column; align-items: stretch; gap: 4px; min-width: 120px; max-width: 180px; }
.rs-cell-weak .rs-topic { font-size: 13px; text-align: left; }
.rs-cell-none { color: var(--e-text-3); }
.rs-cell-streak { display: inline-flex; align-items: center; gap: 4px; }
.rs-cell-streak .ico { width: 15px; height: 15px; color: var(--e-streak); }
.rs-cell-streak.is-zero { color: var(--e-text-3); }
.rs-cell-streak.is-zero .ico { color: var(--e-icon); }
.rs-table .rs-col-active { white-space: nowrap; color: var(--e-text-2); }
.rs-table .rs-col-open { text-align: right; }
.rs-row-acts { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
/* The row's Remind is the bell alone; its name is in aria-label. */
.rs-icon-btn { width: 38px; padding: 0; }
.rs-icon-btn .btn-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* ── nothing matches ──────────────────────────────────────────────────── */
.rs-none {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 36px 20px;
  border: 1.5px dashed var(--e-border-strong);
  border-radius: var(--e-radius-card);
  color: var(--e-text-2);
  text-align: center;
}
.rs-none-ico {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: 4px;
  border-radius: 16px;
  background: var(--e-surface-3);
  color: var(--e-icon);
}
.rs-none-ico .ico { width: 20px; height: 20px; }
.rs-none-text { color: var(--e-text); font-size: 16px; font-weight: 680; }
.rs-none-sub { font-size: 14px; }
.rs-none .actions { justify-content: center; margin-top: 6px; }

/* ── invites this educator sent ───────────────────────────────────────── */
.rs-pending { padding: 22px 0 0; overflow: hidden; }
.rs-invite:last-child { padding-bottom: 16px; }
.rs-pending-head { margin-bottom: 10px; padding: 0 20px; }
.rs-pending-head .h2 { display: flex; align-items: center; gap: 10px; }
.rs-pending-n {
  min-width: 26px;
  padding: 1px 8px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface-3);
  color: var(--e-text-2);
  font-size: 13px;
  font-weight: 720;
  line-height: 1.6;
  letter-spacing: 0;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.rs-pending-sub { margin-top: 2px; color: var(--e-text-2); font-size: 14px; }
.rs-invites { margin: 0; padding: 0; list-style: none; }
.rs-invite {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  padding: 12px 20px;
  border-top: 1px solid var(--e-hair);
}
/* Closed invites sit in a light wash, quieter than the live ones. */
.rs-invite[data-state="expired"],
.rs-invite[data-state="declined"] { background: var(--e-surface-2); }
.rs-invite-ico {
  display: grid;
  flex: none;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 12px;
  background: var(--e-accent-soft);
  color: var(--e-accent);
}
.rs-invite[data-state="expired"] .rs-invite-ico,
.rs-invite[data-state="declined"] .rs-invite-ico { background: var(--e-surface-3); color: var(--e-icon); }
.rs-invite-ico .ico { width: 17px; height: 17px; }
.rs-invite-main { flex: 1 1 200px; min-width: 0; }
.rs-invite-email { font-size: 15px; font-weight: 640; overflow-wrap: anywhere; }
.rs-invite.is-closed .rs-invite-email { color: var(--e-text-2); }
.rs-invite-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin-top: 2px; color: var(--e-text-2); font-size: 13px; }
.rs-invite-meta.is-warn { color: var(--e-warn-ink); font-weight: 620; }
.rs-state-pill {
  padding: 1px 9px;
  border-radius: var(--e-radius-pill);
  background: var(--e-surface-3);
  color: var(--e-text-2);
  font-size: 12px;
  font-weight: 680;
  line-height: 1.6;
}
.rs-state-pill.is-expired { background: var(--e-warn-soft); color: var(--e-warn-ink); }
.rs-invite .tiny { margin-top: 3px; }
.rs-invite-actions { display: flex; flex: none; align-items: center; gap: 18px; margin-left: auto; font-size: 14px; }
.rs-invite-actions .link-btn { padding: 6px 0; }
/* With a mouse, Cancel waits for the row's hover or keyboard focus. */
@media (hover: hover) and (pointer: fine) {
  .rs-invite .rs-cancel { opacity: 0; transition: opacity 0.15s var(--e-ease); }
  .rs-invite:hover .rs-cancel,
  .rs-invite:focus-within .rs-cancel { opacity: 1; }
}
.rs-invite .rs-cancel:hover { color: var(--e-risk-ink); }
.rs-pending-more { padding: 10px 20px 8px; border-top: 1px solid var(--e-hair); font-size: 14px; }
@media (min-width: 640px) {
  .rs-pending { padding-top: 24px; }
  .rs-pending-head,
  .rs-invite,
  .rs-pending-more { padding-left: 26px; padding-right: 26px; }
}
@media (max-width: 479px) {
  .rs-invite-actions { width: 100%; padding-left: 46px; }
}

/* ── empty roster ─────────────────────────────────────────────────────── */
.rs-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 36px 22px 30px;
  text-align: center;
}
@media (min-width: 640px) {
  .rs-empty { padding: 44px 40px 36px; }
}
.rs-empty-text { max-width: 46ch; color: var(--e-text-2); font-size: 16px; line-height: 1.5; }
.rs-empty .btn { margin-top: 4px; }
@media (max-width: 479px) {
  .rs-empty .btn { width: 100%; }
}

.roster .rs-foot { color: var(--e-text-2); text-align: center; }

/* ── fingers and less motion ──────────────────────────────────────────── */
@media (pointer: coarse) {
  .rs-filter,
  .rs-view-btn,
  .rs-table .th-btn { min-height: 44px; }
  .rs-card-foot .btn,
  .rs-row-acts .btn { min-height: 44px; }
  .rs-icon-btn { width: 44px; }
  /* Send again and Cancel: a finger's height, with room between them. */
  .rs-invite-actions .link-btn { display: inline-flex; align-items: center; min-height: 44px; padding: 0 4px; }
  .rs-greet-act .btn { min-height: 44px; }
}
@media (prefers-reduced-motion: reduce) {
  .rs-card,
  .rs-filter,
  .rs-view-btn,
  .rs-stats .rs-stat.is-link,
  .rs-invite .rs-cancel { transition: none; }
  .rs-card:hover,
  .rs-stats .rs-stat.is-link:hover { transform: none; }
}
