@import url('https://fonts.googleapis.com/css2?family=Fredoka:wght@500;600&family=Nunito:wght@400;600;700;800&display=swap');

/* ==================================================================
   Kwaderno (built on the Classroom Management System) — green / mustard / cream
   Flat, page-based layout: no card boxes, no nested panels.

   Three families, each with one job:
     --brand-*   deep olive green — structure: bars, headers, primary actions
     --accent-*  terracotta       — emphasis: highlights, warnings, destructive
     --sand-*    warm cream       — surfaces, borders, soft fills
   Terracotta is used sparingly on purpose; that restraint is what keeps the
   scheme calm even though the colour itself is saturated.
   ================================================================== */

:root {
  --brand-900: #012E25;
  --brand-800: #013F33;
  --brand-700: #015443;   /* Kwaderno green */
  --brand-600: #0F6A56;
  --brand-100: #CFE3DA;
  --brand-050: #E2EEE8;

  --accent-700: #6E2A1A;
  --accent-600: #7E3322;
  --accent-500: #8A3A26;  /* Kwaderno red-brown: warnings and Delete only */
  --accent-400: #B05A43;
  --accent-100: #F2D9D1;
  --accent-050: #FBEFEA;

  --sand-300: #D8CBB8;
  --sand-200: #E6DACA;
  --sand-100: #F1E7D8;
  --sand-050: #F8F1E6;

  /* Inverted: the page is the light cream and the things sitting on it —
     the breadcrumb bar, cards, attendance rows — are the warmer sand.
     `--field` keeps inputs and small controls light so they still read as
     wells against those sand panels.
     `--white` is true white, used only for text on olive or terracotta. */
  --canvas: #FEF6F0;
  --surface: #F3EADD;
  --surface-alt: #EADFCF;
  --field: #FFFDFA;
  /* Attendance rows deliberately share the breadcrumb bar's colour — bound to
     the same value rather than copied, so the two can never drift apart. */
  --att-card: var(--surface);
  --white: #FFFFFF;

  --ink: #1E2B26;
  --ink-soft: #56645E;   /* clears 4.5:1 on cream and sand */
  --rule: #E6DACA;

  --radius: 6px;
}

* { box-sizing: border-box; }

html, body { height: 100%; margin: 0; }

body {
  background: var(--canvas);
  color: var(--ink);
  font: 15px/1.5 Nunito, "Segoe UI", system-ui, -apple-system, Roboto, Arial, sans-serif;
  display: flex;
  flex-direction: column;
}

h1, h2 { margin: 0; font-weight: 700; letter-spacing: -.015em; }

a { color: inherit; text-decoration: none; }

/* Present to assistive tech, invisible on screen. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------- Top bar ---------------- */

/* Circuit-trace texture over the olive gradient — inline SVG so it stays
   self-contained and offline. Kept near-transparent so it reads as texture
   rather than decoration competing with the wordmark. */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.15rem 2rem;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='60' viewBox='0 0 120 60'%3E%3Cg fill='none' stroke='%23ffffff' stroke-opacity='.10' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M0 18h30l10-10h32'/%3E%3Cpath d='M72 8v26h20l10 10h18'/%3E%3Cpath d='M0 46h22l8-8'/%3E%3Cpath d='M46 60v-14l8-8h18'/%3E%3Cpath d='M96 0v10'/%3E%3Cpath d='M108 22h12'/%3E%3C/g%3E%3Cg fill='%23ffffff' fill-opacity='.14'%3E%3Ccircle cx='30' cy='18' r='2'/%3E%3Ccircle cx='72' cy='34' r='2'/%3E%3Ccircle cx='22' cy='46' r='2'/%3E%3Ccircle cx='96' cy='10' r='2'/%3E%3Ccircle cx='108' cy='22' r='2'/%3E%3C/g%3E%3Cg fill='none' stroke='%23ffffff' stroke-opacity='.12'%3E%3Crect x='52' y='11' width='8' height='8' rx='1.5'/%3E%3Crect x='100' y='40' width='8' height='8' rx='1.5'/%3E%3C/g%3E%3C/svg%3E"),
    linear-gradient(100deg, var(--brand-900) 0%, var(--brand-700) 60%, var(--brand-600) 100%);
  background-repeat: repeat, no-repeat;
  border-bottom: 3px solid var(--accent-500);
  color: var(--white);
  flex: 0 0 auto;
}

/* Icon-only, pinned to the far right of the bar. */
.topbar-nav {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  border-radius: var(--radius);
  border: 1px solid rgba(255, 255, 255, .28);
  color: var(--white);
  transition: background .12s, border-color .12s;
}

.topbar-nav:hover { background: rgba(255, 255, 255, .13); border-color: var(--accent-400); }
.topbar-nav.is-current { background: var(--accent-500); border-color: var(--accent-600); }
.topbar-nav:focus-visible { outline: 3px solid var(--accent-400); outline-offset: 2px; }

.brand { display: flex; align-items: center; gap: .8rem; margin-right: auto; }

/* The seal carries its own ring and edge, so it needs no frame around it. */
.brand-mark {
  width: 54px;
  height: 54px;
  flex: 0 0 auto;
  object-fit: contain;
}

.brand-name { display: block; font-size: 1.12rem; font-weight: 700; }

.brand-sub {
  display: block;
  font-size: .7rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--sand-200);
}

/* ---------------- Breadcrumbs ---------------- */

/* The address bar takes every pixel the two button clusters don't, the way a
   browser's does. The clusters are near enough the same width that this leaves
   it visually centred as well as full-width. */
.crumbs {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 1.15rem;
  /* Deliberately tighter than the page's 2rem gutter: the outer buttons hug
     the window edge the way a browser toolbar's do, rather than lining up
     with the content below. */
  padding: .5rem .75rem;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
}

/* Home / back / forward sit together as one cluster, browser-fashion. */
.navbtns {
  display: flex;
  align-items: center;
  gap: .1rem;
  justify-self: start;
}

/* Tools live at the far right, mirroring the nav cluster. */
.crumb-actions {
  display: flex;
  align-items: center;
  gap: .25rem;
  justify-self: end;
}

/* Sized to the address bar's own height so the whole row reads as one band. */
.navbtn {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--brand-700);
  cursor: pointer;
  transition: background .12s;
}

.navbtn:hover:not(:disabled) { background: var(--brand-100); }
.navbtn:active:not(:disabled) { background: var(--brand-050); }
/* Greyed when there is nowhere to go, the way a browser greys its arrows. */
.navbtn:disabled { opacity: .3; cursor: default; }
.navbtn:focus-visible { outline: 3px solid var(--accent-400); outline-offset: 1px; }
.navbtn.is-current { background: var(--accent-500); color: var(--white); }

/* The supplied artwork is black PNG, so it is used as a mask rather than an
   image: the colour comes from currentColor, which means hover, focus and
   is-current all recolour the icon for free and nothing is hard-coded. */
.navbtn-img {
  width: 19px;
  height: 19px;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
}

.i-home       { -webkit-mask-image: url("assets/home.png");       mask-image: url("assets/home.png"); }
.i-database   { -webkit-mask-image: url("assets/database.png");   mask-image: url("assets/database.png"); }
.i-attendance { -webkit-mask-image: url("assets/attendance.png"); mask-image: url("assets/attendance.png"); }
.i-noclass    { -webkit-mask-image: url("assets/no-class.png");   mask-image: url("assets/no-class.png"); }
.i-list       { -webkit-mask-image: url("assets/list.png");       mask-image: url("assets/list.png"); }
.i-calendar   { -webkit-mask-image: url("assets/calendar.png");   mask-image: url("assets/calendar.png"); }
.i-table      { -webkit-mask-image: url("assets/table.png");      mask-image: url("assets/table.png"); }
.i-classes    { -webkit-mask-image: url("assets/Classes.png");    mask-image: url("assets/Classes.png"); }

/* ---------------- Current Classes (a day's timetable) ---------------- */

/* --- Day / full-schedule switch. A control, so it may carry a fill. --- */

.viewswitch { display: flex; gap: 0; }

.viewswitch-btn {
  padding: .3rem .75rem .34rem;
  border: 1px solid var(--brand-600);
  background: none;
  font: inherit;
  font-size: .78rem;
  font-weight: 700;
  color: var(--brand-700);
  cursor: pointer;
  transition: background .12s, color .12s;
}

/* Jump to one date, sitting inline in the toolbar beside the record count it
   narrows rather than claiming a band of its own. */
.datefilter {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
}

.datefilter input[type="date"] {
  width: auto;
  min-width: 9.5rem;
  min-height: 0;
  padding: .28rem .45rem;
  font-size: .8rem;
}

/* --- Import attendance: the picked file's details sit below a rule, so the
       dialog reads as "choose a file" then "here is what it holds". --- */

#importAttResult {
  margin-top: 1rem;
  padding-top: .95rem;
  border-top: 1px solid var(--rule);
}

.import-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .25rem 1rem;
}

.import-head-class { font-size: .95rem; font-weight: 800; color: var(--brand-800); }
.import-head-meta { font-size: .8rem; color: var(--ink-soft); }

.import-mode {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .7rem;
  margin-top: .95rem;
}

#importAttRows { margin-top: .9rem; }

/* One button, a short chooser under it. */
.datamenu-wrap { position: relative; display: flex; }

.datamenu {
  position: absolute;
  top: calc(100% + .35rem);
  right: 0;
  z-index: 40;
  min-width: 12rem;
  display: flex;
  flex-direction: column;
  gap: .1rem;
  padding: .25rem;
  background: var(--canvas);
  border: 1px solid var(--sand-300);
  border-radius: var(--radius);
  box-shadow: 0 10px 26px rgba(1, 64, 52, .16);
}

.datamenu-item {
  padding: .45rem .6rem;
  border: 0;
  border-radius: var(--radius);
  background: none;
  font: inherit;
  font-size: .82rem;
  font-weight: 600;
  color: var(--brand-700);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  transition: background .12s;
}

.datamenu-item:hover { background: var(--brand-050); }

/* The class was on the markup all along with no rule behind it, so the most
   destructive item in the app rendered exactly like "Back up…". */
.datamenu-rule { margin: .35rem .2rem; border: 0; border-top: 1px solid var(--rule); }
.datamenu-item.is-danger { color: var(--accent-700); }
.datamenu-item.is-danger:hover { background: var(--accent-050); color: var(--accent-800); }
.datamenu-item:focus-visible { outline: 3px solid var(--accent-400); outline-offset: -2px; }

/* Matched to the view switch beside it, so the pair reads as one row of
   controls rather than a button parked next to some icons. */
#exportBtn {
  height: 30px;
  padding: 0 .7rem;
  font-size: .78rem;
  border-radius: var(--radius);
}

/* Icon-only variant: the mask takes currentColor, so the active state
   recolours the artwork along with the button. */
.viewswitch-btn.is-icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 30px;
  padding: 0;
}

.viewswitch-btn:first-child { border-radius: var(--radius) 0 0 var(--radius); }
.viewswitch-btn:last-child  { border-radius: 0 var(--radius) var(--radius) 0; border-left: 0; }
.viewswitch-btn:hover:not(.is-on) { background: var(--brand-050); }
.viewswitch-btn.is-on { background: var(--brand-700); color: var(--white); }
.viewswitch-btn:focus-visible { outline: 3px solid var(--accent-400); outline-offset: 1px; }

.toolbar-spacer { flex: 1 1 auto; }

/* --- Term chips: prelim / midterm / finals, six weeks each. --- */

/* Its own line above the register, so it reads as the register's control
   rather than another thing wedged beside the view switch. */
.termrow {
  display: flex;
  align-items: center;
  gap: .7rem;
  margin-top: 1.1rem;
  padding-bottom: .6rem;
  border-bottom: 1px solid var(--rule);
}

/* Classworks puts outlined cards or a bordered grid straight underneath, so
   the rule would be a second edge saying the same thing. The register keeps
   its rule, because a bare table needs the anchor. */
#panelClassworks .termrow { border-bottom: 0; }

/* In table view the view switch moves into this row. It takes no margin of its
   own: the search already carries margin-left:auto, and a second auto would
   split the free space between them and leave the search stranded mid-row. */
#panelClassworks .termrow .viewswitch { flex: 0 0 auto; }
/* With no search to push it (a term with no register), the switch still goes
   to the end. */
#panelClassworks .termrow #cwSearch[hidden] ~ .viewswitch { margin-left: auto; }

/* Its own line, ending where the view switch above it ends. */
.cw-export-row { display: flex; justify-content: flex-end; gap: .5rem; margin-top: .55rem; }

/* 30px, matching the view switch and the search field beside it, so the row
   reads level rather than nearly level. */
.btn.is-tall { height: 30px; padding-top: 0; padding-bottom: 0; font-size: .76rem; }

.termrow-label {
  font-size: .66rem;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.termfilter { display: flex; gap: .25rem; }

.termrow-hint { font-size: .78rem; font-style: italic; color: var(--ink-soft); }

/* Sits at the far end of the term row, matching the roster's search field. */
.register-search {
  margin-left: auto;
  width: auto;
  min-width: 15rem;
  max-width: 22rem;
  min-height: 0;
  padding: .32rem .55rem;
  font-size: .82rem;
}

.termchip {
  padding: .28rem .6rem .32rem;
  border: 1px solid var(--brand-600);
  border-radius: var(--radius);
  background: none;
  font: inherit;
  font-size: .74rem;
  font-weight: 700;
  color: var(--brand-700);
  white-space: nowrap;
  cursor: pointer;
  transition: background .12s, color .12s;
}

/* Sits beside the chips but is an action, not a fourth term: same metrics so
   the row reads level, with a gap so the group of three stays a group. */
#gsOpenBtn {
  margin-left: .55rem;
  padding: .28rem .7rem .32rem;
  font-size: .74rem;
  /* The chips take the page's line height; .btn pins its own, which left this
     one a couple of pixels short of them. */
  line-height: inherit;
}

/* It toggles, so it has to show which way it is set — the same filled state
   the term chips and the view switch use. */
#gsOpenBtn.is-on {
  background: var(--brand-700);
  border-color: var(--brand-800);
  color: var(--white);
}

.termchip:hover:not(.is-on):not(:disabled) { background: var(--brand-050); }
.termchip.is-on { background: var(--brand-700); color: var(--white); }
.termchip:disabled { opacity: .35; cursor: default; }
.termchip:focus-visible { outline: 3px solid var(--accent-400); outline-offset: 1px; }

/* --- Calendar view --- */

.cal-month { margin-top: 1.3rem; }

/* Month name between its two arrows, centred over the grid. */
.cal-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .9rem;
  margin-bottom: .7rem;
}

.cal-title { margin: 0; font-size: .95rem; font-weight: 800; color: var(--brand-800); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: .3rem; }

.cal-head {
  padding-bottom: .2rem;
  font-size: .66rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-align: center;
}

.cal-cell {
  min-height: 3.5rem;
  padding: .3rem .35rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: none;
  font: inherit;
  text-align: left;
  color: var(--ink-soft);
}

.cal-cell.is-blank { border-color: transparent; }
.cal-daynum { display: block; font-size: .8rem; font-weight: 700; }
.cal-tally { display: block; margin-top: .15rem; font-size: .65rem; font-weight: 700; }

/* A day the class doesn't meet stays quiet. */
.cal-cell.is-off { opacity: .4; }

/* A day it meets but has no record: outlined, still clearly a school day. */
.cal-cell.is-meets { border-color: var(--brand-600); color: var(--brand-700); }

/* The same day, but already past: a gap in the record rather than a plan. */
.cal-cell.is-missed {
  border-color: var(--accent-600);
  border-style: dashed;
  color: var(--accent-700);
  background: var(--accent-050);
}

/* A day with a real roll call: filled, so it carries at a glance. */
.cal-cell.is-taken {
  background: var(--brand-700);
  border-color: var(--brand-800);
  color: var(--white);
  cursor: pointer;
}
.cal-cell.is-taken .cal-daynum { color: var(--white); }
.cal-cell.is-taken:hover { background: var(--brand-600); }

.cal-cell.is-noclass {
  background: var(--accent-500);
  border-color: var(--accent-600);
  color: var(--white);
  cursor: pointer;
}
.cal-cell.is-noclass .cal-daynum { color: var(--white); }
.cal-cell.is-noclass:hover { background: var(--accent-600); }
.cal-cell:focus-visible { outline: 3px solid var(--accent-400); outline-offset: 1px; }

.cal-legend {
  display: flex; flex-wrap: wrap; gap: .9rem;
  margin-top: 1.1rem; font-size: .73rem; color: var(--ink-soft);
}
.cal-key { display: inline-flex; align-items: center; gap: .35rem; }
.cal-swatch {
  display: inline-block; width: 12px; height: 12px;
  border-radius: 3px; border: 1px solid var(--rule);
}
.cal-swatch.is-taken   { background: var(--brand-700); border-color: var(--brand-800); }
.cal-swatch.is-missed  { background: var(--accent-050); border-color: var(--accent-600); border-style: dashed; }
.cal-swatch.is-noclass { background: var(--accent-500); border-color: var(--accent-600); }
.cal-swatch.is-meets   { border-color: var(--brand-600); }
.cal-swatch.is-off     { opacity: .4; }

/* --- Register view: students down, dates across. --- */

/* Layout only: the register sits at the page colour and is held together by
   its rules, not by filled rows. Doubled class so these beat the base .grid
   fills, which are declared later in the file. */
.grid.register-grid { background: none; }
.grid.register-grid tbody tr { background: none; }
.grid.register-grid tbody tr:hover { background: var(--brand-050); }
.grid.register-grid tbody td { border-bottom: 1px solid var(--sand-300); }

/* Fixed layout: the name and total columns hold their stated widths and the
   dates share whatever is left, so the totals always finish at the right edge
   and the date columns are what widen or narrow as a term fills up. */
.grid.register-grid { width: 100%; table-layout: fixed; }

/* The olive band stays; only the body rows drop their fill. */
.grid.register-grid thead th {
  vertical-align: bottom;
  padding: .45rem .3rem .5rem;
}

/* Wide enough for the longest surname, and no wider — so the centred label
   sits over the names instead of over the dates as well. min-width matters as
   much as width: once a term fills with dates the browser will otherwise
   squeeze this column and the names start colliding with the marks. */
.grid.register-grid th.reg-name-head,
.grid.register-grid td.primary-cell {
  width: 17rem;
  min-width: 17rem;
}

.grid.register-grid th.reg-name-head {
  vertical-align: middle;
  text-align: center;
  padding-left: .75rem;
  padding-right: .75rem;
}

/* Names read as a list, not as headings — the marks are what carry weight. */
.grid.register-grid td.primary-cell {
  font-weight: 500;
  color: var(--ink);
}

/* The name column stays put while the dates scroll under it. It needs its own
   background because the body rows are deliberately unfilled — without one the
   scrolling marks would show straight through the names. */
.grid.register-grid th.reg-name-head,
.grid.register-grid td.primary-cell {
  position: sticky;
  left: 0;
  z-index: 2;
  border-right: 1px solid var(--sand-300);
}

.grid.register-grid td.primary-cell { background: var(--canvas); z-index: 1; }
.grid.register-grid tbody tr:hover td.primary-cell { background: var(--brand-050); }

/* Body type is smaller than the rest of the app's tables so a long term fits.
   Scoped to tbody on purpose: the headers keep their own size. */
.grid.register-grid tbody td { font-size: .76rem; }
.grid.register-grid td.primary-cell { font-size: .78rem; }
.grid.register-grid td.mk { font-size: .74rem; }
.grid.register-grid td.reg-total { font-size: .74rem; }

/* Upright headers keep the date and total columns narrow. */
.upright {
  display: inline-block;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
  font-size: .68rem;
  letter-spacing: .02em;
}

/* Named reg-total, not tally: ".tally" is the pill used by the attendance day
   rows, and reusing it here dressed every total column as a lozenge. */
/* Dates carry no width, so fixed layout hands them the leftover space. */
.register-grid th.mk-head { text-align: center; }
.register-grid th.reg-total { width: 2.375rem; text-align: center; }
.register-grid td.mk { text-align: center; font-weight: 800; font-size: .8rem; padding: .4rem .25rem; }
.register-grid td.reg-total {
  text-align: center;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: .4rem .25rem;
}
.register-grid .sep { border-left: 2px solid var(--sand-300); }

/* One colour per status, so a row reads without checking the letter. */
.register-grid .mk-P { color: var(--brand-600); }
.register-grid .mk-L { color: var(--accent-500); }
.register-grid .mk-E { color: var(--brand-800); }
.register-grid .mk-A { color: var(--accent-700); }
.register-grid .mk-x { color: var(--sand-300); }

/* A day the class did not meet. Tinted down its whole length so the column
   reads as one cancelled date, rather than as a column of blanks that happen
   to look like dates nobody has marked. Terracotta is what "no class" wears
   everywhere else in the app. */
.register-grid .mk-nc {
  color: var(--accent-700);
  background: var(--accent-050);
  font-size: .68rem;
  letter-spacing: .02em;
}

.register-grid th.mk-head.is-noclass {
  background: var(--accent-600);
  border-bottom-color: var(--accent-700);
}

/* Tap it and it says why. Underlined rather than made to look like a button,
   because it still has to read as a register cell. */
.register-grid .is-tappable { cursor: pointer; }
.register-grid td.is-tappable:hover { background: var(--accent-100); }
.register-grid td.is-tappable > * , .register-grid td.is-tappable { text-decoration-line: underline; text-decoration-style: dotted; text-underline-offset: 2px; }
.register-grid th.is-tappable:hover { background: var(--accent-700); }
.register-grid .is-tappable:focus-visible { outline: 3px solid var(--accent-400); outline-offset: -3px; }

/* --- The week as a grid: classes down, days across. --- */

.table-scroll { overflow-x: auto; }

/* The register scrolls down inside itself once it is taller than the cap set
   in capRegisterHeight, instead of running off the bottom of the page. */
.table-scroll.register-scroll { overflow-y: auto; }

/* The dates have to stay put while the names scroll under them, or a mark
   halfway down the class belongs to no day in particular. The name column is
   already stuck to the left; where the two meet, the corner has to sit above
   both or the dates slide across underneath it. */
.register-scroll .register-grid thead th {
  position: sticky;
  top: 0;
  z-index: 3;
}

.register-scroll .grid.register-grid th.reg-name-head { z-index: 4; }

/* Fixed layout so the eight columns divide the width between them instead of
   growing past it — the whole week has to fit without a sideways scroll. */
.schedule-grid { table-layout: fixed; width: 100%; font-size: .8rem; }
.schedule-grid th, .schedule-grid td { padding: .45rem .4rem; }
.schedule-grid td { vertical-align: top; }
.schedule-grid .sched-cell { min-width: 0; }
.schedule-grid th:first-child, .schedule-grid td:first-child { width: 6.5rem; }
.schedule-grid .sched-stack { display: flex; flex-direction: column; gap: .25rem; }
.schedule-grid .sched-sub { font-size: .76rem; font-weight: 400; color: var(--ink-soft); }

/* Today's column is the one you are most likely looking for. */
.schedule-grid th.is-today { background: var(--accent-600); }
.schedule-grid td.is-today { background: var(--brand-050); }
.schedule-grid tbody tr:hover td.is-today { background: var(--brand-100); }

.schedule-grid .is-free { color: var(--ink-soft); }

/* A class sitting in a time slot. */
.sched-class {
  display: block;
  padding: .22rem .4rem .26rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--field);
  min-width: 0;
}

.sched-class-code { display: block; font-size: .74rem; font-weight: 700; color: var(--brand-800); }
.sched-class-name {
  display: block;
  font-size: .66rem;
  line-height: 1.25;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}

/* ---------------- Classworks ---------------- */

.cw-list { display: flex; flex-direction: column; gap: .55rem; }

/* Each activity stands on its own card — page colour inside a hairline
   outline, so the separation comes from the rule and the gap, not a fill. */
.cw-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .8rem 1rem;
  border: 1px solid var(--sand-300);
  border-radius: 10px;
  background: var(--canvas);
  cursor: pointer;
  transition: border-color .12s, background .12s;
}

.cw-card:hover { background: var(--field); border-color: var(--brand-600); }
.cw-card:focus-visible { outline: 3px solid var(--accent-400); outline-offset: -3px; }

.cw-main { flex: 1 1 auto; min-width: 0; }
.cw-title { margin: 0; font-size: .98rem; font-weight: 700; color: var(--brand-800); }

.cw-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .2rem 1rem;
  margin: .3rem 0 0;
  font-size: .78rem;
  color: var(--ink-soft);
}

