/* ============================================================================
   LIBESOLE — PAGE STYLES

   The fourth and last stylesheet. tokens.css holds the values, base.css the raw
   elements, components.css the reusable pieces, layout.css the frame — and this
   file holds the rest: how the blocks of a page are spaced, a small set of
   named helpers, and the styles that belong to one screen and nowhere else.

   Two rules this file exists to keep:

     1. No colour is written here. Every colour is a token.  [KICKOFF 6.2]
     2. No style attribute is written in the HTML or in a page script. If a
        thing needs styling it gets a name, and the name is defined here.
        A test enforces this — see server/tests/webPages.test.js.
   ============================================================================ */


/* ----------------------------------------------------------------------------
   1. HOW A PAGE FLOWS

   Every top-level block in a page is separated by the same gap, so no page has
   to remember its own spacing and no two pages drift apart.
   -------------------------------------------------------------------------- */

.content > * { margin-bottom: var(--space-6); }
.content > *:last-child { margin-bottom: 0; }

/* Blocks that carry no height of their own must not leave a hole behind. */
.content > .hidden,
.content > div:empty { margin-bottom: 0; }

/* A run of cards or rows that belong together, closer than the page gap. */
.stack-tight > * + * { margin-top: var(--space-3); }


/* ----------------------------------------------------------------------------
   2. TRUNCATION

   A flex child will not shrink below its content unless it is told it may, and
   a long school name or a long parent name would otherwise push the sidebar
   out of shape.
   -------------------------------------------------------------------------- */

.brand > div,
.sidebar-user > div,
.card-head > div,
.row-between > div { min-width: 0; }


/* ----------------------------------------------------------------------------
   3. NAMED HELPERS

   Small, deliberately few. Each one earned its place by appearing on several
   pages; anything used once is written in that page's own section below.
   -------------------------------------------------------------------------- */

/* A form or a block of settings, kept to a comfortable reading width rather
   than stretched across a desktop monitor. */
.column      { max-width: 680px; }
.column-sm   { max-width: 480px; }
.text-column { max-width: 52ch; }

/* A control that sits inside a row of controls and should size to its content
   instead of filling the row. */
.field-inline { width: auto; }
.field-wide   { width: auto; min-width: 190px; }

