/* Admin interface — deliberately a different surface from the public pages.
   Dense, full-width, desk-first. The public side is for children; this is an
   instrument.

   The palette is not invented. db/packs/4-mathematics-en/generate_assets.py
   declares INK/ACCENT/SHADE/GRID and draws every diagram with them, so the
   admin adopts the same values and the SVGs sit natively in the page instead
   of clashing with it. */

/* --ink, --accent, --paper, --error, --error-tint and --font-ui
   used to live in a locally declared block here; they are now var(--sk-*)
   aliases onto tokens.css, the one file allowed a global custom-property
   block (see spec/assets/stylesheet_isolation_spec.rb). --shade, --grid and
   --slate have no tokens.css equivalent and stay local, alongside the
   layout-only --rail-width and --bar-height. Declared on body.admin rather
   than .admin-body: .admin-bar (the top bar) is .admin-body's sibling, not
   its descendant, and needs --slate, --grid and --bar-height itself, so the
   properties have to live on an element both share. */

body.admin {
  --shade:  #f4a340;  /* attention ONLY — missing figure, difficulty 3, warning */
  --grid:   #c9ccd1;
  --slate:  #0f1720;

  --rail-width: 260px;
  --bar-height: 52px;

  margin: 0;
  background: var(--sk-fill-1);
  color: var(--sk-ink);
  font-family: var(--sk-font-ui);
  font-size: 14px;
  line-height: 1.5;
}

/* Neither this file nor staff.css (the other stylesheet that styles this
   same body.admin surface) declared a box-sizing reset at all, unlike every
   other surface (application.css's .app-legacy, student.css's
   .sikai-student, landing.css's .sikai-public each declare their own). The
   surface bug this caused: .admin-form textarea below sets `width: 100%;
   max-width: 34rem` and the generic input/select/textarea rules further
   down add padding and a border on top of that -- under the browser's
   content-box default, the padding and border push the rendered element
   wider than its own max-width. */
body.admin,
body.admin * {
  box-sizing: border-box;
}

/* ── Top bar ───────────────────────────────────────────────────────── */

.admin-bar {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  height: var(--bar-height);
  padding: 0 1rem;
  background: var(--slate);
  color: var(--sk-fill-1);
}

.admin-bar__mark {
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--sk-fill-1);
  text-decoration: none;
}

.admin-bar__context { flex: 1; color: var(--grid); }

.admin-bar__user {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--grid);
}

.admin-bar__signout {
  background: none;
  border: 1px solid var(--grid);
  border-radius: 3px;
  color: var(--sk-fill-1);
  cursor: pointer;
  font: inherit;
  padding: 0.2rem 0.6rem;
}

/* ── Body: full-height rail beside a full-width canvas ─────────────── */

.admin-body {
  display: flex;
  min-height: calc(100vh - var(--bar-height));
}

.admin-rail {
  width: var(--rail-width);
  flex: 0 0 var(--rail-width);
  background: var(--slate);
  color: var(--grid);
  padding: 1rem 0;
  overflow-y: auto;
}

.admin-rail:empty { display: none; }

/* No max-width. The content is dense and wide and the admin is at a desk. */
.admin-canvas { flex: 1; min-width: 0; padding: 1.5rem 2rem; }

.admin-canvas h1 { font-size: 1.4rem; font-weight: 600; margin: 0 0 1rem; }

