/*
 * app.css - sits on top of the SB Admin 2 theme.
 *
 * Only additions: the mobile drawer (the theme had none - jQuery was doing
 * that work), form validation states, print rules for report cards, and a few
 * small screens' worth of table handling.
 */

/* ------------------------------------------------------------ sidebar --- */

.sidebar-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 1035;
  display: none;
}
.sidebar-backdrop.is-visible {
  display: block;
}
body.drawer-open {
  overflow: hidden;
  height: 100%;
}

/* The theme collapses the sidebar to zero width below 768px. On a phone it
   should slide in over the content as a smooth scrollable off-canvas drawer. */
@media (max-width: 768px) {
  .sidebar {
    position: fixed !important;
    top: 0 !important;
    bottom: 0 !important;
    left: 0 !important;
    height: 100% !important;
    height: 100vh !important;
    height: 100dvh !important;
    max-height: 100% !important;
    max-height: 100vh !important;
    max-height: 100dvh !important;
    z-index: 1040 !important;
    width: 15.5rem !important;
    max-width: 85vw !important;
    overflow-y: auto !important;
    overflow-x: hidden !important;
    -webkit-overflow-scrolling: touch !important;
    overscroll-behavior-y: contain !important;
    touch-action: pan-y !important;
    padding-bottom: calc(5rem + env(safe-area-inset-bottom, 24px)) !important;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
  }
  .sidebar.is-open,
  .sidebar.is-open.toggled {
    transform: translateX(0) !important;
    width: 15.5rem !important;
    overflow-y: auto !important;
    overflow-x: hidden !important;
  }
  .sidebar.is-open .nav-item .nav-link span,
  .sidebar.is-open .sidebar-brand-text {
    display: inline !important;
    font-size: 0.85rem;
  }
  .sidebar.is-open .nav-item .collapse,
  .sidebar.is-open .nav-item .collapsing {
    position: static !important;
    max-height: none !important;
    overflow: visible !important;
    margin: 0 0.75rem;
    box-shadow: none;
    animation: none;
  }
  .sidebar.is-open .nav-item .nav-link {
    text-align: left;
    padding: 0.7rem 1rem;
    width: 100%;
  }
  .sidebar.is-open .nav-item .nav-link i {
    margin-right: 0.35rem;
    font-size: 0.9rem;
  }
}

/* The theme's .collapse is display:none by default; .show reveals it. */
.collapse:not(.show) {
  display: none;
}
.collapse.show {
  display: block;
}

.sidebar .nav-item .collapse-inner .collapse-item.active {
  font-weight: 700;
  color: var(--sfs-brand, #4e73df) !important;
}
.sidebar .nav-item.active > .nav-link {
  font-weight: 700;
  color: #fff !important;
}

/* ------------------------------------------------------------- modals --- */

.modal {
  position: fixed;
  inset: 0;
  z-index: 1050;
  display: none;
  overflow-x: hidden;
  overflow-y: auto;
}
.modal.show {
  display: block;
}
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1040;
  background-color: #000;
  opacity: 0.5;
}
body.modal-open {
  overflow: hidden;
}
.dropdown-menu.show {
  display: block;
}

/* ------------------------------------------------------------- forms ---- */

.form-control.is-invalid,
.is-invalid .form-control {
  border-color: #e74a3b;
}
.form-control.is-invalid:focus {
  box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25);
}
.field-error {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.8rem;
  color: #e74a3b;
}
.form-control:focus,
.btn:focus {
  outline: 2px solid transparent;
  outline-offset: 2px;
}
/* Keyboard users need a visible ring even where the theme removed it. */
.btn:focus-visible,
.form-control:focus-visible,
.nav-link:focus-visible,
.page-link:focus-visible {
  outline: 2px solid var(--sfs-brand, #4e73df);
  outline-offset: 2px;
}
.btn:disabled,
.btn.disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
.required-mark {
  color: #e74a3b;
}

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

.table-responsive-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.table td,
.table th {
  vertical-align: middle;
}
.table-actions {
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
}
.table-actions form {
  margin: 0;
  display: inline-flex;
}
.table-actions .btn {
  margin: 0;
  font-weight: 700 !important;
}

/* Below tablet a wide action column pushes the table off screen; stacking
   the buttons keeps every row reachable without a horizontal scroll. */
@media (max-width: 576px) {
  .card-body {
    padding: 0.85rem;
  }
  h1.h3 {
    font-size: 1.25rem;
  }
}

/* ------------------------------------------------------- empty states --- */

.empty-state {
  padding: 2.5rem 1rem;
  text-align: center;
  color: #858796;
}
.empty-state i {
  font-size: 2.5rem;
  opacity: 0.35;
  margin-bottom: 0.75rem;
  display: block;
}
.empty-state h5 {
  color: #5a5c69;
}

/* ------------------------------------------------------------ alerts ---- */

.alert.fade-out {
  opacity: 0;
  transition: opacity 0.4s ease;
}

/* ------------------------------------------------------- student card -- *
 * A school ID card, not a list row.
 *
 * Printed in a grid rather than one per line because a school prints these
 * sixty at a time: a list wastes three-quarters of every page, and the
 * paper comes out of the same budget as the exercise books. Three across
 * on A4 with a dashed edge to cut along.
 * ----------------------------------------------------------------------- */

.id-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: .75rem;
}

.id-card {
  position: relative;          /* the stamp sits behind everything below */
  /* And the layering stops at the edge of the card.
     Without this the `z-index: 1` below is not "above the stamp", it is
     "above the stamp, and above anything else on the page sitting at zero" -
     so a page of sixty cards put sixty things into the page's own stacking
     order and the topbar menu opened behind them. Isolating the card means
     the numbers inside it only ever argue with each other. */
  isolation: isolate;
  border: 1px dashed #b7b9cc;
  border-radius: .5rem;
  overflow: hidden;
  background: #fff;
  font-size: .78rem;
  display: flex;
  flex-direction: column;
  break-inside: avoid;
  page-break-inside: avoid;
}

/* The school's stamp, faded behind the card. Faint enough to read straight
   through - a watermark that competes with the text it sits under has
   defeated itself - and large enough to be obviously part of the card
   rather than a smudge. */
.id-card-stamp {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 78%; height: 78%;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  opacity: .07;
  pointer-events: none;
  z-index: 0;
}

/* Everything else rides above it. */
.id-card-top,
.id-card-body,
.id-card-foot,
.id-card-parent,
.id-card-private { position: relative; z-index: 1; }

/* The school's own band across the top, which is what makes a piece of
   paper read as belonging to somewhere. */
