/* Palette. The four colours below were darkened just far enough to clear WCAG AA
   (4.5:1) in the pairings the app actually renders — white text on the filled
   variants, and the colour itself as text on white/--bg/#eef1f9. Each was solved by
   lowering HSL lightness only, so the hue is unchanged and the shift is the smallest
   one that passes:
     --brand   #4f6df5 -> #4a69f5   4.34 -> 4.54   (lightness 63.5% -> 62.5%)
     --ok      #1fa971 -> #19875a   3.01 -> 4.54   (39.2% -> 31.2%, the worst offender)
     --danger  #e05252 -> #db3838   3.82 -> 4.52   (60.0% -> 54.0%)
     --muted   #6b7285 -> #676e80   4.25 -> 4.53   (47.1% -> 45.3%)
   --brand-dark (5.12), --ink and the confirmed/cancelled pill colours already
   passed and are untouched. --warn is only ever a legend dot, which is non-text
   and so needs 3:1, not 4.5:1. */
:root {
  --bg: #f4f6fb;
  --card: #ffffff;
  --ink: #1c2233;
  --muted: #676e80;
  --brand: #4a69f5;
  --brand-dark: #3b55d9;
  --ok: #19875a;
  --warn: #e8a13c;
  --danger: #db3838;
  --line: #e3e7f0;
  /* Form-control borders only. WCAG 1.4.11 wants 3:1 for the boundary that
     identifies an input, and --line manages just 1.24:1 against a white field.
     Kept as its own variable rather than darkening --line, which would restyle
     every card, table row and tab divider in the app — those are decorative,
     and a card is already distinguished by its fill and shadow. Same hue. */
  --field-line: #7c8fb9;
  --radius: 14px;
  --shadow: 0 1px 3px rgba(28, 34, 51, 0.08);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  background: var(--bg);
  color: var(--ink);
  line-height: 1.45;
  min-height: 100vh;
}

.app-header {
  background: var(--card);
  border-bottom: 1px solid var(--line);
  padding: 6px 18px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  position: sticky;
  top: 0;
  z-index: 20;
}
.app-header h1 { font-size: 17px; font-weight: 700; }
.app-logo {
  grid-column: 2; grid-row: 1;
  width: 150px; height: 150px; border-radius: 50%; object-fit: cover;
  border: 1px solid var(--line); background: #fff;
}
.shell-top {
  grid-column: 1; grid-row: 1;
  justify-self: start;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.shell-meta,
.shell-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  flex-wrap: nowrap;
  white-space: nowrap;
}
.shell-meta {
  color: var(--muted);
  font-size: 13px;
  order: 1;
}
.shell-actions { order: 2; }
.shell-login {
  color: var(--brand);
  font-weight: 600;
  white-space: nowrap;
  font-size: 16.9px; /* 30% larger than the surrounding 13px meta text */
}

/* Child switcher pill (one parent, several children on one login). Styled as a
   ghost/sm button; tapping it opens the "Who are you booking for?" chooser. Kept 30%
   larger than the surrounding meta text — the scoped selector beats button.sm's size. */