/* A grey inset panel — a summary, a preview, a quiet aside. */
.panel {
  padding: var(--space-4);
  background: var(--surface-sunk);
  border-radius: var(--radius);
}
.panel-outline {
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

/* A list of rows divided by hairlines rather than boxed in a table. */
.ruled > * {
  padding: var(--space-3) 0;
  border-top: 1px solid var(--line);
}
.ruled > *:first-child { border-top: 0; }

/* A footnote or total sitting under a block it belongs to. */
.ruled-top {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

/* The one number that matters, at two smaller sizes than the default. */
.stat-value.is-md { font-size: var(--text-xl); }
.stat-value.is-sm { font-size: var(--text-lg); }

/* Text that was typed by a person, with their line breaks kept. */
.typed { white-space: pre-wrap; margin: 0; }

/* The grey bar that stands in for a line of text while it loads. */
.loading-bar { height: 14px; width: 180px; margin: 0 auto 10px; }

/* A table that fills its card edge to edge, with the card drawing the border. */
.table-flush { border: 0; border-radius: 0; }


/* ----------------------------------------------------------------------------
   4. ATTENDANCE REGISTER            pages/teacher/attendance.html

   Four radio buttons per student, one per status. They are the whole interface,
   so they are centred in their columns and given a real target to hit.
   -------------------------------------------------------------------------- */

.register-choice {
  justify-content: center;
  min-height: 36px;
  padding: 0;
  border: 0;
  background: none;
}
.register-choice input { margin: 0; }

/* The staff register's optional note. Seven columns is more than a 360px
   screen has room for, and left to itself the note box collapses to about
   34px — below the 44px a finger can reliably hit. The table already scrolls
   sideways, so giving the box a floor costs nothing and keeps it tappable. */
/* Wide enough to read a real note in.
   160px fitted "Optional" and nothing else: a remark somebody actually typed —
   "Left after the fourth period" — was cut off inside the box with no way to
   see the rest without clicking into it. The register scrolls sideways on a
   phone anyway, so the column can have the room. */
.note-input { min-width: 248px; }

/* A register that has been saved. The marks stay readable — a teacher still
   needs to see what was recorded — but the rows are visibly no longer
   something you change, and the radios refuse the tap rather than moving and
   then being rejected on save. */
.is-locked { opacity: 0.72; }
.is-locked .choice { cursor: not-allowed; }
.is-locked input { pointer-events: none; }

/* 36px keeps a class of forty on one screen under a mouse. Under a thumb it is
   too small to hit reliably, so on a touch device or a narrow screen the row
   grows to the full 44px and the register simply gets longer.  [KICKOFF 6.4]

   This override lives here rather than in layout.css because pages.css is the
   last stylesheet loaded — a media query adds no specificity, so the rule above
   would otherwise win no matter which file the override sat in. */
@media (pointer: coarse), (max-width: 860px) {
  /* Square, not just tall: at 360px the four status columns squeeze to 28px
     wide, so the target has to be widened as well as heightened. The register
     already scrolls sideways inside its own container and the student's name
     is pinned to the left, so the extra width costs nothing that matters. */
  .register-choice { min-height: var(--touch); min-width: var(--touch); }
}


/* ----------------------------------------------------------------------------
   5. MARKS SHEET                    pages/teacher/marks.html

   This screen is typed, not clicked: the mark boxes are narrow, sit in a
   column of their own, and Enter moves to the next student.
   -------------------------------------------------------------------------- */

.marks-input {
  min-height: 40px;
  padding: var(--space-2) var(--space-3);
}
.marks-col { width: 140px; }

/* Same reasoning as the register above: compact for typing with a keyboard,
   full size for a thumb. */
@media (pointer: coarse), (max-width: 860px) {
  .marks-input { min-height: var(--touch); }
}


/* ----------------------------------------------------------------------------
   6. HOMEWORK COMPOSER              pages/teacher/homework.html

   Each question is a card inside the form, tinted so the questions read as a
   list within the page rather than as more of the form around them.
   -------------------------------------------------------------------------- */

.question-row { background: var(--surface-alt); }
.question-marks { width: 90px; }


/* ----------------------------------------------------------------------------
   7. FEES                           pages/parent/fees.html

   The four payment choices are buttons that look like cards, because that is
   what they are: a parent is choosing an amount, not filling in a form.
   [KICKOFF 11.3]
   -------------------------------------------------------------------------- */

/* One month of the year, as a row a thumb can hit: the tick, the month, and
   what it costs. The row's state paints it — paid green, overdue red, ticked
   electric blue — and the vacation months are on the list without a tick. */
.fee-row {
  display: flex; align-items: center; gap: var(--space-3);
  min-height: 60px; padding: var(--space-3);
  border: 1px solid var(--line); border-left: 4px solid var(--line);
  border-radius: var(--radius); background: var(--surface);
  margin-bottom: var(--space-2);
  cursor: pointer;
}
.fee-row[data-state="vacation"] { cursor: default; opacity: 0.72; background: var(--surface-sunk); }
.fee-row[data-state="paid"] { cursor: default; border-left-color: var(--success); }
.fee-row[data-state="partial"] { border-left-color: var(--warning); }
.fee-row[data-state="overdue"] { border-left-color: var(--danger); }
.fee-row[data-state="upcoming"] { border-left-color: var(--accent-cyan); }
.fee-row[data-state="selected"] {
  border-color: var(--primary); border-left-color: var(--primary);
  background: var(--primary-soft); box-shadow: var(--primary-glow);
}
/* The month to pay next, before anything is ticked. */
.fee-row.fee-next { border-color: var(--accent-cyan); }

.fee-tick { width: 28px; display: grid; place-items: center; flex-shrink: 0; }
.fee-tick input { width: 22px; height: 22px; min-height: 0; accent-color: var(--primary); cursor: pointer; }
.fee-paid-mark { color: var(--success-ink); font-weight: var(--weight-bold); }
.fee-main { flex: 1; min-width: 0; }
.fee-month { font-weight: var(--weight-semi); }
.fee-right { text-align: right; display: grid; gap: 2px; justify-items: end; }
.fee-amount { font-weight: var(--weight-semi); font-variant-numeric: tabular-nums; }
.fee-status { font-size: var(--text-xs); color: var(--ink-muted); }
/* The status wears the INK of its colour, not the fill: --success is mixed to
   sit behind white text on a button, and at 12px on a card it falls under
   WCAG AA. The -ink tokens are the same colours graded for reading. */
.fee-row[data-state="paid"] .fee-status { color: var(--success-ink); }
.fee-row[data-state="overdue"] .fee-status { color: var(--danger-ink); }
.fee-row[data-state="partial"] .fee-status { color: var(--warning-ink); }
.fee-row[data-state="selected"] .fee-status { color: var(--primary); }

/* How much of the year is paid. */
.fee-progress { height: 8px; border-radius: var(--radius-full); background: var(--surface-sunk); overflow: hidden; }
.fee-progress > span { display: block; height: 100%; background: var(--success); border-radius: inherit; }

.fee-selected { padding: var(--space-2) 0; border-bottom: 1px solid var(--line); }
.fee-total { padding-top: var(--space-3); }


/* ----------------------------------------------------------------------------
   9. PROFILE                        pages/shared/profile.html
   -------------------------------------------------------------------------- */

.avatar-lg {
  width: 56px;
  height: 56px;
  font-size: var(--text-md);
}

/* The Profile photo section: the photo large, what it is beside it. */
.avatar-xl {
  width: 112px;
  height: 112px;
  font-size: var(--text-xl);
}
.profile-photo {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  flex-wrap: wrap;
}
/* The words take the rest of the row, and drop below the photo only on a phone. */
.profile-photo > div:last-child { flex: 1 1 220px; min-width: 0; }


/* ----------------------------------------------------------------------------
   10. A BUTTON THAT IS NOT A BUTTON

   Used where a whole row or tile is clickable and the visible styling comes
   from what is inside it.
   -------------------------------------------------------------------------- */

.bare-button {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: inherit;
  cursor: pointer;
}


/* ----------------------------------------------------------------------------
   11. SPACING AND WIDTH HELPERS

   The few that earned a name by turning up on page after page. Everything here
   is a token — no raw pixel value, so a change to the spacing scale moves the
   whole product together.
   -------------------------------------------------------------------------- */

.spaced      { margin-top: var(--space-4); }
.spaced-sm   { margin-top: var(--space-2); }
.below       { margin-bottom: var(--space-5); }
.below-sm    { margin-bottom: var(--space-3); }
.below-xs    { margin-bottom: var(--space-1); }
.beside      { margin-left: var(--space-2); }

.roomy       { padding: var(--space-8); }
.flush       { padding: 0; }

/* Columns that must not collapse below a readable width inside a flex row. */
.col-96      { min-width: 96px; }
.col-150     { min-width: 150px; }
.col-220     { max-width: 220px; }
.col-320     { max-width: 320px; }

/* A link that carries its own styling — a card, a tile, a row. */
.plain-link  { text-decoration: none; color: inherit; }
.plain-link:hover { text-decoration: none; }

/* One row in a hairline-ruled list. */
.ruled-row {
  padding: var(--space-3) 0;
  border-top: 1px solid var(--line);
}
.ruled-row:first-child { border-top: 0; }

/* The one thing on a page that should catch the eye first. */
.highlight {
  border-color: var(--highlight-edge);
  background: var(--primary-soft);
}


/* ----------------------------------------------------------------------------
   13. TONE ON A NUMBER OR A WORD

   Colour is never the only thing carrying meaning — every one of these sits
   beside a word or a number that says the same thing. These exist so a page
   can tint a figure without writing a colour into the markup.  [KICKOFF 6.2]
   -------------------------------------------------------------------------- */

.ink-success { color: var(--success-ink); }
.ink-warning { color: var(--warning-ink); }
.ink-danger  { color: var(--danger-ink); }

/* A card marked as needing attention — an unread message, a new notice. */
/* An unread message, and what happens when it is opened.

   A 3px line down the left edge was the whole of it, which on a page of white
   cards is not a difference anybody notices — and it used to clear itself the
   moment the card scrolled past, so a parent could lose the marker without
   having read a word. Unread now looks unread: a tinted card, a solid accent
   down the side, the sender in full ink. Opening one is a deliberate tap, and
   the card fades back to an ordinary one so the change is visible rather than
   just true. */
.card-unread {
  border-left: 4px solid var(--primary);
  background: var(--primary-soft);
  cursor: pointer;
}
.card-unread:hover { background: var(--primary-soft-2); }
.card-unread [data-teacher],
.card-unread [data-title] { color: var(--ink); font-weight: var(--weight-bold); }

/* The dot is the part that reads as "unread" at a glance, the way it does in
   every mail app somebody has already learned. */
.unread-dot {
  width: 9px; height: 9px; flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--primary);
}

/* Said once per list rather than on every card. */
.unread-hint { color: var(--ink-muted); font-size: var(--text-sm); }

/* Opening one is worth seeing happen: the tint leaves over a moment rather
   than snapping, so the eye follows which card changed. */
.card-unread, .card-unread [data-teacher], .card-unread [data-title] {
  transition: background-color 320ms ease, border-color 320ms ease, color 320ms ease;
}
@media (prefers-reduced-motion: reduce) {
  .card-unread, .card-unread [data-teacher], .card-unread [data-title] { transition: none; }
}

/* A bar that fills to show how much of something is done. Its width is the one
   thing a page may still set inline, because the width IS the data. */
.progress > span { display: block; height: 100%; }


/* ---------------------------------------------------- the student record */

/* A photograph, or the initials that stand in for one. Square and fixed, so a
   record with a photo and one without line up on the same page rather than
   jumping by 80px. */
.student-photo {
  width: 96px;
  height: 96px;
  flex: 0 0 96px;
  border-radius: var(--radius-lg);
  background: var(--primary-soft);
  color: var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  overflow: hidden;
  border: 1px solid var(--highlight-edge);
}
.student-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }

.student-head { gap: var(--space-5); align-items: center; }
.student-name { font-size: var(--text-xl); font-weight: var(--weight-bold); }

/* The marks line: one bar per examination, oldest first. A sparkline would be
   prettier and harder to read a number off, and a number is what a Principal
   is looking for. */
.progress-row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) 0; }
.progress-label { min-width: 120px; }
.progress-track {
  flex: 1;
  height: 10px;
  border-radius: 999px;
  background: var(--surface-sunk);
  overflow: hidden;
}
.progress-fill { height: 100%; border-radius: 999px; background: var(--primary); }
.progress-fill.is-good { background: var(--success); }
.progress-fill.is-warn { background: var(--warning); }
.progress-fill.is-bad  { background: var(--danger); }
.progress-value { min-width: 92px; text-align: right; font-weight: var(--weight-semi); }

