/* ============================================================
   School Result System - Custom CSS
   Most styling is handled by Tailwind CDN. This file holds:
     - Print styles (so only result cards print)
     - Loading overlay flex display
     - Small overrides for nav active state
   ============================================================ */

body {
  font-family: 'DM Sans', system-ui, -apple-system, sans-serif;
}

#loading-overlay {
  display: none;
}
#loading-overlay.show {
  display: flex;
}

#toast {
  transition: transform 0.2s ease, opacity 0.2s ease;
}
#toast.show {
  display: block;
}

.nav-link.active {
  background-color: #3730a3;
}

/* ============================================================
   ADMIN PAGE — mobile
   The tab bar scrolls horizontally on narrow screens; give it a
   slim, unobtrusive scrollbar so the overflow is discoverable.
   ============================================================ */
.admin-tabbar {
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}
.admin-tabbar::-webkit-scrollbar {
  height: 4px;
}
.admin-tabbar::-webkit-scrollbar-thumb {
  background: rgba(99, 102, 241, 0.35);
  border-radius: 4px;
}

/* Responsive table: on phones the .resp-table collapses each row into a
   stacked, labelled card instead of a 6-column horizontal-scroll table.
   The label comes from each <td>'s data-label. Above the sm breakpoint
   (640px) it renders as a normal table. */
@media (max-width: 639px) {
  .resp-table thead { display: none; }
  .resp-table,
  .resp-table tbody { display: block; width: 100%; }
  .resp-table .resp-row {
    display: block;
    border: 1px solid #e2e8f0;       /* slate-200 */
    border-radius: 0.5rem;
    padding: 0.25rem 0.25rem;
    margin-bottom: 0.75rem;
    background: #fff;
  }
  /* Labelled cells become two-column (label | value) flex rows. Cells without a
     data-label (e.g. the empty-state colspan message) keep their normal flow. */
  .resp-table .resp-row > td[data-label] {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
    width: 100%;
    text-align: right;
    padding: 0.5rem 0.5rem;
    border-bottom: 1px solid #f1f5f9; /* slate-100 */
  }
  .resp-table .resp-row > td[data-label]:last-child { border-bottom: 0; }
  .resp-table .resp-row > td[data-label]::before {
    content: attr(data-label);
    font-weight: 600;
    color: #475569;                   /* slate-600 */
    text-align: left;
    flex: 0 0 auto;
    white-space: nowrap;
  }
  /* Inputs/selects inside a card cell take the remaining width (overrides
     Tailwind's max-w-xs on the assignment selects). */
  .resp-table .resp-row > td[data-label] select,
  .resp-table .resp-row > td[data-label] input {
    width: 100%;
    max-width: 60%;
  }
  /* A cell holding a multi-control group (marked .resp-stack, e.g. the
     result-release datetime + Set/Clear) needs more room than a 60%-capped single
     input. Stack the label ABOVE the controls and let the group fill the card
     width. (Class-based so it doesn't rely on :has().) */
  .resp-table .resp-row > td[data-label].resp-stack {
    flex-direction: column;
    align-items: stretch;
    text-align: left;
  }
  .resp-table .resp-row > td[data-label].resp-stack > div { width: 100%; }
  /* min-width:0 lets a datetime-local (which has a wide intrinsic min width from
     its spin/calendar UI) shrink inside the flex group instead of overflowing the
     card; the input still grows to fill via flex:1 1 auto. */
  .resp-table .resp-row > td[data-label].resp-stack input {
    max-width: none; flex: 1 1 auto; min-width: 0;
  }
  /* An empty value/actions cell shouldn't leave a lonely label. */
  .resp-table .resp-row > td[data-label]:empty { display: none; }
}

/* ============================================================
   HS MARKS ENTRY PAGE
   Indigo/violet theme ported from the standalone HS-Marks-Entry
   app. The bare `select` rule is scoped to this page so the
   admin selects keep their default styling.
   ============================================================ */
.hs-serif { font-family: 'DM Serif Display', serif; }

.card-glow {
  box-shadow: 0 0 0 1px rgba(99,102,241,0.15), 0 8px 32px rgba(99,102,241,0.10), 0 2px 8px rgba(0,0,0,0.06);
}

.bg-mesh {
  background-color: #f5f3ff;
  background-image:
    radial-gradient(at 20% 20%, rgba(139,92,246,0.12) 0px, transparent 55%),
    radial-gradient(at 80% 80%, rgba(99,102,241,0.10) 0px, transparent 55%),
    radial-gradient(at 50% 10%, rgba(167,139,250,0.08) 0px, transparent 50%);
}

#page-hs-marks-entry select,
#page-hs-entry-review select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%236366f1' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  background-size: 1.25rem;
}
#page-hs-marks-entry select:disabled,
#page-hs-entry-review select:disabled { opacity: 0.4; cursor: not-allowed; }

