@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=JetBrains+Mono:wght@400;500;600;700&display=swap');

/* Basic fonts and Tailwind base layer rules are loaded from CDN in Jaspr */
/* Neutral ScholarFee defaults — replaced at runtime with the active
   school's primary (--brand-*) and secondary (--brand-2-*) colors by
   lib/theme/brand_theme.dart. */
:root {
  --brand-rgb: 10 10 10;
  --brand-light: #F4F4F4;
  --brand-dark: #1A1A1A;
  --brand-contrast: #FFFFFF;
  --brand-2-rgb: 64 64 64;
  --brand-2-light: #F0F0F0;
  --brand-2-dark: #303030;
  --brand-2-contrast: #FFFFFF;
}

body {
  font-family: "Inter", ui-sans-serif, system-ui, sans-serif;
}

/* ═══════════════════════════════════════════════════════════
   Mobile-first foundation
   ═══════════════════════════════════════════════════════════ */

html {
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
}

/* Momentum scrolling + contained overscroll for scroll regions.
   Contain only the axis each region actually scrolls: a blanket
   `overscroll-behavior: contain` on x-scrollers (tables) also swallows
   vertical wheel/trackpad input, freezing page scroll over any table. */
.overflow-x-auto,
.overflow-y-auto {
  -webkit-overflow-scrolling: touch;
}
.overflow-x-auto {
  overscroll-behavior-x: contain;
}
.overflow-y-auto {
  overscroll-behavior-y: contain;
}

/* iOS Safari zooms the page when a focused control's font-size is below
   16px. Force 16px on touch-sized screens so focus never triggers zoom. */
@media (max-width: 1023px) {
  input:not([type='checkbox']):not([type='radio']),
  select,
  textarea {
    font-size: 16px !important;
  }
}

/* Comfortable touch targets on coarse-pointer (touch) devices.
   Applied via .tap-target so tiny decorative controls (e.g. the theme
   toggle) keep their compact size. */
@media (pointer: coarse) {
  .tap-target {
    min-height: 44px;
    min-width: 44px;
  }
  button:not(.no-tap),
  a[href],
  select,
  input:not([type='checkbox']):not([type='radio']) {
    min-height: 44px;
  }
  input[type='checkbox'],
  input[type='radio'] {
    min-width: 20px;
    min-height: 20px;
  }
}

/* Safe-area helpers for notched phones */
.pb-safe {
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
.bottom-nav-safe {
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 0.25rem);
}

/* When a modal/bottom-sheet (.sf-modal) is open, hide the mobile app chrome
   (bottom tab bar + sticky header). Those are position:fixed at the root, and
   on mobile browsers a modal rendered inside the scrolling <main> can paint
   *beneath* them — so the bottom nav ends up covering the modal's action
   button. Hiding the chrome while a modal is open removes the overlap. */
body:has(.sf-modal) .app-mobile-chrome {
  display: none !important;
}

/* ═══════════════════════════════════════════════════════════
   Phone density (see lib/pages/layout.dart — the .sf-app <main>)
   ═══════════════════════════════════════════════════════════

   The pages are written at desktop density: `space-y-8` between blocks,
   `p-6`/`p-8` cards, and `py-4` form controls. On a ~700px phone screen that
   spends most of the first screenful on padding — the Activity log's table and
   the staff list both started below the fold.

   Tightening it here rather than in each page keeps one source of truth and
   leaves the desktop layout untouched: everything below applies only under
   768px, and only inside the signed-in app. Touch targets are unaffected —
   the `pointer: coarse` rules above still hold every control at 44px.

   Deliberately NOT touched: font sizes (16px on controls stops iOS zooming),
   gap utilities inside cards, and anything outside .sf-app. */
@media (max-width: 767px) {
  /* Vertical rhythm between page blocks: 2rem -> 1.25rem */
  .sf-app .space-y-8 > :not([hidden]) ~ :not([hidden]) {
    margin-top: 1.25rem;
  }
  .sf-app .space-y-6 > :not([hidden]) ~ :not([hidden]) {
    margin-top: 1rem;
  }

  /* Card padding. p-8 is a desktop flourish; p-6 is the common card. */
  .sf-app .p-8 {
    padding: 1rem;
  }
  .sf-app .p-6 {
    padding: 1rem;
  }
  .sf-app .sm\:p-8 {
    padding: 1rem;
  }

  /* Form controls: the tallest repeated element on filter-heavy pages
     (Activity log, Report Cards). Height is still governed by the 44px
     touch-target minimum, so this only removes slack. */
  .sf-app select,
  .sf-app input:not([type='checkbox']):not([type='radio']) {
    padding-top: 0.625rem;
    padding-bottom: 0.625rem;
  }

  /* Filter grids stack one control per row by default, which is what makes
     three filters 300px tall. Two across fits comfortably at 16px type. */
  .sf-app .grid.grid-cols-1.sm\:grid-cols-3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.5rem;
  }
}

/* ═══════════════════════════════════════════════════════════
   Toast notifications (see lib/services/toast_service.dart)
   ═══════════════════════════════════════════════════════════ */