/* ---------------------------------------------------------------- noticeboard
   A notice is read, not scanned, so the body keeps its line breaks and sits at
   a comfortable reading width rather than stretching across a desktop screen.
   A withdrawn notice is dimmed rather than removed: the office and the
   Principal need to see that it was up and is not any more. */
.notice-title {
  font-size: var(--text-lg);
  font-weight: var(--weight-semi);
  margin-top: var(--space-3);
}
.notice-body {
  color: var(--ink-soft);
  white-space: pre-wrap;
  max-width: 72ch;
  margin: var(--space-2) 0 var(--space-3);
}
/* Every notice on the board. A notification links to one by id, so the jump
   has to leave it clear of the sticky top bar rather than under it. */
.notice-card { scroll-margin-top: var(--space-16); }

.is-withdrawn { opacity: 0.62; }
.is-withdrawn .notice-title { text-decoration: line-through; }

/* A congratulation — the notice an achievement or an award posts by itself.
   The same card with a gold edge, a warmer glow and one celebration mark: a
   school announcement that reads as an occasion, not a game's pop-up. The
   mark is decorative and hidden from a screen reader, which reads the words. */
.notice-congrats {
  border-color: var(--warning-line);
  border-left: 4px solid var(--warning-ink);
  box-shadow: var(--card-shadow), 0 0 0 1px var(--warning-line);
}
.notice-congrats .notice-title { color: var(--warning-ink); }
.notice-congrats-mark {
  font-size: var(--text-xl);
  line-height: 1;
  margin-top: var(--space-3);
}
.notice-congrats .notice-body { color: var(--ink); }

/* ----------------------------------------------------------- academic calendar
   A year is read a month at a time, so the months are headings and not another
   badge on every card. The date is set above the title rather than below it,
   because the date is what somebody opened the page to find — a parent looking
   for when the holidays start should not have to read a sentence first.

   A day that has gone is dimmed rather than dropped: only the office sees the
   past at all, and the office needs it to correct a date that was typed wrong.
   Today's entry is marked along its edge, the same 4px accent an unread message
   wears, so the eye lands on it without a colour that means anything else. */
.cal-month {
  font-size: var(--text-sm);
  font-weight: var(--weight-semi);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: var(--space-6) 0 var(--space-2);
}
.cal-month:first-child { margin-top: 0; }

.cal-when {
  margin-top: var(--space-3);
  font-weight: var(--weight-semi);
  color: var(--primary);
}
.cal-entry .notice-title {
  margin-top: var(--space-1);
  font-size: var(--text-base);
}
.cal-entry.is-past { opacity: 0.58; }
.cal-entry.is-today { border-left: 4px solid var(--primary); }


/* ----------------------------------------------------------------------------
   14. GETTING THE APP                  app/index.html

   The only page anybody reaches before they have an account: the Android
   download, and how to get past the phone's warning about an app that did not
   come from the Play Store.
   -------------------------------------------------------------------------- */

.steps { margin: 0; padding-left: var(--space-5); display: grid; gap: var(--space-3); }
.steps li { color: var(--ink-soft); font-size: var(--text-sm); }
.steps strong { color: var(--ink); font-weight: var(--weight-semi); }


/* ----------------------------------------------------------------------------
   15. THE DATE SHEET                    student, parent and teacher pages

   The one page in the portal that is meant to leave the screen: a school hands
   out a date sheet on paper, and the print dialog is also how a phone saves it
   as a PDF. The card keeps its heading with its table rather than being split
   across two sheets of paper.
   -------------------------------------------------------------------------- */

.datesheet { break-inside: avoid; }

@media print {
  .datesheet { border: 1px solid var(--grey-400); }
  .datesheet .card-head { border-bottom: 1px solid var(--grey-400); }
}


/* ----------------------------------------------------------------------------
   16. SALARY                            office/salary.html

   Two narrow number boxes in a table of figures: a monthly salary and, for the
   two roles no register is kept for, the days somebody was not there. Sized to
   the numbers they hold so a row of them still reads as a table.
   -------------------------------------------------------------------------- */

.salary-input {
  width: 8rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The days somebody was not there, for the two roles no register is kept for.
   Narrow, because it holds a number under five. */
.days-input {
  width: 4.5rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The full arithmetic, folded away. Open it and it reads as the paragraph it
   is; closed, it is one line that does not compete with the total above it. */
.how { margin-top: var(--space-3); }
.how > summary {
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--primary);
  min-height: var(--touch);
  display: flex;
  align-items: center;
}
.how p { margin-top: var(--space-2); }


/* ----------------------------------------------------------------------------
   17. ACTIVITIES                activity/*.html, office/activities.html,
                                 and the Highlights on every profile

   Houses in their own colour; records as ruled rows; the Incharge's shortcuts
   as tiles. Every colour is a token — a house card takes its colour from the
   same --house-* pair the badge uses, so a house looks the same on every page.
   -------------------------------------------------------------------------- */

.check { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); cursor: pointer;
          min-height: var(--touch); }
.check input { width: auto; min-height: 0; margin: 0; }

/* Choosing a certificate: the file button is a thing a finger has to hit. */
.field input[type="file"] { min-height: var(--touch); padding: var(--space-2) 0; font-size: var(--text-sm); color: var(--ink-soft); }

/* A student in a search list: name first, class and ID after. */
.pick-row { justify-content: space-between; gap: var(--space-3); text-align: left; }

/* A certificate, opened in the page. */
.image-view { width: min(92vw, 900px); max-width: none; }
.image-view-picture {
  display: block; max-width: 100%; max-height: 70vh;
  margin: var(--space-3) auto;
  border-radius: var(--radius); border: 1px solid var(--line);
}

/* Anything tinted by a house — a house card, a standings bar — carries the
   colour in two custom properties, set from data-house, and draws from them. */
.house-tint { --h: var(--line-strong); --h-soft: var(--surface-sunk); }
.house-tint[data-house="GREEN"]  { --h: var(--house-green);  --h-soft: var(--house-green-soft); }
.house-tint[data-house="RED"]    { --h: var(--house-red);    --h-soft: var(--house-red-soft); }
.house-tint[data-house="YELLOW"] { --h: var(--house-yellow); --h-soft: var(--house-yellow-soft); }
.house-tint[data-house="ORANGE"] { --h: var(--house-orange); --h-soft: var(--house-orange-soft); }
.house-tint[data-house="BLUE"]   { --h: var(--house-blue);   --h-soft: var(--house-blue-soft); }
.house-tint[data-house="PURPLE"] { --h: var(--house-purple); --h-soft: var(--house-purple-soft); }
.house-tint[data-house="MAROON"] { --h: var(--house-maroon); --h-soft: var(--house-maroon-soft); }
.house-tint[data-house="SKY"]    { --h: var(--house-sky);    --h-soft: var(--house-sky-soft); }
.house-tint[data-house="PINK"]   { --h: var(--house-pink);   --h-soft: var(--house-pink-soft); }

.house-grid {
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.house-card {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-5);
  background: linear-gradient(180deg, var(--h-soft), var(--surface) 75%);
  border: 1px solid var(--card-edge);
  border-top: 4px solid var(--h);
  border-radius: var(--radius-lg);
  box-shadow: var(--card-shadow);
}
.house-card-name { font-size: var(--text-md); font-weight: var(--weight-bold); color: var(--h); }
.house-card-motto { font-size: var(--text-sm); color: var(--ink-soft); font-style: italic; }
.house-card-figures { display: flex; gap: var(--space-6); margin-top: var(--space-1); }
.house-card-figure { font-size: var(--text-xl); font-weight: var(--weight-bold); color: var(--figure); line-height: 1.1; }
.house-card-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: auto; padding-top: var(--space-2); }