.btn-submit {
  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
  transition: all 0.2s ease;
}
.btn-submit:hover:not(:disabled) {
  background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
  transform: translateY(-1px);
  box-shadow: 0 6px 20px rgba(99,102,241,0.35);
}
.btn-submit:active:not(:disabled) { transform: translateY(0); box-shadow: none; }
.btn-submit:disabled { opacity: 0.45; cursor: not-allowed; }

.step-dot {
  width: 22px; height: 22px; border-radius: 50%;
  background: #ede9fe; color: #6366f1;
  font-size: 11px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0; transition: background 0.2s, color 0.2s;
}
.step-dot.active { background: #6366f1; color: #fff; }
.step-dot.done   { background: #a5b4fc; color: #fff; }
.step-line { height: 1px; width: 24px; background: #e0e7ff; flex-shrink: 0; }

.result-card {
  page-break-inside: avoid;
  page-break-after: always;
}
.result-card:last-child {
  page-break-after: auto;
}

/* Subject table inside result cards */
.subjects-table {
  width: 100%;
  border-collapse: collapse;
}
.subjects-table th,
.subjects-table td {
  border: 1px solid #cbd5e1;
  padding: 6px 10px;
  text-align: left;
  font-size: 0.92rem;
}
.subjects-table th {
  background-color: #f1f5f9;
  font-weight: 600;
}
.subjects-table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.summary-row {
  background-color: #fef9c3;
  font-weight: 600;
}

.status-pass {
  color: #15803d;
  font-weight: 700;
}
.status-fail {
  color: #b91c1c;
  font-weight: 700;
}

/* Compact the result-card subject table on small phones so all 5 columns fit */
@media (max-width: 480px) {
  .subjects-table th,
  .subjects-table td {
    padding: 4px 6px;
    font-size: 0.8rem;
  }
}

/* ============================================================
   HOME PAGE (public result lookup) — visual template
   Indigo hero with layered depth + a whisper of gold, plus a
   gentle entrance. Scoped to #page-home so nothing else shifts.
   ============================================================ */
#page-home .home-hero {
  background-color: #312e81;
  background-image:
    radial-gradient(115% 130% at 100% 0%, rgba(129, 140, 248, 0.40) 0%, transparent 46%),
    radial-gradient(100% 120% at 0% 100%, rgba(91, 33, 182, 0.45) 0%, transparent 52%),
    linear-gradient(135deg, #4338ca 0%, #312e81 55%, #1e1b4b 100%);
}
/* Faint dot texture for depth (kept very subtle; clipped by rounded corners). */
#page-home .home-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: radial-gradient(rgba(255, 255, 255, 0.06) 1px, transparent 1.5px);
  background-size: 22px 22px;
  pointer-events: none;
}

/* Gradient icon badge shared by the three section headers. The header SVGs use
   stroke/fill:currentColor, so the white text colour paints them white. */
#page-home .home-badge {
  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
  color: #fff;
  box-shadow: 0 6px 16px rgba(99, 102, 241, 0.35);
}

/* Section cards: soft indigo-tinted shadow + a gentle hover lift. */
#page-home .home-card {
  background: #fff;
  border-radius: 1.5rem;
  box-shadow:
    0 0 0 1px rgba(99, 102, 241, 0.10),
    0 10px 30px rgba(49, 46, 129, 0.08),
    0 2px 6px rgba(0, 0, 0, 0.04);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
#page-home .home-card:hover {
  transform: translateY(-3px);
  box-shadow:
    0 0 0 1px rgba(99, 102, 241, 0.18),
    0 18px 40px rgba(49, 46, 129, 0.16),
    0 3px 10px rgba(0, 0, 0, 0.06);
}
/* The primary "Check Your Result" card: an indigo wash + ring so it reads as the
   main action. It holds the form, so it does NOT lift on hover (would jump while
   typing). Rules come after .home-card so they win at equal specificity. */
#page-home .home-card-primary {
  background:
    radial-gradient(130% 150% at 0% 0%, rgba(224, 231, 255, 0.85) 0%, rgba(255, 255, 255, 0) 42%),
    #ffffff;
  box-shadow:
    0 0 0 1px rgba(99, 102, 241, 0.22),
    0 16px 44px rgba(49, 46, 129, 0.14),
    0 2px 8px rgba(0, 0, 0, 0.05);
}
#page-home .home-card-primary:hover {
  transform: none;
}

/* Social icon buttons — recognisable platform colour on hover. */
#page-home .social-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 9999px;
  background: #eef2ff;
  color: #4338ca;
  transition: transform 0.18s ease, background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}
#page-home .social-btn:hover {
  transform: translateY(-2px);
  color: #fff;
  box-shadow: 0 8px 18px rgba(49, 46, 129, 0.20);
}
#page-home .social-ig:hover {
  background: linear-gradient(45deg, #f09433, #e6683c, #dc2743, #cc2366, #bc1888);
}
#page-home .social-yt:hover { background: #ff0000; }
#page-home .social-web:hover { background: #4f46e5; }
#page-home .social-btn:focus-visible {
  outline: 2px solid #4f46e5;
  outline-offset: 2px;
}