#sfee-toasts {
  position: fixed;
  z-index: 100;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(env(safe-area-inset-bottom, 0px) + 5.5rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  width: min(92vw, 24rem);
  pointer-events: none;
}
@media (min-width: 1024px) {
  #sfee-toasts {
    bottom: 1.5rem;
    left: auto;
    right: 1.5rem;
    transform: none;
    align-items: flex-end;
  }
}
.sfee-toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  width: 100%;
  padding: 0.875rem 1rem;
  border-radius: 1rem;
  background: #171717;
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.4;
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.25);
  animation: toastIn 0.25s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;
  cursor: pointer;
}
.sfee-toast.leaving {
  animation: toastOut 0.2s ease-in forwards;
}
.sfee-toast .sfee-toast-icon {
  flex-shrink: 0;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 900;
  margin-top: 0.0625rem;
}
.sfee-toast-success .sfee-toast-icon { background: #10b981; color: #fff; }
.sfee-toast-error .sfee-toast-icon { background: #ef4444; color: #fff; }
.sfee-toast-info .sfee-toast-icon { background: #6366f1; color: #fff; }
.sfee-toast-loading .sfee-toast-icon {
  background: transparent;
  border: 2px solid rgb(255 255 255 / 0.3);
  border-top-color: #fff;
  animation: spin 0.8s linear infinite;
}

@keyframes toastIn {
  from { opacity: 0; transform: translateY(12px) scale(0.97); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes toastOut {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to { opacity: 0; transform: translateY(8px) scale(0.97); }
}

/* Skeleton shimmer for loading placeholders */
@keyframes shimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}
.skeleton {
  border-radius: 0.75rem;
  background: linear-gradient(90deg, #f0f0ef 25%, #e6e6e4 50%, #f0f0ef 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s ease infinite;
}

/* Respect users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

.serif {
  font-family: "Libre Baskerville", serif;
}

.font-mono {
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
}

/* ═══════════════════════════════════════════════════════════
   Animation Keyframes (replaces motion/react from React app)
   ═══════════════════════════════════════════════════════════ */

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes fadeOut {
  from { opacity: 1; }
  to { opacity: 0; }
}

@keyframes slideUp {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

@keyframes slideDown {
  from { opacity: 0; transform: translateY(-10px); }
  to { opacity: 1; transform: none; }
}

@keyframes slideInLeft {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

@keyframes slideOutLeft {
  from { transform: translateX(0); }
  to { transform: translateX(-100%); }
}

@keyframes scaleIn {
  from { opacity: 0; transform: scale(0.95); }
  to { opacity: 1; transform: scale(1); }
}

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.animate-fadeIn {
  animation: fadeIn 0.3s ease-out forwards;
}

.animate-slideUp {
  animation: slideUp 0.3s ease-out forwards;
}

.animate-slideDown {
  animation: slideDown 0.3s ease-out forwards;
}

.animate-slideInLeft {
  animation: slideInLeft 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

@keyframes slideInRight {
  from { transform: translateX(100%); }
  to { transform: translateX(0); }
}

.animate-slideInRight {
  animation: slideInRight 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

.animate-scaleIn {
  animation: scaleIn 0.2s ease-out forwards;
}

/* Staggered entrance for card grids. Put .stagger-children on the container;
   each direct child fades up shortly after the one before it. Capped at the
   10th child — beyond that the wait reads as lag, not polish.

   Only use this where the set of children is stable. On a filtered list (the
   students table, the finance ledger) the nth-child indices shift as the user
   types, which re-runs the delays on rows that had already settled. */
.stagger-children > * {
  animation: slideUp 0.35s ease-out both;
}
.stagger-children > *:nth-child(1) { animation-delay: 0ms; }
.stagger-children > *:nth-child(2) { animation-delay: 45ms; }
.stagger-children > *:nth-child(3) { animation-delay: 90ms; }
.stagger-children > *:nth-child(4) { animation-delay: 135ms; }
.stagger-children > *:nth-child(5) { animation-delay: 180ms; }
.stagger-children > *:nth-child(6) { animation-delay: 225ms; }
.stagger-children > *:nth-child(7) { animation-delay: 270ms; }
.stagger-children > *:nth-child(8) { animation-delay: 315ms; }
.stagger-children > *:nth-child(9) { animation-delay: 360ms; }
.stagger-children > *:nth-child(n + 10) { animation-delay: 405ms; }

/* Hide scrollbar utility (used in tab headers) */
.hide-scrollbar {
  -ms-overflow-style: none;
  scrollbar-width: none;
}
.hide-scrollbar::-webkit-scrollbar {
  display: none;
}

/* Slow spin animation for refresh icon */
.animate-spin-slow {
  animation: spin 2s linear infinite;
}

/* Report-card print target — populated only at print time, hidden on screen. */
#print-root {
  display: none;
}

/* ═══════════════════════════════════════════════════════════
   Print Styles (matching React legacy index.css exactly)
   ═══════════════════════════════════════════════════════════ */

@media print {
  /* Hide the sidebar and any print-hide elements on all pages */
  aside,
  .print-hide,
  .print\:hidden {
    display: none !important;
  }

  /* Reset layout constraints to allow flow printing */
  html, body {
    height: auto !important;
    min-height: auto !important;
    overflow: visible !important;
    background: white !important;
    color: black !important;
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }

  /* Basic Jaspr app root resetting */
  #jaspr-app,
  #jaspr-app > div,
  main,
  main > div {
    display: block !important;
    height: auto !important;
    min-height: auto !important;
    overflow: visible !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 100% !important;
    max-width: 100% !important;
    border: none !important;
    box-shadow: none !important;
    background: transparent !important;
  }

  /* Special receipt mode behavior */
  body.printing-receipt main > div > *:not(#receipt-portal-container) {
    display: none !important;
  }

  #receipt-portal-container {
    display: block !important;
    visibility: visible !important;
    position: absolute !important;
    left: 0 !important;
    top: 0 !important;
    width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    background: white !important;
    color: black !important;
    box-shadow: none !important;
    border: none !important;
  }

  #receipt-portal-container * {
    visibility: visible !important;
  }

  /* Special report card mode behavior */
  @page {
    size: A4;
    margin: 10mm;
  }

  /* Report-card printing clones the card(s) into #print-root, a direct child
     of <body>. Hiding every other body child isolates it cleanly no matter how
     deeply nested the source was (single preview or bulk-print modal). */
  body.printing-report-card > *:not(#print-root) {
    display: none !important;
  }

  body.printing-report-card #print-root {
    display: block !important;
    position: static !important;
    width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    background: white !important;
    color: black !important;
  }

  /* ── One-page A4 fit ──────────────────────────────────────
     A report card is one sheet of A4, always. The layouts themselves are
     sized in millimetres (see the rc-* block below) and absorb a long subject
     list by stepping type down rather than by growing, so printing only has
     to strip the on-screen page chrome and let them through at their own
     size. Nothing here is keyed to a utility class any more — the templates
     own their own measurements. */
  body.printing-report-card #print-root .rc-sheet {
    width: 100% !important;
    min-height: 0 !important;
    height: auto !important;
    max-height: 277mm !important; /* A4 less the 10mm @page margins */
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    overflow: hidden !important;
  }

  /* Never split a marks row, a domain block or a signing area across pages —
     on a one-sheet document a split is a bug, not a page break. */
  body.printing-report-card #print-root .rc-sheet tr,
  body.printing-report-card #print-root .rc-domain,
  body.printing-report-card #print-root .rc-remark,
  body.printing-report-card #print-root .rc-fin,
  body.printing-report-card #print-root .rc-foot,
  body.printing-report-card #print-root .rc-of-comments,
  body.printing-report-card #print-root .rc-of-domains,
  body.printing-report-card #print-root .rc-of-mid {
    page-break-inside: avoid !important;
    break-inside: avoid !important;
  }

  #print-root .report-page-break-after {
    page-break-after: always !important;
    break-after: page !important;
  }
  /* Avoid a trailing blank page after the final card. */
  #print-root .report-page-break-after:last-child {
    page-break-after: auto !important;
    break-after: auto !important;
  }

  .report-page-break {
    page-break-before: always !important;
    break-before: page !important;
  }

  .report-page-break-after {
    page-break-after: always !important;
    break-after: page !important;
  }

  .pos-thermal-print {
    max-width: 320px !important;
    font-size: 11px !important;
    line-height: 1.2 !important;
    font-family: monospace !important;
    padding: 10px !important;
    margin: 0 auto !important;
  }

  .pos-thermal-print * {
    font-family: monospace !important;
    background: transparent !important;
    color: black !important;
    border-color: black !important;
  }
}

/* ═══════════════════════════════════════════════════════════
   Dark Mode (CSS filter inversion — matches React exactly)
   ═══════════════════════════════════════════════════════════ */

html.dark {
  filter: invert(1) hue-rotate(180deg);
  background-color: #0A0A0A;
}

html.dark body {
  background-color: #0A0A0A;
}

html.dark img, 
html.dark video, 
html.dark .no-invert {
  filter: invert(1) hue-rotate(180deg);
}

html.dark text, html.dark span, html.dark p, html.dark h1, html.dark h2, html.dark h3, html.dark h4, html.dark h5, html.dark h6 {
  -webkit-font-smoothing: antialiased;
}

/* ═══════════════════════════════════════════════════════════
   Report card templates (.rc-*)
   ═══════════════════════════════════════════════════════════

   Three layouts — Classic, Compact, Modern — over one shared measurement
   system. The hard rule all three keep: a card is ONE sheet of A4. The sheet
   is sized in millimetres rather than pixels so what a bursar sees on screen
   is the paper, and a long subject list is absorbed by the density steps
   (.rc-d1 … .rc-d4, chosen in Dart from the subject count) stepping type and
   row padding down — never by letting the card run onto a second page.

   Every size below is expressed through the custom properties on .rc-sheet,
   so a density step is four numbers, not a re-skin of the whole card.
   ─────────────────────────────────────────────────────────── */

.rc-sheet {
  /* Density-controlled (overridden per .rc-d*). */
  --rc-fs: 10px;        /* body text */
  --rc-tbl-fs: 10px;    /* marks table */
  --rc-row-pad: 3.4px;  /* marks row vertical padding */
  --rc-gap: 9px;        /* between top-level sections */

  /* Fixed across densities. */
  --rc-brand: #0A0A0A;
  --rc-ink: #111827;
  --rc-muted: #6b7280;
  --rc-faint: #9ca3af;
  --rc-rule: #d1d5db;
  --rc-hair: #e5e7eb;
  --rc-wash: #f8fafc;
  --rc-label-fs: 7.2px;

  box-sizing: border-box;
  width: 210mm;
  min-height: 297mm;
  padding: 10mm;
  margin: 0 auto;
  background: #fff;
  color: var(--rc-ink);
  font-size: var(--rc-fs);
  line-height: 1.3;
  display: flex;
  flex-direction: column;
  gap: var(--rc-gap);
  /* Screen-only page chrome; print strips all three. */
  border: 1px solid var(--rc-hair);
  border-radius: 6px;
  box-shadow: 0 1px 3px rgb(0 0 0 / 6%);
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

.rc-sheet *,
.rc-sheet *::before,
.rc-sheet *::after { box-sizing: border-box; }

/* Density steps. A nursery class with six subjects gets a roomy sheet; a
   senior class with twenty-four gets a tight one; both get one sheet.

   These are the PDF generator's own steps converted from points to pixels
   (1pt = 4/3px), because the same card printed from here and from the bursar
   desktop app has to come out the same size. Which step a card gets is decided
   once, in `ReportTemplate.densityStep` in `shared/`, which both renderers
   call — see `densityClass()`. */
.rc-d1 { --rc-fs: 14.4px; --rc-tbl-fs: 14.4px; --rc-row-pad: 8px;   --rc-gap: 17px;   --rc-label-fs: 10.7px; }
.rc-d2 { --rc-fs: 13.7px; --rc-tbl-fs: 13.6px; --rc-row-pad: 5.8px; --rc-gap: 13.4px; --rc-label-fs: 10.3px; }
.rc-d3 { --rc-fs: 13.1px; --rc-tbl-fs: 12.8px; --rc-row-pad: 3.2px; --rc-gap: 9.5px;  --rc-label-fs: 10px; }
.rc-d4 { --rc-fs: 12.4px; --rc-tbl-fs: 11.5px; --rc-row-pad: 2.2px; --rc-gap: 6.8px;  --rc-label-fs: 9.5px; }
.rc-d5 { --rc-fs: 11.2px; --rc-tbl-fs: 10.4px; --rc-row-pad: 1px;   --rc-gap: 4.4px;  --rc-label-fs: 8.9px; }

/* ── Shared atoms ────────────────────────────────────────── */

.rc-cell-label {
  display: block;
  font-size: var(--rc-label-fs);
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--rc-faint);
}

.rc-cell {
  min-width: 0;
  padding: 2px 5px;
  border: 1px solid var(--rc-hair);
  border-radius: 4px;
  background: #fff;
  line-height: 1.25;
}

.rc-cell-value {
  display: block;
  font-weight: 800;
  font-size: calc(var(--rc-fs) + 0.6px);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rc-num { font-variant-numeric: tabular-nums; text-align: center; }
.rc-empty { color: var(--rc-faint); font-style: italic; }

/* Passport. Sized to a real 35×45mm passport's proportion, scaled to fit. */
.rc-passport {
  flex: 0 0 auto;
  width: 21mm;
  height: 27mm;
  border: 1px solid var(--rc-rule);
  border-radius: 4px;
  background: #fff;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--rc-faint);
  font-size: var(--rc-label-fs);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.rc-passport img { width: 100%; height: 100%; object-fit: cover; }
.rc-passport-sm { width: 17mm; height: 22mm; }

/* Marks table. */
.rc-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--rc-tbl-fs);
  table-layout: fixed;
}
.rc-table th,
.rc-table td {
  padding: var(--rc-row-pad) 3px;
  text-align: center;
  vertical-align: middle;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rc-table th {
  font-weight: 800;
  background: var(--rc-wash);
  text-transform: none;
}
/* Only the subject name and the remark are prose; everything else is a
   figure, and a figure that wraps has broken the row height budget. */
.rc-table .rc-col-subject { width: 22%; text-align: left; font-weight: 700; }
.rc-table .rc-col-remark  { width: 15%; text-align: left; color: var(--rc-muted); }
.rc-table thead .rc-col-subject,
.rc-table thead .rc-col-remark { color: var(--rc-ink); }

.rc-ruled th, .rc-ruled td { border: 1px solid var(--rc-rule); }
.rc-striped tbody tr:nth-child(even) { background: var(--rc-wash); }
.rc-striped th { border-bottom: 1.5px solid var(--rc-brand); background: none; }
.rc-striped td { border-bottom: 1px solid var(--rc-hair); }

.rc-total-row td {
  font-weight: 800;
  background: var(--rc-wash);
}

/* Grading key. */
.rc-key {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 10px;
  padding: 4px 7px;
  border: 1px solid var(--rc-hair);
  border-radius: 4px;
  font-size: calc(var(--rc-fs) - 0.6px);
  color: var(--rc-muted);
}
.rc-key-label {
  font-size: var(--rc-label-fs);
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--rc-faint);
}
.rc-key-band strong { color: var(--rc-ink); font-weight: 800; }

/* Behavioural domains. */
.rc-domain { min-width: 0; }
.rc-domain-title {
  font-size: var(--rc-label-fs);
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--rc-faint);
  padding-bottom: 2px;
  margin-bottom: 3px;
  border-bottom: 1px solid var(--rc-hair);
}
.rc-domain-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 0.5px 0;
}
.rc-domain-score { font-weight: 800; font-variant-numeric: tabular-nums; }