.shell-meta .child-switcher {
  font-size: 16.9px;
  max-width: 45vw; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Post-login / on-demand chooser: one full-width button per child, active one marked. */
.child-choices { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.child-choices .child-choice { width: 100%; }
.child-choices .child-choice.active { outline: 2px solid var(--brand); font-weight: 600; }

/* Logo in the top corner of the auth (login/register) pages (inline-end:
   right in LTR, left in RTL — opposite the floating language toggle). */
.corner-logo {
  position: fixed; top: 14px; inset-inline-end: 14px; width: 168px; height: 168px;
  border-radius: 50%; object-fit: cover; background: #fff;
  box-shadow: var(--shadow); z-index: 30;
}

.version-badge {
  position: fixed; top: 6px; inset-inline-end: 8px; z-index: 200;
  font-size: 11px; color: var(--muted); background: var(--card);
  padding: 2px 7px; border-radius: 10px; border: 1px solid var(--line);
  pointer-events: none; opacity: 0.85;
}

/* Language toggle on the pre-login (auth) screens: floats opposite the corner
   logo. inset-inline-start = left in LTR, right in RTL. */
.lang-toggle.auth-lang {
  position: fixed; top: 10px; inset-inline-start: 12px; z-index: 40;
  background: var(--card); border: 1px solid var(--line);
}

@media (max-width: 480px) {
  .corner-logo { width: 120px; height: 120px; }
}

.container { max-width: 760px; margin: 0 auto; padding: 16px 14px 90px; }

.app-footer { padding: 0 14px 28px; text-align: center; }
.app-footer a { color: var(--muted); font-size: 12.5px; text-decoration: none; }
.app-footer a:hover { color: var(--brand); text-decoration: underline; }

.pwa-install-footer { margin-top: 14px; }
.pwa-install-btn {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--brand); color: #fff; border: none;
  padding: 8px 20px; border-radius: var(--radius); font-size: 13px;
  font-weight: 500; cursor: pointer;
}
.pwa-install-btn:hover { background: var(--brand-dark); }
.pwa-install-btn svg { flex-shrink: 0; }
.pwa-shortcut-link {
  display: block; margin-top: 6px;
  background: none; border: none; color: var(--brand);
  font-size: 12px; cursor: pointer; text-decoration: underline;
  padding: 0;
}
.pwa-shortcut-link:hover { color: var(--brand-dark); }

.pwa-steps { list-style: none; counter-reset: pwa-step; margin: 14px 0 0; padding: 0; }
.pwa-steps li {
  counter-increment: pwa-step; display: flex; align-items: center;
  gap: 12px; padding: 14px 0; font-size: 14px; line-height: 1.45;
}
.pwa-steps li + li { border-top: 1px solid var(--line); }
.pwa-steps li::before {
  content: counter(pwa-step); flex-shrink: 0; width: 28px; height: 28px;
  border-radius: 50%; background: var(--brand); color: #fff;
  font-weight: 700; font-size: 13px;
  display: flex; align-items: center; justify-content: center;
}
.pwa-step-icon {
  flex-shrink: 0; width: 40px; height: 40px; border-radius: 10px;
  background: #eef1f9; display: flex; align-items: center; justify-content: center;
}
.pwa-step-icon svg { width: 22px; height: 22px; }

.tabs {
  display: flex; gap: 8px; overflow-x: auto; padding: 12px 14px;
  background: var(--card); border-bottom: 1px solid var(--line);
  position: sticky; top: 164px; z-index: 15;
  -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
html[dir="rtl"] .tabs { justify-content: flex-start; }
html[dir="ltr"] .tabs { justify-content: flex-start; }
.tabs::-webkit-scrollbar { display: none; }
.tab-btn {
  border: 1px solid #d6dced; background: linear-gradient(180deg, #ffffff 0%, #f4f7ff 100%); color: #55607a;
  border-radius: 999px; padding: 11px 18px; font-size: 14.5px; font-weight: 700;
  white-space: nowrap; cursor: pointer; flex-shrink: 0;
  box-shadow: 0 6px 18px rgba(74, 105, 245, 0.08);
  transition: transform 180ms ease, box-shadow 180ms ease, background-color 180ms ease, border-color 180ms ease, color 180ms ease;
}
.tab-btn:hover {
  border-color: rgba(74, 105, 245, 0.42);
  color: var(--brand);
  box-shadow: 0 10px 24px rgba(74, 105, 245, 0.14);
  transform: translateY(-1px);
}
.tab-btn:active { transform: translateY(0) scale(0.98); }
.tab-btn.active {
  background: linear-gradient(180deg, #607cf8 0%, var(--brand) 100%);
  border-color: var(--brand);
  color: #fff;
  box-shadow: 0 12px 26px rgba(74, 105, 245, 0.24);
}
.tab-btn .badge {
  background: var(--danger); color: #fff; border-radius: 999px;
  font-size: 11px; padding: 1px 6px; margin-inline-start: 5px;
}

.card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px; margin-bottom: 14px; box-shadow: var(--shadow);
}
.card h2 { font-size: 16px; margin-bottom: 10px; }
.card h3 { font-size: 14px; margin: 12px 0 6px; }
.muted { color: var(--muted); font-size: 13.5px; }
.small { font-size: 12.5px; }

button, .btn {
  font: inherit; border: none; border-radius: 10px; padding: 10px 16px;
  font-weight: 600; cursor: pointer; background: var(--brand); color: #fff;
  font-size: 14px; touch-action: manipulation;
  transition: transform 180ms ease, box-shadow 180ms ease, background-color 180ms ease, color 180ms ease, border-color 180ms ease;
}
button:hover { background: var(--brand-dark); box-shadow: 0 10px 20px rgba(59, 85, 217, 0.18); transform: translateY(-1px); }
button:active { transform: translateY(0) scale(0.98); box-shadow: none; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
button.secondary { background: #eef1f9; color: var(--ink); }
button.secondary:hover { background: #e2e7f4; }
button.danger { background: var(--danger); }
button.danger:hover { background: #c74444; }
button.ok { background: var(--ok); }
button.ghost {
  background: rgba(74, 105, 245, 0.06); color: var(--brand); padding: 9px 14px;
  border: 1px solid rgba(74, 105, 245, 0.16);
}
button.ghost:hover {
  background: rgba(74, 105, 245, 0.12);
  border-color: rgba(74, 105, 245, 0.3);
}
button.sm { padding: 9px 14px; font-size: 13.5px; border-radius: 999px; }

@media (hover: none) {
  .tab-btn:hover,
  button:hover { transform: none; box-shadow: none; }
}

input, select, textarea {
  font: inherit; width: 100%; padding: 11px 12px; border: 1px solid var(--field-line);
  border-radius: 10px; background: #fff; color: var(--ink); font-size: 15px;
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--brand); outline-offset: -1px; }

/* Keyboard focus indicator. Buttons/links/tabs had none: the reset above strips the
   UA default and only fields re-declared one. :focus-visible keeps the ring off
   mouse clicks but on for keyboard and assistive tech. The 2px offset places it
   OUTSIDE filled controls (e.g. .tab-btn.active), where brand-on-brand would vanish. */
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 4px; }
/* These two only ever take focus programmatically (skip link target, opening modal)
   and are containers, not controls — a ring around the whole region is noise. */
.modal:focus, #tab-content:focus { outline: none; }

/* Off-screen until focused, then pinned over the sticky header (z-index > .app-header). */
.skip-link {
  position: fixed; top: -100px; inset-inline-start: 12px; z-index: 300;
  background: var(--card); color: var(--brand); border: 1px solid var(--brand);
  border-radius: 8px; padding: 10px 16px; font-size: 14px; font-weight: 600;
  text-decoration: none; box-shadow: var(--shadow);
}
.skip-link:focus { top: 10px; }
label { display: block; font-size: 13px; font-weight: 600; color: var(--muted); margin: 10px 0 4px; }
/* Looks like a label but names a GROUP of controls (a button set), which a real
   <label> cannot own — the group carries aria-labelledby pointing here instead. */
.a11y-field-label { display: block; font-size: 13px; font-weight: 600; color: var(--muted); margin: 10px 0 4px; }
.form-row { display: flex; gap: 10px; }
.form-row > * { flex: 1; }
/* Checkbox rows (e.g. notification toggles): keep the box its natural size, not the
   full-width input default, and lay it out inline with its label text. */
.check-row { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 500; color: var(--ink); margin: 8px 0; cursor: pointer; }
.check-row input[type=checkbox] { width: auto; flex: 0 0 auto; margin: 0; padding: 0; accent-color: var(--brand); transform: scale(1.15); }
/* Custom date field: a dd/mm/yyyy display box laid over a visually-hidden native
   <input type=date>. A native date input renders its own text in the browser's UI
   locale (mm/dd/yyyy on an en-US browser) and no page-level attribute overrides it, so
   the visible box is a plain text input we control while the native one still supplies
   the value and the calendar / mobile picker. See public/js/teacher/availability.js.
   direction:ltr keeps the whole field LTR (dates read dd/mm/yyyy left-to-right) so the
   caret's inset-inline-end and the display's padding-inline-end resolve to the SAME
   (right) side in Hebrew RTL too — otherwise the icon overlaps the first digits. */
.date-field { position: relative; direction: ltr; }
.date-field .date-display { cursor: pointer; padding-inline-end: 40px; }
.date-field .date-native {
  position: absolute; inset-inline-start: 12px; bottom: 8px;
  width: 1px; height: 1px; min-height: 0; padding: 0; margin: 0; border: 0;
  opacity: 0; pointer-events: none;
}
.date-field .date-caret {
  position: absolute; inset-inline-end: 12px; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; color: var(--muted); opacity: .75; cursor: pointer;
}

.auth-wrap { max-width: 400px; margin: 8vh auto 0; padding: 0 16px; }
.auth-logo { text-align: center; margin-bottom: 18px; }
.auth-logo .mark {
  width: 64px; height: 64px; border-radius: 18px; background: var(--brand);
  color: #fff; font-size: 30px; display: inline-flex; align-items: center;
  justify-content: center; font-weight: 800; margin-bottom: 10px;
}
.auth-logo h1 { font-size: 20px; }
.auth-logo p { color: var(--muted); font-size: 14px; }

.list-item {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding: 12px 0; border-bottom: 1px solid var(--line);
}
.list-item:last-child { border-bottom: none; }
.list-item .grow { flex: 1; min-width: 0; }
.list-item .title { font-weight: 600; font-size: 14.5px; }
.list-item .sub { color: var(--muted); font-size: 12.5px; }
.actions { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }

.pill {
  display: inline-block; font-size: 11.5px; font-weight: 700; border-radius: 999px;
  padding: 2px 9px; text-transform: uppercase; letter-spacing: 0.3px;
}
.pill.pending { background: #fdf1dc; color: #94650e; }
.pill.confirmed { background: #e0f5ec; color: #157a52; }
.pill.open { background: #e8ecfd; color: var(--brand-dark); }
.pill.held { background: #fdf1dc; color: #94650e; }
.pill.booked { background: #e0f5ec; color: #157a52; }
.pill.cancelled, .pill.rejected, .pill.late_cancelled { background: #fbe4e4; color: #a53030; }
.pill.completed { background: #eef1f9; color: var(--muted); }
.pill.invited { background: #fdf1dc; color: #94650e; }
.pill.active { background: #e0f5ec; color: #157a52; }
.pill.paused { background: #fbe4e4; color: #a53030; }
.pill.removed { background: #eef1f9; color: var(--muted); }
/* Lesson mode — a different dimension from status above (delivery, not lifecycle),
   so these get their own hues rather than reusing/overloading a status colour. */
.pill.zoom { background: #ede7fc; color: #7c3aed; }
.pill.frontal { background: #e0f4f7; color: #0e7490; }

.ticket-hero {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  background: linear-gradient(135deg, var(--brand), #7a5cf0);
  color: #fff; border-radius: var(--radius); padding: 18px; margin-bottom: 14px;
}
.ticket-hero .ticket-info { display: flex; align-items: center; gap: 16px; }
.ticket-hero .num { font-size: 42px; font-weight: 800; line-height: 1; }
.ticket-hero .label { font-size: 14px; opacity: 0.9; }
.ticket-hero.low { background: linear-gradient(135deg, #e8823c, #e05252); }
.btn-payment {
  background: rgba(255, 255, 255, 0.25); color: #fff; border: 1px solid rgba(255, 255, 255, 0.5);
  padding: 8px 16px; border-radius: 6px; font-size: 14px; font-weight: 600;
  text-decoration: none; display: inline-block; transition: all 0.2s;
  cursor: pointer;
}
.btn-payment:hover { background: rgba(255, 255, 255, 0.35); border-color: rgba(255, 255, 255, 0.7); }

.day-group { margin-bottom: 4px; }
.day-group h3 {
  font-size: 13px; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.4px; margin: 14px 0 8px;
}
.slot-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.slot-chip {
  border: 1.5px solid var(--brand); color: var(--brand); background: var(--card);
  border-radius: 10px; padding: 9px 14px; font-weight: 700; font-size: 14px; cursor: pointer;
}
.slot-chip:hover { background: var(--brand); color: #fff; }

.confirm-slot {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  background: linear-gradient(135deg, var(--brand), #7a5cf0);
  color: #fff; border-radius: var(--radius); padding: 16px; margin: 14px 0;
}
.confirm-slot-day { font-size: 15px; font-weight: 600; opacity: 0.95; }
.confirm-slot-time { font-size: 30px; font-weight: 800; line-height: 1; }

.toast {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
  background: var(--ink); color: #fff; border-radius: 10px; padding: 12px 20px;
  font-size: 14px; z-index: 100; max-width: 90vw; box-shadow: 0 4px 14px rgba(0,0,0,0.25);
  animation: toast-in 0.2s ease-out;
}
.toast.error { background: var(--danger); }
.toast.ok { background: var(--ok); }
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 8px); } }

.modal-backdrop {
  position: fixed; inset: 0; background: rgba(20, 24, 38, 0.5);
  display: flex; align-items: center; justify-content: center; z-index: 50; padding: 16px;
}
.modal {
  background: var(--card); border-radius: var(--radius); padding: 20px;
  width: 100%; max-width: 420px; max-height: 85vh; overflow-y: auto;
}
.modal h2 { font-size: 17px; margin-bottom: 8px; }
.modal .modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; }

/* Assign-student modal (Calendar tab) */
.assign-search {
  width: 100%; padding: 8px 10px; margin-bottom: 8px;
  border: 1px solid var(--field-line); border-radius: 8px;
  font-size: 14px; background: var(--card); color: var(--ink);
}
.assign-list { max-height: 260px; overflow-y: auto; border: 1px solid var(--line); border-radius: 8px; }
.assign-item {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; padding: 9px 12px; border: none; border-bottom: 1px solid var(--line);
  background: none; cursor: pointer; font-size: 14px; color: var(--ink); text-align: start;
}
.assign-item:last-child { border-bottom: none; }
.assign-item:hover:not(.disabled) { background: var(--bg); }
.assign-item.disabled { opacity: .5; cursor: not-allowed; }
.assign-tickets { font-size: 12px; color: var(--muted); white-space: nowrap; }
.assign-tickets.zero { color: var(--danger); }
.mode-toggle { display: flex; gap: 6px; }
.mode-toggle button.active { background: var(--brand); color: #fff; }
.field-row { display: flex; align-items: center; gap: 10px; margin: 12px 0; }
.field-row label { font-size: 14px; white-space: nowrap; }

.remove-day-list { margin: 10px 0; max-height: 240px; overflow-y: auto; overflow-x: hidden; border: 1px solid var(--line); border-radius: 8px; }
.remove-day-row { display: flex; align-items: center; gap: 8px; padding: 8px 10px; font-size: 13.5px; border-bottom: 1px solid var(--line); cursor: pointer; }
.remove-day-row:last-child { border-bottom: none; }
.remove-day-row input { flex-shrink: 0; width: 18px; height: 18px; }
.remove-day-row span { flex: 1; min-width: 0; overflow-wrap: break-word; }
.remove-day-row.excluded { opacity: 0.5; text-decoration: line-through; }

.divider { display: flex; align-items: center; gap: 10px; color: var(--muted); font-size: 12.5px; margin: 14px 0; }
.divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: var(--line); }

.empty { text-align: center; color: var(--muted); padding: 24px 10px; font-size: 14px; }
.error-text { color: var(--danger); font-size: 13.5px; margin-top: 8px; }

table.mini { width: 100%; border-collapse: collapse; font-size: 13px; }
table.mini th, table.mini td { text-align: start; padding: 6px 8px; border-bottom: 1px solid var(--line); }
table.mini th { color: var(--muted); font-weight: 600; font-size: 12px; }

pre.email-body { white-space: pre-wrap; font: 12.5px/1.5 inherit; background: #f7f8fc; border-radius: 8px; padding: 10px; margin-top: 6px; }

/* --- Month calendar --- */
/* The 7-column grid mirrors automatically under [dir="rtl"] (Sunday lands on
   the right), matching printed Hebrew calendars — deliberate product choice.
   Chevron glyphs are mirrored so "back" points along the reading direction. */
[dir="rtl"] .cal-nav [data-nav] { transform: scaleX(-1); }
.cal-nav { display: flex; align-items: center; justify-content: space-between; margin: 8px 0 10px; }
.cal-title { font-weight: 700; font-size: 15px; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.cal-head div {
  text-align: center; font-size: 11px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; padding: 4px 0;
}
.cal-cell {
  min-height: 52px; border: 1px solid var(--line); border-radius: 8px;
  padding: 3px 4px; background: #fff; overflow: hidden; position: relative;
}
.cal-cell.blank { border: none; background: transparent; }
.cal-cell.today { border-color: var(--brand); box-shadow: inset 0 0 0 1px var(--brand); }
.cal-cell.clickable { cursor: pointer; }
.cal-cell.clickable:hover { background: #f2f5ff; }
.cal-cell.has-slots { background: #eef2ff; border-color: #c3cdf7; }
.cal-cell.selected { background: var(--brand); border-color: var(--brand); }
.cal-cell.selected .cal-daynum { color: #fff; }
.cal-daynum { font-size: 11.5px; font-weight: 700; color: var(--muted); }
.cal-badge {
  position: absolute; top: 3px; inset-inline-end: 4px; background: var(--brand); color: #fff;
  font-size: 10px; font-weight: 700; border-radius: 999px; padding: 1px 5px;
}
.cal-cell.selected .cal-badge { background: #fff; color: var(--brand); }
.cal-chip {
  font-size: 9.5px; font-weight: 700; border-radius: 4px; padding: 1px 3px;
  margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-chip.confirmed { background: #e0f5ec; color: #157a52; }
.cal-chip.pending { background: #fdf1dc; color: #94650e; }
.cal-chip.open { background: #e8ecfd; color: var(--brand-dark); }
.cal-more { font-size: 9.5px; color: var(--muted); margin-top: 2px; }
.cal-legend { display: flex; gap: 14px; font-size: 12.5px; color: var(--muted); margin-bottom: 10px; flex-wrap: wrap; }
.cal-legend .dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-inline-end: 5px; }
.cal-legend .dot.confirmed { background: var(--ok); }
.cal-legend .dot.pending { background: var(--warn); }
.cal-legend .dot.open { background: var(--brand); }
.day-title { font-size: 14px; margin: 14px 0 8px; }
.ok-box { background: #e0f5ec; border-radius: 10px; padding: 12px 14px; }

/* --- Calendar view toggle (Month / Week) --- */
.cal-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.cal-toolbar .cal-legend { margin-bottom: 0; }
.view-toggle { display: inline-flex; gap: 4px; }
.view-toggle button {
  background: #eef1f9; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: 4px 12px;
}
.view-toggle button.active { background: var(--brand); border-color: var(--brand); color: #fff; }
/* Week grid: fewer rows, so give each day real vertical room. */
.cal-grid.week .cal-cell { min-height: 120px; }

/* --- Multi-pane layouts (teacher dashboard) --- */
.split { display: grid; gap: 14px; align-items: start; }
@media (min-width: 900px) {
  .container.wide {
    max-width: none;
    width: 100%;
    padding-inline: clamp(18px, 2.4vw, 36px);
  }
  .split.two { grid-template-columns: 320px minmax(0, 1fr); }
  .split.three { grid-template-columns:
      minmax(240px, 1fr)      /* Students */
      minmax(0, 2.2fr)        /* Calendar */
      minmax(260px, 1.2fr);   /* Daily */
  }
  .teacher-calendar-layout {
    grid-template-columns:
      clamp(220px, 17vw, 320px)
      minmax(0, 1fr)
      clamp(250px, 20vw, 360px);
    align-items: stretch;
    min-height: calc(100vh - 255px);
  }
  .teacher-calendar-layout > .card {
    height: 100%;
    margin-bottom: 0;
  }
  .teacher-calendar-layout .cal-main {
    display: flex;
    flex-direction: column;
    min-height: 0;
  }
  .teacher-calendar-layout #teacher-cal {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-height: 0;
  }
  .teacher-calendar-layout #teacher-cal > #cal-cells.cal-grid {
    flex: 1;
    grid-auto-rows: minmax(0, 1fr);
    align-content: stretch;
  }
  .teacher-calendar-layout #teacher-cal > #cal-cells.cal-grid.week {
    min-height: clamp(240px, 34vh, 420px);
  }
  .teacher-calendar-layout .cal-cell {
    min-height: clamp(78px, 11vh, 132px);
  }
}
@media (max-width: 899px) {
  /* On phones: month calendar first, then the day view, students list last. */
  .split.three .cal-main { order: -1; }
  .split.three > .card:first-child { order: 1; }
  .teacher-calendar-layout > .card { margin-bottom: 0; }
}
.student-card {
  border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px;
  cursor: pointer; margin-bottom: 8px; background: #fff;
}
.student-card:hover { background: #f2f5ff; }
.student-card.selected { border-color: var(--brand); background: #eef2ff; box-shadow: inset 0 0 0 1px var(--brand); }
.student-card .title { font-weight: 700; font-size: 14.5px; }
.student-card .sub { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
/* Removed (archived) students: kept for their lesson history but signed-out & wiped.
   Dashed border + muted title read as "archived" without dimming text below the
   contrast floor — colours come from theme vars so this holds in dark / high-contrast. */
.student-card.removed { border-style: dashed; }
.student-card.removed .title { color: var(--muted); }
.list-section-label {
  font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px;
  color: var(--muted); margin: 14px 4px 8px;
}
.tickets-left {
  margin-top: 12px; background: #eef2ff; border-radius: 10px; padding: 10px 14px;
  font-weight: 600; color: var(--brand-dark);
}
.tickets-left.low { background: #fdecec; color: #a53030; }
.tickets-left .num { font-size: 26px; font-weight: 800; margin-inline-end: 4px; }
.count-pill { background: #eef1f9; border-radius: 999px; padding: 2px 9px; font-size: 12px; font-weight: 700; color: var(--muted); }
.cal-cell.selected-day { outline: 2px solid var(--brand); outline-offset: -1px; }

/* --- Availability: calendar + day detail --- */
@media (min-width: 900px) {
  .split.avail-split { grid-template-columns: minmax(0, 1.7fr) minmax(260px, 1fr); }
}
/* Taller month cells here so the count badge + open/booked breakdown fit. */
#avail-cal .cal-cell { min-height: 60px; }
.avail-mini { display: flex; flex-direction: column; gap: 1px; margin-top: 3px; }
.avail-mini span { font-size: 9.5px; font-weight: 700; line-height: 1.25; white-space: nowrap; }
.avail-mini .o { color: var(--brand-dark); }
.avail-mini .b { color: #157a52; }

@media (max-width: 480px) {
  .cal-cell { min-height: 44px; padding: 2px 3px; }
  .cal-chip { font-size: 8.5px; }
}

@media (min-width: 700px) {
  .container { padding-top: 22px; }
  .card { padding: 20px; }
}

/* ============================================================================
   Accessibility widget (public/js/a11y-widget.js) — text size, contrast, motion.
   Everything below keys off data attributes on <html>, which the widget sets and
   the inline head scripts in index.html / accessibility.html re-apply before the
   first paint. Default state (no attributes / "normal") changes nothing above.
   ============================================================================ */

/* --- Text size ---
   CSS zoom, not a root font-size: this stylesheet sizes everything in px, so
   scaling a rem base would move nothing at all.
   The zoom goes on BODY, not on <html>. Zooming the root leaves the root's own
   width at the unzoomed viewport (375px on a phone), so full-width children still
   resolve 100% against 375 and are then magnified — 116px of horizontal overflow
   at 150% on a phone, which is exactly the reflow failure text scaling exists to
   avoid. Zooming body instead lays its contents out in a correspondingly narrower
   box, so the page reflows and stays scroll-free. */
html[data-a11y-text] body {
  /* On BODY, not <html>: the browser then resolves body's own width in the zoomed
     coordinate space (250px of layout room at 150% on a 375px phone), so the
     responsive breakpoints still fire and the page reflows. Do NOT also divide
     body's width here — that double-counts the zoom, squeezes body below its
     content's minimum width, and reintroduces the sideways scroll it was meant
     to remove. Viewport units, however, ignore zoom and do need correcting. */
  zoom: var(--a11y-zoom, 1);
  min-height: calc(100vh / var(--a11y-zoom, 1));
}
html[data-a11y-text] .modal { max-height: calc(85vh / var(--a11y-zoom, 1)); }
/* Fixed-position elements sit outside body's box, so they need the same correction. */
html[data-a11y-text] .toast { max-width: calc(90vw / var(--a11y-zoom, 1)); }

/* --- Stop animations ---
   The widget toggle, plus the OS-level setting honoured unconditionally: a user who
   has asked their system for reduced motion should not have to ask again here. */
html[data-a11y-motion="reduced"] *,
html[data-a11y-motion="reduced"] *::before,
html[data-a11y-motion="reduced"] *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  scroll-behavior: auto !important;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --- The widget itself ---
   Bottom inline-start is the one free corner: the version badge, auth corner logo
   and language toggle all sit along the top, and the toast is bottom-centre. */
.a11y-widget { position: fixed; bottom: 16px; inset-inline-start: 16px; z-index: 150; }
.a11y-toggle {
  width: 36px; height: 36px; border-radius: 50%; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--brand); color: #fff; box-shadow: 0 2px 10px rgba(28, 34, 51, 0.3);
}
.a11y-toggle:hover { background: var(--brand-dark); }
.a11y-panel {
  position: absolute; bottom: 60px; inset-inline-start: 0;
  /* vw/vh are immune to zoom, so divide them down or the panel outgrows the screen
     at 150% on a phone (--a11y-zoom is 1 until the widget sets it). */
  width: 280px; max-width: calc((100vw - 32px) / var(--a11y-zoom, 1));
  max-height: calc((100vh - 120px) / var(--a11y-zoom, 1)); overflow-y: auto;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 6px 24px rgba(28, 34, 51, 0.22); padding: 14px;
}
.a11y-panel[hidden] { display: none; }
.a11y-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.a11y-panel-title { font-size: 15px; font-weight: 700; }
.a11y-close { background: transparent; color: var(--muted); font-size: 22px; line-height: 1; padding: 2px 8px; }
.a11y-close:hover { background: transparent; color: var(--ink); }
.a11y-group { margin-bottom: 14px; }
.a11y-group-label { font-size: 12px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.4px; margin-bottom: 6px; }
.a11y-options { display: flex; flex-wrap: wrap; gap: 6px; }
.a11y-opt {
  background: var(--card); color: var(--ink); border: 1px solid var(--line);
  border-radius: 8px; padding: 7px 11px; font-size: 13px; font-weight: 600;
}
.a11y-opt:hover { background: var(--bg); }
/* aria-pressed IS the state — style from it so the visual and the accessible
   state can never drift apart. --brand-dark, not --brand: white on --brand measures
   4.34:1, just under AA, and this widget of all things should clear the bar. The
   global palette is Phase 4's job. */
.a11y-opt[aria-pressed="true"] { background: var(--brand-dark); border-color: var(--brand-dark); color: #fff; }
.a11y-opt[aria-pressed="true"]:hover { background: var(--brand-dark); filter: brightness(0.92); }
/* Each text-size button previews its own step. */
.a11y-size-100 { font-size: 12px; }
.a11y-size-115 { font-size: 13.5px; }
.a11y-size-130 { font-size: 15px; }
.a11y-size-150 { font-size: 17px; }
.a11y-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; border-top: 1px solid var(--line); padding-top: 10px; }
.a11y-statement { font-size: 12px; color: var(--brand-dark); text-decoration: none; }
.a11y-statement:hover { text-decoration: underline; }

/* --- PWA install banner (legacy, kept for update banner keyframe) --- */

/* --- PWA update banner --- */
.pwa-update-banner {
  position: fixed; top: 0; left: 0; right: 0; z-index: 95;
  background: var(--brand); color: #fff;
  padding: 12px 16px; padding-top: calc(12px + env(safe-area-inset-top, 0px));
  display: flex; align-items: center; gap: 12px;
  animation: pwaSlideDown 300ms ease-out;
}
.pwa-update-banner .pwa-text { flex: 1; font-size: 14px; font-weight: 500; }
.pwa-update-banner button { background: #fff; color: var(--brand); }
.pwa-update-banner button:hover { background: #eef1f9; }

@keyframes pwaSlideDown {
  from { transform: translateY(-100%); }
  to { transform: translateY(0); }
}

/* --- Standalone / installed PWA adjustments --- */
@media (display-mode: standalone) {
  .pwa-install-footer { display: none !important; }
  body { overscroll-behavior-y: none; }
}

/* Safe-area padding for notched devices (iPhone X+, etc.) */
@supports (padding-bottom: env(safe-area-inset-bottom)) {
  .container { padding-bottom: calc(90px + env(safe-area-inset-bottom, 0px)); }
  .a11y-widget { bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
  .toast { bottom: calc(20px + env(safe-area-inset-bottom, 0px)); }
  .app-footer { padding-bottom: calc(28px + env(safe-area-inset-bottom, 0px)); }
}

/* --- High contrast -------------------------------------------------------- */
html[data-a11y-contrast="high"] {
  --bg: #ffffff;
  --card: #ffffff;
  --ink: #000000;
  --muted: #1a1a1a;
  --brand: #0b3ec9;
  --brand-dark: #06258f;
  --ok: #0a6b3d;
  --warn: #6d4200;
  --danger: #b00014;
  --line: #000000;
  --shadow: none;
}
html[data-a11y-contrast="high"] button.secondary,
html[data-a11y-contrast="high"] .a11y-opt,
html[data-a11y-contrast="high"] .view-toggle button {
  background: #fff; color: #000; border: 2px solid #000;
}
html[data-a11y-contrast="high"] button.secondary:hover,
html[data-a11y-contrast="high"] button.ghost:hover,
html[data-a11y-contrast="high"] .a11y-opt:hover { background: #e6e6e6; }
html[data-a11y-contrast="high"] input,
html[data-a11y-contrast="high"] select,
html[data-a11y-contrast="high"] textarea { background: #fff; color: #000; border: 2px solid #000; }
html[data-a11y-contrast="high"] .card,
html[data-a11y-contrast="high"] .cal-cell,
html[data-a11y-contrast="high"] .student-card,
html[data-a11y-contrast="high"] .modal,
html[data-a11y-contrast="high"] .a11y-panel { background: #fff; border: 2px solid #000; }
html[data-a11y-contrast="high"] .cal-cell.blank { border: none; }
/* Status colours become solid fills with white text — semantics kept, contrast maxed. */
html[data-a11y-contrast="high"] .pill.confirmed,
html[data-a11y-contrast="high"] .pill.booked,
html[data-a11y-contrast="high"] .pill.active,
html[data-a11y-contrast="high"] .cal-chip.confirmed { background: #0a6b3d; color: #fff; }
html[data-a11y-contrast="high"] .pill.pending,
html[data-a11y-contrast="high"] .pill.held,
html[data-a11y-contrast="high"] .pill.invited,
html[data-a11y-contrast="high"] .cal-chip.pending { background: #6d4200; color: #fff; }
html[data-a11y-contrast="high"] .pill.open,
html[data-a11y-contrast="high"] .cal-chip.open { background: #0b3ec9; color: #fff; }
html[data-a11y-contrast="high"] .pill.cancelled,
html[data-a11y-contrast="high"] .pill.rejected,
html[data-a11y-contrast="high"] .pill.late_cancelled,
html[data-a11y-contrast="high"] .pill.paused { background: #b00014; color: #fff; }
html[data-a11y-contrast="high"] .pill.completed,
html[data-a11y-contrast="high"] .pill.removed { background: #000; color: #fff; }
html[data-a11y-contrast="high"] .pill.zoom { background: #4c1d95; color: #fff; }
html[data-a11y-contrast="high"] .pill.frontal { background: #164e63; color: #fff; }
html[data-a11y-contrast="high"] .count-pill { background: #000; color: #fff; }
/* Decorative gradients flatten to a solid so white text has a known ratio. */
html[data-a11y-contrast="high"] .ticket-hero,
html[data-a11y-contrast="high"] .confirm-slot { background: #06258f; }
html[data-a11y-contrast="high"] .ticket-hero.low { background: #8a0010; }
html[data-a11y-contrast="high"] .ok-box { background: #fff; border: 2px solid #0a6b3d; }
html[data-a11y-contrast="high"] .tickets-left { background: #fff; border: 2px solid #0b3ec9; color: #06258f; }
html[data-a11y-contrast="high"] .tickets-left.low { background: #fff; border-color: #b00014; color: #8a0010; }
html[data-a11y-contrast="high"] .cal-cell.has-slots { background: #fff; border: 3px solid #0b3ec9; }
html[data-a11y-contrast="high"] .cal-cell.clickable:hover { background: #e6e6e6; }
html[data-a11y-contrast="high"] pre.email-body { background: #fff; border: 2px solid #000; }
html[data-a11y-contrast="high"] .toast { background: #000; color: #fff; border: 2px solid #fff; }
html[data-a11y-contrast="high"] .modal-backdrop { background: rgba(0, 0, 0, 0.75); }

/* --- Dark ----------------------------------------------------------------- */
html[data-a11y-contrast="dark"] {
  --bg: #0f131b;
  --card: #181d28;
  --ink: #f0f3f9;
  --muted: #b3bccd;
  --brand: #8fa6ff;
  --brand-dark: #b3c3ff;
  --ok: #4ad295;
  --warn: #f0b45e;
  --danger: #ff8b8b;
  --line: #2b3446;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
/* --brand is light here so it works as link/border colour on a dark page, which
   means anything FILLED with it needs dark text instead of the light default. */
html[data-a11y-contrast="dark"] button,
html[data-a11y-contrast="dark"] .btn,
html[data-a11y-contrast="dark"] .tab-btn.active,
html[data-a11y-contrast="dark"] .view-toggle button.active,
html[data-a11y-contrast="dark"] .auth-logo .mark,
html[data-a11y-contrast="dark"] .cal-badge,
html[data-a11y-contrast="dark"] .slot-chip:hover,
html[data-a11y-contrast="dark"] .a11y-opt[aria-pressed="true"],
html[data-a11y-contrast="dark"] .a11y-toggle,
html[data-a11y-contrast="dark"] .tab-btn .badge,
html[data-a11y-contrast="dark"] .cal-cell.selected .cal-daynum,
html[data-a11y-contrast="dark"] .toast { color: #0f131b; }
/* Transparent-background buttons must keep LIGHT text — the blanket rule above
   turns button text dark for the brand-filled ones, which on these lands dark-on-dark. */
html[data-a11y-contrast="dark"] button.ghost { background: transparent; color: var(--brand); }
html[data-a11y-contrast="dark"] .a11y-close { background: transparent; color: var(--muted); }
html[data-a11y-contrast="dark"] .a11y-close:hover { color: var(--ink); }
html[data-a11y-contrast="dark"] button.ghost:hover,
html[data-a11y-contrast="dark"] button.secondary,
html[data-a11y-contrast="dark"] .view-toggle button,
html[data-a11y-contrast="dark"] .count-pill,
html[data-a11y-contrast="dark"] .pwa-step-icon { background: #262d3b; color: var(--ink); }
html[data-a11y-contrast="dark"] button.secondary:hover,
html[data-a11y-contrast="dark"] .a11y-opt:hover { background: #313a4c; }
html[data-a11y-contrast="dark"] .a11y-opt,
html[data-a11y-contrast="dark"] .slot-chip,
html[data-a11y-contrast="dark"] .tab-btn { background: var(--card); color: var(--ink); }
/* The three rules above are more specific than the base .tab-btn.active /
   [aria-pressed="true"] / :hover rules they sit on top of, so without this they
   strip the brand fill from the SELECTED state while the block further up has
   already switched its text to dark — dark-on-dark, measured at 1.1:1. Put the
   fill back at a specificity that wins. */
html[data-a11y-contrast="dark"] .tab-btn.active,
html[data-a11y-contrast="dark"] .view-toggle button.active,
html[data-a11y-contrast="dark"] .a11y-opt[aria-pressed="true"],
html[data-a11y-contrast="dark"] .slot-chip:hover { background: var(--brand); border-color: var(--brand); }
/* --line is a near-invisible 1.3:1 against the dark field fill; fields need a
   3:1 boundary of their own here too (WCAG 1.4.11). */
html[data-a11y-contrast="dark"] input,
html[data-a11y-contrast="dark"] select,
html[data-a11y-contrast="dark"] textarea { background: #10151e; color: var(--ink); border-color: #6e7b96; }
html[data-a11y-contrast="dark"] .cal-cell,
html[data-a11y-contrast="dark"] .student-card { background: var(--card); }
html[data-a11y-contrast="dark"] .cal-cell.blank { background: transparent; }
html[data-a11y-contrast="dark"] .cal-cell.clickable:hover,
html[data-a11y-contrast="dark"] .student-card:hover { background: #222a38; }
html[data-a11y-contrast="dark"] .cal-cell.has-slots,
html[data-a11y-contrast="dark"] .student-card.selected,
html[data-a11y-contrast="dark"] .tickets-left { background: #1c2440; border-color: #3a4770; color: var(--ink); }
/* Pills whose text colour comes from a variable invert badly (light on light);
   the ones with a hardcoded dark-on-pastel pair stay readable as-is. */
html[data-a11y-contrast="dark"] .pill.open,
html[data-a11y-contrast="dark"] .cal-chip.open { background: #1c2440; color: #b3c3ff; }
html[data-a11y-contrast="dark"] .pill.completed,
html[data-a11y-contrast="dark"] .pill.removed { background: #262d3b; color: #c3ccdd; }
html[data-a11y-contrast="dark"] .ok-box { background: #12301f; }
html[data-a11y-contrast="dark"] pre.email-body { background: #10151e; }
html[data-a11y-contrast="dark"] .ticket-hero,
html[data-a11y-contrast="dark"] .confirm-slot { background: linear-gradient(135deg, #3b4fd8, #6a48d8); color: #fff; }
html[data-a11y-contrast="dark"] .btn-payment { color: #fff; }
html[data-a11y-contrast="dark"] .cal-cell.selected .cal-badge { background: #0f131b; color: #b3c3ff; }
html[data-a11y-contrast="dark"] .modal-backdrop { background: rgba(0, 0, 0, 0.7); }