@keyframes homeFadeUp {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
#page-home .home-fade { animation: homeFadeUp 0.5s ease both; }
#page-home .home-fade:nth-child(2) { animation-delay: 0.06s; }

@media (prefers-reduced-motion: reduce) {
  #page-home .home-fade { animation: none; }
}

/* ============================================================
   PRINT
   ============================================================ */
/* ============================================================
   HTML result-card view (Option A) — the lightweight marksheet a
   student sees instead of the PDF. All rules are scoped under
   .result-view so the certificate's own (green/navy, Times) look
   never leaks into the app's indigo Tailwind theme. The .paper is a
   fixed 720px "document" that JS scales to fit the viewport (rvFit),
   so a dense report-card table never has to reflow onto a phone.
   ============================================================ */
.result-view {
  --rv-paper:#ffffff; --rv-green:#3f8138; --rv-green-d:#2f6a2a; --rv-gfill:#e7f0e1;
  --rv-navy:#213a7a; --rv-ink:#161616; --rv-red:#c01f1a;
  --rv-serif:"Times New Roman",Times,"Liberation Serif",Georgia,serif;
}
.result-view .rv-bar { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:12px; margin-bottom:14px; }
.result-view .rv-card { width:100%; max-width:820px; margin:0 auto; }
.result-view .tabnum { font-variant-numeric:tabular-nums; }

.result-view .paper {
  width:720px; margin:0; background:var(--rv-paper); color:var(--rv-ink); font-family:var(--rv-serif);
  padding:22px 24px 24px; position:relative; overflow:hidden; transform-origin:top left;
  box-shadow:0 1px 0 rgba(0,0,0,.05), 0 16px 40px -18px rgba(20,20,40,.5);
}
.result-view .paper::before {
  content:""; position:absolute; left:50%; top:52%; width:58%; height:58%;
  transform:translate(-50%,-50%); background:url("assets/logo.png") center/contain no-repeat;
  opacity:.05; z-index:0; pointer-events:none;
}
/* `.paper > .wm` must out-specify `.paper > *{position:relative}` or the tiled
   text watermark drops into the document flow instead of tiling behind it. */
.result-view .paper > .wm {
  position:absolute; inset:0; z-index:0; overflow:hidden; pointer-events:none; color:#1f346e;
  opacity:.05; font-size:13px; letter-spacing:.14em; line-height:2.5; word-spacing:.3em;
  text-transform:uppercase; padding:8px;
}
.result-view .paper > * { position:relative; z-index:1; }

.result-view .head { display:flex; align-items:center; gap:14px; margin-bottom:8px; }
.result-view .head .crest { width:90px; height:auto; flex:0 0 auto; }
.result-view .head .titles { flex:1; text-align:center; }
/* inline-block so its box == the text width; JS matches the banner + subtitle to it */
.result-view .schoolname { display:inline-block; font-size:22px; font-weight:700; color:#111; line-height:1.12; margin:0; text-wrap:balance; }
.result-view .subtitle { font-size:18px; margin:5px 0 0; text-align:center; }
.result-view .sub-in { display:inline-block; }
.result-view .banner { margin:8px auto 0; width:66%; max-width:100%; text-align:center; font-size:14.5px; background:var(--rv-gfill); border:1px solid var(--rv-green); padding:6px 10px; color:#111; }
/* Name / Class / Roll line. Margin top+bottom gives it breathing room between the
   exam banner above and the marks table below (it read as cramped against both).
   Shared rule, so the marksheet and the student's own card stay identical. */
.result-view .idrow { display:flex; justify-content:space-between; gap:12px; font-size:14px; margin:9px 2px 11px; flex-wrap:wrap; }
.result-view .idrow b { color:var(--rv-navy); }
.result-view .idrow b.lbl { color:inherit; }  /* labels bold but keep the plain ink colour */

.result-view table.marks { width:100%; border-collapse:collapse; table-layout:fixed; }
.result-view table.marks th, .result-view table.marks td { border:1px solid var(--rv-green); padding:3px 5px; text-align:center; word-wrap:break-word; }
.result-view table.marks thead th { background:var(--rv-gfill); color:var(--rv-navy); font-weight:700; font-size:10.5px; line-height:1.12; padding:3px 3px; white-space:nowrap; }
.result-view table.marks thead th.l { text-align:left; padding-left:7px; }  /* match td.sub's 7px left pad */
.result-view table.marks td { font-size:12.5px; color:var(--rv-ink); }
.result-view table.marks td.sub { text-align:left; font-weight:700; padding-left:7px; white-space:nowrap; }
.result-view table.marks td.tt { font-weight:700; font-size:13px; }
.result-view table.marks td.teach { color:#333; }
/* !important so a fail's red beats the table's own td colour (which is a more
   specific two-type selector). Mirrors the PDF's red conditional formatting. */
.result-view td.red { color:var(--rv-red) !important; font-weight:700; }

/* fixed layout → each subject's lecturer mini-grid has equal columns, so lecturer
   columns line up down the sheet and empty ones stay blank (not merged). Only
   INTERNAL grid lines are drawn; the enclosing cell supplies the outer frame. */
/* The grid must FILL the cell even when a wrapped subject name makes the row taller
   than the grid's natural height (else the column dividers stop short and the border
   looks "broken"). A nested <table height:100%> does NOT resolve against a td, so the
   grid is wrapped in a block flex column (.lectwrap) — a block's height:100% DOES
   resolve inside a td — and the table fills it as a flex:1 item (flexbox stretches
   reliably where table-% height won't). tr height:50% then splits that full height. */
.result-view .lectwrap { height:100%; display:flex; flex-direction:column; }
.result-view table.lect { width:100%; border-collapse:collapse; table-layout:fixed; flex:1 1 auto; }
.result-view table.lect tr { height:50%; }
.result-view table.lect td { border:0; border-right:1px solid var(--rv-green); border-bottom:1px solid var(--rv-green); padding:2px 3px; font-size:11px; text-align:center; height:17px; }
.result-view table.lect td:last-child { border-right:0; }
.result-view table.lect tr:last-child td { border-bottom:0; }
.result-view table.lect td.lt { background:rgba(63,129,56,.09); color:var(--rv-navy); font-weight:700; }
.result-view table.lect td.le { background:transparent; }
/* A definite height on the CELL lets the .lectwrap flex column's own height:100%
   resolve (a td's computed height counts as definite for a BLOCK child's %). The flex
   column then stretches the grid to fill the cell — see .lectwrap / table.lect above.
   `height:1px` is the classic idiom, and the 1px is NOT a real height: a td always
   fills its row, and a percentage child resolves against that USED height, so the grid
   still fills. It must not be `height:100%`, which asks for 100% of the TABLE's height
   — harmless until the marksheet grows the table to fill the A5 page (table.marks
   flex:1 0 auto), at which point the FIRST such row swallowed the entire slack and
   printed one enormous subject row with the rest left natural. HS never hit this
   because papers mode has no .lectcell. */
.result-view td.lectcell { padding:0 !important; height:1px; }

.result-view .sumrow { display:grid; grid-template-columns:1fr 1fr; gap:16px; margin:13px 0 0; }
.result-view table.sum { width:100%; border-collapse:collapse; }
.result-view table.sum td { border:1px solid var(--rv-green); padding:5px 9px; font-size:13px; }
.result-view table.sum td.k { color:var(--rv-navy); font-weight:700; width:56%; }
.result-view table.sum td.v { text-align:center; color:#111; font-weight:700; }
.result-view table.sum td.empty { height:25px; border:1px solid var(--rv-green); }
.result-view .footnote { font-size:12px; color:#333; margin:8px 2px 0; font-style:italic; }
.result-view .comments { border:1px solid var(--rv-green); margin-top:13px; padding:11px 13px; min-height:74px; font-size:13.5px; color:#111; display:flex; flex-direction:column; justify-content:center; text-align:justify; }
.result-view .sign { display:grid; grid-template-columns:1fr 1fr 1fr; gap:10px; margin-top:100px; text-align:center; }
.result-view .sign div { color:var(--rv-navy); font-weight:700; font-size:13.5px; letter-spacing:.03em; }

/* Hidden on screen; revealed inside @media print (.print-only rule below). */
.print-only {
  display: none;
}

@media print {
  body {
    background: white !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* Hide nav, footer, buttons, search UI */
  .no-print,
  nav,
  footer,
  #toast,
  #loading-overlay {
    display: none !important;
  }

  main {
    max-width: 100% !important;
    padding: 0 !important;
  }

  /* Pages the router hid (class="hidden") stay hidden */
  .page.hidden {
    display: none !important;
  }

  /* On the Home page: hide hero + search form, show only the result card */
  #page-home > div:not(#lookup-result) {
    display: none !important;
  }

  /* Result preview: hide the action buttons row */
  #result-preview-title,
  #result-preview-refresh,
  #result-preview-print {
    display: none !important;
  }

  /* Elements that appear ONLY when printing (hidden on screen, see below). */
  .print-only {
    display: block !important;
  }

  /* HS Results Preview broadsheet: same A4-landscape fit-to-width treatment as
     Entry Review. `print-landscape` is set on <body> by renderHsResultsPreview. */
  body.print-landscape #hs-results-preview-content {
    box-shadow: none !important;
    padding: 0 !important;
  }
  body.print-landscape #hs-results-preview-content table.hs-preview-broadsheet {
    width: 100% !important;
    table-layout: auto;
    font-size: 9px;
  }
  body.print-landscape #hs-results-preview-content table.hs-preview-broadsheet th,
  body.print-landscape #hs-results-preview-content table.hs-preview-broadsheet td {
    padding: 2px 4px !important;
  }
  /* Name column (2nd): fit the longest name on ONE line rather than wrapping. The
     subject columns have vertical headers + short marks, so they stay narrow and
     with table-layout:auto the name column sizes to its content while the rest
     reflow to fill the A4-landscape page width. */
  body.print-landscape #hs-results-preview-content table.hs-preview-broadsheet th:nth-child(2),
  body.print-landscape #hs-results-preview-content table.hs-preview-broadsheet td:nth-child(2) {
    white-space: nowrap !important;
  }
  /* "Print all classes" view (Results Preview AND Entry Review): each class
     wrapper starts on a fresh page; its heading stays with its table. Scoped to
     .all-class-page so it applies in any print-landscape content container. */
  body.print-landscape .all-class-page {
    break-before: page;
    page-break-before: always;
  }
  body.print-landscape .all-class-page:first-child {
    break-before: auto;
    page-break-before: avoid;
  }
  body.print-landscape .all-class-page > h2 {
    break-after: avoid;
    page-break-after: avoid;
  }

  .result-card {
    box-shadow: none !important;
    border: 1px solid #475569;
    margin: 0 0 12mm 0;
    padding: 10mm !important;
    background: white !important;
  }

  /* HTML result-card view: print the certificate at its true (un-scaled) size on
     A4 portrait. rvFit applies an inline transform:scale() + a fixed host height
     for the on-screen fit; both are reset here so the .paper prints full-size. The
     shared .result-card border/padding above is dropped — the .paper is the sheet. */
  .result-view.result-card {
    border: none !important;
    padding: 0 !important;
    margin: 0 !important;
  }
  .result-view .rv-bar { display: none !important; }
  .result-view .rv-card {
    height: auto !important;
    max-width: none !important;
  }
  /* Scoped to the single-card VIEWER (.rv-card) so it never strips the scale the
     marksheet-print path applies to .ms-sheet .paper (which is NOT under .rv-card). */
  .result-view .rv-card .paper {
    transform: none !important;
    margin: 0 !important;
    box-shadow: none !important;
  }


  /* Print student marksheets: isolate the overlay (a direct <body> child), one A5
     page per .ms-sheet. `body.print-marksheets` is set by openMarksheetPrint's
     Print button and cleared on afterprint/close. */
  body.print-marksheets > *:not(#ms-print-root) { display: none !important; }
  body.print-marksheets #ms-print-root {
    position: static !important; inset: auto !important; overflow: visible !important;
    background: #fff !important; z-index: auto !important;
  }
  body.print-marksheets #ms-print-root .ms-toolbar { display: none !important; }
  body.print-marksheets #ms-print-root .ms-pages { display: block; gap: 0; padding: 0; }
  /* MOBILE PARITY. A phone lays the print job out against its narrow layout viewport
     and then shrink-to-fits, which made the printed sheet differ from desktop. Pin
     every level to the real A5 page width so there is nothing left to shrink, and
     cancel the screen-only preview transform (it must never reach paper). */
  body.print-marksheets #ms-print-root,
  body.print-marksheets #ms-print-root .ms-pages {
    width: 148mm !important; min-width: 148mm !important; max-width: 148mm !important;
    margin: 0 !important;
  }
  body.print-marksheets .ms-sheet {
    box-shadow: none !important; margin: 0 auto;
    transform: none !important;              /* undo the mobile preview scale */
    margin-bottom: 0 !important;
    page-break-after: always; break-after: page;
  }
  body.print-marksheets .ms-sheet:last-child {
    page-break-after: auto; break-after: auto;
  }

  /* PRINT-ONLY, and it is a print artefact rather than a restyle — so it must NOT
     reach the on-screen preview, which should keep the card's normal 1px box.
     The comments box is a lone block, not a collapsed table: collapsed table borders
     get snapped to whole device pixels (and rounded up, never allowed to vanish),
     while a standalone border gets no such treatment and thins out under the card's
     0.714 zoom — so a 1px box printed as a hairline beside the tables.
     The rounding is renderer-specific, so this value is EMPIRICAL, tuned against
     printed output: 1.3px, 2px and 2.8px all read lighter than the tables — 3px
     (≈2.14px after the zoom) is where it lands.
     NOT scoped to body.print-marksheets: printing goes through a dedicated iframe
     (printMarksheetsIsolated) whose body carries no such class, so the selector has
     to be the sheet itself to apply on the path that actually prints. */
  .ms-sheet .paper .comments { border-width: 3px; }

  /* HS/HSS Entry Review broadsheet: print on A4 landscape and shrink the table to
     fit the page width on a single sheet. `print-landscape` is set on <body>
     by Pages.renderReviewSheet while that page is active. */
  body.print-landscape #hs-review-content,
  body.print-landscape #hss-review-content {
    box-shadow: none !important;
    padding: 0 !important;
  }
  body.print-landscape #hs-review-content table,
  body.print-landscape #hss-review-content table {
    width: 100% !important;
    table-layout: auto;
    font-size: 9px;
  }
  body.print-landscape #hs-review-content th,
  body.print-landscape #hs-review-content td,
  body.print-landscape #hss-review-content th,
  body.print-landscape #hss-review-content td {
    padding: 2px 4px !important;
  }
  /* Name column (2nd): fit the longest name on ONE line rather than wrapping. The
     subject columns have vertical headers + short marks, so they stay narrow;
     table-layout:auto sizes the name column to its content and reflows the rest
     to fill the A4-landscape page width. */
  body.print-landscape #hs-review-content th:nth-child(2),
  body.print-landscape #hs-review-content td:nth-child(2),
  body.print-landscape #hss-review-content th:nth-child(2),
  body.print-landscape #hss-review-content td:nth-child(2) {
    white-space: nowrap !important;
  }
}