/* The standings: a thin bar of the house's colour, as long as its share. */
.standing-bar { height: 6px; border-radius: var(--radius-full); background: var(--surface-sunk); overflow: hidden; }
.standing-bar > span { display: block; height: 100%; background: var(--h); border-radius: inherit; }

/* The Incharge's shortcuts. */
.tile-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); }
.tile {
  display: flex; flex-direction: column; gap: var(--space-1);
  padding: var(--space-5);
  background: var(--surface);
  border: 1px solid var(--card-edge);
  border-radius: var(--radius-lg);
  box-shadow: var(--card-shadow);
  color: var(--ink); text-decoration: none;
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.tile:hover { border-color: var(--primary); box-shadow: var(--card-glow); text-decoration: none; }
.tile-icon { font-size: var(--text-lg); color: var(--accent-cyan); }
.tile-title { font-weight: var(--weight-semi); }
.tile-note { font-size: var(--text-sm); color: var(--ink-muted); }

/* A record in a list: what it is, then its detail, then its buttons. */
.record-row { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--space-3); }
.record-main { min-width: 0; flex: 1 1 260px; }
.record-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.record-ended { opacity: 0.7; }

/* The Highlights on a profile: a card per section, each with its own accent
   so a reader finds "awards" by its colour before its heading. */
.hl-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.hl-card { --a: var(--primary); border-top: 3px solid var(--a); }
.hl-card[data-accent="cyan"]    { --a: var(--accent-cyan); }
.hl-card[data-accent="violet"]  { --a: var(--accent-violet); }
.hl-card[data-accent="magenta"] { --a: var(--accent-magenta); }
.hl-card[data-accent="gold"]    { --a: var(--warning-ink); }
.hl-card[data-accent="green"]   { --a: var(--success-ink); }
.hl-card[data-accent="blue"]    { --a: var(--primary); }
.hl-title { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); font-weight: var(--weight-semi); color: var(--a); }
.hl-count {
  min-width: 24px; padding: 0 var(--space-2);
  border-radius: var(--radius-full);
  background: var(--a); color: var(--ink-invert);
  font-size: var(--text-xs); font-weight: var(--weight-bold); text-align: center;
}
.hl-item { padding: var(--space-2) 0; border-top: 1px solid var(--line); }
.hl-item:first-of-type { border-top: 0; }
.hl-item-title { font-weight: var(--weight-medium); }


/* ----------------------------------------------------------------------------
   18. THE MONTH CALENDAR                  every role's Academic calendar page

   A whole month on one card. The date's own colour says what kind of day it
   is — red for a holiday or a Sunday, saffron for a festival, violet for a
   school event — and small dots beneath show everything else on that day.
   Tapping a date explains it below the month. Every colour is a --cal-* token.
   -------------------------------------------------------------------------- */

[data-tone="holiday"]  { --t: var(--cal-holiday);  --t-soft: var(--cal-holiday-soft); }
[data-tone="festival"] { --t: var(--cal-festival); --t-soft: var(--cal-festival-soft); }
[data-tone="event"]    { --t: var(--cal-event);    --t-soft: var(--cal-event-soft); }
[data-tone="activity"] { --t: var(--cal-activity); --t-soft: var(--cal-activity-soft); }
[data-tone="exam"]     { --t: var(--cal-exam);     --t-soft: var(--cal-exam-soft); }
[data-tone="meeting"]  { --t: var(--cal-meeting);  --t-soft: var(--cal-meeting-soft); }
[data-tone="term"]     { --t: var(--cal-term);     --t-soft: var(--cal-term-soft); }

.cal-card { padding: var(--space-5); }
.cal-nav { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-4); }
.cal-nav-title { font-size: var(--text-lg); font-weight: var(--weight-bold); color: var(--heading); text-align: center; flex: 1; }
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-1); }
.cal-dow {
  text-align: center; font-size: var(--text-xs); font-weight: var(--weight-semi);
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-muted); padding: var(--space-1) 0;
}
.cal-dow.is-sunday { color: var(--cal-holiday); }
.cal-day {
  position: relative;
  min-height: 56px; padding: var(--space-1);
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: 2px;
  background: var(--surface-alt); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
  font: inherit; cursor: pointer;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.cal-day:hover { border-color: var(--primary); }
.cal-day[data-tone] { background: var(--t-soft); border-color: var(--t); }
.cal-day[data-tone] .cal-num { color: var(--t); }
.cal-day.is-blank { visibility: hidden; }
.cal-day.is-today { box-shadow: 0 0 0 2px var(--primary); }
.cal-day.is-selected { box-shadow: 0 0 0 2px var(--primary), var(--card-glow); }
.cal-num { font-size: var(--text-md); font-weight: var(--weight-bold); line-height: 1.4; }
.cal-dots { display: flex; flex-wrap: wrap; justify-content: center; gap: 3px; }
.cal-dot { width: 7px; height: 7px; border-radius: var(--radius-full); background: var(--t); }

.cal-legend { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin-top: var(--space-4); }
.cal-key { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--ink-soft); }
.cal-swatch { width: 12px; height: 12px; border-radius: 3px; background: var(--t); }

.cal-detail { border-left: 4px solid var(--t, var(--primary)); }
.cal-detail-date { font-size: var(--text-md); font-weight: var(--weight-bold); color: var(--heading); }
.cal-detail-item { padding: var(--space-3) 0; border-top: 1px solid var(--line); }
.cal-detail-item:first-of-type { border-top: 0; }
.cal-kind { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); font-weight: var(--weight-bold);
            text-transform: uppercase; letter-spacing: 0.05em; color: var(--t); }

@media (max-width: 480px) {
  /* Seven dates across a phone, each still a 44px target: the month card
     reaches into the page's side padding (4px from the screen's edge) and
     gives up its own, rather than the dates shrinking. */
  .cal-card { padding: var(--space-1); margin-inline: calc(var(--space-1) - var(--space-4)); }
  .cal-grid { gap: 2px; }
  .cal-nav { padding: var(--space-2) var(--space-2) 0; }
  .cal-legend { padding: 0 var(--space-2) var(--space-2); }
  .cal-day { min-height: 44px; min-width: 44px; padding: 2px; }
  .cal-num { font-size: var(--text-sm); }
  .cal-dot { width: 5px; height: 5px; }
}


/* ----------------------------------------------------------------------------
   ATOM — the student's AI study assistant
   pages/student/profile.html (the card) and pages/student/atom.html (the chat)

   A subtle identity of its own — a round robot mark and a lit edge — drawn
   entirely from the existing tokens, so it reads as part of Libesole in both
   themes rather than as a widget bolted onto it.
   -------------------------------------------------------------------------- */

