:root {
  --green: #1E6B49;
  --green-dark: #154D37;
  --gold: #B68A45;
  --red: #B24B3D;
  --bg: #F6F4EF;
  --panel: #FFFFFF;
  --border: #E1DED2;
  --text: #14201B;
  --muted: #5B6860;
  /* Soft tints for the finder views: matchplay green, coaching gold,
     social slate, full red. */
  --green-soft: #E3EFE8;
  --gold-soft: #F4EBDD;
  --slate: #4F6D8A;
  --slate-soft: #E4EAF1;
  --red-soft: #F6E3E0;
  --hatch: #EFEDE5;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, "Segoe UI", Roboto, sans-serif;
}

.app-shell { display: flex; align-items: flex-start; min-height: 100vh; }
/* Real gap found live: .main-panel (base.html's own <main>) never had
   a desktop width rule at all — a flex item with no flex-basis/width
   of its own just grows to fill whatever's left beside the sidebar,
   which on any reasonably wide monitor left cards and tables
   stretched edge-to-edge with a lot of dead whitespace inside them.
   Capped to a comfortable reading/working width and centred in the
   remaining flex space (margin:auto DOES center a flex item within
   its container, not just block-level content) instead. */
.main-panel { max-width: 900px; width: 100%; margin: 0 auto; padding: 32px 32px; }
/* Login/Setup have no sidebar, so .content (flex:1 1 auto, capped at
   max-width) is the ONLY flex child — with nothing else in the row, a
   plain flex layout leaves its box flush against the left edge rather
   than centering it, which is what left the login card stranded in
   the top-left corner instead of top-centre. Scoped to just this case
   (a .logged-out modifier, set by base.html) so every logged-in page's
   existing content-hugs-the-sidebar layout is untouched. */
.app-shell.logged-out { justify-content: center; }

.sidebar {
  flex: 0 0 260px;
  /* Without this, a flex item's automatic min-width defaults to its
     content's min-content size — so any unbreakable content inside
     (an overflowing toggle label, a native date input's own intrinsic
     minimum) could silently force the whole sidebar wider than its
     260px flex-basis, then narrower again once that content changed
     on the next render. That's the real cause behind the pane
     visibly "breathing" wider/narrower while a booking ran — pinning
     min-width to 0 makes the 260px basis an actual hard cap; anything
     that still doesn't fit is clipped by overflow-x below instead of
     resizing the pane. */
  min-width: 0;
  overflow-x: hidden;
  /* Reserves the vertical scrollbar's own width permanently, whether
     or not one is currently needed — without this, a classic
     (non-overlay) scrollbar rendering — e.g. Windows' own Basic theme,
     common over RDP — appearing/disappearing repeatedly changes the
     content box's available width each time, which can retrigger the
     exact same width-driven reflow min-width:0 above was already
     written to prevent, just in the OTHER direction (a scrollbar
     stealing width rather than unbreakable content adding it).
     Reported live 2026-09-15: an oscillating sidebar + scrollbar while
     a booking ran, on Edge under RDP. Unsupported browsers just ignore
     this property and keep their previous (stable, for them) rendering. */
  scrollbar-gutter: stable;
  /* Sticky, not fixed: fixed positioning would pull it out of the
     flex flow entirely, breaking the @media stack-above-content
     layout on narrow screens below. Sticky keeps it in flow (so that
     still works) while pinning it to the viewport as the content
     column scrolls past it — the actual ask: don't let the nav
     scroll away with a long page (Help, a big My Bookings table). */
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  background: #11201A;
  color: #F3F1E8;
  padding: 16px 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.sidebar-brand { display: flex; align-items: center; justify-content: center; }
.brand-logo { width: 100%; height: auto; aspect-ratio: 1 / 1; border-radius: 18px; display: block; flex-shrink: 0; }
.brand-link { display: block; width: 100%; border-radius: 18px; transition: transform 0.15s ease, box-shadow 0.15s ease; }
.brand-link:hover { transform: scale(1.03); box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25); }
.brand-link:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .brand-link { transition: none; } .brand-link:hover { transform: none; } }
.login-logo { height: 140px; width: 140px; border-radius: 22px; display: block; margin: 0 auto 12px; }
.login-actions { text-align: center; }
.version { font-weight: 400; opacity: 0.6; font-size: 0.8em; }