/* layouts/school.html.erb's <main> is this rule's transitional home now that
   Admin::/Teach:: render layout 'school' instead of layout 'staff' -- see
   that layout's own comment. <main> sits inside .sk-shell__panel, which is a
   column flexbox (staff.css), so it needs the same flex/min-height .sk-scroll
   already relies on to fill and scroll, not just admin-canvas's old padding.
   A screen that has already been rebuilt on .sk-scroll (only /teach's classes
   index today) supplies its own padding, so this rule's padding is zeroed
   for it via :has() -- otherwise the two would stack and pad that one screen
   twice -- and takes the flex context with it; see that rule below for why
   the flex context is not on every screen. As each further screen moves to
   sk-*, its .admin-canvas-shaped markup goes with it and this list shrinks;
   nothing here is meant to outlive Plan C/D's rebuilds.

   color/font-family/font-size/line-height are here for the same reason and
   are the same kind of loss as --grid and --shade below, one step further
   out: body.admin above declares this surface's typographic base, and
   body.school (staff.css) declares only margin and box-sizing. Nothing else
   supplies it -- .sk-shell and .sk-shell__panel set no font, .sk-scroll sets
   font-family alone, and stylesheet_isolation_spec guarantees no bare
   `body {}` rule exists in any stylesheet -- so every screen on this shell
   rendered in the browser's default serif at 16px. Unlike --grid and
   --shade, which break one declaration each and are named in a var(), these
   are INHERITED: they reach every element under <main> without being read
   anywhere, which is why the custom-property guard could not see them.
   stylesheet_isolation_spec now compares the two rules' inherited
   declarations directly. `background` and `margin` are deliberately not
   copied down: body.school resets the margin itself and .sk-shell paints
   --sk-page behind the whole shell.

   --grid and --shade are redeclared here, not just widened onto: both were
   declared once, on body.admin (see that rule's own comment on why they
   live there rather than in tokens.css), and body.school main cannot see a
   custom property a body.admin ancestor never wraps it in. --grid was
   found by rendering the page and reading computed styles, not by
   reasoning about the cascade -- var(--grid) with no fallback made the
   *entire* border declaration using it invalid, and a browser silently
   drops an invalid declaration rather than partially applying it, so the
   Remove button below rendered borderless with every other one of its
   properties (background, radius, padding) intact.

   --shade has the identical defect and was found the correct way the
   second time: by enumerating every custom property admin.css declares on
   body.admin and grepping every var() read of each, rather than waiting
   for another rendered page to reveal it. It reaches .admin-warnings
   (admin/deep_lessons/show), .admin-status--refused/--failed
   (admin/deep_lessons/_status, rendered by both index and show),
   .outcome--self and .answers__try and .student-tally__figure--self dt/dd
   (all teach/students/show) -- none of which are .admin-canvas-scoped, so
   Finding 1's selector-widening pass never touched them; only the custom
   property they read was broken. .admin-slot__warn, .ribbon__band--d3,
   .ribbon__notch and .item__missing also read --shade but render only
   under Curriculum:: (layout 'staff', body.admin), so they were never at
   risk. .admin-notice reads it too and matches no view in the app today --
   redeclaring --shade here costs nothing and covers it if that ever
   changes.

   --slate, --rail-width and --bar-height stay on body.admin alone: every
   selector reading them (.admin-bar, .admin-rail, .admin-body) is markup
   only layouts/staff.html.erb renders -- confirmed by grepping every
   layouts/ file and every Admin::/Teach:: view for those class names,
   not by assuming the two structurally similar properties (--grid,
   --shade) exhausted the list. */
body.school main {
  --grid: #c9ccd1;
  --shade: #f4a340;

  flex: 1;
  min-height: 0;
  padding: 1.5rem 2rem;

  color: var(--sk-ink);
  font-family: var(--sk-font-ui);
  font-size: 14px;
  line-height: 1.5;
}

/* Only the rebuilt case gets a flex context. .sk-scroll relies on `flex: 1`
   to fill the panel and scroll inside it, so its parent must be a column
   flexbox -- but a flex parent BLOCKIFIES its children, and the screens still
   on .admin-canvas-shaped markup put inline controls directly in <main>:
   teach/classrooms/show's "All classes" and "Add student", the same pair on
   teach/students/index, teach/students/all's "All classes". Under the flex
   <main> each stretched to the full width of the page -- the accent-blue
   .admin-button rendering as a 1368px bar -- and adjacent-sibling margins
   stopped collapsing, so the vertical rhythm grew on every legacy screen
   too. .admin-canvas was a plain block container and did neither.

   :has() rather than a class on <main>, because which shape a screen is
   depends on what the screen renders, not on what the layout knows: the
   layout yields into a bare landmark on purpose (see layouts/school.html.erb).
   Verified in a rendered browser, not assumed -- /teach's <main> computes
   `display: flex; padding: 0px` and every legacy screen's computes
   `display: block; padding: 24px 32px`. Both this rule and the flex context
   it carries retire with the last .admin-canvas-shaped screen. */
body.school main:has(> .sk-scroll) {
  display: flex;
  flex-direction: column;
  padding: 0;
}