/* The card on My profile */
.atom-card { border-top: 3px solid var(--primary); box-shadow: var(--card-glow); }
.atom-card-head { gap: var(--space-4); }
.atom-mark {
  width: 48px; height: 48px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius-full);
  background: var(--primary-soft);
  border: 1px solid var(--primary-ring);
  font-size: var(--text-xl); line-height: 1;
}
.atom-tagline {
  margin: var(--space-4) 0;
  color: var(--figure); font-weight: var(--weight-semi); letter-spacing: 0.02em;
}
.atom-card-actions {
  display: grid; gap: var(--space-2);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.atom-card-actions .atom-card-main { grid-column: 1 / -1; }
.atom-card-actions .btn-ghost { grid-column: 1 / -1; justify-self: start; }

/* The chat page */
.atom-chat { display: flex; flex-direction: column; overflow: hidden; }
.atom-head { align-items: flex-start; }

.atom-setup { padding: var(--space-5) var(--space-6); border-bottom: 1px solid var(--line); background: var(--surface-alt); }
.atom-setup-intro { margin-bottom: var(--space-4); }
.atom-setup-grid {
  display: grid; gap: 0 var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
}
.atom-topic-field { grid-column: span 2; }

.atom-log {
  display: flex; flex-direction: column; gap: var(--space-4);
  padding: var(--space-6);
  min-height: 280px; max-height: min(62vh, 640px);
  overflow-y: auto; overscroll-behavior: contain;
}

.atom-msg { display: flex; gap: var(--space-3); align-items: flex-start; max-width: 100%; }
.atom-msg.is-user { flex-direction: row-reverse; }
.atom-avatar {
  width: 34px; height: 34px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius-full);
  background: var(--primary-soft); color: var(--primary);
  font-size: var(--text-sm); font-weight: var(--weight-bold);
}
.atom-msg.is-user .atom-avatar { background: var(--avatar-bg); color: var(--avatar-ink); }

.atom-bubble {
  min-width: 0; max-width: min(720px, 85%);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg);
  border-top-left-radius: var(--radius-sm);
  background: var(--surface-sunk); color: var(--ink);
  line-height: var(--leading-normal);
  overflow-wrap: anywhere;
}
.atom-msg.is-user .atom-bubble {
  background: var(--primary-solid); color: var(--on-primary);
  border-top-left-radius: var(--radius-lg); border-top-right-radius: var(--radius-sm);
  white-space: pre-wrap;
}
.atom-msg.is-error .atom-bubble { background: var(--danger-bg); color: var(--danger-ink); border: 1px solid var(--danger-line); }

.atom-bubble > * + * { margin-top: var(--space-2); }
.atom-bubble h4 { font-size: var(--text-base); font-weight: var(--weight-bold); color: var(--heading); margin-top: var(--space-3); }
.atom-bubble h4:first-child { margin-top: 0; }
.atom-bubble ul, .atom-bubble ol { padding-left: var(--space-6); }
.atom-bubble li + li { margin-top: var(--space-1); }
.atom-bubble code { font-family: var(--font-mono); font-size: 0.92em; padding: 0 var(--space-1); border-radius: var(--radius-sm); background: var(--surface); }
.atom-retry { margin-top: var(--space-3); }

.atom-thinking {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 0 var(--space-6) var(--space-4) calc(var(--space-6) + 34px + var(--space-3));
  color: var(--ink-soft); font-size: var(--text-sm);
}
.atom-dots { display: inline-flex; gap: 3px; color: var(--primary); }
.atom-dots span { animation: atom-dot 1.2s infinite var(--ease); }
.atom-dots span:nth-child(2) { animation-delay: 0.2s; }
.atom-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes atom-dot { 0%, 80%, 100% { opacity: 0.25; } 40% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .atom-dots span { animation: none; } }

.atom-compose {
  display: flex; gap: var(--space-3); align-items: flex-end;
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--line);
}
.atom-compose textarea { flex: 1; min-height: 48px; max-height: 180px; resize: vertical; }
.atom-foot { padding: 0 var(--space-6) var(--space-4); }

/* Camera and gallery sit beside the text box on a wide screen. */
.atom-attach { flex: none; display: inline-flex; align-items: center; gap: var(--space-2); min-height: 48px; }

/* On a phone the row wraps: the text box takes the whole first line, and the
   three buttons sit under it. Squeezing a textarea between two buttons leaves
   it about eight characters wide, which is no way to type a question. */
@media (max-width: 640px) {
  .atom-compose { flex-wrap: wrap; }
  .atom-compose textarea { order: -1; flex: 1 0 100%; }
  .atom-compose #send { flex: 1; }
  .atom-attach-text { display: none; }
  .atom-attach { padding-inline: var(--space-4); }
}

/* While the microphone is listening: the button lights and pulses, so it is
   obvious the phone is hearing you — and obvious how to stop it. */
.atom-attach.is-listening {
  border-color: var(--danger);
  color: var(--danger-ink);
  animation: atom-listening 1.4s ease-in-out infinite;
}
@keyframes atom-listening {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 90, 90, 0.35); }
  50%      { box-shadow: 0 0 0 8px rgba(255, 90, 90, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .atom-attach.is-listening { animation: none; }
}

/* The picture waiting to be sent, above the text box. */
.atom-photo {
  display: flex; align-items: center; gap: var(--space-3);
  margin: 0 var(--space-6) var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-sunk);
}
.atom-photo img { width: 72px; height: 72px; object-fit: cover; border-radius: var(--radius-sm); }
.atom-photo-note { flex: 1; display: grid; gap: 2px; }

/* A photo the student already sent, inside their own message. */
.atom-sent-photo {
  display: block; max-width: 260px; width: 100%; height: auto;
  border-radius: var(--radius-sm); margin-bottom: var(--space-2);
}

/* A quiz inside Atom's bubble */
.atom-quiz { width: 100%; max-width: 760px; }
.atom-q { border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-4); margin: var(--space-3) 0 0; background: var(--surface); }
.atom-q-text { font-weight: var(--weight-semi); padding: 0 var(--space-1); }
.atom-q-options { display: grid; gap: var(--space-2); margin-top: var(--space-3); }
.atom-option {
  display: flex; gap: var(--space-3); align-items: center; text-align: left;
  min-height: 44px; padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--ink); font: inherit; cursor: pointer;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.atom-option:hover:not(:disabled) { border-color: var(--primary); background: var(--primary-soft); }
.atom-option:disabled { cursor: default; }
.atom-option-key {
  width: 26px; height: 26px; flex: none; display: grid; place-items: center;
  border-radius: var(--radius-full); background: var(--surface-sunk);
  font-size: var(--text-xs); font-weight: var(--weight-bold);
}
.atom-option.is-correct { border-color: var(--success); background: var(--success-bg); color: var(--success-ink); }
.atom-option.is-wrong { border-color: var(--danger); background: var(--danger-bg); color: var(--danger-ink); }
.atom-q-feedback { margin-top: var(--space-3); }
.atom-q-feedback.is-right { color: var(--success-ink); }
.atom-q-feedback.is-missed { color: var(--ink-soft); }

@media (max-width: 640px) {
  .atom-card-actions { grid-template-columns: 1fr 1fr; }
  .atom-card-actions .btn-secondary:last-of-type { grid-column: 1 / -1; }
  .atom-head, .atom-setup, .atom-log, .atom-compose { padding-left: var(--space-4); padding-right: var(--space-4); }
  .atom-foot { padding: 0 var(--space-4) var(--space-4); }
  .atom-thinking { padding-left: calc(var(--space-4) + 34px + var(--space-3)); }
  .atom-topic-field { grid-column: auto; }
  .atom-bubble { max-width: calc(100% - 34px - var(--space-3)); }
  .atom-log { max-height: none; min-height: 220px; }
  .atom-head .pill-tabs { flex-wrap: nowrap; overflow-x: auto; width: 100%; padding-bottom: var(--space-1); }
  .atom-head .pill-tab { flex: none; }
}

