/* =============================================================================
   SKOOLSOS — shell additions on top of the Edudash theme, and the print sheet.
   ========================================================================== */

:root {
  --skoolsos-primary: #25A194;
}

/* The school identity block in the top bar. */
.school-identity {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.school-identity img {
  width: 40px;
  height: 40px;
  object-fit: contain;
  flex-shrink: 0;
}
/* Two lines rather than one line and an ellipsis. "Wanda Schools and Colleges"
   truncated to "Wanda Schools an…" is not the school's name, and this block is
   the thing that tells somebody which school they are signed in to. */
.school-identity .school-name {
  font-weight: 600;
  line-height: 1.2;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.school-identity .school-motto {
  font-size: 11px;
  opacity: 0.7;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Toasts stack in the corner and are never allowed over a modal. */
#toastHost {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 1050;
  max-width: 360px;
}

/* The idle lock covers everything, including the sidebar — a locked screen
   that still shows the class list is not locked. */
#lockOverlay {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(17, 24, 39, 0.92);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The print area is invisible on screen and is the ONLY thing visible on
   paper. Keeping it in the same document means the app's own stylesheet
   applies, so printed tables match the ones on screen. */
.print-area {
  display: none;
}

/* =============================================================================
   PRINT

   Tuned for paper, not for the screen. Schools print class lists, registers and
   report cards in hundreds, so every millimetre of margin and every point of
   font size is a real cost in sheets and toner.

   The choices below: a slim running letterhead rather than a full-page one, a
   9pt table with tight cell padding, headings that repeat across pages, rows
   that never split, and no heavy fills — a grey header band on a 40-row table
   is a surprising amount of toner for no information.
   ========================================================================== */
@media print {

  /* Hide the application chrome rather than rebuilding the page: one rule, and
     anything added to the shell later is hidden by default.

     EXCEPT ON A REPORT TAB, where the page IS the printout.

     Everything else in the app prints by building a sheet into #printArea and
     hiding the rest, so this rule is exactly right for them. An analysis report
     does the opposite: the tab already drops the sidebar and the toolbar, and
     `window.print()` is meant to put the page itself on paper. This rule hid
     that page too — #printArea is empty on a report tab, so Print produced a
     blank sheet with the header and footer and no marks at all.

     So the blanket hide skips the report tab, and the empty #printArea is
     hidden there instead. */
  body:not(.report-tab) > *:not(#printArea) { display: none !important; }
  body.report-tab #printArea { display: none !important; }

  /*
   * AND A REPORT TAB NEEDS A PAPER MARGIN OF ITS OWN.
   *
   * `@page { margin: 0 }` below is right for the sheet printer, which puts the
   * margin back as padding on its repeating header and footer rows so every
   * page gets one. A report tab has no such rows — it is the page itself — so
   * with a zero margin its table runs into the non-printable edge and the first
   * and last columns are clipped by the printer.
   *
   * Landscape for the wide ones. Twenty-five columns down a portrait sheet is
   * not a table anybody can read.
   */
  body.report-tab { page: reportportrait; }
  body.report-tab.report-wide { page: reportlandscape; }
}

/* NO BROWSER HEADER OR FOOTER. The browser prints its own header and footer (page title, date, the
   app's address) in the top and bottom page margins, but only when those
   margins are deeper than about 8 mm. Kept at 6 mm, there is no room for
   them, so nothing needs unticking in the print dialogue. */
@page reportportrait { size: A4 portrait; margin: 6mm 10mm; }
@page reportlandscape { size: A4 landscape; margin: 6mm 10mm; }

@media print {

  html, body {
    background: #fff !important;
    margin: 0;
    padding: 0;
  }

  .print-area {
    display: block !important;
    position: relative;
    color: #000;
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    font-size: 9pt;
  }

  /* EVERYTHING ON PAPER IS BLACK.
     The app runs a dark theme, and the theme sets colours EXPLICITLY rather
     than letting them inherit — `h1..h6 { color: var(--text-primary-light) }`,
     the `.text-secondary-light` helpers, and so on. Those declarations are not
     inside a screen-only media query, so they follow the markup onto paper: the
     report title was computing to near-white, and printing white on white. The
     .print-area colour above cannot fix that on its own, because an explicit
     declaration on the element always beats a colour inherited from its parent.

     Backgrounds are deliberately NOT reset here: the sheet's tbody cell carries
     the crest as an inline background-image, and a blanket
     `background: none !important` would erase it. The cards and badges further
     down handle their own. */
  .print-area,
  .print-area * {
    color: #000 !important;
    text-shadow: none !important;
  }

  /* ...AND EVERY SURFACE IS WHITE, for the same reason.
     The theme paints cards, panels, avatars and table stripes with solid
     surfaces that are near-black in dark mode. Those follow the markup onto
     paper: a student record printed as a page of black cards is unreadable and
     an absurd amount of toner. Anything that still needs to be distinguishable
     — badges, table cells, the header rule — is given a border further down.

     Scoped to these four regions rather than to .print-area so the sheet's
     tbody cell is left alone: it carries the crest, and a blanket reset would
     erase the thing the school asked to have on every page. */
  .print-header, .print-header *,
  .print-title,  .print-title *,
  .print-body,   .print-body *,
  .print-footer, .print-footer * {
    background: none !important;
    background-color: transparent !important;
    box-shadow: none !important;
  }

  /* Headings inside printed content are the theme's screen sizes — 60px on a
     sheet of A4 is most of a fifth of the page for one line. */
  .print-body h1, .print-body h2, .print-body h3,
  .print-body h4, .print-body h5, .print-body h6 {
    font-size: 10pt !important;
    font-weight: 700;
    margin: 0 0 1mm !important;
    line-height: 1.2 !important;
  }

  /* NO PAGE MARGIN — deliberately, and not to save paper.
     The browser draws its OWN header and footer in the page margin: the page
     title, the date, and the URL of the app. A class list handed to a parent
     should not carry the internal address of the school's system, and it is not
     something the person printing should have to remember to untick in the
     dialog every time. With no margin there is nowhere to draw them.

     The margin the paper still needs is put back as PADDING on the repeating
     header and footer rows below, which means it is applied to every page
     rather than once to the document. */
  @page {
    size: A4 portrait;
    margin: 0;
  }
  /* A wide table asks for landscape — the caller passes `landscape: true`, and
     it saves the shrunken, unreadable print a squeezed portrait table gives.
     The named page has to be CLAIMED with `page:`; declaring @page landscape
     and never naming it, as this did, is dead CSS — the audit trail was
     quietly printing in portrait with its columns crushed. */
  .print-landscape { page: landscape; }
  @page landscape {
    size: A4 landscape;
    margin: 0;
  }

  /* THE RUNNING HEADER AND FOOTER.
     Carried by the sheet table's thead/tfoot — see the note in ui.print(). The
     table must be full width and must not collapse its own borders, or the
     repeated groups pick up the table border on every page. */
  .print-sheet {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
  }
  .print-sheet > thead { display: table-header-group; }
  .print-sheet > tfoot { display: table-footer-group; }
  .print-sheet > thead > tr > td,
  .print-sheet > tbody > tr > td,
  .print-sheet > tfoot > tr > td {
    border: 0;
    vertical-align: top;
  }
  /* The paper margin, carried by the rows that repeat — so every sheet gets it,
     not just the first. Comfortably clear of the non-printable edge that every
     printer reserves for itself.

     The HEIGHTS are pinned, and that matters for more than tidiness: the
     watermark tile is built to be exactly one page of content tall, and it can
     only stay one-per-page if these two bands are the size it was told they
     are. 34mm + 247mm + 16mm = 297mm, one sheet of A4. On a table cell a height
     acts as a minimum, so nothing is clipped if a school has a long address. */
  .print-sheet > thead > tr > td { padding: 12mm 10mm 0; height: 34mm; }
  .print-sheet > tbody > tr > td { padding: 0 10mm; }
  .print-sheet > tfoot > tr > td { padding: 0 10mm 10mm; height: 16mm; }

  /* THE WATERMARK — one crest, centred, on every page.
     A background on the tbody cell rather than a fixed layer or a fixed
     background, because both of those reach only page one in Chrome — which is
     exactly how a real school printout came back with a crest on sheet 1 and
     nothing on sheet 2. The tbody box spans every page, so a background that
     REPEATS down it is drawn on all of them whatever the paginator does.

     ui.print() hands over a tile that is already ONE PAGE OF CONTENT tall with
     the crest centred in it and the fade painted in, so `repeat-y` at full
     width lays down exactly one crest per sheet at its true proportions. */
  .print-sheet__body {
    background-repeat: repeat-y;
    background-position: center top;
    background-size: 100% auto;
    /* 100% has to mean the 190mm of CONTENT, not the 210mm padding box, or the
       tile comes out 273mm tall and the one-crest-per-page arithmetic is wrong
       from the first sheet. */
    background-origin: content-box;
    background-clip: content-box;
    /* A floor, so a SHORT list still shows the crest: the tile's crest sits in
       the middle of a 247mm page, and a two-row list would otherwise only
       reveal the empty band above it. Kept under one page (34 + 240 + 16 =
       290mm) so it can never be the thing that pushes out a second sheet. */
    height: 240mm;
    /* Browsers strip background images from print unless asked not to. Without
       this the watermark simply does not appear on paper. */
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  .print-header {
    height: 17mm;
    display: flex;
    align-items: center;
    gap: 4mm;
    border-bottom: 1pt solid #000;
    padding-bottom: 1.5mm;
    margin-bottom: 3mm;
  }
  /* The two side cells share the leftover space EQUALLY (`flex: 1 1 0`), which
     is what puts the details on the centre line of the page. Sizing the middle
     cell instead would centre it between the badge and the term — off-centre on
     the sheet, and visibly so once the badge and the term differ in width. */
  .print-header__badge,
  .print-header__meta {
    flex: 1 1 0;
    min-width: 0;
  }
  .print-header__meta { text-align: right; }
  .print-badge {
    height: 14mm;
    width: auto;
    max-width: 100%;
    object-fit: contain;
    display: block;
  }
  .print-header__text {
    flex: 0 1 auto;
    min-width: 0;
    text-align: center;
  }
  .print-school {
    font-size: 14pt;
    font-weight: 700;
    letter-spacing: 0.2pt;
    line-height: 1.05;
  }
  .print-motto {
    font-size: 7.5pt;
    font-style: italic;
  }
  .print-contacts {
    font-size: 7pt;
  }
  /* The term, kept to the right of the letterhead where it is visible on every
     sheet without taking a line of its own. */
  .print-term {
    font-size: 8.5pt;
    font-weight: 600;
    white-space: nowrap;
  }

  .print-title {
    position: relative;
    z-index: 1;
    text-align: center;
    margin: 0 0 3mm;
  }
  /* !important is not decoration here. The Edudash theme ships
     `h2, .h2 { font-size: var(--h2) !important }` outside any media query, and
     --h2 clamps up to 3.75rem. Without matching it, the report title prints at
     about 45pt and takes a fifth of the sheet for one line. */
  .print-title h2 {
    font-size: 11pt !important;
    line-height: 1.2 !important;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5pt;
    margin: 0 !important;
  }
  .print-subtitle {
    font-size: 8.5pt;
    margin-top: 0.8mm;
  }

  .print-body {
    position: relative;
    z-index: 1;
  }

  .print-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 8.5pt;
  }
  .print-table th,
  .print-table td {
    border: 0.4pt solid #666;
    padding: 0.9mm 1.4mm;
    text-align: left;
    vertical-align: top;
    line-height: 1.25;
  }
  /* A rule under the headings rather than a filled band: it reads just as
     clearly and costs no toner across hundreds of sheets. */
  .print-table thead th {
    font-weight: 700;
    border-bottom: 1pt solid #000;
  }
  /* Repeat the column headings when a long list runs on — otherwise page three
     of a class list is a grid of unlabelled numbers. */
  .print-table thead { display: table-header-group; }
  .print-table tfoot { display: table-footer-group; }
  .print-table tr { page-break-inside: avoid; }

  /* Cards and panels reflowed for paper: the screen's boxes, shadows and
     rounded corners waste space and ink on a printed sheet. */
  .print-body .card,
  .print-body .shadow-1 {
    border: 0 !important;
    box-shadow: none !important;
    margin: 0 0 2mm !important;
    break-inside: avoid;
  }
  .print-body .card-body { padding: 0 !important; }
  .print-body .card-header {
    padding: 0 0 1mm !important;
    border-bottom: 0.4pt solid #999 !important;
    background: none !important;
  }
  /* KEEP THE GRID ON PAPER.
     The record cards lay their fields out on the Bootstrap grid, and this rule
     used to flatten it to `display: block`. That turned every side-by-side pair
     into two lines — forty times over on a student record, which is the
     difference between one sheet and three. */
  .print-body .row {
    display: flex;
    flex-wrap: wrap;
  }
  /* Detail rows are declared col-12 because a phone has to stack them. Paper is
     not a phone: pair them up and halve the page count. */
  .print-body .row > .col-12 {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .print-body .row > [class*="col-"] {
    break-inside: avoid;
    padding-right: 4mm;
  }

  /* The photo is worth printing — it is what makes a record identifiable at a
     desk. The EMPTY placeholder circle is not: it is 30mm of blank page saying
     "no photo", which the blank space says by itself. */
  .print-body span.w-120-px { display: none !important; }
  .print-body img.w-120-px {
    width: 24mm !important;
    height: 24mm !important;
    margin-bottom: 2mm !important;
  }
  .print-body .badge,
  .print-body [class*="bg-"] {
    background: none !important;
    color: #000 !important;
    border: 0.4pt solid #666;
    padding: 0 1mm;
  }

  /* Spacing kept tight on purpose: this band is pinned to 16mm, and the
     watermark tile is built assuming exactly that. */
  .print-footer {
    font-size: 7pt;
    text-align: center;
    border-top: 0.4pt solid #999;
    padding-top: 1mm;
    margin-top: 1mm;
  }

  .page-break { page-break-after: always; }

  /* Anything explicitly marked as screen-only never reaches paper — the
     "not connected yet" notices on sample tabs, for instance. */
  .no-print { display: none !important; }
}

/* -----------------------------------------------------------------------------
   Record forms.

   These were a slide-in drawer off the right edge (.my-sidebar). They are now
   centred modals: a form is one finished decision and belongs in the middle of
   the page, at a width that suits the number of fields rather than the height
   of the window. The drawer put a two-field form at the top of a mostly empty
   full-height column and a long one hard against the bottom of the screen.

   The theme's own .my-sidebar rules are left alone — this file no longer has
   anything to say about them.

   Two things the modal does need from us. Select2 renders its dropdown into
   the modal (ui.js sets dropdownParent), and it has to sit ABOVE the modal's
   own stacking context or the list opens behind the dialog. And a modal
   holding a long form must scroll its body, not the page.
   -------------------------------------------------------------------------- */
#panelHost .select2-container,
#modalHost .select2-container { z-index: 1060; }

#panelHost .modal-body,
#modalHost .modal-body { max-height: min(70vh, 720px); }

/* The tab strip that replaces the legacy show/hide button rows. */
.button-tab .nav-link {
  background: var(--neutral-200, #eef1f6);
  color: var(--text-secondary-light, #5b6b79);
  border: 0;
}
.button-tab .nav-link.active {
  background: var(--skoolsos-primary);
  color: #fff;
}

/* The template ships max-h-500-px but not the smaller step the section list
   on the dashboard needs, so the list scrolls inside its card instead of
   pushing the page down when a school has thirty streams. */
.max-h-200-px { max-height: 200px; }

/* The total that sits in the middle of a donut.
   Sized explicitly rather than with a heading class: the theme's h5 is built
   for a page title, and once a donut is small enough to share a dashboard row
   the number was wider than the hole and printed over the ring. */
.donut-center__value {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.15;
}
.donut-center__label { font-size: 11px; }

/* =============================================================================
   SIGN IN

   One white sheet on a pale page. The form sits on the left of it, plainly,
   with no card of its own; the school's own panel is inset on the right.

   Everything here is prefixed .auth- and is loaded only by /login. The signed-in
   app never sees these rules, so nothing below can reach a screen inside it.
   ========================================================================== */

.auth-page {
  --auth-brand: var(--skoolsos-primary, #25A194);
  --auth-ink: #101828;
  min-height: 100vh;
  padding: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  /* What shows if the photograph is slow, missing, or blocked. */
  background-color: #1c2333;
}

/* THE PHOTOGRAPH, on its own layer.
   Its own layer because it is blurred, and a filter on .auth-page itself would
   blur the sheet sitting on top of it. Scaled up slightly so the blur has
   material to work with at the edges instead of fading to nothing. */
.auth-page::before {
  content: '';
  position: fixed;
  inset: 0;
  background: url('../images/thumbs/login-img.png') center / cover no-repeat;
  filter: blur(5px);
  transform: scale(1.06);
  pointer-events: none;
}

/* THE DIM, in front of it.
   Not decoration: at full strength the photograph competes with the form for
   attention and the white sheet stops reading as the thing to look at. Blurred
   and dimmed, it is a setting rather than a subject. */
.auth-page::after {
  content: '';
  position: fixed;
  inset: 0;
  background: rgba(16, 24, 40, .62);
  pointer-events: none;
}

/* ...both of which the sheet has to sit above. */
.auth-sheet { position: relative; z-index: 1; }

/* Sized to its contents rather than stretched to the viewport, so it sits as a
   card in the middle of the page instead of a full-bleed panel with a margin.
   The shadow is heavier than before because it now floats over a photograph
   rather than a flat grey. */
.auth-sheet {
  width: 100%;
  max-width: 1040px;
  background: #fff;
  border-radius: 26px;
  box-shadow: 0 30px 70px rgba(0, 0, 0, .35);
  padding: 18px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}

/* ------------------------------------------------------------------- form */

.auth-formcol {
  display: flex;
  flex-direction: column;
  padding: 30px 20px 8px;
}

/* The form is held to a comfortable reading width and centred in its half,
   rather than stretched across it — a 600px-wide text box is a worse target
   than a 400px one, however much room there is. */
.auth-form {
  width: 100%;
  max-width: 400px;
  margin: auto;
}

.auth-form h1 {
  font-size: 30px !important;
  font-weight: 700;
  color: var(--auth-ink) !important;
  line-height: 1.15 !important;
  margin: 0 0 8px !important;
}
.auth-form__sub {
  font-size: 14.5px;
  color: #667085;
  margin: 0 0 28px;
  line-height: 1.5;
}

.auth-field { margin-bottom: 18px; }
.auth-field label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  color: #344054;
  margin-bottom: 7px;
}
.auth-input-wrap { position: relative; }
.auth-field input {
  width: 100%;
  height: 50px;
  padding: 0 44px 0 16px;
  font-size: 14.5px;
  color: var(--auth-ink);
  background: #fff;
  border: 1px solid #dfe3ea;
  border-radius: 12px;
  outline: none;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.auth-field input::placeholder { color: #98a2b3; }
.auth-field input:focus {
  border-color: var(--auth-brand);
  box-shadow: 0 0 0 4px rgba(37, 161, 148, .14);
}
.auth-field__icon {
  position: absolute;
  right: 16px;
  top: 50%;
  transform: translateY(-50%);
  color: #98a2b3;
  cursor: pointer;
  line-height: 1;
  font-size: 18px;
}

/* The row under the password: a reminder on the left, the way out on the
   right. There is no self-service reset here — accounts are issued by the
   school — so the right-hand side says who to ask instead of linking to a
   page that would only tell somebody to ask them anyway. */
.auth-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: -2px 0 22px;
  font-size: 13.5px;
}
.auth-remember {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: #475467;
  cursor: pointer;
  user-select: none;
}
.auth-remember input {
  width: 17px;
  height: 17px;
  accent-color: var(--auth-brand);
  cursor: pointer;
  margin: 0;
}
.auth-meta__help {
  color: var(--auth-brand);
  font-weight: 500;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.auth-meta__help:hover { text-decoration: underline; }

/* The one full-strength action on the page, in the school's own colour — the
   same colour as the panel opposite, so the screen reads as one thing. */
.auth-submit {
  width: 100%;
  height: 52px;
  text-align: center;
  border: 0;
  border-radius: 999px;
  background: var(--auth-brand);
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: filter .15s ease, transform .05s ease;
}
.auth-submit:hover:not(:disabled) { filter: brightness(.93); }
.auth-submit:active:not(:disabled) { transform: translateY(1px); }
.auth-submit:disabled { opacity: .6; cursor: default; }

.auth-error {
  background: #fef3f2;
  border: 1px solid #fecdca;
  color: #b42318;
  font-size: 13.5px;
  border-radius: 10px;
  padding: 11px 14px;
  margin-bottom: 18px;
}

.auth-form__foot {
  margin: 20px 0 0;
  text-align: center;
  font-size: 13px;
  color: #667085;
  line-height: 1.6;
}

.auth-copyright {
  margin-top: auto;
  padding-top: 24px;
  text-align: center;
  font-size: 12.5px;
  color: #98a2b3;
}

/* ------------------------------------------------------------------ panel */

.auth-brandpanel {
  position: relative;
  overflow: hidden;
  border-radius: 20px;
  padding: 36px;
  display: flex;
  flex-direction: column;
  color: #fff;
  background: var(--auth-brand);
}
/* A soft light from the top left. Enough to keep a flat brand colour from
   reading as a plain block, not enough to notice. */
.auth-brandpanel::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(110% 80% at 15% 0%, rgba(255, 255, 255, .14), transparent 62%);
  pointer-events: none;
}
.auth-brandpanel > * { position: relative; }

/* The school's identity, stacked and centred at the top of the panel.

   The badge leads. A school crest is the thing staff recognise from across a
   room, and at 42px beside the name it read as a favicon — this is the school's
   own sign-in screen, so its badge should look like it belongs to them rather
   than like a browser tab icon. */
.auth-brandpanel__mark {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 20px;
  margin-bottom: 40px;
}
/* On a white plate: most school crests are dark, detailed line art drawn for
   paper, and laid straight onto a saturated brand colour they disappear. The
   plate is what makes a real crest legible without asking the school to
   redraw it. */
.auth-brandpanel__mark img {
  height: clamp(88px, 9vw, 120px);
  width: clamp(88px, 9vw, 120px);
  object-fit: contain;
  background: #fff;
  border-radius: 26px;
  padding: 10px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, .22);
}
/* The name is now the largest thing on the panel and carries the modern
   display treatment: heavy weight, tight tracking, and balanced wrapping so
   "Wanda Schools and Colleges" breaks into even lines instead of leaving one
   word stranded on the last row. */
.auth-brandpanel__mark span {
  font-weight: 800;
  font-size: clamp(24px, 2.4vw, 34px);
  line-height: 1.12;
  letter-spacing: -.8px;
  text-wrap: balance;
  min-width: 0;
  max-width: 18ch;
}

/* The headline rotates. It is the only motion on the page, and it is what
   keeps a screen people open every morning from feeling dead. */
/* Stepped DOWN, deliberately. The school's own name is the headline of this
   panel now; leaving the strapline the same size as before would have left two
   things competing to be read first. */
.auth-headline {
  font-size: clamp(19px, 1.6vw, 24px) !important;
  font-weight: 700;
  line-height: 1.25 !important;
  letter-spacing: -.3px;
  margin: 0 0 12px !important;
  max-width: 22ch;
  color: #fff !important;
}
.auth-subline {
  font-size: 15px;
  line-height: 1.65;
  margin: 0;
  max-width: 46ch;
  color: rgba(255, 255, 255, .82);
}

/* Everything on the panel lines up under the badge. Mixing a centred crest
   with left-aligned text below it reads as a mistake rather than a choice. */
.auth-brandpanel { text-align: center; align-items: center; }
.auth-headline,
.auth-subline { margin-inline: auto; }
.auth-chips { justify-content: center; }
.auth-rotator { transition: opacity .45s ease, transform .45s ease; }
.auth-rotator.is-out { opacity: 0; transform: translateY(10px); }

.auth-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: auto;
  padding-top: 32px;
}
.auth-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgba(255, 255, 255, .14);
  border: 1px solid rgba(255, 255, 255, .24);
  color: #fff;
  font-size: 12px;
  font-weight: 500;
  padding: 6px 12px;
  border-radius: 999px;
}

/* ------------------------------------------------------------------ small */

@media (max-width: 1199.98px) {
  .auth-brandpanel { padding: 28px; }
  .auth-brandpanel__mark { margin-bottom: 26px; }
}

/* The same crest and name above the form, for the widths where the panel is
   not shown. Without it the school's identity on a phone was one grey line of
   body text — and a phone is where most staff sign in. */
.auth-formcol__mark {
  display: none;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 14px;
  margin-bottom: 28px;
}
/* Shown as soon as there is a school to show. The sign-in page hides it again
   at the widths where the brand panel carries the identity instead — see the
   min-width rule below — but the lock and first-password screens have no panel
   beside them and want it at every width. */
.auth-formcol__mark.is-ready { display: flex; }
.auth-formcol__mark img {
  height: 92px;
  width: 92px;
  object-fit: contain;
  border-radius: 22px;
  padding: 8px;
  background: #fff;
  box-shadow: 0 10px 26px rgba(16, 24, 40, .14);
}
.auth-formcol__mark span {
  font-weight: 800;
  font-size: 25px;
  line-height: 1.12;
  letter-spacing: -.7px;
  text-wrap: balance;
  max-width: 18ch;
  color: #101828;
}

@media (max-width: 991.98px) {
  /* The panel is decoration; on a small screen the form is the whole job. */
  .auth-sheet {
    grid-template-columns: 1fr;
    max-width: 520px;
    min-height: 0;
  }
  .auth-brandpanel { display: none; }
  .auth-formcol { padding: 20px 8px 4px; }
}

/* Wide enough for the brand panel: it carries the crest, so repeating it above
   the form would show the same badge twice on one screen. */
@media (min-width: 992px) {
  .auth-formcol .auth-formcol__mark { display: none; }
}

@media (max-width: 575.98px) {
  .auth-page { padding: 12px; }
  .auth-sheet { border-radius: 22px; padding: 14px; }
  .auth-form h1 { font-size: 28px !important; }
}

/* Motion is a nicety, and some people have asked their system not to show it. */
@media (prefers-reduced-motion: reduce) {
  .auth-rotator { transition: none; }
}

/* =========================================================================
   SELECT2
   -------------------------------------------------------------------------
   Select2 ships its own light-only look, which is neither the template's nor
   dark-mode aware. These rules make an upgraded dropdown indistinguishable
   from the plain .form-control beside it — same height, same border, same
   radius — and give it the dark palette when the app is in dark mode.

   Colours come from the template's own CSS variables rather than fixed hex,
   so a school that changes its primary colour changes these too.
   ========================================================================= */

/* Select2 wraps its control in a bare <span class="selection">, which computes
   as inline-block here and so shrinks to the width of the chosen text — the
   control ended up 147px wide inside a 328px field, with the arrow stranded at
   the far right. Making the wrapper a block lets the container's own width:100%
   reach the control. */
.select2-container .selection { display: block; }

.select2-container--default .select2-selection--single {
  height: 46px;
  padding: 6px 4px;
  border: 1px solid var(--input-form-dark, #d1d5db);
  border-radius: 8px;
  background-color: var(--white, #fff);
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
  line-height: 32px;
  color: var(--text-primary-light, #374151);
  padding-left: 12px;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
  height: 44px;
  right: 6px;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder {
  color: var(--text-secondary-light, #6b7280);
}
.select2-container--default.select2-container--focus .select2-selection--single,
.select2-container--default.select2-container--open .select2-selection--single {
  border-color: var(--primary-600, #487fff);
}

.select2-dropdown {
  border-color: var(--input-form-dark, #d1d5db);
  border-radius: 8px;
  background-color: var(--white, #fff);
  /* Above Bootstrap's modal (1055) — a dropdown that opens behind the dialog
     it belongs to is the classic Select2-in-a-modal failure. */
  z-index: 1060;
}
.select2-container--default .select2-results__option {
  color: var(--text-primary-light, #374151);
  padding: 8px 12px;
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
  background-color: var(--primary-600, #487fff);
  color: #fff;
}
.select2-container--default .select2-results__option[aria-selected="true"] {
  background-color: var(--primary-50, #eef2ff);
  color: var(--primary-600, #487fff);
}
.select2-container--default .select2-search--dropdown .select2-search__field {
  border: 1px solid var(--input-form-dark, #d1d5db);
  border-radius: 6px;
  padding: 8px 10px;
  background-color: var(--white, #fff);
  color: var(--text-primary-light, #374151);
}
.select2-results__option--group > .select2-results__group {
  color: var(--text-secondary-light, #6b7280);
  font-weight: 600;
  padding: 8px 12px 4px;
}

/* Dark mode. The template flips its variables, but Select2's own stylesheet
   hard-codes white backgrounds, so those have to be overridden by hand. */
[data-theme="dark"] .select2-container--default .select2-selection--single,
[data-theme="dark"] .select2-dropdown,
[data-theme="dark"] .select2-container--default .select2-search--dropdown .select2-search__field {
  background-color: var(--neutral-100, #1f2937);
  border-color: var(--neutral-300, #374151);
  color: var(--text-primary-light, #e5e7eb);
}
[data-theme="dark"] .select2-container--default .select2-selection--single .select2-selection__rendered,
[data-theme="dark"] .select2-container--default .select2-results__option {
  color: var(--text-primary-light, #e5e7eb);
}
[data-theme="dark"] .select2-container--default .select2-selection--single .select2-selection__arrow b {
  border-color: var(--text-secondary-light, #9ca3af) transparent transparent transparent;
}
[data-theme="dark"] .select2-container--default.select2-container--open
  .select2-selection--single .select2-selection__arrow b {
  border-color: transparent transparent var(--text-secondary-light, #9ca3af) transparent;
}

/* A select2 inside a table toolbar (rows per page) has to stay small, or the
   toolbar grows to the height of a form field for no reason. */
.dataTable-wrapper .select2-container--default .select2-selection--single,
.table-toolbar .select2-container--default .select2-selection--single {
  height: 34px;
  padding: 0;
}
.dataTable-wrapper .select2-container--default .select2-selection--single .select2-selection__rendered,
.table-toolbar .select2-container--default .select2-selection--single .select2-selection__rendered {
  line-height: 32px;
}
.dataTable-wrapper .select2-container--default .select2-selection--single .select2-selection__arrow,
.table-toolbar .select2-container--default .select2-selection--single .select2-selection__arrow {
  height: 32px;
}

/* =============================================================================
   PLATFORM SUPPORT BANNER

   Shown to a school while somebody from the platform is looking at their
   screens. It names who, says why, and is not dismissible — a support session
   somebody can hide is a support session that is effectively secret.
   ========================================================================== */
#supportBanner {
  position: sticky;
  top: 0;
  z-index: 1100;
  background: #7c2d12;
  color: #fff;
  padding: 9px 18px;
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  flex-wrap: wrap;
  text-align: center;
}
#supportBanner strong { font-weight: 700; }

/* A session that can CHANGE things looks different from one that can only
   look. Same banner, louder: red rather than brown, and it pulses for the
   first few seconds so somebody already on the page notices it arrive rather
   than discovering it later in their audit trail. */
#supportBanner.support-banner--writing {
  background: #b91c1c;
  animation: supportArrived 1.2s ease-in-out 0s 3;
}
#supportBanner.support-banner--writing .support-reason { color: #fecaca; }

@keyframes supportArrived {
  0%, 100% { background: #b91c1c; }
  50%      { background: #7f1d1d; }
}

/* Somebody who has asked not to see motion still gets the colour. */
@media (prefers-reduced-motion: reduce) {
  #supportBanner.support-banner--writing { animation: none; }
}
#supportBanner .support-reason {
  color: #fed7aa;
  font-style: italic;
}
#supportBanner button {
  background: rgba(255, 255, 255, .16);
  border: 1px solid rgba(255, 255, 255, .35);
  color: #fff;
  border-radius: 999px;
  padding: 3px 12px;
  font-size: 12px;
  cursor: pointer;
}
#supportBanner button:hover { background: rgba(255, 255, 255, .28); }

@media print {
  /* It is a screen warning, not part of anything the school prints. */
  #supportBanner { display: none !important; }
}

/* A raw print — ID cards, labels, badges. The ordinary print sheet paints the
   school letterhead and the crest watermark; those belong on a class list, not
   on a wall of ID cards, where the watermark shows through the children's
   faces. This strips both back to just what was handed to it. */
@media print {
  .print-area.print-raw::before,
  .print-area.print-raw::after { content: none !important; background: none !important; }
  .print-area.print-raw { padding: 0 !important; }
}

/* =============================================================================
   THE OPTIONS GRID — one row per student, one column per subject.

   113 students against 34 subjects is 3,842 tick boxes, and the table is twice
   as wide as the screen. Scrolled right by a few hundred pixels the student's
   name has gone, so somebody is ticking a row they cannot identify; scrolled
   down, the subject codes have gone too. Both directions produce the same
   quiet error — the right box in the wrong row.

   So the two columns that say WHO and the header row that says WHAT stay put.
   Backgrounds come from the theme's own variables rather than a literal white,
   because a sticky cell has rows sliding underneath it and must be opaque in
   both light and dark.
   ========================================================================== */

.options-grid {
  /* Its own scroll box. Without a height the header has nothing to stick
     inside and scrolls away with the page. */
  max-height: calc(100vh - 260px);
  overflow: auto;
}

/* The subject codes, across the top. */
.options-grid thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--neutral-50, #eef2f6);
}

/* Who the row belongs to: the number and the name. */
.options-grid tbody td:first-child,
.options-grid tbody td:nth-child(2) {
  position: sticky;
  z-index: 2;
  background: var(--white, #fff);
}
.options-grid thead th:first-child,
.options-grid thead th:nth-child(2) {
  position: sticky;
  z-index: 4;   /* the corner, above both the header and the name column */
}

.options-grid tbody td:first-child,
.options-grid thead th:first-child  { left: 0; }
.options-grid tbody td:nth-child(2),
.options-grid thead th:nth-child(2) { left: 44px; }

/* A row with nothing chosen is tinted, and the tint has to carry across the
   sticky cells or the highlight stops at the third column.

   Painted as a LAYER over the opaque card colour rather than used on its own:
   in dark mode --warning-50 is a 6% wash, and a frozen column with a see-
   through background has the scrolling rows sliding visibly underneath it. An
   ordinary cell gets its opacity from the card it sits on; a sticky one has to
   carry its own. */
.options-grid tbody tr.bg-warning-50 td:first-child,
.options-grid tbody tr.bg-warning-50 td:nth-child(2),
.options-grid tbody tr.bg-warning-50 td:last-child {
  background-color: var(--white, #fff);
  background-image: linear-gradient(var(--warning-50, #fff8e6), var(--warning-50, #fff8e6));
}

/* The running total, kept at the right edge: it is what the person filling
   the grid in is actually watching. */
.options-grid tbody td:last-child,
.options-grid thead th:last-child {
  position: sticky;
  right: 0;
  background: var(--white, #fff);
  z-index: 2;
}
.options-grid thead th:last-child { background: var(--neutral-50, #eef2f6); z-index: 4; }

/* An edge on the sticky columns so it reads as a frozen pane rather than as
   text that failed to scroll. */
.options-grid tbody td:nth-child(2),
.options-grid thead th:nth-child(2) { box-shadow: 1px 0 0 var(--neutral-200, #e5e7eb); }
.options-grid tbody td:last-child,
.options-grid thead th:last-child   { box-shadow: -1px 0 0 var(--neutral-200, #e5e7eb); }

/* Tighter tick columns: 34 of them, and every pixel saved is a subject that
   fits on screen without scrolling. */
.options-grid tbody td { padding: 6px 4px; }
.options-grid .form-check-input { margin: 0; }
.options-grid tbody td:nth-child(2) { min-width: 190px; }

/* =============================================================================
   TABLES ON A PHONE — one card per row.

   The student register is ten columns and 1,419px wide. On a 375px screen that
   is four screenfuls, of which TWO COLUMNS are visible at a time: reading one
   student means dragging the table sideways and back, and comparing two is not
   possible at all. It scrolls, so nothing looks broken; it is simply unusable.

   Below the tablet breakpoint each row becomes a card and each cell a labelled
   line. The label comes from `data-label`, which ui.table puts on every cell
   from the column's own heading — so this works for every table in the app
   without any screen being touched.

   Only tables built by ui.table opt in, via .table-stack. The matrices that
   are drawn by hand — the options grid, the permission matrix, the class list
   editor — are deliberately left alone: a grid whose whole meaning is the
   intersection of a row and a column does not survive being stacked.
   ========================================================================== */
@media (max-width: 767.98px) {

  .table-stack,
  .table-stack tbody,
  .table-stack tr,
  .table-stack td {
    display: block;
    width: auto;
  }

  /* The header row is what the labels replace. */
  .table-stack thead { display: none; }

  .table-stack > tbody > tr {
    border: 1px solid var(--neutral-200, #e5e7eb);
    border-radius: 10px;
    padding: 8px 12px;
    margin-bottom: 10px;
    background: var(--white, #fff);
  }

  /* The two-class selector is deliberate. The theme sizes cells with
     `.table > :not(caption) > * > *`, which counts as two classes and beats a
     plain `.table-stack td` however late it appears — 16px of padding and a
     27px line height on ten fields is a 643px card, one student per screenful
     of a phone. Matching its weight brings that to about a third. */
  .table-stack.table > tbody > tr > td {
    border: 0 !important;
    padding: 4px 0 !important;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 14px;
    text-align: right;
    font-size: 13px;
    line-height: 1.35;
  }

  /* The column's own heading, carried down by ui.table. */
  .table-stack.table > tbody > tr > td[data-label]::before {
    content: attr(data-label);
    flex: 0 0 40%;
    text-align: left;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary-light, #64748b);
  }

  /* A cell with no heading — the row number, the tick box, the row menu — has
     nothing to announce, so it sits on its own line rather than opposite an
     empty label. */
  .table-stack.table > tbody > tr > td:not([data-label]) { justify-content: flex-end; }

  /* The empty-state cell is a message, not a field. */
  .table-stack.table > tbody > tr > td[colspan] { display: block; text-align: center; }
  .table-stack.table > tbody > tr > td[colspan]::before { content: none; }

  /* Nothing to scroll sideways any more. */
  .table-stack { min-width: 0 !important; }

  /* A stacked table is a list of cards, and the card it sits in should not
     add a second frame around each one. */
  .card-body.p-0 > .p-0 > .table-responsive:has(.table-stack) { padding: 12px; }
}

/* -----------------------------------------------------------------------------
   THE OPTIONS GRID ON A SMALL SCREEN.

   The frozen columns are 357px wide, which on a 375px phone leaves NOTHING for
   the tick boxes — the pane that makes the grid readable on a laptop makes it
   unusable on a phone. So below the tablet breakpoint the number and the total
   stop being frozen and the name column is capped: one narrow frozen column
   still says whose row it is, and the rest of the screen is boxes to tick.
   -------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  /* !important, and not for want of trying anything else.
     The theme pins the first column of EVERY table with
     `.table > tbody tr > td:first-child { position: sticky !important }`.
     No amount of specificity beats an !important, so unpinning the row number
     here needs one of its own — without it the number stayed frozen and sat
     directly on top of the name it was supposed to sit beside. */
  .options-grid table > tbody > tr > td:first-child,
  .options-grid table > thead > tr > th:first-child,
  .options-grid table > tbody > tr > td:last-child,
  .options-grid table > thead > tr > th:last-child {
    position: static !important;
  }
  .options-grid table > tbody > tr > td:nth-child(2),
  .options-grid table > thead > tr > th:nth-child(2) {
    left: 0;
    min-width: 120px;
    max-width: 120px;
  }
  .options-grid tbody td:nth-child(2) { font-size: 11px; line-height: 1.25; }
  .options-grid { max-height: calc(100vh - 200px); }
}

/* -----------------------------------------------------------------------------
   THE ROW MENU, on a phone.

   It is anchored to the LEFT of its button and 160px wide, and in a stacked
   card that button sits at the right-hand edge — so View/Edit/Archive opened
   100px off the side of a 375px screen and could not be reached. Anchored to
   the right instead, it opens inwards and lands on the card.
   -------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  .table-stack .dropdown-menu {
    left: auto !important;
    right: 0 !important;
    max-width: calc(100vw - 40px);
  }
}

/* -----------------------------------------------------------------------------
   THE TOP BAR, when its contents no longer fit on one line.

   The theme fixes it at `height: 4.5rem`. On a phone the module switcher and
   the search take more than the width, so the row wraps — and the second line,
   carrying the theme toggle and the account menu, is drawn 32px BELOW the bar's
   own bottom edge, on top of the page title underneath it. The bar was the
   right height for its contents on a laptop and was never told to grow.
   -------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  .navbar-header {
    height: auto;
    padding-block: 10px;
  }
  .navbar-header > .row {
    row-gap: 10px;
  }
}

/* =============================================================================
   THE MARKS ENTRY BOARD — the legacy screen's own palette.

   This is the one screen in the system that is deliberately NOT themed. The
   staff read six of these boards a day and navigate them by colour alone:
   amber means the column is in, red means it is not, dark blue opens something,
   light blue is a template, green prints or imports. Those colours are
   Bootstrap 3's, straight out of the old system, and they have been the same
   for years.

   Two consequences follow, and both are on purpose:

     They do NOT follow the school's brand colour. A school whose primary is
     green would otherwise repaint "open this" in the same green as "print",
     and the one thing this screen must never do is make two different actions
     look alike.

     They are scoped to `.marks-board`, so nothing here reaches the rest of the
     application. The theme's own buttons are untouched everywhere else.
   -------------------------------------------------------------------------- */
.marks-board .lgbtn {
  display: inline-block;
  margin: 0 4px 4px 0;
  padding: 5px 10px;
  font-size: 12px;
  line-height: 1.5;
  font-weight: 400;
  text-align: center;
  vertical-align: middle;
  white-space: normal;
  border: 1px solid transparent;
  border-radius: 3px;
  cursor: pointer;
  color: #fff;
}
.marks-board .lgbtn:disabled,
.marks-board .lgbtn[disabled] {
  opacity: 0.65;
  cursor: not-allowed;
}

.marks-board .lgbtn-primary { background: #337ab7; border-color: #2e6da4; }
.marks-board .lgbtn-primary:hover:not([disabled]) { background: #286090; border-color: #204d74; }

.marks-board .lgbtn-info    { background: #5bc0de; border-color: #46b8da; }
.marks-board .lgbtn-info:hover:not([disabled])    { background: #31b0d5; border-color: #269abc; }

.marks-board .lgbtn-success { background: #5cb85c; border-color: #4cae4c; }
.marks-board .lgbtn-success:hover:not([disabled]) { background: #449d44; border-color: #398439; }

.marks-board .lgbtn-warning { background: #f0ad4e; border-color: #eea236; }
.marks-board .lgbtn-warning:hover:not([disabled]) { background: #ec971f; border-color: #d58512; }

.marks-board .lgbtn-danger  { background: #d9534f; border-color: #d43f3a; }
.marks-board .lgbtn-danger:hover:not([disabled])  { background: #c9302c; border-color: #ac2925; }

.marks-board .lgbtn-default {
  background: #fff;
  border-color: #ccc;
  color: #333;
}
.marks-board .lgbtn-default:hover:not([disabled]) { background: #e6e6e6; border-color: #adadad; }

/* The stream chips. `label label-warning` in the old markup — a small amber
   pill rather than a button, which is why they are a different shape from the
   class buttons above them. */
.marks-board .lglabel {
  display: inline-block;
  margin: 0 4px 4px 0;
  padding: 0.3em 0.6em 0.35em;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  color: #fff;
  text-align: center;
  border: 0;
  border-radius: 0.25em;
  cursor: pointer;
}
.marks-board .lglabel-warning { background: #f0ad4e; }
.marks-board .lglabel-warning:hover { background: #ec971f; }

/* The stream a board is currently showing. The legacy drew every chip the same
   and left you to read the heading above the table to know which one you were
   on; a ring adds the answer without changing a single colour. */
.marks-board .lglabel.is-on,
.marks-board .lgbtn.is-on {
  outline: 2px solid #1f2937;
  outline-offset: 1px;
}

/* The grid itself. Legacy cells are top-aligned with a stack of small buttons
   in them, and the table scrolls sideways rather than squeezing the columns —
   a term can carry eight exam batches and they will not fit any screen. */
.marks-board table > tbody > tr > td { vertical-align: top; }
.marks-board table > thead > tr > th { white-space: nowrap; }
.marks-board .board-scroll { overflow-x: auto; }

/* The grid is laid out on FIXED columns.

   Left to itself the browser sizes a table to its max-content width, and a row
   carrying seven buttons is one very long line — the whole Marks Entry column
   came out 1,800px wide and every batch after it was pushed off the end. Fixed
   columns make the buttons wrap inside their cell instead, which is how the old
   screen reads: a small block of buttons per column, and the table scrolls
   sideways only because a term really can have eight of them. */
.marks-board #boardTable {
  table-layout: fixed;
  width: auto;
  min-width: 100%;
}
.marks-board #boardTable th,
.marks-board #boardTable td {
  overflow-wrap: anywhere;
}

/* -----------------------------------------------------------------------------
   TIGHTER COLUMNS ON THE TWO MARKS GRIDS.

   Both of these are wide by nature — the board carries a column per exam batch
   and the whole-columns sheet carries a box per paper per batch — and the
   theme's table padding (11px each side) is generous for a five-column list and
   ruinous here. Eight batches cost 176px of padding alone, which is a whole
   column pushed off the page.

   So the padding comes down and the buttons come in. Nothing is made smaller
   than it can be read at: 11px on a button label and 8px of cell padding is
   what the old system used, and the staff have read it for years.
   -------------------------------------------------------------------------- */
.marks-board .table > :not(caption) > * > *,
.marks-sheet .table > :not(caption) > * > * {
  padding: 6px 8px;
}

.marks-board .lgbtn {
  padding: 4px 7px;
  font-size: 11px;
  margin: 0 3px 3px 0;
}
.marks-board .lglabel {
  font-size: 11px;
  margin: 0 3px 3px 0;
}

/* The mark boxes carry their own padding on top of the cell's, and a column of
   forty of them is where the width goes. */
.marks-sheet input[type="number"] {
  padding-inline: 4px;
}

/* =========================================================================
   A REPORT OPENED IN ITS OWN TAB.
   ========================================================================= */
/*
 * The analyses are read and printed, not worked in, so they open in a new tab
 * rather than in a modal over the board — which is what the school asked for
 * and what the legacy did with target="_blank". A tab lets somebody keep the
 * board open, put two analyses side by side, and print one of them without
 * losing their place.
 *
 * The shell stays exactly as it is; the report route adds `report-tab` to the
 * body and the chrome steps out of the way. Nothing is removed from the DOM,
 * so navigating back inside that tab restores the whole application.
 */
body.report-tab .sidebar,
body.report-tab .navbar-header,
body.report-tab footer { display: none !important; }

body.report-tab .dashboard-main { margin-left: 0 !important; width: 100% !important; }
body.report-tab .dashboard-main-body { margin: 0; padding: 16px 20px; }

/* -----------------------------------------------------------------------------
   AN ANALYSIS IS CENTRED, AND STOPS AT 90% OF THE SCREEN.

   With the sidebar gone the report had the whole window, and on a wide monitor
   that is the worst thing to give a table: a ranking of forty names ran the
   full 2,560px with the name at the far left and the average at the far right,
   and reading one row meant tracking across two feet of glass. Line length is
   the whole of readability here.

   90% rather than a fixed pixel width because these tables differ enormously —
   a subject comparison is six columns and a whole-class matrix is thirty — and
   a cap that suits one starves the other. The wide ones still scroll inside
   their own container; what this stops is the SHORT ones sprawling.

   Only above 1200px. Below it the screen is the constraint and the page should
   use all of it.
   -------------------------------------------------------------------------- */
@media (min-width: 1200px) {
  body.report-tab .dashboard-main-body > * {
    max-width: 90%;
    margin-inline: auto;
  }

  /*
   * A WIDE REPORT GETS THE ROOM THE SCHOOL'S OWN SHEET TAKES.
   *
   * 90% is right for a ranking or a distribution — those are short tables and
   * a long line is a hard line to read. The A-level marksheet is not that: it
   * is five subjects, each with its papers and an average, and the legacy gives
   * it 98% of the page up to 1,600px. Held to 90% on a laptop it fitted, but
   * only by wrapping "Mathematics" into "Mathe matics" and standing the paper
   * columns on end — which fits the page and loses the document.
   */
  body.report-tab.report-wide .dashboard-main-body > * {
    max-width: min(98%, 1600px);
  }
}

/* On paper there is no screen to be a proportion of, and a 90% column would
   just waste a margin the printer already leaves. */
@media print {
  body.report-tab .dashboard-main-body > * {
    max-width: none;
    margin-inline: 0;
  }
}

@media print {
  /* The toolbar is for the person at the screen; the paper does not need it. */
  body.report-tab .report-actions,
  body.report-tab .breadcrumb,
  body.report-tab [data-nav] { display: none !important; }

  body.report-tab .dashboard-main-body { padding: 0; }
  body.report-tab .card { box-shadow: none !important; border: 0 !important; }

  /* A long analysis is many pages, and a table without a repeating head is
     unreadable from page two onwards. */
  body.report-tab thead { display: table-header-group; }
  /* And the head itself must be unbreakable: Chrome repeats a table head on
     each page ONLY when it cannot be split, and on these pages it computed as
     `break-inside: auto` — which is why the head printed on page one alone. */
  body.report-tab thead { break-inside: avoid; page-break-inside: avoid; }
  body.report-tab tr, body.report-tab td, body.report-tab th { page-break-inside: avoid; }

  /* -------------------------------------------------------------------------
     THE "WHY" COLUMN ON A COMPARISON, ON PAPER.

     On screen it is a button that opens a form. On paper it has to become the
     RULED BOX a teacher fills in by hand — which is how most of these are
     actually completed: the sheet goes round a departmental meeting and comes
     back written on. So the button and the "written by" line disappear, and an
     empty cell keeps a line to write on.

     .reason-edit is inside the cell rather than the toolbar, so the blanket
     [data-nav] rule above does not reach it.
     ---------------------------------------------------------------------- */
  body.report-tab .reason-edit,
  body.report-tab .reason-by { display: none !important; }

  body.report-tab .reason-blank {
    display: block;
    min-height: 1.6em;
    border-bottom: 1px dotted #999;
  }
}

/* -----------------------------------------------------------------------------
   THE GRADE, AS A SUBSCRIPT ON ITS MARK.

   Small enough to read as a note on the number rather than a second number,
   and coloured from the band's own is_fail flag. `vertical-align` is set
   rather than left to the browser's own subscript drop, which on a 10px table
   pushed the letter far enough below the baseline to add a line of height to
   every row — the very thing this replaced a stacked badge to avoid.
   -------------------------------------------------------------------------- */
.grade-sub {
  font-size: 0.72em;
  font-weight: 600;
  vertical-align: baseline;
  position: relative;
  bottom: -0.15em;
  margin-left: 1px;
  line-height: 1;
}
.grade-sub.g-ok   { color: #15803d; }
.grade-sub.g-fail { color: #b91c1c; }

/* -----------------------------------------------------------------------------
   THE SCHOOL'S BADGE BEHIND EVERY PRINTED PAGE.

   Hidden on screen: the crest is already in the sidebar there, and behind a
   table of marks it would only make them harder to read. On paper it is the
   only thing that says whose numbers these are once the sheet has left the
   building.

   `position: fixed` is what makes it repeat: a fixed element is painted onto
   every page of a print job rather than once at the top of the first. It sits
   ABOVE the table rather than behind it, because the cells carry their own
   background colours and anything behind them would show only in the gaps.
   Faint enough at 7% to read straight through.

   `print-color-adjust: exact` because browsers drop background imagery when
   printing unless told the colour is the point.
   -------------------------------------------------------------------------- */
.report-watermark { display: none; }

@media print {
  body.report-tab .report-watermark {
    display: flex;
    position: fixed;
    inset: 0;
    align-items: center;
    justify-content: center;
    z-index: 9999;
    pointer-events: none;
    /* The report tab centres its children inside 90% of the page. This one is
       the page, so it opts out rather than inheriting a margin it should not
       have. */
    max-width: none;
    margin: 0;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  body.report-tab .report-watermark img {
    width: 55%;
    max-width: 420px;
    max-height: 55%;
    object-fit: contain;
    opacity: 0.07;
  }
}

/* A paper's line on the marks-status table, under the subject it belongs to. */
.paper-line > td { background: #fbfcfe; }

/* -----------------------------------------------------------------------------
   MARKS STATUS AND MISSING MARKS — every column on the page.

   The theme gives `.bordered-table` `min-width: max-content`: a table is never
   narrower than its contents laid out on ONE line. For a list of names that is
   harmless; for the missing-marks list it made a student with fifteen gaps a
   6,000px row, and on paper everything past the first sheet's width was cut
   off. These tables take the width they are given and wrap inside it.
   -------------------------------------------------------------------------- */
.status-table,
.missing-table {
  min-width: 0 !important;
  width: 100%;
}
.status-table td, .missing-table td { overflow-wrap: anywhere; }

/* The subtotal under each stream: read as a sum, so it is set apart. */
.status-table .status-subtotal > td {
  background: #eef2f7 !important;
  border-top: 2px solid #cbd5e1;
}
.status-table .missing-link { text-decoration: underline dotted; }
.status-stream > h6 { break-after: avoid; page-break-after: avoid; }

/* A gap is a pill on screen, and a pill that wraps with its neighbours. */
.missing-gaps { line-height: 1.9; }
.missing-gap {
  display: inline-block;
  margin: 0 4px 2px 0;
  padding: 1px 8px;
  border-radius: 4px;
  background: #fde8e8;
  color: #b91c1c;
  font-weight: 500;
  white-space: normal;
}
/* The teacher who owes the mark is the tooltip on screen and printed on paper. */
.missing-gap .missing-staff { display: none; }

@media print {
  /* Nothing on a report tab scrolls on paper, so nothing may be clipped by a
     scrolling box either: the container shows its whole table. */
  body.report-tab .table-responsive { overflow: visible !important; }

  /* Every table on a report tab fits the sheet — the theme's
     `min-width: max-content` is what used to push them off it. Tables that
     are meant to be only as wide as their figures (`w-auto`) stay that way. */
  body.report-tab .card-body table.table { min-width: 0 !important; }
  body.report-tab .card-body table.table:not(.w-auto) { width: 100% !important; }

  /* THE HEAD ON EVERY PAGE. Chrome only repeats a table's head across pages
     when the table is laid out in plain block flow: inside the theme's flex
     card and flex main column it printed the head once, on page one. And the
     theme pins every first column with `position: sticky`, which on paper has
     nothing to stick to and also stops the head repeating. */
  body.report-tab .dashboard-main,
  body.report-tab .dashboard-main-body > * > .card { display: block !important; }
  body.report-tab .card-body .table > tbody tr > td:first-child,
  body.report-tab .card-body .table > thead tr > th:first-child { position: static !important; }

  /* The theme's 16px of cell padding and 16px headings are screen sizes. On
     paper they doubled every row and pushed columns off the sheet. The two
     mark sheets size their own cells (see .analysis-fit and .marksheet). */
  body.report-tab .card-body .bordered-table:not(.analysis-fit):not(.marksheet) > thead > tr > th,
  body.report-tab .card-body .bordered-table:not(.analysis-fit):not(.marksheet) > tbody > tr > td,
  body.report-tab .card-body .bordered-table:not(.analysis-fit):not(.marksheet) > tbody > tr > th {
    padding: 3px 6px !important;
    font-size: 10.5px !important;
    line-height: 1.3;
  }

  .status-table, .missing-table { table-layout: fixed; }
  .status-table .badge { padding: 0 4px; font-size: 9.5px; }
  .status-table .missing-link { color: #b91c1c !important; text-decoration: none; }
  .status-stream { break-before: auto; }

  /* A gap on paper is plain text with the teacher beside it, separated by
     semicolons — the pill's tint is toner and wraps worse. */
  .missing-gaps { line-height: 1.35; }
  .missing-gap {
    display: inline;
    padding: 0;
    margin: 0;
    background: none !important;
    color: #000 !important;
  }
  .missing-gap + .missing-gap::before { content: "; "; }
  .missing-gap .missing-staff { display: inline; color: #555; font-size: 9px; }
}

/* -----------------------------------------------------------------------------
   THE SUBJECT ANALYSIS (exam batch) — read as a league table.

   The subject is the line's name, so it is the dark, bold thing; the teacher
   is a note under it. The grade counts are one block under one heading, a
   nought is faded so the real counts stand out, and the mean — the column the
   table is sorted on — is the heaviest figure on the row.
   -------------------------------------------------------------------------- */
.subject-analysis { min-width: 0 !important; width: 100%; table-layout: fixed; }
.subject-analysis .sa-subject { display: block; font-weight: 600; color: #1e2a3e; }
.subject-analysis .sa-staff { display: block; font-size: 11px; color: #6b7a90; line-height: 1.25; }
.subject-analysis .sa-rank { color: #6b7a90; }
.subject-analysis td.sa-count { color: #1e2a3e !important; font-weight: 500; }
.subject-analysis td.sa-zero { color: #b8c2cf !important; font-weight: 400; }
.subject-analysis td.sa-average { color: #1a3e6f !important; font-weight: 700; }
.subject-analysis td.sa-weight { font-weight: 600; }
.subject-analysis .sa-missing { color: #b91c1c; font-weight: 600; }
.subject-analysis th.sa-group { border-bottom: 1px solid #cbd5e1 !important; letter-spacing: .03em; }
.subject-analysis > tbody > tr > td { overflow-wrap: anywhere; }
/* The A-level table states no widths of its own; its subject column gets the
   room a subject name and its "subsidiary" note need. */
#alevelSubjects.subject-analysis > thead > tr:first-child > th:nth-child(2) { width: 26%; }
#alevelSubjects.subject-analysis > tbody > tr > td:nth-child(2) { color: #1e2a3e !important; font-weight: 600; }
#alevelSubjects.subject-analysis > tbody > tr > td:nth-child(2) .text-xs {
  font-size: 0.82em; font-weight: 400; color: #6b7a90 !important; line-height: 1.25;
}
/* The first column is not pinned: the theme's sticky first cell has nothing
   to stick to in a table that fits, and it stops heads repeating on paper. */
.subject-analysis > tbody tr > td:first-child,
.subject-analysis > thead tr > th:first-child { position: static !important; }

@media print {
  /* The best and worst side by side, as on screen — stacked, they took a page
     and a half of a four-page analysis. */
  body.report-tab .performer-row { display: flex !important; flex-wrap: nowrap; }
  body.report-tab .performer-row > .col-md-6 { flex: 0 0 50%; max-width: 50%; width: 50%; }

  /* A heading and its note belong on the page their table starts on. */
  body.report-tab .card-body h6,
  body.report-tab .card-body h6 + p { break-after: avoid; page-break-after: avoid; }
  body.report-tab .sa-section > h6 { margin-top: 12px !important; }

  .subject-analysis > thead > tr > th,
  .subject-analysis > tbody > tr > td {
    font-size: 10px !important;
    padding: 2px 5px !important;
    line-height: 1.2 !important;
  }
  .subject-analysis .sa-staff { font-size: 9px; }
  .subject-analysis td.sa-zero { color: #999 !important; }
}

/* -----------------------------------------------------------------------------
   A WIDE ANALYSIS FITS THE PAGE. IT DOES NOT SCROLL SIDEWAYS.

   The mark sheet is a student per row against every subject the class sits —
   25 columns for this school — and it was being put in a scrolling container.
   That is the wrong answer for this table: it is read by running an eye ACROSS
   a student's row, and a row you have to drag into view is a row nobody
   compares. It is also printed, and a printout cannot be scrolled at all.

   So the columns are told to share the width rather than claim it. Everything
   here is about buying horizontal room:

     fixed layout   columns divide the table's width instead of each sizing to
                    its own content, so twenty subjects cannot conspire to make
                    the table wider than the page.
     tight cells    4px of padding rather than 11, and 11px type. This is the
                    density the old system printed at and the staff read it
                    fine; it is a table of two-digit numbers, not prose.
     names wrap     the one column with real text gets a width and is allowed
                    to use two lines, which is far cheaper than letting it push
                    every subject column off the page.

   Below 1200px it scrolls again. On a phone there is no width to share out and
   squeezing 25 columns into 400px helps nobody.

   AND ALWAYS ON PAPER, which is why `print` is on the query rather than left to
   the width test. A media query with no type applies to print too, and print
   measures the PAGE: A4 landscape is about 1,122 CSS pixels, so a width test of
   1200 fails there — the table would go back to sizing itself to its contents
   and the printer would clip whatever ran past the sheet. Paper is the one
   place a sideways scroll cannot save you.
   -------------------------------------------------------------------------- */
@media (min-width: 1200px), print {
  /* `min-width: max-content` is the theme's, and it is the whole problem: it
     tells the table never to be narrower than its contents, which defeats
     `table-layout: fixed` completely — the columns were told to share 1,199px
     and the table went on being 1,383px wide anyway. */
  .analysis-fit {
    table-layout: fixed !important;
    width: 100% !important;
    min-width: 0 !important;
  }
  /* !important because the theme reaches these cells through
     `.table > :not(caption) > * > *`, which is specific enough to beat a plain
     class selector and sets 16px of padding — 400px of it across 25 columns. */
  .analysis-fit > thead > tr > th,
  .analysis-fit > tbody > tr > td,
  .analysis-fit.table > :not(caption) > * > * {
    padding: 4px 3px !important;
    font-size: 11px !important;
    line-height: 1.25;
    overflow-wrap: anywhere;
  }
  /* The grade under a score, small enough to cost no width of its own. */
  .analysis-fit .badge {
    padding: 0 3px;
    font-size: 9px;
    line-height: 1.4;
  }
  /* The container stops scrolling — there is nothing left to scroll to. */
  .analysis-fit-wrap { overflow-x: visible; }
}

/* =========================================================================
   THE A-LEVEL SUBJECT MARKSHEET.
   =========================================================================
   A performance cell holds a small table of papers, so the type has to come
   down and the padding has to go. Lifted from the school's own sheet rather
   than reinvented — this is a document their staff already read.            */
/* !important throughout, and it is not laziness.
 *
 * The theme reaches every cell in a `.table` through
 * `.table > :not(caption) > * > *`, which is specific enough to beat a plain
 * class selector — and it matches the cells of the paper table NESTED inside a
 * performance cell just as happily as the outer ones. It was applying 16px of
 * padding there: 96px of empty space inside a 124px column, which is why the
 * paper table could not fit however small the type was made. */
.marksheet { font-size: 10.5px; table-layout: fixed; width: 100%; }
.marksheet th,
.marksheet td,
.marksheet.table > :not(caption) > * > * {
  padding: 3px !important;
  vertical-align: top;
}

/* A heading is one word and must stay one word — wrapped, a fixed column turns
   "Stream" into "Stre am" and "Pts" into "P t s", which reads as damage rather
   than as a narrow column.

   The SIZE is set here too, and that is the part that actually matters: the
   theme's `th` is larger than this table's 10.5px, so at the widths above the
   headings did not fit and were ellipsised down to "Ad…" and "Sig…" — a column
   whose name has been eaten. At 9.5px every heading fits its column outright,
   so nothing wraps and nothing is hidden. */
.marksheet thead th {
  font-size: 9.5px !important;
  white-space: nowrap;
  overflow: hidden;
}
/* `> tr > td`, the OUTER cells only. Written as `tbody td` it also matched the
   paper table nested inside every performance cell, and `overflow-wrap:
   anywhere` there broke "Avg" into "A v g" and stood every mark on end — a row
   184px tall that fitted the page and lost the document. */
.marksheet tbody > tr > td { overflow-wrap: anywhere; }
.marksheet table.inner td { overflow-wrap: normal; white-space: nowrap; }

/* The subject's own tinted column, which is what makes five subjects scannable
   down the page. Straight from the legacy sheet. */
.marksheet .col-subname {
  font-weight: 600;
  background: #f5f8fc;
  text-align: center;
  font-size: 9.5px;
  line-height: 1.25;
}

/* THE PAPERS, as a small table inside the performance cell: a column per paper
   with its own letter, then Avg carrying the grade and the points. It is the
   thing the sheet exists to show, and a row of numbers cannot say which paper
   is which. */
/* Its cells cannot wrap — "41.5" broken over two lines is not a mark — so the
   table has a width it cannot go below, and that floor is what decides whether
   the sheet fits. At 8px with 1px of padding a three-paper subject needs about
   120px; the column it sits in gets about 124. Every point of type and pixel of
   padding here is a column of the page. */
.marksheet table.inner { width: 100%; border-collapse: collapse; background: #fff; }
.marksheet table.inner td {
  border: 1px solid #d4e0ea;
  padding: 1px !important;
  text-align: center;
  font-size: 8px;
  line-height: 1.2;
}
.marksheet table.inner .hdr { background: #f0f4f9; font-weight: 600; }
.marksheet table.inner .avg { background: #eef6f3; font-weight: 600; }

/* One paper, or a subsidiary: a pill rather than a table of one column. */
.marksheet .simple-score {
  text-align: center;
  font-weight: 500;
  background: #fefaf5;
  padding: 3px 2px;
  border-radius: 14px;
  line-height: 1.3;
}
.marksheet .paper-label {
  display: inline-block;
  font-weight: 600;
  background: #eef2f7;
  padding: 0 4px;
  border-radius: 10px;
  margin-right: 4px;
  font-size: 8.5px;
}

/* Pass and fail are the SCALE's, not a hardcoded fifty — see the renderer. */
.marksheet .g-ok   { color: #15803d; font-weight: 600; }
.marksheet .g-fail { color: #b91c1c; font-weight: 600; }
.marksheet sup.pts { font-size: 7px; font-weight: normal; }

.marksheet .col-adm { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 9px; }
.marksheet .no-data { color: #94a3b8; }

/* ON PAPER THE SHEET IS DRAWN AT 80% AND LAID OUT ON THE WIDTH THAT BUYS.

   A4 landscape leaves about 1,090px. The named columns take 700 of them, so
   five performance cells got 77px each — and a three-paper cell needs about
   120, so the papers spilled over their neighbours and the headings ran into
   each other. `zoom` makes the table lay itself out on 1,360px and print it
   at the sheet's width: every cell gets the room it has on a laptop screen,
   in type that is still larger than the school's own printed sheet. */
@media print {
  body.report-tab .marksheet { zoom: 0.8; }
  /* The short columns' headings — "Stream Pos", "Total Points" — are wider
     than their columns in one line; on paper they wrap onto two small lines
     rather than being cut or running into the next heading. */
  body.report-tab .marksheet thead th {
    white-space: normal;
    font-size: 8.5px !important;
    line-height: 1.15;
    overflow: hidden;
    overflow-wrap: normal;
    word-break: normal;
  }
  /* A size down on the names, so "Mathematics" and a two-word name sit on
     their own lines instead of being broken inside a word. */
  body.report-tab .marksheet > tbody > tr > td { font-size: 9px !important; }
  body.report-tab .marksheet .col-subname { font-size: 8.5px; }
}

/* -----------------------------------------------------------------------------
   THE SIGN-IN PAGE'S RESET PANEL.

   Two forms in one panel, because there are two ways back into an account and
   which one you need depends on who you are — not on something the page can
   work out for you. The divider between them is doing real work: without it the
   second form reads as more fields belonging to the first, and people fill in
   all four.
   -------------------------------------------------------------------------- */
.auth-hint {
  margin: 6px 0 0;
  font-size: 12px;
  line-height: 1.45;
  color: #6b7280;
}
.auth-optional {
  font-weight: 400;
  color: #9ca3af;
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 22px 0 18px;
  color: #9ca3af;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: #e5e7eb;
}

/* The second action is real, but it is not the one most people on this page
   want, so it is outlined rather than filled. */
.auth-submit--quiet {
  background: transparent;
  color: var(--auth-brand);
  border: 1px solid var(--auth-brand);
}

/* The reply from a reset endpoint is usually GOOD news, and the box it lands in
   is the one built for errors. Green when the server said OK. */
.auth-error--ok {
  color: #166534;
  background: #f0fdf4;
  border-color: #bbf7d0;
}

/* And grey while the request is still out. An SMTP handshake takes a few
   seconds, and without a word here the button simply sits there looking
   pressed and inert. */
.auth-error--working {
  color: #475569;
  background: #f8fafc;
  border-color: #e2e8f0;
}

/* =============================================================================
   THE ANALYSIS REPORTS, DRESSED.

   Appearance only — every table keeps the columns and the order its renderer
   gives it. What changes is that the page now reads as a DOCUMENT rather than
   as an application screen with a table dropped into it, because that is what
   it is: printed, signed, filed, and handed to a head of department.

   The palette is the school's own sheet — the navy rule under the heading, the
   pale blue header band, the barely-there zebra. Those are the colours their
   staff already associate with these reports.
   ========================================================================== */
@media screen {
  /* A tinted ground, so the white sheet has an edge. */
  body.report-tab .dashboard-main-body { background: #f4f7fc; }

  body.report-tab .dashboard-main-body > * > .card {
    border: 1px solid #e3e9f2;
    border-radius: 10px;
    box-shadow: 0 2px 10px rgba(20, 40, 80, 0.05);
  }
}

/* ---- the heading block ---------------------------------------------------- */

/* A rule with weight, as the legacy has. It is what separates "which report is
   this" from the report, and on paper it is the only thing that does. */
body.report-tab .card-body > .text-center.border-bottom {
  border-bottom: 2px solid #1a3e6f !important;
}
body.report-tab .card-body > .text-center h5 {
  font-size: 20px;
  font-weight: 700;
  color: #1a3e6f;
  letter-spacing: 0.01em;
}
/* Spaced small caps: it is a label for the page, not a sentence. */
body.report-tab .card-body > .text-center h6 {
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.09em;
  color: #40587c;
}

/* ---- the sentence that says what you are looking at ------------------------ */
body.report-tab .report-actions > span:first-child {
  max-width: 74ch;
  line-height: 1.55;
}

/* ---- section headings inside a report -------------------------------------- */

/* An accent bar rather than a bigger font. A report has several of these and
   they need to be findable while scrolling, not loud. */
body.report-tab .card-body h6.text-md {
  position: relative;
  padding-left: 10px;
}
/* `currentColor`, and NO colour of its own — "Best performers" is green and
   "Worst performers" is red, and those two colours are the fastest thing on
   the page to read. Painting every heading navy took that away; the bar now
   takes the heading's colour instead of overriding it. */
body.report-tab .card-body h6.text-md::before {
  content: "";
  position: absolute;
  left: 0;
  top: 2px;
  bottom: 2px;
  width: 3px;
  border-radius: 2px;
  background: currentColor;
}

/* ---- the tables ------------------------------------------------------------ */

/* Specific enough to beat the theme's `.table > :not(caption) > * > *`, which
   is what paints these cells by default. */
body.report-tab .card-body table.table > thead > tr > th,
body.report-tab .card-body table.table > tbody > tr > th {
  background: #eef2f7;
  color: #1e2a3e;
  font-weight: 600;
  border-bottom: 1px solid #cbd5e1;
}

body.report-tab .card-body table.table > :not(caption) > * > * {
  border-color: #dbe4ee;
}

/* Barely-there zebra. On a forty-row analysis it is the difference between
   following a row across and losing it; any stronger and it prints as bands. */
@media screen {
  body.report-tab .card-body table.table > tbody > tr:nth-child(even) > td {
    background: #fafcff;
  }
  body.report-tab .card-body table.table > tbody > tr:hover > td {
    background: #f1f6fd;
  }
}

/* Figures in a column line up only if the digits are the same width. */
body.report-tab .card-body table.table td,
body.report-tab .card-body table.table th {
  font-variant-numeric: tabular-nums;
}

/* The two summary tables under the distribution are read as a block of
   figures, so they get a little more air than a long list would want. */
body.report-tab #overallTable > tbody > tr > th {
  text-align: left;
  white-space: nowrap;
}

/* ---- on paper -------------------------------------------------------------- */
@media print {
  /* The tints go: a header band across forty printed sheets is a surprising
     amount of toner for something a rule says just as clearly. */
  body.report-tab .card-body table.table > thead > tr > th,
  body.report-tab .card-body table.table > tbody > tr > th {
    background: none !important;
    border-bottom: 1pt solid #000 !important;
  }
  body.report-tab .card-body > .text-center.border-bottom {
    border-bottom: 1.5pt solid #000 !important;
  }
  body.report-tab .card-body h6.text-md::before { background: currentColor; }
}

/* =============================================================================
   THE TERM MARK SHEET — students down, every batch across.

   The hardest layout in the system to fit on a page: a class of ninety against
   five batches, each of which may be sat in three papers. Everything here is
   about spending the width on the thing being typed and nothing else.
   ========================================================================== */

/* A BOX BIG ENOUGH TO TYPE INTO AND READ BACK.

   It was a `form-control-sm` in a 62px cell: about eleven pixels of text in a
   box the size of a checkbox, entered from a paper script by somebody reading
   the number off a page. A mark out of a hundred needs three characters and
   two decimals — the box is now sized for that and no wider, because every
   pixel here comes off a batch further along the row. */
.mark-box {
  width: 68px;
  height: 38px;
  padding: 4px 6px;
  font-size: 15px;
  font-weight: 600;
}
.mark-cell { width: 76px; }

/* The number's own spinner is useless here — marks are typed, never stepped —
   and it takes a third of the width of the box to say so. */
.mark-box::-webkit-outer-spin-button,
.mark-box::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.mark-box[type="number"] { -moz-appearance: textfield; }

/* The grade sits against the mark rather than in a column of its own. Where the
   batch is a single mark it goes beside the box, so the pair reads as one
   thing and the box keeps its own line. */
.mark-with-grade {
  display: inline-flex;
  align-items: center;
  gap: 1px;
}
.total-cell { white-space: nowrap; }

/* THE NAME COLUMN TAKES WHAT IT NEEDS.

   It was given a 158px minimum and a second line carrying a payment reference,
   which made the widest column on the sheet the one nobody is reading. The
   name wraps now rather than pushing the marks off the page. */
.student-cell {
  max-width: 150px;
  min-width: 110px;
  overflow-wrap: anywhere;
  line-height: 1.25;
}

/* =============================================================================
   A SELECT IS THE SAME HEIGHT AS EVERY OTHER CONTROL.

   Every native control in this application is 44px — a text box, a date box, a
   plain <select>. A select2-enhanced one measured 65px, so any row mixing the
   two sat its dropdowns twenty-one pixels taller than the inputs beside them
   and its labels on two different lines. The marks-log filter bar is where it
   shows worst: four dropdowns, then From and To sitting visibly lower.

   THE TWENTY-ONE PIXELS ARE A PHANTOM LINE BOX. select2 puts two children in
   its container: the visible selection, and a zero-height `.dropdown-wrapper`
   that is `display: inline-block`. An inline-level box generates a line box
   even when it is empty and has no height, and at this font size that line box
   is nineteen pixels tall. The remaining two are the selection's own 46px
   against the 44px everything else uses.

   Making the wrapper a BLOCK removes the line box without hiding the element or
   taking it out of flow, so select2 still has it to hang the dropdown on —
   checked open, and the dropdown still matches the control's width exactly.
   ========================================================================== */
.select2-container > .dropdown-wrapper { display: block; }

.select2-container--default .select2-selection--single { height: 44px; }
.select2-container--default .select2-selection--single .select2-selection__rendered {
  line-height: 32px;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
  height: 42px;
}

/* =============================================================================
   THE REPORT WIZARD — decisions on the left, the card on the right.

   A report batch is thirteen switches, a layout and a set of weighted columns.
   Nobody can hold that in their head and picture the result, so the page is
   split and the card is redrawn on every change. The card is what the school is
   actually deciding; the form is only how they say it.
   ========================================================================== */
.rw-split {
  display: grid;
  grid-template-columns: minmax(360px, 4fr) minmax(420px, 5fr);
  gap: 20px;
  align-items: start;
}

/* On a laptop the two columns are already tight; below that they stack, with
   the card underneath — a preview narrower than about 400px stops being a
   preview and becomes a diagram. */
@media (max-width: 1200px) {
  .rw-split { grid-template-columns: 1fr; }
}

/* The card follows the page down, because the switches at the bottom of the
   form are exactly the ones whose effect is hardest to imagine. */
.rw-preview { position: sticky; top: 16px; }

.rw-previewbar {
  display: flex; justify-content: space-between; align-items: center;
  font-size: 13px; font-weight: 600; color: #475569;
  padding: 0 4px 8px;
}
/* Said plainly, because the marks are invented and somebody checking a total
   against their own records deserves to know that before they start. */
.rw-sample {
  font-weight: 500; font-size: 11px; color: #92400e;
  background: #fef3c7; border-radius: 999px; padding: 2px 10px;
}

.rw-paper {
  background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: 10px;
  padding: 14px; max-height: calc(100vh - 120px); overflow: auto;
}

.rw-step {
  font-size: 12px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: #1d4ed8; margin-bottom: 12px;
}

.rw-group { margin-bottom: 14px; }
.rw-grouptitle {
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: #64748b; margin-bottom: 6px;
}
.rw-switch {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 5px 0; font-size: 13px; cursor: pointer;
}
.rw-switch input { margin-top: 3px; flex: 0 0 auto; }
.rw-note { display: block; font-size: 11px; color: #94a3b8; }

/* ---- the columns ---------------------------------------------------------
   Laid out as a grid rather than a form per column: the weights are read DOWN
   the page as a sum, and a stack of labelled boxes hides that. */
.rw-column {
  display: grid;
  grid-template-columns: 1fr 90px 70px 150px 28px;
  gap: 6px; align-items: center;
  padding: 8px; margin-bottom: 6px;
  background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px;
}
.rw-column .form-control { height: 34px; font-size: 13px; padding: 4px 8px; }
.rw-cbatches {
  grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 4px; align-items: center;
}
.rw-chip {
  font-size: 11px; background: #e0e7ff; color: #3730a3;
  border-radius: 999px; padding: 2px 9px;
}
.rw-chip--empty { background: #fef3c7; color: #92400e; }
.rw-pick {
  font-size: 11px; border: 1px dashed #cbd5e1; background: transparent;
  border-radius: 999px; padding: 2px 9px; color: #475569;
}
.rw-drop {
  border: 0; background: transparent; color: #b91c1c;
  font-size: 20px; line-height: 1; cursor: pointer;
}

/* The one number that decides whether this report can print at all. */
.rw-coltotal {
  font-size: 12px; padding: 7px 10px; border-radius: 6px; margin: 8px 0 10px;
}
.rw-coltotal.is-ok  { background: #f0fdf4; color: #15803d; }
.rw-coltotal.is-off { background: #fef2f2; color: #b91c1c; }

/* =============================================================================
   THE CARD, AS THE WIZARD SHOWS IT.

   The card itself is styled in report_card.css, which the printer loads on
   its own into a blank tab. All that belongs here is how it is SHRUNK to sit
   beside the form — one file for the card, one line for the preview.
   ========================================================================== */
.rw-paper .rp-card {
  background: #faf9d0;           /* the paper colour the school prints on */
  padding: 12px; border-radius: 4px;
  font-size: 9px; color: #111; line-height: 1.35;
  box-shadow: 0 1px 3px rgba(0,0,0,.15);
}

/* The identity fields on the wording panel: a tick and the school's own name
   for it, side by side, because the label is meaningless without the field. */
.rw-idfield {
  display: flex; align-items: center; gap: 10px; margin-bottom: 6px;
}
.rw-idfield .rw-switch { flex: 0 0 44%; }
.rw-idfield .form-control { flex: 1; }
.rw-previewbar #btnWording { margin-left: auto; margin-right: 10px; }

/* =============================================================================
   REPORT CARDS — the list beside the card.

   The same split as the wizard, and for the same reason: the thing being chosen
   and the thing it produces belong on one screen. Here the left column is the
   stream's register, so a class teacher can walk down it and read each card
   without going back to a list.
   ========================================================================== */
.rc-split { display: grid; grid-template-columns: 300px 1fr; gap: 16px; align-items: start; }
@media (max-width: 1100px) { .rc-split { grid-template-columns: 1fr; } }

.rc-list {
  background: #fff; border: 1px solid #e2e8f0; border-radius: 8px;
  max-height: 78vh; overflow: auto;
}
.rc-listhead {
  padding: 10px 14px; border-bottom: 1px solid #e2e8f0;
  font-size: 13px; font-weight: 600; color: #334155;
  display: flex; justify-content: space-between; align-items: baseline;
  position: sticky; top: 0; background: #fff; z-index: 1;
}
.rc-hint { font-weight: 400; font-size: 11px; color: #64748b; }

.rc-row {
  display: block; width: 100%; text-align: left; background: none;
  border: 0; border-bottom: 1px solid #f1f5f9; padding: 9px 14px; cursor: pointer;
}
.rc-row:hover { background: #f8fafc; }
.rc-row.is-open { background: #eff6ff; box-shadow: inset 3px 0 0 #2563eb; }
.rc-name { display: block; font-size: 13px; color: #0f172a; }
.rc-meta { display: block; font-size: 11px; color: #64748b; }

/* The card at reading size, not the wizard's shrunken one: this screen is where
   somebody checks a card before it goes home. */
/* The card paints its own paper and padding — report_card.css does that for
   the printer too. All this adds is the lift off the page and somewhere for a
   wide A-level table to scroll. */
.rc-paper { border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,.15); overflow-x: auto; }

.rc-waiting {
  padding: 40px; text-align: center; color: #64748b; font-size: 13px;
}

/* A class of five hundred comes fifty at a time; this is how you walk it. */
.rc-pager {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  margin-top: 14px; font-size: 12px; color: #475569;
}

/* =============================================================================
   THE REPORT SHEET.

   Five hundred learners down and twenty subjects across. The learner column
   has to stay put while the subjects scroll, or the name at the left is lost
   by the third subject and every row becomes a number with nobody attached.
   ========================================================================== */
.rs-sheet { font-size: 12px; }
.rs-sheet th, .rs-sheet td { padding: 5px 7px; white-space: nowrap; }
.rs-sheet .rs-sticky {
  position: sticky; left: 0; background: #fff; z-index: 2;
}
.rs-sheet thead .rs-sticky { z-index: 3; }
.rs-sheet .rs-name { left: 34px; min-width: 180px; white-space: normal; }
.rs-sheet tbody tr:nth-child(even) .rs-sticky { background: #f8fafc; }

/* The grade beside the mark, small enough not to compete with it. */
.rs-grade { font-size: 10px; font-weight: 700; color: #15803d; margin-left: 3px; }
/* Marks as entered: several numbers in one cell, so they are quieter. */
.rs-raw { font-size: 11px; color: #334155; }

/* ==========================================================================
   THE SIDEBAR AS A DRAWER (below 1200px, where style.css stops pinning it).
   app.js opens and closes it; this makes it slide rather than jump, keeps a
   closed drawer out of the Tab order, stops the page scrolling underneath an
   open one, and lets the dimmed page behind it be tapped to close it.
   ========================================================================== */
@media (max-width: 1199.98px) {
  .sidebar {
    transition: inset-inline-start .25s ease, visibility 0s linear .25s;
    visibility: hidden;
    overflow-y: auto;
  }
  .sidebar.sidebar-open {
    transition: inset-inline-start .25s ease, visibility 0s;
    visibility: visible;
    box-shadow: 0 0 40px rgba(16, 24, 40, .25);
  }
  body.overlay-active { overflow: hidden; }
  body.overlay-active::after { position: fixed; cursor: pointer; }
}
@media (prefers-reduced-motion: reduce) {
  .sidebar, .sidebar.sidebar-open { transition: none; }
}
/* The keyboard's place in the menu, visible. */
.sidebar-menu a:focus-visible,
.sidebar-mobile-toggle:focus-visible,
.sidebar-close-btn:focus-visible {
  outline: 2px solid var(--primary-600, #487fff);
  outline-offset: 2px;
}
/* Focus is moved to the content after choosing from the drawer; that is for
   screen readers, and a ring round the whole page would only confuse. */
#appContent:focus { outline: none; }

/* ==========================================================================
   DROP ZONES (ui.dropZone / ui.bindDropZones).
   ========================================================================== */
.drop-zone { position: relative; gap: 8px; min-height: 44px; height: auto; transition: background-color .15s, border-color .15s; }
.drop-zone:focus-visible { outline: 2px solid var(--primary-600, #487fff); outline-offset: 2px; }
.drop-zone__icon { font-size: 18px; line-height: 1; color: var(--text-secondary-light, #667085); pointer-events: none; }
.drop-zone__prompt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; pointer-events: none; }
.drop-zone--over { border-style: solid !important; }
.drop-zone--over .drop-zone__icon { color: var(--primary-600, #487fff); }
.drop-zone--filled { border-style: solid !important; }
.drop-zone--filled .drop-zone__icon { color: #16a34a; }
.drop-zone--error { border-color: var(--danger-600, #dc2626) !important; }
.drop-zone__clear {
  flex-shrink: 0; border: 0; background: transparent; padding: 0 4px;
  font-size: 18px; line-height: 1; color: var(--text-secondary-light, #667085); cursor: pointer;
}
.drop-zone__clear:hover { color: var(--danger-600, #dc2626); }

/* ==========================================================================
   STUDENT PROFILE (views/student_profile.js) — the header card and tabs.
   Theme variables only, so it follows the dark theme like everything else.
   ========================================================================== */
.sp-hero { border-top: 4px solid var(--primary-600, #487fff); }
.sp-photo {
  width: 112px; height: 112px; border-radius: 50%; object-fit: cover; flex-shrink: 0;
  border: 4px solid var(--primary-100, #e4f1ff);
}
.sp-photo--empty {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--neutral-200, #e4e7ec); color: var(--text-secondary-light, #667085); font-size: 44px;
}
.sp-chip {
  display: inline-block; padding: 3px 12px; border-radius: 999px; font-size: 12px; font-weight: 500;
  background: var(--primary-50, #f5f8ff); color: var(--primary-600, #487fff);
}
.sp-facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px;
  padding-top: 16px; border-top: 1px solid var(--neutral-200, #e4e7ec);
}
.sp-fact { min-width: 0; overflow-wrap: anywhere; }
.sp-detail { padding: 6px 0; border-bottom: 1px dashed var(--neutral-200, #e4e7ec); }
.sp-tile {
  height: 100%; padding: 16px; border-radius: 8px; background: var(--neutral-50, #f5f6fa);
}
.sp-tile > i { font-size: 20px; color: var(--primary-600, #487fff); }
.sp-tile--success > i { color: var(--success-600, #16a34a); }
.sp-tile--warning > i { color: var(--warning-600, #d97706); }
.sp-tile--danger  > i { color: var(--danger-600, #dc2626); }
.sp-avatar {
  width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--neutral-200, #e4e7ec);
}
/* The marks grid: a row per exam batch, so the batch name stays in view
   while the subjects scroll sideways. */
.sp-grid th.sp-sticky {
  position: sticky; left: 0; z-index: 2; min-width: 150px;
  background: var(--white, #fff);
}
.sp-grid thead th { white-space: nowrap; }
.sp-grade { color: var(--primary-600, #487fff); font-weight: 600; }
@media (max-width: 575px) {
  .sp-photo { width: 84px; height: 84px; }
}

/* ---- The staff profile header (views/teachers/staff_profile.js) --------- */
.staff-profile-band {
  height: 88px;
  background: linear-gradient(120deg, var(--primary-600, #487fff), var(--primary-400, #7aa2ff));
}
.staff-profile-photo {
  width: 124px; height: 124px; margin-top: -56px; flex-shrink: 0;
  border-radius: 16px; overflow: hidden; border: 4px solid var(--white, #fff);
  background: var(--neutral-200, #e4e7ec); box-shadow: 0 4px 14px rgba(16, 24, 40, .12);
}
.staff-profile-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.staff-profile-photo span {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
  font-size: 40px; color: var(--neutral-500, #667085);
}
.staff-profile-head .badge-pill {
  display: inline-flex; align-items: center; padding: 3px 12px; border-radius: 999px;
  font-size: 12px; font-weight: 500;
  background: var(--primary-50, #eef2ff); color: var(--primary-600, #487fff);
}
@media (max-width: 575px) {
  .staff-profile-photo { width: 96px; height: 96px; margin-top: -44px; }
}