.cw-bit { display: inline-flex; align-items: center; gap: .3rem; }
.cw-bit svg { flex: 0 0 auto; opacity: .8; }
.cw-bit.is-done { color: var(--brand-600); font-weight: 600; }
/* Terracotta only where it means something: this one is the term's exam. */
.cw-bit.is-exam { color: var(--accent-700); font-weight: 600; }
.cw-bit.is-done svg, .cw-bit.is-exam svg { opacity: 1; }

.cw-actions { display: flex; align-items: center; gap: .15rem; flex: 0 0 auto; }

.cw-iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background .12s, color .12s;
}

.cw-iconbtn:hover { background: var(--brand-050); color: var(--brand-700); }
.cw-iconbtn.is-danger:hover { background: var(--accent-050); color: var(--accent-700); }
.cw-iconbtn:focus-visible { outline: 3px solid var(--accent-400); outline-offset: -2px; }

/* The way in, at the end of the list — the school-year tile's dashed
   terracotta edge, laid out as a row rather than a square. */
.cw-add {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .9rem 1rem;
  border: 2px dashed var(--accent-400);
  border-radius: 10px;
  background: var(--canvas);
  font: inherit;
  font-size: .92rem;
  font-weight: 700;
  color: var(--brand-700);
  cursor: pointer;
  transition: border-color .15s, background .15s;
}

.cw-add:hover, .cw-add:focus-visible {
  background: var(--sand-100);
  border-color: var(--accent-600);
  outline: none;
}

.cw-add-plus { font-size: 1.35rem; line-height: 1; font-weight: 400; }

/* Score sheet: a number box and an excused tick per student. */
.cw-score-input {
  width: 4.5rem;
  min-height: 0;
  padding: .25rem .4rem;
  font-size: .85rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.cw-score-input:disabled { opacity: .4; }
.cw-out-of { font-size: .78rem; color: var(--ink-soft); white-space: nowrap; }

/* A select sits taller than a baseline-aligned row expects, so the score sheet
   centres its rows where the import list does not. */
#cwScoreRows .import-row { align-items: center; }
#cwScoreRows .imp-name { font-weight: 600; }
#cwScoreRows .import-row.is-excused .imp-name { color: var(--ink-soft); }

.cw-status {
  flex: 0 0 auto;
  width: auto;
  min-width: 7.5rem;
  min-height: 0;
  padding: .25rem .4rem;
  font-size: .8rem;
}

/* ---------------- Marking against criteria ----------------
   The class down one side, one student's criteria on the other. Six boxes a
   student will not fit on a row beside a name, and a criterion is worth
   nothing to mark against if you cannot read what it asks for. */

.cw-board {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  gap: 1rem;
  margin-top: .9rem;
  border-top: 1px solid var(--rule);
}

.cw-board-list {
  display: flex;
  flex-direction: column;
  max-height: 26rem;
  overflow-y: auto;
  padding: .5rem .4rem 0 0;
  border-right: 1px solid var(--rule);
}

.cw-board-detail { max-height: 26rem; overflow-y: auto; padding: .8rem .2rem 0 0; }

/* --- the class list --- */

.cw-who {
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  padding: .4rem .45rem;
  border: 0;
  border-radius: var(--radius);
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background .12s;
}

.cw-who:hover { background: var(--brand-050); }
.cw-who.is-on { background: var(--brand-700); color: var(--white); }
.cw-who:focus-visible { outline: 3px solid var(--accent-400); outline-offset: -2px; }

.cw-who-mark {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  background: var(--sand-100);
  font-size: .72rem;
  font-weight: 800;
  color: var(--brand-700);
}

.cw-who.is-on .cw-who-mark { background: var(--brand-600); color: var(--white); }

.cw-who-name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: .82rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cw-who-score {
  flex: 0 0 auto;
  font-size: .78rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.cw-who-score.is-blank { font-weight: 400; opacity: .55; }

.cw-who-flag {
  flex: 0 0 auto;
  padding: 0 .35rem;
  border-radius: 999px;
  background: var(--sand-100);
  font-size: .62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--ink-soft);
}

.cw-who-flag.is-absent { background: var(--accent-050); color: var(--accent-700); }
.cw-who.is-on .cw-who-flag { background: rgba(255, 255, 255, .2); color: var(--white); }

/* --- the student being marked --- */

.cw-mark-head {
  display: flex;
  align-items: flex-start;
  gap: .8rem;
  padding-bottom: .7rem;
  border-bottom: 1px solid var(--rule);
}

.cw-mark-who { flex: 1 1 auto; min-width: 0; }

/* The flags and group actions under the name: given room, and on their own
   line, rather than jammed against each other and the student number. */
.cw-mark-marks {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: .35rem;
}
.cw-mark-name { margin: 0; font-size: 1.02rem; font-weight: 800; color: var(--brand-800); }
.cw-mark-no { margin: .1rem 0 .3rem; font-size: .76rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

.cw-mark-tally { flex: 0 0 auto; text-align: right; }

.cw-mark-score {
  margin: 0;
  font-size: 1.35rem;
  font-weight: 800;
  color: var(--brand-800);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.cw-mark-score.is-over { color: var(--accent-700); }
.cw-mark-percent { margin: 0; font-size: .76rem; color: var(--ink-soft); }

/* What the students were asked to do, kept in front of whoever is marking. */
.cw-mark-brief {
  margin-top: .8rem;
  padding: .6rem .75rem;
  border-left: 3px solid var(--accent-400);
  background: var(--sand-050);
}

.cw-mark-brief-head {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  margin-bottom: .2rem;
}

.cw-mark-brief-label {
  margin: 0;
  font-size: .66rem;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--accent-700);
}

.cw-mark-brief-toggle {
  margin-left: auto;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: .72rem;
  font-weight: 700;
  color: var(--accent-700);
  text-decoration: underline;
  cursor: pointer;
}

.cw-mark-brief-toggle:hover { color: var(--accent-600); }
.cw-mark-brief-toggle:focus-visible { outline: 3px solid var(--accent-400); outline-offset: 2px; }

.cw-mark-brief-text { margin: 0; font-size: .82rem; line-height: 1.45; white-space: pre-wrap; }

/* Folded to three lines — enough to recognise the brief, not enough to push
   the criteria off the screen. */
.cw-mark-brief-text.is-folded {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* --- one card per criterion --- */

/* ---- A rubric in sections, on the marking sheet ---- */
.cw-sec {
  margin-top: .8rem;
  border: 1px solid var(--sand-300);
  border-radius: var(--radius);
  overflow: hidden;
}

.cw-sec-head {
  display: flex;
  align-items: center;
  gap: .6rem;
  width: 100%;
  padding: .5rem .75rem;
  border: 0;
  background: var(--surface);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.cw-sec-head:hover { background: var(--surface-alt); }
.cw-sec-head:focus-visible { outline: 2px solid var(--brand-600); outline-offset: -2px; }

.cw-sec-chev {
  width: 12px;
  font-size: .62rem;
  color: var(--ink-soft);
  transform: rotate(90deg);
  transition: transform .12s;
}
.cw-sec.is-closed .cw-sec-chev { transform: none; }

.cw-sec-title {
  flex: 1 1 auto;
  font-size: .78rem;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--brand-800);
}
.cw-sec-count { font-size: .72rem; font-weight: 600; color: var(--ink-soft); white-space: nowrap; }
.cw-sec-sum {
  min-width: 4.5rem;
  text-align: right;
  font-size: .9rem;
  font-weight: 800;
  color: var(--brand-800);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.cw-sec-sum.is-blank { color: var(--ink-soft); font-weight: 700; }

.cw-sec-fill { height: 3px; background: var(--sand-200); }
.cw-sec-fill i { display: block; height: 100%; background: var(--brand-600); transition: width .15s; }

.cw-sec.is-closed .cw-sec-rows { display: none; }
.cw-sec-note { margin: 0; padding: .55rem .75rem; font-size: .78rem; color: var(--ink-soft); }

/* A criterion inside a section: a row, not a card. */
.cw-crit-card.is-row {
  margin: 0;
  padding: .45rem .75rem;
  align-items: center;
  border: 0;
  border-top: 1px solid var(--rule);
  border-radius: 0;
}
.cw-sec-rows .cw-crit-card.is-row:first-child { border-top: 0; }
.cw-crit-card.is-row .cw-crit-title { font-size: .84rem; font-weight: 600; color: var(--ink); }
.cw-crit-card.is-row .cw-crit-desc { font-size: .74rem; }

/* The grand total under the last section. */
.cw-sec-total {
  display: flex;
  justify-content: flex-end;
  align-items: baseline;
  gap: .8rem;
  margin-top: .9rem;
  padding-top: .6rem;
  border-top: 2px solid var(--brand-700);
}
.cw-sec-total span {
  font-size: .75rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.cw-sec-total b { font-size: 1.1rem; color: var(--brand-800); }

.cw-crit-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: .3rem .8rem;
  margin-top: .7rem;
  padding: .65rem .75rem .7rem;
  border: 1px solid var(--sand-300);
  border-radius: var(--radius);
  background: var(--canvas);
}

.cw-crit-card.is-over { border-color: var(--accent-500); }
.cw-crit-card.is-excused { opacity: .5; }

.cw-crit-text { min-width: 0; }
.cw-crit-title { margin: 0; font-size: .88rem; font-weight: 700; color: var(--brand-800); }
.cw-crit-desc { margin: .15rem 0 0; font-size: .78rem; line-height: 1.4; color: var(--ink-soft); }

.cw-crit-mark { display: flex; align-items: center; gap: .35rem; }

.cw-mark-input {
  width: 3.6rem;
  min-height: 0;
  padding: .3rem .35rem;
  font-size: .95rem;
  font-weight: 700;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.cw-mark-input:disabled { opacity: .4; }
.cw-crit-out { font-size: .8rem; color: var(--ink-soft); white-space: nowrap; }

/* How much of the criterion has been awarded, at a glance down the column. */
.cw-crit-bar {
  grid-column: 1 / -1;
  height: 4px;
  margin-top: .35rem;
  border-radius: 999px;
  background: var(--sand-200);
  overflow: hidden;
}

.cw-crit-fill {
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--brand-600);
  transition: width .12s;
}

.cw-crit-card.is-over .cw-crit-fill { background: var(--accent-500); }

/* Narrow screens cannot hold two panes, so the list goes above. */
@media (max-width: 720px) {
  .cw-board { grid-template-columns: minmax(0, 1fr); }
  .cw-board-list { max-height: 11rem; border-right: 0; border-bottom: 1px solid var(--rule); }
}

/* Absence explains a blank score, so it is worth a word. Being present or late
   explains nothing, and gets no mark. */
.cw-absent {
  flex: 0 0 auto;
  padding: .05rem .45rem;
  border: 1px solid var(--accent-100);
  border-radius: 999px;
  background: var(--accent-050);
  font-size: .7rem;
  font-weight: 700;
  color: var(--accent-700);
  white-space: nowrap;
}

.score-search {
  width: 100%;
  min-height: 0;
  padding: .4rem .6rem;
  font-size: .85rem;
}

/* The count sits above the search and needs no rule under it — the board
   below draws its own, and two lines there wasted a band of space. */
#cwScoreDialog .listbar {
  margin-top: .5rem;
  padding-bottom: 0;
  border-bottom: 0;
}

#cwScoreDialog .cw-board { margin-top: .6rem; }
#cwScoreDialog .import-rows { margin-top: .2rem; }

/* The term grade needs room for "100.0%", which the register's 2.4rem totals
   column wraps onto two lines. */
#cwGrid th.reg-total, #cwGrid td.reg-total { width: 4.4rem; white-space: nowrap; }

/* Pick what kind of work it is before filling anything in. */
.cw-picker { display: flex; flex-direction: column; margin-top: .6rem; }

.cw-pick {
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .5rem .4rem;
  border: 0;
  border-bottom: 1px solid var(--sand-100);
  background: none;
  font: inherit;
  font-size: .92rem;
  font-weight: 600;
  color: var(--brand-800);
  text-align: left;
  cursor: pointer;
  transition: background .12s;
}

.cw-pick:hover { background: var(--brand-050); }
.cw-pick:focus-visible { outline: 3px solid var(--accent-400); outline-offset: -2px; }

/* The icon sits in a soft disc so the names line up on one edge whatever
   shape the glyph is. */
.cw-pick-disc {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  background: var(--brand-050);
  color: var(--brand-700);
}

.cw-pick-name { flex: 1 1 auto; min-width: 0; }
.cw-pick-note { font-size: .74rem; font-weight: 400; font-style: italic; color: var(--ink-soft); }

/* The chevron — last child, and the only bare svg in the row. */
.cw-pick > svg { flex: 0 0 auto; color: var(--sand-300); }
.cw-pick:hover > svg { color: var(--brand-600); }

.cw-pick.is-custom { color: var(--accent-700); border-bottom: 0; }
.cw-pick.is-custom .cw-pick-disc { background: var(--accent-050); color: var(--accent-700); }
.cw-pick.is-custom:hover { background: var(--accent-050); }

.cw-topic { margin: .18rem 0 0; font-size: .82rem; color: var(--brand-700); }
.cw-topic.is-blank { color: var(--ink-soft); opacity: .65; }
.cw-exam-check { margin-top: .3rem; }

/* Max points is derived once a rubric exists, so it stops looking editable. */
.is-derived { background: var(--surface); color: var(--ink-soft); }

/* --- The class record, laid out as the college's own form --- */

/* Doubled class so this beats .grid.register-grid's fixed layout. Fixed would
   take its widths from the first row, which here is the banded header with a
   colspan across the class-standing columns — and everything else collapses. */
.grid.record-grid { table-layout: auto; width: 100%; font-size: .72rem; }

/* Ruled all the way through, like the form it copies: every cell has edges. */
.grid.record-grid th,
.grid.record-grid td {
  border: 1px solid var(--sand-300);
  padding: .18rem .3rem;
}

.grid.record-grid tbody td { font-size: .72rem; }
.grid.record-grid tbody td.primary-cell { font-size: .74rem; font-weight: 500; }
.grid.record-grid td.mk { font-size: .72rem; padding: .18rem .2rem; }

/* Unused columns recede — but only their body cells. The header band stays
   solid olive the whole way across, or the numbering looks half-disabled. */
.grid.record-grid tbody td.is-empty { background: var(--sand-050); }

.record-grid .rec-corner { background: none; border-bottom: 0; }

.record-grid .rec-standing {
  text-align: center;
  font-size: .74rem;
  letter-spacing: .04em;
  text-transform: none;
  white-space: normal;
  padding: .35rem .5rem;
}

.record-grid .rec-tall { width: 2.6rem; text-align: center; vertical-align: bottom; }

/* ------- The term's paperwork: permits and class cards ---------------
   A register of its own, opened from the record rather than crammed onto it.
   Two ticks per student, and the second is shut until the first is made. */

/* The count of who still owes a permit, on the button that opens the lot. */
/* min-width:0 because the modal body is a grid, and a grid item's automatic
   minimum is its content -- without this the wrapper stretches to the widest
   the table would like to be and the table's own 100% follows it off the
   edge of the dialog. */
.papers-wrap { min-width: 0; max-height: 22rem; overflow-y: auto; }

/* Fixed layout so the two tick columns keep their width and the names take
   whatever is left -- the dialog is a fixed width, and a table that sets its
   own ends up scrolling sideways inside it. */
/* No top margin: the table is the whole of its wrapper, and a gap above it
   would show as a band of cream inside the outline. */
.grid.papers-table { width: 100%; table-layout: fixed; margin-top: 0; }
.papers-table th, .papers-table td { padding: .35rem .6rem; }

/* Ruled, not filled. The dialog's own background runs straight through the
   rows and a hairline tells them apart, so the register reads as a list on
   paper rather than a block of shaded data. The heading keeps its band --
   it is the one part that should sit apart from the page.

   Written as .grid.papers-table because the base .grid rules are no less
   specific and come later; one class is not enough to beat them. */
.grid.papers-table tbody tr { background: transparent; }

/* The base grid rules a terracotta line under every heading, to set it off
   from a page it sits on. Here the heading already sits inside an outline
   and the line only reads as something leaking out from under the band. */
.grid.papers-table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  font-size: .68rem;
  letter-spacing: .06em;
  border-bottom: 0;
}

.papers-table tbody td { border-bottom: 1px solid var(--rule); }
.papers-table tbody tr:last-child td { border-bottom: 0; }

/* Term, filter and search on one line above the register. */
.papers-filters {
  min-width: 0;          /* a grid item in the modal body; see .papers-wrap */
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: .6rem;
}

.papers-filters .papers-pick { flex: 0 0 9rem; }
.papers-filters .papers-search { flex: 1 1 8rem; min-width: 0; }
/* The shared search style carries a 15rem minimum for the wider screens it
   sits on; here it shares a line with three dropdowns and has to give way. */
.papers-filters .register-search { width: 100%; min-width: 0; max-width: none; }

/* The two dropdowns are the search bar's size, not a form field's: the same
   height, type and padding, so the three read as one row of controls. The
   right padding stays wider, because that is where the arrow sits. */
.papers-filters select {
  min-height: 0;
  height: 30px;
  padding: .32rem 1.6rem .32rem .55rem;
  font-size: .82rem;
}

.papers-filters .register-search { height: 30px; }

/* The outline the whole thing sits in. No padding, so the heading band runs
   right out to it instead of leaving a strip of cream down either side. */
.papers-wrap {
  border: 1px solid var(--sand-300);
  border-radius: var(--radius);
}

/* Percentages rather than rems: fixed layout divides the dialog's width
   between these four, and a rem that does not quite fit pushes the last
   column off the edge. */
.papers-table th.papers-pick-col, .papers-table td.papers-pick-col {
  width: 6%;
  text-align: center;
}
/* The app's form fields carry a 39px minimum height; a tick box inherits it
   and stretches every row, so it is put back to a plain box here. */
.papers-pick-col input {
  min-height: 0;
  width: 15px;
  height: 15px;
  margin: 0;
  vertical-align: middle;
  cursor: pointer;
  accent-color: var(--brand-700);
}

.papers-table th.papers-no, .papers-table td.papers-no {
  width: 7%;
  text-align: center;
  color: var(--ink-soft);
}

.papers-table th.papers-name, .papers-table td.papers-name { width: 39%; }
.papers-table td.papers-name { cursor: pointer; }

/* A ticked row is tinted, so the selection reads down the list. */
.grid.papers-table tbody tr.is-picked { background: var(--brand-050); }

/* The bar of changes for everyone ticked. Dark, like the table's heading, so
   it reads as a toolbar that has appeared rather than one more panel; the
   choices inside are light switches in the same red and green the table
   uses for the answers they set. */
.papers-bulk {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .45rem 1.1rem;
  padding: .45rem .5rem .45rem .85rem;
  border-radius: var(--radius);
  background: var(--brand-800);
  color: var(--white);
}

.papers-bulk-count {
  padding: .12rem .55rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, .14);
  font-size: .74rem;
  font-weight: 800;
  white-space: nowrap;
}

.papers-bulk-group { display: inline-flex; align-items: center; gap: .45rem; }

.papers-bulk-label {
  font-size: .64rem;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .7);
}

/* Two answers joined into one switch. */
.papers-seg {
  display: inline-flex;
  border-radius: 999px;
  background: var(--canvas);
  padding: 2px;
}

.papers-seg-btn {
  padding: .22rem .7rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  font: inherit;
  font-size: .72rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}

.papers-seg-btn.is-yes { color: var(--brand-700); }
.papers-seg-btn.is-no { color: var(--accent-700); }
.papers-seg-btn.is-yes:hover { background: var(--brand-100); }
.papers-seg-btn.is-no:hover { background: var(--accent-100); }
.papers-seg-btn:focus-visible { outline: 2px solid var(--white); outline-offset: 1px; }

/* A plain cross at the far end, rather than another labelled button. */
.papers-bulk-clear {
  margin-left: auto;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--white);
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
}
.papers-bulk-clear:hover { background: rgba(255, 255, 255, .16); }

/* Names at the size of the status words beside them, so a row reads as one
   line of text. Written as .grid.papers-table to beat the base .grid size. */
.grid.papers-table td.papers-name { font-size: .72rem; }