/* :not(.sk-subhead__title) because <main> now also holds the per-screen
   sk-subhead partial on a rebuilt screen, and its <h1 class="sk-subhead__title">
   is more specific by class count but not by element count -- this selector's
   extra body/main/h1 type selectors would otherwise outrank it and silently
   overwrite the subhead's own font-size/weight/margin the moment such a
   screen also had a bare admin-canvas-style <h1> elsewhere in the codebase. */
body.school main h1:not(.sk-subhead__title) { font-size: 1.4rem; font-weight: 600; margin: 0 0 1rem; }

/* Stable ids are data — they key the importer and get quoted in bug reports —
   so they read as data rather than as prose. */
.id { font-family: var(--sk-font-ui); font-size: 0.85em; color: var(--sk-accent); }

@media (max-width: 900px) {
  .admin-body { flex-direction: column; }
  .admin-rail { width: auto; flex: none; }
}

/* ── The library ───────────────────────────────────────────────────── */

.admin-grade { margin-bottom: 2rem; }

.admin-grade__heading {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--sk-ink);
  border-bottom: 1px solid var(--grid);
  padding-bottom: 0.35rem;
  margin: 0 0 0.75rem;
}

.admin-grade__slots {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0.75rem;
}

.admin-slot {
  display: block;
  border: 1px solid var(--grid);
  border-radius: 4px;
  padding: 0.75rem;
  background: #fff;
  color: var(--sk-ink);
  text-decoration: none;
}

a.admin-slot:hover { border-color: var(--sk-accent); }

.admin-slot--empty { background: transparent; border-style: dashed; opacity: 0.6; }

.admin-slot__subject { display: block; font-weight: 600; }

.admin-slot__counts {
  display: block;
  font-size: 0.8rem;
  color: #5b6570;
  margin-bottom: 0.5rem;
}

.admin-slot__warn {
  display: inline-block;
  margin-top: 0.5rem;
  font-size: 0.75rem;
  color: var(--shade);
  font-weight: 600;
}

/* ── Coverage ribbon ───────────────────────────────────────────────── */

.ribbon { display: flex; gap: 1px; height: 8px; margin-bottom: 2px; }

.ribbon__segment {
  display: flex;
  position: relative;
  gap: 1px;
  min-width: 2px;
  background: var(--grid);
}

.ribbon__band { display: block; }

/* Difficulty reads as increasing weight, not as three unrelated hues.
   Level 3 lands on --shade because multi-step items are what an operator
   checks a thin lesson for. */