/* ------------------------------------------------------------------ *
 * The school hierarchy — Chairman at the top, teachers at the foot.
 *
 * One level per post, numbered, each joined to the next by a line down the
 * middle, so it reads as the tree it is on a desk and on a phone alike.
 * A level with several people (Heads of Department, teachers) lays them out
 * side by side and wraps.
 * ------------------------------------------------------------------ */
.tree { list-style: none; margin: 0; padding: 0; }
.tree-level { position: relative; }
.tree-level + .tree-level { padding-top: var(--space-8); }
/* The line from the level above, ending in a small arrowhead. */
.tree-level + .tree-level::before {
  content: ""; position: absolute; top: 0; left: 50%; width: 2px; height: calc(var(--space-8) - 6px);
  background: var(--line-strong); transform: translateX(-1px);
}
.tree-level + .tree-level::after {
  content: ""; position: absolute; top: calc(var(--space-8) - 8px); left: 50%;
  border: 6px solid transparent; border-top-color: var(--line-strong); border-bottom: 0;
  transform: translateX(-6px);
}
.tree-level-head {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.tree-step {
  width: 28px; height: 28px; flex: none; display: grid; place-items: center;
  border-radius: var(--radius-full); background: var(--primary); color: var(--ink-invert);
  font-size: var(--text-sm); font-weight: var(--weight-bold);
}
.tree-level-title { margin: 0; font-size: var(--text-md); font-weight: var(--weight-semi); }
.tree-group + .tree-group { margin-top: var(--space-5); }
.tree-group-title {
  text-align: center; font-size: var(--text-sm); color: var(--ink-muted); margin-bottom: var(--space-2);
}
.tree-people {
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3);
}
.tree-person {
  width: 300px; max-width: 100%;
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-4); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.tree-person .avatar { width: 64px; height: 64px; flex: none; font-size: var(--text-lg); }
.tree-person-body { min-width: 0; flex: 1; }
.tree-person-name { font-weight: var(--weight-semi); overflow-wrap: anywhere; }
.tree-person-post { font-size: var(--text-sm); color: var(--ink-muted); margin-bottom: var(--space-2); }
.tree-contact { display: grid; gap: 2px; font-size: var(--text-sm); }
/* A number and an address are each a target of their own: tall enough for a
   mouse on a desk, and for a thumb on a phone or tablet.  [KICKOFF 6.4] */
.tree-contact a { display: flex; align-items: center; min-height: 24px; overflow-wrap: anywhere; }
.tree-contact .muted { font-size: var(--text-sm); }
.tree-person-actions { display: flex; gap: var(--space-2); margin-top: var(--space-3); flex-wrap: wrap; }
/* A post nobody holds yet — shown only to the people who can fill it. */
.tree-empty {
  width: 300px; max-width: 100%; padding: var(--space-4); text-align: center;
  border: 1px dashed var(--line-strong); border-radius: var(--radius-lg); color: var(--ink-muted);
}
@media (max-width: 640px) {
  .tree-person, .tree-empty { width: 100%; }
}
@media (max-width: 1024px), (pointer: coarse) {
  .tree-contact a { min-height: 44px; }
}

/* --------------------------------------------------------------------------
   REGISTERING A SCHOOL, IN STEPS — details, colours, review.
   -------------------------------------------------------------------------- */
.form-steps {
  display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0 0 var(--space-5); padding: 0;
  list-style: none; counter-reset: step;
}
.form-steps li {
  counter-increment: step;
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-full);
  background: var(--surface-sunk); color: var(--ink-muted); font-size: var(--text-sm);
}
.form-steps li::before {
  content: counter(step);
  width: 22px; height: 22px; display: grid; place-items: center; flex: none;
  border-radius: var(--radius-full); background: var(--line-strong); color: var(--ink);
  font-size: var(--text-xs); font-weight: var(--weight-bold);
}
.form-steps li[aria-current="step"] { background: var(--primary-soft); color: var(--ink); font-weight: var(--weight-semi); }
.form-steps li[aria-current="step"]::before { background: var(--primary-solid); color: var(--on-primary); }
.form-steps li.is-done::before { background: var(--success-bg); color: var(--success-ink); }
.step-title { margin: 0 0 var(--space-2); font-size: var(--text-md); font-weight: var(--weight-semi); color: var(--heading); }

/* --------------------------------------------------------------------------
   THE SCHOOL THEME EDITOR — presets, five colours, and a live preview.
   -------------------------------------------------------------------------- */
.theme-presets { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-4) 0; }
.theme-preset {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--touch); padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong); border-radius: var(--radius-full);
  background: var(--surface); color: var(--ink); font: inherit; font-size: var(--text-sm); cursor: pointer;
}
.theme-preset:hover { border-color: var(--primary); }
.theme-preset[aria-pressed="true"] { border-color: var(--primary); background: var(--primary-soft); font-weight: var(--weight-semi); }
.theme-preset:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.theme-swatches { display: inline-flex; }
.theme-swatch {
  width: 16px; height: 16px; border-radius: var(--radius-full);
  background: var(--swatch); border: 2px solid var(--surface);
}
.theme-swatch + .theme-swatch { margin-left: -6px; }

.theme-editor { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); gap: var(--space-6); align-items: start; }
@media (max-width: 960px) { .theme-editor { grid-template-columns: minmax(0, 1fr); } }
.theme-fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(165px, 1fr)); gap: var(--space-3) var(--space-4); }
.theme-fields .field-error:empty { min-height: 0; }
.theme-fields .field { margin-bottom: 0; gap: var(--space-1); }
.colour-input { display: flex; align-items: center; gap: var(--space-2); }
.colour-input input[type="color"] {
  width: var(--touch); height: var(--touch); flex: none; padding: 2px;
  border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--field-bg); cursor: pointer;
}
.colour-input input[type="text"] { font-family: var(--font-mono); text-transform: uppercase; max-width: 12ch; }

/* The preview carries its own tokens (set by themeEditor.js), so everything
   inside it is drawn in the school's colours whatever this page is wearing. */