.papers-table td.papers-name {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.papers-table th.papers-col, .papers-table td.papers-col {
  width: 24%;
  text-align: center;
}

.grid.papers-table tbody tr:hover { background: var(--brand-050); }
.papers-table tr.is-left td.papers-name { color: var(--ink-soft); font-style: italic; }

/* The state itself, written out: Submitted / Not submitted, Received / Not
   received. Pressing it changes what it says, and says it again the other way
   when pressed back.

   No fill and no outline -- it sits on the row's own background, so a column
   of these reads as a list of words rather than a row of buttons. Colour is
   what carries the state: terracotta for what is still owed, olive for what
   has been handed over, so a glance down either column finds the stragglers
   without reading a word. */
.papers-tick {
  display: inline-block;
  width: 100%;
  padding: .3rem .4rem;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  font: inherit;
  font-size: .72rem;
  font-weight: 700;
  white-space: nowrap;
  color: var(--accent-700);
  cursor: pointer;
}

/* Only on hover does it look like something to press. */
.papers-tick:hover:not(:disabled) { background: var(--brand-050); }
.papers-tick:focus-visible { outline: 2px solid var(--brand-600); outline-offset: -2px; }

.papers-tick.is-on { color: var(--brand-700); }

/* Pressed but not saved yet: a dotted underline, gone once Save is pressed. */
.papers-tick.is-changed {
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}

/* The count of unsaved changes, at the left of the footer. */
.papers-unsaved {
  margin-right: auto;
  align-self: center;
  font-size: .78rem;
  font-weight: 700;
  color: var(--accent-700);
}

/* No permit in, so there is no card to hand back. It keeps the colour of
   what it is -- still owed -- and only loses its weight. */
.papers-tick.is-shut {
  opacity: .55;
  font-weight: 600;
  cursor: not-allowed;
}


/* Max Score sits between the band and the numbers, the way the paper does. */
.record-grid .rec-max th {
  background: var(--surface);
  color: var(--brand-800);
  border-bottom: 1px solid var(--sand-300);
}

.record-grid .rec-maxlabel {
  text-align: right;
  font-size: .68rem;
  letter-spacing: .06em;
  padding-right: .5rem;
}

.record-grid .rec-maxval {
  text-align: center;
  font-size: .72rem;
  font-variant-numeric: tabular-nums;
}

.record-grid .rec-numbers th {
  text-align: center;
  font-size: .68rem;
  padding: .25rem .2rem .3rem;
}

.record-grid .rec-no { width: 2.2rem; min-width: 2.2rem; text-align: center; }
.record-grid .rec-num { width: 1.8rem; }

/* The register's 17rem rule keys off a different class name, so the name
   column needs its own width here or the names wrap to three lines. */
/* Capped rather than pinned, so a long roster never forces the table wider
   than the page — the name clips instead of the grid scrolling. */
.grid.record-grid th.rec-name,
.grid.record-grid td.primary-cell {
  width: 12rem;
  /* The register pins this column at 17rem; released here so max-width can
     actually bite and the record fits the page instead of scrolling. */
  min-width: 0;
  max-width: 12rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.grid.record-grid th.rec-name { text-align: center; }

.record-grid td.rec-no {
  font-size: .74rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.record-grid td.rec-val {
  text-align: center;
  font-size: .78rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The name column stays put; so does the numbering beside it. */
.record-grid td.rec-no, .record-grid th.rec-no { position: sticky; left: 0; z-index: 2; background: var(--canvas); }
.record-grid td.primary-cell, .record-grid th.rec-name { left: 2.4rem; }
.record-grid th.rec-no, .record-grid th.rec-name { background: var(--brand-800); }
.record-grid .rec-max th.rec-maxlabel { background: var(--surface); }

.cw-term-grade { font-weight: 800; color: var(--brand-800); }
.field-hint { font-weight: 400; color: var(--ink-soft); text-transform: none; letter-spacing: 0; }

/* --- Stepping across the week. Flat, on a hairline, not a boxed panel. --- */

.daynav {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-top: 1.2rem;
  padding-bottom: .7rem;
  border-bottom: 1px solid var(--rule);
}

.daynav-arrow {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  padding: 0 0 .18rem;
  border: 1px solid var(--brand-600);
  border-radius: 50%;
  background: none;
  font-size: 1.2rem;
  line-height: 1;
  color: var(--brand-700);
  cursor: pointer;
  transition: background .12s;
}

.daynav-arrow:hover:not(:disabled) { background: var(--brand-050); }
.daynav-arrow:disabled { opacity: .3; cursor: default; }
.daynav-arrow:focus-visible { outline: 3px solid var(--accent-400); outline-offset: 2px; }

.daynav-strip { display: flex; flex: 1 1 auto; gap: .3rem; min-width: 0; }

.daypip {
  flex: 1 1 0;
  min-width: 0;
  padding: .3rem .2rem .35rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: none;
  font: inherit;
  line-height: 1.15;
  text-align: center;
  cursor: pointer;
  transition: background .12s, border-color .12s;
}

.daypip:hover { background: var(--surface); }
.daypip:focus-visible { outline: 3px solid var(--accent-400); outline-offset: -2px; }
.daypip.is-viewing { background: var(--surface); border-color: var(--brand-600); }

.daypip-day  { display: block; font-size: .66rem; font-weight: 800; letter-spacing: .05em;
               text-transform: uppercase; color: var(--ink-soft); }
.daypip-date { display: block; font-size: .95rem; font-weight: 800; color: var(--brand-800); }
.daypip.is-today .daypip-date { color: var(--accent-600); }

/* A dot means the day has classes; no dot means a free day. */
.daypip-dot { display: block; width: 5px; height: 5px; margin: .18rem auto 0;
              border-radius: 50%; background: var(--brand-600); }
.daypip-dot.is-free { background: none; }

/* --- One card per class. Not the banner card: that belongs to school
       years and classes. This is a worklist, so the status leads. --- */

.today-card { display: flex; flex-direction: column; text-align: left; }

.today-strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
  padding: .5rem .9rem .45rem;
  border-bottom: 1px solid var(--sand-300);
}

.today-flag { font-size: .67rem; font-weight: 800; letter-spacing: .09em; text-transform: uppercase; }
.today-when { font-size: .74rem; font-weight: 700; color: var(--ink-soft); white-space: nowrap; }

.today-card.is-now      .today-flag { color: var(--accent-600); }
.today-card.is-upcoming .today-flag { color: var(--brand-700); }
.today-card.is-done     .today-flag { color: var(--ink-soft); }

/* One group, enclosed: its heading and its members inside a single ruled
   block, so a sheet of forty reads as eight groups rather than forty rows.
   `flex: 0 0 auto` matters: the board's list is a flex column, and without it
   every block is shrunk to a sliver and its members clipped away by the
   overflow below. */
.cw-group-block {
  flex: 0 0 auto;
  margin: .6rem 0;
  border: 1px solid var(--sand-300);
  border-radius: 10px;
  overflow: hidden;
  background: var(--canvas);
}

.cw-group-block .import-row { border-bottom: 1px solid var(--sand-200); }
.cw-group-block .import-row:last-child { border-bottom: 0; }

/* Nobody's group: ruled the same way, but plainly not a team. */
.cw-group-block.is-loose { border-style: dashed; background: transparent; }

/* A group's heading in the scoring list: a band, not a row, so the members
   under it read as belonging together. */
.cw-group-head {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin: .7rem 0 .15rem;
  padding: .3rem .5rem;
  border-radius: var(--radius);
  background: var(--brand-050);
  border-left: 3px solid var(--brand-600);
}

/* Inside a block it is the block's own top band, so it keeps no margin. */
.cw-group-block .cw-group-head { margin: 0; border-radius: 0; border-left: 0;
  border-bottom: 1px solid var(--sand-300); }

/* The group's heading, and under it the status that applies to all of it.
   Stacked rather than side by side: the heading keeps the full width of its
   band, so the name does not wrap and the tally stays hard against the right
   where it has always been. The row carries the rule along the bottom so the
   heading does not draw a second one halfway up. */
.cw-group-block .cw-group-headrow {
  border-bottom: 1px solid var(--sand-300);
}

.cw-group-block .cw-group-headrow .cw-group-head { border-bottom: 0; }

.cw-group-setall {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .25rem .5rem .35rem;
}

.cw-group-setall-label {
  font-size: .64rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* In the group's pane it sits beside the score, the size a student's own
   status is; in the fallback band under a heading it takes the rest of the
   line so a long status is never clipped. */
.cw-mark-head .cw-group-status { flex: 0 0 auto; align-self: flex-start; }

.cw-group-setall .cw-group-status {
  flex: 1 1 auto;
  width: auto;
  min-height: 0;
  padding: .14rem 1.2rem .14rem .4rem;
  font-size: .72rem;
  font-weight: 700;
  color: var(--brand-700);
}

.cw-group-name { font-size: .82rem; font-weight: 800; color: var(--brand-800); }
.cw-group-fill { margin-left: auto; font-size: .76rem; font-weight: 700; color: var(--ink-soft); }
.cw-group-head .cw-score-input { flex: 0 0 4.5rem; }

/* The board's list: the same blocks as the plain sheet, and the group's own
   criteria marked on the block's header. */
.cw-group-block .cw-who { border-radius: 0; border-left: 0; border-right: 0; }
.cw-group-block .cw-who:last-child { border-bottom: 0; }

/* The group's row: picked like a name, and showing as picked. */
.cw-group-head.is-pick { width: 100%; cursor: pointer; font: inherit; text-align: left; }
.cw-group-head.is-pick:hover { background: var(--brand-100, var(--brand-050)); }
.cw-group-head.is-pick.is-on { background: var(--brand-700); }
.cw-group-head.is-pick.is-on .cw-group-name,
.cw-group-head.is-pick.is-on .cw-group-tally { color: var(--white); }

.cw-crit-foot { margin: .8rem 0 0; font-size: .76rem; color: var(--ink-soft); }

/* How the group's shared criteria stand, beside its name in the list. */
.cw-group-tally {
  margin-left: auto;
  font-size: .76rem;
  font-weight: 800;
  color: var(--brand-800);
  font-variant-numeric: tabular-nums;
}

.cw-group-tally.is-blank { color: var(--ink-soft); font-weight: 700; }

/* "Group 7 — one mark for everyone", then "Godwin's own": two short headings
   that say who each criterion below is being marked for. */
.cw-crit-sec {
  margin: .9rem 0 .35rem;
  font-size: .72rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.cw-crit-sec:first-child { margin-top: 0; }

/* Rated by the group: shown, not typed. The box is plainly not a field. */
.cw-crit-card.is-fromgroup { background: var(--brand-050); border-left: 3px solid var(--brand-600); }
.cw-crit-card.is-fromgroup .cw-mark-input {
  background: transparent;
  border-style: dashed;
  color: var(--brand-800);
  cursor: default;
}

.cw-crit-from { margin: .15rem 0 0; font-size: .73rem; font-weight: 700; color: var(--brand-700); }

/* A criterion the whole group shares: marked once, and it says so. */
.cw-crit-card.is-shared { border-left: 3px solid var(--brand-600); background: var(--brand-050); }
.cw-crit-tag {
  margin-left: .45rem;
  padding: .05rem .4rem;
  border-radius: 999px;
  border: 1px solid var(--brand-600);
  font-size: .64rem;
  font-weight: 800;
  color: var(--brand-800);
}

/* ---- Grouping a class: pick a group, then click the names ---------- */

.cw-group-search { flex: 1 1 10rem; min-width: 8rem; max-width: 16rem; }

.cw-group-chips {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  padding-bottom: .2rem;
}

.cw-chip {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .3rem .7rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--canvas);
  font: inherit;
  font-size: .8rem;
  font-weight: 700;
  color: var(--brand-800);
  cursor: pointer;
}

.cw-chip:hover { border-color: var(--brand-600); }

/* The group being filled: everything clicked goes here. */
.cw-chip.is-on {
  background: var(--brand-700);
  border-color: var(--brand-800);
  color: var(--white);
}

.cw-chip-count {
  padding: 0 .35rem;
  border-radius: 999px;
  background: var(--field);
  font-size: .7rem;
  color: var(--ink-soft);
}

.cw-chip.is-on .cw-chip-count { background: var(--brand-600); color: var(--white); }
.cw-chip.is-loose.is-on { background: var(--accent-600); border-color: var(--accent-700); }
.cw-chip.is-add { border-style: dashed; color: var(--ink-soft); }

/* Every name at once: a class of forty is one screen, not a scroll. */
.cw-group-pick {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: .35rem;
  max-height: 24rem;
  overflow-y: auto;
  padding: .2rem;
}

.gp-name {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .4rem .55rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--canvas);
  font: inherit;
  font-size: .82rem;
  text-align: left;
  color: var(--ink);
  cursor: pointer;
}

.gp-name:hover { border-color: var(--brand-600); background: var(--brand-050); }
.gp-name-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.gp-name-tag {
  flex: 0 0 auto;
  min-width: 1.4rem;
  padding: 0 .3rem;
  border-radius: 999px;
  background: var(--field);
  font-size: .72rem;
  font-weight: 800;
  color: var(--ink-soft);
  text-align: center;
}

/* In a group, but not the one being filled. */
.gp-name.is-in { border-color: var(--sand-300); background: var(--sand-100); }
.gp-name.is-in .gp-name-tag { background: var(--brand-050); color: var(--brand-800); }

/* In the group being filled: clicking again takes them out. */
.gp-name.is-here { border-color: var(--brand-700); background: var(--brand-050); font-weight: 700; }
.gp-name.is-here .gp-name-tag { background: var(--brand-700); color: var(--white); }

/* Peer evaluation: who is answering, and what they say about each groupmate. */
.cw-peer { display: flex; flex-direction: column; gap: .5rem; max-height: 26rem; overflow-y: auto; }

.cw-peer-who { display: flex; align-items: center; flex-wrap: wrap; gap: .4rem; }
.cw-peer-label { font-size: .74rem; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-soft); margin-right: .2rem; }

/* The rating table: a column per criterion, a line per groupmate. */
.peer-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--canvas);
}

.peer-table th,
.peer-table td {
  border: 1px solid var(--sand-300);
  padding: .4rem .55rem;
  text-align: center;
  vertical-align: middle;
}

.peer-table thead th {
  font-size: .8rem;
  font-weight: 800;
  color: var(--brand-800);
  background: var(--brand-050);
}

/* What each criterion asks, under its own title and nowhere else. */
.peer-table .peer-desc-row th {
  font-size: .73rem;
  font-weight: 400;
  line-height: 1.35;
  color: var(--ink-soft);
  background: var(--brand-050);
  border-top: 0;
}

.peer-table .peer-th-name { width: 14rem; text-align: left; }

/* Five criteria or more: the names give way to the columns being marked, and
   a long one is cut with the whole of it on hover. */
.peer-table.is-tight .peer-th-name { width: 8.5rem; }
.peer-table.is-tight .peer-td-name {
  max-width: 8.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Tighter still where the columns are many: the cells give up their padding
   before the boxes give up being typeable. */
.peer-table.is-tight th,
.peer-table.is-tight td { padding: .35rem .3rem; }
.peer-table.is-tight .peer-td-mark .cw-mark-input { width: 2.3rem; }
.peer-table.is-tight .peer-out-of { margin-left: .2rem; font-size: .7rem; }
.peer-table.is-tight .peer-th-total { width: 4.6rem; }
.peer-table .peer-th-total { width: 6rem; }

.peer-table .peer-td-name {
  text-align: left;
  font-size: .85rem;
  font-weight: 700;
  color: var(--brand-800);
}

/* A bar with five stops, the name of the stop, and the points it comes to. */
.peer-td-mark { white-space: nowrap; }

.peer-dial { display: flex; flex-direction: column; gap: .1rem; }

.peer-range {
  width: 100%;
  min-width: 5.5rem;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  accent-color: var(--brand-700);
  cursor: pointer;
}

/* Nothing chosen yet: the handle sits at the left but says nothing by it. */
.peer-range.is-blank { accent-color: var(--sand-300); }

/* The bumps themselves, under the bar and lined up with its stops. */
.peer-ticks { display: flex; justify-content: space-between; padding: 0 .3rem; }
.peer-tick {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--sand-300);
}

.peer-entry { display: flex; align-items: center; justify-content: center; gap: .3rem; }

.peer-level-name {
  min-width: 3.6rem;
  font-size: .72rem;
  font-weight: 700;
  color: var(--brand-700);
  text-align: right;
}

/* A score that lands between two stops, so the name is not a judgement. */
.peer-level-name.is-custom { color: var(--ink-soft); font-style: italic; }

.peer-table.is-tight .peer-level-name { min-width: 3rem; font-size: .68rem; }
.peer-table.is-tight .peer-range { min-width: 4.5rem; }
.peer-td-mark .cw-mark-input { width: 2.6rem; text-align: center; padding-left: .2rem; padding-right: .2rem; }
.peer-td-mark .cw-mark-input.is-over { border-color: var(--accent-600); color: var(--accent-700); }
.peer-out-of { margin-left: .3rem; font-size: .74rem; color: var(--ink-soft); }

.peer-td-total {
  font-size: .85rem;
  font-weight: 800;
  color: var(--brand-800);
  font-variant-numeric: tabular-nums;
}

/* Some of the row filled in, but not all of it. */
.peer-td-total.is-part { color: var(--accent-700); }

/* How far through this student's answers you are, in the footer. */
.cw-peer-state { margin-right: auto; font-size: .78rem; font-weight: 700; color: var(--ink-soft); }
.cw-peer-state.is-done { color: var(--brand-700); }

.cw-group-toggle { margin: .2rem 0 0; }
.cw-form-groups { max-height: 16rem; overflow-y: auto; }

/* On the rubric board, beside the name being marked. */
.cw-group-tag {
  display: inline-block;
  padding: .1rem .5rem;
  border-radius: 999px;
  background: var(--brand-050);
  border: 1px solid var(--brand-600);
  font-size: .7rem;
  font-weight: 800;
  color: var(--brand-800);
}

/* On the record, no longer in the class: named in the same ink, set in italic
   so the row reads as closed rather than merely blank. */
.gs-table tr.is-left .gs-name { color: var(--ink-soft); font-style: italic; }
.gs-cell.is-left { color: var(--ink-soft); }
.record-grid tr.is-left .primary-cell { color: var(--ink-soft); font-style: italic; }

/* A cancelled day overrules the clock: no "in session", no live highlight. */
.today-card.is-cancelled .today-flag { color: var(--accent-700); }
.today-card.is-cancelled { border-color: var(--sand-300); }

/* The class happening right now is the one worth finding at a glance. */
.today-card.is-now { border-color: var(--accent-500); }
.today-card.is-cancelled.is-now { border-color: var(--sand-300); }
.today-card.is-done { opacity: .74; }
.today-card.is-done:hover { opacity: 1; }

.today-body { padding: .85rem 1rem .95rem; flex: 1 1 auto; }
.today-code { font-size: 1.02rem; font-weight: 800; color: var(--brand-800); }
.today-title { margin-top: .05rem; font-size: .85rem; color: var(--ink); }
.today-meta { margin-top: .3rem; font-size: .77rem; color: var(--ink-soft); }

.today-times { margin-top: .6rem; display: flex; flex-wrap: wrap; gap: .3rem; }
.today-slot {
  padding: .16rem .5rem .2rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--field);
  font-size: .73rem;
  font-weight: 700;
  color: var(--brand-800);
  white-space: nowrap;
}
.today-slot.is-now { border-color: var(--accent-500); color: var(--accent-700); }

/* Status on the left, the roll-call action on the right. */
.today-foot-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin: .85rem 1rem 0;
  padding: .55rem 0 .8rem;
  border-top: 1px solid var(--sand-300);
}

.today-foot-row .btn { margin-left: auto; flex: 0 0 auto; }

.today-foot {
  min-width: 0;
  font-size: .77rem;
  font-weight: 700;
  color: var(--brand-700);
}
.today-foot.is-taken   { color: var(--ink-soft); }
/* Over, and never called. Worth catching from across the page. */
.today-foot.is-missed  { color: var(--accent-700); font-weight: 800; }
.today-foot.is-noclass { color: var(--accent-700); }

/* The address bar. A control, so it takes --field like every other input. */
.crumbbar {
  width: 100%;
  min-width: 0;
  min-height: 38px;
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .4rem .9rem;
  background: var(--field);
  border: 1px solid var(--rule);
  border-radius: 10px;
}

.crumbbar-mark {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  object-fit: contain;
}

.crumbs ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: .45rem;
  font-size: .82rem;
  min-width: 0;
  /* On a narrow screen the trail scrolls rather than wrapping or truncating,
     and setCrumbs pins it to the right so "where you are" stays in view. */
  overflow-x: auto;
  scrollbar-width: none;
}

.crumbs ol::-webkit-scrollbar { display: none; }

/* The trail never wraps inside the pill; the last crumb gives way first. */
.crumbs li { display: flex; align-items: center; gap: .45rem; white-space: nowrap; }
.crumbs li:last-child { min-width: 0; }

.crumbs li + li::before {
  content: "›";
  color: var(--accent-600);
  font-weight: 700;
}

/* Only the trail links underline — the nav buttons are controls, not crumbs. */
.crumbs ol a { color: var(--brand-700); font-weight: 600; }
.crumbs ol a:hover { text-decoration: underline; }

/* Where you are, truncated rather than wrapped when the pill runs out. */
.crumbs .here {
  color: var(--ink-soft);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------- Page ---------------- */

.page {
  flex: 1 1 auto;
  padding: 1.6rem 2rem 3rem;
  min-height: 0;
  overflow: auto;
}

.page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.25rem;
  flex-wrap: wrap;
  padding-bottom: .8rem;
  border-bottom: 3px solid var(--accent-500);
}

.page-head h1 { font-size: 1.6rem; color: var(--brand-800); }

.page-sub { margin: .25rem 0 0; font-size: .88rem; color: var(--ink-soft); }

.head-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

.toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .6rem 1rem;
  padding: 1rem 0 .25rem;
}

.toolbar input[type="search"] { max-width: 26rem; }

/* Roster actions sit at the far right of the search row. */
.toolbar-actions {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-left: auto;
  flex-wrap: wrap;
}

/* With actions present the count sits beside the search instead of being
   pushed right, so only one element claims the free space. */
#panelStudents .toolbar .count { margin-left: 0; }

.count {
  margin-left: auto;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent-700);
  white-space: nowrap;
}

/* ---------------- Tabs ---------------- */

/* Tab strip and the page's actions share one row, and that row's rule is the
   only separator the class page needs. */
.tabs-bar {
  display: flex;
  align-items: flex-end;
  gap: 0;
  flex-wrap: wrap;
  margin-top: .6rem;
  padding-bottom: .5rem;
  border-bottom: 2px solid var(--rule);
}

.tabs-bar .head-actions { padding-bottom: .1rem; }


.tabs {
  display: flex;
  gap: .25rem;
  /* overflow-x alone would make overflow-y compute to `auto`, which paints a
     stray vertical scrollbar on the strip. Pin it shut. */
  overflow-x: auto;
  overflow-y: hidden;
  margin-bottom: -.5rem;
}

.tab {
  position: relative;
  padding: .6rem 1.15rem .65rem;
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-soft);
  white-space: nowrap;
  border-bottom: 3px solid transparent;
  transition: color .12s, background .12s, border-color .12s;
}

.tab:hover { color: var(--brand-700); background: var(--sand-050); }

.tab[aria-selected="true"] {
  color: var(--brand-800);
  border-bottom-color: var(--accent-500);
}

.tab:focus-visible { outline: 3px solid var(--accent-400); outline-offset: -3px; }

/* ---------------- Empty-tab placeholder ---------------- */

.placeholder {
  max-width: 34rem;
  margin: 3.5rem auto;
  text-align: center;
  color: var(--ink-soft);
}

.placeholder-mark {
  display: grid;
  place-items: center;
  width: 3.6rem;
  height: 3.6rem;
  margin: 0 auto .9rem;
  border-radius: 50%;
  background: var(--sand-050);
  border: 2px solid var(--sand-300);
  color: var(--accent-600);
  font-size: 1.6rem;
  line-height: 1;
}

.placeholder h2 {
  font-size: 1.15rem;
  color: var(--brand-800);
  margin-bottom: .4rem;
}

.placeholder p { margin: 0 0 .5rem; font-size: .9rem; }

.placeholder-note {
  font-size: .83rem;
  font-style: italic;
  color: var(--accent-700);
}

/* ---------------- Card grid ---------------- */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: 1.4rem;
  margin-top: 1.5rem;
}

.card {
  display: flex;
  flex-direction: column;
  /* Card bodies sit at page colour, so the outline is what separates them. */
  border: 1px solid var(--sand-300);
  border-radius: 10px;
  background: var(--canvas);
  overflow: hidden;
  cursor: pointer;
  transition: box-shadow .15s, transform .15s, border-color .15s;
}

.card:hover,
.card:focus-visible {
  border-color: var(--accent-500);
  box-shadow: 0 10px 26px rgba(1, 64, 52, .16);
  transform: translateY(-2px);
  outline: none;
}

/* --- Banner: the coloured strip that carries the title --- */

.card-banner {
  position: relative;
  padding: .8rem 1rem .85rem;
  min-height: 5rem;
  color: var(--white);
  overflow: hidden;
}

/* Decorative arc, echoing Google Classroom's illustrated header. */
.card-banner::after {
  content: "";
  position: absolute;
  right: -3.5rem;
  bottom: -5.5rem;
  width: 12rem;
  height: 12rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, .09);
  pointer-events: none;
}

.card-banner::before {
  content: "";
  position: absolute;
  right: 1.2rem;
  top: -3.2rem;
  width: 7rem;
  height: 7rem;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .16);
  pointer-events: none;
}

/* One treatment for every card, so colour never implies a difference that
   isn't there. The other three were dropped along with the rotation. */
.banner-a { background: linear-gradient(135deg, var(--brand-900), var(--brand-700)); }

.card-eyebrow {
  display: block;
  font-size: .64rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--sand-200);
  margin-bottom: .3rem;
}

.card-title {
  position: relative;
  margin: 0;
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -.01em;
  overflow-wrap: break-word;
  padding-right: 4.5rem; /* clears the status flag */
}

.card-meta {
  position: relative;
  margin: .35rem 0 0;
  font-size: .8rem;
  color: rgba(255, 255, 255, .85);
  overflow-wrap: break-word;
}

/* The flag sits on a coloured banner, so it needs its own light treatment
   rather than the status colours it would otherwise inherit. */
.card-flag {
  position: absolute;
  top: .9rem;
  right: .9rem;
  z-index: 1;
  background: var(--sand-050);
  color: var(--brand-800);
  border-color: var(--sand-300);
}

/* --- Body: stats and details --- */

.card-body {
  flex: 1 1 auto;
  padding: .7rem 1rem;
  display: grid;
  gap: .4rem;
}

.card-stats { display: flex; gap: 1.9rem; }

.stat-num {
  display: block;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--brand-700);
  font-variant-numeric: tabular-nums;
}

.stat-label {
  display: block;
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.card-details { display: grid; gap: .3rem; }

.detail {
  display: flex;
  gap: .5rem;
  font-size: .84rem;
  align-items: baseline;
}

.detail-label {
  flex: 0 0 4.6rem;
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--accent-700);
}

.detail-value { color: var(--ink); overflow-wrap: break-word; min-width: 0; }
.detail-value.muted { color: var(--ink-soft); }

/* --- Footer: actions --- */

.card-foot {
  display: flex;
  gap: .4rem;
  align-items: center;
  padding: .5rem 1rem .6rem;
  border-top: 1px solid var(--rule);
  flex-wrap: wrap;
}

.card-foot .spacer { flex: 1 1 auto; }

/* --- "Add new" tile --- */

.card-add {
  display: grid;
  place-content: center;
  gap: .4rem;
  min-height: 11rem;
  padding: 1.5rem;
  text-align: center;
  border: 2px dashed var(--accent-400);
  border-radius: 10px;
  background: var(--canvas);
  color: var(--brand-700);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  transition: background .15s, border-color .15s;
}

.card-add:hover, .card-add:focus-visible {
  background: var(--sand-100);
  border-color: var(--accent-600);
  outline: none;
}

.card-add .plus {
  font-size: 2rem;
  line-height: 1;
  font-weight: 400;
}

.card-add .hint {
  font-size: .74rem;
  font-weight: 600;
  color: var(--ink-soft);
}

/* ---------------- Data grid ---------------- */

.grid {
  width: 100%;
  border-collapse: collapse;
  margin-top: 1.1rem;
  font-size: .9rem;
}

.grid thead th {
  text-align: left;
  padding: .55rem .75rem;
  background: var(--brand-800);
  color: var(--white);
  font-size: .7rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-weight: 700;
  white-space: nowrap;
  border-bottom: 2px solid var(--accent-500);
}

.grid tbody td {
  padding: .6rem .75rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: middle;
  overflow-wrap: break-word;
}

.grid tbody tr { background: var(--surface); }
.grid tbody tr:hover { background: var(--brand-050); }

.num, .grid td.num { text-align: right; font-variant-numeric: tabular-nums; }
.col-actions, .grid td.col-actions { text-align: right; white-space: nowrap; }

.primary-cell {
  font-weight: 700;
  color: var(--brand-700);
  font-size: .95rem;
}