.ribbon__band--d1 { background: #a9c9dd; }
.ribbon__band--d2 { background: var(--sk-accent); }
.ribbon__band--d3 { background: var(--shade); }

.ribbon__notch {
  position: absolute;
  inset: 0;
  border: 1.5px solid var(--shade);
  border-radius: 1px;
}

/* ── Rail contents ─────────────────────────────────────────────────── */

.rail-pack {
  padding: 0 1rem 0.75rem;
  font-weight: 600;
  color: var(--sk-fill-1);
  border-bottom: 1px solid #22303c;
  margin-bottom: 0.5rem;
}

.rail-lesson {
  display: block;
  padding: 0.4rem 1rem;
  color: var(--grid);
  text-decoration: none;
  border-left: 3px solid transparent;
}

.rail-lesson:hover { background: #16222c; }

.rail-lesson--current { border-left-color: var(--sk-accent); background: #16222c; color: var(--sk-fill-1); }

.rail-lesson__title { display: block; }

.rail-lesson__title-ne { display: block; font-size: 0.85em; color: #8b96a1; }

/* ── Canvas furniture ──────────────────────────────────────────────── */

.admin-meta { color: #5b6570; margin: 0 0 1rem; }

.admin-title-ne { font-size: 0.85em; color: #5b6570; }

.admin-warnings {
  border-left: 3px solid var(--shade);
  background: #fdf6ec;
  padding: 0.75rem 1rem;
  margin-bottom: 1.5rem;
}

.admin-warnings h2 { font-size: 0.85rem; margin: 0 0 0.5rem; text-transform: uppercase; letter-spacing: 0.06em; }

.admin-warnings ul { margin: 0; padding-left: 1.1rem; font-family: var(--sk-font-ui); font-size: 0.8rem; }

.admin-table { width: 100%; border-collapse: collapse; }

.admin-table th {
  text-align: left;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #5b6570;
  border-bottom: 1px solid var(--grid);
  padding: 0.4rem 0.5rem;
}

.admin-table td { border-bottom: 1px solid var(--grid); padding: 0.5rem; vertical-align: top; }

.admin-table a { color: var(--sk-ink); text-decoration: none; }
.admin-table a:hover { color: var(--sk-accent); }

.admin-table__ribbon { width: 40%; }
.admin-table__ribbon .ribbon { height: 12px; margin: 0; }

/* ── Pack switcher ─────────────────────────────────────────────────── */

.pack-switcher select {
  font: inherit;
  background: #16222c;
  color: var(--sk-fill-1);
  border: 1px solid #22303c;
  border-radius: 3px;
  padding: 0.2rem 0.4rem;
}

/* ── Lesson screen ─────────────────────────────────────────────────── */

.admin-block { margin-bottom: 1.75rem; }

.admin-block h2 {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #5b6570;
  border-bottom: 1px solid var(--grid);
  padding-bottom: 0.3rem;
  margin: 0 0 0.6rem;
}

.admin-block ul { margin: 0; padding-left: 1.1rem; }

/* The error half of a misconception is quoted student thinking, not a
   warning — so it is set apart without --shade, which means attention. */
.admin-misconception__error { color: #5b6570; font-style: italic; }

/* ── Topic screen ──────────────────────────────────────────────────── */

.topic-assets { display: flex; flex-wrap: wrap; gap: 1rem; }

.topic-asset { margin: 0; border: 1px solid var(--grid); padding: 0.5rem; background: #fff; }
.topic-asset img { display: block; max-width: 320px; height: auto; }
.topic-asset figcaption { font-size: 0.8rem; color: #5b6570; margin-top: 0.4rem; }

.worked-steps { margin: 0.4rem 0; padding-left: 1.3rem; }
.worked-answer { font-weight: 600; }

.item__difficulty { font-family: var(--sk-font-ui); letter-spacing: 0.1em; white-space: nowrap; }

.item__choices { margin: 0.3rem 0 0; padding-left: 1.1rem; font-size: 0.9em; }
.item__choice--answer { font-weight: 600; color: var(--sk-accent); }

.item__aid { color: #5b6570; font-size: 0.9em; }

.item__figure { max-width: 240px; height: auto; display: block; margin-top: 0.4rem; }

/* A declared figure that is not on disk. --shade earns its use here: this is
   the incompleteness the admin exists to reveal. */
.item--asset-missing { background: #fdf6ec; }
.item__missing { color: var(--shade); font-weight: 600; font-family: var(--sk-font-ui); font-size: 0.8rem; }

/* A retired question stays on the table (see Item.live) rather than
   vanishing, so it fades instead of disappearing -- the row is still there
   to restore. */
.item--retired { opacity: 0.5; }

/* Same treatment, one level up: a withdrawn lesson or topic stays listed
   (see Lesson.live / Topic.live) rather than vanishing, for the same reason
   a retired item does. */
.lesson--retired,
.topic--retired { opacity: 0.5; }

/* A table cell, not .admin-actions' flex row -- .button_to is already
   display: inline-block via the .admin-canvas rule above, so Edit and the
   retire/restore button only need space between them. */
.item__actions { white-space: nowrap; }
.item__actions .button_to { margin-left: 0.5rem; }

/* ── Items screen ──────────────────────────────────────────────────── */

.admin-filters {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.75rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--grid);
}

.admin-filters select {
  font: inherit;
  padding: 0.25rem 0.4rem;
  border: 1px solid var(--grid);
  border-radius: 3px;
  background: #fff;
  color: var(--sk-ink);
}

.admin-filters__clear { color: var(--sk-accent); font-size: 0.85rem; }

/* ── Sidebar sections ──────────────────────────────────────────────── */

/* The rail used to be contextual only -- the pack spine, hidden by
   .admin-rail:empty everywhere else. It now carries persistent nav above that
   spine, so the two need a visible seam or they read as one long list. The
   seam only appears when there is contextual content beneath it. */

.rail-nav:not(:last-child) {
  border-bottom: 1px solid #22303c;
  margin-bottom: 0.75rem;
  padding-bottom: 0.75rem;
}

/* Same idiom as .rail-lesson above: dark ground, --grid text, a left border
   that lights up --accent for the current one. */
.rail-nav__item {
  display: block;
  padding: 0.4rem 1rem;
  color: var(--grid);
  text-decoration: none;
  border-left: 3px solid transparent;
}

.rail-nav__item:hover { background: #16222c; }

.rail-nav__item--current { border-left-color: var(--sk-accent); background: #16222c; color: var(--sk-fill-1); }

/* ── Forms and actions ─────────────────────────────────────────────── */

/* New surface, not a restyle. The admin was read-only, so it had no form,
   input or button rules at all; /teach rendering under this layout is what
   needs them.
   Scoped to .admin-canvas rather than body.admin: the top bar has its own
   treatments (.admin-bar__signout, .pack-switcher select) that a broader
   selector would quietly outrank.

   Every rule below also matches body.school main X, for the same reason and
   under the same care: layouts/school.html.erb's <main> is Admin::/Teach::'s
   .admin-canvas now (see the comment above body.school main further up this
   file), and body.school alone would reach past <main> into the header and
   nav, outranking .sk-hdr__signout and the tab links the same way a bare
   body.admin selector would have outranked the top bar here. Transitional:
   each screen drops its half of this pairing as it moves off admin-table/
   admin-form markup onto sk-*, rather than this list growing forever.

   Every body.school selector below excludes sk-*-classed controls by name.
   A screen does not move off this block all at once: the classes rebuild
   put an sk-* *form* under body.school main while five screens beside it
   still needed these rules, and an element selector qualified by body.school
   main outranks a single class (body.school main input[type="text"] is 0,2,3
   against .sk-input's 0,1,0), so that form's fields silently took legacy
   padding, a 3px radius and a fixed width: 20rem inside a 780px column, and
   its Create button a 3px radius beside a Cancel link correctly drawn as a
   12px .sk-btn--quiet. The :not() is what lets a control opt out one control
   at a time, which is the granularity the rebuild actually proceeds at. The
   un-rebuilt screens are untouched: none of them puts an sk-* class on a
   control.

   The third instance is the class leaderboard switch (see
   :not(.sk-switch__track) on the .button_to rule near the end of this
   block). A switch is a button_to whose whole job is to look like neither a
   button nor a link: an .sk-switch__track is 0,1,0 against that selector's
   0,4,3, so it took a white background, a 1px --grid border and a 3px
   radius -- which meant .sk-switch__track--on's accent background lost, the
   two states rendered identically, the white .sk-switch__knob vanished into
   a white track, and the hover rule below turned the whole control red as
   though flipping a leaderboard destroyed something. /sa renders the same
   markup under body.sa, which this block never reaches, so the class detail
   screen is the first place the collision could happen. */

.admin-button,
.admin-canvas input[type="submit"],
body.school main input[type="submit"]:not(.sk-btn) {
  display: inline-block;
  font: inherit;
  background: var(--sk-accent);
  color: var(--sk-fill-1);
  border: 1px solid var(--sk-accent);
  border-radius: 3px;
  padding: 0.35rem 0.8rem;
  margin: 0 0.5rem 1rem 0;
  text-decoration: none;
  cursor: pointer;
}

/* Darkened by filter rather than a second hex, so the hover cannot drift out
   of step if --accent is ever retuned. */
.admin-button:hover,
.admin-canvas input[type="submit"]:hover,
body.school main input[type="submit"]:not(.sk-btn):hover { filter: brightness(0.9); }

.admin-canvas .field,
body.school main .field { margin-bottom: 1rem; }

/* Labels read as column headings do -- small, uppercase, muted -- so a form
   and a table look like the same instrument. */
.admin-canvas .field label,
body.school main .field label {
  display: block;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #5b6570;
  margin-bottom: 0.25rem;
}

/* Values copied from .admin-filters select above, which already settled what
   a control looks like on this surface. */
.admin-canvas input[type="text"],
.admin-canvas input[type="email"],
.admin-canvas input[type="password"],
.admin-canvas select,
.admin-canvas textarea,
body.school main input[type="text"]:not(.sk-input),
body.school main input[type="email"]:not(.sk-input),
body.school main input[type="password"]:not(.sk-input),
body.school main select:not(.sk-input),
body.school main textarea:not(.sk-input) {
  font: inherit;
  padding: 0.25rem 0.4rem;
  border: 1px solid var(--grid);
  border-radius: 3px;
  background: #fff;
  color: var(--sk-ink);
}

.admin-canvas input[type="text"],
.admin-canvas input[type="email"],
.admin-canvas input[type="password"],
.admin-canvas textarea,
body.school main input[type="text"]:not(.sk-input),
body.school main input[type="email"]:not(.sk-input),
body.school main input[type="password"]:not(.sk-input),
body.school main textarea:not(.sk-input) { width: 20rem; max-width: 100%; }

.admin-canvas input:focus,
.admin-canvas select:focus,
.admin-canvas textarea:focus,
body.school main input:not(.sk-input):focus,
body.school main select:not(.sk-input):focus,
body.school main textarea:not(.sk-input):focus { border-color: var(--sk-accent); }

.admin-canvas .field--checkbox,
body.school main .field--checkbox { display: grid; grid-template-columns: auto 1fr; gap: 8px; align-items: center; }
.admin-canvas .field--checkbox .field__hint,
body.school main .field--checkbox .field__hint { grid-column: 1 / -1; margin: 0; font-size: 0.9em; opacity: 0.75; }

.admin-canvas .hint,
body.school main .hint { font-size: 0.8rem; color: #5b6570; margin: 0.25rem 0 0; }

.admin-canvas .form-errors,
body.school main .form-errors {
  border-left: 3px solid var(--sk-error);
  background: var(--sk-error-tint);
  color: var(--sk-error);
  margin: 0 0 1.25rem;
  padding: 0.75rem 1rem 0.75rem 2.2rem;
}

/* Rails renders button_to as a <form>, which is block -- so Remove would drop
   to its own line and pull every table row taller. */
.admin-canvas .button_to,
body.school main .button_to { display: inline-block; }

/* Deliberately not .admin-button: Remove destroys a class and every child in
   it, so it stays quiet until you are actually on it, then reads red.
   :not(.admin-button) because this selector outranks .admin-button on
   specificity -- without the exclusion it captured every button_to on the
   staff surface, which made the review queue's Publish button look exactly
   like a destructive one and left the two verbs indistinguishable. Scoped to
   main rather than body.school for the same reason as the rest of this
   block: body.school would also reach the header's Sign out button_to, which
   already has its own .sk-hdr__signout treatment. */
.admin-canvas .button_to button:not(.admin-button),
body.school main .button_to button:not(.admin-button):not(.sk-btn):not(.sk-switch__track) {
  font: inherit;
  background: #fff;
  color: var(--sk-ink);
  border: 1px solid var(--grid);
  border-radius: 3px;
  padding: 0.2rem 0.6rem;
  cursor: pointer;
}

.admin-canvas .button_to button:not(.admin-button):hover,
body.school main .button_to button:not(.admin-button):not(.sk-btn):not(.sk-switch__track):hover { border-color: var(--sk-error); color: var(--sk-error); }

/* ── Item edit screen ──────────────────────────────────────────────── */

/* _form.html.erb renders a flat run of labels and controls with no .field
   wrapper around each pair, so the rhythm has to come from the label rather
   than from a container the way .admin-canvas .field does above. Capped
   width so a textarea does not stretch edge-to-edge on a phone -- the
   generic input/select/textarea rules above already set border, padding and
   focus colour; this only adds the vertical spacing this form is missing. */
.admin-form label {
  display: block;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #5b6570;
  margin: 1rem 0 0.25rem;
}

.admin-form label:first-child { margin-top: 0; }

.admin-form textarea { display: block; width: 100%; max-width: 34rem; }

/* Same border-left/tint idiom as .admin-canvas .form-errors above, under its
   own class because the item form is BEM-named (.admin-form__errors) rather
   than the bare .form-errors the rest of the staff surface uses. */
.admin-form__errors {
  border-left: 3px solid var(--sk-error);
  background: var(--sk-error-tint);
  color: var(--sk-error);
  margin: 0 0 1.25rem;
  padding: 0.75rem 1rem 0.75rem 2.2rem;
}

/* The pack-disagreement banner: --shade for attention, same idiom as
   .admin-warnings, because "the pack changed under a field you locked" is
   the same class of thing an operator should notice before trusting the
   rest of the form. */
.admin-notice {
  border-left: 3px solid var(--shade);
  background: #fdf6ec;
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
}

.admin-notice p { margin: 0 0 0.5rem; }

.admin-notice__action { margin: 0 0.5rem 0 0; }

.admin-notice__keep { font-size: 0.85rem; color: #5b6570; }

/* The revision list below the form: quietest at the top of the visual
   hierarchy, since the form above it is what an editor came to act on. */
.revisions { list-style: none; margin: 0; padding: 0; }

.revisions li {
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--grid);
  font-size: 0.9em;
}

.revisions li:last-child { border-bottom: 0; }

.revision__when { display: inline-block; min-width: 8rem; color: #5b6570; font-size: 0.85em; }
.revision__who { margin-right: 0.5rem; }
.revision__what { font-weight: 600; }

/* One per changed field, so a multi-field save reads as a list rather than
   one run-on line. */
.revision__field {
  display: block;
  margin: 0.2rem 0 0 8rem;
  font-family: var(--sk-font-ui);
  font-size: 0.85em;
  color: #5b6570;
}

@media (max-width: 480px) {
  /* The 8rem hanging indent above assumes room beside .revision__when; on a
     narrow phone it would leave almost nothing for the field itself, so the
     field drops to its own full-width line instead. */
  .revision__field { margin-left: 0; }
}

/* ── Student report ────────────────────────────────────────────────── */

/* One child, read by a teacher: who they are, how they are doing, and what
   they actually typed. Every rule below serves one of two distinctions the
   page is not allowed to blur — an unscored pool is not a zero, and a
   self-check is not a grade — so neither of them is carried by colour alone.
   No new hex: the whole section is built from the properties above. */

.student-facts,
.student-tally { display: flex; flex-wrap: wrap; gap: 2rem; margin: 0 0 1.5rem; }

.student-facts dt,
.student-tally dt {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #5b6570;
}

.student-facts dd, .student-tally dd { margin: 0.15rem 0 0; }

.student-tally {
  border-top: 1px solid var(--grid);
  border-bottom: 1px solid var(--grid);
  padding: 0.75rem 0;
}

.student-tally dd { font-size: 1.4rem; font-weight: 600; line-height: 1.1; }

/* The share of the figures beside it that nothing checked (D-02). Set apart
   with --shade because "some of this is the child's own word" is exactly the
   kind of thing a teacher should notice before trusting the rest. */
.student-tally__figure--self dt { color: var(--shade); }
.student-tally__figure--self dd { color: var(--shade); }

.student-empty { font-style: italic; }

.student-badges { list-style: none; padding: 0; display: grid; gap: 4px; }

/* ── Mastery ───────────────────────────────────────────────────────── */

.admin-table__mastery { width: 9rem; }

.mastery { display: flex; align-items: center; gap: 0.5rem; white-space: nowrap; }

.mastery__bar {
  display: block;
  width: 4rem;
  height: 6px;
  background: var(--grid);
  border-radius: 3px;
  overflow: hidden;
}

.mastery__fill { display: block; height: 100%; background: var(--sk-accent); }

.mastery__figure { font-family: var(--sk-font-ui); font-size: 0.85em; }

/* A pool with no counted items has no mastery, which is not the same thing as
   scoring zero — so it gets words and no bar at all. A bar drawn at 0% width
   would read as a failing child; the empty track alone would read as one too. */
.mastery--unscored { color: #5b6570; font-style: italic; font-size: 0.85em; }

/* ── Outcome pills ─────────────────────────────────────────────────── */

.outcome {
  display: inline-block;
  border: 1px solid var(--grid);
  border-radius: 10px;
  padding: 0.1rem 0.55rem;
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}

/* Graded: solid border, the app's own two verdicts. Red is allowed here —
   this file dresses staff screens only, and the child never sees it. */
.outcome--graded-yes { border-color: var(--sk-accent); color: var(--sk-accent); }
.outcome--graded-no { border-color: var(--sk-error); background: var(--sk-error-tint); color: var(--sk-error); }

/* Self-marked: a dashed border, so the difference from a graded verdict
   survives a greyscale print and a colour-blind reader — the label already
   says who judged, and this makes it visible before the label is read. */
.outcome--self {
  border-style: dashed;
  border-color: var(--shade);
  background: #fdf6ec;
  color: #8a5a13;
}

/* Recorded and never judged (a `task` item): deliberately the quietest. */
.outcome--none { color: #5b6570; }

/* ── Answers ───────────────────────────────────────────────────────── */

.answers__when { white-space: nowrap; font-size: 0.85em; color: #5b6570; }

/* "Correct on the third try" is what a teacher is scanning for, so the retry
   is marked and a first try is left unmarked. */
.answers__try {
  display: inline-block;
  margin-left: 0.35rem;
  border: 1px solid var(--shade);
  border-radius: 2px;
  padding: 0 0.25rem;
  font-family: var(--sk-font-ui);
  font-size: 0.9em;
  color: var(--shade);
}

.answers__prompt { max-width: 28rem; }

.answers__where { display: block; font-size: 0.8em; color: #5b6570; margin-top: 0.2rem; }

/* The two answers sit side by side and must not be confusable at a glance:
   what the child wrote is the loud one, in ink; what was expected is quiet
   and dashed off to one side. */
.answers__given { font-family: var(--sk-font-ui); font-weight: 600; max-width: 16rem; word-break: break-word; }

.answers__expected {
  font-family: var(--sk-font-ui);
  font-size: 0.85em;
  color: #5b6570;
  border-left: 2px solid var(--grid);
  padding-left: 0.5rem;
  max-width: 16rem;
  word-break: break-word;
}

/* A correct answer needs no expected answer beside it, so the cell is empty —
   and the rule above would otherwise draw a border down a column of nothing,
   which reads as a missing value rather than as one that was never needed. */
.answers__expected:empty { border-left: 0; }

/* ── Deep lesson review ────────────────────────────────────────────── */

/* The D-13 queue. Status is carried by the word first -- these rules only
   reinforce it -- so the screen still reads correctly in greyscale. */
.admin-status {
  display: inline-block;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--grid);
  border-radius: 3px;
  color: #5b6570;
}

.admin-status--ready { border-color: var(--sk-accent); color: var(--sk-accent); }
.admin-status--published { border-color: #2e7d4f; color: #2e7d4f; }
.admin-status--refused,
.admin-status--failed { border-color: var(--shade); color: #96601a; }

/* button_to renders a <form>, so the row is laid out on the forms rather
   than on the buttons inside them. */
.admin-actions { display: flex; gap: 0.75rem; align-items: center; }
.admin-actions .button_to { margin: 0; }

/* The destructive verb must not look like the primary one. Outline rather
   than filled, so publishing stays the obvious action on the screen. */
.admin-button--quiet {
  background: transparent;
  color: var(--sk-ink);
  border-color: var(--grid);
}

.admin-button--quiet:hover { filter: none; border-color: var(--sk-ink); }

.admin-review-example { border-left: 3px solid var(--grid); padding-left: 1rem; margin-bottom: 1.25rem; }

.admin-review-example svg,
.admin-table svg { max-width: 100%; height: auto; }

.admin-link { color: var(--sk-accent); }

/* ---- Sign-in history --------------------------------------------------
   A failed attempt is tinted rather than shouted at: this table is read by
   scanning, and most failures are a child mistyping their own PIN, not an
   attack. The bursts panel above the table is what escalates. */
.admin-note {
  margin: 0 0 12px;
  max-width: 70ch;
  color: #4b5563;
  line-height: 1.5;
}

.admin-muted { color: #6b7280; }

.login-event--failed { background: rgb(217 164 65 / 12%); }

.admin-table td,
.admin-table th { vertical-align: top; }

/* Tabular figures, which the mono used to supply by construction -- see the
   longer note in parents.css. These are dense tables; a proportional "1"
   under a "6" is what makes an ID column look crooked. */
.id,
.item__difficulty,
.mastery__figure,
.answers__given {
  font-variant-numeric: tabular-nums;
}