.theme-preview {
  display: grid; grid-template-columns: 132px minmax(0, 1fr); min-height: 340px; overflow: hidden;
  border: 1px solid var(--line-strong); border-radius: var(--radius-lg);
  background: var(--bg); color: var(--ink); box-shadow: var(--shadow);
  font-size: var(--text-sm);
}
.tp-sidebar {
  display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-3) var(--space-2);
  background-color: var(--sidebar-bg); background-image: var(--sidebar-bg-image, none); color: var(--sidebar-ink);
}
.tp-school { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); color: var(--on-primary); font-weight: var(--weight-semi); overflow-wrap: anywhere; }
.tp-logo {
  width: 26px; height: 26px; flex: none; display: grid; place-items: center; overflow: hidden;
  border-radius: var(--radius); background: var(--on-primary); color: var(--sidebar-bg); font-size: var(--text-xs); font-weight: var(--weight-bold);
}
.tp-logo img { width: 100%; height: 100%; object-fit: cover; }
.tp-nav { padding: var(--space-2); border-radius: var(--radius); border-left: 3px solid transparent; }
.tp-nav.is-active { background: var(--sidebar-active); color: var(--on-primary); border-left-color: var(--sidebar-active-edge); font-weight: var(--weight-semi); }
.tp-main { display: flex; flex-direction: column; min-width: 0; }
.tp-header {
  padding: var(--space-3) var(--space-4); background: var(--surface-glass, var(--surface));
  border-bottom: 1px solid var(--line); color: var(--heading); font-weight: var(--weight-semi);
}
.tp-body { display: grid; gap: var(--space-3); padding: var(--space-3); }
.tp-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
.tp-card {
  display: grid; gap: var(--space-1); padding: var(--space-3);
  background: var(--surface); border: 1px solid var(--card-edge, var(--line)); border-radius: var(--radius);
}
.tp-label { color: var(--ink-muted); font-size: var(--text-xs); }
.tp-figure { color: var(--figure); font-size: var(--text-lg); font-weight: var(--weight-bold); }
.tp-input {
  padding: var(--space-2) var(--space-3); border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--field-bg, var(--surface)); color: var(--ink);
  box-shadow: 0 0 0 3px var(--primary-ring); border-color: var(--primary);
}
.tp-text { margin: 0; color: var(--ink-soft); }
.tp-link { color: var(--primary); text-decoration: underline; }
.tp-highlight {
  display: inline-block; padding: 0 var(--space-2); border-radius: var(--radius-full);
  background: var(--primary-soft); color: var(--primary); font-size: var(--text-xs); font-weight: var(--weight-semi);
}
.tp-status { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; }
.tp-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.tp-btn, .tp-btn-secondary {
  display: inline-flex; align-items: center; padding: var(--space-2) var(--space-3);
  border-radius: var(--radius); font-weight: var(--weight-semi); border: 1px solid transparent;
}
.tp-btn { background-color: var(--primary-solid); background-image: var(--primary-fill); color: var(--on-primary); }
.tp-btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--secondary-edge, var(--line-strong)); }
@media (max-width: 480px) {
  .theme-preview { grid-template-columns: minmax(0, 1fr); }
  .tp-sidebar { flex-direction: row; flex-wrap: wrap; }
  .tp-school { width: 100%; margin-bottom: var(--space-1); }
}

/* A school's colours, small, on its card in the list. */
.school-colours { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--ink-muted); }

/* The review step: what will be created, read back. */
.review-list { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-2) var(--space-4); margin: 0; }
.review-list dt { color: var(--ink-muted); font-size: var(--text-sm); }
.review-list dd { margin: 0; overflow-wrap: anywhere; }
@media (max-width: 480px) { .review-list { grid-template-columns: minmax(0, 1fr); } .review-list dd { margin-bottom: var(--space-2); } }

/* --------------------------------------------------------------------------
   A SCHOOL'S PROFILES — the seven who can sign in there, a card each
   (profilesEditor.js). Ticked cards are lit; Principal is always on.
   -------------------------------------------------------------------------- */