.id-card-top {
  display: flex; align-items: center; gap: .45rem;
  padding: .4rem .55rem;
  background: var(--sfs-brand, #1e2600); color: #fff;
}
.id-card-crest {
  width: 22px; height: 22px; border-radius: 50%;
  object-fit: cover; background: #fff; flex: 0 0 auto;
}
.id-card-school {
  font-size: .68rem; font-weight: 700; line-height: 1.15;
  text-transform: uppercase; letter-spacing: .02em;
}

.id-card-body { display: flex; gap: .55rem; padding: .55rem; }

.id-card-photo {
  width: 58px; height: 68px;
  object-fit: cover;
  border: 1px solid #d1d3e2; border-radius: .25rem;
  background: #f5f5f7; flex: 0 0 auto;
}

.id-card-who { flex: 1 1 auto; min-width: 0; }
.id-card-name {
  font-weight: 700; color: #1d1d1f; line-height: 1.2;
  font-size: .85rem;
  word-break: break-word;
}
/* `.id-card-class` was removed with the class itself: a card showing a
   class is wrong the day the student moves up. */

.id-card-line {
  display: flex; gap: .3rem; align-items: baseline;
  margin-top: .1rem;
}
.id-card-label {
  color: #6e707e; font-size: .78rem; font-weight: 600;
  flex: 0 0 auto;
}

/* No extra letter-spacing. Courier already sets characters well apart, and
   adding more made "VBN58" read as "VBN5 8" - which matters for a number
   somebody reads down a phone line or copies onto a form. */
.id-card .pin {
  font-family: "Courier New", monospace;
  font-weight: 700;
  font-size: .86rem;
  color: #1d1d1f;
}

.id-card-foot {
  border-top: 1px dashed #d1d3e2;
  padding: .45rem .55rem;
  color: #6e707e; font-size: .68rem; line-height: 1.4;
  margin-top: auto;
}
.id-card-foot strong { color: #1d1d1f; }

/* The one line that belongs to the whole card rather than to either half,
   so it sits at the foot of both. */
.id-card-private {
  border-top: 1px dashed #d1d3e2;
  padding: .4rem .55rem;
  font-size: .64rem; color: #858796; line-height: 1.35;
  background: #fbfbfd;
}
.id-card-parent {
  border-top: 1px dashed #d1d3e2;
  padding: .45rem .55rem;
  font-size: .68rem; line-height: 1.4; color: #6e707e;
}

@media print {
  /* Three across on A4, and never split down the middle of a card. The
     surrounding panel loses its padding too: a school printing sixty of
     these is paying for the paper out of the same budget as the exercise
     books, and an inch of margin on every page is a real cost. */
  .id-cards {
    grid-template-columns: repeat(3, 1fr);
    gap: .4rem;
  }
  .id-cards .card-body,
  .card-body:has(.id-cards) { padding: 0 !important; }
  .id-card {
    font-size: .68rem;
    box-shadow: none;
  }
  .id-card-top,
  .id-card-stamp,
  .id-card-private {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .id-card-photo { width: 48px; height: 56px; }
}

/* -------------------------------------------------------- report card --- */

.report-card {
  background: #fff;
  padding: 1.5rem;
}
.report-card .school-header {
  text-align: center;
  border-bottom: 2px solid var(--sfs-brand, #4e73df);
  padding-bottom: 0.75rem;
  margin-bottom: 1rem;
}
.report-card .school-header img {
  height: 70px;
  width: auto;
  margin-bottom: 0.5rem;
}
.report-card table {
  font-size: 0.8rem;
}
.report-student-meta {
  table-layout: fixed;
  width: 100%;
}
.report-student-meta th {
  width: 32%;
  padding-left: 0;
  color: #3a3b45;
  font-weight: 700;
  vertical-align: top;
}
.report-student-meta td {
  color: #2d3038;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.report-student-photo img {
  border: 1px solid #d8dce6;
  padding: .2rem;
  background: #fff;
}
.report-signature-row { row-gap: 1rem; }
.report-signature-slot {
  min-height: 7rem;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
}
.report-signature-image {
  display: block;
  max-width: 100%;
  width: auto;
  height: auto;
  max-height: 3.5rem;
  object-fit: contain;
  margin: 0 auto .25rem;
}
.report-signature-pending {
  min-height: 3.5rem;
  display: flex;
  align-items: center;
  color: #858796;
  font-size: .75rem;
}
.report-signature-line {
  width: 60%;
  height: 3.5rem;
  border-bottom: 1px solid #687080;
}
.report-signature-label {
  width: 60%;
  margin-right: auto;
  margin-left: auto;
  margin-top: .35rem;
  padding-top: .3rem;
  border-top: 1px solid #aeb4c0;
  font-size: .78rem;
  font-weight: 700;
  text-align: center;
}

/* The person, under the role. A report card that says "Class teacher"
   over a blank line tells a parent nothing; the name is the point. */
.report-signature-name {
  width: 60%;
  margin-right: auto;
  margin-left: auto;
  font-size: .72rem;
  text-align: center;
  color: #4a5160;
}
.report-school-stamp {
  display: block;
  max-width: 100%;
  width: auto;
  height: auto;
  max-height: 4.5rem;
  object-fit: contain;
  margin: 0 auto .25rem;
}

/* -------------------------------- unified document signatures --- */
.sfs-signatures-block {
  margin-top: 2rem;
  width: 100%;
  page-break-inside: avoid;
  break-inside: avoid;
}

.sfs-signatures-row {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
  align-items: flex-start;
  gap: 1.5rem;
  width: 100%;
  margin: 0;
  padding: 0;
}

.sfs-signature-item {
  flex: 1 1 0;
  max-width: 220px;
  min-width: 130px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.sfs-signature-img-box {
  height: 60px;
  min-height: 60px;
  max-height: 60px;
  width: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  margin-bottom: 4px;
}

.sfs-signature-img {
  max-height: 56px;
  max-width: 160px;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
}

.sfs-signature-line {
  width: 100%;
  border-top: 1.5px solid #2e384d;
  margin: 0 0 6px 0;
}

.sfs-signature-name {
  font-size: 0.84rem;
  font-weight: 700;
  color: #1a1e2d;
  line-height: 1.25;
}

.sfs-signature-title {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: #64748b;
  margin-top: 2px;
  line-height: 1.2;
}

@media print {
  .sfs-signatures-block {
    margin-top: 1.5rem !important;
    page-break-inside: avoid !important;
    break-inside: avoid !important;
  }
  .sfs-signatures-row {
    display: flex !important;
    flex-direction: row !important;
    justify-content: space-around !important;
    align-items: flex-start !important;
    gap: 1.5rem !important;
    width: 100% !important;
  }
  .sfs-signature-item {
    flex: 1 1 0 !important;
    max-width: 220px !important;
    min-width: 120px !important;
  }
  .sfs-signature-img-box {
    height: 55px !important;
    min-height: 55px !important;
    max-height: 55px !important;
  }
  .sfs-signature-img {
    max-height: 52px !important;
  }
  .sfs-signature-line {
    border-top: 1.5px solid #000 !important;
  }
  .sfs-signature-name {
    color: #000 !important;
    font-weight: 700 !important;
  }
  .sfs-signature-title {
    color: #444 !important;
    font-size: 0.7rem !important;
  }
}


@media (max-width: 575.98px) {
  .report-card { padding: 1rem; }
  .report-student-summary { flex-direction: column-reverse; }
  .report-student-photo { margin-bottom: .75rem; }
  .report-student-meta th { width: 36%; }
}

/* --------------------------------------------------------- mark entry --- */

.mark-input {
  max-width: 70px;
}
td[data-total],
td[data-grade] {
  font-weight: 700;
}

/* ------------------------------------------------------------- print ---- */

@media print {
  .sidebar,
  .topbar,
  .sticky-footer,
  .scroll-to-top,
  .no-print,
  .sidebar-backdrop {
    display: none !important;
  }
  #content-wrapper,
  #wrapper,
  body {
    background: #fff !important;
    margin: 0 !important;
    padding: 0 !important;
  }
  .container-fluid {
    padding: 0 !important;
  }
  .card {
    border: none !important;
    box-shadow: none !important;
  }
  .report-card {
    padding: 0;
  }
  .report-student-summary {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 130px;
    align-items: start !important;
    column-gap: 1rem;
    margin-right: 0;
    margin-left: 0;
  }
  .report-student-summary > .col-md-8,
  .report-student-summary > .col-md-4 {
    width: auto;
    max-width: none;
    flex: none;
    padding-right: 0;
    padding-left: 0;
  }
  .report-student-photo {
    display: flex;
    justify-content: center;
    align-items: flex-start;
  }
  .report-student-photo img {
    display: block;
    flex: 0 0 110px;
    width: 110px !important;
    height: 130px !important;
    margin: 0 auto;
    object-fit: cover;
  }
  .report-student-meta {
    margin-top: 0;
  }
  .print-page-break {
    page-break-after: always;
  }
  a[href]::after {
    content: "";
  }
}

/* =========================================================== CBT ======= */
/*
 * The exam-hall layout: a candidate bar across the top, one question in the
 * middle, and a numbered palette down the side - the arrangement every
 * Nigerian candidate already knows from JAMB.
 */

.cbt-body {
  background: #eef1f6;
  min-height: 100vh;
}

/* ------------------------------------------------------- candidate bar - */

.cbt-header {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.6rem 1rem;
  background: linear-gradient(180deg, #14532d 0%, #166534 100%);
  color: #fff;
  position: sticky;
  top: 0;
  z-index: 1030;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}

.cbt-header__candidate {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-right: auto;
}
.cbt-header__photo {
  width: 46px;
  height: 46px;
  border-radius: 4px;
  object-fit: cover;
  background: #fff;
  border: 2px solid rgba(255, 255, 255, 0.5);
}
.cbt-header__name {
  font-weight: 700;
  line-height: 1.2;
}
.cbt-header__meta {
  font-size: 0.78rem;
  opacity: 0.85;
}
.cbt-header__label {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.8;
}
.cbt-header__value {
  font-weight: 700;
}

.cbt-clock {
  font-variant-numeric: tabular-nums;
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.1;
  padding: 0.1rem 0.7rem;
  border-radius: 0.3rem;
  background: rgba(255, 255, 255, 0.15);
}
.cbt-clock.is-low {
  background: #f59e0b;
  color: #1f2937;
}
.cbt-clock.is-critical {
  background: #dc2626;
  color: #fff;
  animation: cbt-pulse 1s ease-in-out infinite;
}
@keyframes cbt-pulse { 50% { opacity: 0.5; } }
@media (prefers-reduced-motion: reduce) {
  .cbt-clock.is-critical { animation: none; }
}

/* ------------------------------------------------------------ layout -- */

.cbt-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 260px;
  gap: 1rem;
  padding: 1rem;
  max-width: 1200px;
  margin: 0 auto;
  align-items: start;
}

.cbt-paper {
  background: #fff;
  border-radius: 0.4rem;
  box-shadow: 0 1px 3px rgba(16, 24, 40, 0.12);
  padding: 1.5rem;
  min-height: 60vh;
  display: flex;
  flex-direction: column;
}

/* ---------------------------------------------------------- question -- */

.cbt-question__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  border-bottom: 2px solid #166534;
  padding-bottom: 0.5rem;
  margin-bottom: 1rem;
}
.cbt-question__number {
  font-size: 1.05rem;
  font-weight: 800;
  margin: 0;
  color: #14532d;
}
.cbt-question__number small {
  font-weight: 400;
  color: #6b7280;
  font-size: 0.8rem;
}
.cbt-question__marks {
  font-size: 0.75rem;
  color: #6b7280;
}
.cbt-question__text {
  font-size: 1.05rem;
  line-height: 1.6;
  margin-bottom: 1.1rem;
}
.cbt-question__image {
  max-width: 100%;
  max-height: 260px;
  border-radius: 0.35rem;
  margin-bottom: 1rem;
}

.cbt-options {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
}
.cbt-options li { margin-bottom: 0.6rem; }

.cbt-option {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  border: 1.5px solid #d1d5db;
  border-radius: 0.35rem;
  padding: 0.75rem 0.9rem;
  margin: 0;
  cursor: pointer;
  background: #fff;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.cbt-option:hover { background: #f6f8fa; border-color: #9ca3af; }
.cbt-option.is-chosen {
  border-color: #166534;
  background: #ecfdf3;
}
.cbt-option input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.cbt-option__key {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: #e5e7eb;
  color: #374151;
  font-weight: 800;
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cbt-option.is-chosen .cbt-option__key {
  background: #166534;
  color: #fff;
}
.cbt-option__text { padding-top: 0.15rem; }
.cbt-option:focus-within {
  outline: 3px solid #166534;
  outline-offset: 2px;
}

.cbt-clear {
  background: none;
  border: none;
  color: #6b7280;
  font-size: 0.8rem;
  padding: 0;
  cursor: pointer;
}
.cbt-clear:hover { color: #dc2626; text-decoration: underline; }

/* -------------------------------------------------------- navigation -- */

.cbt-nav {
  margin-top: auto;
  padding-top: 1.25rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  border-top: 1px solid #e5e7eb;
}
.cbt-nav__position {
  font-weight: 700;
  color: #6b7280;
  font-size: 0.9rem;
}

.cbt-btn {
  border: none;
  border-radius: 0.3rem;
  padding: 0.6rem 1.25rem;
  font-weight: 700;
  cursor: pointer;
}
.cbt-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.cbt-btn--primary { background: #166534; color: #fff; }
.cbt-btn--primary:not(:disabled):hover { background: #14532d; }
.cbt-btn--ghost { background: #e5e7eb; color: #374151; }
.cbt-btn--ghost:not(:disabled):hover { background: #d1d5db; }
.cbt-btn--submit {
  width: 100%;
  background: #b91c1c;
  color: #fff;
  padding: 0.8rem;
  font-size: 1rem;
}
.cbt-btn--submit:not(:disabled):hover { background: #991b1b; }
.cbt-btn:focus-visible { outline: 3px solid #14532d; outline-offset: 2px; }

/* ----------------------------------------------------------- palette -- */

.cbt-side { position: sticky; top: 86px; }

.cbt-side__block {
  background: #fff;
  border-radius: 0.4rem;
  box-shadow: 0 1px 3px rgba(16, 24, 40, 0.12);
  padding: 0.9rem;
  margin-bottom: 0.75rem;
}
.cbt-side__title {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #6b7280;
  font-weight: 800;
  margin-bottom: 0.6rem;
}

.cbt-palette {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
  gap: 0.35rem;
}
.cbt-pip {
  aspect-ratio: 1 / 1;
  min-width: 34px;
  border: 1.5px solid #d1d5db;
  border-radius: 0.25rem;
  background: #fff;
  color: #374151;
  font-weight: 700;
  font-size: 0.85rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.cbt-pip:hover { border-color: #166534; }
.cbt-pip.is-answered {
  background: #16a34a;
  border-color: #15803d;
  color: #fff;
}
.cbt-pip.is-current {
  outline: 3px solid #f59e0b;
  outline-offset: 1px;
}
.cbt-pip:focus-visible { outline: 3px solid #166534; outline-offset: 2px; }

.cbt-legend {
  list-style: none;
  padding: 0;
  margin: 0.8rem 0 0;
  font-size: 0.75rem;
  color: #6b7280;
}
.cbt-legend li {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-bottom: 0.3rem;
}
.cbt-legend .cbt-pip {
  width: 16px;
  min-width: 16px;
  height: 16px;
  pointer-events: none;
}

.cbt-tally {
  display: flex;
  text-align: center;
}
.cbt-tally > div { flex: 1; }
.cbt-tally strong {
  display: block;
  font-size: 1.5rem;
  line-height: 1.1;
  color: #14532d;
}
.cbt-tally span {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #6b7280;
}

.cbt-side__note {
  font-size: 0.72rem;
  color: #6b7280;
  margin: 0.6rem 0 0;
  text-align: center;
}

/* An answer that has not reached the server yet. */
.cbt-question.cbt-unsaved {
  border-left: 4px solid #dc2626;
  padding-left: 0.9rem;
}
.cbt-question.cbt-unsaved::after {
  content: "Not saved - check your connection";
  display: block;
  font-size: 0.78rem;
  color: #dc2626;
  margin-top: 0.5rem;
}

/* ------------------------------------------------------------ mobile -- */

@media (max-width: 900px) {
  .cbt-layout { grid-template-columns: minmax(0, 1fr); }
  .cbt-side { position: static; }
  /* Palette below the paper on a phone, so the question leads. */
  .cbt-side { order: 2; }
  .cbt-paper { order: 1; }
}

@media (max-width: 576px) {
  .cbt-header { gap: 0.6rem; padding: 0.5rem 0.75rem; }
  .cbt-header__photo { width: 38px; height: 38px; }
  .cbt-header__name { font-size: 0.85rem; }
  .cbt-header__subject { display: none; }
  .cbt-clock { font-size: 1.2rem; }
  .cbt-layout { padding: 0.6rem; }
  .cbt-paper { padding: 1rem; }
  .cbt-question__text { font-size: 1rem; }
  .cbt-btn { padding: 0.55rem 0.9rem; }
}

/* --------------------------------------------------- CBT invigilation -- */

/* Selecting question text is the first step to copying it out. */
.cbt-noselect .cbt-question__text,
.cbt-noselect .cbt-option__text {
  -webkit-user-select: none;
  user-select: none;
}

.cbt-warning {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1080;
  background: #b91c1c;
  color: #fff;
  text-align: center;
  padding: 0.6rem 1rem;
  font-weight: 700;
  font-size: 0.9rem;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

.cbt-side__block--warn {
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  position: relative;
  --card-corner-accent: #f59e0b;
  --card-top-hairline: rgba(245, 158, 11, 0.45);
  --card-left-edge: #94a3b8;
}
.cbt-side__block--warn::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 12px;
  right: 12px;
  height: 1px;
  background: var(--card-top-hairline);
  pointer-events: none;
  z-index: 2;
}
.cbt-side__block--warn::before {
  content: "";
  position: absolute;
  top: -1px;
  left: -1px;
  width: 48px;
  height: 32px;
  border-top: 1.5px solid var(--card-corner-accent);
  border-left: 1.5px solid var(--card-left-edge);
  border-top-left-radius: 12px;
  pointer-events: none;
  z-index: 3;
}
.cbt-rules {
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 0.78rem;
  color: #4b5563;
}
.cbt-rules li {
  padding: 0.2rem 0 0.2rem 1.1rem;
  position: relative;
}
.cbt-rules li::before {
  content: "\f06a";
  font-family: "Font Awesome 6 Free";
  font-weight: 900;
  position: absolute;
  left: 0;
  color: #f59e0b;
  font-size: 0.7rem;
}
.cbt-fs-btn {
  width: 100%;
  margin-top: 0.6rem;
  font-size: 0.8rem;
  padding: 0.45rem;
}

/* ---- staff invigilation report ---- */

.integrity-row {
  border: 1px solid #e2e8f0;
  padding: 0.75rem 1rem;
  margin-bottom: 0.6rem;
  background: #fff;
  border-radius: 12px;
  position: relative;
  --card-corner-accent: #94a3b8;
  --card-top-hairline: rgba(148, 163, 184, 0.4);
  --card-left-edge: #cbd5e1;
}
.integrity-row::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 12px;
  right: 12px;
  height: 1px;
  background: var(--card-top-hairline);
  pointer-events: none;
  z-index: 2;
}
.integrity-row::before {
  content: "";
  position: absolute;
  top: -1px;
  left: -1px;
  width: 48px;
  height: 32px;
  border-top: 1.5px solid var(--card-corner-accent);
  border-left: 1.5px solid var(--card-left-edge);
  border-top-left-radius: 12px;
  pointer-events: none;
  z-index: 3;
}
.integrity-row--high {
  --card-corner-accent: #ef4444;
  --card-top-hairline: rgba(239, 68, 68, 0.45);
  --card-left-edge: #94a3b8;
}
.integrity-row--medium {
  --card-corner-accent: #f59e0b;
  --card-top-hairline: rgba(245, 158, 11, 0.45);
  --card-left-edge: #94a3b8;
}
.integrity-row--info {
  --card-corner-accent: #0ea5e9;
  --card-top-hairline: rgba(14, 165, 233, 0.4);
  --card-left-edge: #94a3b8;
}

.integrity-meter {
  height: 8px;
  border-radius: 4px;
  background: #e5e7eb;
  overflow: hidden;
}
.integrity-meter span {
  display: block;
  height: 100%;
  background: #dc2626;
}

/* ------------------------------------------------- confirm dialog ------ */

.sfs-confirm .modal-dialog {
  max-width: 460px;
}
.sfs-confirm .modal-content {
  border: none;
  border-radius: 0.5rem;
  box-shadow: 0 16px 48px rgba(16, 24, 40, 0.28);
}
.sfs-confirm .modal-header {
  border-bottom: 1px solid #e3e6f0;
  padding: 1rem 1.25rem;
}
.sfs-confirm .modal-title {
  font-weight: 700;
  font-size: 1.05rem;
}
.sfs-confirm .modal-body {
  padding: 1.25rem;
  color: #4b5563;
  line-height: 1.55;
}
.sfs-confirm .modal-footer {
  border-top: 1px solid #e3e6f0;
  padding: 0.85rem 1.25rem;
}
.sfs-confirm .btn {
  min-width: 96px;
  font-weight: 600;
}

/* Centred on desktop, but a phone should not have to reach the middle of a
   tall screen - sit it near the top where the thumb already is. */
@media (max-width: 576px) {
  .sfs-confirm .modal-dialog-centered {
    align-items: flex-start;
    margin-top: 2rem;
  }
  .sfs-confirm .modal-footer {
    flex-direction: column-reverse;
  }
  .sfs-confirm .modal-footer .btn {
    width: 100%;
    margin: 0.25rem 0;
  }
}

/* ------------------------------------------------------------- choice cards
   A radio that is a decision, not a preference - test versus live money,
   bank transfer versus card. Rendered as a block you click anywhere on, so
   the target is the whole option rather than a 16px circle, and the chosen
   one is obvious at a glance across the room. */
.sfs-choice {
  display: block;
  border: 1px solid #e3e6f0;
  border-radius: .35rem;
  padding: .75rem 1rem .75rem 2.75rem;
  margin-bottom: .5rem;
  cursor: pointer;
  transition: border-color .12s ease, background-color .12s ease;
}
.sfs-choice:hover { background-color: #f8f9fc; }
.sfs-choice .custom-control-label { cursor: pointer; }
.sfs-choice .custom-control-label::before,
.sfs-choice .custom-control-label::after { left: -1.75rem; top: .15rem; }

/* The selected option. `:has` where it is supported, and a JS-set class
   everywhere else, so the highlight never depends on one or the other. */
.sfs-choice.is-chosen { border-color: var(--sfs-brand, #4e73df); background-color: var(--sfs-brand-tint, #eaeffb); }
.sfs-choice.is-chosen.sfs-choice-danger {
  border-color: #e74a3b;
  background-color: #fdeceb;
}
.sfs-choice-danger.is-chosen .sfs-choice-title { color: #c0392b; }
.sfs-choice-title { font-weight: 700; }
.sfs-choice-note { display: block; font-size: .8rem; color: #858796; }

/* A save button that stays within reach on a long settings form, so nobody
   types a key into the third card and then has to go looking for it. */
.sfs-save-bar {
  position: sticky;
  bottom: 0;
  background: linear-gradient(to bottom, rgba(248,249,252,0), #f8f9fc 40%);
  padding: .75rem 0 .5rem;
  z-index: 5;
}

/* Checkbox variant of the choice card, for the payment methods. */
.sfs-choice.custom-checkbox .custom-control-label::before,
.sfs-choice.custom-checkbox .custom-control-label::after { left: -1.75rem; top: .15rem; }

/* ------------------------------------------------------------------ *
 * The to-do board.
 *
 * Built phone-first: a task is one tall, tappable card with its button
 * at the bottom where a thumb already is. From tablet up the same card
 * lays out in a row, because on a wide screen a column of tall cards
 * wastes the space that would otherwise show more of the list.
 * ------------------------------------------------------------------ */

.sfs-hello { font-size: 1.35rem; font-weight: 800; color: #3a3b45; margin-bottom: .15rem; }
.sfs-hello-note { color: #6e707e; font-size: .95rem; margin-bottom: 1.25rem; }

.sfs-todo { display: grid; gap: .55rem; margin-bottom: 1.5rem; }

/* Kept short on purpose. This is a list of jobs, and a list you have to
   scroll is one where the last job never gets done - the height has to earn
   itself, so the padding, the icon and the button are all sized to the text
   rather than to the box. */
.sfs-task {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  padding: .75rem .85rem;
  box-shadow: 0 .1rem .5rem rgba(58, 59, 69, .06);
  position: relative;
  --card-corner-accent: var(--sfs-brand, #4e73df);
  --card-top-hairline: rgba(78, 115, 223, 0.4);
  --card-left-edge: #94a3b8;
}
.sfs-task::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 12px;
  right: 12px;
  height: 1px;
  background: var(--card-top-hairline, rgba(78, 115, 223, 0.4));
  pointer-events: none;
  z-index: 2;
}
.sfs-task::before {
  content: "";
  position: absolute;
  top: -1px;
  left: -1px;
  width: 48px;
  height: 32px;
  border-top: 1.5px solid var(--card-corner-accent, var(--sfs-brand, #4e73df));
  border-left: 1.5px solid var(--card-left-edge, #94a3b8);
  border-top-left-radius: 12px;
  pointer-events: none;
  z-index: 3;
}
.sfs-task-overdue {
  --card-corner-accent: #ef4444;
  --card-top-hairline: rgba(239, 68, 68, 0.45);
  --card-left-edge: #94a3b8;
}
.sfs-task-now {
  --card-corner-accent: var(--sfs-brand, #4e73df);
  --card-top-hairline: rgba(78, 115, 223, 0.4);
  --card-left-edge: #94a3b8;
}
.sfs-task-soon {
  --card-corner-accent: #06b6d4;
  --card-top-hairline: rgba(6, 182, 212, 0.4);
  --card-left-edge: #94a3b8;
}

.sfs-task-head { display: flex; gap: .65rem; align-items: flex-start; }

.sfs-task-icon {
  flex: 0 0 auto;
  width: 2.1rem; height: 2.1rem;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--sfs-brand-tint, #eaeffb); color: var(--sfs-brand, #4e73df);
  font-size: .85rem;
}
.sfs-task-overdue .sfs-task-icon { background: #fdeceb; color: #e74a3b; }
.sfs-task-soon    .sfs-task-icon { background: #e7f6f8; color: #36b9cc; }

.sfs-task-title { font-weight: 700; color: #3a3b45; line-height: 1.25; margin: 0; }
.sfs-task-detail { color: #6e707e; font-size: .85rem; margin: .15rem 0 0; line-height: 1.4; }

.sfs-task-flag {
  display: inline-block;
  font-size: .65rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .03em;
  padding: .05rem .4rem; border-radius: 1rem;
  background: #fdeceb; color: #c0392b;
  margin-bottom: .2rem;
}

/* Full width on a phone: a button you have to aim for is a button that
   gets mis-tapped on a bus. Short, though - full width is about being easy
   to hit sideways, not about being tall. */
.sfs-task .btn {
  width: 100%; font-weight: 700;
  padding: .4rem 1rem; font-size: .85rem;
}

.sfs-all-clear {
  background: #fff;
  border: 1px solid #e3e6f0;
  border-radius: .6rem;
  padding: 2rem 1.25rem;
  text-align: center;
  margin-bottom: 1.5rem;
}
.sfs-all-clear i { font-size: 2.25rem; color: #1cc88a; margin-bottom: .6rem; }
.sfs-all-clear p { color: #6e707e; margin: 0; }

/* Quick links: the handful of places someone goes when they have no job
   waiting. Two across on a phone, four from tablet up. */
.sfs-jump {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .6rem;
}
.sfs-jump a {
  display: flex; flex-direction: column; align-items: center; gap: .4rem;
  background: #fff; border: 1px solid #e3e6f0; border-radius: .6rem;
  padding: .9rem .5rem; text-align: center;
  color: #3a3b45; font-size: .8rem; font-weight: 600;
  text-decoration: none;
}
.sfs-jump a:hover { border-color: var(--sfs-brand, #4e73df); color: var(--sfs-brand, #4e73df); text-decoration: none; }
.sfs-jump i { font-size: 1.15rem; color: var(--sfs-brand, #4e73df); }

@media (min-width: 576px) {
  .sfs-jump { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (min-width: 768px) {
  .sfs-hello { font-size: 1.6rem; }
  .sfs-task {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    padding: .7rem 1rem;
  }
  .sfs-task-head { flex: 1 1 auto; }
  .sfs-task .btn { width: auto; flex: 0 0 auto; white-space: nowrap; }
}

/* ------------------------------------------------------------------ *
 * "What do you want to do?" - the sidebar, said out loud.
 *
 * One column on a phone, two from tablet, three on a desktop. Each row
 * is a whole tappable line rather than a small link, because a menu you
 * have to aim at is the menu people give up on.
 * ------------------------------------------------------------------ */

.sfs-do { margin-bottom: 1.5rem; }

.sfs-do-head { margin-bottom: 1rem; }
.sfs-do-title { font-size: 1.05rem; font-weight: 800; color: #3a3b45; margin: 0 0 .6rem; }
/* The dates under a headline figure: present, and never competing
   with the number itself for attention. */
.sfs-do-note { font-size: .76rem; color: #858796; margin: -.35rem 0 .7rem; }

.sfs-search { position: relative; }
.sfs-search i {
  position: absolute; left: .85rem; top: 50%; transform: translateY(-50%);
  color: #b7b9cc; font-size: .85rem;
}
.sfs-search input {
  width: 100%;
  padding: .7rem .9rem .7rem 2.4rem;
  border: 1px solid #d1d3e2; border-radius: 2rem;
  font-size: .9rem; background: #fff; color: #3a3b45;
}
.sfs-search input:focus {
  outline: none; border-color: var(--sfs-brand, #4e73df);
  box-shadow: 0 0 0 .18rem rgba(78, 115, 223, .15);
}

.sfs-do-none { color: #858796; font-size: .9rem; margin: .75rem 0; }

/* The groups stack; each is a heading over a grid of cards. The cards
   themselves are `.sfs-flow` - the same component the job tiles use, so
   there is one card in this codebase rather than two that resemble each
   other and drift apart. */
.sfs-groups { display: block; }

.sfs-group { margin-bottom: 1.5rem; }
.sfs-group-title {
  font-size: .72rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .05em; color: #858796;
  margin: 0 0 .6rem; display: flex; align-items: center; gap: .45rem;
}
.sfs-group-title i { color: var(--sfs-brand, #4e73df); }

.sfs-group-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: .6rem; grid-template-columns: minmax(0, 1fr);
}
.sfs-group-list > li { min-width: 0; }
.sfs-group-list .sfs-flow { height: 100%; }

/* Still used by the student portal's tiles, which have no body text to
   hang a call to action from. */
.sfs-go { color: #d1d3e2; font-size: .7rem; flex: 0 0 auto; }

/* Where to go from here, at the foot of every page. */
.sfs-next {
  margin: 2rem 0 .5rem;
  padding-top: 1rem;
  border-top: 1px solid #e3e6f0;
}
.sfs-next-title {
  font-size: .72rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .05em; color: #858796; margin-bottom: .6rem;
}
.sfs-next-list { display: flex; flex-wrap: wrap; gap: .5rem; }
.sfs-next-list a {
  display: inline-flex; align-items: center; gap: .4rem;
  background: #fff; border: 1px solid #e3e6f0; border-radius: 2rem;
  padding: .45rem .9rem; font-size: .8rem; font-weight: 600;
  color: #3a3b45; text-decoration: none;
}
.sfs-next-list a:hover { border-color: var(--sfs-brand, #4e73df); color: var(--sfs-brand, #4e73df); text-decoration: none; }
.sfs-next-list i { color: var(--sfs-brand, #4e73df); font-size: .8rem; }

@media (min-width: 768px) {
  .sfs-group-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1200px) {
  .sfs-group-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ------------------------------------------------------------------ *
 * A child's card, on a parent's dashboard.
 *
 * Parents and students have almost no menu, so whatever is not on this
 * card is out of reach for them. The amount owing is the reason they
 * opened the page, so it is the biggest thing on it.
 * ------------------------------------------------------------------ */

.sfs-kids { display: grid; gap: .75rem; margin-bottom: 1.75rem; }

.sfs-kid {
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  padding: 1rem;
  box-shadow: 0 .1rem .5rem rgba(58, 59, 69, .06);
  position: relative;
  --card-corner-accent: #ef4444;
  --card-top-hairline: rgba(239, 68, 68, 0.45);
  --card-left-edge: #94a3b8;
}
.sfs-kid::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 12px;
  right: 12px;
  height: 1px;
  background: var(--card-top-hairline, rgba(239, 68, 68, 0.45));
  pointer-events: none;
  z-index: 2;
}
.sfs-kid::before {
  content: "";
  position: absolute;
  top: -1px;
  left: -1px;
  width: 48px;
  height: 32px;
  border-top: 1.5px solid var(--card-corner-accent, #ef4444);
  border-left: 1.5px solid var(--card-left-edge, #94a3b8);
  border-top-left-radius: 12px;
  pointer-events: none;
  z-index: 3;
}
.sfs-kid.is-clear {
  --card-corner-accent: #10b981;
  --card-top-hairline: rgba(16, 185, 129, 0.45);
  --card-left-edge: #94a3b8;
}

.sfs-kid-top {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: .75rem;
}
.sfs-kid-name { font-weight: 800; color: #3a3b45; line-height: 1.25; }
.sfs-kid-class { font-size: .95rem; font-weight: 600; color: #3a3b45; margin-top: .2rem; }

.sfs-kid-money { text-align: right; flex: 0 0 auto; }
.sfs-kid-owing {
  font-size: 1.3rem; font-weight: 800; color: #e74a3b; line-height: 1.1;
}
.sfs-kid-owing-note { font-size: .85rem; color: #6e707e; margin-top: .15rem; }
.sfs-kid-clear { color: #1cc88a; font-weight: 700; font-size: .85rem; }

.sfs-kid-bar {
  height: .35rem; background: #eaecf4; border-radius: 1rem;
  overflow: hidden; margin-top: .8rem;
}
.sfs-kid-bar span { display: block; height: 100%; background: #1cc88a; }
.sfs-kid-bar-note { font-size: .95rem; font-weight: 600; color: #3a3b45; margin-top: .5rem; }

.sfs-kid-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 0.55rem;
  margin-top: 0.85rem;
}
.sfs-kid-links a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  border: 1px solid #d1d3e2;
  border-radius: 6px;
  padding: 0.35rem 0.75rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: #3a3b45;
  text-decoration: none;
  background: #fff;
  transition: border-color .15s ease, background-color .15s ease,
              color .15s ease;
}
.sfs-kid-links a:hover { text-decoration: none; }
.sfs-kid-links i { font-size: .75rem; }

/* Coloured by what the button is about - money, marks, attendance. Four
   identical grey buttons make a parent read all four labels every time;
   four colours make the one they want findable at a glance, which is the
   whole difference on a phone in a hurry. */
/* The tint is the resting state, not the hover one. These are the card's
   content rather than links inside a sentence, so they should look like
   buttons before you touch them: a row of white outlines only becomes
   visible once the pointer is on it, which is no use at all on a phone,
   where there is no hover at all. Lifting to white then reads as the button
   coming forward under your finger.

   Written as `.sfs-kid-links a.…` for a reason. The base rule above is
   `.sfs-kid-links a`, which outranks a lone class - a bare `.sfs-tone-money`
   would lose to it and the tint would never appear, with nothing to show
   that a rule had been written at all. */
.sfs-kid-links a.sfs-tone-money {
  background: var(--sfs-brand-tint, #eef3fd); border-color: var(--sfs-brand, #4e73df); color: var(--sfs-brand-dark, #2e59d9);
}
.sfs-kid-links a.sfs-tone-money i { color: var(--sfs-brand, #4e73df); }
.sfs-kid-links a.sfs-tone-money:hover {
  background: #fff; border-color: var(--sfs-brand-dark, #2e59d9); color: var(--sfs-brand-dark, #2e59d9);
}

.sfs-kid-links a.sfs-tone-marks {
  background: #f3effc; border-color: #6f42c1; color: #59359a;
}
.sfs-kid-links a.sfs-tone-marks i { color: #6f42c1; }
.sfs-kid-links a.sfs-tone-marks:hover {
  background: #fff; border-color: #59359a; color: #59359a;
}

.sfs-kid-links a.sfs-tone-days {
  background: #ecf8fa; border-color: #36b9cc; color: #21808d;
}
.sfs-kid-links a.sfs-tone-days i { color: #36b9cc; }
.sfs-kid-links a.sfs-tone-days:hover {
  background: #fff; border-color: #21808d; color: #21808d;
}

.sfs-kid-links a.sfs-tone-plain {
  background: #f8f9fc; border-color: #b7b9cc; color: #3a3b45;
}
.sfs-kid-links a.sfs-tone-plain i { color: #858796; }
.sfs-kid-links a.sfs-tone-plain:hover {
  background: #fff; border-color: #858796; color: #1f2937;
}

/* The one to press - paying a bill, taking today's register. Filled rather
   than outlined so it is obvious at a glance, but the same size as the rest:
   the card should point somewhere, not shout. One name for both cards, since
   "the thing you came here to do" is the same idea on each. */
.sfs-kid-links a.sfs-kid-do {
  background: #1cc88a; border-color: #1cc88a;
  color: #fff; font-weight: 700;
}
.sfs-kid-links a.sfs-kid-do i { color: #fff; }
.sfs-kid-links a.sfs-kid-do:hover {
  background: #17a673; border-color: #17a673; color: #fff;
}

@media (min-width: 768px) {
  .sfs-kids { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ------------------------------------------------------------------ *
 * Jobs: pick one, then be walked through it.
 * ------------------------------------------------------------------ */

.sfs-flows { display: grid; gap: .6rem; margin-bottom: 1.75rem; }

.sfs-flow {
  display: flex; align-items: center; gap: .9rem;
  background: #fff; border: 1px solid #e3e6f0; border-radius: .6rem;
  padding: .9rem 1rem; text-decoration: none; color: #3a3b45;
  box-shadow: 0 .1rem .5rem rgba(58, 59, 69, .05);
}
.sfs-flow:hover { border-color: var(--sfs-brand, #4e73df); color: #1f2937; text-decoration: none; }
.sfs-flow-icon {
  flex: 0 0 auto; width: 2.75rem; height: 2.75rem;
  display: flex; align-items: center; justify-content: center;
  border-radius: .5rem; background: var(--sfs-brand-tint, #eaeffb); color: var(--sfs-brand, #4e73df); font-size: 1.05rem;
}
.sfs-flow-body { flex: 1 1 auto; min-width: 0; }
.sfs-flow-body strong { display: block; font-size: 1rem; color: #111827;
                        line-height: 1.3; }
.sfs-flow-body em {
  display: block; font-style: normal; font-size: .92rem;
  color: #1f2937; margin-top: .2rem; line-height: 1.45;
}

/* The words on the way out. The title of these cards is already an
   instruction, but a title is not an affordance: without this the card is
   a paragraph in a box, and on a phone there is no hover to discover the
   link with. The arrow nudges on hover so the whole tile feels pressable. */
.sfs-flow-go {
  display: inline-flex; align-items: center; gap: .35rem;
  margin-top: .55rem;
  font-size: .82rem; font-weight: 700; color: var(--sfs-brand, #4e73df);
}
.sfs-flow-go i { font-size: .72rem; transition: transform .15s ease; }
.sfs-flow:hover .sfs-flow-go i { transform: translateX(.2rem); }

.student-portal-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: .5rem;
  max-width: 100%;
}
.student-portal-actions form { margin: 0; }

@media (max-width: 767.98px) {
  .student-portal-actions {
    justify-content: flex-start;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(7rem, 100%), 1fr));
    width: 100%;
    margin-top: .75rem;
  }
  .student-portal-actions .btn { white-space: normal; }
}

/* A job with nothing left in it. Said quietly - it is not a warning, and
   the tile is still worth opening to look back over what was done. */
.sfs-flow.is-finished .sfs-flow-icon { background: #e9f9f2; color: #13855c; }
.sfs-flow.is-finished .sfs-flow-go { color: #13855c; }
.sfs-flow.is-finished .sfs-flow-go i { transform: none; }

/* how far through a job the school already is */
.sfs-progress { margin-bottom: 1.25rem; }
.sfs-progress-bar {
  height: .5rem; background: #eaecf4; border-radius: 1rem; overflow: hidden;
}
.sfs-progress-bar span {
  display: block; height: 100%; background: #1cc88a; border-radius: 1rem;
}
.sfs-progress-note { font-size: .8rem; color: #858796; margin-top: .4rem; }

/* the chain of steps */
.sfs-steps { list-style: none; margin: 0 0 1.5rem; padding: 0; }
.sfs-step {
  display: flex; gap: .9rem;
  padding: 0 0 1.25rem; position: relative;
}
/* the line joining one step to the next */
.sfs-step::before {
  content: ""; position: absolute; left: 1.05rem; top: 2.2rem; bottom: 0;
  width: 2px; background: #e3e6f0;
}
.sfs-step:last-child::before { display: none; }
.sfs-step.is-done::before { background: #1cc88a; }

.sfs-step-mark {
  flex: 0 0 auto; width: 2.15rem; height: 2.15rem; z-index: 1;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; background: #fff;
  border: 2px solid #e3e6f0; color: #858796;
  font-weight: 700; font-size: .85rem;
}
.sfs-step.is-done .sfs-step-mark {
  background: #1cc88a; border-color: #1cc88a; color: #fff;
}
.sfs-step.is-now .sfs-step-mark {
  border-color: var(--sfs-brand, #4e73df); color: var(--sfs-brand, #4e73df);
  box-shadow: 0 0 0 .2rem rgba(78, 115, 223, .15);
}

.sfs-step-body { flex: 1 1 auto; min-width: 0; padding-top: .2rem; }
.sfs-step-label { font-weight: 700; color: #3a3b45; line-height: 1.35; }
.sfs-step.is-done .sfs-step-label { color: #858796; }
.sfs-step-note { font-size: .85rem; color: #6e707e; margin-top: .2rem; line-height: 1.4; }
.sfs-step-tag {
  font-size: .68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .03em; color: #858796;
  background: #eaecf4; border-radius: 1rem; padding: .1rem .45rem;
  margin-left: .35rem;
}
.sfs-step-go { margin-top: .6rem; font-weight: 700; width: 100%; }

/* The steps that are not next. A finished one is still worth opening - to
   change what was set, or just to look - and a grey underlined word is the
   weakest way to say so. Same shape as the calls to action on every other
   card, so the whole dashboard offers one gesture rather than several. */
.sfs-step-go-again {
  display: inline-flex; align-items: center; gap: .35rem;
  margin-top: .5rem;
  font-size: .82rem; font-weight: 700; color: var(--sfs-brand, #4e73df);
  text-decoration: none;
}
.sfs-step-go-again:hover { color: var(--sfs-brand-dark, #2e59d9); text-decoration: none; }
.sfs-step-go-again i { font-size: .72rem; transition: transform .15s ease; }
.sfs-step-go-again:hover i { transform: translateX(.2rem); }

/* Already done. Said in the colour of the tick beside it, and quietly -
   it is not work, it is a way back in. */
.sfs-step-go-again.is-done { color: #13855c; }
.sfs-step-go-again.is-done:hover { color: #0f6b4a; }

@media (min-width: 768px) {
  .sfs-flows { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sfs-step-go { width: auto; }
}

/* ------------------------------------------------------------------ *
 * A bursar's numbers. Amounts, not counts of things - and every one of
 * them a link, because each is something to act on rather than note.
 * ------------------------------------------------------------------ */

.sfs-figures { display: grid; gap: .6rem; margin-bottom: 1.75rem; }

/* Same treatment as the buttons on a parent's card: the colour is the
   resting state, and hovering lifts the card to white. A left-edge stripe
   on a white card is a detail somebody has to already be looking at; a
   tinted card is visible from across the room, which is the point when the
   whole reason for the colour is "this one needs you". */
.sfs-figure {
  display: flex; align-items: flex-start; gap: .85rem;
  background: #f8f9fc; border: 1px solid #b7b9cc;
  border-radius: .6rem;
  padding: .9rem 1rem; text-decoration: none; color: #3a3b45;
  box-shadow: 0 .1rem .5rem rgba(58, 59, 69, .05);
  transition: background-color .15s ease, border-color .15s ease;
}
.sfs-figure:hover {
  background: #fff; border-color: var(--sfs-brand, #4e73df); color: #1f2937;
  text-decoration: none;
}

/* Settled, and worth a glance rather than a look. */
.sfs-figure-good { background: #e9f9f2; border-color: #1cc88a; }
.sfs-figure-good:hover { background: #fff; border-color: #13855c; }

/* Something is waiting on somebody. Amber rather than red on purpose:
   unreconciled payments and unfinished transfers are ordinary work, not an
   emergency, and a dashboard that cries wolf gets ignored. */
.sfs-figure-warn { background: #fdf7e6; border-color: #f6c23e; }
.sfs-figure-warn:hover { background: #fff; border-color: #dda20a; }

.sfs-figure-icon {
  flex: 0 0 auto; width: 2.4rem; height: 2.4rem;
  display: flex; align-items: center; justify-content: center;
  border-radius: .5rem; background: #fff; color: #858796;
}
.sfs-figure-good .sfs-figure-icon { color: #13855c; }
.sfs-figure-warn .sfs-figure-icon { color: #b9880a; }

.sfs-figure-body { flex: 1 1 auto; min-width: 0; }
.sfs-figure-label {
  font-size: .75rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: #6e707e;
}
.sfs-figure-value {
  font-size: 1.3rem; font-weight: 800; color: #3a3b45;
  line-height: 1.2; margin: .1rem 0;
}
.sfs-figure-sub { font-size: .8rem; font-weight: 600; color: #6e707e; }
.sfs-figure-note { font-size: .92rem; color: #1f2937; line-height: 1.45; }

/* The words on the way out. Without these the card is a fact, and a fact is
   something you read and leave - these are all links, but a number in a box
   does not look like one, and on a phone there is no hover to discover it
   with. The arrow nudges on hover so the whole card feels pressable. */
.sfs-figure-go {
  margin-top: .55rem;
  font-size: .82rem; font-weight: 700;
  color: var(--sfs-brand, #4e73df);
  display: inline-flex; align-items: center; gap: .35rem;
}
.sfs-figure-go i { font-size: .72rem; transition: transform .15s ease; }
.sfs-figure:hover .sfs-figure-go i { transform: translateX(.2rem); }

.sfs-figure-good .sfs-figure-go { color: #13855c; }
.sfs-figure-warn .sfs-figure-go { color: #b9880a; }

@media (min-width: 768px) {
  .sfs-figures { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1200px) {
  .sfs-figures { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* The five stand-in photos, shown as pictures rather than a list of colour
   names - nobody can tell what "sand" looks like from the word. */
.sfs-avatars { display: flex; flex-wrap: wrap; gap: .75rem; }
.sfs-avatar {
  display: flex; flex-direction: column; align-items: center; gap: .3rem;
  border: 2px solid #e3e6f0; border-radius: .5rem;
  padding: .5rem; cursor: pointer; margin: 0;
  font-size: .75rem; color: #6e707e;
  transition: border-color .15s ease;
}
.sfs-avatar img { width: 54px; height: 54px; border-radius: .35rem; }
.sfs-avatar input { margin: 0; }
.sfs-avatar:hover { border-color: #b7b9cc; }
.sfs-avatar:has(input:checked) { border-color: var(--sfs-brand, #4e73df); background: #f6f8fe; }
.sfs-avatar:has(input:checked) span { color: var(--sfs-brand-dark, #2e59d9); font-weight: 700; }

/* ------------------------------------------------------------------ *
 * The other ways in, at the foot of a login page.
 *
 * Three doors into this system and no sign on any of them. Somebody who
 * has landed on the wrong one is on a phone and already lost, so these
 * are things to press rather than sentences to read.
 * ------------------------------------------------------------------ */

.sfs-login-ways {
  display: grid; gap: .5rem;
  margin: 1.25rem 0 .75rem;
  border-top: 1px solid #e3e6f0;
  padding-top: 1rem;
}

.sfs-login-ways a {
  display: flex; align-items: center; gap: .7rem;
  border: 1px solid #e3e6f0; border-radius: .5rem;
  padding: .65rem .8rem;
  color: #6e707e; text-decoration: none; background: #fff;
  transition: border-color .15s ease, background-color .15s ease;
}
.sfs-login-ways a:hover {
  border-color: var(--sfs-brand, #4e73df); background: #f6f8fe;
  color: #3a3b45; text-decoration: none;
}

.sfs-login-ways a > i:first-child {
  flex: 0 0 2rem; height: 2rem;
  display: flex; align-items: center; justify-content: center;
  border-radius: .4rem;
  background: var(--sfs-brand-tint, #eaeffb); color: var(--sfs-brand, #4e73df); font-size: .85rem;
}
.sfs-login-ways span { flex: 1 1 auto; min-width: 0; }
/* Blue, because the sign-in button beside them is near-black and so was
   this: a page where the one thing to press and the three ways out are all
   the same colour gives the eye nothing to land on. */
.sfs-login-ways strong {
  display: block; font-size: .85rem; font-weight: 600; line-height: 1.25;
  color: var(--sfs-brand-dark, #2e59d9);
}
.sfs-login-ways a:hover strong { color: #1c3faa; }
.sfs-login-ways em {
  display: block; font-style: normal; font-size: .75rem;
  color: #858796; margin-top: .1rem;
}
.sfs-login-go { color: #d1d3e2; font-size: .7rem; flex: 0 0 auto; }

.sfs-login-lost {
  text-align: center; font-size: .78rem; color: #858796;
  margin: 0; padding-top: .25rem;
}
.sfs-login-lost i { color: #b7b9cc; }

/* Form on the left, the other ways in on the right. One column on a phone,
   where there is nothing to sit beside anything; two on a laptop, where a
   single narrow strip down the middle of the screen wastes most of it. */
.sfs-login-split { display: grid; gap: 1.25rem; }

.sfs-login-main { min-width: 0; }
.sfs-login-aside { min-width: 0; }

@media (min-width: 992px) {
  .sfs-login-split {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: 2rem;
    align-items: start;
  }
  /* The rule above the ways becomes the rule between them and the form. */
  .sfs-login-aside .sfs-login-ways {
    margin-top: 0;
    border-top: 0;
    border-left: 1px solid #e3e6f0;
    padding-top: 0;
    padding-left: 1.75rem;
  }
  .sfs-login-aside .sfs-login-lost {
    text-align: left;
    padding-left: 1.75rem;
    margin-top: .75rem;
  }
}

/* ------------------------------------------------------------------ *
 * The menu item that vanished under the pointer.
 *
 * The theme paints a dropdown item's hover background pure black
 * (`.collapse-item:hover { background-color: #000 }`) while the item's
 * own text is #3a3b45, and these templates darken it further with
 * `text-dark`. Hovering therefore put near-black text on a black
 * background and the item disappeared - on the white panel it sits in,
 * which reads as the menu losing a line rather than highlighting one.
 *
 * A light ground instead: the panel is white, so the highlight should be
 * a shade of it rather than its opposite. Loaded after the theme, and at
 * the same weight as the rule it replaces, so no !important is needed.
 * ------------------------------------------------------------------ */

.sidebar .nav-item .collapse .collapse-inner .collapse-item:hover,
.sidebar .nav-item .collapsing .collapse-inner .collapse-item:hover {
  background-color: var(--sfs-brand-tint, #eaeffb);
  color: var(--sfs-brand-dark, #2e59d9);
}

/* `text-dark` is a Bootstrap utility and outranks the rule above on its
   own, so the hover colour is stated for it too. */
.sidebar .nav-item .collapse .collapse-inner .collapse-item.text-dark:hover,
.sidebar .nav-item .collapsing .collapse-inner .collapse-item.text-dark:hover {
  color: var(--sfs-brand-dark, #2e59d9) !important;
}

/* The chosen item, so the page you are on is legible without hovering. */
.sidebar .nav-item .collapse .collapse-inner .collapse-item.active,
.sidebar .nav-item .collapse .collapse-inner .collapse-item.text-dark.active {
  color: var(--sfs-brand-dark, #2e59d9) !important;
  background-color: #f2f5fd;
  font-weight: 700;
}


/* ===================================================================== *
 * Data tables, after the CRM dashboard this school asked them to match.
 *
 * Desktop: the table sits in its own bordered card. Headings are small,
 * uppercase and grey on a tinted strip; rows are separated by a hairline
 * and the last one carries none, so the card closes cleanly.
 *
 * Phone: each row becomes a card laid out as a two-column grid. Short
 * fields (a date, a count, an amount) pair up two to a line, which is what
 * keeps a nine-column table down to a card you can read without scrolling.
 * The identity column and the buttons take the full width -- a name and an
 * action are the two things that must never be squeezed into half a row.
 *
 * The label above each value comes from the column heading, attached by
 * app.js from the table's own <thead>.
 * ===================================================================== */

/* Every table here already sits inside a .card, which draws the bordered,
   rounded frame the reference gets from .table-wrap. Adding it again here
   would box each table twice, so only the inside of the table is styled. */

.table thead th {
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: #6b7280;
  background: #f9fafb;
  padding: 10px 14px;
  border-top: 0;
  border-bottom: 1px solid #e5e7eb;
}
.table tbody td {
  padding: 12px 14px;
  border-top: 0;
  border-bottom: 1px solid #f0f1f3;
  font-size: 14px;
  color: #1f2937;
  vertical-align: top;
}
.table tbody tr:last-child td {
  border-bottom: none;
}

/* The two-line identity cell: a name with something quieter beneath it. */
.table .uname {
  font-weight: 600;
}
.table .muted {
  color: #9ca3af;
  font-size: 12px;
}

/* Pill badges, in the weights the reference uses. */
.table .badge,
.sfs-pill {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
}

/* Row actions. */
.action-btn {
  font-size: 12px;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: 7px;
  border: none;
  cursor: pointer;
  color: #fff;
  background: #2563eb;
}
.action-btn:hover {
  background: #1d4ed8;
  color: #fff;
}
.action-btn.secondary { background: #6b7280; }
.action-btn.secondary:hover { background: #4b5563; }
.action-btn.mine { background: #16a34a; }
.action-btn.mine:hover { background: #15803d; }
.action-btn.danger { background: #dc2626; }
.action-btn.danger:hover { background: #b91c1c; }
.action-btn.locked {
  background: #e5e7eb;
  color: #9ca3af;
  cursor: not-allowed;
}

@media (max-width: 768px) {
  /* Only a table app.js could label: one with no <thead> has no headings
     to stack with, and clipping it would hide the columns this rescues. */
  .sfs-stacked-wrap {
    border-radius: 12px;
    background: transparent;
    border: none;
    overflow: visible;
  }
  .sfs-stacked,
  .sfs-stacked tbody {
    display: block;
    width: 100%;
  }
  .sfs-stacked thead {
    /* Off-screen rather than display:none, so a screen reader still hears
       the headings the sighted reader now gets from the labels. */
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .sfs-stacked tbody tr {
    display: grid;
    grid-template-columns: 1fr 1fr;
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    margin-bottom: 12px;
    overflow: hidden;
  }
  .sfs-stacked tbody td {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 10px 14px;
    border-bottom: 1px solid #f0f1f3;
    min-width: 0;
    text-align: left !important;
  }
  .sfs-stacked tbody td::before {
    content: attr(data-label);
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: #9ca3af;
  }
  /* A name, and the buttons, each get the whole width. */
  .sfs-stacked tbody td.full-mobile {
    grid-column: 1 / -1;
  }
  /* Row actions, two to a line. Five of them stacked one per line is
     most of a phone screen spent on one record, and the buttons a school
     uses most -- edit, open -- end up below the fold of the card. */
  .sfs-stacked tbody td.sfs-cell-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .35rem;
    align-items: center;
  }
  .sfs-stacked tbody td.sfs-cell-actions::before {
    grid-column: 1 / -1;
    margin-bottom: .1rem;
  }
  /* When an action cell has 3 buttons/forms or .sfs-actions-3, all 3 sit on the SAME line */
  .sfs-stacked tbody td.sfs-cell-actions.sfs-actions-3,
  .sfs-stacked tbody td.sfs-cell-actions:has(> :nth-child(3):last-child),
  .sfs-stacked tbody td.sfs-cell-actions:has(> form:nth-child(3):last-child),
  .sfs-stacked tbody td.sfs-cell-actions:has(> .btn:nth-child(3):last-child) {
    grid-template-columns: repeat(3, 1fr) !important;
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    align-items: center !important;
    justify-content: space-between !important;
    gap: .3rem !important;
    width: 100% !important;
  }
  .sfs-stacked tbody td.sfs-cell-actions.sfs-actions-3 > .btn,
  .sfs-stacked tbody td.sfs-cell-actions.sfs-actions-3 > form,
  .sfs-stacked tbody td.sfs-cell-actions.sfs-actions-3 > a,
  .sfs-stacked tbody td.sfs-cell-actions:has(> :nth-child(3):last-child) > .btn,
  .sfs-stacked tbody td.sfs-cell-actions:has(> :nth-child(3):last-child) > form,
  .sfs-stacked tbody td.sfs-cell-actions:has(> :nth-child(3):last-child) > a {
    flex: 1 1 0 !important;
    min-width: 0 !important;
    width: auto !important;
    margin: 0 !important;
  }
  /* Complex actions, decision forms, and toolbars span full card width with flexible clean layout */
  .sfs-stacked tbody td.sfs-cell-decision,
  .sfs-stacked tbody td.sfs-cell-actions.sfs-cell-decision,
  .sfs-stacked tbody td.sfs-cell-actions:has(.sfs-decision-toolbar),
  .sfs-stacked tbody td.sfs-cell-actions:has(.sfs-action-group),
  .sfs-stacked tbody td.sfs-cell-actions:has(input, textarea, select, .input-group) {
    display: flex !important;
    flex-direction: column !important;
    gap: 0.35rem !important;
    grid-column: 1 / -1 !important;
    width: 100% !important;
    align-items: stretch !important;
  }
  /* Checkboxes and radio buttons inside stacked cards must never carry Bootstrap's -1.25rem negative margin */
  .sfs-stacked tbody td .form-check-input,
  .sfs-stacked tbody td input[type="checkbox"],
  .sfs-stacked tbody td input[type="radio"] {
    position: static !important;
    margin-left: 0.25rem !important;
    margin-top: 0.25rem !important;
  }
  .sfs-stacked tbody td.sfs-cell-decision:has(input[type="checkbox"]),
  .sfs-stacked tbody td:has(input[type="checkbox"]:only-child),
  .sfs-stacked tbody td:has(.custom-checkbox) {
    align-items: flex-start !important;
  }

  /* Attendance Register: status text and radio buttons on mobile cards (2x2 grid) */
  .sfs-stacked .student-register-row td:first-child {
    grid-column: 1 / -1 !important;
    padding: 12px 16px !important;
    background: #f8fafc;
    border-bottom: 1px solid #e2e8f0;
  }
  .sfs-stacked .student-register-row td.sfs-status-cell {
    grid-column: span 1 !important;
    padding: 4px 10px !important;
    display: flex !important;
    flex-direction: row !important;
    align-items: center !important;
    border-bottom: 1px solid #f1f5f9;
  }
  .sfs-stacked .student-register-row td.sfs-status-cell:nth-child(2),
  .sfs-stacked .student-register-row td.sfs-status-cell:nth-child(4) {
    border-right: 1px solid #f1f5f9;
  }
  .sfs-stacked .student-register-row td.sfs-status-cell::before {
    display: none !important;
  }
  .sfs-stacked .student-register-row td.sfs-status-cell label {
    display: flex !important;
    align-items: center !important;
    justify-content: flex-start !important;
    width: 100% !important;
    padding: 6px 4px !important;
    margin: 0 !important;
    min-height: 40px !important;
    cursor: pointer !important;
  }
  .sfs-stacked .student-register-row td.sfs-status-cell input[type="radio"] {
    margin: 0 !important;
    flex-shrink: 0 !important;
    width: 1.15rem !important;
    height: 1.15rem !important;
    cursor: pointer !important;
  }
  .sfs-stacked .student-register-row td.sfs-status-cell .status-label-text {
    font-size: 0.85rem !important;
    font-weight: 600 !important;
    color: #334155 !important;
    margin-left: 0.5rem !important;
    user-select: none !important;
  }
  /* A button, or the little POST form around one, is one cell of the grid. */
  .sfs-stacked tbody td.sfs-cell-actions > .btn,
  .sfs-stacked tbody td.sfs-cell-actions > .action-btn,
  .sfs-stacked tbody td.sfs-cell-actions > form {
    width: 100%;
    margin: 0 !important;
    display: block;
  }
  .sfs-stacked tbody td.sfs-cell-actions > .btn,
  .sfs-stacked tbody td.sfs-cell-actions > .action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    height: 28px !important;
    min-height: 28px !important;
    max-height: 28px !important;
    font-size: 11px !important;
    font-weight: 600 !important;
    padding: 2px 4px !important;
    line-height: 1.1 !important;
    border-radius: 6px !important;
    white-space: nowrap !important;
  }
  .sfs-stacked tbody td.sfs-cell-actions > form > .btn,
  .sfs-stacked tbody td.sfs-cell-actions > form > .action-btn,
  .sfs-stacked tbody td.sfs-cell-actions > form > button {
    width: 100%;
    margin: 0 !important;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 28px !important;
    min-height: 28px !important;
    max-height: 28px !important;
    font-size: 11px !important;
    font-weight: 600 !important;
    padding: 2px 4px !important;
    line-height: 1.1 !important;
    border-radius: 6px !important;
    white-space: nowrap !important;
  }
  /* An odd one out spans both columns rather than leaving a gap, the same
     rule the cells themselves follow. */
  .sfs-stacked tbody td.sfs-cell-actions > *:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }

  /* A long sentence that merely contains a link still wants the width,
     but not the grid. */
  .sfs-stacked tbody td.full-mobile:not(.sfs-cell-actions) .btn {
    margin: 0 .35rem .35rem 0;
  }
  /* An empty cell would otherwise show a heading over nothing. */
  .sfs-stacked tbody td.sfs-cell-empty {
    display: none;
  }
}

@media (max-width: 420px) {
  .sfs-stacked tbody td {
    padding: 8px 12px;
  }
  .sfs-stacked tbody td::before {
    font-size: 9px;
  }
}

/* ------------------------------------------------ actions in a row ---- *
 * The buttons in a table row take the reference shape: small, semibold,
 * softly rounded, flat colour. Compact 28px height keeps rows neat and
 * prevents tall card bloat on mobile.
 *
 * Mapped onto the Bootstrap colour classes the templates already use, so
 * no screen has to be rewritten to get the new look.
 * ------------------------------------------------------------------- */

.table td .btn,
.table td .btn-sm {
  font-size: 11px;
  font-weight: 600;
  padding: 2px 7px;
  height: 28px;
  min-height: 28px;
  max-height: 28px;
  border-radius: 6px;
  border: none;
  line-height: 1.1;
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
}
.table td .btn:hover {
  color: #fff;
}
.table td .btn-primary   { background: #2563eb; }
.table td .btn-primary:hover { background: #1d4ed8; }
.table td .btn-success   { background: #16a34a; }
.table td .btn-success:hover { background: #15803d; }
.table td .btn-danger    { background: #dc2626; }
.table td .btn-danger:hover { background: #b91c1c; }
.table td .btn-secondary { background: #6b7280; }
.table td .btn-secondary:hover { background: #4b5563; }
.table td .btn-warning   { background: #d97706; }
.table td .btn-warning:hover { background: #b45309; }
/* Purple belongs to this family too, or a row action would keep the
   full-size button's shade while its neighbours use the flatter one. */
.table td .btn-purple    { background: #6f42c1; }
.table td .btn-purple:hover { background: #5e37a6; }
.table td .btn-info      { background: #0ea5e9; }
.table td .btn-info:hover { background: #0284c7; }

/* The outline variants keep their outline, at the same size. */
.table td .btn-outline-primary,
.table td .btn-outline-success,
.table td .btn-outline-danger,
.table td .btn-outline-secondary,
.table td .btn-outline-info {
  background: transparent;
  border: 1px solid currentColor;
  height: 28px;
  min-height: 28px;
  max-height: 28px;
  font-size: 11px;
  font-weight: 600;
  padding: 2px 7px;
  line-height: 1.1;
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
}
.table td .btn-outline-primary   { color: #2563eb; }
.table td .btn-outline-success   { color: #16a34a; }
.table td .btn-outline-danger    { color: #dc2626; }
.table td .btn-outline-secondary { color: #6b7280; }
.table td .btn-outline-info      { color: #0ea5e9; }
.table td .btn-outline-primary:hover   { background: #2563eb; color: #fff; }
.table td .btn-outline-success:hover   { background: #16a34a; color: #fff; }
.table td .btn-outline-danger:hover    { background: #dc2626; color: #fff; }
.table td .btn-outline-secondary:hover { background: #6b7280; color: #fff; }
.table td .btn-outline-info:hover      { background: #0ea5e9; color: #fff; }

/* Status pills, in the reference weights, mapped onto the badge colours
   the templates already ask for. */
.table td .badge {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
}
.table td .badge-success, .table td .bg-success { background: #dcfce7 !important; color: #166534 !important; }
.table td .badge-danger,  .table td .bg-danger  { background: #fee2e2 !important; color: #b91c1c !important; }
.table td .badge-warning, .table td .bg-warning { background: #fef9c3 !important; color: #854d0e !important; }
.table td .badge-info,    .table td .bg-info    { background: #dbeafe !important; color: #1d4ed8 !important; }
.table td .badge-secondary, .table td .bg-secondary { background: #f3f4f6 !important; color: #6b7280 !important; }
/* ------------------------------------------- decision and action toolbars ---- */

.sfs-decision-toolbar,
.sfs-action-group {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: .25rem;
}
.sfs-action-group {
  justify-content: center;
  width: 100%;
}
.sfs-decision-toolbar form,
.sfs-action-group form {
  margin: 0;
  display: inline-flex;
}
.sfs-decision-toolbar .btn,
.sfs-action-group .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700 !important;
  padding: 2px 7px;
  min-height: 28px;
  height: 28px;
  max-height: 28px;
  line-height: 1.1;
  border-radius: 6px;
  white-space: nowrap;
}
.table-actions-wrap {
  vertical-align: middle;
}
/* All table action buttons text bold */
.table td .btn,
.table td .btn-sm,
.table th .btn,
.table-actions .btn,
.table-actions form .btn,
.table-actions form button,
.table-actions-wrap .btn,
.table-actions-wrap form .btn,
.table-actions-wrap form button,
.sfs-action-group .btn,
.sfs-action-group form .btn,
.sfs-action-group form button,
.sfs-decision-toolbar .btn,
.sfs-decision-toolbar form .btn,
.sfs-decision-toolbar form button,
.sfs-cell-actions .btn,
.sfs-cell-actions button,
.sfs-cell-actions form .btn,
.sfs-cell-actions form button,
.sfs-cell-decision .btn,
.sfs-cell-decision button,
.sfs-cell-decision form .btn,
.sfs-cell-decision form button {
  font-weight: 700 !important;
  letter-spacing: 0.01em;
}

@media (min-width: 769px) {
  .table-actions,
  .table-actions-wrap,
  td.sfs-cell-decision,
  td.table-actions-wrap {
    white-space: nowrap !important;
  }
  .sfs-action-group {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    align-items: center !important;
    justify-content: center !important;
    gap: .3rem !important;
    width: auto !important;
  }
  .sfs-action-group form {
    margin: 0 !important;
    display: inline-flex !important;
  }
  .sfs-action-group .btn,
  .sfs-action-group form .btn,
  .sfs-action-group form button {
    white-space: nowrap !important;
    flex: 0 0 auto !important;
  }
}
@media (max-width: 768px) {
  .sfs-decision-toolbar,
  .sfs-action-group {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    align-items: center !important;
    justify-content: space-between !important;
    gap: .25rem !important;
    width: 100% !important;
  }
  .sfs-decision-toolbar > .btn,
  .sfs-decision-toolbar > form,
  .sfs-action-group > .btn,
  .sfs-action-group > form {
    flex: 1 1 0 !important;
    width: auto !important;
    margin: 0 !important;
    min-width: 0 !important;
  }
  .sfs-decision-toolbar > form > .btn,
  .sfs-action-group > form > .btn,
  .sfs-action-group > form > button {
    width: 100% !important;
  }
  .sfs-decision-toolbar .btn,
  .sfs-decision-toolbar form .btn,
  .sfs-action-group .btn,
  .sfs-action-group form .btn {
    width: 100% !important;
    min-height: 28px !important;
    height: 28px !important;
    max-height: 28px !important;
    padding: 2px 4px !important;
    font-size: 11px !important;
    font-weight: 700 !important;
    line-height: 1.1 !important;
    white-space: nowrap !important;
  }
}

/* ------------------------------------------- form action toolbars ---- */
.sfs-form-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1rem;
}
@media (max-width: 576px) {
  .sfs-form-actions {
    flex-direction: column;
    align-items: stretch;
  }
  .sfs-form-actions > .btn,
  .sfs-form-actions > a.btn,
  .sfs-form-actions > button {
    width: 100% !important;
    margin-left: 0 !important;
    text-align: center;
    justify-content: center;
  }
}

.sfs-filter-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
@media (max-width: 576px) {
  .sfs-filter-actions {
    display: flex !important;
    width: 100% !important;
    gap: 0.5rem !important;
  }
  .sfs-filter-actions > .btn,
  .sfs-filter-actions > a.btn,
  .sfs-filter-actions > button {
    flex: 1 1 0 !important;
    margin-left: 0 !important;
    text-align: center;
    justify-content: center;
    display: inline-flex;
    align-items: center;
  }
}



/* ------------------------------------------------- hovering a row ---- *
 * The theme's .table-hover washes the row grey and greys the text with it
 * (sb-admin-2.css: `color:#858796; background:rgba(0,0,0,.075)`), so the
 * row somebody is pointing at is the one hardest to read. It should be the
 * easiest.
 *
 * A pale blue tint instead, the same family as the buttons, and the text
 * stays dark -- darker, in fact, so the row lifts towards the reader
 * rather than fading away from them.
 * ------------------------------------------------------------------- */

.table-hover tbody tr:hover {
  background-color: #f4f7ff;
  color: #111827;
}
.table-hover tbody tr:hover td {
  color: #111827;
}
/* Links and quiet sub-lines keep their meaning, just darker. */
.table-hover tbody tr:hover td .text-muted,
.table-hover tbody tr:hover td .muted,
.table-hover tbody tr:hover td small {
  color: #4b5563 !important;
}

@media (max-width: 768px) {
  /* Each row is a card here; a whole card greying under a thumb is worse
     than on a wide screen, where at least the neighbours stay readable. */
  .sfs-stacked.table-hover tbody tr:hover {
    background-color: #fff;
    border-color: #c7d6fb;
  }
}

/* ---------------------------------------- the rest of the phone view --- */

@media (max-width: 768px) {
  /* A totals foot is not a set of records, it is a short list of figures.
     One full-width line each, label left and amount right, the way a
     receipt reads. */
  .sfs-stacked tfoot {
    display: block;
    width: 100%;
  }
  .sfs-stacked tfoot tr,
  .sfs-stacked tbody tr.sfs-total-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    grid-template-columns: none;
    padding: 10px 14px;
    margin-bottom: 0;
    border: 0;
    border-bottom: 1px solid #f0f1f3;
    border-radius: 0;
    background: #fff;
  }
  .sfs-stacked tfoot tr:first-child {
    border-top: 1px solid #e5e7eb;
    border-radius: 12px 12px 0 0;
  }
  .sfs-stacked tfoot tr:last-child {
    border-bottom: 0;
    border-radius: 0 0 12px 12px;
  }
  .sfs-stacked tfoot {
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    overflow: hidden;
  }
  .sfs-stacked tfoot th,
  .sfs-stacked tfoot td {
    display: block;
    border: 0;
    padding: 0;
    font-size: 14px;
    text-align: left !important;
  }
  .sfs-stacked tfoot th:last-child,
  .sfs-stacked tfoot td:last-child {
    text-align: right !important;
    white-space: nowrap;
  }
  /* The label is the cell itself here, so no heading above it. */
  .sfs-stacked tfoot th::before,
  .sfs-stacked tfoot td::before {
    content: none;
  }

  /* Each row already draws its own card on a phone, so the outer card's
     border and shadow around them is a box inside a box. Only cards that
     actually hold a stacked table lose their frame -- a form card still
     needs its padding, or the inputs sit against the edge of the screen. */
  .card.sfs-table-card,
  .card.shadow.sfs-table-card {
    border: 0 !important;
    box-shadow: none !important;
    background: transparent !important;
    margin-bottom: 1rem !important;
  }
  .card.sfs-table-card > .card-body {
    /* The row cards below carry their own 14px, so this holds none: two
       lots of padding would inset them further than everything else. */
    padding: 0 !important;
    background: transparent !important;
  }
  .card.sfs-table-card > .card-header {
    /* Matched to the row cards' own inner padding, so the heading sits
       directly above the text it labels instead of jutting out to the
       left of it. */
    padding-left: 4px !important;
    padding-right: 4px !important;
    padding-top: 0 !important;
    padding-bottom: 0.75rem !important;
    background: transparent !important;
    border-bottom: 0 !important;
  }
  /* The footer holding the pager lines up with them too. */
  .card.sfs-table-card > .card-footer {
    padding-left: 4px !important;
    padding-right: 4px !important;
    background: transparent !important;
    border-top: 0 !important;
  }

  /* The other jobs of the same kind: two to a line, not one each. */
  .sfs-next-list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .5rem;
  }
  .sfs-next-list a {
    justify-content: center;
    text-align: center;
    padding: .55rem .6rem;
    font-size: .78rem;
  }
}

/* ------------------------------------------------------------------ *
 * Flex gap utilities (Bootstrap 4 polyfill)
 * ------------------------------------------------------------------ */
.gap-0 { gap: 0 !important; }
.gap-1 { gap: 0.25rem !important; }
.gap-2 { gap: 0.45rem !important; }
.gap-3 { gap: 0.75rem !important; }
.gap-4 { gap: 1rem !important; }
.gap-5 { gap: 1.5rem !important; }
.row-gap-1 { row-gap: 0.25rem !important; }
.row-gap-2 { row-gap: 0.45rem !important; }
.row-gap-3 { row-gap: 0.75rem !important; }
.column-gap-1 { column-gap: 0.25rem !important; }
.column-gap-2 { column-gap: 0.45rem !important; }
.column-gap-3 { column-gap: 0.75rem !important; }

/* ------------------------------------------------------------------ *
 * Responsive Page Header (Title + Actions)
 * Wraps cleanly and maintains neat, compact touch targets and balanced
 * space between titles and buttons across desktop, tablets, and phones.
 * ------------------------------------------------------------------ */
.d-sm-flex.align-items-center.justify-content-between,
.page-header-row {
  display: flex !important;
  flex-wrap: wrap !important;
  align-items: center !important;
  justify-content: space-between !important;
  gap: 0.5rem 0.75rem;
}
.d-sm-flex.align-items-center.justify-content-between > h1,
.page-header-row > h1,
.page-title-heading {
  min-width: 0;
  max-width: 100%;
  font-size: 1.3rem;
  line-height: 1.25;
  word-break: break-word;
  margin-bottom: 0 !important;
}
.d-sm-flex.align-items-center.justify-content-between > .no-print,
.page-header-row > .no-print,
.page-actions-wrap,
.page-actions-wrap > div,
.page-actions-wrap .d-flex {
  display: flex !important;
  flex-wrap: wrap !important;
  align-items: center !important;
  gap: 0.35rem 0.45rem !important;
  max-width: 100%;
}
.d-sm-flex.align-items-center.justify-content-between > .no-print .btn,
.page-header-row > .no-print .btn,
.page-actions-wrap .btn {
  white-space: nowrap;
  border-radius: 6px;
  padding: 0.32rem 0.65rem !important;
  font-size: 0.8rem !important;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1.35;
}

/* Card headers with title + action buttons wrap cleanly */
.card-header.d-flex,
.card-header.d-sm-flex {
  flex-wrap: wrap !important;
  gap: 0.5rem 0.75rem !important;
}

@media (max-width: 576px) {
  .d-sm-flex.align-items-center.justify-content-between,
  .page-header-row {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 0.45rem !important;
    margin-bottom: 1.15rem !important;
  }
  .d-sm-flex.align-items-center.justify-content-between > h1,
  .page-header-row > h1,
  .page-title-heading {
    width: 100% !important;
    flex: 1 1 100% !important;
    font-size: 1.2rem !important;
    line-height: 1.3;
    margin-bottom: 0 !important;
    padding-bottom: 0 !important;
  }
  .d-sm-flex.align-items-center.justify-content-between > .no-print,
  .page-header-row > .no-print,
  .page-actions-wrap,
  .page-actions-wrap > div,
  .page-actions-wrap .d-flex {
    width: 100% !important;
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    gap: 0.35rem 0.45rem !important;
    margin-top: 0 !important;
  }
  .d-sm-flex.align-items-center.justify-content-between > .no-print .btn,
  .page-header-row > .no-print .btn,
  .page-actions-wrap .btn {
    white-space: nowrap;
    padding: 0.3rem 0.6rem !important;
    font-size: 0.78rem !important;
    font-weight: 600;
    border-radius: 6px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1.3;
  }
  .card-header.d-flex,
  .card-header.d-sm-flex {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 0.5rem !important;
  }
  .card-header.d-flex > h6,
  .card-header.d-flex > h5,
  .card-header.d-flex > h4,
  .card-header.d-sm-flex > h6,
  .card-header.d-sm-flex > h5,
  .card-header.d-sm-flex > h4 {
    margin-bottom: 0 !important;
  }
}

@media (max-width: 420px) {
  .d-sm-flex.align-items-center.justify-content-between,
  .page-header-row {
    gap: 0.4rem !important;
  }
  .d-sm-flex.align-items-center.justify-content-between > h1,
  .page-header-row > h1,
  .page-title-heading {
    font-size: 1.1rem !important;
  }
  .d-sm-flex.align-items-center.justify-content-between > .no-print,
  .page-header-row > .no-print,
  .page-actions-wrap,
  .page-actions-wrap > div,
  .page-actions-wrap .d-flex {
    gap: 0.3rem 0.4rem !important;
  }
  .d-sm-flex.align-items-center.justify-content-between > .no-print .btn,
  .page-header-row > .no-print .btn,
  .page-actions-wrap .btn {
    padding: 0.28rem 0.55rem !important;
    font-size: 0.76rem !important;
  }
}

/* ------------------------------------------------ the bar at the top --- */

/* The school's name, beside the menu button. It has to give way before the
   account menu does, so it truncates rather than wraps or pushes. */
.sfs-school {
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;
}
.sfs-school span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 768px) {
  /* 4.375rem of header is a lot of a phone screen to spend before the page
     has said anything. */
  .topbar {
    height: 3.25rem;
    margin-bottom: 1rem !important;
  }
  .topbar #sidebarToggleTop {
    height: 2rem;
    width: 2rem;
    margin-right: .5rem !important;
  }
  .topbar .nav-item .nav-link .img-profile {
    height: 1.7rem;
    width: 1.7rem;
  }
  .topbar .nav-link {
    padding-top: .35rem;
    padding-bottom: .35rem;
  }
  .sfs-school span {
    font-size: .78rem;
  }
}

@media (max-width: 420px) {
  .sfs-school span {
    font-size: .72rem;
  }
}


@media (max-width: 768px) {
  /* A little more room at the edge of a phone. The theme's 0.75rem puts
     every card hard against the bezel, which is what made the page look
     like it had no margin at all rather than a deliberate one. */
  #content .container-fluid {
    padding-left: 1rem;
    padding-right: 1rem;
  }
  /* An ordinary card's own padding on top of that is too much of a narrow
     screen to give away. */
  .card:not(.sfs-table-card) > .card-body {
    padding: 1rem;
  }
  .card:not(.sfs-table-card) > .card-header {
    padding: .7rem 1rem;
  }
}

/* ------------------------------------------ the top of a list screen --- */

@media (max-width: 768px) {
  /* "Search Class", then a label reading "Name", then a box whose
     placeholder reads "Search class name": three ways of saying the same
     thing, stacked, each costing a line of a small screen before any
     result appears.
     The heading and the label go off-screen rather than away, so a screen
     reader still announces the field it is looking at. */
  .sfs-search-card > .card-header {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .sfs-search-card .form-group > label[for="q"] {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .sfs-search-card {
    margin-bottom: 1rem !important;
  }
  .sfs-search-card > .card-body {
    padding: .75rem !important;
  }
  .sfs-search-card .form-group {
    margin-bottom: .5rem;
  }
  .sfs-search-card .form-group:last-child {
    margin-bottom: 0;
  }
  /* Search and Reset share a line.
     Sized by flex rather than by percentage: two calc(50% - gap) inline
     blocks plus the whitespace the browser keeps between them come to more
     than the width available, so they wrapped and stacked. Flex divides
     what is actually there and the gap is not part of the sum.
     Only a group whose own children are buttons -- :has(> .btn) -- or the
     row would turn a label and its input side by side. */
  .sfs-search-card .form-group:has(> .btn),
  .sfs-search-card [class*="col-"]:has(> .btn):not(:has(input, select, textarea)) {
    display: flex;
    flex-wrap: nowrap;
    gap: .5rem;
    align-items: stretch;
    /* Stacked under the last field rather than beside it, so it needs a
       gap of its own. Eight pixels between an input and a filled button
       reads as the button belonging to the box above it. */
    margin-top: .6rem;
  }
  .sfs-search-card .form-group:has(> .btn) > .btn,
  .sfs-search-card [class*="col-"]:has(> .btn):not(:has(input, select, textarea)) > .btn {
    flex: 1 1 0;
    width: auto;
    min-width: 0;
    margin: 0 !important;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* The gap under the page heading, and between the cards above the table.
     1.5rem each is generous on a laptop and most of a thumb on a phone. */
  .container-fluid > .mb-4,
  .card.mb-4 {
    margin-bottom: 1rem !important;
  }
  .container-fluid > .d-sm-flex.mb-4 {
    margin-bottom: .75rem !important;
  }

  /* Form controls: the theme sizes these for a mouse. */
  .sfs-search-card .form-control,
  .sfs-search-card .custom-select,
  .sfs-search-card select {
    height: calc(1.5em + .6rem + 2px);
    padding: .3rem .6rem;
    font-size: .85rem;
  }
  .sfs-search-card .btn {
    padding: .35rem .6rem;
    font-size: .85rem;
  }
}

/* ===================================================================== *
 * Forms on a phone.
 *
 * Three shapes needed work, found by going through all 113 forms rather
 * than the one that was reported:
 *
 *   form-inline   Bootstrap lays these out as a flex row at whatever width
 *                 the controls ask for. One of them asks for 9rem in an
 *                 inline style, which cannot shrink, so the row spilled
 *                 sideways inside a table card.
 *   column forms  A button group is a column beside the fields on a wide
 *                 screen and underneath them on a narrow one, where it
 *                 needs a gap it never needed before.
 *   lone buttons  A single button left at half width beside empty space,
 *                 where the whole width was available.
 * ===================================================================== */

@media (max-width: 768px) {

  /* --- inline forms: stack the control, pair the buttons ------------- */
  .form-inline {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .4rem;
    align-items: stretch;
    width: 100%;
  }
  .form-inline > .form-control,
  .form-inline > .custom-select,
  .form-inline > input:not([type="hidden"]),
  .form-inline > select,
  .form-inline > textarea,
  .form-inline > label {
    grid-column: 1 / -1;
    /* !important because one of these carries style="width:9rem" inline,
       and an inline style outranks any class. */
    width: 100% !important;
    max-width: none !important;
    min-width: 0;
    margin: 0 !important;
  }
  .form-inline > .btn {
    width: 100%;
    margin: 0 !important;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .form-inline > .btn:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }

  /* --- any form's button group, not only a filter card's ------------- */
  form .form-row > [class*="col-"]:has(> .btn):not(:has(input:not([type="hidden"]), select, textarea)),
  form .form-row > .form-group:has(> .btn):not(:has(input:not([type="hidden"]), select, textarea)) {
    display: flex;
    flex-wrap: nowrap;
    gap: .5rem;
    align-items: stretch;
    /* Stacked under the last field rather than beside it. */
    margin-top: .6rem;
  }
  form .form-row > [class*="col-"]:has(> .btn):not(:has(input:not([type="hidden"]), select, textarea)) > .btn,
  form .form-row > .form-group:has(> .btn):not(:has(input:not([type="hidden"]), select, textarea)) > .btn {
    flex: 1 1 0;
    width: auto;
    min-width: 0;
    margin: 0 !important;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* --- a form's own submit row, outside a form-row ------------------- */
  form > .btn,
  form > .form-group:last-child > .btn:only-child {
    width: 100%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
}
/* ------------------------------------- spacing between stacked cards --- *
 * On a wide screen a row of cards sits side by side and `mb-4` is the gap
 * under the row. On a phone they stack, so the same 1.5rem becomes a gap
 * between every card -- three terms on the calendar cost 72px of nothing.
 *
 * The margin is on the column, not the card, which is why the earlier rule
 * for `.card.mb-4` never touched these.
 * --------------------------------------------------------------------- */

@media (max-width: 768px) {
  .row > [class*="col-"].mb-4,
  .row > [class*="col-"].mb-3 {
    margin-bottom: .5rem !important;
  }
  /* Any card standing on its own, not only one in a column. */
  .card.mb-4,
  .card.mb-3 {
    margin-bottom: .5rem !important;
  }
  /* The row itself, once its children carry their own gap. */
  .container-fluid > .row,
  .card-body > .row {
    margin-bottom: 0;
  }

  /* Headings inside a card: sized for a page title, not for a card. */
  .card-body h4 {
    font-size: 1.1rem;
  }
  .card-body h5 {
    font-size: .98rem;
  }
  .card-body > .row > [class*="col-"] > p:last-child,
  .card-body > p:last-child {
    margin-bottom: 0;
  }

  /* Buttons: the theme sizes these for a mouse pointer. Still comfortably
     above the 44px tap target once the line-height is counted, because the
     padding is what shrinks, not the text. */
  .btn {
    padding: .35rem .7rem;
    font-size: .85rem;
  }
  .btn-sm {
    padding: .22rem .55rem;
    font-size: .78rem;
  }
  .btn i {
    font-size: .8em;
  }

  /* The stacked banner: its button lands under the text rather than beside
     it, so the theme's 1rem top margin on top of the card padding is more
     air than the card needs. */
  .card-body .mt-3.mt-md-0 {
    margin-top: .6rem !important;
  }
}


@media (max-width: 768px) {
  /* Inside a card: the theme's paragraph and heading margins are set for a
     page, where a paragraph is followed by more paragraphs. In a card each
     line is a separate fact and the gaps read as emptiness. */
  .card:not(.sfs-table-card) > .card-body {
    padding: .8rem;
  }
  .card-body h4,
  .card-body h5,
  .card-body h6 {
    margin-bottom: .25rem;
  }
  .card-body p {
    margin-bottom: .4rem;
  }
  .card-body p:last-child,
  .card-body > :last-child {
    margin-bottom: 0;
  }
  .card-body .text-xs {
    margin-bottom: .15rem !important;
  }
  /* A badge sitting beside a heading should not stretch the line it is on. */
  .card-body .badge {
    line-height: 1.3;
  }
  /* Rows inside a card carry the theme's 15px gutters, which on a phone
     is padding nobody asked for on both sides of a single column. */
  .card-body > .row {
    margin-left: -.4rem;
    margin-right: -.4rem;
  }
  .card-body > .row > [class*="col-"] {
    padding-left: .4rem;
    padding-right: .4rem;
  }
}

/* ============================================================ everywhere *
 * The same treatment as the calendar, applied to the shapes it shares
 * with the rest of the site.
 *
 * These need !important only because Bootstrap's spacing utilities carry
 * it themselves: `py-3` on a card header is `padding: 1rem !important`,
 * and no ordinary rule can answer that. The utilities are used 119 and 45
 * times respectively, so the alternative was editing them out of 164
 * places by hand.
 * ===================================================================== */

@media (max-width: 768px) {
  /* Card headers. py-3 is a comfortable strip on a laptop and a bar across
     a phone. */
  .card > .card-header.py-3 {
    padding-top: .55rem !important;
    padding-bottom: .55rem !important;
  }
  .card > .card-header.py-2 {
    padding-top: .4rem !important;
    padding-bottom: .4rem !important;
  }
  .card > .card-header h6 {
    font-size: .82rem;
  }

  /* The figure cards across the top of the money screens: py-2 on the card
     plus the body's own padding is two lots of the same air. */
  .card.py-2 {
    padding-top: 0 !important;
    padding-bottom: 0 !important;
  }

  /* An empty state is not worth half a screen. */
  .card-body.py-5,
  .py-5 {
    padding-top: 1.5rem !important;
    padding-bottom: 1.5rem !important;
  }
  .card-body.py-4 {
    padding-top: .9rem !important;
    padding-bottom: .9rem !important;
  }

  /* The remaining spacing utilities on cards and columns, so one card does
     not sit twice as far from the next as its neighbour does. */
  .row > [class*="col-"].mb-2,
  .card.mb-2 {
    margin-bottom: .4rem !important;
  }
  .row > [class*="col-"].mt-3,
  .row > [class*="col-"].mt-4 {
    margin-top: .5rem !important;
  }
  .card.mt-4,
  .card.mt-3 {
    margin-top: .5rem !important;
  }

  /* My own card components, to the same rhythm as the Bootstrap ones. */
  .sfs-figure,
  .sfs-flow {
    padding: .7rem .8rem;
  }
  .sfs-figures,
  .sfs-flows,
  .sfs-group-list {
    gap: .5rem;
  }
  .sfs-group {
    margin-bottom: .9rem;
  }
  .sfs-next {
    margin-top: 1.25rem;
    padding-top: .75rem;
  }
}

/* ----------------------------------------- a table that carries on ----- *
 * Instead of a sentence telling somebody which way to scroll, the table
 * simply looks unfinished at the edge it continues past: the last column
 * fades out and a soft shadow leans in from that side.
 *
 * Why not words: "Scroll sideways for more" beside a right-pointing arrow
 * reads as an instruction to move right, but a finger goes *left* to bring
 * the right-hand columns into view. The arrow invited the one gesture that
 * does nothing.
 *
 * This says the same thing by showing it, and it needs no JavaScript at
 * all. The trick is `background-attachment`: the two white patches are
 * `local`, so they travel with the content and slide out of the way as it
 * scrolls, while the two shadows are `scroll` and stay pinned to the box.
 * At the far left the left-hand shadow is covered by its white patch; at
 * the far right the same happens on the right. In between, both show. So
 * the shadows appear and disappear on their own, exactly when there is
 * something beyond that edge.
 * --------------------------------------------------------------------- */

.table-responsive,
.table-responsive-wrap {
  background:
    /* the covers, which move with the content */
    linear-gradient(to right, #fff 30%, rgba(255, 255, 255, 0)) left center,
    linear-gradient(to left, #fff 30%, rgba(255, 255, 255, 0)) right center,
    /* the shadows, which stay with the box */
    radial-gradient(farthest-side at 0 50%,
                    rgba(58, 59, 69, .18), rgba(58, 59, 69, 0)) left center,
    radial-gradient(farthest-side at 100% 50%,
                    rgba(58, 59, 69, .18), rgba(58, 59, 69, 0)) right center;
  background-repeat: no-repeat;
  background-size: 2.5rem 100%, 2.5rem 100%, .9rem 100%, .9rem 100%;
  background-attachment: local, local, scroll, scroll;
}

/* A stacked table has no sideways scroll left, so it needs no edges. */
@media (max-width: 768px) {
  .sfs-stacked-wrap {
    background: none;
  }
}

@media print {
  .table-responsive,
  .table-responsive-wrap {
    background: none;
  }
}

/* ------------------------------------------------ the swipe, performed - *
 * A hand that does the gesture, with a trail behind it, while the table
 * moves under it. Three passes, then it goes.
 *
 * It travels LEFT, because that is the way a finger goes to bring the
 * right-hand columns into view. The earlier arrow pointed right -- at the
 * hidden columns -- and invited the one gesture that does nothing.
 *
 * At the top rather than the middle: over the heading row, where the eye
 * already is when it lands on a table, and clear of the data.
 * --------------------------------------------------------------------- */

.sfs-swipe {
  position: absolute;
  top: .45rem;
  right: 1.1rem;
  z-index: 3;
  pointer-events: none;          /* never in the way of the table itself */
  animation: sfs-swipe-run 2.2s ease-in-out 3;
}

/* The trail: a comet tail behind the hand, so the eye reads a movement
   rather than a sequence of positions. Behind means to the right, since
   the hand is travelling left. */
.sfs-swipe::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 60%;
  width: 3.2rem;
  height: .3rem;
  transform: translateY(-50%);
  border-radius: 999px;
  background: linear-gradient(to right,
              rgba(37, 99, 235, .55), rgba(37, 99, 235, 0));
  animation: sfs-swipe-tail 2.2s ease-in-out 3;
}

.sfs-swipe i {
  display: block;
  font-size: 1.5rem;
  color: #2563eb;
  /* A pale halo, so the hand reads against a busy row as well as a blank one. */
  text-shadow:
     0 0 .5rem #fff, 0 0 .5rem #fff,
     0 0 .9rem #fff, 0 0 .9rem #fff;
}

.sfs-swipe.is-done {
  opacity: 0;
  transition: opacity .35s ease;
}

@keyframes sfs-swipe-run {
  0%        { transform: translateX(0);       opacity: 0; }
  12%       { transform: translateX(0);       opacity: 1; }
  55%       { transform: translateX(-3.2rem); opacity: 1; }
  70%       { transform: translateX(-3.2rem); opacity: 0; }
  71%, 100% { transform: translateX(0);       opacity: 0; }
}

/* The tail only exists while the hand is moving: a streak behind a
   stationary hand looks like a smudge. */
@keyframes sfs-swipe-tail {
  0%, 14%   { opacity: 0; transform: translateY(-50%) scaleX(.2); }
  30%       { opacity: 1; transform: translateY(-50%) scaleX(1); }
  55%       { opacity: .8; transform: translateY(-50%) scaleX(1); }
  68%, 100% { opacity: 0; transform: translateY(-50%) scaleX(.2); }
}

@media (max-width: 768px) {
  .sfs-swipe i { font-size: 1.3rem; }
  .sfs-swipe::before { width: 2.6rem; }
}

/* Asked not to be shown moving things: the fading edges still say it. */
@media (prefers-reduced-motion: reduce) {
  .sfs-swipe { display: none; }
}

/* ------------------------------------------------ a table you can pull - *
 * The cursor is the only thing that says a table can be taken hold of, so
 * it says it: an open hand over the table, a closed one while it is being
 * pulled.
 *
 * Only where there is something to pull towards -- app.js adds the class
 * to a real scroll container, and a table that fits never gets it.
 * --------------------------------------------------------------------- */

.is-draggable {
  cursor: grab;
}
.is-draggable.is-dragging {
  cursor: grabbing;
  /* A drag would otherwise sweep a selection across every cell it passed. */
  user-select: none;
  -webkit-user-select: none;
}
.is-draggable.is-dragging * {
  cursor: grabbing !important;
}

/* Links and buttons keep their own pointer: those are still clickable,
   and a grab cursor over a button says the wrong thing. */
.is-draggable a,
.is-draggable button,
.is-draggable input,
.is-draggable select,
.is-draggable label,
.is-draggable .btn {
  cursor: pointer;
}

/* A touch screen pans natively and has no cursor to show. */
@media (hover: none) {
  .is-draggable {
    cursor: auto;
  }
}

/* ------------------------------------------------ financial statement --- */

/* The formal document a bursar prepares and a head signs. Deliberately
   plainer than the dashboards around it: this is the one page here that
   leaves the building on paper, and a printed financial statement that
   looks like a web dashboard does not get taken seriously by a bank or a
   board. Same letterhead as the report card, because both are the
   school's own paper. */

.statement {
  background: #fff;
  padding: 1.5rem;
  color: #1a1a1a;
}
.statement .school-header {
  text-align: center;
  border-bottom: 2px solid var(--sfs-brand, #4e73df);
  padding-bottom: .75rem;
  margin-bottom: 1rem;
}
.statement .school-header img {
  height: 70px;
  width: auto;
  margin-bottom: .5rem;
}
.statement .school-header h6 {
  letter-spacing: .06em;
  font-weight: 700;
}

.statement-meta {
  width: 100%;
  font-size: .78rem;
  margin-bottom: 1.5rem;
  border-collapse: collapse;
}
.statement-meta th,
.statement-meta td {
  padding: .3rem .5rem .3rem 0;
  vertical-align: top;
  text-align: left;
}
.statement-meta th {
  width: 12%;
  color: #6e707e;
  font-weight: 600;
  text-transform: uppercase;
  font-size: .66rem;
  letter-spacing: .04em;
  white-space: nowrap;
}
.statement-meta td { width: 38%; }

@media (max-width: 768px) {
  .statement {
    padding: 1rem 0.75rem;
  }
  .statement-meta,
  .statement-meta tbody,
  .statement-meta tr {
    display: block !important;
    width: 100% !important;
  }
  .statement-meta tr {
    margin-bottom: 0.75rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px dashed #e3e6f0;
  }
  .statement-meta th,
  .statement-meta td {
    display: block !important;
    width: 100% !important;
    padding: 2px 0 !important;
    text-align: left !important;
    white-space: normal !important;
    word-break: break-word;
  }
  .statement-meta th {
    font-size: 0.7rem;
    text-transform: uppercase;
    color: #858796;
    margin-top: 0.35rem;
    letter-spacing: 0.05em;
  }
  .statement-meta td {
    font-size: 0.82rem;
    font-weight: 500;
    color: #3a3b45;
  }
}

.statement-section {
  font-weight: 700;
  margin: 1.5rem 0 .5rem;
  padding-bottom: .25rem;
  border-bottom: 1px solid #d1d3e2;
}

.statement-table { font-size: .82rem; }
.statement-table thead th {
  font-size: .66rem;
  letter-spacing: .04em;
  color: #6e707e;
  border-top: none;
  border-bottom: 1px solid #d1d3e2;
}
/* Figures line up on the decimal point when they are all the same width,
   which is what makes a column of money readable at a glance. */
.statement-table td.text-right,
.statement-table th.text-right {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.statement-subtotal th { border-top: 1px solid #d1d3e2; }
.statement-total th {
  border-top: 2px solid #5a5c69;
  border-bottom: 2px solid #5a5c69;
  font-weight: 700;
}
.statement-result {
  margin-top: 1.25rem;
  font-size: .95rem;
}
.statement-result th {
  border-top: 2px solid #5a5c69;
  border-bottom: 3px double #5a5c69;
  padding: .6rem 0;
}

.statement-notes {
  font-size: .78rem;
  color: #3a3b45;
  padding-left: 1.1rem;
  margin-bottom: 0;
}
.statement-notes li { margin-bottom: .4rem; }

.statement-signatures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1.5rem;
  margin-top: 3rem;
}
.statement-sign { text-align: center; }
.statement-sign-line {
  border-bottom: 1px solid #5a5c69;
  height: 2.5rem;
  margin-bottom: .35rem;
}
.statement-sign-label {
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
}
.statement-sign-name {
  font-size: .72rem;
  color: #6e707e;
}
.statement-stamp {
  height: 70px;
  width: auto;
  margin-bottom: .35rem;
}

.statement-foot {
  margin-top: 2rem;
  padding-top: .5rem;
  border-top: 1px solid #e3e6f0;
  font-size: .66rem;
  color: #858796;
  text-align: center;
}

@media print {
  /* A4 with a real margin: this one is filed and sometimes hole-punched,
     unlike the id cards where the paper is the cost that matters. */
  @page { margin: 14mm; }
  .statement { padding: 0; }
  .statement .school-header,
  .statement-total th,
  .statement-result th,
  .statement-sign-line {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  /* A section must not begin at the foot of a page with its table
     overleaf, and the signatures must not land on a page of their own. */
  .statement-section { break-after: avoid; page-break-after: avoid; }
  .statement-table { break-inside: auto; }
  .statement-table tr { break-inside: avoid; page-break-inside: avoid; }
  .statement-signatures { break-inside: avoid; page-break-inside: avoid; }
  .statement-result { break-inside: avoid; page-break-inside: avoid; }
  /* The whole point of a wide table is lost if it scrolls on paper. */
  .statement .table-responsive-wrap { overflow: visible !important; }
}

/* -------------------------------------------------- the purple button --- */

/* A purple button, for actions worth reading twice before pressing.

   Its own class rather than a repainted `btn-warning`, so the markup
   says what it is. Written the way Bootstrap writes its own buttons --
   every state, including the ones nobody looks at until a keyboard user
   tabs onto one -- because a button that loses its colour on focus or
   goes grey when disabled is worse than one that was never styled.

   `#6f42c1` is the purple already in this palette (`--purple`, and the
   marks tile on the dashboard), so these match something rather than
   introducing a second purple.

   It reads better than the yellow it replaces, which is worth recording:
   white on `#f6c23e` measured 1.65:1, well under the 4.5:1 that normal
   text needs, so those labels were genuinely hard to read. White on this
   purple is 6.51:1, and 8.16:1 and 8.75:1 on the hover and active
   states. */

.btn-purple,
.btn-purple.disabled,
.btn-purple:disabled {
  color: #fff;
  background-color: #6f42c1;
  border-color: #6f42c1;
}

.btn-purple:hover {
  color: #fff;
  background-color: #5e37a6;
  border-color: #59359a;
}

.btn-purple:focus,
.btn-purple.focus,
.btn-purple:not(:disabled):not(.disabled):active:focus,
.btn-purple:not(:disabled):not(.disabled).active:focus,
.show > .btn-purple.dropdown-toggle:focus {
  color: #fff;
  background-color: #5e37a6;
  border-color: #59359a;
  box-shadow: 0 0 0 0.2rem rgba(133, 94, 202, 0.5);
}

.btn-purple:not(:disabled):not(.disabled):active,
.btn-purple:not(:disabled):not(.disabled).active,
.show > .btn-purple.dropdown-toggle {
  color: #fff;
  background-color: #59359a;
  border-color: #533291;
}

.btn-outline-purple {
  color: #6f42c1;
  border-color: #6f42c1;
  background-color: transparent;
}

.btn-outline-purple:hover,
.btn-outline-purple:not(:disabled):not(.disabled):active,
.btn-outline-purple:not(:disabled):not(.disabled).active,
.show > .btn-outline-purple.dropdown-toggle {
  color: #fff;
  background-color: #6f42c1;
  border-color: #6f42c1;
}

.btn-outline-purple:focus,
.btn-outline-purple.focus {
  box-shadow: 0 0 0 0.2rem rgba(133, 94, 202, 0.5);
}

.btn-outline-purple.disabled,
.btn-outline-purple:disabled {
  color: #6f42c1;
  background-color: transparent;
}

@media print {
  /* Printed buttons are rare but the ink should match the screen. */
  .btn-purple {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  .border-left-primary::before,
  .border-left-primary::after,
  .border-left-secondary::before,
  .border-left-secondary::after,
  .border-left-success::before,
  .border-left-success::after,
  .border-left-info::before,
  .border-left-info::after,
  .border-left-warning::before,
  .border-left-warning::after,
  .border-left-danger::before,
  .border-left-danger::after,
  .border-left-light::before,
  .border-left-light::after,
  .border-left-dark::before,
  .border-left-dark::after,
  .sfs-task::before,
  .sfs-task::after,
  .sfs-kid::before,
  .sfs-kid::after,
  .integrity-row::before,
  .integrity-row::after,
  .cbt-side__block--warn::before,
  .cbt-side__block--warn::after,
  .directive-hero-card::before,
  .directive-hero-card::after {
    display: none !important;
  }
}

/* ------------------------------------------------------------ timetable --
   A timetable is read by scanning a column, not by reading sentences, so
   the grid lines matter more here than anywhere else in the system and the
   subject has to be the thing the eye lands on. The letterhead is almost
   invisible on screen and becomes the top of an official sheet on paper. */

/* The timetable borrows the report card's document shell -- .report-card and
   its .school-header -- so the two sheets are the same object in the reader's
   hands. Only the grid itself is particular to a timetable. */

.tt-grid {
  table-layout: fixed;
  min-width: 750px;
}
.tt-grid th, .tt-grid td { vertical-align: middle; }
.tt-grid thead th {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  white-space: nowrap;
}
.tt-daycol {
  width: 7.5rem;
  white-space: nowrap;
  background: #f8f9fc;
  font-size: .8rem;
}
.tt-grid td { padding: .5rem; }
.tt-subject {
  display: block;
  font-weight: 700;
  font-size: .82rem;
  color: #3a3b45;
}
.tt-venue {
  display: block;
  font-size: .7rem;
  color: #858796;
}
.tt-teacher {
  display: block;
  font-size: .72rem;
  color: #4e73df;
  font-weight: 500;
}
.tt-presets {
  gap: .25rem;
}
.tt-preset-btn {
  font-size: .75rem;
  padding: .2rem .45rem;
}
.tt-free { background: #fcfcfd; }
.tt-nothing { color: #d1d3e2; }
.tt-clash {
  background: #fdeced;
  outline: 2px solid #e74a3b;
  outline-offset: -2px;
}
/* Shown, not hidden behind a hover. A control that only appears when the
   pointer is already on it cannot be found by anyone looking for it, and is
   unreachable entirely on a touch screen -- which is what a head teacher
   editing the timetable on a tablet is using. */
.tt-tools {
  display: inline-flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: .25rem;
  margin-top: .25rem;
  white-space: nowrap;
}
/* The delete button needs a POST, so it has to be wrapped in a form, and a
   form is a block. It was wearing Bootstrap's `d-inline`, whose `!important`
   put the layout of this row in another stylesheet's hands; it is set here
   instead so the two controls sit on one line because this file says so. */
.tt-tool-form {
  display: inline-flex;
  margin: 0;
}
.tt-tools > *, .tt-tool-form > .tt-btn { flex: 0 0 auto; }
.tt-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15rem;
  height: 1.15rem;
  padding: 0;
  border: 0;
  border-radius: .15rem;
  background: #2b2b2b;
  color: #fff;
  font-size: .6rem;
  line-height: 1;
}
.tt-btn:hover, .tt-btn:focus {
  background: #000;
  color: #fff;
  text-decoration: none;
}
.tt-btn-remove:hover, .tt-btn-remove:focus { background: #c0392b; }
.tt-footnote {
  font-size: .72rem;
  color: #858796;
}

@media print {
  /* The timetable is the report card's twin: the same white document shell,
     the same centred school header, the same bordered table. So almost
     nothing is print-only here -- what you see is what comes out, which is
     the property that makes a document trustworthy and was exactly what the
     earlier print-only rewrite gave up.

     No @page rule lives here. It takes no selector, so landscape belongs to
     the timetable template itself or it would turn every printed document
     in the system on its side. */

  /* One class to a page. */
  .tt-sheet { break-inside: avoid; break-after: page; }
  .tt-sheet:last-of-type { break-after: auto; }

  /* The screen's filter, counts and banners carry `no-print`; this is the
     page heading and its buttons, and a flash message, which do not. */
  body.timetable-sheet .container-fluid > .d-sm-flex,
  body.timetable-sheet > .alert,
  body.timetable-sheet .container-fluid > .alert {
    display: none !important;
  }

  .tt-grid { min-width: 0 !important; width: 100% !important; }
  .tt-grid tbody tr { break-inside: avoid; }
  .table-responsive-wrap { overflow: visible !important; background: none !important; }
  .tt-tools { display: none !important; }
}

/* ------------------------------------------------------- exam mode --- */
/* The shell a candidate sees. Full width on purpose: a question and its
   options inside a centred column on a hall monitor is a narrow strip of
   text with two hands of empty screen either side, and the thing being
   read is the one thing that should have the room. */

.cbt-hall {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: .7rem 1.25rem;
  background: var(--sfs-brand, #1e2600);
  color: #fff;
  border-bottom: 3px solid var(--sfs-on-brand-muted, #c8d400);
}
.cbt-hall__school {
  display: flex;
  align-items: center;
  gap: .7rem;
  min-width: 0;
}
.cbt-hall__crest {
  height: 42px;
  width: 42px;
  object-fit: cover;
  border-radius: 50%;
  background: #fff;
  flex: 0 0 auto;
}
.cbt-hall__name {
  font-weight: 800;
  font-size: 1rem;
  line-height: 1.15;
  letter-spacing: .01em;
}
.cbt-hall__mode {
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .18em;
  color: var(--sfs-on-brand-muted, #c8d400);
}
.cbt-hall__candidate { text-align: right; }
.cbt-hall__who { font-weight: 700; font-size: .9rem; }
.cbt-hall__meta { font-size: .72rem; opacity: .75; }

.cbt-hall__body {
  width: 100%;
  max-width: none;
  padding: 1.25rem;
}
.cbt-hall__foot {
  padding: .75rem 1.25rem 1.5rem;
  text-align: center;
  font-size: .72rem;
  color: #858796;
}

@media (max-width: 575.98px) {
  .cbt-hall { padding: .6rem .9rem; }
  .cbt-hall__candidate { text-align: left; }
  .cbt-hall__name { font-size: .88rem; }
  .cbt-hall__body { padding: .9rem; }
}

/* ------------------------------------------------- chrome above content --- */
/* The bar and its menus belong on top of the page, whatever the page is
   made of. Stated once here rather than discovered again the next time a
   screen introduces something positioned. */

.topbar {
  position: relative;
  z-index: 1020;
}
.topbar .dropdown-menu {
  z-index: 1045;
}

/* ============================================================== theme === */
/* The school's own colour, applied to the parts of the theme that come
   from sb-admin-2 rather than from this file. Overridden here rather than
   edited there: the vendor stylesheet is something we replace wholesale
   when it is upgraded, and a colour hand-edited into it is a change that
   quietly disappears the day somebody does.

   Everything reads --sfs-brand, which partials/theme.html sets from one
   admin field. The fallbacks are the blue it shipped with, so a page that
   somehow loses the variables still looks like itself. */

/* ---- the menu ---- */
.sidebar.bg-gradient-dark,
.sidebar.bg-gradient-primary {
  background-color: var(--sfs-brand, #4e73df) !important;
  background-image: linear-gradient(
      180deg,
      var(--sfs-brand, #4e73df) 10%,
      var(--sfs-brand-dark, #3c59ad) 100%) !important;
}
.sidebar .nav-item .nav-link,
.sidebar .sidebar-brand-text,
.sidebar .nav-item .nav-link i {
  color: var(--sfs-on-brand-muted, rgba(255, 255, 255, .78)) !important;
}
.sidebar .nav-item.active .nav-link,
.sidebar .nav-item .nav-link:hover,
.sidebar .nav-item .nav-link:hover i,
.sidebar .nav-item.active .nav-link i {
  color: var(--sfs-on-brand, #fff) !important;
}
.sidebar .nav-item.active .nav-link {
  background: rgba(255, 255, 255, .13);
}
.sidebar hr.sidebar-divider {
  border-top-color: rgba(255, 255, 255, .2);
}
/* The submenu that drops out of a section keeps the brand on its items. */
.sidebar .collapse-inner .collapse-item.active,
.sidebar .collapse-inner .collapse-item:hover {
  background: var(--sfs-brand-tint, #eaeffb);
  color: var(--sfs-brand-dark, #2e59d9) !important;
}

/* ---- the buttons ---- */
.btn-primary {
  background-color: var(--sfs-brand, #4e73df);
  border-color: var(--sfs-brand, #4e73df);
  color: var(--sfs-on-brand, #fff);
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active,
.btn-primary.active,
.btn-primary:not(:disabled):not(.disabled):active {
  background-color: var(--sfs-brand-dark, #2e59d9);
  border-color: var(--sfs-brand-dark, #2e59d9);
  color: var(--sfs-on-brand, #fff);
}
.btn-primary:focus,
.btn-primary.focus,
.btn-primary:not(:disabled):not(.disabled):active:focus {
  box-shadow: 0 0 0 .2rem var(--sfs-brand-tint, #eaeffb);
}
.btn-outline-primary {
  color: var(--sfs-brand, #4e73df);
  border-color: var(--sfs-brand, #4e73df);
}
.btn-outline-primary:hover,
.btn-outline-primary:not(:disabled):not(.disabled):active {
  background-color: var(--sfs-brand, #4e73df);
  border-color: var(--sfs-brand, #4e73df);
  color: var(--sfs-on-brand, #fff);
}
.btn-link { color: var(--sfs-brand, #4e73df); }
.btn-link:hover { color: var(--sfs-brand-dark, #2e59d9); }

/* ---- the rest of what reads as "the theme" ---- */
.text-primary { color: var(--sfs-brand, #4e73df) !important; }
.bg-primary { background-color: var(--sfs-brand, #4e73df) !important; }
.border-left-primary {
  --card-corner-accent: var(--sfs-brand, #4e73df);
  --card-top-hairline: var(--sfs-brand-tint, rgba(78, 115, 223, 0.4));
}
.badge-primary {
  background-color: var(--sfs-brand, #4e73df);
  color: var(--sfs-on-brand, #fff);
}
a { color: var(--sfs-brand, #4e73df); }
a:hover { color: var(--sfs-brand-dark, #2e59d9); }
.page-item.active .page-link {
  background-color: var(--sfs-brand, #4e73df);
  border-color: var(--sfs-brand, #4e73df);
  color: var(--sfs-on-brand, #fff);
}
.page-link { color: var(--sfs-brand, #4e73df); }
.form-control:focus {
  border-color: var(--sfs-brand, #4e73df);
  box-shadow: 0 0 0 .2rem var(--sfs-brand-tint, #eaeffb);
}
/* ------------------------------------------------------------------ *
 * Prevent double-rendered radio buttons and checkboxes.
 *
 * When native <input> elements sit inside Bootstrap 4 .custom-control
 * containers (.custom-radio, .custom-checkbox, .custom-switch), Bootstrap
 * renders its own styled pseudo-elements (::before / ::after) and relies
 * on .custom-control-input to visually hide the browser's native control.
 * If the class is omitted or mistakenly set to form-check-input, both
 * the native browser control and the custom drawn one render directly
 * on top of each other.
 *
 * These rules guarantee that ANY radio or checkbox inside .custom-control
 * is completely hidden and accessible via label clicks, and properly
 * drives the custom indicator even if custom-control-input class is absent.
 * ------------------------------------------------------------------ */
.custom-control > input[type="radio"],
.custom-control > input[type="checkbox"],
.custom-control-input {
  position: absolute !important;
  left: 0 !important;
  top: 0 !important;
  z-index: -1 !important;
  width: 1rem !important;
  height: 1.25rem !important;
  opacity: 0 !important;
  pointer-events: none !important;
}

.custom-control > input[type="radio"]:checked ~ .custom-control-label::before,
.custom-control > input[type="checkbox"]:checked ~ .custom-control-label::before,
.custom-control-input:checked ~ .custom-control-label::before {
  color: #fff !important;
  background-color: var(--sfs-brand, #4e73df) !important;
  border-color: var(--sfs-brand, #4e73df) !important;
}

.custom-radio > input[type="radio"]:checked ~ .custom-control-label::after,
.custom-radio .custom-control-input:checked ~ .custom-control-label::after {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e") !important;
}

.custom-checkbox > input[type="checkbox"]:checked ~ .custom-control-label::after,
.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e") !important;
}
.nav-pills .nav-link.active {
  background-color: var(--sfs-brand, #4e73df);
  color: var(--sfs-on-brand, #fff);
}

/* ---- choosing the colour ---- */
.sfs-theme-pick {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  flex-wrap: wrap;
}
.sfs-theme-field { flex: 0 0 11rem; }
.sfs-colour-well {
  height: calc(1.5em + .75rem + 2px);
  padding: .15rem .25rem;
  cursor: pointer;
}
.sfs-theme-swatches {
  display: flex;
  gap: .4rem;
  flex-wrap: wrap;
  padding-bottom: 1rem;
}
.sfs-swatch {
  width: 1.9rem;
  height: 1.9rem;
  border: 2px solid #fff;
  border-radius: .35rem;
  box-shadow: 0 0 0 1px #d1d3e2;
  padding: 0;
  cursor: pointer;
}
.sfs-swatch:hover, .sfs-swatch:focus {
  box-shadow: 0 0 0 2px #858796;
  outline: none;
}

/* ------------------------------------------------- a menu you can reach --- */
/* The menu grows with what is open in it, and the page grows with the
   menu. Opening a section near the bottom makes the page taller and you
   scroll the page, which is what it has always done and what everything
   else on a page does.

   It briefly scrolled inside itself instead, with sections hanging over
   the ones above them when there was no room below. That keeps the menu
   still, and costs you sight of the items it covers - a worse trade on a
   menu whose whole job is to show you what there is. */

/* And the flyout cannot be taller than the window it appears in. */
.sidebar .nav-item .collapse,
.sidebar .nav-item .collapsing {
  max-height: calc(100vh - 1rem);
  overflow-y: auto;
}
.sidebar .nav-item .collapse .collapse-inner {
  max-height: none;
}

/* ---- a menu that had nowhere to go down ---- */
/* Set by app.js, which is the only thing that can know: whether a menu
   fits depends on where its heading happens to be, how tall the menu
   happens to be and how tall the window happens to be, and CSS cannot ask
   about any of the three. These rules only say what "upward" means. */

/* Only the icon-only menu flips. There the submenu really does float
   beside its heading and cannot make the page any taller, so running off
   the bottom leaves nothing to scroll to. In the expanded menu it is in
   the flow, where running on simply makes the page longer. */
.sidebar.toggled .nav-item .collapse.sfs-drops-up {
  top: auto;
  bottom: 0;
}
.dropdown-menu.sfs-drops-up {
  top: auto;
  bottom: 100%;
  margin-top: 0;
  margin-bottom: .125rem;
}
/* Whichever way it went, it still cannot be taller than the window. */
.dropdown-menu {
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
}

/* ------------------------------------------------- the transfer clock --- */
/* Big enough to read without leaning in, and amber rather than grey: this
   is the one thing on the page with a deadline on it. */

.sfs-clock {
  border: 1px solid #f0c36d;
  background: #fff9ec;
  border-radius: .5rem;
  padding: .8rem .9rem;
  margin-bottom: 1rem;
  text-align: left;
}
.sfs-clock__face {
  font-size: 1.15rem;
  font-weight: 800;
  color: #8a6100;
  line-height: 1.2;
}
.sfs-clock__note {
  font-size: .82rem;
  color: #6b5420;
  margin-top: .35rem;
  line-height: 1.45;
}
/* Under five minutes it stops being information and becomes a warning. */
.sfs-clock.is-urgent {
  border-color: #e74a3b;
  background: #fdecea;
}
.sfs-clock.is-urgent .sfs-clock__face { color: #a32318; }
.sfs-clock.is-urgent .sfs-clock__note { color: #7a2a22; }
.sfs-clock.is-over {
  border-color: #b7b9cc;
  background: #f1f1f4;
}
.sfs-clock.is-over .sfs-clock__face { color: #4b4d58; }

/* --------------------------------------------- the transfer, on a phone --- */
/* Sized so the amount, the account number and the green button all land on
   one screen. The amount was `display-4` - 3.5rem, most of a phone's first
   screen spent on a figure the person already knows, pushing the button
   they need to press out of sight. */

.sfs-pay-amount {
  font-size: 2.1rem;
  font-weight: 800;
  color: #3a3b45;
  line-height: 1.1;
  margin-bottom: .6rem;
}
.sfs-pay-account {
  font-size: 1.75rem;
  font-weight: 800;
  letter-spacing: .08em;
  line-height: 1.15;
  margin-bottom: .4rem;
  color: #3a3b45;
}
.sfs-clock--tight {
  padding: .45rem .6rem;
  margin-bottom: .6rem;
  text-align: center;
}
.sfs-clock--tight .sfs-clock__face { font-size: .95rem; }

@media (max-width: 575.98px) {
  .sfs-pay-amount { font-size: 1.9rem; }
  .sfs-pay-account { font-size: 1.6rem; }
}

/* The one thing on the page that must never be out of reach. */
@media (max-width: 767.98px) {
  .sfs-pay-act {
    position: sticky;
    bottom: 0;
    z-index: 5;
    background: #fff;
    padding: .6rem 0 .5rem;
    margin: 0 -.5rem;
    box-shadow: 0 -0.35rem .9rem rgba(255, 255, 255, .95);
  }
}

/* The notice that replaces an expired account number. Red, because the
   mistake it prevents costs real money. */
.sfs-dead {
  border: 1px solid #e74a3b;
  background: #fdecea;
  border-radius: .5rem;
  padding: .8rem .9rem;
  margin-bottom: 1rem;
  text-align: left;
}
.sfs-dead__title {
  font-size: 1.05rem;
  font-weight: 800;
  color: #a32318;
  line-height: 1.25;
  margin-bottom: .35rem;
}
.sfs-dead__note {
  font-size: .85rem;
  color: #7a2a22;
  line-height: 1.5;
}

/* The sentences either side of the ticking one. Quieter than it, because
   they never change and only have to be read once. */
.sfs-clock__intro,
.sfs-clock__after {
  font-size: .78rem;
  line-height: 1.4;
  color: #6b5420;
  margin: 0;
}
.sfs-clock__intro { margin-bottom: .2rem; }
.sfs-clock__after { margin-top: .2rem; }
.sfs-clock.is-urgent .sfs-clock__intro,
.sfs-clock.is-urgent .sfs-clock__after { color: #7a2a22; }
.sfs-clock.is-over .sfs-clock__intro,
.sfs-clock.is-over .sfs-clock__after { color: #4b4d58; }
.sfs-clock--tight { text-align: left; }

/* The turning arrow on the "getting you a new one" screen. */
.sfs-spin { animation: sfs-spin 1.4s linear infinite; }
@keyframes sfs-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .sfs-spin { animation: none; }
}

/* -------------------------------------------- the please-wait screen --- */
/* Obviously busy, and obviously counting. A page that looks stopped is a
   page somebody presses back on, and pressing back here starts the payment
   over - which is the thing this screen exists to spare them. */

.sfs-waiting {
  position: relative;
  width: 5.5rem;
  height: 5.5rem;
  margin: 0 auto 1rem;
}
.sfs-waiting__ring {
  position: absolute;
  inset: 0;
  border: .35rem solid var(--sfs-brand-tint, #eaeffb);
  border-top-color: var(--sfs-brand, #4e73df);
  border-radius: 50%;
  animation: sfs-spin 1s linear infinite;
}
.sfs-waiting__count {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.1rem;
  font-weight: 800;
  color: var(--sfs-brand, #4e73df);
}
.sfs-waiting__say {
  font-size: 1.05rem;
  color: #3a3b45;
}
@media (prefers-reduced-motion: reduce) {
  .sfs-waiting__ring { animation: none; border-top-color: var(--sfs-brand, #4e73df); }
}

/* Sidebar Brand Header - balanced logo and role title */
.sidebar .sidebar-brand {
  height: 4.375rem;
  padding: 0.75rem 1rem !important;
  text-transform: none !important;
}

.sidebar .sidebar-brand .sidebar-brand-icon {
  display: flex;
  align-items: center;
  justify-content: center;
}

.sidebar .sidebar-brand .sidebar-brand-icon img,
.sidebar .sidebar-brand img {
  width: 36px !important;
  height: 36px !important;
  min-width: 36px !important;
  min-height: 36px !important;
  max-width: 36px !important;
  max-height: 36px !important;
  object-fit: cover !important;
  border-radius: 50% !important;
}

.sidebar .sidebar-brand .sidebar-brand-text,
.sidebar .sidebar-brand-text {
  font-size: 0.85rem !important;
  font-weight: 700 !important;
  text-transform: none !important;
  letter-spacing: normal !important;
}

.sidebar .d-flex #sidebarToggle {
  margin-bottom: 0 !important;
}

/* Anti-double-submit and button spinner */
[data-is-spinning="true"],
.btn[data-is-spinning="true"] {
  cursor: not-allowed !important;
  opacity: 0.85 !important;
  pointer-events: none !important;
}
[data-is-spinning="true"] i.fa-spinner {
  display: inline-block;
  animation: sfs-spin 0.85s linear infinite;
}
/* Suppress SB Admin 2's automatic pseudo-element chevron (\f107 / \f105) to eliminate duplicate arrows */
.faq-card .card-header[data-toggle="collapse"]::after,
.faq-card .card-header.collapsed::after,
.faq-card .card-header::after,
.card-header.faq-header[data-toggle="collapse"]::after,
.card-header.faq-header.collapsed::after,
.card-header.faq-header::after {
  display: none !important;
  content: none !important;
}

.card .card-header.faq-header[data-toggle="collapse"] {
  padding-right: 1.25rem !important;
}