/* A4 landscape only when the review broadsheet is the active page. A bare
   @page applies to the whole print job, so it is gated by the body class via
   the rule's presence (the class is removed on other pages). */
@page bhss-landscape {
  size: A4 landscape;
  margin: 8mm;
}
body.print-landscape {
  page: bhss-landscape;
}

/* A5 portrait for the student-marksheet print job. Full-bleed page (margin:0);
   each card is inset ~5mm inside its .ms-sheet (fitMarksheetSheets' PAD), so it
   clears the printer's non-printable edge without a second blank page. */
@page bhss-a5 {
  size: A5 portrait;
  margin: 0;
}
body.print-marksheets {
  page: bhss-a5;
}

/* ============================================================
   PRINT STUDENT MARKSHEETS (A5) — admin Printable PDFs preview
   overlay. Each card (the same .result-view .paper the student
   sees) sits in an A5 .ms-sheet, scaled to fit by fitMarksheetSheets.
   On screen it's a full-page review overlay; @media print (above)
   isolates #ms-print-root so each .ms-sheet is one A5 page.
   ============================================================ */
.ms-overlay {
  position: fixed; inset: 0; z-index: 60; background: #334155; overflow: auto;
  -webkit-overflow-scrolling: touch;
}
/* MOBILE PARITY — defeat Chrome/WebKit TEXT AUTOSIZING ("font boosting").
   A phone inflates the font size of any text block sitting in a container WIDER than
   the viewport, so it stays readable without pinch-zoom. The card is a fixed 720px on
   a ~390px viewport — a textbook trigger — so the school name was boosted past its
   22px, overflowed its flex column and wrapped onto 2 lines. The marksheet is a
   to-scale document, not reflowing page text, so autosizing must be off: this is what
   makes the mobile card render identically to desktop. Applies on screen AND in print
   (the boosted size otherwise carries into the printed page). */