.profile-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
  gap: var(--space-3); margin: var(--space-4) 0 var(--space-3);
}
.profile-card {
  position: relative; display: flex; gap: var(--space-3); align-items: flex-start;
  padding: var(--space-3) var(--space-4); border: 1.5px solid var(--line);
  border-radius: var(--radius-lg); background: var(--surface-alt); cursor: pointer;
  transition: border-color 150ms ease, background-color 150ms ease;
}
.profile-card input { flex: none; width: 20px; height: 20px; margin: 2px 0 0; accent-color: var(--primary); cursor: pointer; }
.profile-card:hover { border-color: var(--primary); }
.profile-card.is-on { border-color: var(--primary); background: var(--primary-soft); }
.profile-card.is-locked { cursor: default; }
.profile-card.is-locked input { cursor: default; }
.profile-card:has(input:focus-visible) { outline: 2px solid var(--primary); outline-offset: 2px; }
.profile-card-body { display: grid; gap: var(--space-1); min-width: 0; }
.profile-card-title { font-weight: var(--weight-semi); color: var(--heading); }
.profile-card-text { font-size: var(--text-sm); color: var(--ink-soft); }
.profile-card-off { overflow-wrap: anywhere; }
.profile-card.is-on .profile-card-off { display: none; }
.profile-card.is-locked .profile-card-off { display: block; }
.profile-summary { color: var(--ink-soft); }
.profile-chips { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.tight-list { margin: var(--space-1) 0 0; padding-left: 1.2em; }
.tight-list li + li { margin-top: 2px; }
@media (prefers-reduced-motion: reduce) { .profile-card { transition: none; } }

/* --------------------------------------------------------------------------
   THE BUS MAP — busMap.js. Map pictures side by side, the route's stops and
   the bus on top. A bus that is not live is drawn grey, never as if it were.
   -------------------------------------------------------------------------- */
.bus-map {
  position: relative; overflow: hidden; height: 300px; border-radius: var(--radius-lg);
  border: 1px solid var(--line); background: var(--surface-sunk); touch-action: none; user-select: none;
  cursor: grab;
}
@media (min-width: 720px) { .bus-map { height: 400px; } }
.bus-map.is-dragging { cursor: grabbing; }
.bus-map.is-plain {
  background-image: linear-gradient(var(--line) 1px, transparent 1px), linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 40px 40px;
}
.bus-map-tiles img { position: absolute; left: 0; top: 0; width: 256px; height: 256px; pointer-events: none; }
.bus-map-lines { position: absolute; inset: 0; pointer-events: none; }
/* The route: dashed and straight between two stops no bus has driven yet,
   solid along the road where one has. */
.bus-map-route { fill: none; stroke: var(--map-route); stroke-width: 3; stroke-dasharray: 7 6; stroke-linecap: round; opacity: 0.7; }
.bus-map-route.is-driven { stroke-width: 5; stroke-dasharray: none; stroke-linejoin: round; opacity: 0.85; }
.bus-map-trail { fill: none; stroke: var(--accent, var(--primary)); stroke-width: 4; opacity: 0.85; }
.bus-map-marks { position: absolute; inset: 0; pointer-events: none; }
.bus-map-stop, .bus-map-bus { position: absolute; left: 0; top: 0; }
/* A stop: a grey circle until the bus has reached it on this trip, then green. */
.bus-map-stop-dot {
  position: absolute; transform: translate(-50%, -50%); display: grid; place-items: center;
  width: 22px; height: 22px; border-radius: 50%; background: var(--map-stop); color: var(--map-stop-ink);
  border: 2px solid var(--map-stop-ink); box-shadow: var(--shadow-md); font-size: 11px; font-weight: var(--weight-bold);
}
.bus-map-stop.is-mine .bus-map-stop-dot { width: 28px; height: 28px; outline: 3px solid var(--map-route); }
.bus-map-bus-icon {
  position: absolute; transform: translate(-50%, -50%); display: grid; place-items: center;
  width: 38px; height: 38px; border-radius: 50%; background: var(--success); color: var(--white);
  border: 3px solid var(--white); box-shadow: var(--shadow-md);
}
.bus-map-bus.is-stale .bus-map-bus-icon { background: var(--ink-muted); }
.bus-map-bus.is-live .bus-map-bus-icon::after {
  content: ""; position: absolute; inset: -9px; border-radius: 50%; border: 2px solid var(--success);
  animation: bus-pulse 1.8s ease-out infinite;
}
@keyframes bus-pulse { from { opacity: 0.9; transform: scale(0.7); } to { opacity: 0; transform: scale(1.4); } }
@media (prefers-reduced-motion: reduce) { .bus-map-bus.is-live .bus-map-bus-icon::after { animation: none; opacity: 0.5; } }
.bus-map-tag {
  position: absolute; transform: translate(-50%, -100%); margin-top: -18px; white-space: nowrap;
  padding: 2px 8px; border-radius: var(--radius-full); background: var(--surface); color: var(--ink);
  font-size: var(--text-xs); font-weight: var(--weight-semi); box-shadow: var(--shadow-md);
}
.bus-map-bus .bus-map-tag { margin-top: -26px; }
.bus-map-controls { position: absolute; right: 10px; top: 10px; display: grid; gap: 6px; }
.bus-map-btn {
  width: var(--touch, 44px); height: var(--touch, 44px); border: 1px solid var(--line-strong); border-radius: 12px;
  background: var(--surface); color: var(--ink); font-size: 1.2rem; font-weight: var(--weight-bold); cursor: pointer;
}
.bus-map-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
/* The map's own credit, dark on light whatever the page's theme, because it
   sits on the map pictures. */
.bus-map-credit {
  position: absolute; right: 0; bottom: 0; padding: 2px 6px; background: var(--map-credit-bg);
  color: var(--map-credit-ink); font-size: 10px; border-top-left-radius: 6px; pointer-events: none;
}

/* ---- the state of a trip, in words and a colour ---- */
.trip-state { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: var(--weight-semi); }
.trip-state::before { content: ""; width: 10px; height: 10px; border-radius: 50%; background: var(--ink-muted); flex: none; }
.trip-state.is-LIVE::before { background: var(--success); box-shadow: 0 0 0 4px var(--success-bg); }
.trip-state.is-DELAYED::before, .trip-state.is-WAITING::before { background: var(--warning); }
.trip-state.is-OFFLINE::before, .trip-state.is-NO_GPS::before { background: var(--danger); }

/* ---- the driver's page: big, plain, and said once ---- */
.drive-safe {
  display: flex; gap: var(--space-3); align-items: flex-start; padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg); background: var(--warning-bg); color: var(--warning-ink); font-weight: var(--weight-semi);
}
.trip-actions { display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); }
.trip-btn { min-height: 64px; font-size: var(--text-lg, 1.15rem); border-radius: 18px; }
.trip-kind { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-3); }
.trip-kind label {
  display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--touch, 44px);
  padding: var(--space-2) var(--space-4); border: 1.5px solid var(--line); border-radius: var(--radius-full); cursor: pointer;
}
.trip-kind input:checked + span { font-weight: var(--weight-semi); color: var(--primary); }
.trip-kind label:has(input:checked) { border-color: var(--primary); background: var(--primary-soft); }
/* ---- the driver's page: in English or Hindi, steps, and where the trip is ---- */
.lang-switch { display: inline-flex; padding: 3px; gap: 3px; border-radius: var(--radius-full); border: 1px solid var(--line); background: var(--surface); }
.lang-switch button {
  min-height: var(--touch, 44px); min-width: 84px; padding: 0 var(--space-4); border: 0; border-radius: var(--radius-full);
  background: transparent; color: var(--ink-soft); font: inherit; font-weight: var(--weight-semi); cursor: pointer;
}
.lang-switch button[aria-pressed="true"] { background: var(--primary-solid); color: var(--on-primary); }
.how-box > summary { list-style: none; cursor: pointer; }
.how-box > summary::-webkit-details-marker { display: none; }
.how-box > summary::after { content: "▾"; margin-left: auto; color: var(--ink-muted); }
.how-box:not([open]) > summary::after { content: "▸"; }
.how-steps { margin: 0; padding: 0; list-style: none; counter-reset: how; display: grid; gap: var(--space-3); }
.how-steps li { display: flex; gap: var(--space-3); align-items: flex-start; font-size: var(--text-md, 1rem); line-height: 1.5; }
.how-steps li::before {
  counter-increment: how; content: counter(how); flex: none; display: grid; place-items: center;
  width: 30px; height: 30px; border-radius: 50%; background: var(--primary-solid); color: var(--on-primary); font-weight: var(--weight-bold);
}
.trip-banner { padding: var(--space-3) var(--space-4); border-radius: var(--radius-lg); font-weight: var(--weight-semi); line-height: 1.45; }
.trip-banner.is-ok { background: var(--success-bg); color: var(--success-ink); border: 1px solid var(--success-line); }
.trip-banner.is-bad { background: var(--danger-bg); color: var(--danger-ink); border: 1px solid var(--danger-line); }
.next-stop { display: grid; gap: 2px; padding: var(--space-3) var(--space-4); border-radius: var(--radius-lg); background: var(--surface-sunk); }
.next-stop-name { font-size: var(--text-lg, 1.15rem); font-weight: var(--weight-bold); color: var(--ink); overflow-wrap: anywhere; }
.stop-list li.is-next::before { background: var(--primary-solid); color: var(--on-primary); }
.stop-list li.is-next .stop-name { font-weight: var(--weight-bold); }
.trip-status-line { display: grid; gap: var(--space-1); margin: var(--space-3) 0; }
.stop-list { margin: 0; padding: 0; list-style: none; counter-reset: stop; }
.stop-list li { display: flex; gap: var(--space-3); align-items: baseline; padding: var(--space-2) 0; border-bottom: 1px solid var(--line); }
.stop-list li:last-child { border-bottom: 0; }
.stop-list li::before {
  counter-increment: stop; content: counter(stop); flex: none; display: grid; place-items: center;
  width: 24px; height: 24px; border-radius: 50%; background: var(--primary-soft); color: var(--primary);
  font-size: var(--text-xs); font-weight: var(--weight-bold);
}
.stop-list li.is-mine::before { background: var(--primary-solid); color: var(--on-primary); }
.stop-list .stop-name { flex: 1; min-width: 0; overflow-wrap: anywhere; }

/* ---- the Transport Incharge's stop editor ---- */
.stop-editor-row { display: grid; gap: var(--space-2); grid-template-columns: 1.4fr 1.4fr 0.8fr 0.8fr auto; align-items: end; }
@media (max-width: 760px) { .stop-editor-row { grid-template-columns: 1fr 1fr; } .stop-editor-row .stop-editor-actions { grid-column: 1 / -1; } }
.stop-editor-actions { display: flex; gap: var(--space-1); }
.stop-editor-note { grid-column: 1 / -1; }
.route-form, .bus-form, .stops-editor { display: block; }
.route-form.is-new { margin-bottom: var(--space-4); }

/* ---- route versions: what waits, the review, the trips it reaches ---- */
.waiting-version {
  display: grid; gap: var(--space-1); padding: var(--space-3); border-radius: var(--radius-lg);
  border: 1px dashed var(--line-strong); background: var(--surface-sunk);
}
.review-panel { display: grid; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius-lg); border: 1px solid var(--line); }
.review-modes { border: 0; padding: 0; margin: 0; display: grid; gap: var(--space-2); }
.review-modes input[type="date"] { width: auto; display: inline-block; min-height: 36px; padding: 2px 8px; }
.plan-picks { display: grid; gap: var(--space-1); padding-left: var(--space-5); max-height: 260px; overflow-y: auto; }
.review-summary { display: grid; gap: var(--space-2); }

/* ---- a stop this trip has reached: green, on this trip alone ---- */
.stop-list li.is-reached::before { content: "✓"; background: var(--success); color: var(--white); }
.stop-list li.is-reached .stop-name { color: var(--success-ink); }
.bus-map-stop.is-reached .bus-map-stop-dot { background: var(--map-stop-reached); }
/* A stop's name, beside its circle — on the map pictures, which are light in either theme. */
.bus-map-stop-name {
  position: absolute; left: 15px; top: 0; transform: translateY(-50%); max-width: 160px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; padding: 1px 6px; border-radius: var(--radius-full); background: var(--map-credit-bg); color: var(--map-credit-ink);
  font-size: 11px; font-weight: var(--weight-semi); box-shadow: var(--shadow-md);
}