/* ---------------- Student roster (directory list) ---------------- */

/* The class page reads as one centred column rather than stretching edge to
   edge — a roster row has nothing useful to put in 1200px of width. */
#viewClass {
  width: 100%;
  max-width: 900px;
  margin-inline: auto;
}

.roster { margin-top: .9rem; }

.roster-letter {
  padding: .3rem .3rem .22rem;
  background: var(--canvas);
  border-bottom: 2px solid var(--sand-200);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .16em;
  color: var(--accent-700);
}

.roster-group + .roster-group { margin-top: .7rem; }

.roster-row {
  display: flex;
  align-items: center;
  gap: .85rem;
  padding: .5rem .3rem;
  border-bottom: 1px solid var(--sand-100);
  transition: background .12s;
}

.roster-row:hover { background: var(--surface); }

/* Nothing below it to divide from. */
.roster-group.is-single .roster-row { border-bottom: 0; }

.roster-index {
  flex: 0 0 1.6rem;
  text-align: right;
  font-size: .8rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

/* Name and student number stack; the number is the quieter of the two. */
.roster-name {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: .08rem;
  overflow-wrap: break-word;
}

.roster-name .fullname {
  font-size: .95rem;
  color: var(--ink);
}

.roster-name .studentno {
  font-size: .78rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

.roster-status { flex: 0 0 6.5rem; }

.roster-enrolled {
  flex: 0 0 11rem;
  display: flex;
  flex-direction: column;
  gap: .08rem;
  font-size: .78rem;
  color: var(--ink-soft);
}

.roster-enrolled .course-year { color: var(--brand-700); font-weight: 700; }

/* Row selection checkbox, shown only on the database page. */
.roster-pick {
  flex: 0 0 auto;
  width: auto;
  margin: 0;
  accent-color: var(--brand-700);
  cursor: pointer;
}

.roster-row.is-picked { background: var(--brand-050); }

/* The whole row is the hit target for selection, not just the checkbox. */
.roster-row.pickable { cursor: pointer; user-select: none; }
.roster-row.pickable:hover { background: var(--brand-050); }

/* Filter dropdowns sit inline in the toolbar and size to their content. */
.toolbar select {
  width: auto;
  min-width: 9.5rem;
  min-height: 0;
  padding: .32rem .5rem;
  font-size: .85rem;
}

/* ---------------- List bar (select-all + bulk actions) ----------------
   Deliberately flat — a hairline rule, no panel and no background fill. */

.listbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .6rem .9rem;
  margin-top: .9rem;
  padding: .35rem .3rem .5rem;
  border-bottom: 2px solid var(--sand-200);
}

.listbar-check {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .82rem;
  font-weight: 700;
  color: var(--brand-800);
  white-space: nowrap;
  cursor: pointer;
}

.listbar-check input {
  width: auto;
  min-height: 0;
  margin: 0;
  accent-color: var(--brand-700);
  cursor: pointer;
}

.listbar-bulk {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .45rem;
}

.listbar-bulk select {
  width: auto;
  min-width: 9.5rem;
  min-height: 0;
  padding: .25rem .45rem;
  font-size: .8rem;
}

.listbar .count { margin-left: auto; }

.roster-actions { flex: 0 0 auto; display: inline-flex; gap: .3rem; }

/* Narrow screens: drop the index, let the name take the row. */
@media (max-width: 700px) {
  .roster-row { flex-wrap: wrap; gap: .5rem .7rem; }
  .roster-index { display: none; }
  .roster-name { flex: 1 1 60%; order: 1; }
  .avatar { order: 0; }
  .roster-status { flex: 0 0 auto; order: 2; }
  .roster-actions { order: 3; margin-left: auto; }
}

.avatar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .02em;
  border: 1px solid;
}

.avatar-0 { background: var(--brand-700);  color: var(--white);      border-color: var(--brand-800); }
.avatar-1 { background: var(--accent-500); color: var(--white);      border-color: var(--accent-700); }
.avatar-2 { background: var(--brand-100);  color: var(--brand-800);  border-color: var(--brand-600); }
.avatar-3 { background: var(--sand-100);   color: var(--accent-700); border-color: var(--sand-300); }

.mono { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--brand-700); white-space: nowrap; }
.muted { color: var(--ink-soft); }

/* ---------------- Status pills ---------------- */

.pill {
  display: inline-block;
  padding: .12rem .55rem;
  border-radius: 999px;
  font-size: .72rem;
  font-weight: 700;
  border: 1px solid;
  white-space: nowrap;
}

.pill-active       { background: var(--brand-700);  color: var(--white);     border-color: var(--brand-800); }
.pill-inactive     { background: var(--field);      color: var(--ink-soft);  border-color: var(--rule); }
.pill-transferred  { background: var(--sand-100);   color: var(--accent-700); border-color: var(--sand-300); }
.pill-dropped      { background: var(--accent-050); color: var(--accent-700); border-color: var(--accent-500); }

/* A student who has left, shown in the day editor for the record only: the
   mark they were given that day stays readable and stays untouchable. */
/* The roster's status filter sits at the far end of the strip, where the
   count sits on every other list bar. */
#rosterStatusSelect { margin-left: auto; }

.day-row.is-left { opacity: .6; }
.day-row.is-left select { pointer-events: none; }

.empty {
  margin: 2.5rem 0 0;
  text-align: center;
  color: var(--ink-soft);
  font-style: italic;
}

/* ---------------- Buttons ---------------- */

/* `.btn { display }` would otherwise beat the UA's [hidden] rule. */
[hidden] { display: none !important; }

.btn {
  display: inline-block;
  font: inherit;
  font-size: .82rem;
  font-weight: 700;
  line-height: 1.35;
  border-radius: var(--radius);
  border: 1px solid transparent;
  padding: .38rem .8rem;
  cursor: pointer;
  white-space: nowrap;
  transition: filter .12s, background .12s;
}

.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn:not(:disabled):hover { filter: brightness(1.08); }

.btn:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
.grid tbody tr:focus-visible {
  outline: 3px solid var(--accent-400);
  outline-offset: 1px;
}

.btn-primary    { background: var(--brand-700);  color: var(--white); border-color: var(--brand-800); }
.btn-accent   { background: var(--accent-400); color: var(--brand-900); border-color: var(--accent-600); }
/* Outline buttons sit beside the green primary, so they take a green outline
   to match it. The exception is further down: row-level Edit, which pairs with
   a terracotta Delete and keeps a terracotta outline. */
.btn-ghost  { background: var(--field);    color: var(--brand-700); border-color: var(--brand-600); }
.btn-danger { background: var(--accent-600); color: var(--white); border-color: var(--accent-700); }

.btn-sm { padding: .22rem .55rem; font-size: .72rem; }

/* Row actions pair Edit with a terracotta Delete, not a green primary. */
.roster-actions .btn-ghost,
.row-btns .btn-ghost { border-color: var(--accent-500); }

.row-btns { display: inline-flex; gap: .3rem; }

/* ---------------- Dropdown menu ---------------- */

.menu-wrap { position: relative; display: inline-block; }

.menu {
  position: absolute;
  right: 0;
  top: calc(100% + .3rem);
  z-index: 30;
  min-width: 12rem;
  padding: .25rem;
  display: grid;
  gap: .1rem;
  background: var(--field);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: 0 10px 26px rgba(1, 64, 52, .2);
}

.menu button {
  font: inherit;
  font-size: .85rem;
  text-align: left;
  padding: .45rem .6rem;
  border: none;
  border-radius: var(--radius);
  background: none;
  color: var(--ink);
  cursor: pointer;
}

.menu button:hover { background: var(--sand-050); color: var(--brand-800); }
.menu button:focus-visible { outline: 2px solid var(--accent-500); outline-offset: -2px; }

/* ---------------- Attendance roll call ---------------- */

.roll-top { display: flex; align-items: flex-end; gap: 1.3rem; flex-wrap: wrap; }
.roll-date { flex: 0 0 12rem; }
.roll-noclass { padding-bottom: .55rem; }

.roll-note { margin: .55rem 0 0; font-size: .82rem; color: var(--ink-soft); }

/* Progress, count and shortcuts read as one band, divided from the setup
   above it by a hairline rather than by guesswork spacing. */
.roll-meter {
  margin-top: 1rem;
  padding-top: .9rem;
  border-top: 1px solid var(--rule);
}

.roll-meter-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .7rem;
  margin-top: .5rem;
}

/* Locked: an existing record is on file and overwriting hasn't been agreed to
   yet. Dimmed, but still clickable — the click re-asks instead of marking. */
.roll-marks.is-locked .mark-btn,
.roll-bulk.is-locked .btn {
  opacity: .4;
}

.roll-marks.is-locked .mark-btn:hover,
.roll-bulk.is-locked .btn:hover {
  opacity: .55;
}

/* Overwriting a saved day is the one destructive thing this dialog can do, so
   the notice carries terracotta rather than sitting in the quiet grey. */
.roll-note.is-warning {
  padding-left: .55rem;
  border-left: 3px solid var(--accent-500);
  color: var(--accent-700);
  font-weight: 700;
}

.roll-progress {
  height: 5px;
  border-radius: 999px;
  background: var(--sand-200);
  overflow: hidden;
  margin-top: .4rem;
}

.roll-progress-fill {
  height: 100%;
  width: 0;
  background: var(--brand-600);
  transition: width .18s;
}

.roll-count {
  margin: 0;
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--accent-700);
}

.roll-card {
  display: flex;
  align-items: center;
  gap: .9rem;
  padding: 1.1rem .2rem .9rem;
}

.roll-card .avatar { width: 3.2rem; height: 3.2rem; font-size: 1.05rem; }

.roll-name { margin: 0; font-size: 1.35rem; font-weight: 700; color: var(--brand-800); }
.roll-no { margin: .1rem 0 0; font-size: .82rem; color: var(--ink-soft); }

/* Bulk marking, sitting with the roll call rather than in the footer. */
.roll-bulk {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .4rem;
  margin: 0;
}

.roll-bulk-label {
  margin-right: .2rem;
  font-size: .66rem;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.roll-marks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: .5rem;
}

/* Big targets — this gets tapped once per student. */
.mark-btn {
  font: inherit;
  font-size: .9rem;
  font-weight: 700;
  padding: .75rem .5rem;
  border-radius: var(--radius);
  border: 1px solid var(--rule);
  background: var(--field);
  color: var(--ink);
  cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}

.mark-btn:hover { border-color: var(--brand-600); background: var(--brand-050); }
.mark-btn:focus-visible { outline: 3px solid var(--accent-400); outline-offset: 1px; }

.mark-btn[data-mark="Present"].is-on { background: var(--brand-700); border-color: var(--brand-800); color: var(--white); }
.mark-btn[data-mark="Late"].is-on    { background: var(--sand-300); border-color: var(--sand-300); color: var(--brand-900); }
.mark-btn[data-mark="Excused"].is-on { background: var(--accent-100); border-color: var(--accent-500); color: var(--accent-700); }
.mark-btn[data-mark="Absent"].is-on  { background: var(--accent-600); border-color: var(--accent-700); color: var(--white); }

.roll-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
  padding-top: .9rem;
}

.roll-current { font-size: .78rem; color: var(--ink-soft); }

.roll-summary {
  margin: 0 0 .6rem;
  font-size: .88rem;
  color: var(--brand-800);
  font-weight: 700;
}

/* ---------------- Attendance history ---------------- */

.history { margin-top: .5rem; }

/* Week heading: flat rule, matching the roster's letter headings. */
.week-head {
  display: flex;
  align-items: baseline;
  gap: .7rem;
  flex-wrap: wrap;
  margin-top: 1.4rem;
  padding: .3rem .3rem .25rem;
  border-bottom: 2px solid var(--sand-200);
}

.week-head:first-child { margin-top: .4rem; }

.week-label {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent-700);
}

.week-range { font-size: .78rem; color: var(--ink-soft); }

.week-meetings {
  margin-left: auto;
  font-size: .74rem;
  font-weight: 700;
  color: var(--ink-soft);
  white-space: nowrap;
}

.week-meetings.is-short { color: var(--accent-700); }

/* Attendance days (v1.3): plain paper rows, no banner wash. The date leaf,
   the counts in words, a thin bar split by mark, and the share who came. A
   cancelled day is muted and says its kind in coloured text, not a badge. */
.att-list { display: grid; gap: .55rem; margin-top: .9rem; }