.sidebar-nav { display: flex; flex-direction: column; gap: 1px; }
.nav-group {
  margin: 9px 10px 3px; font-size: 0.7em; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: #7F9188;
}
.nav-group:first-child { margin-top: 0; }
.nav-divider { height: 1px; background: #2A3A32; margin: 7px 6px; }
.sidebar-nav a {
  display: flex; align-items: center; gap: 10px;
  color: #DCE3DE; text-decoration: none; padding: 7px 10px;
  border-radius: 8px; font-size: 0.94em; border-left: 3px solid transparent;
}
.sidebar-nav .nav-icon { flex: 0 0 auto; color: #9DB0A6; }
.sidebar-nav .nav-label { flex: 1 1 auto; min-width: 0; }
.sidebar-nav a:hover { background: rgba(255,255,255,0.06); color: #F3F1E8; }
.sidebar-nav a:hover .nav-icon { color: #F3F1E8; }
.sidebar-nav a.active {
  background: rgba(91, 227, 143, 0.12); border-left-color: #5BE38F; color: #FFFFFF; font-weight: 700;
}
.sidebar-nav a.active .nav-icon { color: #5BE38F; }
.sidebar-nav a:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
/* Counts and "on" dots share one right-hand column, the same width,
   so the dots line up under the counts. */
.nav-count, .nav-on { flex: 0 0 auto; min-width: 24px; display: inline-flex; align-items: center; justify-content: center; }
.nav-count {
  padding: 1px 7px; border-radius: 999px; background: #2A3A32; color: #F3F1E8;
  font-size: 0.78em; font-weight: 700; font-variant-numeric: tabular-nums;
}
.nav-on::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
  background: #5BE38F; box-shadow: 0 0 6px rgba(91, 227, 143, 0.6);
}
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Player Rating+Credit / "Next booking" countdown each get their own
   bordered box, matching the sidebar's own dark-panel look. */
/* Booking engine status line (desktop sidebar and the phone drawer) —
   the desktop counterpart of the phone's green » tab. */
.engine-line {
  display: flex; align-items: center; gap: 8px; font-size: 0.85em; color: #B8C4BD;
  padding: 2px 4px;
}
.engine-line .engine-dot {
  width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid #8FA096; flex-shrink: 0;
}
.engine-line.engine-warm { color: #F3F1E8; }
.engine-line.engine-warm .engine-dot { background: #5BE38F; border-color: #5BE38F; box-shadow: 0 0 6px rgba(91, 227, 143, 0.6); }
.status-tiles { border: 1px solid #2A3A32; border-radius: 10px; overflow: hidden; }
.rating-block, .countdown-block { padding: 8px 12px; }
.countdown-block { border-top: 1px solid #2A3A32; text-decoration: none; }
.countdown-block:hover { background: rgba(255,255,255,0.05); }
.refresh-icon { opacity: 0.7; vertical-align: -2px; margin-left: 4px; }
.credit-refresh-btn:hover .refresh-icon { opacity: 1; }
.credit-refresh-btn:disabled .refresh-icon { animation: cc-spin 1s linear infinite; }
@keyframes cc-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .credit-refresh-btn:disabled .refresh-icon { animation: none; } }
.test-mode-chip {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: 8px;
  background: rgba(182, 138, 69, 0.16); border: 1px solid rgba(182, 138, 69, 0.45);
  color: #E9C98F; font-size: 0.85em; font-weight: 700; text-decoration: none;
}
.test-mode-chip:hover { background: rgba(182, 138, 69, 0.26); }
.chip-icon { flex: 0 0 auto; }
.login-rejected-chip {
  display: grid; gap: 2px; padding: 8px 10px; border-radius: 8px; text-decoration: none;
  background: rgba(178, 75, 61, 0.2); border: 1px solid rgba(226, 120, 104, 0.6); color: #F4B8AE; font-size: 0.85em;
}
.login-rejected-chip strong { color: #FFD4CC; }
.login-rejected-chip:hover { background: rgba(178, 75, 61, 0.3); }
.settings-rejected { margin: 0 0 14px; }
.login-check { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 4px; }
.login-check-button { padding: 8px 14px; font-size: 0.9em; }
.login-check-result { font-size: 0.9em; color: var(--muted); }
.login-check-result.is-ok { color: var(--green-dark); font-weight: 600; }
.login-check-result.is-bad { color: var(--red); font-weight: 600; }
.tour-step .login-check { margin-top: 14px; }
.setup-card .login-check { margin-top: 14px; }
.setup-done { margin-top: 16px; padding: 14px; border-radius: 10px; background: var(--green-soft); }
.setup-done h2 { margin: 0 0 4px; font-size: 1.1em; color: var(--green-dark); }
.setup-done p { margin: 0 0 12px; }
.setup-done .button-row a { text-decoration: none; }
.setup-warning { margin: 6px 0 0; font-size: 0.85em; color: var(--muted); }
/* Setup wizard — one narrow column, big touch targets, same on desktop. */
.wizard { max-width: 560px; margin-left: auto; margin-right: auto; }
.wizard-steps { list-style: none; display: flex; gap: 6px; margin: 4px 0 18px; padding: 0; }
.wizard-steps li { flex: 1; min-width: 0; }
.wizard-steps li > a, .wizard-steps li > .wizard-dot { text-decoration: none; }
.wizard-steps a { display: flex; align-items: center; gap: 8px; color: inherit; }
.wizard-steps li { display: flex; align-items: center; gap: 8px; padding-top: 8px; border-top: 3px solid var(--line, #ddd); color: var(--muted); font-size: 0.88em; }
.wizard-steps li.is-current { border-top-color: var(--green); color: inherit; font-weight: 600; }
.wizard-steps li.is-done { border-top-color: var(--green-soft); }
.wizard-dot { flex: none; width: 24px; height: 24px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  background: var(--line, #e6e6e6); font-size: 0.85em; font-weight: 700; }
.wizard-steps li.is-current .wizard-dot { background: var(--green); color: #fff; }
.wizard-steps li.is-done .wizard-dot { background: var(--green-soft); color: var(--green-dark); }
.wizard-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wizard-form { display: flex; flex-direction: column; gap: 14px; }
.wizard-form h2 { margin: 0; font-size: 1.25em; }
.wizard-kicker { margin: 0; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.wizard-help { margin: -6px 0 2px; color: var(--muted); }
.wizard-form label { display: flex; flex-direction: column; gap: 6px; font-weight: 600; }
.wizard-form input, .wizard-form select, .wizard-form textarea { font-size: 16px; padding: 12px; width: 100%; box-sizing: border-box; }
.wizard-form textarea { font-family: ui-monospace, monospace; font-size: 14px; resize: vertical; word-break: break-all; }
.wizard-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.wizard-actions { display: flex; gap: 10px; justify-content: flex-end; align-items: center; margin-top: 4px; }
.wizard-actions > * { min-height: 46px; padding-left: 22px; padding-right: 22px; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; }
.wizard-error { margin: 0 0 12px; padding: 10px 12px; border-radius: 8px; background: var(--red-soft); color: var(--red); }
.wizard-ok { margin: 0; color: var(--green-dark); font-weight: 600; }
.wizard-next { margin-top: 14px; }
.wizard-skip { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line, #e6e6e6); text-align: center; }
.wizard-skip .link-button { font-size: 0.95em; }
.wizard-skip .field-hint { display: block; margin-top: 4px; }
.help-wizard { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin: 0 0 14px; }
.help-wizard .primary-button { text-decoration: none; }
.wizard-summary { list-style: none; padding: 0; margin: 8px 0 12px; line-height: 1.8; }
@media (max-width: 520px) {
  .wizard-actions > * { flex: 1; padding-left: 10px; padding-right: 10px; }
  .wizard-label { font-size: 0.92em; }
  .wizard-steps li:not(.is-current) .wizard-label { display: none; }
}
.p4a-login-fold { margin: 0 0 10px; }
.schedule-later.centre-fold { margin: -8px 0 10px; }
.schedule-later.centre-fold > summary { padding: 2px 0; }
.centre-fold .schedule-later-set { font-weight: 400; color: var(--muted); }
.session-options { gap: 4px 16px; }
.session-options > label.checkbox { margin-right: 0; }
.pref-fold { margin: 12px 0 0; }
#settings-preferences .pref-list { border-top: none; }
#settings-preferences .pref-list > .pref-row:first-child { padding-top: 0; }
.pref-fold .schedule-later-set { font-weight: 400; color: var(--muted); }
.pref-fold-intro { margin: 0 0 12px; font-size: 0.9em; color: var(--muted); }
.pref-fold .settings-grid > .settings-password { margin-bottom: 12px; }
.p4a-login-fold .schedule-later-set { font-weight: 400; color: var(--muted); overflow-wrap: anywhere; }
.p4a-login-fold .settings-grid > .settings-password { margin-bottom: 0; }
.status-outcome { margin: 14px 0 0; padding: 10px 12px; border-radius: 9px; font-weight: 600; }
.status-outcome.tone-good { background: var(--green-soft); color: var(--green-dark); }
.status-outcome.tone-bad { background: var(--red-soft); color: var(--red); }
.status-outcome.tone-info { background: var(--gold-soft); color: #7A5A26; }
.settings-fold > summary { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; cursor: pointer; list-style: none; }
.settings-fold > summary::-webkit-details-marker { display: none; }
.settings-fold > summary::before { content: "▸"; color: var(--green); transition: transform 0.15s ease; display: inline-block; }
.settings-fold[open] > summary::before { transform: rotate(90deg); }
.settings-fold-title { font-weight: 700; font-size: 1.15em; }
.settings-fold-hint { font-size: 0.88em; }
.settings-fold-body { margin-top: 12px; }
@media (prefers-reduced-motion: reduce) { .settings-fold > summary::before { transition: none; } }
.finder-error { margin-top: 14px; }
.sidebar-logout-btn {
  display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%;
  padding: 9px 12px; border-radius: 8px; border: 1px solid #3A4D43; background: transparent;
  color: #DCE3DE; font: inherit; font-size: 0.9em; cursor: pointer;
}
.sidebar-logout-btn:hover { background: rgba(255,255,255,0.06); color: #FFFFFF; }
/* Beats the app-wide solid-green button[type=submit] rule. */
button[type=submit].sidebar-logout-btn { background: transparent; color: #DCE3DE; border: 1px solid #3A4D43; font-weight: 400; font-size: 0.9em; }
button[type=submit].sidebar-logout-btn:hover { background: rgba(255,255,255,0.06); color: #FFFFFF; }
.rating-block { display: flex; flex-direction: column; gap: 4px; }
.rating-row { display: flex; justify-content: space-between; color: #F3F1E8; font-size: 0.9em; }
.rating-row strong { font-variant-numeric: tabular-nums; }
/* Real gap found live: Credit Balance only ever updated as a side
   effect of some OTHER dispatch happening to print it, so it could
   sit stale with no way to ask Padel4all directly — this makes the
   figure itself the button, styled to look exactly like the plain
   text it replaces until you actually hover/tap it. */
.credit-refresh-btn {
  background: none; border: none; padding: 0; margin: 0;
  color: inherit; font: inherit; cursor: pointer;
}
.credit-refresh-btn:hover strong, .credit-refresh-btn:focus-visible strong {
  text-decoration: underline;
}
.credit-refresh-btn:disabled { cursor: wait; opacity: 0.7; }
.countdown-block {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  color: #E9C98F; font-weight: 700; font-size: 0.9em;
}
/* Two short lines ("Next" / "booking") rather than one cramped line
   squeezed against the digits — real gap found live: "Next booking
   in" wrapped mid-word ("Next booking" / "in") once the countdown's
   own fixed-width digits left it too little room. */
.countdown-block span { line-height: 1.25; }
.countdown-block strong { font-size: 1.15em; font-variant-numeric: tabular-nums; white-space: nowrap; }

.sidebar-status {
  display: flex; flex-direction: column; gap: 8px; margin-top: auto;
  padding-top: 12px; border-top: 1px solid #2A3A32;
}
.button-row { display: flex; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
/* getting_started.html's own "Book a session"/"Book a court" links —
   .primary-button/.secondary-button are plain classes (not scoped to
   button[type=submit] alone) so they already colour an <a> the same
   as a real button, but an anchor is inline by default: no box model
   for padding to properly expand into, and a default underline. */
.button-row a.primary-button, .button-row a.secondary-button {
  display: inline-block; text-decoration: none;
}

.wizard-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.wizard-head h1 { margin-bottom: 0; }
.wizard-skip { margin-bottom: 0 !important; white-space: nowrap; }
.wizard-dots { display: flex; gap: 6px; margin: 14px 0 22px; }
.wizard-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--border); }
.wizard-dot.active { background: var(--green); }
.wizard-step h3 { margin-top: 0; }
.wizard-nav { justify-content: flex-end; }
.button-row .inline-form { margin: 0; }
/* A toggle button plus a short status line next to it (Court Monitor's
   own Turn on/off + "Currently: On/Off") — a bare <form> and <p>
   sitting next to each other otherwise each keep their own default
   block margin, stacking them instead of reading as one line. */
.inline-status-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.inline-status-row form, .inline-status-row p { margin: 0; }
.inline-checkbox { display: flex; align-items: center; gap: 8px; font-weight: normal; cursor: pointer; }
.secondary-button {
  background: none; border: 1px solid var(--border); color: var(--text);
  padding: 10px 18px; border-radius: 8px; font-size: 0.95em; font-weight: 600; cursor: pointer;
  transition: background-color 0.15s ease, transform 0.1s ease;
}
.secondary-button:hover { background: var(--bg); }
.secondary-button:disabled { color: var(--muted); cursor: not-allowed; opacity: 0.6; }
.secondary-button:disabled:hover { background: none; }
/* Session/Court Finder's own "Select" — sits inline right next to a
   session name and the "?" icon now, not alone in its own column, so
   the normal .secondary-button size (built for a standalone action
   like Cancel/Book) read as oversized next to them. */
.small-button { padding: 5px 12px; font-size: 0.85em; }
/* Desktop: the Availability/Select pair sits on the SAME line as the
   session name, pinned to the right via the parent's own flex space-
   between (.session-finder-name-cell below) — consistently, regardless
   of name length, rather than the old approach of letting the name's
   own text just flow into them (which looked fine or not by accident
   of how long a given session's name happened to be). flex-wrap:wrap
   plus min-width:0 on the name itself means a long name wraps onto
   its own second line rather than squeezing or overflowing past the
   buttons — more likely to matter now that "Availability" (spelled
   out, not a tiny "?") takes noticeably more width than it used to.
   Overridden back to stacked on mobile by the existing .session-
   finder-actions { flex: 1 0 100%; } rule in the phone-width media
   query further down, which deliberately keeps its own separate line
   there. */
.session-finder-name-cell { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.session-finder-name { flex: 1 1 auto; min-width: 0; font-weight: 700; }
/* Date/time and session name are the two things actually being
   scanned for — bolding them (both Session and Court Finder, since
   both tables share [data-label="Date and Time"]; both PC and mobile,
   since this isn't scoped to any media query) makes each row scannable
   at a glance instead of everything reading at the same visual weight. */
.finder-table td[data-label="Date and Time"] { font-weight: 700; }
/* A blank row between each Finder entry, purely for the small visual
   gap requested between boxes — invisible by default (mobile's own
   card layout already has its own 10px margin-bottom gap and would
   otherwise render this as an extra empty card); given actual height
   only on desktop, where .finder-table's own boxed-row borders further
   below would otherwise touch directly. Sits AFTER each session's own
   <tr> in the DOM, so a dynamically-inserted .session-info-row (see
   sessions_page.html's own insertDetailRow(), which always inserts
   right after the session row) lands BETWEEN the row and this spacer —
   the gap stays between separate sessions, never between a session and
   its own open Availability result. */
.finder-table tr.finder-row-spacer td { border: none; padding: 0; height: 0; }
.session-finder-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
/* button[type=submit]'s own rule below (higher specificity — an
   attribute selector counts the same as a class) otherwise wins over
   a plain .secondary-button class, same reasoning as .link-button's
   own !important further down — real bug found live: Save/Verify
   player names/etc. (type=submit buttons using formaction to post
   elsewhere, not "the" primary action of their form) rendered as
   solid green, indistinguishable from Book Session/Book Court. */
button[type=submit].secondary-button {
  background: none; color: var(--text);
}
button[type=submit].secondary-button:hover { background: var(--bg); }

/* Status Log's own Detailed view (status_log_detailed.html) — the raw
   captured engine output. */
.panel-log {
  background: #11201A; color: #D9E6DE; padding: 12px; border-radius: 8px;
  font-size: 0.8em; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
  height: 65vh; overflow-y: auto; margin: 0;
}
/* The "still going" banner for whichever button-triggered scan is
   currently running — see _activity_banner's own docstring
   (courtcatcha_web.py) for what does and doesn't show this. Sits at
   the bottom of the centre pane (base.html), the same spot a flash
   message renders in once the action's actually done — same box
   shape/padding as .flash below, kept a neutral colour rather than
   gold so "still going" reads visibly differently from "done". */
.content-activity {
  display: flex; align-items: center; gap: 10px; margin: 16px 0;
  padding: 10px 14px; background: var(--bg); border: 1px solid var(--border);
  border-radius: 8px; font-size: 0.9em; color: var(--text);
}
/* .content-activity's own `display: flex` above otherwise beats the
   browser's native [hidden] styling on specificity (a class selector
   outranks a bare attribute selector) — without this, the box still
   rendered ("None in progress") even carrying the hidden attribute. */
.content-activity[hidden] { display: none; }
/* Nudged up a touch: centring on the whole line box (which includes the
   space below the text for descenders) left the dots visibly low
   against the words beside them. In em so it scales with the text. */
.content-activity-dots { display: inline-flex; gap: 3px; flex-shrink: 0; position: relative; top: -0.06em; }
/* Same specificity-vs-the-browser's-own-[hidden]-styling fix as
   .content-activity[hidden] just above — needed here too now that
   the "Next booking" pop-up (base.html) hides just the dots (not the
   whole .content-activity box) once a booking's actually done. */
.content-activity-dots[hidden] { display: none; }
.content-activity-dots span {
  width: 6px; height: 6px; border-radius: 50%; background: var(--green);
  animation: content-activity-bounce 1.2s infinite ease-in-out both;
}
.content-activity-dots span:nth-child(1) { animation-delay: -0.32s; }
.content-activity-dots span:nth-child(2) { animation-delay: -0.16s; }
/* "(Re)connecting to Padel4all" (base.html) — a small floating pill,
   not a modal: the page stays fully usable while the engine starts.
   Same dark court green as the » menu tab, whose colour it explains. */
.engine-status {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(16px + env(safe-area-inset-bottom, 0px)); z-index: 45;
  display: flex; align-items: center; gap: 10px; width: max-content; max-width: calc(100vw - 32px);
  white-space: nowrap;
  padding: 10px 18px; border-radius: 999px; background: #11201A; color: #F3F1E8;
  font-size: 0.9em; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
}
.engine-status[hidden] { display: none; }
.engine-status .content-activity-dots span { background: #5BE38F; }
@keyframes content-activity-bounce {
  0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
  40% { transform: scale(1); opacity: 1; }
}
/* Deliberately NOT disabled under prefers-reduced-motion — unlike a
   decorative flourish, this is the only visible signal that a scan is
   still running rather than stuck, so it stays animated regardless. */
.last-refreshed-row {
  display: flex; align-items: baseline; justify-content: flex-start; gap: 10px; margin-bottom: 14px;
}
.last-refreshed-row .muted { margin: 0; }
.hide-cancelled-toggle {
  display: inline-flex; align-items: baseline; gap: 6px; font-size: 0.88em; color: var(--muted); cursor: pointer;
  white-space: nowrap;
}
.hide-cancelled-toggle input[type="checkbox"] { margin: 0; position: relative; top: 2px; }

/* Narrow screens: the sidebar becomes a hidden, off-canvas drawer,
   opened by a small tab fixed to the screen's own left edge — real
   gap found live with an earlier full-width top-bar version of this
   toggle: it had to sit at a HIGHER z-index than the drawer to stay
   clickable while closed, which then left it visually overlapping the
   drawer's own brand/logo header the moment the drawer opened. A
   left-edge tab has no such conflict (hidden outright once open, see
   base.html's own setOpen()) and stays out of the content's way
   entirely rather than permanently occupying a header strip. Nine nav
   items plus Log out only really works as a drawer at this width.
   Hidden by default (desktop); only ever shown inside the ≤780px
   media query below. */
.nav-edge-tab {
  display: none;
  /* A 48px square ending above where the page's first card starts
     (.main-panel's 70px top padding on phones) — real gap found live:
     at 66px tall from 16px down it covered the start of every page's
     title ("Sat 3 Oct" read "at 3 Oct"). */
  position: fixed; top: calc(12px + env(safe-area-inset-top, 0px)); left: 0; z-index: 40;
  width: 48px; height: 48px; border: none; border-radius: 0 12px 12px 0;
  background: #11201A; color: #F3F1E8; font-size: 1.6em; line-height: 1;
  cursor: pointer; box-shadow: 2px 2px 10px rgba(0,0,0,0.2);
  transition: color 0.3s ease;
}
/* Warm booking engine ready (see base.html's setEngineIndicator) — a
   bright green that reads clearly against the tab's dark background. */
.nav-edge-tab.engine-warm { color: #5BE38F; }
.nav-backdrop {
  display: none;
  position: fixed; inset: 0; background: rgba(10, 16, 13, 0.5); z-index: 30;
  opacity: 0; transition: opacity 0.2s ease;
  backdrop-filter: blur(1px); -webkit-backdrop-filter: blur(1px);
}
.nav-backdrop.visible { display: block; opacity: 1; }


.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 28px;
  /* A soft lift rather than a flat, purely-bordered box — subtle
     enough to stay out of the way on a dense page (Scheduled
     Bookings/Current Bookings stack several cards in a row) while
     still reading as a distinct surface, not just a line drawn on the
     background. */
  box-shadow: 0 1px 2px rgba(20, 32, 27, 0.04), 0 6px 16px rgba(20, 32, 27, 0.05);
}
.card + .card { margin-top: 18px; }
.card.narrow { max-width: 380px; margin: 60px auto; }

h1 { margin-top: 0; }
h2 { margin: 16px 0 4px; font-size: 1.05em; }

/* Home: a compact welcome with the two main actions beside the logo,
   then cards for next games, what's running, and every page. */
.home-hero { display: flex; align-items: center; gap: 22px; }
.home-logo { width: 96px; height: 96px; border-radius: 20px; flex: 0 0 auto; display: block; }
.home-hero h1 { margin: 0; font-size: 1.6em; line-height: 1.2; }
.home-tagline { color: var(--muted); margin: 4px 0 14px; }
.home-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.home-actions a { text-decoration: none; }
.home-actions .secondary-button { font-size: 1em; font-weight: 600; padding: 12px 22px; }
.form-title-row h2 { margin: 0; font-size: 1.15em; }
.home-h2 { margin: 0 0 12px; font-size: 1.15em; }
.card h2 + .item-list, .form-title-row + .item-list { margin-top: 10px; }
.home-setup { border-left: 4px solid var(--red); }
.home-setup h2 { margin: 0; font-size: 1.15em; }
.home-setup .page-intro { margin: 4px 0 12px; }
a.home-setup-button { display: inline-block; text-decoration: none; }
.glance-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
.glance {
  display: grid; gap: 6px; padding: 12px; border: 1px solid var(--border); border-radius: 10px;
  text-decoration: none; color: var(--text); background: var(--panel);
}
.glance:hover { border-color: var(--green); background: var(--green-soft); }
.glance-label { font-size: 0.78em; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.glance strong { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 1em; }
.glance-none { color: var(--muted); font-weight: 600; }
.glance-sub { font-weight: 400; color: var(--muted); font-size: 0.9em; }
.home-footnote { margin-top: 14px; text-align: center; }
.home-footnote a { color: var(--green-dark); }

fieldset {
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 18px;
  padding: 14px 16px;
}
legend { font-weight: 600; padding: 0 6px; }
input:read-only { background: var(--bg); color: var(--muted); cursor: default; }

label { display: block; margin-bottom: 10px; font-size: 0.95em; }
label.radio, label.checkbox { display: inline-flex; align-items: center; gap: 6px; margin-right: 16px; }
input[type=text], input[type=email], input[type=password], input[type=date],
input[type=time], input[type=number], input[type=tel], input[type=url], select {
  display: block; width: 100%; margin-top: 4px;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px;
  font-size: 1em;
}
/* Date/time pickers only ever hold a fixed, short number of digits
   ("dd/mm/yyyy", "--:--") — real gap found live: they inherited the
   same width:100% as a free-text field, stretching to fill whatever
   width their container (a form, a field-row column) happened to be,
   which just wasted space and made the small font look lost in a
   wide box. Narrower and a larger, easier-to-read font instead.
   .time-select is every half-hour-slot <select> in the app (Court
   Finder's Earliest/Latest time, Court Monitor's own Time) — not a
   native time input at all (see _run_at_field.html's own comment on
   why), but still wants this same compact date/time-picker sizing
   rather than a plain full-width <select> like Session name's own.
   input[type=time] is Run-at Time specifically — deliberately still a
   native picker, not a half-hour <select>, so it can accept any
   minute (see _run_at_field.html's own comment on why). */
input[type=date], input[type=time], .time-select {
  font-size: 1.1em;
  /* Real gap found live: a native type=date control and a native
     type=time control don't compute the same height from identical
     padding/font-size — each browser draws its own picker chrome
     (calendar icon, spinner, whatever) with a slightly different
     internal box, so Date and Time ended up visibly different
     heights sitting side by side. An explicit height (not just
     padding) forces both to match; box-sizing: border-box is already
     the page-wide default (see the * rule up top) so this height
     includes the border, not just the content box. Browsers already
     vertically-centre a text-like input's own value within whatever
     height it's given. */
  height: 44px; padding-top: 0; padding-bottom: 0;
  /* Real gap found live: a fixed height alone does NOT vertically
     centre a native date/time input's text on every browser — several
     mobile browsers (iOS Safari in particular) position a single-line
     value relative to line-height, not the box's own height, and left
     it sitting at the top of the 44px box otherwise. 42px = the 44px
     height minus this rule's own 1px top+bottom border (box-sizing:
     border-box is the page-wide default — see the * rule up top), so
     the line box exactly fills the content area and centres within
     it. This is the single generic input[type=date]/input[type=time]
     rule every date/time field on every page shares, so fixing it
     here fixes it everywhere at once rather than page by page. */
  line-height: 42px;
}
/* width, not max-width — real gap found live: max-width is only a
   ceiling, and these sit in a flex item with flex-grow: 0 (see
   .run-at-field/.field-row.tight), which sizes to the INPUT's own
   intrinsic rendered width, not the available space. On an iPad that
   intrinsic width came out smaller than intended, so raising max-width
   alone changed nothing — nothing was pushing the box up to that
   ceiling in the first place. An explicit width is a real target, not
   just a limit; flex-shrink (still 1, inherited) still lets it give
   way on a phone too narrow to fit it at all. */
input[type=date] { width: 170px; padding-left: 32px; }
input[type=time] { width: 110px; padding-left: 32px; }
.time-select { width: 110px; padding-left: 32px; }
/* Real gap found live: these widths were tuned against a narrow phone
   screen, but they're not scoped to the mobile media query below —
   they apply everywhere, including a much wider iPad, where the same
   110/170px boxes truncated their own value text instead of just
   looking small. Anything wider than a phone gets noticeably more
   room; there's no shortage of it there. */
@media (min-width: 600px) {
  input[type=date] { width: 240px; padding-left: 40px; }
  input[type=time] { width: 190px; padding-left: 40px; }
  .time-select { width: 190px; padding-left: 40px; }
  label:has(> input[type=date])::after, label:has(> input[type=time])::after, label:has(> .time-select)::after {
    left: 14px; font-size: 1.2em;
  }
}
/* Court Finder's own Duration — a plain unstyled <select> otherwise
   inherits width: 100% from the generic input/select rule up top, and
   stretches to fill its own row's full width once it's the only field
   left on that row (see courts_page.html) — fine for a long list of
   named options, but odd for a two-option 60/90 minutes choice. */
.duration-select { max-width: 110px; }

/* Court Finder's own date-RANGE picker (Flatpickr, courts_page.html)
   — same 44px-tall/left-icon convention as every native date/time box
   above, kept as its own rule rather than folded into that shared one:
   a range's own display text ("28 Sep 2026 to 02 Oct 2026") needs real
   room to read un-truncated, wider than a single date ever needs. */
.date-range-input {
  font-size: 1.05em; height: 44px; padding-top: 0; padding-bottom: 0; line-height: 42px;
  /* Plain left padding, no icon overlay — real bug found live, twice:
     every other date/time box's own 📅/🕐 icon (below) is a plain
     Unicode glyph at a predictable width, but on iOS the CALENDAR
     emoji specifically renders as a small illustrated graphic with a
     date baked into the artwork itself (visible in a live screenshot:
     "JUL 17"), which is both wider and less predictable than a normal
     emoji glyph — no fixed padding reliably cleared it, and it kept
     overlapping the date text's own leading characters regardless of
     how much room this was given. Simplest robust fix: this ONE input
     just doesn't use the icon convention at all — Flatpickr's own
     click-to-open cursor and the "Select date range" placeholder
     already say what it is without one. */
  padding-left: 12px; width: 230px; cursor: pointer; background: var(--panel);
  /* Defensive truncation (a clean "…") in case a display format ever
     produces text too long for this box on a given screen size —
     real bug found live: without this, an overflowing value just
     auto-scrolled to show wherever the cursor last was (the END of
     the string, cutting off the START instead) rather than clipping
     tidily. The real fix was shortening the display format itself
     (see courts_page.html's own Flatpickr init) — this is only a
     safety net on top of that. */
  text-overflow: ellipsis; overflow: hidden; white-space: nowrap;
}
@media (min-width: 600px) {
  .date-range-input { width: 320px; padding-left: 14px; }
}
@media (min-width: 600px) {
  label:has(> .date-range-input)::after { left: 14px; font-size: 1.2em; }
}
/* Flatpickr's own green theme override lives in courts_page.html now,
   NOT here — real bug found live: Flatpickr's own CSS (loaded via a
   <link> in courts_page.html's own content block, i.e. in <body>,
   AFTER this file's own <head> <link>) has the exact same selector
   specificity as these override rules did when they lived here, and
   CSS's own tie-break is "whichever rule's stylesheet was loaded
   LATER in the document wins" — so Flatpickr's own default blue
   always won regardless of what this file said. Moved to a <style>
   block placed AFTER Flatpickr's own <link> instead, which is the
   actual fix (later still, same specificity, now correctly on top). */

/* Calendar/clock icon inside every date/time box — real gap found
   live: on a PC, Chrome/Edge already draw their own calendar icon
   inside a native date box for free, but mobile Safari draws NONE at
   all (there, a native date box is just plain text, tap anywhere to
   open the wheel picker), so the mobile app always looked one icon
   short of the desktop rendering. A custom icon here makes every box
   look the same on every browser instead of depending on what a given
   browser happens to draw by default.

   Left, not right — real gap found live: on the right, this icon sat
   on top of a native date control's OWN right-aligned chrome (the
   browser's real calendar icon in Chrome/Edge), which visually
   collided with it in a way the left side of the box never has
   anything else drawn on it at all.

   `> input`/`> select` (direct child) matches every date/time field on
   every page — see the actual markup: session/monitor/court-finder's
   own <label>Session date<input...></label> shape, and
   _run_at_field.html's own <label class="run-at-field"><span>...</span>
   <input type="time">...</label> shape — in both, the field is the
   LAST direct child of its <label>. bottom: 0; height: 44px on the
   icon itself (rather than trying to vertically centre against the
   label's own full height, caption text and all) anchors it to the
   FIELD's own box specifically — that field is always its label's
   bottom edge, and always 44px tall (the shared height rule above),
   regardless of what label text/caption sits above it. */
label:has(> input[type=date]), label:has(> input[type=time]), label:has(> .time-select) { position: relative; }
label:has(> input[type=date])::after, label:has(> input[type=time])::after, label:has(> .time-select)::after {
  position: absolute; left: 10px; bottom: 0; height: 44px;
  display: flex; align-items: center;
  font-size: 1.05em; line-height: 1; pointer-events: none;
}
label:has(> input[type=date])::after { content: "📅"; }
label:has(> input[type=time])::after, label:has(> .time-select)::after { content: "🕐"; }
/* The native indicator (where a browser draws one at all — mobile
   Safari doesn't) stays functionally in place, just invisible, rather
   than removed outright — real gap found live: `display: none` on it
   in Chrome/Edge also disables the click-to-open behaviour tied to
   it, silently breaking the picker on desktop while looking identical
   to `opacity: 0` until you actually tried clicking. Sized/positioned
   over the LEFT side of the box now, under our own custom icon above,
   so a click there still lands on it — Chrome/Edge otherwise still
   draw their own real calendar/clock icon at the box's right edge, now
   sitting harmlessly alongside our left-side one instead of under it. */
input[type=date]::-webkit-calendar-picker-indicator,
input[type=time]::-webkit-calendar-picker-indicator {
  opacity: 0; position: absolute; left: 0; width: 34px; height: 100%;
  margin: 0; cursor: pointer;
}

button[type=submit], .primary-button {
  background: var(--green);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: 12px 22px;
  font-size: 1.05em;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.1s ease;
}
button[type=submit]:hover, .primary-button:hover { background: var(--green-dark); }
/* A small tactile "press" on tap/click — mainly felt on a touchscreen,
   where there's no :hover state to signal a tap actually registered. */
button[type=submit]:active, .primary-button:active, .secondary-button:active {
  transform: scale(0.97);
}
button[type=submit]:disabled, .primary-button:disabled {
  background: #9CA9A2; color: #F3F1E8; cursor: not-allowed; opacity: 0.7;
}
button[type=submit]:disabled:hover, .primary-button:disabled:hover { background: #9CA9A2; }

.inline-form { display: inline; margin: 0 0 10px; }

/* Sessions/Courts pages: several fields side by side on one line
   (matching the Tkinter reference's own dense rows) instead of one
   full-width field per line — wraps to stacked below ~700px. */
.field-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-end; }
.field-row > label { flex: 1 1 160px; margin-bottom: 14px; }
.field-row > label.grow { flex: 2 1 260px; }
/* Checkbox labels sit at their own natural width (not stretched by
   the flex-grow every other label gets) so two of them — e.g. "Hide
   my name"/"Join waitlist if full" — sit snugly next to each other
   instead of spreading apart with a big gap. A trailing .push-right
   element (e.g. "Refresh sessions") still gets pushed to the far
   right. */
.field-row > label.checkbox { flex: 0 0 auto; margin-bottom: 14px; }
.field-row > .push-right { margin-left: auto !important; }
.rating-inline { flex: 0 0 auto; margin-bottom: 14px; display: inline-flex; align-items: center; gap: 6px; font-weight: 700; }
.rating-inline strong { font-variant-numeric: tabular-nums; }
.field-row.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
.field-row.two-col > label { margin-bottom: 14px; }
/* The tick sits right next to "Player N" itself, not off at the far
   right edge of the row. */
.player-label-row { display: flex; justify-content: flex-start; align-items: center; gap: 6px; }
/* Real bug found live: "Player 1" etc. wrapped onto two lines because
   the label's own text was a flex child competing for space with the
   input (which has its own width:100% from the generic input rule
   above, fighting for the same row) — min-width:0 on the input lets
   IT shrink instead, and flex:0 0 auto + nowrap on the label text
   means it never breaks. */
.player-label-text { flex: 0 0 auto; white-space: nowrap; }
.player-label-row input { flex: 1 1 auto; min-width: 0; margin-top: 0; }
/* Fixed width, and kept in place (just hidden) when a name isn't
   verified, so each player's field box starts in the same spot with or
   without a tick (found live 2026-09-25: ticked rows' boxes were shifted). */
.verified-tick { color: var(--green); font-weight: 700; flex: 0 0 1em; width: 1em; text-align: center; }
.verified-tick.is-empty { visibility: hidden; }
/* Verify player names result, under the player fields (courts_page.html). */
.verify-banner { margin: 10px 0 0; color: var(--green-dark); font-weight: 600; }
.verify-banner.verify-failed { color: var(--red); }

.finder-table-scroll { overflow-x: auto; max-height: 420px; overflow-y: auto; }
.finder-table { width: 100%; border-collapse: collapse; font-size: 0.88em; }
.finder-table th, .finder-table td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
/* "Booking made" used to hold a whole sentence (the timing note
   later moved to Status Log only — see web_app.py's own
   current_bookings() comment), which is why this column got its own
   wrapping rule in the first place. Now it's just a short timestamp,
   but real gap found live: the max-width this rule used to carry
   (220px) outlived that need and started constraining the column on
   MOBILE too, where the card layout wants this cell's full row width
   — left the label on its own line with the value wrapped below it
   instead of side by side like every other field. white-space:normal
   alone (no max-width) is all this column still actually needs;
   .bookings-table's own desktop-only per-column width (13%, see that
   rule's own comment) already constrains it there. */
.finder-table th.made-at-cell, .finder-table td.made-at-cell { white-space: normal; }
/* Current Bookings' Sessions and Courts tables — two SEPARATE <table>
   elements that now share the exact same column shape (see
   cloud/web_app.py's own _DISPLAY_HEADERS), but each one used to
   auto-size its own columns purely from its own content, so "Date and
   Time"/"Centre"/etc never lined up between the two even though
   they're conceptually the same column. Fixed per-column widths (and
   table-layout: fixed to make them actually apply) forces both tables
   onto the same grid; wrapping instead of nowrap keeps a long Session
   name or Centre readable within its own fixed-width cell rather than
   overflowing into the next column, which fixed table layout doesn't
   otherwise clip on its own. */
.bookings-table { table-layout: fixed; }
.bookings-table th, .bookings-table td { white-space: normal; }
.bookings-table th:nth-child(1), .bookings-table td:nth-child(1) { width: 19%; }
.bookings-table th:nth-child(2), .bookings-table td:nth-child(2) { width: 13%; }
.bookings-table th:nth-child(3), .bookings-table td:nth-child(3) { width: 13%; }
.bookings-table th:nth-child(4), .bookings-table td:nth-child(4) { width: 19%; }
.bookings-table th:nth-child(5), .bookings-table td:nth-child(5) { width: 8%; }
.bookings-table th:nth-child(6), .bookings-table td:nth-child(6) { width: 8%; }
.bookings-table th:nth-child(7), .bookings-table td:nth-child(7) { width: 20%; }
/* !important: button[type=submit]'s own rule above outranks a plain
   .link-button class on specificity alone (an attribute selector counts
   the same as a class) — same reasoning as .quit-button's override
   just above. */
.link-button {
  background: none !important; border: none !important; padding: 0 !important;
  margin: 0 0 10px !important; color: var(--green-dark) !important;
  text-decoration: underline; cursor: pointer; font-size: 0.9em !important;
  font-weight: 400 !important; display: block;
}
.link-button:hover { color: var(--green) !important; }
/* Log out sits among the sidebar's own nav links (.sidebar-nav a,
   0.95em) — .link-button's own smaller 0.9em (shared with e.g. dialog
   Cancel buttons elsewhere) read as visually "smaller than the menu"
   next to them. Keeps .link-button's own green colour as-is. */
/* Real gap found live: .link-button's own padding: 0 !important left
   "Log out" flush against the sidebar's edge, 10px further left than
   every .sidebar-nav a above it (their own padding: 9px 10px) — lines
   its left edge up with theirs instead. */
.sidebar-logout { font-size: 0.95em !important; padding-left: 10px !important; }

.muted { color: var(--muted); font-size: 0.9em; }
.warning { color: var(--red); font-weight: 600; }
.status-ok { color: var(--green-dark); font-weight: 600; }
/* The forwarding address on the Help page (and anywhere else a short
   literal value — an email address, a key name — needs to read as
   "copy this exactly", not prose. */
.mono-inline {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 4px; padding: 1px 5px; font-size: 0.92em;
}

/* Run booking at (book.html) — date + time pickers on one row instead
   of a single free-text "YYYY-MM-DD HH:MM" field. Lives on the light
   .card main panel here, not a dark sidebar, so it reuses the generic
   input/label/.secondary-button rules above rather than the dark-
   themed .run-at-row variant styled for _booking_controls.html. */
.run-at-fields { margin-bottom: 14px; }
.run-at-fields .control-label { font-weight: 700; margin-bottom: 4px; font-size: 0.95em; }
.run-at-fields .control-label .muted { font-weight: 400; }
.run-at-fields .run-at-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; }
.run-at-fields .run-at-row + .run-at-row { margin-top: 10px; }
/* Wraps each date/time input with its own small "Date"/"Time" caption
   — real gap found live: a blank native date/time picker shows no
   hint at all on some mobile browsers (just an empty grey box), so
   the two fields were indistinguishable until you actually tapped
   one. flex-end above lines Clear up with the INPUTS themselves, not
   the shorter caption text sitting above them. */
/* flex: 0 1 auto (not 1 1 auto) — real gap found live: the Time field
   used to flex-grow to fill the whole row, which left "Repeat weekly"
   sitting much further right than the visible Time box, with a big
   stretch of empty label-width in between. Shrink-only lets this
   field hug its input's actual width instead. */
.run-at-field { display: flex; flex-direction: column; gap: 3px; flex: 0 1 auto; min-width: 0; margin-bottom: 0; }
.run-at-fields input[type=date], .run-at-fields input[type=time] {
  /* font-size explicit here, not left to the generic input[type=date]
     rule above — real bug found live: .run-at-row (this component's
     own inner wrapper) collides by NAME with an unrelated leftover
     dark-sidebar rule from _booking_controls.html (a component this
     cloud fork never actually uses), which sets font-size: 0.85em at
     equal specificity to a plain `input[type=date]` selector — so the
     generic fix never actually reached this specific field.

     No width override here any more — real gap found live: width:
     100% fought the generic rule's own explicit width (170px/110px,
     wider still on tablet/desktop), stretching this field to its
     PARENT's width instead — and that parent (.run-at-field, flex-
     grow: 0) sizes ITSELF from this very input's own width, so 100%
     of "whatever the input turns out to be" just fed back into
     whatever the browser's intrinsic guess happened to be, which is
     exactly the narrow-on-iPad bug the generic fix exists to remove. */
  margin-top: 0; font-size: 1.1em;
}
.run-at-fields .secondary-button { flex: 0 0 auto; padding: 8px 14px; }
/* baseline lines the checkbox up with the actual text next to it, not
   just its top edge (which read as floating noticeably above it). */
.repeat-weekly-label { display: flex; align-items: baseline; gap: 8px; white-space: normal; }
.repeat-weekly-label input { flex: 0 0 auto; }
/* Real gap found live, twice: sharing a row with Time meant "Repeat
   weekly" position depended on exactly how wide a native time input
   rendered on a given device (which varied enough between an iPhone
   and an iPad alone to break a fixed margin tuned for one of them) —
   its own row (see _run_at_field.html) removes that dependency
   entirely rather than trying to out-guess it a third time. */
.run-at-row .repeat-weekly-label { margin-bottom: 0; }

dialog {
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 24px;
  max-width: 320px;
  width: 90%;
}
dialog::backdrop { background: rgba(20, 32, 27, 0.45); }
dialog h2 { margin: 0 0 4px; font-size: 1.1em; }
.payment-method-choice { display: flex; gap: 16px; margin: 10px 0; }
.payment-method-choice label.radio { display: inline-flex; align-items: center; gap: 6px; margin-right: 0; }
.dialog-actions {
  display: flex; justify-content: flex-end; align-items: center; gap: 16px;
  margin-top: 14px;
}
.dialog-actions .link-button { margin: 0 !important; }
/* Same [hidden]-vs-display:flex specificity trap as .content-activity[hidden]
   — without this a status pop-up's Close showed mid-booking (found live
   2026-09-25, "Finding your court" with Close underneath). */
.dialog-actions[hidden] { display: none; }

/* Shared centred "progress -> outcome" look — the global Booking in
   progress pop-up (base.html) and an individual booking's own
   /status page both use this, so a booking reads the same way
   wherever it's being watched from. */
.progress-dialog { text-align: center; }
.progress-dialog-logo {
  width: 64px; height: 64px; border-radius: 14px; margin: 0 auto 14px; display: block;
}
.progress-dialog .content-activity {
  /* Real bug found live 2026-09-20: the base class's own `display:
     flex` (needed elsewhere for a single-line "dots beside text"
     inline spinner) breaks down the moment this label wraps onto two
     lines — flex sizing measured the label as if unwrapped, so the
     dots ended up shoved out to the container's own edge instead of
     sitting next to the actual last word. Plain block/inline flow has
     none of that: the label (a <span>, inline by default) and the
     dots after it in the DOM just wrap together like normal text,
     so the dots always land right after the last word wherever that
     happens to fall, on however many lines. */
  display: block;
  margin: 0; border: none; background: none; padding: 0; font-size: 1em;
}
.progress-dialog .content-activity-label { text-align: center; }
/* Raised, not lowered: an inline-flex box with no text sits its bottom on
   the text baseline, so the dots need lifting to line up with the
   middle of the lowercase letters (found live 2026-09-25: they sat low). */
.progress-dialog .content-activity-dots { margin-left: 6px; vertical-align: 0; }
.progress-dialog .dialog-actions { justify-content: center; margin-top: 16px; }
/* A dialog opened with showModal() takes focus itself, and iOS Safari
   then draws a blue focus ring round the whole pop-up. */
.progress-dialog:focus, .progress-dialog:focus-visible { outline: none; }
.progress-dialog { border-radius: 16px; box-shadow: 0 12px 40px rgba(17, 32, 26, 0.25); border: none; }
.progress-dialog.has-result .content-activity-label { font-weight: 700; font-size: 1.08em; }
.status-result-icon {
  width: 56px; height: 56px; border-radius: 50%; margin: 2px auto 14px;
  display: flex; align-items: center; justify-content: center; font-size: 1.7em; font-weight: 700; line-height: 1;
}
.status-result-icon[hidden] { display: none; }
.progress-dialog-logo[hidden] { display: none; }
.status-result-icon.tone-good { background: var(--green-soft); color: var(--green); }
.status-result-icon.tone-bad { background: var(--red-soft); color: var(--red); }
.status-result-icon.tone-info { background: var(--gold-soft); color: var(--gold); font-family: Georgia, serif; font-style: italic; }
.status-result-detail { margin: 8px 0 0; color: var(--muted); font-size: 0.92em; line-height: 1.45; }
.status-result-detail[hidden] { display: none; }

/* Booking status page (status.html) */
.status-kicker {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px;
  font-size: 0.78em; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted);
}
.status-pill {
  background: var(--gold-soft); color: #7A5A26; border-radius: 999px; padding: 2px 9px;
  letter-spacing: 0.04em;
}
h1.status-title { margin: 0; font-size: 1.45em; line-height: 1.25; }
.status-subtitle { margin: 4px 0 0; color: var(--muted); }
.status-details { margin: 16px 0 0; padding: 0; border-top: 1px solid var(--border); }
.status-details > div {
  display: grid; grid-template-columns: 84px minmax(0, 1fr); gap: 12px;
  padding: 9px 0; border-bottom: 1px solid var(--border); font-size: 0.93em;
}
.status-details dt { color: var(--muted); }
.status-details dd { margin: 0; }
.status-note { margin: 12px 0 0; font-size: 0.85em; color: var(--muted); }
.status-actions { margin-top: 18px; }
.status-actions a.secondary-button { text-decoration: none; }

.flash {
  /* Now the last thing in .content (base.html), not the first — the
     same spot a scan's own "still going" banner occupies while it's
     running, so a result reads as a continuation of that, not a jump
     back up to the top of the page. */
  background: #FFF7E6;
  border: 1px solid var(--gold);
  border-radius: 8px;
  padding: 10px 14px;
  margin-top: 16px;
}
.flash p { margin: 4px 0; }

/* Wide content (My Bookings' table) gets its own horizontal scroll —
   the page body itself must never scroll sideways. */
.table-scroll { overflow-x: auto; margin-top: 14px; }
table { border-collapse: collapse; width: 100%; font-size: 0.92em; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
th { color: var(--muted); font-weight: 600; }
td form { margin: 0; }

pre#log-tail {
  background: #14201B;
  color: #D9E6DE;
  padding: 14px;
  border-radius: 8px;
  overflow-x: auto;
  max-height: 320px;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Status Log's own leading icon column (a magnifying glass, not the
   text link "Detailed" it replaced) — no underline on an emoji glyph,
   and a narrow fixed column so it doesn't stretch with the table. */
#status-log-table th:first-child, #status-log-table td:first-child { width: 1%; white-space: nowrap; }
.status-log-detail-link { text-decoration: none; font-size: 1.05em; }

/* Message column: truncated with an ellipsis rather than pushing the
   table wider than the page (real bug found live — a long booking/
   detail line ran off the right edge of the screen entirely). Needs
   table-layout: fixed for the ellipsis to actually take effect at
   all — otherwise the column just grows to fit the text, same as the
   bug being fixed. Full text still available via the cell's own
   title attribute (a native browser tooltip on hover). */
#status-log-table { table-layout: fixed; width: 100%; }
.status-log-message {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  cursor: help;
}

/* Session Finder's own "Availability" — reinstated Check availability/
   Who's playing, sitting next to Select rather than the standalone
   app's own multi-select-rows-then-a-shared-button UI. Used to be a
   small unlabelled "?" icon; styled as a normal .secondary-button.
   small-button now (same as Select right next to it) since text says
   what it does without needing a tooltip to explain a glyph. */
.session-info-row td { white-space: normal; }
/* "Show players" toggle, when unticked (sessions_page.html) — one rule hides every
   .session-info-players span at once, including ones rendered AFTER
   the toggle is switched on (renderSessionInfo() always creates the
   span; this just controls whether it's visible), so there's nothing
   to re-render when the checkbox changes. */
body.hide-session-players .session-info-players { display: none; }
/* Groups a Session Finder row together with its own "Availability"
   result underneath into one visually boxed unit on desktop too —
   mobile already does this (the media query further below boxes each
   <tr> itself as a flex "card"; this is deliberately scoped to
   min-width so it never doubles up with that). Borders/radius go on
   the <td>s rather than the <tr> itself: under border-collapse:
   collapse (.finder-table's own base rule above), a border set
   directly on <tr> isn't reliably rendered across browsers, whereas
   cell borders always are. A row with no Availability result open yet
   still gets its own fully-closed box — only one that DOES have one
   open drops its shared bottom edge so the two rows read as one. */
@media (min-width: 781px) {
  /* Cancels .finder-table's own base border-bottom (set unconditionally
     on every td above) for a row that has an Availability result open
     right underneath it, so the shared edge between the two actually
     disappears instead of leaving a stray line across the middle of
     what should read as one box. */
  .finder-table tbody tr:has(+ .session-info-row) td { border-bottom: none; }
  .finder-table tbody tr:not(.session-info-row):not(.finder-row-spacer) td {
    border-top: 1px solid var(--border);
  }
  .finder-table tbody tr:not(.session-info-row):not(.finder-row-spacer) td:first-child { border-left: 1px solid var(--border); border-top-left-radius: 8px; }
  .finder-table tbody tr:not(.session-info-row):not(.finder-row-spacer) td:last-child { border-right: 1px solid var(--border); border-top-right-radius: 8px; }
  .finder-table tbody tr:not(.session-info-row):not(.finder-row-spacer):not(:has(+ .session-info-row)) td {
    border-bottom: 1px solid var(--border);
  }
  .finder-table tbody tr:not(.session-info-row):not(.finder-row-spacer):not(:has(+ .session-info-row)) td:first-child { border-bottom-left-radius: 8px; }
  .finder-table tbody tr:not(.session-info-row):not(.finder-row-spacer):not(:has(+ .session-info-row)) td:last-child { border-bottom-right-radius: 8px; }
  .finder-table tbody tr.session-info-row td {
    border-left: 1px solid var(--border); border-right: 1px solid var(--border); border-bottom: 1px solid var(--border);
    border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;
  }
  /* The 2-3px gap itself, desktop only — see .finder-row-spacer's own
     base rule (height: 0) above for why mobile gets none. */
  .finder-table tr.finder-row-spacer td { height: 3px; }
}

/* A red "✕" icon in place of a text "Remove" button — Scheduled
   Bookings' own Recurring table. */
.icon-btn-remove {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  border: 1px solid #D9534F; border-radius: 50%; background: none;
  color: #D9534F; font-size: 0.9em; line-height: 1; cursor: pointer;
}
.icon-btn-remove:hover { background: #D9534F; color: #fff; }

/* ---- List pages (Current Bookings, Scheduled, Waitlist, Court Monitor) ----
   One row per item: a coloured left edge, the item's name in bold with
   a grey detail line under it, small status badges, then its actions. */
.page-intro { margin: -6px 0 14px; color: var(--muted); font-size: 0.93em; }
.card > h2:first-child { margin-top: 0; }
.item-list { display: grid; gap: 6px; }
.item-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 8px 14px;
  padding: 10px 12px; background: var(--panel);
  border: 1px solid var(--border); border-left: 4px solid var(--kind, var(--green)); border-radius: 0 8px 8px 0;
}
.item-row.is-dimmed { opacity: 0.6; }
.item-main { min-width: 0; }
.item-title { font-weight: 700; }
.item-meta { color: var(--muted); font-size: 0.88em; margin-top: 2px; font-variant-numeric: tabular-nums; }
.item-meta .sep { margin: 0 6px; opacity: 0.6; }
/* A detail line's parts ("Matchplay Session 2.5 - 3.25", "· Basset Down")
   each stay whole: a long line breaks between parts, not inside one. */
.item-meta .mp { display: inline-block; }
.item-side { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
.item-side form { margin: 0; }
.item-side .secondary-button { padding: 6px 14px; font-size: 0.88em; }
.item-toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 0.85em; color: var(--muted); margin: 0; cursor: pointer; }
.item-toggle input { margin: 0; width: 16px; height: 16px; accent-color: var(--green); }
.pill {
  display: inline-block; font-size: 0.75em; font-weight: 700; padding: 2px 9px; border-radius: 999px; white-space: nowrap;
  background: #ECE9E0; color: var(--muted);
}
.pill-good { background: var(--green-soft); color: var(--green-dark); }
.pill-test { background: var(--gold-soft); color: #7A5A26; }
.pill-bad { background: var(--red-soft); color: var(--red); }
/* A match's type as a pill beside its availability, in the Match Finder's
   colours (League green, Friendly slate — see .kind-match/.kind-social). */
.pill.pill-kind { background: var(--kind-soft); color: var(--kind); }
/* Public / Private: outlined, so they don't read as another status. */
.pill.pill-visibility { background: transparent; border: 1px solid var(--line, #e6e6e6); color: var(--muted); }
.pill.pill-visibility.is-private { border-color: var(--slate); color: var(--slate); }
a.pill { text-decoration: none; }
a.pill-bad:hover { background: var(--red); color: #fff; }
.empty-note { color: var(--muted); margin: 4px 0 0; }
.status-line { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.status-line form { margin: 0; }
.status-line p { margin: 0; }
.inline-add { display: flex; flex-wrap: wrap; gap: 10px 12px; align-items: flex-end; }
.inline-add > label { margin: 0; flex: 0 0 auto; }
.inline-add > label.grow { flex: 1 1 260px; }
.inline-add select, .inline-add input { height: 44px; }
.inline-add .time-select { width: 140px; }
.inline-add .duration-select { width: 90px; max-width: none; }
.inline-add button { height: 44px; flex: 0 0 auto; }

/* ---- Help page: same heading rows and label/description lists as the
   booking status page, with a contents strip at the top. ---- */
.help-contents { display: flex; flex-wrap: wrap; gap: 6px; }
.help-contents a {
  font-size: 0.85em; padding: 5px 11px; border-radius: 999px; text-decoration: none;
  border: 1px solid var(--border); background: var(--panel); color: var(--text);
}
.help-contents a:hover { border-color: var(--green); background: var(--green-soft); color: var(--green-dark); }
a.help-link-button { text-decoration: none; padding: 7px 14px; font-size: 0.88em; }
.help-card { scroll-margin-top: 16px; }
.help-card .form-title-row { margin-bottom: 6px; }
.help-card h2 { margin: 0; font-size: 1.15em; }
.help-open { font-size: 0.88em; color: var(--green); font-weight: 600; text-decoration: none; white-space: nowrap; }
.help-open:hover { text-decoration: underline; }
.help-list { margin: 10px 0 0; padding: 0; border-top: 1px solid var(--border); }
.help-list > div {
  display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 14px;
  padding: 10px 0; border-bottom: 1px solid var(--border); font-size: 0.93em; line-height: 1.5;
}
.help-list dt { font-weight: 600; }
.help-list dd { margin: 0; }
.help-list .pill { vertical-align: 1px; }
.help-inline-legend { display: inline-flex; margin: 0 4px; font-size: 0.95em; vertical-align: 0; }
.help-chip { padding: 2px 10px; min-height: 0; font-size: 0.85em; cursor: default; }
.help-icon {
  display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%;
  font-size: 0.75em; font-weight: 700; vertical-align: 1px;
}
.help-icon.tone-good { background: var(--green-soft); color: var(--green); }
.help-icon.tone-bad { background: var(--red-soft); color: var(--red); }
.help-icon.tone-info { background: var(--gold-soft); color: var(--gold); font-family: Georgia, serif; font-style: italic; }
.help-address {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 12px 0 4px;
  padding: 10px 12px; background: var(--bg); border-radius: 9px;
}
.help-address code { font-family: ui-monospace, "SF Mono", Consolas, monospace; font-size: 0.95em; font-weight: 600; }
.help-address .help-copy { padding: 5px 12px; font-size: 0.85em; margin-left: auto; }
.help-subhead { margin: 18px 0 0; font-size: 0.95em; }
.feature-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-top: 12px; }
.feature-tile {
  display: flex; gap: 12px; align-items: flex-start; padding: 12px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--panel); color: var(--text); text-decoration: none;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.help-card a.feature-tile { color: var(--text); }
.feature-tile:hover { border-color: var(--green); background: var(--green-soft); }
.feature-icon {
  flex: 0 0 38px; width: 38px; height: 38px; border-radius: 10px; background: var(--bg);
  display: flex; align-items: center; justify-content: center; font-size: 1.2em;
}
.feature-text { display: grid; gap: 2px; font-size: 0.9em; line-height: 1.4; }
.feature-text span { color: var(--muted); }
.help-card .page-intro { margin: 4px 0 0; }
.help-card a:not(.help-open):not(.secondary-button):not(.primary-button) { color: var(--green-dark); }
.help-card a.primary-button, .help-card a.primary-button:visited { color: #fff; }

/* ---- Password with a show/hide eye (login page) ---- */
.password-field { position: relative; margin: 4px 0 18px; }
.card.narrow input[type=email] { height: 44px; }
.password-field input { margin: 0; padding-right: 46px; height: 44px; }
.password-toggle {
  position: absolute; top: 0; right: 0; width: 44px; height: 44px; padding: 0; margin: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 0 6px 6px 0; color: var(--muted); cursor: pointer;
}
.password-toggle:hover { color: var(--green); }
.password-toggle { touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.password-toggle[aria-pressed="true"] { color: var(--green); }
.password-toggle[hidden] { display: none; }
.settings-grid > .settings-password { margin-bottom: 14px; }
.settings-grid > .settings-password.span-2 { grid-column: 1 / -1; }
.settings-password .field-hint { margin-top: 4px; }
#settings-account { margin-top: 14px; }
.settings-password > label { margin-bottom: 0; }
.settings-password .password-field { margin: 4px 0 0; }
.password-toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }
.password-toggle .icon-hide, .password-toggle[aria-pressed="true"] .icon-show { display: none; }
.password-toggle[aria-pressed="true"] .icon-hide { display: block; }

/* ---- Settings ---- */
.settings-version { margin: 14px 0 0; text-align: center; font-size: 0.8em; color: var(--muted); }
.settings-card { scroll-margin-top: 16px; }
.settings-card h2 { margin: 0; font-size: 1.15em; }
.settings-card .page-intro { margin: 4px 0 14px; }
.settings-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 16px; }
.settings-grid > label { margin-bottom: 14px; }
.settings-grid > label.span-2 { grid-column: 1 / -1; }
.settings-grid input, .settings-grid select { height: 44px; }
.settings-grid label > .field-hint { display: block; margin-top: 4px; }
.field-hint { display: block; margin-top: 4px; font-size: 0.82em; color: var(--muted); }
.settings-note { margin: 2px 0 0; padding: 10px 12px; background: var(--bg); border-radius: 9px; font-size: 0.88em; color: var(--muted); }
.pref-list { border-top: 1px solid var(--border); }
.pref-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 12px 0; margin: 0; border-bottom: 1px solid var(--border); cursor: pointer;
}
.pref-row-stack { display: block; cursor: default; }
.pref-list > :last-child { border-bottom: none; padding-bottom: 0; }
.pref-text { display: grid; gap: 2px; font-size: 0.93em; line-height: 1.45; margin: 0; }
.pref-text span { color: var(--muted); font-size: 0.95em; }
.pref-number { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; color: var(--muted); font-size: 0.9em; }
.pref-number input { width: 70px; height: 40px; margin: 0; text-align: center; }
.pref-license { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.pref-license input { max-width: 360px; margin: 0; height: 44px; }
.pref-row-stack .field-hint { margin-top: 6px; }
/* On/off switch — still a real checkbox (same name, same checked state
   on submit), just drawn as a switch. */
input[type=checkbox].switch {
  appearance: none; -webkit-appearance: none; flex: 0 0 auto; margin: 0; cursor: pointer;
  width: 46px; height: 26px; border-radius: 999px; background: #CFCBBE; position: relative;
  transition: background-color 0.15s ease;
}
input[type=checkbox].switch::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.25); transition: transform 0.15s ease;
}
input[type=checkbox].switch:checked { background: var(--green); }
input[type=checkbox].switch:checked::after { transform: translateX(20px); }
input[type=checkbox].switch:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { input[type=checkbox].switch, input[type=checkbox].switch::after { transition: none; } }
.settings-save {
  position: sticky; bottom: 0; z-index: 5; margin-top: 14px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 -4px 16px rgba(17, 32, 26, 0.06);
}
.settings-save span { font-size: 0.88em; }
.settings-save .settings-saved { color: var(--green-dark); font-weight: 600; }
.settings-save { transition: background-color 0.3s ease, border-color 0.3s ease; }
.settings-save.just-saved { background: var(--green-soft); border-color: var(--green); }
/* The Connecting to Padel4all pill sits above a page's Save bar, never on it. */
body:has(.settings-save) .engine-status { bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }
@media (prefers-reduced-motion: reduce) { .settings-save { transition: none; } }
.pref-fold-message { margin: 0 0 12px; }

/* ---- Getting started tour ---- */
.tour-progress { height: 6px; border-radius: 999px; background: var(--bg); overflow: hidden; margin-top: 4px; }
.tour-progress span { display: block; height: 100%; width: 0; background: var(--green); border-radius: 999px; transition: width 0.25s ease; }
.tour-count { margin: 6px 0 16px; font-size: 0.8em; color: var(--muted); font-weight: 600; letter-spacing: 0.04em; }
.tour-step h2 { margin: 0; font-size: 1.3em; }
.tour-step .page-intro { margin: 6px 0 0; }
.tour-kicker {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 6px;
  font-size: 0.78em; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted);
}
.tour-kicker .pill { letter-spacing: 0.03em; }
.tour-checklist dd { text-align: right; }
.tour-checklist > div { grid-template-columns: minmax(0, 1fr) auto; }
a.tour-action { display: inline-block; margin-top: 16px; text-decoration: none; }
.tour-nav { display: flex; justify-content: space-between; gap: 10px; margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--border); }
.tour-done-icon {
  width: 60px; height: 60px; border-radius: 50%; margin: 4px auto 12px; background: var(--green-soft); color: var(--green);
  display: flex; align-items: center; justify-content: center; font-size: 1.8em; font-weight: 700;
}
.tour-center { text-align: center; }
.tour-center-row { justify-content: center; }
@media (prefers-reduced-motion: reduce) { .tour-progress span { transition: none; } }

/* ---- Compact booking forms (Book a Session / Book a Court) ---- */
.form-title-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.form-title-row h1 { margin: 0; font-size: 1.45em; }
.form-title-row .rating-inline { margin-bottom: 0; font-weight: 400; color: var(--muted); font-size: 0.92em; }
.form-title-row .rating-inline strong { color: var(--text); font-weight: 700; }
.date-name-row > label.date-field { flex: 0 0 auto; }
.date-name-row > label.name-field { flex: 1 1 220px; }
.date-name-row > label.duration-field { flex: 0 0 90px; }
/* Every box on a row the same 44px as the date pickers, so labels and
   boxes line up across the row. */
.date-name-row input, .date-name-row select, .finder-search select { height: 44px; }
/* The outline the generic submit-button rule (border: none, further
   up) strips from Save / Verify player names. */
button[type=submit].secondary-button { border: 1px solid var(--border); background: var(--panel); }

/* "Schedule for later" (_run_at_field.html): folded unless opened or set. */
.schedule-later { margin: 0 0 14px; }
.schedule-later > summary {
  display: block; padding: 4px 0; cursor: pointer; list-style: none;
  color: var(--green); font-weight: 600; font-size: 0.95em;
}
.schedule-later > summary::-webkit-details-marker { display: none; }
.schedule-later > summary::before { content: "▸"; display: inline-block; margin-right: 6px; transition: transform 0.15s ease; }
.schedule-later[open] > summary::before { transform: rotate(90deg); }
.schedule-later-set { color: var(--text); font-weight: 700; }
.schedule-later-panel { background: var(--bg); border-radius: 9px; padding: 12px; margin-top: 8px; }
.schedule-later-hint { margin: 0 0 8px; font-size: 0.85em; }
.players-fold .field-row.two-col > label { margin-bottom: 10px; }
.players-fold .players-verify { margin-top: 4px; }
.players-fold .verify-banner, .players-fold .content-activity { margin: 4px 0 10px; }
@media (prefers-reduced-motion: reduce) { .schedule-later > summary::before { transition: none; } }

/* Court Finder search: one line on desktop (dates, from, to, minutes,
   Search); a two-row grid on a phone (see the mobile block). */
.finder-search { display: flex; flex-wrap: wrap; gap: 10px 12px; align-items: flex-end; margin-top: 8px; }
.finder-search > label { flex: 0 0 auto; margin: 0; }
.finder-search .date-range-input { width: 210px; }
.finder-search .time-select { width: 140px; }
.finder-search .duration-select { width: 90px; max-width: none; }
.finder-search .fs-go { flex: 0 0 auto; height: 44px; }

/* ---- Court Finder: one strip per searched day, free times as chips ---- */
.finder-summary { margin: 16px 0 4px; }
.day-strip {
  display: grid; grid-template-columns: 92px 1fr; gap: 12px; align-items: center;
  padding: 10px 0; border-bottom: 1px solid var(--border);
}
.day-strip:last-child { border-bottom: none; }
.day-strip-date { font-weight: 700; line-height: 1.2; }
.day-strip-date span { display: block; color: var(--muted); font-weight: 400; font-size: 0.88em; }
.time-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.time-chip {
  font: inherit; font-size: 0.9em; font-weight: 600; font-variant-numeric: tabular-nums;
  padding: 6px 14px; min-height: 36px; border-radius: 999px; cursor: pointer;
  border: 1.5px solid var(--green); background: var(--green-soft); color: var(--green);
}
.time-chip:hover, .time-chip[aria-pressed="true"] { background: var(--green); color: #fff; }
.time-chip:focus-visible, .month-day:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.day-strip-none { color: var(--muted); font-size: 0.9em; font-style: italic; }

/* ---- Session Finder: month calendar + grouped list ---- */

.session-legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 0.82em; color: var(--muted); margin: -4px 0 12px; }
.session-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; vertical-align: -1px; background: var(--kind); }
.session-legend .legend-hollow i { background: none; border: 1.5px solid var(--muted); }
.kind-match { --kind: var(--green); --kind-soft: var(--green-soft); }
.kind-coach { --kind: var(--gold); --kind-soft: var(--gold-soft); }
.kind-social { --kind: var(--slate); --kind-soft: var(--slate-soft); }

.month-cal { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; margin-bottom: 14px; }
.month-dow { font-size: 0.72em; color: var(--muted); text-align: center; text-transform: uppercase; letter-spacing: 0.06em; }
.month-day {
  min-height: 64px; padding: 6px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel);
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px;
  font: inherit; color: var(--text); text-align: left; cursor: pointer;
}
.month-day.is-empty { background: transparent; border-style: dashed; cursor: default; color: var(--muted); }
.month-day.is-past { opacity: 0.5; }
.month-day.no-matches { opacity: 0.45; }
.month-day:not(.is-empty):hover { border-color: var(--green); }
.month-day[aria-pressed="true"] { border: 2px solid var(--green); padding: 5px; opacity: 1; }
.month-num { font-weight: 700; font-size: 0.85em; font-variant-numeric: tabular-nums; padding: 0 2px; }
.month-num small { font-weight: 400; color: var(--muted); margin-left: 3px; font-size: 0.85em; }
.month-day.is-today .month-num { background: var(--green); color: #fff; border-radius: 999px; padding: 0 7px; }
.month-day.is-today .month-num small { color: #fff; }
.month-dots { display: flex; flex-wrap: wrap; gap: 3px; }
.month-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--kind); }
.month-dot.is-full { background: none; border: 1.5px solid var(--kind); }
.month-dot[hidden] { display: none; }

.session-finder.view-month .session-day:not(.is-selected) { display: none; }
.session-day-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  font-weight: 700; font-size: 0.92em; padding: 8px 2px 6px; margin: 0 0 6px;
  border-bottom: 2px solid var(--green); background: var(--panel);
}
.session-day-head label.checkbox { margin: 0; font-weight: 400; color: var(--muted); }
.finder-scanned { margin: 0 0 8px; }
.session-row {
  border: 1px solid var(--border); border-left: 4px solid var(--kind); border-radius: 0 8px 8px 0;
  background: var(--panel); margin-bottom: 5px;
}
.session-row[hidden] { display: none; }
.session-row-main {
  display: grid; align-items: center; gap: 4px 12px; padding: 8px 10px;
  grid-template-columns: 96px minmax(0, 1fr) auto auto; grid-template-areas: "time name badge actions";
}
.session-row-time { grid-area: time; font-size: 0.88em; color: var(--muted); font-variant-numeric: tabular-nums; }
.session-row .session-finder-name { grid-area: name; font-weight: 700; }
.session-badge {
  grid-area: badge; font-size: 0.75em; font-weight: 700; padding: 2px 9px; border-radius: 999px; white-space: nowrap;
  background: var(--green-soft); color: var(--green-dark);
}
.session-badge.is-full { background: var(--red-soft); color: var(--red); }
.session-badge[hidden] { display: none; }
.session-row .session-finder-actions { grid-area: actions; }
.session-info { margin: 0 10px; padding: 8px 0 9px; font-size: 0.9em; border-top: 1px dashed var(--border); }
.session-info[hidden] { display: none; }
.session-day-none { margin: 4px 2px 8px; font-size: 0.9em; }


/* Every mobile override lives in this ONE block, positioned at the
   very END of the file deliberately — not for tidiness, but because a
   real, repeated bug came from having it near the TOP instead: at
   equal selector specificity, CSS's own tiebreaker is source order
   (whichever rule comes LAST in the file wins), and this file has
   plenty of base rules for the exact same selectors (.card,
   .field-row, .field-row.two-col, input[type=date] max-width, table/
   td, #status-log-table, even a couple of !important declarations)
   sitting further down than an earlier version of this block was.
   Every one of those silently defeated this block's own override —
   confirmed live: Player 1-4 kept rendering two-per-row on a phone no
   matter what this block said, My Player Rating stayed pushed right,
   and Status Log's message column kept truncating with an ellipsis
   instead of wrapping — because the LATER, unconditional rule for
   each of those always won regardless of screen width. Any new mobile
   override belongs in here, at the bottom, not spliced in next to the
   desktop rule it's overriding. */
@media (max-width: 780px) {
  .app-shell { flex-direction: column; }
  .nav-edge-tab { display: block; }
  /* The logo's own desktop rule (.brand-logo, width: 100%) assumes a
     full-width sidebar column — needs an actual fixed size on the
     drawer's own narrower width instead. Centred (.sidebar-brand's own
     base rule already does that) — no close button to share the row
     with any more, the edge tab/backdrop are the only way to dismiss
     the drawer now. */
  .sidebar-brand .brand-link { width: 84px; }
  .sidebar-brand .brand-logo { width: 84px; height: 84px; flex: 0 0 auto; }
  .sidebar {
    position: fixed; top: 0; left: 0; z-index: 35;
    /* Narrower than before — real gap found live: at the old 84vw/
       300px, the sidebar was wider than its own content (nav links,
       the Next booking countdown box) needed, leaving the countdown
       box's left/right margins reading unevenly against the drawer's
       own edges instead of sitting symmetrically inside it. */
    width: 76vw; max-width: 270px; height: 100vh;
    padding-left: calc(18px + env(safe-area-inset-left, 0px));
    padding-top: 14px; padding-bottom: 14px;
    box-shadow: 4px 0 24px rgba(0,0,0,0.25);
    transform: translateX(-100%);
    transition: transform 0.25s cubic-bezier(0.22, 0.61, 0.36, 1);
    /* Real gap found live: the desktop 24px gap between brand/nav/
       status (three sections) plus roomier per-link padding below
       added up to more height than a phone screen actually has —
       Credit Balance/Next booking ended up needing a scroll to reach.
       Tighter throughout so the whole drawer fits on one screen. */
    gap: 10px; overflow-y: auto;
  }
  .sidebar.open { transform: translateX(0); }
  /* Clears the edge tab's own bottom edge (top: 16px + height: 44px)
     plus a small gap, so it never overlaps a card's own heading
     sitting flush at the top-left of the page. */
  .main-panel { padding: calc(env(safe-area-inset-top, 0px) + 70px) 16px 32px; width: 100%; max-width: none; }
  /* .keep-inline opts a specific .field-row OUT of the blanket "stack
     everything" treatment below — Court Finder's own From/To and
     Duration/Earliest/Latest groups are deliberately short, narrow
     fields meant to stay paired on one line (wrapping via the base
     rule's own flex-wrap if they truly don't fit), not stacked one
     per line like Sessions' own Join waitlist/Hide my name/Player
     Rating cluster still needs. */
  .field-row:not(.keep-inline), .field-row.two-col:not(.keep-inline) { display: block; }
  .field-row > label { margin-bottom: 14px; }
  /* Tighter than the base 160px flex-basis — Duration/Earliest time/
     Latest time are all short fields (a number, a "HH:MM") that don't
     need anywhere near that much room, and fitting three of them on
     one line at all requires giving up that generous a minimum. */
  .field-row.tight > label { flex: 1 1 90px; }
  /* "Join waitlist"/"Hide my name" read as two closely-related toggles
     — real gap found live: each carried the same 14px bottom margin
     an actual full-width INPUT field needs to separate it from what
     follows, which read as an oddly large gap between two short
     checkboxes stacked right on top of each other. */
  .field-row > label.checkbox { margin-bottom: 6px; }
  /* My Player Rating no longer has a row of sibling fields to sit at
     the END of (everything's stacked now) — push-right's own
     margin-left: auto just stranded it on its own line, right-aligned
     with nothing to its left. */
  .field-row > .push-right { margin-left: 0 !important; margin-bottom: 14px; }
  /* Refresh / Hide cancelled / Hide test bookings stacked instead of
     one flex row with no wrap — real gap found live: "Hide test
     bookings" ran off the right edge of the screen instead of ever
     reaching a second line. */
  .last-refreshed-row { flex-direction: column; align-items: flex-start; gap: 12px; }
  /* Roomier tap targets and a touch less padding hunting for space on
     a phone-width card — 28px on all four sides of every card added
     up fast on a 375px-wide screen. */
  .card { padding: 20px; }
  .card + .card { margin-top: 14px; }
  button[type=submit], .primary-button, .secondary-button:not(.small-button) { min-height: 44px; }
  /* session-info-btn no longer belongs here — it reads as text
     ("Availability") now, not a small square icon, so it needs auto
     width like any other .small-button, not a fixed 32px square that
     would clip its own label. */
  .icon-btn-remove { width: 32px; height: 32px; }
  /* Taller than desktop (a thumb, not a mouse pointer) but tighter
     than an earlier version of this rule (13px) — that, plus the
     drawer's own 24px section gap, pushed Credit Balance/Next booking
     below the fold on a real phone screen. */
  .sidebar-nav { gap: 0; }
  .sidebar-nav a { padding: 8px 10px; font-size: 0.98em; }
  .sidebar-status { gap: 8px; padding: 10px 0; }
  .sidebar-brand { margin-bottom: 4px; }
  /* Forces the "?"/Select pair (.session-finder-actions) onto its own
     line below the session name — a 100%-wide flex item can never
     share a row with anything else, so combined with the parent td's
     own flex-wrap (below) this always starts a fresh line. */
  .session-finder-actions { flex: 1 0 100%; }

  /* Every table becomes one card per row instead of a horizontally-
     scrolling grid — the explicit design brief: never make the user
     scroll sideways to read anything, only up/down (modelled on
     Padel4all's own mobile site, whose own schedule reads as a plain
     vertical list rather than a table at all). The header row itself
     disappears; each cell shows its own column name instead, via
     data-label (every table in every template sets this per <td> —
     see e.g. current_bookings.html's own comment on why headers can't
     just be read from the <thead> in CSS: attr() only ever reads the
     SAME element's own attribute, never a sibling's). Applies to
     every plain <table> (Scheduled Bookings, Waitlist, Monitored
     Courts, Status Log) and .finder-table (Session/Court Finder,
     Current Bookings) alike; #status-log-table's own desktop-only
     table-layout/ellipsis rules (below) are overridden back out. */
  table, .finder-table { display: block; width: 100%; border: none; }
  table thead, .finder-table thead { display: none; }
  table tbody, .finder-table tbody, table tr, .finder-table tr { display: block; width: 100%; }
  table tr, .finder-table tr {
    margin-bottom: 10px; padding: 4px 14px;
    border: 1px solid var(--border); border-radius: 10px; background: var(--panel);
  }
  /* .finder-row-spacer (see style.css's own desktop-only 3px gap rule)
     would otherwise inherit the rule above and render as its own
     empty bordered card — mobile's 10px margin-bottom already gives
     every card a gap, so it has no job to do here at all. */
  .finder-table tr.finder-row-spacer { display: none; }
  /* A "?" check's own result (.session-info-row, a separate <tr>
     inserted right after the session's own row — see
     sessions_page.html's own insertDetailRow()) used to land in its
     own separate bordered card sitting directly on top of the session
     row's, reading as two disconnected boxes instead of one result.
     :has() lets the row ABOVE a .session-info-row know one follows it
     with no JS/template change, so the shared edge between them can
     be dropped and the two visually become one box. Degrades
     gracefully (two separate boxes, still fully readable) on a
     browser old enough not to support :has() at all. */
  .finder-table tr:has(+ .session-info-row) {
    margin-bottom: 0; border-bottom: none;
    border-bottom-left-radius: 0; border-bottom-right-radius: 0;
  }
  .finder-table tr.session-info-row {
    margin-top: 0; border-top: none; padding-top: 0;
    border-top-left-radius: 0; border-top-right-radius: 0;
  }
  /* Label then value, wrapping onto a second line together rather
     than being squeezed onto one (flex-wrap: wrap) if the value is
     long — a booking's own name or a Status Log message can run much
     longer than "Date"/"Time" ever does, and a fixed left/right split
     would either clip it or force the row itself wider. */
  table td, .finder-table td {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 9px 0; border-bottom: 1px solid var(--border);
    white-space: normal; font-size: 0.92em;
  }
  table td:last-child, .finder-table td:last-child { border-bottom: none; }
  table td::before, .finder-table td::before {
    content: attr(data-label); font-weight: 600; color: var(--muted); flex: 0 0 auto;
  }
  table td:not([data-label])::before, .finder-table td:not([data-label])::before { content: none; }
  /* A cell with no visible content and no label (a pure spacer/action
     column some rows leave empty, e.g. Scheduled Bookings' own
     "Confirm card now" column) would otherwise still draw an empty
     row with just a bottom border — collapse it instead. */
  table td:empty:not([data-label]), .finder-table td:empty:not([data-label]) { display: none; }
  /* No horizontal OR clipped-height scrolling once rows are full-width
     stacked cards — this is now just part of the page's own natural
     vertical flow ("easy to go up and down"). */
  .finder-table-scroll, .table-scroll { overflow: visible; max-height: none; }
  /* Status Log's own desktop-only fixed-width/ellipsis treatment
     (needed there to stop a long message pushing a real GRID table
     wider than the screen) is moot once every row is a stacked card
     instead — the message now just wraps like any other cell. */
  #status-log-table { table-layout: auto; }
  /* Same real bug class as #status-log-table above, just found later:
     .bookings-table's own desktop-only table-layout:fixed + explicit
     per-column nth-child widths (added to line up Current Bookings'
     Sessions/Courts tables — see that rule's own comment) bled into
     the mobile card layout too, since nothing here reset them. A
     fixed ~19% width on a 375px phone left no room for a label and
     its value to sit side by side, so every field wrapped onto its
     own separate line instead — restoring table-layout:auto and each
     column back to width:auto here is what brings back the normal
     stacked-card look. Selectors matched to the desktop rule's own
     (needs equal specificity to actually win, not just come later). */
  .bookings-table { table-layout: auto; }
  .bookings-table th:nth-child(1), .bookings-table td:nth-child(1),
  .bookings-table th:nth-child(2), .bookings-table td:nth-child(2),
  .bookings-table th:nth-child(3), .bookings-table td:nth-child(3),
  .bookings-table th:nth-child(4), .bookings-table td:nth-child(4),
  .bookings-table th:nth-child(5), .bookings-table td:nth-child(5),
  .bookings-table th:nth-child(6), .bookings-table td:nth-child(6),
  .bookings-table th:nth-child(7), .bookings-table td:nth-child(7) { width: auto; }
  /* Real gap found live: this rule reset overflow/text-overflow but
     left white-space: nowrap in place from the desktop rule above —
     with nowrap and overflow now visible (instead of hidden), a long
     message had nowhere to go but spill off the right edge of the
     card/screen instead of wrapping. white-space: normal is the actual
     fix; overflow/text-overflow above are harmless once nowrap is
     gone, kept for clarity. */
  .status-log-message { overflow: visible; text-overflow: clip; white-space: normal; max-width: 100%; }

  /* Finder views on a phone: time and status on the first line, the
     name under them, buttons last. */
  .day-strip { grid-template-columns: 64px 1fr; }
  .month-cal { gap: 3px; }
  .month-day { min-height: 50px; padding: 4px; }
  .month-day[aria-pressed="true"] { padding: 3px; }
  .month-dot { width: 7px; height: 7px; }
  .month-day.is-empty .month-num small { display: none; }
  .session-row-main {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "time badge" "name name" "actions actions";
  }
  .session-row .session-finder-actions { flex: none; margin-top: 4px; }

  /* Home on a phone: logo above the welcome, one glance box per line. */
  .home-hero { flex-direction: column; text-align: center; gap: 12px; }
  .home-actions { justify-content: center; }
  .glance-grid { grid-template-columns: minmax(0, 1fr); }

  /* Settings on a phone: one field per line. */
  .settings-grid { grid-template-columns: minmax(0, 1fr); }
  .pref-license input { max-width: none; }

  /* Help on a phone: label above its description. */
  .help-list > div { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .tour-checklist > div { grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
  .feature-grid { grid-template-columns: minmax(0, 1fr); }
  .help-address .help-copy { min-height: 0; }

  /* List rows on a phone: details on top, badges and actions under. */
  .item-row { grid-template-columns: minmax(0, 1fr); }
  .item-side { justify-content: flex-start; }
  .inline-add > label.grow { flex-basis: 100%; }
  .inline-add .time-select, .inline-add input[type=date] { width: 100%; }
  .inline-add > label.fs-half { flex: 1 1 40%; }

  /* Compact booking forms on a phone: the wide field (Session /
     Preferred times) first on its own line, then Date (and Minutes)
     side by side. */
  .field-row.date-name-row { display: flex; flex-wrap: wrap; gap: 0 10px; }
  .date-name-row > label.name-field { order: -1; flex: 1 1 100%; }
  .date-name-row > label.date-field { flex: 1 1 auto; }
  .date-name-row > label.duration-field { flex: 0 0 84px; }
  .schedule-later-panel { padding: 10px; }

  /* Court Finder search: Dates + Search on the first row, From / To /
     Minutes on the second. */
  .finder-search { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 84px; gap: 10px 8px; }
  .finder-search .fs-dates { grid-column: 1 / 3; grid-row: 1; }
  .finder-search .fs-go { grid-column: 3; grid-row: 1; width: 100%; padding: 0 6px; min-height: 44px; }
  .finder-search .date-range-input, .finder-search .time-select, .finder-search .duration-select { width: 100%; }
}

/* Admin dashboard */
.admin-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-top: 12px; }
.admin-stats > div, .admin-stats > a { display: flex; flex-direction: column; gap: 2px; padding: 12px; border-radius: 10px; background: var(--green-soft); color: inherit; text-decoration: none; }
.admin-stats > a.admin-stat-warn { background: var(--gold-soft); }
.admin-stats strong { font-size: 1.6em; line-height: 1.1; color: var(--green-dark); }
.admin-stats span { font-weight: 600; }
.admin-stats em { font-style: normal; font-size: 0.82em; color: var(--muted); }
.admin-chart { display: grid; grid-template-columns: repeat(14, 1fr); gap: 4px; align-items: end; height: 150px; margin: 8px 0 14px; }
.admin-day { display: flex; flex-direction: column; align-items: center; gap: 4px; height: 100%; min-width: 0; }
.admin-bars { flex: 1; width: 100%; display: flex; align-items: flex-end; justify-content: center; gap: 2px; }
.admin-bars i { display: block; width: 28%; min-height: 1px; border-radius: 3px 3px 0 0; }
.admin-day > span { font-size: 0.68em; color: var(--muted); white-space: nowrap; overflow: hidden; max-width: 100%; }
.k-active { background: var(--green); } .k-tasks { background: #9CC5AE; } .k-bookings { background: #C99A3E; }
.admin-legend { font-size: 0.8em; color: var(--muted); display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.admin-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-left: 8px; }
.admin-split { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; }
.admin-split .pill { margin: 0 4px 6px 0; }
.admin-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0 -4px; }
.admin-table { width: 100%; min-width: 980px; font-size: 0.85em; }
.admin-table th { text-align: left; white-space: nowrap; color: var(--muted); font-weight: 600; padding: 6px 8px; }
.admin-table td { vertical-align: top; padding: 8px; border-top: 1px solid var(--line, #e6e6e6); }
.admin-table .pill { font-size: 0.85em; margin: 0 2px 3px 0; white-space: nowrap; }
.admin-list { list-style: none; margin: 0; padding: 0; }
.admin-list li { padding: 8px 0; border-top: 1px solid var(--line, #e6e6e6); font-size: 0.92em; }
.admin-list li.is-bad { color: var(--red); }
@media (max-width: 520px) { .admin-chart { height: 110px; } .admin-stats strong { font-size: 1.35em; } }
.admin-who { min-width: 220px; }
.admin-label-form { display: flex; gap: 4px; margin-bottom: 3px; }
.admin-label-form input { flex: 1; min-width: 0; padding: 5px 7px; font-size: 1em; font-weight: 600; }
.admin-label-form .small-button { padding: 4px 9px; min-height: 0; }
.admin-stack { flex-direction: column-reverse; justify-content: flex-start; align-items: stretch; gap: 0; width: 60%; margin: 0 auto; }
.admin-stack i { width: 100%; border-radius: 0; }
.k-users { background: var(--green); } .k-shared { background: #9CC5AE; } .k-redis { background: #C99A3E; }
.admin-mini { width: 100%; font-size: 0.88em; }
.admin-mini td { padding: 6px 6px 6px 0; border-top: 1px solid var(--line, #e6e6e6); vertical-align: top; }
.admin-mini td:nth-child(2) { white-space: nowrap; font-weight: 600; }
.admin-hours { display: grid; grid-template-columns: repeat(24, 1fr); gap: 2px; align-items: end; height: 80px; margin: 6px 0 12px; }
.admin-hours > div { display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; }
.admin-hours i { display: block; width: 80%; min-height: 1px; background: var(--green); border-radius: 2px 2px 0 0; }
.admin-hours span { font-size: 0.65em; color: var(--muted); height: 1.2em; }

/* Admin: user cards and plain rows (no tables — see admin.html) */
.admin-users { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 10px; }
.au { border: 1px solid var(--line, #e6e6e6); border-radius: 10px; padding: 12px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.au-top { display: flex; justify-content: space-between; gap: 10px; align-items: flex-start; }
.au-name { display: flex; flex-direction: column; min-width: 0; }
.au-name strong { font-size: 1.02em; }
.au-name span { font-size: 0.82em; color: var(--muted); overflow-wrap: anywhere; }
.au-cost { text-align: right; font-weight: 700; white-space: nowrap; }
.au-cost small { display: block; font-weight: 400; font-size: 0.72em; color: var(--muted); }
.au-pills { display: flex; flex-wrap: wrap; gap: 4px; }
.au-pills .pill { font-size: 0.78em; }
.au-line { margin: 0; font-size: 0.85em; color: var(--muted); line-height: 1.45; }
.au-edit summary { cursor: pointer; font-size: 0.82em; color: var(--green-dark); }
.au-edit .admin-label-form { margin-top: 6px; }
.admin-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 8px 0; border-top: 1px solid var(--line, #e6e6e6); font-size: 0.92em; }
.admin-row > span:first-child { min-width: 0; overflow-wrap: anywhere; }
.admin-row strong { white-space: nowrap; }
.admin-row small { font-size: 0.85em; }
.admin-row.is-bad { color: var(--red); }
@media (max-width: 520px) { .admin-users { grid-template-columns: 1fr; } }
.au-stats { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px; text-align: center; }
.au-stats span { display: flex; flex-direction: column; font-size: 0.7em; color: var(--muted); background: var(--green-soft); border-radius: 6px; padding: 5px 2px; }
.au-stats strong { font-size: 1.25em; color: var(--green-dark); }
@media (max-width: 400px) { .au-stats { grid-template-columns: repeat(3, 1fr); } }
.admin-users.hide-inactive .au.is-inactive { display: none; }
.admin-hide-inactive { font-size: 0.85em; font-weight: 400; margin: 0; }
.admin-hidden-note { margin: -6px 0 10px; }
.au-activity { margin-bottom: -2px; }
.au-act { font-size: 0.8em; padding: 5px 0; border-top: 1px solid var(--line, #e6e6e6); line-height: 1.35; overflow-wrap: anywhere; }
.au-act:first-of-type { margin-top: 6px; }
.au-act .muted { white-space: nowrap; margin-right: 4px; }
.au-act.is-bad { color: var(--red); }
.au-problems { color: var(--red); font-weight: 600; }
.dev-badge { position: fixed; top: 0; left: 50%; transform: translateX(-50%); z-index: 9999; background: #B24B3D; color: #fff; font-size: 0.72em; font-weight: 700; letter-spacing: 0.04em; padding: 3px 12px; border-radius: 0 0 8px 8px; pointer-events: none; }
/* Playtomic page */
.pt-club-row { margin: 4px 0 8px; }
.pt-add { margin: 0 0 12px; }
.pt-tabs { display: flex; gap: 6px; }
.pt-tabs a { flex: 1; text-align: center; padding: 9px 12px; border-radius: 999px; background: var(--green-soft); color: var(--green-dark); text-decoration: none; font-weight: 600; }
.pt-tabs a.is-on { background: var(--green); color: #fff; }
.pt-filter { display: flex; gap: 4px; flex-wrap: wrap; }
.pt-day { margin: 16px 0 6px; font-size: 1em; }
.pt-search { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 8px; align-items: end; margin-bottom: 12px; }
.pt-search label { display: flex; flex-direction: column; gap: 4px; font-size: 0.88em; font-weight: 600; }
/* Match criteria: Apply exactly as tall as the boxes beside it. */
.match-filters input, .match-filters select { height: 42px; box-sizing: border-box; }
/* Labels' usual bottom margin lifted the boxes above Apply (found live). */
.match-filters label { margin-bottom: 0; }
.match-filters button[type=submit] { height: 42px; min-height: 0; padding: 0 20px; font-size: 0.9em; margin: 0;
  justify-self: start; width: auto; }
/* My matches: the "need players?" line with Post a match beside it. */
.my-matches-head { display: flex; align-items: center; justify-content: space-between; gap: 8px 16px; flex-wrap: wrap; margin-bottom: 10px; }
.my-matches-head .field-hint { margin: 0; flex: 1 1 260px; }
.my-matches-head .primary-button { text-decoration: none; padding: 8px 16px; font-size: 0.9em; min-height: 0; }
.item-side a.secondary-button { text-decoration: none; display: inline-flex; align-items: center; }
.pt-fav { margin: 0 0 10px; }
.pt-star.is-on { color: #C99A3E; font-weight: 700; }
.pt-find { display: flex; gap: 6px; margin: 0 0 10px; }
.pt-find input { flex: 1; min-width: 0; }
.pt-results { display: grid; gap: 6px; margin: 0 0 12px; }
.pt-result { display: block; padding: 9px 12px; border: 1px solid var(--line, #e6e6e6); border-radius: 8px; color: inherit; text-decoration: none; }
.pt-result.is-current { border-color: var(--green); }
.pt-result span:last-child { display: block; font-size: 0.82em; color: var(--muted); }
.setup-steps li { margin: 0 0 14px; }
.setup-steps .primary-button { display: inline-block; margin: 6px 8px 4px 0; text-decoration: none; }
a.primary-button, a.primary-button:visited { color: #fff; }
.share-key { display: block; margin-top: 6px; word-break: break-all; user-select: all; -webkit-user-select: all; }
.paste-match { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 0 0 12px; }
#paste-fallback { margin: 0 0 12px; }
#paste-fallback textarea { width: 100%; }
/* Padel4all matches: the four seats (Team 1/2 × Left/Right) */
.seat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 12px 0; }
.seat-cell { border: 1px solid var(--line, #e6e6e6); border-radius: 10px; padding: 10px 12px; background: #fff; min-width: 0; }
.seat-cell.is-open { border-style: dashed; background: var(--bg); }
.seat-cell.is-me { border-color: var(--green); }
.seat-label { font-size: 0.78em; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin-bottom: 4px; }
.seat-name { font-weight: 600; margin-bottom: 4px; overflow-wrap: anywhere; }
/* A filled place: name with level to its right, contact on one line, and
   the organiser's Paid tick + Remove pinned along the bottom. */
.seat-cell { display: flex; flex-direction: column; }
.seat-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.seat-top .seat-name { margin-bottom: 0; }
.seat-level { font-weight: 600; color: var(--green); font-variant-numeric: tabular-nums; white-space: nowrap; }
.seat-contact { margin-top: 4px; overflow-wrap: anywhere; }
.seat-contact .sep { margin: 0 4px; }
.seat-foot { margin-top: auto; padding-top: 8px; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.seat-foot form { margin: 0; }
.seat-foot label.checkbox { margin: 0; font-size: 0.9em; }
.seat-foot .link-button { font-size: 0.9em; }
/* Remove: a small button — beats button[type=submit]'s big padding/text. */
.seat-foot button[type=submit].small-button { min-height: 0; padding: 5px 14px; font-size: 0.85em; }
/* Buttons side by side in a row or action bar are all one size, whatever
   they are (a link, a submit, primary or secondary) — Accept next to
   Decline, Cancel match next to Share (found live: mixed sizes). Bold
   throughout, like every button. */
.item-side .secondary-button, .item-side button[type=submit],
.match-actions .secondary-button, .match-actions button[type=submit] {
  padding: 10px 18px; font-size: 0.95em; font-weight: 600; line-height: 1.2;
}
button[type=submit], .primary-button { font-weight: 600; }
.seat-cell input[type=text] { width: 100%; margin-top: 6px; }
.seat-cell .seat-mine { display: flex; gap: 6px; align-items: center; font-weight: 600; }
/* An open place's "add a friend": the name box full width with a compact
   Add under it — side by side, a phone-width place squeezed the box to a
   sliver (found live). The box keeps 16px text: smaller makes iPhone
   Safari zoom the page on focus. */
.seat-friend { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; margin-top: 4px; }
.seat-friend input[type=text] { width: 100%; min-width: 0; margin-top: 0; font-size: 16px; padding: 7px 9px; box-sizing: border-box; }
.seat-friend button.small-button { min-height: 0; padding: 5px 14px; }
@media (max-width: 599px) {
  .seat-grid { gap: 8px; }
  .seat-cell { padding: 8px 10px; }
  .seat-label { font-size: 0.72em; }
  .seat-name { font-size: 1em; }
}
.match-summary { margin: 0 0 10px; }
a.item-row.item-link { color: inherit; text-decoration: none; }
.field-label { display: block; font-weight: 600; margin-bottom: 2px; }
.booking-pick { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 4px 0 14px; }
.booking-pick .field-label { flex-basis: 100%; }
.booking-chip { display: inline-block; padding: 8px 12px; border: 1px solid var(--line, #e6e6e6); border-radius: 999px; color: inherit; text-decoration: none; background: #fff; font-size: 0.92em; }
.booking-chip.is-on { border-color: var(--green); background: var(--green); color: #fff; }
.match-players { margin-top: 2px; }
.match-players .mp { white-space: nowrap; }
/* A button in a .button-row that's hidden until needed (e.g. Post as a match
   after a booking) must stay hidden despite the display rule above. */
.button-row a[hidden] { display: none; }
/* A match's details list (label left, value beside it) and the WhatsApp names switch. */
.match-details { margin: 10px 0 12px; }
.share-names { align-self: center; }
/* Match Finder: ‹ Wed 30 Sep – Tue 13 Oct › above the grid */
.match-range { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 12px 0 6px; }
.match-range .range-label { font-weight: 600; text-align: center; flex: 1; }
.match-range .range-arrow { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 36px; border: 1px solid var(--line, #e6e6e6); border-radius: 8px; font-size: 1.4em; line-height: 1; color: inherit; text-decoration: none; background: #fff; }
.match-range .range-arrow.is-off { opacity: 0.3; }
/* My matches: the row opens the match; Cancel/Leave/Withdraw sits beside it. */
.my-match-row .item-main.item-link { color: inherit; text-decoration: none; min-width: 0; flex: 1; }
.my-match-row .item-side { flex-wrap: wrap; justify-content: flex-end; }
/* Match page buttons in one aligned row (forms add no stray margin). */
.match-actions { align-items: center; }
.match-actions .inline-form { margin: 0; }
.share-names { display: inline-flex; margin-top: 8px; }
.match-cancel-notify { margin: 12px 0; }
.match-cancel-notify .button-row { margin-top: 8px; }
/* Guests joining a match without an account (the public match page). */
.guest-whatsapp { display: flex; flex-direction: column; gap: 8px; margin: 8px 0 16px; }
.guest-whatsapp a { text-align: center; }
.guest-form select, .guest-form input { width: 100%; }
.guest-login { margin-top: 16px; }
.preview-teams { margin-top: 4px; }
.cant-make-it { margin-top: 16px; }

/* ---- Accounts: sign-up, plans, legal ---- */
.login-alt { text-align: center; margin: 10px 0 0; }
.signup-terms { display: flex; align-items: flex-start; gap: 8px; margin: 6px 0 14px; font-size: 0.92em; }
.signup-terms input { margin-top: 3px; }
.link-button.inline-link { display: inline !important; margin: 0 !important; font-size: inherit !important; }
.pref-plan { display: inline-flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pref-plan .small-button { text-decoration: none; }
.plan-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; margin: 14px 0; }
.plan-card { border: 1px solid var(--line, #e6e6e6); border-radius: 12px; padding: 16px 18px; background: #fff; }
.plan-card.is-current { border-color: var(--green); box-shadow: 0 0 0 1px var(--green) inset; }
.plan-card h2 { margin: 0 0 4px; }
.plan-price { font-size: 1.25em; font-weight: 700; margin: 0 0 10px; }
.plan-price .muted { font-size: 0.7em; font-weight: 400; }
.plan-list { margin: 0 0 12px; padding-left: 18px; line-height: 1.6; }
.plan-actions { margin-top: 8px; }
.legal h2 { font-size: 1.05em; margin: 18px 0 6px; }
.legal p, .legal li { line-height: 1.55; }