.ms-overlay,
.ms-overlay .paper {
  -webkit-text-size-adjust: 100%;
  -moz-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
.ms-overlay .ms-toolbar {
  position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 10px;
  background: #1e293b; color: #fff; padding: 10px 16px; box-shadow: 0 2px 10px rgba(0, 0, 0, .35);
}
.ms-overlay .ms-pages {
  display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 20px 8px 60px;
}

/* MARKS ENTRY — active-row highlight. While a mark/assignment/attendance input is
   focused, pages.js adds .mark-row-active to its <tr> so the teacher can see which
   student they're working on. The whole row gets a faint indigo wash; the frozen
   roll-number cell (sticky col A, first-child) is lit stronger + bold so the roll
   number itself stands out. Specificity beats Tailwind's .bg-white on the sticky
   cell (tr.mark-row-active > td:first-child = (0,2,2) vs .bg-white (0,1,0)). */
tr.mark-row-active > td { background-color: #eef2ff; } /* indigo-50 */
tr.mark-row-active > td:first-child {
  background-color: #c7d2fe;                            /* indigo-200 */
  color: #3730a3;                                        /* indigo-800 */
  font-weight: 700;
}
/* MOBILE PREVIEW FIT (screen only — the print rules above reset this). An .ms-sheet is
   a fixed 148mm (~559px) and cannot reflow, so on a phone only part of the page was
   visible. Shrink the whole sheet to the available width with a transform: purely
   VISUAL, so the sheet's layout box stays exactly 148×210mm and the card's zoom-to-fit
   is computed from identical numbers on every device — which is what keeps the printed
   page the same as desktop.
   --ms-prev is measured by fitMarksheetSheets and is 1 on any screen wide enough, so
   this is an identity transform on desktop. Deliberately NOT behind a media query: a
   breakpoint can disagree with the actual measured width, and the variable already
   self-limits to 1. */
@media screen {
  .ms-overlay .ms-pages { padding-left: 0; padding-right: 0; }
  .ms-overlay .ms-sheet {
    transform: scale(var(--ms-prev, 1));
    transform-origin: top center;
    /* Reclaim the space the scale frees, so sheets don't sit in a tall gap.
       Negative whenever --ms-prev < 1; exactly 0 at 1. */
    margin-bottom: calc((var(--ms-prev, 1) - 1) * 210mm);
  }
}
.ms-sheet {
  position: relative; width: 148mm; height: 210mm; overflow: hidden; background: #fff;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .4); flex: 0 0 auto;
  padding: 6mm; box-sizing: border-box;  /* the 6mm printer-safe margin; card fits inside */
  display: flex; justify-content: center; align-items: flex-start;  /* centre the card */
}
/* The card scales to fit via inline `zoom` (fitMarksheetSheets) — zoom prints
   reliably where transform:scale() is dropped by the print renderer. It FILLS the
   A5 page (min-height at A5 proportions) and is a flex column so the signature line
   pins to the BOTTOM, leaving the space above it for signing.

   EVERYTHING ELSE IS INHERITED, DELIBERATELY. The printed marksheet must be the same
   artefact the student sees on their own result page, so this block adds only what
   A5 pagination genuinely needs — no font sizes, no padding, no watermark of its own.
   Anything added here is a way for the two to drift apart. */
.ms-sheet .paper {
  flex: 0 0 auto; z-index: 1;
  box-shadow: none !important;
  min-height: 1048px;              /* 720px × A5 ratio (198/136) → fills the sheet when zoomed */
  display: flex; flex-direction: column;
}
/* The marks table absorbs the page's leftover height so its ROWS get taller, and the
   signature line keeps a fixed ~5-6-line signing gap above it. Without the grow, all
   the slack piles up above the signatures as one large void — which is what HSS did:
   lecturer mode is one row per subject, so its table is far shorter than HS's
   papers mode (2-3 rows per subject) and it never filled the page by luck the way HS
   did. Both sides now fill it by construction.

   This grow was removed once because it broke the nested lecturer-grid cell borders,
   but that root cause was fixed since — a definite height on td.lectcell + the
   .lectwrap flex column (see table.lect above) — so the grid stretches with its row.
   That cell's height must stay `1px`, NOT `100%`: 100% asks for the whole TABLE
   height, so with this grow the first lecturer row swallowed all the slack and printed
   one giant subject row. See the .lectcell rule for the full note.

   PAGINATION, not restyle: this is the A5 page-fill machinery, the same category as
   min-height above, so it does not reopen the drift the .ms-sheet block warns about. */
.ms-sheet .paper table.marks { flex: 1 0 auto; }
.ms-sheet .paper .sign { margin-top: 96px; }

/* Lift the signature line off the bottom edge. Printed geometry at the A5 zoom
   (0.714; 1mm ≈ 3.7795px), measured from the sheet edge including its 6mm padding:
     top edge → school name  = 6mm + 22px×0.714 ≈ 10.2mm
     signatures → bottom     = 6mm + 24px×0.714 ≈ 10.5mm   (inherited 24px)
   Numerically level, yet the top READS roomier: the school name's line-height 1.12
   puts leading above its cap height, while the small signature text has almost none
   below its baseline. 34px ≈ 12.4mm restores the balance a certificate wants — bottom
   a shade generous rather than level. Safe to tune: box-sizing is border-box, so this
   comes out of the content box that table.marks grows into, leaving the card's overall
   height — and therefore the zoom — untouched. */
.ms-sheet .paper { padding-bottom: 34px; }

/* THE "SMALL TRIANGLE" UNDER THE SIGNATURES. The card's big faint crest is drawn as
   .paper::before — a background-image of the school shield, whose base is a downward
   POINT. On the student's dense on-screen card that point is lost behind content, but
   the A5 marksheet stretches the card to 1048px, which scales the crest up and drops
   its tip into the open white space around the signature row, where it reads as a
   stray triangle. It shows on paper more than on screen because @media print sets
   print-color-adjust:exact, which makes the browser render the background faithfully
   instead of dropping/lightening it.

   The marksheet hid this crest until b1f7883 (the parity commit) deleted the rule
   along with the full-page .ms-wm layer it belonged to; the reports began right after.
   Restoring it. The card keeps its tiled TEXT watermark (.wm), which is what actually
   reads as the watermark on the printed sheets, so nothing is visually lost. */
.ms-sheet .paper::before { display: none !important; }

/* ============================================================
   PRINT ALL CLASSES (broadsheets) — the review overlay opened by
   openBroadsheetPrint for "Print Result Broadsheets for Office"
   and "Print Entry Reviews for Class Notice Board".

   Deliberately plainer than .ms-overlay: a marksheet is a
   to-scale A5 document that has to be shown at true size, while
   a broadsheet is a wide table that simply needs room and a
   horizontal scrollbar. Printing does NOT go through these rules
   at all — printBroadsheetsIsolated writes its own document (so
   there is no body.print-* class to mirror here).
   ============================================================ */
.bs-overlay {
  position: fixed; inset: 0; z-index: 60; background: #334155; overflow: auto;
  -webkit-overflow-scrolling: touch;
}
.bs-overlay .bs-toolbar {
  position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 10px;
  background: #1e293b; color: #fff; padding: 10px 16px; box-shadow: 0 2px 10px rgba(0, 0, 0, .35);
}
.bs-overlay .bs-pages {
  display: flex; flex-direction: column; gap: 20px; padding: 20px 16px 60px;
}
/* One class per card, mirroring how it lands on paper. Each scrolls its own wide
   table so the overlay itself never scrolls sideways. */
.bs-overlay .all-class-page {
  background: #fff; border-radius: 6px; padding: 16px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .4);
  overflow-x: auto;
}
.bs-overlay .bs-skipped {
  color: #cbd5e1; font-size: 12px; padding: 0 16px 40px;
}

/* ============================================================
   ENTRY REVIEW broadsheets — reachable horizontal scrollbar
   ============================================================
   The review table is wider than the screen AND tall (one row per student),
   so the container's horizontal scrollbar sat at the BOTTOM of a long table —
   you could only reach it after scrolling the whole page to the bottom. Bound
   the container to the viewport height so it scrolls INTERNALLY: the horizontal
   scrollbar now rides the bottom edge of a box that stays on screen, so it's
   reachable straight away, and the page no longer overflows sideways. The
   frozen roll column keeps working because this is still its scroll container.

   SCREEN ONLY. Print (Ctrl+P → A4 landscape, via body.print-landscape) must
   lay the full table across as many pages as it needs, so it keeps the natural
   height — @media print never sees this rule. max-height (not height) so a
   short class shrinks to fit with no empty scroll box. */
@media screen {
  #hs-review-content,
  #hss-review-content {
    max-height: calc(100vh - 9.5rem);
    overflow: auto;
  }
  /* Results Preview: same idea, but bound only the .preview-scroll wrapper
     around the broadsheet (buildResultPreviewHtml), so the SUMMARY/toppers
     block below it stays in normal page flow instead of inside the scroll box.
     The wrapper sits inside the content div's p-4, hence a touch more offset
     than the review pages to keep the scrollbar the same distance off-screen. */
  #hs-results-preview-content .preview-scroll {
    max-height: calc(100vh - 10.5rem);
    overflow: auto;
  }
  /* Marks Entry: bound the card the same way so its header can pin. Its action
     bar is sticky + a touch taller (py-3 + border), hence a slightly larger
     offset than the review pages. The Clear-Column / Attendance-Helper panels
     sit inside this box, above the table, and scroll away as marks are entered. */
  #hs-marks-content,
  #hss-marks-content {
    max-height: calc(100vh - 10rem);
    overflow: auto;
  }

  /* STICKY TABLE HEADERS. Each card above is its own scroll region (an overflow
     ancestor scopes position:sticky, so a page-scroll can't pin a <thead> — a
     bounded inner region can). The header cells then pin to the top of that
     region while the student rows scroll under them. Screen only: print lays the
     full table across pages and must keep the natural (non-sticky) header. */
  #hs-marks-content thead th,
  #hss-marks-content thead th,
  #hs-review-content thead th,
  #hss-review-content thead th,
  #hs-results-preview-content .preview-scroll thead th {
    position: sticky;
    top: 0;
    z-index: 15;
  }
  /* Entry Review has TWO header rows — subjects (row 1), then teacher abbreviations
     (row 2). Pin row 2 just below row 1 so they don't overlap; row 1's height is
     measured into --review-head-h by pages.js (the vertical subject labels make it
     content-dependent). Falls back to 0 (rows touch) before JS runs. */
  #hs-review-content thead tr:nth-child(2) th,
  #hss-review-content thead tr:nth-child(2) th {
    top: var(--review-head-h, 0px);
  }
  /* The frozen roll cell pins in BOTH axes (top + its existing sticky-left), so it
     must sit above the rest of the header row and the sticky roll data cells. */
  #hs-marks-content thead th:first-child,
  #hss-marks-content thead th:first-child,
  #hs-review-content thead th:first-child,
  #hss-review-content thead th:first-child,
  #hs-results-preview-content .preview-scroll thead th:first-child {
    z-index: 16;
  }
}

/* ============================================================
   UTILITY: smooth scroll
   ============================================================ */
html {
  scroll-behavior: smooth;
}

/* Input number spinner removal (cleaner look) */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
input[type="number"] {
  -moz-appearance: textfield;
}