.att-day {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .75rem .6rem .75rem .8rem;
  border: 1px solid var(--sand-200);
  border-radius: 12px;
  background: #FFFDFA;
  box-shadow: 0 1px 0 #EDE2D2;
  cursor: pointer;
  transition: border-color .12s, box-shadow .12s;
}
.att-day:hover, .att-day:focus-visible {
  border-color: #B9CFC4;
  box-shadow: 0 3px 10px rgba(1, 64, 52, .08);
  outline: none;
}
.att-day:focus-visible { outline: 2px solid var(--brand-700); outline-offset: 2px; }
.att-day.is-noclass { background: #F8F2E9; border-style: dashed; border-color: #D9C9B2; box-shadow: none; }

.att-leaf {
  flex: 0 0 auto;
  display: grid;
  justify-items: center;
  align-content: center;
  gap: .08rem;
  width: 3.3rem;
  padding: .35rem .2rem .4rem;
  border-radius: 9px;
  background: #FFFFFF;
  border: 1px solid var(--sand-200);
  line-height: 1;
}
.leaf-day { font-size: .58rem; font-weight: 800; letter-spacing: .1em; color: var(--brand-700); }
.leaf-num { font-family: Fredoka, Nunito, sans-serif; font-size: 1.35rem; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.leaf-mon { font-size: .55rem; font-weight: 800; letter-spacing: .09em; color: var(--ink-soft); }
.att-day.is-noclass .att-leaf { background: transparent; }
.att-day.is-noclass .leaf-day { color: var(--ink-soft); }

.att-main { flex: 1 1 auto; min-width: 0; display: grid; gap: .3rem; }
.att-line { display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap; }
.att-date { font-family: Fredoka, Nunito, sans-serif; font-size: 1.02rem; font-weight: 600; color: var(--ink); }

/* The kind of a cancelled day, as text. */
.att-kind { font-size: .72rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: #8A3A26; }
.att-kind.is-faculty { color: #4F529A; }
.att-kind.is-notmet { color: var(--ink-soft); }

.att-stats { display: flex; gap: .3rem .9rem; flex-wrap: wrap; font-size: .82rem; }
.stat-present { color: var(--brand-700); font-weight: 800; }
.stat-late    { color: #8A5A00; font-weight: 800; }
.stat-excused { color: #4F529A; font-weight: 800; }
.stat-absent  { color: #8A3A26; font-weight: 800; }
.stat-muted   { color: var(--ink-soft); }

.att-bar { display: flex; gap: 2px; max-width: 420px; height: 6px; border-radius: 3px; overflow: hidden; background: #ECE2D4; }
.att-bar > span { display: block; height: 100%; }
.att-bar .is-present { background: #2F8A6B; }
.att-bar .is-late { background: #E2A33B; }
.att-bar .is-excused { background: #8E91C9; }
.att-bar .is-absent { background: #C4483C; }

.att-reason {
  margin: 0;
  font-size: .82rem;
  font-style: italic;
  color: #3E4B45;
  overflow-wrap: break-word;
}

.att-rate { flex: 0 0 auto; display: grid; justify-items: end; min-width: 4.5rem; padding: 0 .4rem; line-height: 1.1; }
.att-rate b { font-family: Fredoka, Nunito, sans-serif; font-size: 1.45rem; font-weight: 600; }
.att-rate span { font-size: .72rem; color: var(--ink-soft); }
.att-rate.is-good b { color: #015443; }
.att-rate.is-mid b { color: #8A5A00; }
.att-rate.is-low b { color: #8A3A26; }

.att-actions { flex: 0 0 auto; display: flex; align-items: center; padding-left: .5rem; border-left: 1px solid #ECE2D4; }
.att-del {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  border: 0; border-radius: 8px; background: none;
  color: #A08F7A; cursor: pointer;
}
.att-del:hover, .att-del:focus-visible { color: #8A3A26; background: #F6EBE4; }

@media (max-width: 560px) {
  .att-day { gap: .7rem; padding: .65rem .4rem .65rem .6rem; }
  .att-rate { min-width: 0; padding: 0; }
  .att-rate b { font-size: 1.15rem; }
  .att-actions { padding-left: .2rem; border-left: 0; }
}

/* Per-student rows inside the day modal. Centre-aligned, since a baseline
   against a taller select drops the checkbox out of line. */
.day-row { align-items: center; }

.day-row select {
  flex: 0 0 8rem;
  width: auto;
  min-height: 0;
  padding: .2rem .4rem;
  font-size: .78rem;
}

.tally {
  font-size: .72rem;
  font-weight: 700;
  padding: .1rem .5rem;
  border-radius: 999px;
  border: 1px solid;
  white-space: nowrap;
}

.tally-present { background: var(--brand-050); color: var(--brand-800); border-color: var(--brand-600); }
.tally-late    { background: var(--sand-050);  color: var(--accent-700); border-color: var(--sand-300); }
.tally-excused { background: var(--accent-050); color: var(--accent-700); border-color: var(--accent-100); }
.tally-absent  { background: var(--accent-050); color: var(--accent-700); border-color: var(--accent-500); }

.h-Present { color: var(--brand-700); }
.h-Late    { color: var(--accent-700); }
.h-Excused { color: var(--accent-700); }
.h-Absent  { color: var(--accent-600); }

/* ---------------- Import preview ---------------- */

.import-help {
  margin: 0;
  font-size: .84rem;
  color: var(--ink-soft);
  line-height: 1.55;
}

/* .modal-body p holds source line breaks so a confirm can shape its own
   wording. This prose is meant to reflow to the dialog's width instead of the
   width it happens to be typed at, and needs the matching specificity to win. */
.modal-body p.import-help { white-space: normal; }

.import-help code {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: .92em;
}

.import-summary {
  margin: 0;
  padding: .5rem .7rem;
  border-radius: var(--radius);
  background: var(--brand-050);
  border-left: 4px solid var(--brand-600);
  font-size: .86rem;
  color: var(--brand-800);
}

.import-toolbar {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding-bottom: .5rem;
}

.import-count {
  margin-left: auto;
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--accent-700);
}

/* Flat scrolling list — hairline dividers only, no panel around it. */
.import-rows {
  max-height: 20rem;
  overflow-y: auto;
  border-top: 1px solid var(--rule);
}

.import-row {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  padding: .35rem .6rem;
  border-bottom: 1px solid var(--sand-100);
  font-size: .86rem;
}

.import-row:last-child { border-bottom: none; }

/* Without min-height:0 the checkbox inherits the 2.45rem control height and
   pushes the row to two lines. */
.import-row input[type="checkbox"] {
  width: auto;
  min-height: 0;
  flex: 0 0 auto;
  margin: 0;
  accent-color: var(--brand-700);
}
.import-row .imp-index { flex: 0 0 1.8rem; text-align: right; color: var(--ink-soft); font-size: .76rem; }
.import-row .imp-name { flex: 1 1 auto; min-width: 0; }
.import-row .imp-name b { font-weight: 700; }
.import-row .imp-note { flex: 0 0 auto; font-size: .74rem; font-style: italic; color: var(--accent-700); }
.import-row.is-dupe { background: var(--sand-050); }
.import-row.is-dupe .imp-name { color: var(--ink-soft); }

/* ---------------- Forms ---------------- */

input, select {
  font: inherit;
  width: 100%;
  /* Inputs and selects render at slightly different intrinsic heights; pin
     both so side-by-side fields line up exactly. */
  min-height: 2.45rem;
  line-height: 1.4;
  padding: .45rem .6rem;
  /* Fields are near the page colour, so the outline is what defines them. */
  border: 1px solid var(--sand-300);
  border-radius: var(--radius);
  background: var(--field);
  color: var(--ink);
}

/* Left to the browser a textarea comes out monospace with its own border and a
   drag handle. Match the inputs, and grow by scrolling rather than by resizing
   — a dragged-out box pushes the buttons off the bottom of the dialog. */
textarea {
  font: inherit;
  width: 100%;
  line-height: 1.45;
  padding: .45rem .6rem;
  border: 1px solid var(--sand-300);
  border-radius: var(--radius);
  background: var(--field);
  color: var(--ink);
  resize: none;
  overflow-y: auto;
}

input::placeholder, textarea::placeholder { color: #97977E; }

/* Fields sharing a line. The children carry their own flex basis. */
.form-row { display: flex; flex-wrap: wrap; gap: .75rem; }
.form-row > .field { min-width: 0; }

/* A named division inside a long dialog: a hairline and a small label, with
   any note and control riding along on the same line. */
.form-sec {
  display: flex;
  align-items: center;
  gap: .7rem;
  margin-top: .3rem;
  padding-top: .8rem;
  border-top: 1px solid var(--rule);
}

.form-sec-note {
  margin-left: auto;
  font-size: .76rem;
  font-style: italic;
  color: var(--ink-soft);
}

/* A section at the very top of a dialog has nothing above it to divide. */
.form-sec.is-first { margin-top: 0; padding-top: 0; border-top: 0; }

/* `align-content: start` keeps rows at their natural height. Without it a
   field carrying a hint line stretches its neighbour's control, which is what
   knocked Student No. and Status out of line. */
.field { display: grid; gap: .25rem; align-content: start; }

.field > span {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--brand-700);
}

.req { color: var(--accent-600); }

.field-hint {
  font-size: .74rem;
  color: var(--ink-soft);
  font-style: italic;
}

.input-mask {
  font-variant-numeric: tabular-nums;
  letter-spacing: .08em;
  font-weight: 600;
}

.field-row { display: flex; gap: .75rem; flex-wrap: wrap; }
.field-row > .field { flex: 1 1 9rem; min-width: 0; }

/* ---------------- Schedule builder ---------------- */

/* An outline, not a fill: the block sits at the dialog's own colour so it
   reads as part of the form rather than a panel dropped into it. */
.sched-fieldset {
  border: 1px solid var(--sand-300);
  border-radius: var(--radius);
  padding: .3rem .9rem .9rem;
  margin: 0;
  background: none;
}

.sched-fieldset legend {
  padding: 0 .45rem;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--brand-700);
}

.sched-help {
  margin: 0 0 .7rem;
  font-size: .78rem;
  color: var(--ink-soft);
}

.sched-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: .75rem;
  padding: .7rem 0;
  border-top: 1px dashed var(--rule);
}

.sched-row:first-child { border-top: none; padding-top: .2rem; }

/* Course and the code that course uses, side by side and equally weighted. */
.class-course-row > .field { flex: 1 1 10rem; min-width: 0; }
.class-course-row > .btn { flex: 0 0 auto; }

/* Days take the slack so the whole block sits on one line and fills the width. */
.sched-days { flex: 1 1 21rem; min-width: 0; }

/* Matched to the height of the time boxes it sits beside. The row aligns on
   its bottom edge, so equal heights are what puts their centres on one line —
   a shorter button would hang below them. */
.sched-row .btn-danger {
  align-self: flex-end;
  height: 2.45rem;
  display: inline-flex;
  align-items: center;
}

.sched-label {
  display: block;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--brand-700);
  margin-bottom: .3rem;
}

.day-boxes { display: flex; flex-wrap: wrap; gap: .28rem; }

/* Room: a short free-text box, as tall as the time selects beside it. */
.sched-room { flex: 0 1 8rem; min-width: 6rem; }
.sched-room input { width: 100%; height: 2.45rem; box-sizing: border-box; }

/* Checkbox styled as a toggle chip; the real input stays for a11y. */
.day-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.6rem;
  padding: .32rem .45rem;
  border: 1px solid var(--sand-300);
  border-radius: 999px;
  background: var(--field);
  color: var(--ink-soft);
  font-size: .76rem;
  font-weight: 700;
  cursor: pointer;
  user-select: none;
  transition: background .12s, color .12s, border-color .12s;
}

.day-chip input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}

.day-chip:hover { background: var(--sand-100); }

.day-chip:has(input:checked) {
  background: var(--brand-700);
  border-color: var(--brand-800);
  color: var(--white);
}

.day-chip:has(input:focus-visible) {
  outline: 3px solid var(--accent-400);
  outline-offset: 1px;
}

.sched-time { flex: 0 1 8.5rem; min-width: 7rem; }
.sched-time select { font-variant-numeric: tabular-nums; }

.sched-remove { flex: 0 0 auto; margin-bottom: .05rem; }

.form-error {
  margin: 0;
  padding: .5rem .7rem;
  border-radius: var(--radius);
  background: var(--brand-050);
  border-left: 4px solid var(--brand-600);
  color: var(--brand-800);
  font-size: .86rem;
  font-weight: 600;
}

/* ---------------- Modals ---------------- */

.modal {
  width: min(600px, calc(100vw - 2rem));
  padding: 0;
  border: none;
  border-radius: var(--radius);
  box-shadow: 0 24px 60px rgba(1, 64, 52, .28);
  background: var(--canvas);
  color: var(--ink);
}

.modal-wide { width: min(720px, calc(100vw - 2rem)); }
.modal-xl { width: min(900px, calc(100vw - 2rem)); }
.modal-narrow { width: min(430px, calc(100vw - 2rem)); }

.modal::backdrop { background: rgba(20, 32, 28, .5); }

.modal-head {
  padding: .85rem 1.25rem;
  background: var(--brand-700);
  border-bottom: 3px solid var(--accent-500);
  color: var(--white);
}

.modal-head h2 { font-size: 1.02rem; }
.modal-head.danger { background: var(--accent-600); border-bottom-color: var(--accent-700); }

.modal-body { padding: 1.1rem 1.25rem; display: grid; gap: .85rem; }
/* pre-line so a confirm can put its reassurance on a line of its own. */
.modal-body p { margin: 0; white-space: pre-line; }

/* A chooser puts one button per answer here, so the row has to wrap. */
.modal-foot.is-choices { flex-wrap: wrap; }

.modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: .6rem;
  padding: .8rem 1.25rem;
  background: var(--canvas);
  border-top: 1px solid var(--rule);
}

/* ---------------- Classwork details ---------------- */

/* Read-only, and it should look it: nothing in here is a box you can type in.
   One column top to bottom, because this is read rather than scanned. */
#cwViewBody { display: grid; gap: 1.1rem; }

.cwv-facts { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }

.cwv-block { display: grid; gap: .3rem; }

.cwv-label {
  margin: 0;
  font-size: .72rem;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* pre-line, so the paragraphs and numbered questions typed into the form come
   back out with the shape they were written in. */
.cwv-prose {
  margin: 0;
  white-space: pre-line;
  line-height: 1.5;
  color: var(--ink);
}

.cwv-prose.is-blank { color: var(--ink-soft); font-style: italic; }

/* The heading of the criteria list carries the arithmetic beside it. */
.cwv-crit-head { display: flex; align-items: baseline; gap: .6rem; }

.cwv-sum { font-size: .76rem; font-weight: 700; color: var(--brand-700); }

.cwv-crit-desc { margin: 0; font-size: .85rem; line-height: 1.45; color: #3E4B45; }
.cwv-crit-desc.is-blank { color: var(--ink-soft); font-style: italic; }

/* What was left empty, said once. */
.cwv-blankline { margin: -.4rem 0 0; font-size: .85rem; font-style: italic; color: var(--ink-soft); }

/* "Group activity" in the facts line, as text with an icon. */
.cw-bit.is-group { color: var(--brand-700); font-weight: 800; }

/* The criteria, read-only, laid out like the rubric table in the editor. */
.cwv-table { margin-top: .3rem; border: 1px solid #DCCDB8; border-radius: 12px; overflow: hidden; background: #FFFDFA; }
.cwv-trow {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 64px;
  align-items: start;
  border-top: 1px solid #EDE3D5;
}
.cwv-table.is-group .cwv-trow { grid-template-columns: 36px minmax(0, 1fr) 130px 64px; }
.cwv-table:not(.is-group) .cwv-twho,
.cwv-table:not(.is-group) .cwv-trow.is-head > :nth-child(3) { display: none; }
.cwv-trow.is-head {
  align-items: center; min-height: 32px; border-top: 0;
  background: var(--ink); color: #FFFFFF;
  font-size: .66rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
}
.cwv-trow.is-head > * { padding: 0 .6rem; }
.cwv-trow.is-head > :first-child { padding: 0; text-align: center; }
.cwv-trow.is-head > :last-child { text-align: right; padding-right: .9rem; }
.cwv-trow > * + * { border-left: 1px solid #F1E7D8; }
.cwv-trow.is-head > * + * { border-left: 0; }
.cwv-trow:nth-child(even of .cwv-trow:not(.is-head)) { background: #FFFBF4; }
.cwv-tno { padding: .7rem 0; text-align: center; font-size: .8rem; font-weight: 800; color: var(--ink-soft); border-left: 0; }
.cwv-twhat { display: grid; gap: .2rem; padding: .65rem .7rem; min-width: 0; }
.cwv-twhat > b { font-size: .92rem; }
.cwv-twho { align-self: stretch; display: flex; align-items: center; justify-content: center; padding: .5rem; font-size: .8rem; font-weight: 800; text-align: center; cursor: help; }
.cwv-twho.is-you { color: #015443; }
.cwv-twho.is-whole { color: #A2561A; }
.cwv-twho.is-peer { color: #4F529A; }
.cwv-tpts { padding: .65rem .9rem .65rem .5rem; text-align: right; font-size: 1rem; color: var(--ink); font-variant-numeric: tabular-nums; }
.cwv-tsec {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  min-height: 38px; padding: 0 .9rem 0 .8rem;
  border-top: 1px solid #DCCDB8; background: #E9F3EE; color: var(--brand-700);
}
.cwv-tsec-title { font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1rem; }
.cwv-ttotal {
  display: flex; align-items: center; justify-content: space-between;
  min-height: 44px; padding: 0 .9rem 0 1rem;
  border-top: 2px solid var(--ink); background: #F6EEE2; font-size: .85rem;
}
.cwv-ttotal-n { font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1.3rem; color: var(--brand-700); }

@media (max-width: 560px) {
  .cwv-table.is-group .cwv-trow, .cwv-trow { grid-template-columns: 30px minmax(0, 1fr) 52px; }
  .cwv-table.is-group .cwv-twho { grid-column: 2; grid-row: 2; justify-content: flex-start; padding: 0 .7rem .6rem; border-left: 0; }
  .cwv-table.is-group .cwv-tpts { grid-column: 3; grid-row: 1; }
  .cwv-trow.is-head > :nth-child(3) { display: none; }
}

/* ---------------- Faculty absence ---------------- */

/* The two cancelled days that are not a plain holiday. Both carry every
   is-noclass rule -- they are still days the class did not meet -- and are
   ruled rather than filled on top of it, so the eye finds them on a page full
   of holidays without either of them shouting. The ruling runs one way for a
   day that was the students' doing and the other for a day that was yours. */
.cal-cell.is-noclass.is-notmet,
.cal-swatch.is-noclass.is-notmet,
.mk-head.is-noclass.is-notmet {
  background-image: repeating-linear-gradient(
    45deg, transparent 0 3px, rgba(255, 255, 255, .5) 3px 6px);
}

.cal-cell.is-noclass.is-faculty,
.cal-swatch.is-noclass.is-faculty,
.mk-head.is-noclass.is-faculty {
  background-image: repeating-linear-gradient(
    -45deg, transparent 0 3px, rgba(255, 255, 255, .5) 3px 6px);
}

.mk.mk-nc.is-faculty { font-style: italic; }
.mk.mk-nc.is-notmet { font-weight: 800; }

/* The two halves of the day, next to Select all. */
.nc-half { display: inline-flex; gap: .35rem; }

/* ---------------- What the letters mean ---------------- */

/* Under the register and under the calendar: the same vocabulary explained the
   same way in both, so a letter learned in one is a letter read in the other.
   Wraps rather than scrolls -- it is the one thing on the page allowed to take
   a second line. */
.mark-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .3rem .9rem;
  margin: .6rem .15rem .2rem;
  font-size: .73rem;
  color: var(--ink-soft);
}

.mark-key { display: inline-flex; align-items: center; gap: .35rem; }

/* The chip wears the mark's own colour, so the legend matches the grid cell
   rather than merely naming it. */
.mark-chip {
  display: inline-grid;
  place-items: center;
  min-width: 1.5rem;
  padding: .05rem .25rem;
  border-radius: 4px;
  font-size: .7rem;
  font-weight: 800;
  line-height: 1.5;
}

/* ------------- Already recorded, in the no-class dialog ------------- */

/* A class the chosen days already hold something for. Greyed rather than
   hidden: it is still a class on that day, and writing over it is allowed --
   it just should not happen by not noticing. */
.import-row.is-already { background: var(--sand-050); }

.nc-already {
  margin-left: .5rem;
  padding: .05rem .4rem;
  font-size: .68rem;
  font-weight: 700;
  white-space: nowrap;
  border-radius: 999px;
  background: var(--sand-100);
  border: 1px solid var(--sand-300);
  color: var(--ink-soft);
}

/* A roll call cannot be written over, so the row is out of the question
   rather than merely unticked. */
.nc-already.is-blocked {
  background: var(--accent-050);
  border-color: var(--accent-500);
  color: var(--accent-700);
}

.nc-done {
  margin: .5rem .15rem 0;
  font-size: .76rem;
  font-weight: 700;
  color: var(--ink-soft);
}

/* The check itself failed, so the absence of chips means nothing. */
.nc-done.is-warning { color: var(--accent-700); }

/* ---------------- Toasts ---------------- */

.toast-stack {
  position: fixed;
  right: 1.1rem;
  bottom: 1.1rem;
  display: grid;
  gap: .5rem;
  z-index: 50;
  max-width: min(24rem, calc(100vw - 2rem));
}

.toast {
  padding: .6rem .9rem;
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(1, 64, 52, .16);
  font-size: .88rem;
  font-weight: 600;
  border-left: 5px solid;
  background: var(--field);
  overflow-wrap: anywhere;
}

.toast-ok    { color: var(--brand-700);  border-color: var(--brand-600); }
.toast-error { color: var(--accent-700); border-color: var(--accent-500); }

/* ---------------- Responsive ---------------- */

/* Below this width the grids scroll sideways instead of widening the page. */
@media (max-width: 1000px) {
  .topbar, .page { padding-left: 1rem; padding-right: 1rem; }
  /* The nav bar keeps its tight gutter here too, so the outer buttons stay
     hard against the edge instead of being inset further than on desktop. */
  .crumbs { padding-left: .5rem; padding-right: .5rem; }

  .view { min-width: 0; }

  .grid {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
  }
  .grid thead, .grid tbody { display: table; width: 100%; min-width: 52rem; }

  /* These three already sit in their own scroll wrapper and must stay real
     tables. Turning a table into a block makes its head and body lay out
     independently, so the columns stop lining up with each other -- and the
     52rem floor above then pushes a dialog's table off its own edge. */
  .grid.register-grid, .grid.schedule-grid, .grid.papers-table {
    display: table;
    white-space: normal;
  }
  .grid.register-grid thead, .grid.schedule-grid thead, .grid.papers-table thead {
    display: table-header-group; width: auto; min-width: 0;
  }
  .grid.register-grid tbody, .grid.schedule-grid tbody, .grid.papers-table tbody {
    display: table-row-group; width: auto; min-width: 0;
  }
}

/* Below this the schedule block can't hold one line — let days take their own. */
@media (max-width: 820px) {
  .sched-days { flex: 1 1 100%; }
}

/* Keep the header on one tidy line once space gets tight. */
@media (max-width: 560px) {
  .topbar { padding-top: .55rem; padding-bottom: .55rem; gap: .6rem; }
  .brand { gap: .55rem; }
  .brand-mark { width: 32px; height: 32px; }
  .brand-mark svg { width: 19px; height: 19px; }
  .brand-name { font-size: .95rem; line-height: 1.2; }
  .brand-sub { display: none; }
  .topbar-nav { width: 32px; height: 32px; }
  .active-year-label { font-size: .58rem; letter-spacing: .08em; }
  .badge-accent { font-size: .78rem; padding: .18rem .6rem; white-space: nowrap; }
  .page-head h1 { font-size: 1.35rem; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------------- Grade sheet ----------------
   The college's paper form, sitting in the same space the register does.
   This one block deliberately steps outside the cream palette: it is white
   paper with black rules, because that is what it is a picture of. One
   printed page is shown at a time, stepped through with the arrows. */

/* The sheet is a picture of a 960px page and the class column is 900, so the
   whole view bleeds 30px either side rather than the page being shrunk to
   fit. Narrower windows fall back to scrolling it. */
#gsView { margin-inline: -40px; }

/* Three columns of equal outer weight, so the pager lands on the middle of
   the bar rather than being pushed off centre by the actions beside it. */
/* Held to the page's own width and centred on it, so the actions end exactly
   where the sheet's right edge does — the view is bled wider than the page,
   and left to itself the bar would run past it. */
.gs-nav {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: .5rem;
  width: 960px;
  max-width: 100%;
  margin: 1rem auto 0;
}

.gs-nav-side { min-width: 0; }
.gs-pager { display: flex; align-items: center; gap: .8rem; }

.gs-nav-label {
  min-width: 8.5rem;
  font-size: .82rem;
  font-weight: 700;
  text-align: center;
  color: var(--brand-800);
}

.gs-nav-actions {
  display: flex;
  align-items: center;
  justify-self: end;
  gap: .4rem;
  min-width: 0;
}

/* Icon buttons, sized to sit level with the paper dropdown beside them. */
.gs-tool {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid var(--brand-600);
  border-radius: var(--radius);
  background: none;
  color: var(--brand-700);
  cursor: pointer;
  transition: background .12s, color .12s;
}

.gs-tool:hover { background: var(--brand-050); }
.gs-tool:focus-visible { outline: 3px solid var(--accent-400); outline-offset: 1px; }
.gs-tool.is-primary { background: var(--brand-700); border-color: var(--brand-800); color: var(--white); }
.gs-tool.is-primary:hover { background: var(--brand-600); }

/* Terracotta, because it throws work away — the one signal an icon-only
   button can give before it is pressed, and the only one a tablet gets,
   since there is no hover there to show a tooltip. */
.gs-tool.is-danger { border-color: var(--accent-500); color: var(--accent-700); }
.gs-tool.is-danger:hover { background: var(--accent-050); }

/* The artwork is black PNG, so it is worn as a mask and takes its colour from
   the button — the same trick the nav icons use. */
.gs-tool-img {
  display: block;
  width: 17px;
  height: 17px;
  background: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* A button that carries artwork beside its label. */
.btn.has-icon { display: inline-flex; align-items: center; gap: .35rem; }

.btn-icon {
  display: block;
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  background: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

.i-excel  { -webkit-mask-image: url("assets/excel.png");     mask-image: url("assets/excel.png"); }
.i-pdf    { -webkit-mask-image: url("assets/exportPDF.png"); mask-image: url("assets/exportPDF.png"); }
.i-revert { -webkit-mask-image: url("assets/revert.png");    mask-image: url("assets/revert.png"); }

/* Sized to the icon buttons beside it, so the three read as one group. */
.gs-paper {
  width: auto;
  min-width: 9rem;
  height: 2rem;
  min-height: 0;
  padding: 0 .45rem;
  font-size: .76rem;
}

.gs-pages { display: flex; justify-content: center; padding: 1.1rem 0 .5rem; overflow-x: auto; }

/* A 960-wide page holding two sheets. The old system left 96px either side
   of the fold and set 19px type, which reads far larger than its own
   printed sheet does; the gap is narrower here and the type sized to the
   sheet, so a full name fits its column without being clipped. */
.gs-page {
  position: relative;
  display: grid;
  /* minmax(0,…) or a long unbroken name would push its column wider than
     the page, because a grid track will not shrink below min-content. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* The side margins are half the fold, so the space outside a sheet and the
     space from a sheet to the centre line are the same 36px. */
  gap: 72px;
  box-sizing: border-box;
  width: 960px;
  flex: 0 0 auto;
  padding: 28px 36px;
  background: #FFF;
  border: 1px solid var(--sand-300);
  box-shadow: 0 6px 18px rgba(1, 64, 52, .18);
}

/* The fold, drawn as faintly as theirs. */
.gs-page::before {
  content: "";
  position: absolute;
  top: 28px;
  bottom: 28px;
  left: 50%;
  border-left: 1px solid rgba(0, 0, 0, .15);
}

.gs-sheet {
  font-family: "Times New Roman", Times, serif;
  font-size: 14px;
  line-height: 1.3;
  color: #000;
}

.gs-sheet.is-blank { visibility: hidden; }

/* Two columns rather than a centred group: the seal is pinned to the sheet's
   left edge, in line with the TERM label below it, and the name centres in
   what is left. Working from the edge keeps it there whatever the school is
   renamed to — a centred group would drift with the length of the name. */
.gs-head {
  display: grid;
  grid-template-columns: 52px 1fr;
  align-items: center;
  column-gap: 1.25rem;
}

.gs-seal { position: relative; top: -6px; width: 52px; height: 52px; object-fit: contain; }
/* min-width:0 so the flex item can be narrower than the name it holds, and
   visible overflow so the name shows anyway. */
.gs-titles { flex: 0 1 auto; min-width: 0; text-align: center; overflow: visible; }

/* The masthead and the signatures are typed, but must print as plain text —
   so the fields carry no border, no fill and no box of their own. */
.gs-college, .gs-place, .gs-sign-title, .gs-curryr, .gs-cell {
  min-height: 0;
  border: 0;
  border-radius: 0;
  background: none;
  font: inherit;
  color: #000;
}

.gs-sign-title { display: block; width: 100%; padding: 0; text-align: center; }

/* Centred on the space beside the seal, and allowed to overhang it — the
   college's full name is wider than that space at this size. */
.gs-college, .gs-place { display: block; text-align: center; }

/* Everything on the sheet is the sheet's own size — the old form sets one
   size for the whole page and lets the bold carry the masthead. */
.gs-college { font-weight: 700; text-transform: uppercase; white-space: nowrap; }
.gs-kind { margin: .25rem 0 0; text-align: center; }

/* Typed fields only show they are typeable once you are in them. */
.gs-college:hover, .gs-place:hover, .gs-sign-title:hover,
.gs-curryr:hover, .gs-cell:hover { background: #F2F4F8; }

.gs-college:focus-visible, .gs-place:focus-visible, .gs-sign-title:focus-visible,
.gs-curryr:focus-visible, .gs-cell:focus-visible {
  outline: 2px solid var(--accent-500);
  outline-offset: -1px;
  background: #FFF;
}

/* Two label/value pairs per line: TERM with CREDITS, COURSE with CURR/YR. */
.gs-facts {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .05rem .5rem;
  margin: .7rem 0 .35rem;
  font-weight: 700;
}

.gs-fact { display: flex; gap: .3rem; white-space: nowrap; }
.gs-fact-label { flex: 0 0 auto; }
.gs-fact-value { font-weight: 400; }

/* The descriptive title runs under both columns and is the one fact long
   enough to need wrapping. */
.gs-fact.is-wide { grid-column: 1 / -1; white-space: normal; }
.gs-fact.is-wide .gs-fact-value { overflow-wrap: anywhere; }

/* Typed, not derived. */
.gs-curryr { width: 3.5rem; padding: 0 .15rem; font-weight: 400; }

/* Heavier rule round the outside and under the headings than between the
   rows, the way the old sheet draws it. */
.gs-table {
  width: 100%;
  /* Fixed, so the column widths below hold and a long name is clipped
     rather than widening the sheet. */
  table-layout: fixed;
  border-collapse: collapse;
  border: 2px solid #000;
}

.gs-table th, .gs-table td {
  border: 1.25px solid #000;
  padding: 1px 4px;
}

.gs-table th { border-width: 2px; }

.gs-table th { font-weight: 700; text-align: center; }
.gs-table .gs-no { width: 6%; text-align: center; }
.gs-table .gs-mark { width: 11%; text-align: center; padding: 0; }
/* One line a row, whatever the name's length — the old sheet clips rather
   than letting a long name make its row taller than the rest.

   No max-width here. The old `max-width: 0` was the usual truncation trick,
   and it collapsed these cells to 10px — but only because the unscoped
   .gs-name rule further down was turning them into flex containers, where
   max-width bites. With that scoped to the dialog these are table cells
   again, table-layout: fixed pins the columns to the header row, and
   overflow + ellipsis clip a long name inside its cell. max-width would now
   be inert, so it stays out. */
.gs-table td.gs-name {
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The grade boxes fill their cell so the ruled square is what you click. */
.gs-cell {
  width: 100%;
  padding: 1px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* A merged cell is for writing in, so it says so by being wider, not by
   being marked. */
.gs-table td.gs-mark.is-merged { width: auto; }

.gs-count { margin: .5rem 0 0; }

/* Prompt, then a ruled line to sign on, then the office under it. The rule
   is only as wide as the name, so the block is centred and the field sizes
   itself — which is why the name is contenteditable rather than an input. */
.gs-sign { margin-top: 1.1rem; }
.gs-sign-prompt { margin: 0 0 1.6rem; }
.gs-sign-line { text-align: center; }

.gs-sign-name {
  display: inline-block;
  min-width: 12ch;
  padding: 0 .3rem;
  font-weight: 700;
  text-align: center;
  white-space: nowrap;
  color: #000;
}

.gs-sign-name:focus-visible { outline: 2px solid var(--accent-500); outline-offset: 2px; }
.gs-sign-name:hover { background: #F2F4F8; }
.gs-sign-title { margin-top: 0; }

/* There is deliberately no print stylesheet: the PDF is written by the
   server, in gradesheet-pdf.js, so nothing here has to survive a printer. */

/* --- The class list dropdown --- */

/* A heading inside the menu: which year these classes belong to, and
   whatever the menu has to say when there are none. */
.datamenu-note {
  margin: 0;
  padding: .45rem .7rem;
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--sand-100);
}

/* Two lines per class: the code carries it, the title explains it. */
#classListMenu .datamenu-item { display: block; text-align: left; line-height: 1.25; }
.datamenu-code { display: block; font-weight: 700; color: var(--brand-800); }
.datamenu-sub { display: block; font-size: .74rem; font-weight: 400; color: var(--ink-soft); }

/* The class already open, so the menu says where you are. */
#classListMenu .datamenu-item.is-on { background: var(--brand-050); }
#classListMenu .datamenu-item.is-on .datamenu-code { color: var(--brand-700); }

/* Search and the status filter share one line: they answer different halves of
   the same question ("which student" vs "which of them still needs work"), and
   stacking them cost a whole band above a list that wants the height. */
.score-searchrow {
  display: flex;
  align-items: center;
  gap: .5rem;
}

.score-searchrow .score-search { flex: 1; min-width: 0; }

/* One dropdown rather than a row of chips: six marks made a button per mark,
   which read as a toolbar in its own right next to the list it was filtering. */
.filter-select {
  width: auto;
  min-height: 0;
  padding: .34rem 1.6rem .34rem .55rem;
  font-size: .82rem;
  font-weight: 600;
  color: var(--brand-700);
}

/* A filter that is hiding rows says so, so an empty-looking list is never a
   mystery. */
.filter-select.is-narrowing {
  border-color: var(--accent-500);
  color: var(--accent-700);
}

/* --- Two rosters, side by side -------------------------------------
   The import refuses on any difference, so the job of this block is to
   show which name causes it. Both lists are shown in full rather than
   only the offenders: seeing "the workbook stops at Rapisura" is what
   explains a missing person, and a bare list of names does not. */
.gs-cmp {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: .9rem;
  margin-top: .8rem;
}

.gs-cmp-head {
  margin: 0 0 .35rem;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.gs-cmp-n { color: var(--brand-700); }

.gs-cmp-list {
  max-height: 15rem;
  overflow-y: auto;
  border: 1px solid var(--sand-300);
  border-radius: var(--radius);
  background: var(--canvas);
}

/* Scoped to the compare dialog. Unscoped, this also matched the grade
   sheet's own td.gs-name cells: `display: flex` stops a <td> being a table
   cell, so it no longer stretches to its row and an empty one drew a small
   box floating inside the ruling. Two components, one class name. */
.gs-cmp-list .gs-name {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .3rem .55rem;
  font-size: .85rem;
  border-bottom: 1px solid var(--sand-200);
}

.gs-cmp-list .gs-name:last-child { border-bottom: 0; }
.gs-name-no { min-width: 1.6rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.gs-name-text { flex: 1; min-width: 0; }

/* The whole point of the dialog: the ones that do not appear on the other
   side. Terracotta because it is the thing stopping the import. */
.gs-cmp-list .gs-name.is-missing { background: var(--accent-050); }
.gs-cmp-list .gs-name.is-missing .gs-name-text { color: var(--accent-700); font-weight: 600; }

.gs-name-tag {
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--accent-700);
  white-space: nowrap;
}

/* The whole plan, in a line. */
.gs-plan-sum {
  margin: .9rem 0 0;
  font-size: .85rem;
  font-weight: 600;
  color: var(--brand-700);
}

.gs-plan-note { margin: .5rem 0 0; font-size: .82rem; color: var(--ink-soft); }
.gs-plan-note.is-problem { color: var(--accent-700); font-weight: 600; }

/* Names the workbook has and the class does not. Terracotta throughout: this
   block is the one place the import writes something other than marks, so it
   should not read as an aside. */
.gs-add {
  margin-top: .8rem;
  padding: .6rem .7rem;
  border: 1px solid var(--accent-400);
  border-radius: var(--radius);
  background: var(--accent-050);
}

.gs-add-line { margin: 0 0 .45rem; font-size: .85rem; color: var(--accent-700); font-weight: 600; }

.gs-add-list { display: flex; flex-wrap: wrap; gap: .35rem; }

.gs-add-name {
  display: inline-flex;
  align-items: baseline;
  gap: .35rem;
  padding: .2rem .45rem;
  border: 1px solid var(--accent-100);
  border-radius: var(--radius);
  background: var(--canvas);
  font-size: .82rem;
}

/* Enrolling someone already on file is routine; creating a database record
   from a name typed into a spreadsheet is the part worth reading twice. */
.gs-add-where { font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-soft); }
.gs-add-where.is-new { color: var(--accent-700); font-weight: 700; }

.gs-add-confirm {
  display: flex;
  align-items: center;
  gap: .45rem;
  margin-top: .6rem;
  font-size: .85rem;
  font-weight: 600;
  cursor: pointer;
}

.gs-add-confirm input { width: 1rem; height: 1rem; accent-color: var(--accent-500); }

/* The matching case says so plainly rather than showing nothing. */
.roll-note.is-agreed { color: var(--brand-700); font-weight: 600; }

/* ---------------- Backups ---------------- */

.backup-meta { color: var(--ink-soft); font-weight: 400; }

/* The one that would be restored, were you restoring. */
.import-row.is-newest { background: var(--brand-050); }

/* Restore and Delete sit together at the end of the row, clear of the name.
   The auto margin goes on the first of the pair so they stay adjacent. */
#backupRows .import-row { align-items: center; gap: .4rem; }
#backupRows .import-row .btn { flex: 0 0 auto; }
#backupRows .import-row .btn:nth-last-child(2) { margin-left: auto; }


/* Row one: the label, and the button pushed to the far end. Row two: the
   path, given the whole width so it wraps against nothing. */
.backup-where {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .45rem .6rem;
  margin: 0 0 .9rem;
}

.backup-where-label {
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.backup-where .btn { flex: 0 0 auto; margin-left: auto; }

/* 100% basis is what breaks it onto its own line. */
.backup-where code {
  flex: 0 0 100%;
  min-width: 0;
  font-size: .78rem;
  line-height: 1.5;
  color: var(--ink);
  word-break: break-all;
}



/* The one menu item that destroys something, marked as such. Terracotta is
   rationed to warnings and destructive actions, and this is both. */
.datamenu-item.is-danger { color: var(--accent-700); }
.datamenu-item.is-danger:hover { background: var(--accent-050); }

/* A backup holding nothing — the snapshot taken just before a wipe or a
   restore. Marked because it is the one you must not restore by mistake. */
#backupRows .imp-note.is-warning { font-style: normal; font-weight: 700; }

/* --- Late penalty ------------------------------------------------------
   The rule sits in the marking toolbar because it is thought about while
   marking, not while writing the activity. */
.cw-late-rule {
  display: inline-flex; align-items: center; gap: .3rem;
  margin-left: .8rem;
  font-size: .74rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-soft);
}
.cw-late-input {
  width: 3.4rem;
  padding: .15rem .35rem;
  border: 1px solid var(--sand-300);
  border-radius: var(--radius);
  background: var(--canvas);
  font: inherit; font-size: .8rem; font-weight: 700;
  text-align: center;
  color: var(--ink);
}

/* What the mark counts for, beside what was earned. Terracotta because it is
   a deduction, not an error -- the row is still perfectly valid. */
.cw-late-net {
  flex: 0 0 auto;
  min-width: 2.6rem;
  font-size: .82rem; font-weight: 800;
  color: var(--accent-700);
  white-space: nowrap;
}
.import-row.is-late { background: var(--accent-050); }
/* The rubric pane's docked mark, under the earned one. */
.cw-mark-net {
  margin: .1rem 0 0;
  font-size: .82rem; font-weight: 800;
  color: var(--accent-700);
}

/* ==================================================================
   KWADERNO THEME
   Everything below restyles the CMS into Kwaderno without touching the
   rules above: the palette swap at the top does most of it, these finish
   the look (logo bar, Fredoka headings, ruled banners with the mustard
   bookmark) and style the parts Kwaderno adds.
   ================================================================== */

h1, h2, .card-title, .modal-head h2 { font-family: Fredoka, Nunito, sans-serif; font-weight: 600; letter-spacing: 0; }

/* The title bar: cream with the wordmark, instead of the seal on olive. */
.topbar {
  background: var(--canvas);
  background-image: none;
  border-bottom: 1px solid var(--sand-200);
  color: var(--ink);
  padding: .55rem 1.5rem;
}
.brand { gap: .6rem; }
.brand-mark { width: auto; height: 38px; }
.brand-name, .brand-sub { display: none; }
.crumbbar-mark { border-radius: 6px; }
.topbar-nav { color: var(--ink); border-color: var(--sand-300); }
.topbar-nav:hover { background: var(--brand-050); border-color: var(--brand-700); }
.topbar-nav.is-current { background: var(--brand-700); border-color: var(--brand-800); color: var(--white); }

/* Banners: ruled like a notebook page, with the logo's bookmark on top. */
.banner-a {
  background-color: var(--brand-700);
  background-image: repeating-linear-gradient(0deg, rgba(255,255,255,.07) 0, rgba(255,255,255,.07) 1px, transparent 1px, transparent 17px);
}
.card-banner::after { display: none; }
.card-banner::before {
  border: none; border-radius: 0;
  right: 1.1rem; top: 0;
  width: 18px; height: 34px;
  background: #FBB806;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 79%, 0 100%);
}

/* --- Kwaderno additions on the teacher side ------------------------- */

/* Roster: whether the student can sign in to the app. */
.kw-sign { font-size: .78rem; font-weight: 700; white-space: nowrap; }
.kw-sign.is-linked { color: var(--brand-700); }
.kw-sign.is-none { color: var(--ink-soft); }
.kw-sign.is-noid { color: var(--accent-500); }
.kw-unlink {
  border: 1px solid var(--sand-300); background: var(--field); border-radius: 8px;
  font: inherit; font-size: .76rem; font-weight: 800; padding: .2rem .55rem; cursor: pointer; color: var(--ink);
}
.kw-unlink:hover { border-color: var(--brand-700); color: var(--brand-700); }

/* Scores: what the student turned in, under their name. */
.kw-sub { display: flex; flex-wrap: wrap; align-items: center; gap: .15rem .7rem; font-size: .76rem; margin-top: .1rem; }
.kw-sub .when { color: var(--ink-soft); font-weight: 700; }
.kw-sub .when.is-late { color: var(--accent-500); }
.kw-sub .none { color: var(--ink-soft); }
.kw-sub a { color: var(--brand-700); font-weight: 800; text-decoration: underline; text-underline-offset: 2px; }
.kw-subbar { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; padding: .55rem .75rem; margin: .4rem 0 .2rem; border-radius: 10px; background: var(--brand-050); font-size: .85rem; color: var(--brand-800); font-weight: 700; }
.kw-subbar a { color: var(--brand-700); font-weight: 800; }

/* Classwork form: how students do it. */
.kw-modes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .5rem; }
.kw-mode { text-align: left; padding: .6rem .7rem; border: 2px solid var(--sand-200); border-radius: 10px; background: var(--field); cursor: pointer; font: inherit; color: var(--ink); }
.kw-mode b { display: block; font-size: .92rem; }
.kw-mode span { display: block; font-size: .74rem; color: var(--ink-soft); line-height: 1.35; }
.kw-mode[aria-pressed="true"] { border-color: var(--brand-700); background: var(--brand-050); }
.kw-mode:disabled { opacity: .5; cursor: not-allowed; }
.kw-modeopts { display: flex; flex-wrap: wrap; gap: .4rem 1rem; margin-top: .4rem; font-size: .85rem; }
.kw-modeline { font-size: .76rem; font-weight: 800; color: var(--brand-700); }

/* --- The classwork editor: two panes ---------------------------------- */
.modal.kw-editor {
  width: min(1200px, calc(100vw - 2rem));
  height: min(860px, calc(100vh - 2rem));
  border-radius: 18px;
  overflow: hidden;
}
.modal.kw-editor[open] { display: flex; flex-direction: column; }
.kw-ed-head {
  flex: none; display: flex; align-items: center; gap: .9rem;
  padding: .75rem .9rem .75rem 1.4rem; color: var(--white);
  background-color: var(--brand-700);
  background-image: repeating-linear-gradient(0deg, rgba(255,255,255,.07) 0, rgba(255,255,255,.07) 1px, transparent 1px, transparent 17px);
}
.kw-ed-titles { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.kw-ed-titles h2 { font-size: 1.3rem; color: var(--white); }
#kwEdSub { font-size: .8rem; font-weight: 700; color: var(--brand-100); }
.kw-ed-paste { height: 36px; padding: 0 .9rem; border: 1.5px solid rgba(255,255,255,.7); border-radius: 9px; background: none; color: var(--white); font: inherit; font-size: .82rem; font-weight: 800; cursor: pointer; }
.kw-ed-paste:hover { background: rgba(255,255,255,.12); }
.kw-ed-x { width: 40px; height: 40px; border: none; background: none; color: var(--white); font-size: 1.6rem; line-height: 1; cursor: pointer; border-radius: 10px; }
.kw-ed-x:hover { background: rgba(255,255,255,.12); }

.kw-ed-body { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr) 360px; }
.kw-ed-main { min-height: 0; overflow-y: auto; padding: .9rem 1.5rem 1.5rem; display: flex; flex-direction: column; gap: 1rem; }
.kw-ed-tabbar { display: flex; align-items: center; justify-content: space-between; gap: .8rem; border-bottom: 1px solid var(--sand-200); }
.kw-ed-tabs { display: flex; gap: .15rem; }
.kw-ed-tabs button { height: 44px; padding: 0 .9rem; border: none; background: none; font: inherit; font-weight: 800; color: var(--ink-soft); border-bottom: 3px solid transparent; cursor: pointer; }
.kw-ed-tabs button[aria-selected="true"] { color: var(--brand-700); border-bottom-color: var(--brand-700); }
.kw-ed-count { font-size: .75rem; font-weight: 700; color: var(--ink-soft); }
.kw-ed-preview-btn { height: 34px; padding: 0 .8rem; border: 1.5px solid var(--sand-300); border-radius: 9px; background: var(--field); font: inherit; font-size: .82rem; font-weight: 800; cursor: pointer; color: var(--ink); }
.kw-ed-preview-btn[aria-pressed="true"] { background: var(--brand-700); border-color: var(--brand-700); color: var(--white); }
.kw-ed-pane { display: flex; flex-direction: column; gap: .9rem; }
.kw-ed-pane[hidden] { display: none; }
.kw-ed-title { font-family: Fredoka, Nunito, sans-serif !important; font-size: 1.35rem !important; font-weight: 600; height: 52px; }
.kw-ed-pane textarea { line-height: 1.55; }
.kw-ed-rubrichead { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; font-size: .9rem; color: var(--ink-soft); }
.kw-ed-rubrichead b { color: var(--brand-700); font-size: .95rem; }
.kw-ed-rubricbtns { display: flex; gap: .5rem; }
.kw-ed-hint { font-size: .82rem; margin: 0; }
.kw-ed-empty { padding: 2rem; border: 1.5px dashed var(--sand-300); border-radius: 14px; text-align: center; display: flex; flex-direction: column; align-items: center; gap: .7rem; }

.kw-ed-side { min-height: 0; overflow-y: auto; padding: 1.1rem; background: var(--sand-050); border-left: 1px solid var(--sand-200); display: flex; flex-direction: column; gap: 1.2rem; }
.kw-ed-group { display: flex; flex-direction: column; gap: .5rem; }
.kw-ed-label { font-size: .72rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); }
#kwCategory { height: 40px; border: 1.5px solid var(--sand-300); border-radius: 10px; background: var(--white); padding: 0 .6rem; font: inherit; font-weight: 700; }
.kw-ed-side .kw-modes { grid-template-columns: 1fr; }
.kw-ed-side .kw-modeopts { flex-direction: column; padding: .7rem; border-radius: 10px; background: var(--field); border: 1px solid var(--sand-200); }
.kw-ed-side .kw-modeopts .muted { font-size: .76rem; }
.kw-ed-switch { display: flex; align-items: center; justify-content: space-between; gap: .8rem; padding: .75rem; border-radius: 12px; background: var(--field); border: 1px solid var(--sand-200); cursor: pointer; }
.kw-ed-switch span { display: flex; flex-direction: column; }
.kw-ed-switch small { font-size: .76rem; color: var(--ink-soft); }
.kw-ed-switch input { appearance: none; -webkit-appearance: none; width: 46px; height: 26px; border-radius: 13px; background: #C9BBA5; position: relative; cursor: pointer; flex: none; margin: 0; transition: background .15s; }
.kw-ed-switch input::after { content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 10px; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.2); transition: left .15s; }
.kw-ed-switch input:checked { background: var(--brand-700); }
.kw-ed-switch input:checked::after { left: 23px; }
.kw-ed-switch input:focus-visible { outline: 3px solid var(--brand-100); outline-offset: 2px; }

.kw-ed-foot { flex: none; display: flex; align-items: center; gap: .5rem; }
.kw-ed-state { flex: 1; font-size: .82rem; font-weight: 700; color: var(--ink-soft); }
.kw-ed-state.is-posted { color: var(--brand-700); }

/* "What students see": a phone with the classwork as the app shows it. */
.kw-phone { align-self: center; width: 330px; border: 10px solid var(--ink); border-radius: 34px; overflow: hidden; background: var(--canvas); box-shadow: 0 10px 30px rgba(0,0,0,.18); }
.kw-phone-screen { min-height: 560px; display: flex; flex-direction: column; }
.kw-pv-bar { height: 44px; display: flex; align-items: center; padding: 0 .8rem; border-bottom: 1px solid var(--sand-200); font-size: .82rem; font-weight: 700; color: var(--ink-soft); }
.kw-pv { padding: .9rem; display: flex; flex-direction: column; gap: .6rem; font-size: .85rem; }
.kw-pv h3 { margin: 0; font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1.3rem; }
.kw-pv .due { font-weight: 800; color: var(--accent-500); }
.kw-pv .instr { white-space: pre-line; line-height: 1.5; }
.kw-pv .lbl { font-size: .68rem; font-weight: 800; letter-spacing: .1em; color: var(--ink-soft); }
.kw-pv .rrow { display: flex; justify-content: space-between; padding: .35rem 0; border-bottom: 1px solid var(--sand-200); }
.kw-pv .fake-add { height: 40px; border: 1.5px dashed #BFAF98; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-weight: 800; color: var(--brand-700); }
.kw-pv .fake-btn { height: 44px; border-radius: 12px; background: #B8C9C1; color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 800; }

/* A draft is visible only to the teacher. */
.kw-draftline { font-size: .76rem; font-weight: 800; color: #6E5100; }

/* --- Attendance day editor: tap to mark -------------------------------- */
.kw-plea { display: inline-flex; gap: .25rem; margin-left: auto; }
.kw-plea button { width: 40px; height: 34px; border: 1.5px solid var(--sand-300); border-radius: 9px; background: var(--field); font: inherit; font-size: .85rem; font-weight: 800; color: var(--ink-soft); cursor: pointer; }
.kw-plea button:hover:not(:disabled) { border-color: var(--brand-700); }
.kw-plea button:disabled { opacity: .45; cursor: default; }
.kw-plea .kw-plea-p[aria-checked="true"] { background: var(--brand-700); border-color: var(--brand-700); color: #fff; }
.kw-plea .kw-plea-l[aria-checked="true"] { background: #FBB806; border-color: #FBB806; color: #2B2100; }
.kw-plea .kw-plea-e[aria-checked="true"] { background: var(--ink-soft); border-color: var(--ink-soft); color: #fff; }
.kw-plea .kw-plea-a[aria-checked="true"] { background: var(--accent-500); border-color: var(--accent-500); color: #fff; }

/* --- Groups as columns ---------------------------------------------------- */
.modal.kw-groups { width: min(1180px, calc(100vw - 2rem)); }
.kw-gcols { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .7rem; align-items: start; max-height: 58vh; overflow-y: auto; padding: .1rem; }
.kw-gcol { display: flex; flex-direction: column; border: 1.5px solid var(--sand-200); border-radius: 14px; background: var(--field); overflow: hidden; }
.kw-gcol.is-loose { background: var(--sand-050); }
.kw-gcol.is-target { border-color: var(--brand-700); }
.kw-ghead { display: flex; align-items: center; justify-content: space-between; gap: .5rem; padding: .6rem .75rem; border: none; background: none; font: inherit; cursor: pointer; text-align: left; }
.kw-gcol.is-target .kw-ghead { background: var(--brand-050); }
.kw-gtitle { font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1rem; color: var(--brand-700); }
.kw-gcol.is-loose .kw-gtitle { color: var(--ink-soft); }
.kw-gmove { font-size: .75rem; font-weight: 800; color: var(--brand-700); }
.kw-glist { display: flex; flex-direction: column; gap: .25rem; padding: .5rem; min-height: 3.2rem; }
.kw-gname { height: 34px; padding: 0 .65rem; border: 1.5px solid var(--sand-200); border-radius: 9px; background: var(--white); font: inherit; font-size: .82rem; font-weight: 700; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; color: var(--ink); }
.kw-gname:hover { border-color: var(--brand-700); }
.kw-gname.is-picked { background: var(--brand-700); border-color: var(--brand-700); color: var(--white); }
.kw-gname.is-dim { opacity: .35; }
.kw-gempty { font-size: .76rem; color: var(--ink-soft); text-align: center; padding: .5rem .2rem; }
.kw-gadd { min-height: 110px; border: 2px dashed var(--sand-300); border-radius: 14px; background: none; font: inherit; font-weight: 800; color: var(--brand-700); cursor: pointer; }

/* --- Scores: marking with the work open --------------------------------- */
.modal.kw-wide { width: min(1200px, calc(100vw - 2rem)); }
.kw-mark { display: grid; grid-template-columns: 300px minmax(0, 1fr); height: min(calc(100vh - 400px), 600px); min-height: 360px; border: 1px solid var(--sand-200); border-radius: 14px; overflow: hidden; background: var(--field); }
.kw-mark-left { display: flex; flex-direction: column; min-height: 0; border-right: 1px solid var(--sand-200); background: var(--sand-050); }
.kw-mark-filters { display: flex; flex-wrap: wrap; gap: .25rem; padding: .6rem; border-bottom: 1px solid var(--sand-200); }
.kw-mark-filters button { height: 28px; padding: 0 .55rem; border: 1.5px solid var(--sand-300); border-radius: 8px; background: var(--field); font: inherit; font-size: .74rem; font-weight: 800; cursor: pointer; color: var(--ink); }
.kw-mark-filters button.is-on { background: var(--brand-700); border-color: var(--brand-700); color: #fff; }
.kw-mark-names { flex: 1; overflow-y: auto; }
.kw-mark-item { width: 100%; display: flex; align-items: center; gap: .6rem; padding: .55rem .7rem; border: none; border-bottom: 1px solid var(--sand-200); border-left: 4px solid transparent; background: none; font: inherit; text-align: left; cursor: pointer; }
.kw-mark-item.is-on { background: var(--field); border-left-color: var(--brand-700); }
.kw-mark-who { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.kw-mark-who b { font-size: .86rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kw-mark-who small { font-size: .72rem; font-weight: 700; color: var(--ink-soft); }
.kw-mark-who small.is-red, .kw-mark-headwho small.is-red { color: var(--accent-500); }
.kw-mark-score { font-weight: 800; color: var(--brand-700); }
.kw-mark-score.is-none { color: #A89C8A; }
.kw-mark-right { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.kw-mark-head { display: flex; align-items: center; gap: .5rem; padding: .7rem 1rem; border-bottom: 1px solid var(--sand-200); }
.kw-mark-headwho { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.kw-mark-headwho h3 { margin: 0; font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1.15rem; }
.kw-mark-headwho small { font-size: .78rem; font-weight: 700; color: var(--ink-soft); }
.kw-mark-step { width: 36px; height: 36px; border: 1.5px solid var(--sand-300); border-radius: 10px; background: var(--white); font-size: 1.1rem; cursor: pointer; }
.kw-mark-view { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: .5rem; padding: .7rem 1rem; }
.kw-mark-files { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; }
.kw-mark-files button { height: 30px; padding: 0 .7rem; border: 1.5px solid var(--sand-300); border-radius: 8px; background: var(--white); font: inherit; font-size: .78rem; font-weight: 800; cursor: pointer; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kw-mark-files button.is-on { background: var(--brand-700); border-color: var(--brand-700); color: #fff; }
.kw-mark-dl { margin-left: auto; font-size: .8rem; font-weight: 800; color: var(--brand-700); }
.kw-mark-code { flex: 1; min-height: 0; margin: 0; overflow: auto; padding: .8rem 1rem; border-radius: 12px; background: var(--ink); color: #E6F0EC; font: 13px/1.6 ui-monospace, "Cascadia Mono", Consolas, monospace; white-space: pre; }
.kw-mark-pic { flex: 1; min-height: 0; border-radius: 12px; background: #2A3631; display: flex; align-items: center; justify-content: center; overflow: auto; }
.kw-mark-pic img { max-width: 100%; max-height: 100%; object-fit: contain; }
.kw-mark-pdf { flex: 1; min-height: 0; width: 100%; border: 1px solid var(--sand-200); border-radius: 12px; background: #fff; }
.kw-mark-empty { flex: 1; border: 1.5px dashed var(--sand-300); border-radius: 12px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .3rem; color: var(--ink-soft); text-align: center; padding: 1rem; }
.kw-mark-empty b { color: var(--ink); font-size: 1rem; }
.kw-mark-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; padding: .7rem 1rem; border-top: 1px solid var(--sand-200); background: var(--white); }
.kw-mark-scorelab { display: flex; align-items: center; gap: .45rem; font-size: .75rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); }
.kw-mark-score-input { width: 74px; height: 44px; border: 2px solid var(--brand-700); border-radius: 10px; text-align: right; padding: 0 .6rem; font-family: Fredoka, Nunito, sans-serif; font-size: 1.35rem; font-weight: 600; color: var(--ink); }
.kw-mark-of { font-size: .95rem; text-transform: none; letter-spacing: 0; color: var(--ink-soft); }
.kw-mark-net { font-size: .82rem; font-weight: 800; color: var(--accent-500); }
.kw-mark-hint { flex: 1; text-align: right; font-size: .74rem; color: var(--ink-soft); }
.kw-ed-switch input[type="checkbox"] { height: 26px; min-height: 0; width: 46px; padding: 0; border: none; box-shadow: none; }
/* The CMS list this sits in scrolls at 320px; the marking view sizes itself. */
.modal.kw-wide #cwScoreRows { max-height: none; overflow: visible; }
/* Columns grow with their members; nothing inside them may shrink. */
.kw-gcols { grid-auto-rows: max-content; }
.kw-gcol, .kw-glist, .kw-gname, .kw-ghead { flex-shrink: 0; }
.kw-gcol { overflow: visible; height: auto; }

/* --- Online exams: the exam's room (kw-exam.js) ------------------------ */
.kwex-cardline { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; font-size: .8rem; font-weight: 700; color: var(--ink-soft); }
.modal.kw-exam { width: min(1040px, calc(100vw - 2rem)); }
.kwex-tabbar { flex: none; padding: 0 1.5rem; border-bottom: 1px solid var(--sand-200); }
.kwex-body { flex: 1; min-height: 0; overflow-y: auto; padding: 1.1rem 1.5rem 1.6rem; }
.kwex-pane { display: flex; flex-direction: column; gap: 1rem; max-width: 900px; }
.kwex-sec { display: flex; flex-direction: column; gap: .6rem; padding: 1rem 1.1rem; border: 1px solid var(--sand-200); border-radius: 14px; background: var(--field); }
.kwex-h { margin: 0; font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1.15rem; color: var(--brand-800); }
.kwex-sub { margin: .5rem 0 0; font-size: .78rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-soft); }
.kwex-sec p, .kwex-pane > p { margin: 0; }
.kwex-note { font-size: .82rem; color: var(--ink-soft); }
.kwex-warn { padding: .55rem .75rem; border-radius: 10px; background: #FFF3D1; border: 1px solid #EED48C; color: #4A3B0E; font-size: .85rem; font-weight: 700; }
.kwex-warnbox { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; justify-content: space-between; padding: .7rem .9rem; border-radius: 12px; background: #FFF3D1; border: 1px solid #EED48C; color: #4A3B0E; font-weight: 700; font-size: .88rem; }
.kwex-state { font-weight: 700; }
.kwex-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.kwex-sched { display: flex; gap: .7rem; flex-wrap: wrap; align-items: end; }
.kwex-sched input, .kwex-narrow input, .kwex-pool input, .kwex-mark input {
  height: 38px; border: 1.5px solid var(--sand-300); border-radius: 9px; background: var(--white); padding: 0 .55rem; font: inherit;
}
.kwex-narrow { max-width: 320px; }
.kwex-indent { margin-left: 1.9rem; }
.kwex-check { display: flex; gap: .65rem; align-items: flex-start; cursor: pointer; font-weight: 700; }
.kwex-check input { width: 18px; height: 18px; margin: .15rem 0 0; accent-color: var(--brand-700); flex: none; }
.kwex-check span { display: flex; flex-direction: column; }
.kwex-check small { font-weight: 400; font-size: .8rem; color: var(--ink-soft); margin-top: .1rem; }
.kwex-pool { display: grid; grid-template-columns: minmax(0, 1fr) 90px auto; gap: .45rem .7rem; align-items: center; font-size: .9rem; }
.kwex-case { border: 1px solid var(--sand-200); border-radius: 10px; padding: .55rem .75rem; background: var(--white); }
.kwex-case summary { cursor: pointer; font-weight: 800; font-size: .9rem; }
.kwex-case .kwex-check { margin-top: .55rem; font-weight: 600; font-size: .88rem; }

.kwex-resulthead { display: flex; justify-content: space-between; align-items: center; gap: .8rem; flex-wrap: wrap; }
.kwex-tablewrap { overflow-x: auto; border: 1px solid var(--sand-200); border-radius: 12px; background: var(--field); }
.kwex-table { width: 100%; border-collapse: collapse; font-size: .88rem; }
.kwex-table th { text-align: left; font-size: .72rem; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-soft); padding: .6rem .7rem; border-bottom: 1px solid var(--sand-200); background: var(--sand-050); white-space: nowrap; }
.kwex-table td { padding: .55rem .7rem; border-bottom: 1px solid var(--sand-100); vertical-align: top; }
.kwex-table tr:last-child td { border-bottom: none; }
.kwex-table td small { display: block; font-size: .76rem; color: var(--ink-soft); }
.kwex-table .num { text-align: right; white-space: nowrap; font-weight: 800; }
.kwex-name { font-weight: 800; white-space: nowrap; }
.kwex-acts { white-space: nowrap; text-align: right; }
.kwex-acts .btn + .btn { margin-left: .3rem; }
.kwex-done { color: var(--brand-700); font-weight: 800; }
.kwex-todo { color: #6E5100; font-weight: 800; }
.kwex-bad { color: var(--accent-500); font-weight: 800; }

.kwex-revhead { display: flex; align-items: flex-start; gap: 1rem; flex-wrap: wrap; }
.kwex-revhead > div:nth-child(2) { flex: 1; min-width: 200px; }
.kwex-score { display: flex; flex-direction: column; align-items: flex-end; }
.kwex-score b { font: 600 1.6rem/1.1 Fredoka, Nunito, sans-serif; color: var(--brand-800); }
.kwex-score span { font-size: .8rem; font-weight: 700; color: var(--ink-soft); }
.kwex-events { border: 1px solid var(--sand-200); border-radius: 10px; padding: .55rem .8rem; background: var(--field); font-size: .86rem; }
.kwex-events summary { cursor: pointer; font-weight: 800; }
.kwex-events ul { margin: .5rem 0 0; padding-left: 1.1rem; }
.kwex-part { border: 1px solid var(--sand-200); border-radius: 14px; background: var(--field); padding: .3rem 1rem .6rem; }
.kwex-line { display: grid; grid-template-columns: 70px minmax(0, 1fr) 150px; gap: .8rem; padding: .7rem 0; border-top: 1px solid var(--sand-100); align-items: start; }
.kwex-part .kwex-sub + .kwex-line { border-top: none; }
.kwex-num { font-weight: 800; color: var(--ink-soft); font-size: .85rem; }
.kwex-qt { white-space: pre-wrap; }
.kwex-code, .kwex-mono { font-family: ui-monospace, Consolas, monospace; font-size: .85rem; white-space: pre-wrap; }
.kwex-code { margin: .35rem 0; padding: .5rem .7rem; background: var(--sand-050); border: 1px solid var(--sand-200); border-radius: 8px; overflow-x: auto; }
.kwex-ans { margin-top: .3rem; padding: .45rem .6rem; border-radius: 8px; background: var(--white); border: 1px solid var(--sand-200); white-space: pre-wrap; }
.kwex-essay { min-height: 4rem; }
.kwex-key { margin-top: .3rem; font-size: .82rem; color: var(--ink-soft); }
.kwex-mark { display: flex; flex-direction: column; gap: .25rem; align-items: stretch; font-size: .8rem; }
.kwex-mark input { width: 100%; text-align: right; font-weight: 800; }
.kwex-mark small { text-align: right; color: var(--ink-soft); }
.kwex-revfoot { position: sticky; bottom: -1.6rem; padding: .8rem 0 1rem; background: var(--canvas); }
@media (max-width: 720px) {
  .kwex-line { grid-template-columns: 1fr; }
  .kwex-mark { flex-direction: row; align-items: center; }
  .kwex-mark input { width: 90px; }
}

/* ======================================================================
   Kwaderno v1.3 — the redesigned teacher pages (see the "NEW · Teacher
   pages" boards in the Kwaderno UI design canvas). The class record grid,
   History and the Grade Sheet are deliberately left as they were.
   Statuses are plain coloured text, never pill badges.
   ====================================================================== */

/* --- One-row top bar ------------------------------------------------- */
.kwbar { flex: none; background: var(--field); border-bottom: 1px solid var(--sand-200); position: relative; z-index: 30; }
.kwbar-in { max-width: 1240px; margin: 0 auto; padding: 0 20px; min-height: 64px; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; }
.kwbar-brand { display: flex; align-items: center; margin-right: 4px; }
.kwbar-brand img { height: 32px; width: auto; display: block; }
.kwbar-nav { display: flex; align-self: stretch; gap: 2px; flex-wrap: wrap; }
.kwbar-link { display: flex; align-items: center; gap: 8px; padding: 0 13px; min-height: 58px; font-size: .95rem; font-weight: 800; color: #3E4B45; text-decoration: none; border-bottom: 3px solid transparent; border-top: 3px solid transparent; }
.kwbar-link:hover { color: var(--brand-700); background: var(--sand-050); }
.kwbar-link.is-current { color: var(--brand-700); border-bottom-color: var(--brand-700); }
.kwbar-link:focus-visible { outline: 3px solid var(--accent-400); outline-offset: -3px; }
.kwbar-space { flex: 1; }
.kwbar-sy { display: flex; align-items: center; gap: 8px; height: 40px; padding: 0 12px; border: 1.5px solid var(--sand-300); border-radius: 10px; background: var(--canvas); font: inherit; color: var(--brand-700); cursor: pointer; }
.kwbar-sy:hover { border-color: var(--brand-600); }
.kwbar-sy-label { font-size: .8rem; font-weight: 800; color: #3E4B45; }
.kwbar-sy-name { font-size: .9rem; font-weight: 800; }
.kwbar-symenu { right: 0; left: auto; min-width: 270px; }
.datamenu-item.is-on { background: var(--brand-050); color: var(--brand-800); font-weight: 800; }
.datamenu-item.is-strong { font-weight: 800; color: var(--brand-700); }
.kwbar .navbtn { width: 42px; height: 42px; }

/* The slim "where you are" line inside the page */
.trail { max-width: 1200px; margin: 0 auto .6rem; font-size: .88rem; font-weight: 700; color: var(--ink-soft); }
.trail ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .3rem; }
.trail li + li::before { content: "›"; margin-right: .3rem; color: #A89C8A; }
.trail a { color: var(--brand-700); text-decoration: none; }
.trail a:hover { text-decoration: underline; }
.trail .here { color: var(--ink-soft); }

/* --- Page heads and shared bits ---------------------------------------- */
.page > .view { max-width: 1200px; margin-left: auto; margin-right: auto; }
.page-head.kw-head { border-bottom: none; padding-bottom: 0; margin-bottom: 1.2rem; align-items: flex-end; }
.kw-head h1 { font-size: 2.1rem; color: var(--ink); }
.kw-head .page-sub { font-size: .95rem; color: #3E4B45; }
.kw-kicker { margin: 0; font-size: .88rem; font-weight: 800; color: var(--ink-soft); min-height: 1.1em; }
.kw-dayactions { align-items: center; }
.kw-iconbtn { width: 42px; height: 42px; display: inline-flex; align-items: center; justify-content: center; border: 1.5px solid var(--sand-300); border-radius: 11px; background: var(--field); color: #3E4B45; cursor: pointer; }
.kw-iconbtn:hover { border-color: var(--brand-600); color: var(--brand-700); }
.kw-head .btn, .kw-bigbtn { min-height: 42px; padding: .5rem 1rem; border-radius: 11px; font-size: .9rem; }
.kw-h2 { font-size: 1.35rem; margin: 1.8rem 0 .9rem; color: var(--ink); }
.kw-note { font-size: .88rem; color: var(--ink-soft); margin: .8rem 0 0; }
.kw-quiet { margin: 0; color: var(--ink-soft); font-size: .9rem; }
.kw-pad { padding: 1.6rem; text-align: center; }
.kw-emptybox { padding: 2.4rem 1.5rem; border: 1.5px dashed var(--sand-300); border-radius: 16px; text-align: center; color: var(--ink-soft); font-size: .95rem; }
.kw-good { color: var(--brand-700); }
.kw-bad { color: var(--accent-500); font-weight: 700; }
.kw-warn { color: #6E5100; font-weight: 700; }
.kw-strong { font-weight: 800; }
.kw-row { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.kw-linkbtn { border: none; background: none; padding: 0; font: inherit; font-weight: 700; color: var(--ink); text-align: left; cursor: pointer; }
.kw-linkbtn:hover { color: var(--brand-700); text-decoration: underline; }



/* --- Cards: classes and school years ----------------------------------- */
.kw-cardgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 18px; }
.kw-card { position: relative; display: flex; flex-direction: column; border: 1px solid var(--sand-200); border-radius: 16px; background: var(--field); }
.kw-card-link { display: flex; flex-direction: column; flex: 1; color: var(--ink); text-decoration: none; }
.kw-card-link:hover .kw-band-title { text-decoration: underline; }
.kw-band { position: relative; display: flex; flex-direction: column; gap: 1px; padding: 14px 52px 14px 18px; border-radius: 15px 15px 0 0; color: #fff; background-color: var(--brand-700); background-image: repeating-linear-gradient(0deg, rgba(255,255,255,.07) 0, rgba(255,255,255,.07) 1px, transparent 1px, transparent 17px); }
.kw-band.is-past { background-color: #3E5C53; }
.kw-band.is-next { background-color: #0F6A56; }
.kw-band-kicker { font-size: .75rem; font-weight: 800; letter-spacing: .08em; color: #FBB806; }
.kw-band.is-past .kw-band-kicker { color: #D9E4DF; }
.kw-band-title { font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1.5rem; line-height: 1.15; }
.kw-band-sub { font-size: .88rem; font-weight: 700; color: #E2EEE8; }
.kw-band-foot { margin-top: auto; padding-top: 14px; font-size: .88rem; font-weight: 700; color: #CFE3DA; }
.kw-bookmark { position: absolute; top: 0; right: 18px; width: 18px; height: 34px; fill: #FBB806; }
.kw-band.is-past .kw-bookmark { fill: #C9BBA5; }
.kw-card-body { display: flex; flex-direction: column; gap: 8px; padding: 14px 18px; font-size: .9rem; color: #3E4B45; }
.kw-iconline { display: flex; gap: 8px; align-items: flex-start; }
.kw-iconline svg { flex: none; margin-top: 2px; color: var(--ink-soft); }
.kw-iconline b { color: var(--ink); }
.kw-pre { white-space: pre-line; line-height: 1.45; }
.kw-cardnote { font-size: .9rem; font-weight: 800; padding-top: 2px; }
.kw-cardnote.is-bad { color: var(--accent-500); }
.kw-cardnote.is-good { color: var(--brand-700); }
.kw-cardnote.is-muted { color: var(--ink-soft); }
.kw-card-foot { display: flex; align-items: center; gap: 6px; padding: 8px 10px 8px 16px; border-top: 1px solid var(--sand-100); }
.kw-card-foot .spacer { flex: 1; }
.kw-card-when { flex: 1; font-size: .82rem; color: var(--ink-soft); }
.kw-card-foot .btn { min-height: 36px; border-radius: 10px; }
.kw-addtile { min-height: 230px; border: 2px dashed #CBBCA6; border-radius: 16px; background: none; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; color: var(--brand-700); font: inherit; cursor: pointer; }
.kw-addtile b { font-size: 1rem; }
.kw-addtile span { font-size: .82rem; color: var(--ink-soft); }
.kw-addtile:hover { border-color: var(--brand-600); background: var(--sand-050); }
.kw-yearhero { display: flex; flex-wrap: wrap; border: 1px solid var(--sand-200); border-radius: 18px; overflow: hidden; background: var(--field); }
.kw-yearhero .kw-band { flex: 1 1 380px; border-radius: 0; padding: 24px 70px 22px 26px; }
.kw-yearhero .kw-band-title { font-size: 2.4rem; line-height: 1.05; margin-top: 4px; }
.kw-yearhero .kw-band-sub { font-size: 1.1rem; font-weight: 800; }
.kw-yearhero .kw-bookmark { width: 24px; height: 46px; right: 28px; }
.kw-yearhero-side { flex: 1 1 420px; display: flex; flex-direction: column; gap: 14px; padding: 22px 24px; }
.kw-counts { display: flex; flex-wrap: wrap; gap: 6px 28px; font-size: .95rem; }
.kw-counts b { font-size: 1.25rem; }
.kw-yearhero-list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 5px; font-size: .9rem; color: #3E4B45; }
.kw-yearhero-side .kw-row { margin-top: auto; }
.kw-yearhero-side .btn { min-height: 42px; border-radius: 11px; padding: .5rem 1rem; font-size: .88rem; display: inline-flex; align-items: center; text-decoration: none; }

/* --- The ⋯ menu ---------------------------------------------------------- */
.kw-more { position: relative; display: inline-flex; }
.kw-more-btn { width: 38px; height: 38px; display: inline-flex; align-items: center; justify-content: center; border: none; background: none; border-radius: 10px; color: #3E4B45; cursor: pointer; }
.kw-more-btn:hover, .kw-more-btn[aria-expanded="true"] { background: var(--sand-100); }
.kw-pop { position: absolute; right: 0; top: calc(100% + 4px); z-index: 40; min-width: 210px; padding: 6px; border: 1px solid var(--sand-200); border-radius: 12px; background: var(--field); box-shadow: 0 10px 30px rgba(0,0,0,.14); display: flex; flex-direction: column; text-align: left; }
.kw-pop[hidden] { display: none; }
.kw-pop-item { padding: 9px 12px; border: none; border-radius: 8px; background: none; font: inherit; font-size: .9rem; font-weight: 700; color: var(--ink); text-align: left; cursor: pointer; white-space: nowrap; }
.kw-pop-item:hover { background: var(--brand-050); }
.kw-pop-item.is-danger { color: var(--accent-500); font-weight: 800; border-top: 1px solid var(--sand-100); border-radius: 0 0 8px 8px; }
.kw-pop-item.is-danger:hover { background: var(--accent-050); }

/* --- People as a table ------------------------------------------------- */
.kw-tablebox { overflow-x: auto; border: 1px solid var(--sand-200); border-radius: 16px; background: var(--field); margin-top: .9rem; }
.kw-table { width: 100%; min-width: 820px; border-collapse: collapse; font-size: .9rem; }
.kw-table th { text-align: left; padding: 10px 12px; background: var(--sand-050); border-bottom: 1px solid var(--sand-200); font-size: .72rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-soft); white-space: nowrap; }
.kw-table td { padding: 8px 12px; border-bottom: 1px solid #F0E6D8; vertical-align: middle; }
.kw-table tbody tr:last-child td { border-bottom: none; }
.kw-table tr.is-picked td { background: var(--brand-050); }
.kw-table .kw-col-pick { width: 44px; padding-left: 16px; padding-right: 0; }
.kw-table .kw-col-pick input { width: 18px; height: 18px; min-height: 0; margin: 0; accent-color: var(--brand-700); cursor: pointer; }
.kw-table .kw-col-no { width: 40px; text-align: right; color: var(--ink-soft); }
.kw-table .kw-col-more { width: 52px; text-align: right; padding: 2px 8px 2px 0; }
.kw-namebtn { border: none; background: none; padding: 4px 0; font: inherit; font-weight: 800; color: var(--ink); cursor: pointer; text-align: left; }
.kw-namebtn:hover { color: var(--brand-700); text-decoration: underline; }
.kw-namebtn.is-left { color: var(--ink-soft); }
.roster:has(.kw-tablebox) { border: none; }
.listbar.is-picking { background: var(--brand-700); color: #fff; border-radius: 12px; border-bottom-color: transparent; padding: .45rem .7rem; }
.listbar.is-picking .listbar-check, .listbar.is-picking .count { color: #fff; }
.listbar.is-picking .listbar-check input { accent-color: #FBB806; }
.listbar.is-picking .btn-ghost { background: transparent; color: #fff; border-color: rgba(255,255,255,.6); }
.listbar.is-picking .btn-primary { background: #FBB806; color: #2B2100; border-color: #FBB806; }

/* --- A class: header, tabs, classwork ------------------------------------ */
.kw-classhead { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px; padding: 16px 64px 16px 22px; border-radius: 16px; color: #fff; background-color: var(--brand-700); background-image: repeating-linear-gradient(0deg, rgba(255,255,255,.07) 0, rgba(255,255,255,.07) 1px, transparent 1px, transparent 17px); }
.kw-classhead .kw-bookmark { width: 20px; height: 40px; right: 24px; }
.kw-classhead-text { flex: 1 1 360px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.kw-classhead-text .kw-band-kicker { color: #CFE3DA; }
.kw-classhead h1 { font-size: 1.75rem; color: #fff; line-height: 1.15; }
.kw-classhead-sub { font-size: .9rem; font-weight: 700; color: #E2EEE8; }
.kw-classhead-acts { display: flex; flex-wrap: wrap; gap: 8px; }
.kw-rollbtn { height: 42px; padding: 0 16px; display: inline-flex; align-items: center; gap: 8px; border: none; border-radius: 11px; background: #FBB806; color: #2B2100; font: inherit; font-size: .9rem; font-weight: 800; cursor: pointer; }
.kw-editbtn { height: 42px; padding: 0 14px; border: 1.5px solid rgba(255,255,255,.7); border-radius: 11px; background: none; color: #fff; font: inherit; font-size: .9rem; font-weight: 800; cursor: pointer; }
.kw-rollbtn:hover, .kw-editbtn:hover { filter: brightness(1.08); }
#viewClass .tabs-bar { margin-top: .8rem; }
#viewClass .tab { text-transform: none; letter-spacing: 0; font-size: .95rem; font-weight: 800; color: #3E4B45; }
#viewClass .tab[aria-selected="true"] { color: var(--brand-700); border-bottom-color: var(--brand-700); }
.kw-tabcount { font-size: .82rem; font-weight: 700; color: var(--ink-soft); margin-left: 4px; }
#cwTermFilter { gap: 0; border: 1.5px solid var(--sand-300); border-radius: 11px; overflow: hidden; }
#cwTermFilter > * { border: none !important; border-radius: 0 !important; border-left: 1px solid var(--sand-200) !important; min-height: 38px; padding: 0 16px; font-size: .88rem; }
#cwTermFilter > *:first-child { border-left: none !important; }
@media (max-width: 720px) {
  .kwbar-in { padding: 6px 12px; }
  .kwbar-link { min-height: 44px; padding: 0 9px; }
  .kw-head h1 { font-size: 1.6rem; }
}

/* v1.3: a class page is as wide as the rest now; its tables have the room. */
#viewClass { max-width: 1200px; }

/* --- Schedule, day by day (v1.3, option A) ---------------------------- */
.kw-schedays { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 18px; }
.kw-schedday { display: flex; flex-direction: column; border: 1px solid var(--sand-200); border-radius: 16px; background: var(--field); overflow: hidden; }
.kw-schedday.is-today { border: 2px solid var(--brand-700); }
.kw-schedday-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 12px 18px; background: var(--sand-050); }
.kw-schedday-head h2 { font-size: 1.2rem; margin: 0; }
.kw-schedday-head span { font-size: .82rem; font-weight: 800; color: var(--ink-soft); }
.kw-schedday.is-today .kw-schedday-head { background: var(--brand-700); color: #fff; }
.kw-schedday.is-today .kw-schedday-head span { color: #FBB806; }
.kw-schedday-free { padding: 18px; }
.kw-schedrow { display: flex; align-items: center; gap: 14px; padding: 12px 18px; border-top: 1px solid #F0E6D8; color: var(--ink); text-decoration: none; }
.kw-schedrow:hover { background: var(--sand-050); }
.kw-schedrow.is-past { opacity: .82; }
.kw-schedrow-when { flex: 0 0 96px; display: flex; flex-direction: column; }
.kw-schedrow-when b { font-size: .95rem; }
.kw-schedrow-when span { font-size: .75rem; color: var(--ink-soft); }
.kw-schedrow-swatch { width: 4px; align-self: stretch; border-radius: 2px; }
.kw-schedrow-what { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.kw-schedrow-what b { font-size: .95rem; }
.kw-schedrow-what span { font-size: .82rem; color: var(--ink-soft); }
.kw-schedrow-roll { font-size: .82rem; font-weight: 800; color: var(--brand-700); white-space: nowrap; }
.kw-schedrow-roll.is-bad { color: var(--accent-500); }
.kw-schedrow-roll.is-now { color: #6E5100; }

/* --- Classwork cards as notebook pages (v1.3, the teacher's pick) ---------
   The original layout (title, topic, facts, mode line; icons at the right),
   drawn as a ruled page with a red margin line, the category as an index
   tab on top, and the class average stamped like a mark on a paper. */
.cw-list { gap: 1.45rem; padding-top: 1.1rem; }
.cw-sheet { --cat: var(--brand-700); position: relative; }
.cw-sheet.is-quiz { --cat: #5B5EA6; }
.cw-sheet.is-lab { --cat: #2E7D9A; }
.cw-sheet.is-assign { --cat: #A0447A; }
.cw-sheet.is-project { --cat: #3E5C53; }
.cw-sheet.is-recit { --cat: #6E5100; }
.cw-sheet.is-activity { --cat: #B5651D; }
.cw-sheet.is-exam { --cat: #6E5100; }
.cw-tab { position: absolute; left: 56px; top: -21px; height: 22px; padding: 0 12px; border-radius: 8px 8px 0 0;
  background: var(--cat); color: #fff; font-size: .68rem; font-weight: 800; letter-spacing: .08em; display: flex; align-items: center; }
.cw-sheet.is-exam .cw-tab { background: #FBB806; color: #2B2100; }
.cw-card { position: relative; gap: 1rem; min-height: 0; padding: .6rem .9rem .6rem 4.2rem;
  border: 1px solid var(--sand-200); border-radius: 6px 14px 14px 14px; background-color: var(--field);
  background-image: repeating-linear-gradient(180deg, transparent 0, transparent 25px, rgba(91, 127, 166, .13) 25px, rgba(91, 127, 166, .13) 26px);
  box-shadow: 0 2px 0 #EDE2D2, 0 6px 14px rgba(30, 43, 38, .06); transition: transform .15s, box-shadow .15s, border-color .12s; }
.cw-card:hover { background-color: var(--field); border-color: var(--cat); transform: translateY(-2px); box-shadow: 0 2px 0 #EDE2D2, 0 10px 22px rgba(30, 43, 38, .1); }
.cw-sheet.is-exam .cw-card { background-color: #FFF9E8; border-color: #EED48C; }
.cw-margin { position: absolute; left: 2.6rem; top: 0; bottom: 0; width: 2px; background: rgba(196, 72, 60, .45); }
.cw-title { font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1.15rem; line-height: 1.15; color: var(--ink); letter-spacing: 0; }
.cw-topic { margin-top: .05rem; font-size: .85rem; font-style: italic; color: #3E4B45; }
.cw-meta { font-size: .78rem; margin-top: .25rem; }
.cw-bit.is-exam, .cw-meta .cw-bit:first-child { color: var(--cat); font-weight: 800; }
.cw-stamp { flex: none; width: 66px; height: 66px; border-radius: 50%; border: 2.5px solid currentColor; display: flex; flex-direction: column;
  align-items: center; justify-content: center; transform: rotate(-9deg); background: rgba(255, 255, 255, .55); }
.cw-stamp b { font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1.05rem; line-height: 1; }
.cw-stamp small { font-size: .5rem; font-weight: 800; letter-spacing: .06em; margin-top: 2px; }
.cw-stamp.is-good { color: var(--brand-700); }
.cw-stamp.is-mid { color: #B07A00; }
.cw-stamp.is-low { color: #C4483C; }
.cw-stamp.is-empty { color: #A89C8A; border-style: dashed; }
.cw-iconbtn { border-radius: 10px; }
.cw-add { border-radius: 14px; }
@media (max-width: 720px) {
  .cw-card { flex-wrap: wrap; padding-left: 2.8rem; }
  .cw-margin { left: 1.6rem; }
  .cw-tab { left: 2.2rem; }
  .cw-stamp { width: 58px; height: 58px; }
}
.kw-schedrow-what { color: var(--ink); text-decoration: none; }
.kw-schedrow-what:hover b { color: var(--brand-700); text-decoration: underline; }
.kw-schedrow:hover { background: none; }
.kw-schedrow-take { flex: none; min-height: 34px; border-radius: 9px; }
.kw-schedrow-what b, .kw-schedrow-what span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kw-schedrow.is-past .kw-schedrow-take { opacity: 1; }

/* --- Rubric as a table, laid out like the printed one (v1.3, option B) ---
   One row per criterion: number, criterion, what earns it, who scores it
   (group work only), points. Sections are optional tinted bands with their
   subtotal. No pill shapes anywhere: the teacher asked for none. */
.kw-ed-rubrichead { align-items: center; flex-wrap: wrap; }
.kw-rub-who.is-you { color: #015443; }
.kw-rub-who.is-group { color: #A2561A; }
.kw-rub-who.is-peer { color: #4F529A; }

.kw-rubtable { border: 1px solid #DCCDB8; border-radius: 12px; overflow: hidden; background: #FFFDFA; }
.kw-rubtable .cw-rubrics { display: flex; flex-direction: column; }

.kw-rub-head,
.kw-rub-row {
  display: grid;
  grid-template-columns: 26px 30px minmax(0, 1fr) minmax(0, 1.6fr) 72px 34px;
}
.kw-rubtable.is-group .kw-rub-head,
.kw-rubtable.is-group .kw-rub-row {
  grid-template-columns: 26px 30px minmax(0, 1fr) minmax(0, 1.6fr) 150px 72px 34px;
}
.kw-rubtable:not(.is-group) .kw-rub-head > :nth-child(5),
.kw-rubtable:not(.is-group) .kw-rub-whocell { display: none; }

.kw-rub-head {
  align-items: center;
  min-height: 34px;
  background: var(--ink, #1E2B26);
  color: #FFFFFF;
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.kw-rub-head > * { padding: 0 .6rem; }
.kw-rub-head > :nth-child(2) { padding: 0; text-align: center; }
.kw-rub-head > :nth-child(6) { text-align: right; }

.kw-rub-row { align-items: stretch; min-height: 50px; border-top: 1px solid #EDE3D5; }
.kw-rub-row:nth-child(even of .kw-rub-row) { background: #FFFBF4; }
.kw-rub-row > * + * { border-left: 1px solid #F1E7D8; }
.kw-rub-row > .kw-rub-no, .kw-rub-row > .kw-rub-bin { border-left: 0; }
.kw-rub-no {
  display: flex; align-items: center; justify-content: center;
  font-size: .8rem; font-weight: 800; color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* The cells are the fields: no box until you point at one or type in it. */
.kw-rubtable textarea.cw-rubric-title,
.kw-rubtable textarea.cw-rubric-desc {
  width: 100%;
  min-height: 0;
  box-sizing: border-box;
  margin: 0;
  padding: .6rem .6rem;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  line-height: 1.4;
  resize: none;
  overflow: hidden;
  field-sizing: content;
  box-shadow: none;
}
.kw-rubtable textarea.cw-rubric-title { font-size: .88rem; font-weight: 800; }
.kw-rubtable textarea.cw-rubric-desc { font-size: .84rem; color: #3E4B45; }
.kw-rubtable textarea.cw-rubric-title:hover,
.kw-rubtable textarea.cw-rubric-desc:hover { background: #FFFFFF; }
.kw-rubtable textarea.cw-rubric-title:focus,
.kw-rubtable textarea.cw-rubric-desc:focus {
  background: #FFFFFF;
  outline: 2px solid var(--brand-700);
  outline-offset: -2px;
}

.kw-rub-ptscell { display: flex; align-items: center; justify-content: flex-end; padding: 0 .45rem; }

/* Who scores it: a plain field like the points box (neutral border, white,
   coloured text, a small caret), centred in its cell however tall the row
   gets. No coloured edge and no pill: the teacher asked for neither. */
.kw-rub-whocell { display: flex; align-items: center; justify-content: center; }
.kw-rub-who {
  width: 128px;
  height: 34px;
  min-height: 0;
  margin: 0;
  padding: 0 1.6rem 0 .6rem;
  border: 1.5px solid #E3D7C5;
  border-radius: 8px;
  background-color: #FFFFFF;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238A7F70' stroke-width='1.6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .75rem center;
  -webkit-appearance: none;
  appearance: none;
  box-shadow: none;
  font-size: .82rem;
  font-weight: 800;
  white-space: nowrap;
  cursor: pointer;
}
.kw-rub-who:hover { border-color: #CBBCA6; }
.kw-rub-who:focus { border-color: var(--brand-700); outline: 2px solid var(--brand-700); outline-offset: 0; }
.kw-rub-who option { color: var(--ink); }
.kw-rubtable .cw-rubric-score {
  width: 54px;
  height: 34px;
  min-height: 0;
  padding: 0 .45rem;
  border: 1.5px solid #E3D7C5;
  border-radius: 6px;
  background: #FFFFFF;
  text-align: center;
  font-size: .95rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.kw-rub-grip {
  display: flex; align-items: center; justify-content: center;
  width: 100%; padding: 0; border: 0; background: none;
  color: #BBAD98; font-size: .8rem; letter-spacing: -2px;
  cursor: grab;
}
.kw-rub-grip:hover, .kw-rub-grip:focus-visible { color: var(--brand-700); }
.kw-rub-bin {
  display: flex; align-items: center; justify-content: center;
  width: 100%; padding: 0; border: 0; background: none;
  color: #A8977F; cursor: pointer;
}
.kw-rub-bin:hover, .kw-rub-bin:focus-visible { color: #8A3A26; }

/* A section: a tinted band with its subtotal, the criteria under it. */
.kw-rub-sec {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) auto 34px;
  align-items: center;
  min-height: 40px;
  border-top: 1px solid #DCCDB8;
  background: #E9F3EE;
}
.kw-rubtable .cw-rubric-sec-title {
  min-height: 0;
  height: 34px;
  margin: 0 0 0 .2rem;
  padding: 0 .4rem;
  border: 0;
  border-radius: 6px;
  background: transparent;
  font-family: Fredoka, Nunito, sans-serif;
  font-size: 1rem;
  font-weight: 600;
  color: var(--brand-700);
}
.kw-rubtable .cw-rubric-sec-title:hover { background: rgba(255, 255, 255, .6); }
.kw-rubtable .cw-rubric-sec-title:focus { background: #FFFFFF; outline: 2px solid var(--brand-700); outline-offset: -2px; }
.kw-rubtable .cw-rubric-sec-sum { padding: 0 .75rem; font-size: .92rem; color: var(--brand-700); white-space: nowrap; }
.kw-rubtable .cw-rubric-sec-sum.is-empty { font-size: .8rem; color: #8A3A26; }

.kw-rub-addrow {
  display: block;
  width: 100%;
  min-height: 34px;
  padding: 0 0 0 56px;
  border: 0;
  border-top: 1px dashed #E6DACA;
  background: none;
  text-align: left;
  font-size: .78rem;
  font-weight: 800;
  color: var(--brand-700);
  cursor: pointer;
}
.kw-rub-addrow:hover, .kw-rub-addrow:focus-visible { background: #F4FAF6; }

/* Where a dragged row will land. */
.kw-rub-row.is-dragging, .kw-rub-sec.is-dragging { opacity: .45; }
.is-drop-above { box-shadow: inset 0 3px 0 var(--brand-700); }
.is-drop-below { box-shadow: inset 0 -3px 0 var(--brand-700); }

.kw-rub-total {
  display: flex;
  align-items: center;
  gap: .9rem;
  min-height: 46px;
  padding: 0 .9rem 0 1rem;
  border-top: 2px solid var(--ink, #1E2B26);
  background: #F6EEE2;
  font-size: .82rem;
}
.kw-rub-total[hidden] { display: none; }
.kw-rub-total > b:first-child { font-size: .85rem; letter-spacing: .05em; }
.kw-rub-total #kwRubSplit { flex: 1; color: var(--ink-soft); white-space: pre-wrap; }
.kw-rub-total #cwRubricTotal { font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1.35rem; color: var(--brand-700); }

.kw-ed-rubricbtns { align-items: center; }
.kw-rub-addsec {
  height: 36px;
  padding: 0 .9rem;
  border: 1.5px dashed #CBBCA6;
  border-radius: 8px;
  background: none;
  font-size: .82rem;
  font-weight: 800;
  color: var(--brand-700);
  cursor: pointer;
}
.kw-rub-addsec span { font-weight: 600; color: var(--ink-soft); }
.kw-rub-addsec:hover, .kw-rub-addsec:focus-visible { border-color: var(--brand-700); }
.kw-rub-tip { margin-left: auto; font-size: .8rem; color: var(--ink-soft); }
.kw-rub-tip[hidden] { display: none; }

/* On a phone each criterion stacks: title and points, then what earns it,
   then who scores it. */
@media (max-width: 720px) {
  .kw-rub-head { display: none; }
  .kw-rub-row,
  .kw-rubtable.is-group .kw-rub-row {
    grid-template-columns: 26px 24px minmax(0, 1fr) 64px 34px;
    grid-template-areas:
      "grip no title pts bin"
      ".    .  desc  desc desc"
      ".    .  who   who  who";
    padding-bottom: .4rem;
  }
  .kw-rub-row > * + * { border-left: 0; }
  .kw-rub-row .kw-rub-grip { grid-area: grip; }
  .kw-rub-row .kw-rub-no { grid-area: no; }
  .kw-rub-row .cw-rubric-title { grid-area: title; }
  .kw-rub-row .cw-rubric-desc { grid-area: desc; padding-top: 0; }
  .kw-rub-row .kw-rub-whocell { grid-area: who; justify-content: flex-start; padding: 0 .6rem 0 .6rem; }
  .kw-rub-row .kw-rub-ptscell { grid-area: pts; }
  .kw-rub-row .kw-rub-bin { grid-area: bin; }
  .kw-rub-who { width: 9rem; }
  .kw-rub-addrow { padding-left: 50px; }
  .kw-rub-tip { display: none; }
}

/* The classwork editor on a narrow screen: the side column goes under the
   main one and the two scroll as one page, instead of squeezing the main
   pane to a sliver beside a fixed 360px column. */
@media (max-width: 820px) {
  .kw-ed-body { display: flex; flex-direction: column; overflow-y: auto; }
  .kw-ed-main, .kw-ed-side { flex: none; min-height: auto; overflow: visible; }
  .kw-ed-main { padding: .8rem 1rem 1rem; }
  .kw-ed-side { border-left: 0; border-top: 1px solid var(--sand-200); }
  .kw-ed-tabbar { flex-wrap: wrap; gap: .4rem; padding-bottom: .5rem; }
  .kw-ed-tabs { flex: 1 0 100%; }
  .kw-ed-tabs button { flex: 1; padding: 0 .4rem; white-space: nowrap; }
  .kw-ed-count { display: none; }
  .kw-ed-paste { padding: 0 .6rem; }
}

/* A long "What earns it" makes its row taller, and only that cell grows:
   the handle, number, criterion, points and bin keep the first line's height
   at the top (Scored by stays centred), and the column lines run the full row. */
@media (min-width: 721px) {
  .kw-rub-row > .kw-rub-grip,
  .kw-rub-row > .kw-rub-no,
  .kw-rub-row > .kw-rub-bin { align-self: start; height: 50px; }
  .kw-rub-ptscell { align-items: flex-start; padding-top: 8px; }
  .kw-rubtable textarea.cw-rubric-title,
  .kw-rubtable textarea.cw-rubric-desc { padding-top: .85rem; }
}

/* --- The Groups tab in the classwork editor (v1.3) -----------------------
   The groups are shown and changed right here: make them, copy them from an
   earlier activity, move people by tapping, print. Plain text and lines only:
   no chips, pills or tinted tags (the teacher's rule). */
.kw-gp { display: flex; flex-direction: column; gap: 1rem; }
.kw-gp[hidden] { display: none; }

.kw-gp-head { display: flex; align-items: center; flex-wrap: wrap; gap: .6rem 1rem; }
.kw-gp-title { flex: 1; min-width: 0; display: flex; align-items: baseline; flex-wrap: wrap; gap: .3rem .7rem; }
.kw-gp-sum { font-family: Fredoka, Nunito, sans-serif; font-size: 1.3rem; font-weight: 600; color: var(--ink); }
.kw-gp-sizes { display: inline-flex; flex-wrap: wrap; gap: .3rem .7rem; font-size: .95rem; color: var(--ink-soft); }
.kw-gp-warn { color: #8A5A00; font-weight: 800; }
.kw-gp-actions { display: flex; gap: .5rem; }
.kw-gp-src { display: flex; gap: .6rem; margin-top: -.6rem; font-size: .82rem; color: var(--ink-soft); }
.kw-gp-src:empty { display: none; }

/* One look for every control here: 38px tall, 8px corners, a neutral line. */
.kw-gp-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  height: 38px; padding: 0 .9rem; white-space: nowrap;
  border: 1.5px solid #E3D7C5; border-radius: 8px; background: #FFFFFF;
  font: inherit; font-size: .85rem; font-weight: 800; color: var(--ink);
  cursor: pointer;
}
.kw-gp-btn:hover:not(:disabled) { border-color: #CBBCA6; }
.kw-gp-btn:disabled { opacity: .45; cursor: default; }
.kw-gp-btn.is-primary { border-color: var(--brand-700); background: var(--brand-700); color: #FFFFFF; }
.kw-gp-btn.is-primary:hover { background: var(--brand-800, #013F33); }

/* Make groups / Copy groups: a label column, the fields, the button. */
.kw-gp-tools {
  display: flex; flex-direction: column;
  border: 1px solid var(--sand-200); border-radius: 12px; background: #FFFDFA;
}
.kw-gp-line {
  display: grid; grid-template-columns: 8.5rem minmax(0, 1fr) auto;
  align-items: center; gap: .75rem; padding: .7rem .9rem;
}
.kw-gp-line + .kw-gp-line { border-top: 1px solid #F1E7D8; }
.kw-gp-label { font-size: .72rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); }
.kw-gp-fields { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; min-width: 0; }
.kw-gp-fields select,
.kw-gp-fields input {
  width: auto; height: 38px; min-height: 0; margin: 0;
  padding: 0 .6rem; border: 1.5px solid #E3D7C5; border-radius: 8px;
  background-color: #FFFFFF; font-size: .85rem; font-weight: 700;
}
.kw-gp-fields input { width: 4.2rem; text-align: center; font-weight: 800; }
#kwGpReuse { width: 100%; max-width: 26rem; }
#kwGpReuse[hidden], #kwGpReuseNone[hidden] { display: none; }
.kw-gp-note { font-size: .82rem; color: var(--ink-soft); white-space: nowrap; }

.kw-gp-link {
  padding: 0; border: 0; background: none;
  font: inherit; font-weight: 800; color: var(--brand-700);
  cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
}


/* Who is in no group: just their names. */
.kw-gp-loose {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: .4rem .9rem;
  padding: .6rem 0 .7rem; border-bottom: 1px solid var(--sand-200);
}
.kw-gp-loose[hidden] { display: none; }
.kw-gp-loose-label { font-size: .85rem; color: #8A5A00; }
.kw-gp-loose-names { display: contents; }
.kw-gp-name {
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: .88rem; font-weight: 700; color: var(--ink);
  text-decoration: underline dotted #B9AC98; text-underline-offset: 3px;
}
.kw-gp-name:hover { color: var(--brand-700); }
.kw-gp-name[aria-pressed="true"] { color: var(--brand-700); font-weight: 800; text-decoration: underline solid var(--brand-700) 2px; }
.kw-gp-hint { font-size: .8rem; color: var(--ink-soft); }

.kw-gp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); grid-auto-rows: 1fr; gap: .7rem; }
.kw-gp-empty { margin: 0; font-size: .9rem; color: var(--ink-soft); }
.kw-gp-card {
  display: flex; flex-direction: column;
  border: 1px solid var(--sand-200); border-radius: 12px; background: #FFFDFA;
  overflow: hidden;
}
.kw-gp-card.is-target { border-color: #8DBBA6; }
.kw-gp-card.is-new { border-style: dashed; background: none; justify-content: center; min-height: 80px; }
.kw-gp-card-head { display: flex; align-items: baseline; gap: .5rem; padding: .55rem .75rem .4rem; }
.kw-gp-card-head b { flex: 1; font-family: Fredoka, Nunito, sans-serif; font-weight: 600; font-size: 1rem; color: var(--brand-700); }
.kw-gp-count { font-size: .75rem; font-weight: 700; color: var(--ink-soft); }
.kw-gp-member {
  display: block; width: 100%; padding: .38rem .75rem;
  border: 0; border-top: 1px solid #F1E7D8; background: none;
  text-align: left; font: inherit; font-size: .86rem; font-weight: 600; color: var(--ink);
  cursor: pointer;
}
.kw-gp-member:hover { background: #F7F1E8; }
.kw-gp-member[aria-pressed="true"] { background: #E4F1EA; color: var(--brand-700); font-weight: 800; }
.kw-gp-drop {
  margin: .45rem .6rem .6rem; min-height: 32px; padding: 0 .6rem;
  border: 1.5px dashed var(--brand-700); border-radius: 8px; background: none;
  font: inherit; font-size: .8rem; font-weight: 800; color: var(--brand-700);
  cursor: pointer;
}
.kw-gp-drop:hover { background: #F4FAF6; }
.kw-gp-loose .kw-gp-drop { margin: 0; }

/* How the rubric scores the groups: three plain columns. */
.kw-gp-rubric { display: flex; flex-direction: column; gap: .5rem; padding-top: .8rem; border-top: 1px solid var(--sand-200); }
.kw-gp-rubric-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; font-size: .92rem; }
.kw-gp-rubric-head .kw-gp-link { font-size: .82rem; text-decoration: none; }
.kw-gp-rubric-cols { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.kw-gp-col { display: flex; flex-direction: column; gap: .15rem; padding: .2rem .9rem; }
.kw-gp-col:first-child { padding-left: 0; }
.kw-gp-col + .kw-gp-col { border-left: 1px solid var(--sand-200); }
.kw-gp-col-head { font-size: .72rem; letter-spacing: .06em; text-transform: uppercase; }
.kw-gp-col.is-you .kw-gp-col-head { color: #015443; }
.kw-gp-col.is-group .kw-gp-col-head { color: #A2561A; }
.kw-gp-col.is-peer .kw-gp-col-head { color: #4F529A; }
.kw-gp-col-list { font-size: .88rem; font-weight: 700; }
.kw-gp-col-note { font-size: .78rem; color: var(--ink-soft); }
.kw-gp-rubric-cols > .kw-gp-empty { grid-column: 1 / -1; }

.kw-gp-foot { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem 1rem; font-size: .84rem; color: var(--ink-soft); }
.kw-gp-stop {
  margin-left: auto; padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; font-weight: 800; color: #8A3A26;
}
.kw-gp-stop:hover { text-decoration: underline; }

@media (max-width: 720px) {
  .kw-gp-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  .kw-gp-line { grid-template-columns: 1fr; gap: .5rem; }
  .kw-gp-line .kw-gp-btn { width: 100%; }
  #kwGpReuseNone { white-space: normal; }
  .kw-gp-rubric-cols { grid-template-columns: 1fr; }
  .kw-gp-col, .kw-gp-col:first-child { padding: .4rem 0; }
  .kw-gp-col + .kw-gp-col { border-left: 0; border-top: 1px solid var(--sand-200); }
}

/* Bigger view: every group box is the same height, set from the biggest
   group (--kw-gslots names, from renderGroupPicker); more than that scrolls. */
.kw-gcols { align-items: stretch; }
.kw-gcol .kw-glist {
  height: calc(var(--kw-gslots, 4) * 38px + 12px);
  min-height: 0;
  overflow-y: auto;
}
.kw-gadd { min-height: 0; }

/* The ID number in the Students tables: click it to type. It reads as plain
   text until hovered, then shows it can be edited; the field keeps the cell's
   size so the row does not jump. */
.kw-idcell { display: inline-flex; }
.kw-idbtn {
  min-width: 7.5rem; height: 32px; padding: 0 .5rem; margin-left: -.5rem;
  border: 1.5px solid transparent; border-radius: 8px; background: none;
  font: inherit; font-variant-numeric: tabular-nums; text-align: left;
  color: var(--ink); cursor: text;
}
.kw-idbtn.is-empty { color: #8A3A26; font-weight: 700; }
.kw-idbtn:hover, .kw-idbtn:focus-visible { border-color: #E3D7C5; background: #FFFFFF; outline: none; }
.kw-idinput {
  width: 7.5rem; height: 32px; min-height: 0; padding: 0 .5rem; margin: 0 0 0 -.5rem;
  border: 1.5px solid var(--brand-700); border-radius: 8px; background: #FFFFFF;
  font: inherit; font-variant-numeric: tabular-nums; letter-spacing: .02em;
}
.kw-idinput:focus { outline: 2px solid rgba(1, 84, 67, .25); outline-offset: 1px; }
.kw-idinput.is-bad { border-color: #8A3A26; outline-color: rgba(138, 58, 38, .25); }

/* Exam rules: where students get the Exam Lock app. */
.kwex-app { display: grid; gap: .5rem; }
.kwex-applink { flex: 1 1 22rem; min-width: 0; height: 38px; min-height: 0; padding: 0 .7rem; border: 1.5px solid #E3D7C5; border-radius: 8px; background: #FFFFFF; }

/* The category before one is picked: reads as a question, not a value. */
#kwCategory.is-unset { color: var(--ink-soft); font-style: italic; }
#kwCategory option { font-style: normal; color: var(--ink); }

/* The Exam tab inside the classwork editor: the set-up sections, without the
   exam dialog's own frame. */
#kwEdExam .kwex-pane { padding: 0; }
#kwEdExam .kw-ed-empty b { font-size: 1rem; }
#kwEdExam .kw-ed-empty span { max-width: 32rem; color: var(--ink-soft); }
#kwEdExam .kwex-sec > .btn { align-self: flex-start; }
#kwToExamTab { align-self: flex-start; }

/* Download all (ZIP), at the end of the "Turn in online" bar in Scores. */
.kw-subzip { margin-left: auto; }

/* The rubric board for classwork turned in online: the student's files above
   the criteria, with the room to read them. */
.cw-board.has-files .cw-board-list,
.cw-board.has-files .cw-board-detail { max-height: min(72vh, 52rem); }
.cw-board-work { display: flex; flex-direction: column; gap: .35rem; margin: .4rem 0 1rem; }
.cw-board-work-head { margin: 0; font-size: .82rem; font-weight: 700; color: var(--ink-soft); }
.cw-board-work .kw-mark-view { height: 24rem; padding: 0; border: 1px solid var(--sand-200); border-radius: 12px; overflow: hidden; }
.cw-board-work .kw-mark-files { padding: .5rem .6rem 0; }
.cw-board-work .kw-mark-pic, .cw-board-work .kw-mark-pdf, .cw-board-work .kw-mark-code { margin: 0 .6rem .6rem; }

/* Turned in online: how many files, and how large. */
.kw-limits { display: flex; align-items: center; flex-wrap: wrap; gap: .4rem; font-size: .85rem; font-weight: 700; color: var(--ink); }
.kw-limits input { width: 4.2rem; height: 34px; min-height: 0; margin: 0; padding: 0 .4rem; border: 1.5px solid #E3D7C5; border-radius: 8px; background: #FFFFFF; text-align: center; font-weight: 800; }