.rc-two-col   { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.rc-three-col { display: grid; grid-template-columns: 1fr 1fr 1.15fr; gap: 12px; }

/* Remarks. */
.rc-remarks { display: flex; flex-direction: column; gap: 5px; }
.rc-remark p { margin: 1px 0 0; font-weight: 600; color: #374151; }
.rc-remark-inline { display: flex; gap: 6px; align-items: baseline; }
.rc-remark-inline .rc-domain-title { border: none; margin: 0; padding: 0; flex: 0 0 auto; }
.rc-resume { font-weight: 800; color: #374151; }

/* Financial summary. */
.rc-fin {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  gap: 14px;
  padding: 6px 8px;
  border: 1px solid var(--rc-hair);
  border-radius: 4px;
}
.rc-fin-col { min-width: 0; }
.rc-fin-row { display: flex; justify-content: space-between; gap: 8px; font-weight: 600; }
.rc-fin-total { font-weight: 800; border-top: 1px solid var(--rc-hair); margin-top: 2px; padding-top: 2px; }
.rc-owing .rc-num { color: #b91c1c; }
.rc-clear .rc-num { color: #047857; }
.rc-tag {
  margin-left: 4px;
  padding: 0 3px;
  border: 1px solid #fde68a;
  border-radius: 3px;
  background: #fffbeb;
  color: #92400e;
  font-size: calc(var(--rc-label-fs) - 0.4px);
  font-weight: 800;
  text-transform: uppercase;
}

/* Signatures. `margin-top: auto` on the foot is what pins signing to the
   bottom of the sheet: a six-subject card and a twenty-subject card are
   signed in the same place, and the slack sits above the signatures instead
   of trailing under them. */
.rc-foot {
  margin-top: auto;
  padding-top: 6px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px;
  text-align: center;
}
.rc-sig-img { display: block; height: 11mm; margin: 0 auto 2px; object-fit: contain; }
.rc-sig-line {
  border-top: 1px solid var(--rc-rule);
  padding-top: 3px;
  font-size: var(--rc-label-fs);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rc-faint);
}

/* ── Classic ─────────────────────────────────────────────── */

.rc-classic .rc-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--rc-rule);
}
.rc-head-logo { flex: 0 0 auto; width: 15mm; height: 15mm; }
.rc-head-logo img { width: 100%; height: 100%; object-fit: contain; }
.rc-head-main { flex: 1 1 auto; min-width: 0; text-align: center; }
.rc-school-name {
  font-size: calc(var(--rc-fs) * 2.15);
  font-weight: 900;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  line-height: 1.1;
}
.rc-school-loc { font-size: calc(var(--rc-fs) - 0.8px); font-weight: 600; color: var(--rc-muted); }
.rc-doc-title {
  margin-top: 2px;
  font-size: calc(var(--rc-fs) + 0.4px);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #4b5563;
}

.rc-student {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 8px;
  border: 1px solid var(--rc-hair);
  border-left: 3px solid var(--rc-brand);
  border-radius: 5px;
  background: var(--rc-wash);
}
.rc-student-main { flex: 1 1 auto; min-width: 0; }
.rc-student-name {
  font-size: calc(var(--rc-fs) * 1.55);
  font-weight: 900;
  text-transform: uppercase;
  line-height: 1.15;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rc-student-sub { font-weight: 700; color: #4b5563; margin-top: 1px; }
.rc-cells { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; margin-top: 5px; }

/* ── Compact ─────────────────────────────────────────────── */

.rc-compact .rc-head-inline {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 5px;
  border-bottom: 2px solid var(--rc-brand);
}
.rc-compact .rc-head-left { text-align: left; }
.rc-compact .rc-school-name { font-size: calc(var(--rc-fs) * 1.9); }
.rc-head-term { flex: 0 0 auto; text-align: right; }
.rc-head-term-val {
  font-size: calc(var(--rc-fs) * 1.15);
  font-weight: 900;
  text-transform: uppercase;
}

.rc-particulars { display: flex; align-items: stretch; gap: 8px; }
.rc-particulars-grid {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 4px;
}
.rc-span-2 { grid-column: span 2; }

.rc-compact .rc-domain-row { padding: 0; }
.rc-compact .rc-remarks-tight { gap: 2px; }

/* ── Modern ──────────────────────────────────────────────── */

.rc-modern .rc-band {
  display: flex;
  align-items: center;
  gap: 10px;
  /* Bleeds to the sheet edge: the 10mm page padding is cancelled with a
     negative margin, then paid back as the band's own padding. */
  margin: -10mm -10mm 0;
  padding: 5mm 10mm;
  background: var(--rc-brand);
  color: #fff;
}
.rc-band-logo {
  flex: 0 0 auto;
  width: 14mm;
  height: 14mm;
  padding: 1.5mm;
  border-radius: 4px;
  background: #fff;
}
.rc-band-logo img { width: 100%; height: 100%; object-fit: contain; }
.rc-band-main { flex: 1 1 auto; min-width: 0; }
.rc-modern .rc-school-name { font-size: calc(var(--rc-fs) * 2); }
.rc-modern .rc-band .rc-school-loc { color: rgb(255 255 255 / 78%); }
.rc-band-term { flex: 0 0 auto; text-align: right; }
.rc-band-term-val {
  font-size: calc(var(--rc-fs) * 1.2);
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.rc-student-soft {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
  border: none;
  background: none;
}

.rc-tiles { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
.rc-tile { text-align: center; padding: 4px 3px; border-radius: 5px; background: var(--rc-wash); }
.rc-tile .rc-cell-value { font-size: calc(var(--rc-fs) * 1.45); }
.rc-tile-hero { background: var(--rc-brand); border-color: var(--rc-brand); color: #fff; }
.rc-tile-hero .rc-cell-label { color: rgb(255 255 255 / 75%); }

.rc-key-soft { border: none; background: var(--rc-wash); }
.rc-domain-soft .rc-domain-title { border-bottom-color: var(--rc-brand); }
.rc-remarks-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.rc-remark-card { padding: 5px 7px; border-radius: 5px; background: var(--rc-wash); }
.rc-resume-band {
  padding: 3px 7px;
  border-left: 3px solid var(--rc-brand);
  background: var(--rc-wash);
}
.rc-fin-soft { border: none; background: var(--rc-wash); }

/* ── Screen preview only ─────────────────────────────────── */

/* The sheet is a fixed 210mm wide because it is a sheet of paper. On a screen
   narrower than that — a laptop with the sidebar open, or a phone — it would
   otherwise push the dashboard sideways, so the preview containers scroll it
   instead. Print never sees this: #print-root holds the card by then. */
#report-card-print-area,
#bulk-print-area {
  overflow-x: auto;
}

/* Below tablet width, scrolling a sheet sideways to read it is worse than
   reading a smaller one, so it is scaled to fit. The scale is visual only —
   print ignores it and uses the millimetre sizes above. The negative margin
   reclaims the space the scaled-away height would otherwise still occupy. */
@media screen and (max-width: 860px) {
  .rc-sheet {
    transform: scale(0.62);
    transform-origin: top left;
    margin-bottom: -113mm;
  }
}

/* ── Detailed ────────────────────────────────────────────── */

/* The fullest layout. Its trick is the two-column body: attendance, the two
   behavioural domains and the rating key are all short lists, and each one
   given a full-width band of its own (as Classic does) eats the vertical space
   the marks table needs. Moved into a 34% sidebar they cost almost nothing,
   and the main column gets the width for four extra class-context columns. */

.rc-detailed .rc-head-detailed {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 6px;
  border-bottom: 2px solid var(--rc-brand);
}
.rc-detailed .rc-head-main { text-align: center; }
.rc-brand-ink { color: var(--rc-brand); }
.rc-detailed .rc-school-name { font-size: calc(var(--rc-fs) * 2.3); }
.rc-detailed .rc-doc-title {
  margin-top: 4px;
  font-size: calc(var(--rc-fs) * 1.15);
  letter-spacing: 0;
  text-transform: none;
  color: var(--rc-ink);
}

/* Particulars. Label bold, value inline — no boxes, because eight boxed cells
   read as a form to fill in rather than a record of a child. */
.rc-bio {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--rc-hair);
}
/* The passport sits in its own flex column, so a long parent name wraps inside
   its own column instead of running under the photo and being clipped. */
.rc-bio-col { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 2.5px; }
.rc-particular { display: flex; gap: 4px; min-width: 0; }
.rc-particular-label { font-weight: 800; flex: 0 0 auto; }
.rc-particular-value {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rc-split { display: flex; gap: 10px; align-items: flex-start; }
.rc-split-main {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--rc-gap);
}
.rc-split-side {
  flex: 0 0 30%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--rc-gap);
}

.rc-side-title {
  font-size: calc(var(--rc-fs) * 1.1);
  font-weight: 800;
  color: var(--rc-brand);
  padding-bottom: 2px;
  margin-bottom: 3px;
  border-bottom: 1px solid var(--rc-hair);
}

/* Filled heading bands. This is the one layout that uses them: with eleven
   columns the eye needs the header row to be unmistakably a header. */
.rc-headed thead th,
.rc-side-table thead th {
  background: var(--rc-brand);
  color: #fff;
  border-color: var(--rc-brand);
  font-weight: 800;
}
.rc-headed th, .rc-headed td { border: 1px solid var(--rc-hair); }
.rc-headed tbody tr:nth-child(even) { background: var(--rc-wash); }

.rc-strong { font-weight: 800; }

/* Eleven columns across ~125mm. The two prose columns get the width they need
   to hold a real subject name and a real remark; the nine figure columns are
   two or three characters and take what is left. Nothing wraps — a wrapped row
   is a taller row, and twenty of those is a second sheet. */
.rc-detailed .rc-table { table-layout: fixed; }
.rc-detailed .rc-table .rc-col-subject { width: 25%; }
.rc-detailed .rc-table .rc-col-remark { width: 13%; text-align: center; }
.rc-detailed .rc-table th,
.rc-detailed .rc-table td { padding-left: 2px; padding-right: 2px; }
/* In the nine figure columns the heading is the longest string in the column —
   "Grade" is wider than any grade — so the header row sets a size smaller than
   the marks it labels, rather than the marks shrinking to fit their labels. */
.rc-detailed .rc-table th { font-size: calc(var(--rc-tbl-fs) - 1px); }

/* The metric labels are the long side of the pair; the values are a number or
   a word. */
.rc-metrics .rc-metric-label { text-align: left; font-weight: 700; width: 30%; }
.rc-metrics td { white-space: nowrap; }

.rc-side-table {
  width: 100%;
  border-collapse: collapse;
  font-size: calc(var(--rc-fs) - 0.4px);
  table-layout: fixed;
}
.rc-side-table th,
.rc-side-table td {
  padding: var(--rc-row-pad) 4px;
  border: 1px solid var(--rc-hair);
  overflow: hidden;
  text-overflow: ellipsis;
}
.rc-side-table td:first-child { text-align: left; }
.rc-side-table tbody tr:nth-child(even) { background: var(--rc-wash); }
/* The rating key's descriptions are sentences, so they wrap and read left. */
.rc-side-prose .rc-side-table td:last-child {
  text-align: left;
  white-space: normal;
}
.rc-side-prose .rc-side-table { table-layout: auto; }
.rc-side-prose .rc-side-table td:first-child { width: 15%; text-align: center; }
.rc-side-empty {
  padding: 3px 4px;
  border: 1px solid var(--rc-hair);
  color: var(--rc-faint);
  font-style: italic;
}

.rc-scale-text { margin: 0; color: var(--rc-muted); line-height: 1.45; }

.rc-remarks-split { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.rc-remarks-split .rc-remark p { margin-top: 2px; }
/* The one line on the card a parent has to act on. */
.rc-resume-loud {
  margin-top: 3px;
  color: #b91c1c;
  font-weight: 800;
}

/* ── Official ────────────────────────────────────────────── */

/* The ruled form. Where the other four are documents with white space between
   their sections, this one is a grid boxed from edge to edge: every figure
   sits in a cell, the marks table is padded with blank ruled rows so a short
   card is the same document as a full one, and the footer carries what an
   office files rather than what a parent skims. */

.rc-official { position: relative; padding: 5mm; }

/* The school's own logo behind the form, faint enough to read through and
   loud enough to spoil a clean photocopy. */
.rc-of-watermark {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 0;
}
.rc-of-watermark img {
  width: 62%;
  opacity: 0.06;
  transform: rotate(-16deg);
}

.rc-of-frame {
  position: relative;
  z-index: 1;
  border: 2.5px solid var(--rc-brand);
  padding: 2px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
}

/* Masthead. */
.rc-of-mast {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 6px;
  border: 1px solid var(--rc-rule);
}
.rc-of-mast-logo { flex: 0 0 auto; width: 16mm; height: 16mm; }
.rc-of-mast-logo img { width: 100%; height: 100%; object-fit: contain; }
.rc-of-mast-main { flex: 1 1 auto; min-width: 0; text-align: center; }
.rc-of-mast-name {
  font-size: calc(var(--rc-fs) * 2.4);
  font-weight: 900;
  line-height: 1.05;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--rc-brand);
}
.rc-of-motto {
  font-size: calc(var(--rc-fs) * 1.2);
  font-weight: 800;
  color: var(--rc-ink);
}
.rc-of-mast-line { font-size: calc(var(--rc-fs) - 0.3px); font-weight: 600; }

/* The one filled bar on the sheet: what document this is. */
.rc-of-title {
  padding: 2px 6px;
  background: var(--rc-brand);
  color: #fff;
  font-size: calc(var(--rc-fs) * 1.3);
  font-weight: 900;
  text-align: center;
  letter-spacing: 0.01em;
  text-transform: uppercase;
}

/* Particulars: three across, three down, every cell ruled. */
.rc-of-bio {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1.4fr;
  border: 1px solid var(--rc-rule);
  border-width: 1px 0 0 1px;
}
.rc-of-bio-cell {
  display: flex;
  align-items: baseline;
  gap: 4px;
  min-width: 0;
  padding: 1px 4px;
  border: 1px solid var(--rc-rule);
  border-width: 0 1px 1px 0;
}
.rc-of-bio-label {
  flex: 0 0 auto;
  font-weight: 700;
  color: var(--rc-muted);
}
.rc-of-bio-value {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 800;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Marks table: every cell ruled, two header rows, blank rows kept blank. */
.rc-of-marks { table-layout: fixed; }
.rc-of-marks th,
.rc-of-marks td {
  border: 1px solid var(--rc-rule);
  padding: var(--rc-row-pad) 2px;
}
.rc-of-head th {
  background: var(--rc-wash);
  font-size: calc(var(--rc-tbl-fs) - 1.2px);
  font-weight: 800;
  white-space: normal;   /* the only headings allowed two lines */
  line-height: 1.1;
}
.rc-of-maxrow th {
  background: var(--rc-wash);
  font-size: calc(var(--rc-tbl-fs) - 0.8px);
  font-weight: 800;
}
.rc-of-marks .rc-of-subject { width: 19%; text-align: left; font-weight: 700; }
.rc-of-marks .rc-of-remark { width: 13%; text-align: left; }
/* A padded row is the form's own blank line: ruled, and empty. */
.rc-of-blank td { height: 1em; }

/* The three-column band under the marks. */
.rc-of-mid {
  display: grid;
  grid-template-columns: 1fr 1.15fr 1fr;
  gap: 4px;
  align-items: start;
}
.rc-of-mid-col { min-width: 0; }
.rc-of-cap {
  padding: 1px 4px;
  background: var(--rc-wash);
  border: 1px solid var(--rc-rule);
  font-weight: 800;
  text-align: center;
}
.rc-of-kv {
  display: flex;
  justify-content: space-between;
  gap: 6px;
  padding: 1px 4px;
  border: 1px solid var(--rc-rule);
  border-top: none;
  min-width: 0;
}
.rc-of-mid-col > .rc-of-kv:first-child { border-top: 1px solid var(--rc-rule); }
.rc-of-kv > span:first-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rc-of-kv-val { flex: 0 0 auto; font-weight: 800; font-variant-numeric: tabular-nums; }
.rc-of-kv-strong { font-weight: 800; }
.rc-of-band {
  display: grid;
  grid-template-columns: 1.1fr 0.5fr 1.3fr;
  gap: 4px;
  padding: 0 4px;
  border: 1px solid var(--rc-rule);
  border-top: none;
  font-variant-numeric: tabular-nums;
}

/* Behavioural domains, graded in letters. */
.rc-of-domains { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.rc-of-domain { min-width: 0; }
.rc-of-domain-head,
.rc-of-domain-row {
  display: flex;
  justify-content: space-between;
  gap: 6px;
  padding: 1px 4px;
  border: 1px solid var(--rc-rule);
  border-top: none;
  min-width: 0;
}
.rc-of-domain-head {
  border-top: 1px solid var(--rc-rule);
  background: var(--rc-wash);
  font-weight: 800;
}
.rc-of-domain-row > span:first-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rc-of-rating-col { flex: 0 0 22%; text-align: center; }

.rc-of-domainkey {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 2px 10px;
  padding: 1px 4px;
  border: 1px solid var(--rc-rule);
  font-weight: 800;
}
.rc-of-domainkey .rc-of-cap { border: none; background: none; padding: 0; }

/* Comments, and somewhere to stamp. */
.rc-of-comments { display: flex; gap: 4px; align-items: stretch; }
.rc-of-comment-rows { flex: 1 1 auto; min-width: 0; }
.rc-of-comment {
  display: flex;
  gap: 6px;
  padding: 2px 4px;
  border: 1px solid var(--rc-rule);
  border-bottom: none;
  min-height: 2.4em;
}
.rc-of-comment-rows > .rc-of-comment:last-child {
  border-bottom: 1px solid var(--rc-rule);
}
.rc-of-comment-label {
  flex: 0 0 24%;
  font-weight: 800;
  color: var(--rc-muted);
}
.rc-of-comment-body { flex: 1 1 auto; min-width: 0; font-style: italic; font-weight: 600; }
.rc-of-stamp {
  flex: 0 0 26%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--rc-rule);
  text-align: center;
}
.rc-of-stamp img { max-height: 9mm; max-width: 100%; object-fit: contain; }
.rc-of-stamp-label { font-size: var(--rc-label-fs); font-weight: 800; color: var(--rc-muted); }

.rc-of-dates > .rc-of-kv:first-child { border-top: 1px solid var(--rc-rule); }
.rc-of-fin { margin-top: 2px; }

/* The footer that makes it an issued document rather than a printout. */
.rc-of-notice {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  padding-top: 2px;
}
.rc-of-notice > span { font-size: calc(var(--rc-fs) - 1px); white-space: nowrap; }
.rc-of-warn { font-style: italic; font-weight: 700; color: var(--rc-muted); }
.rc-of-printed { font-weight: 800; font-variant-numeric: tabular-nums; }
