:root {
  --ink: #4A2080;
  --muted: #606070;
  --brand: #7A7498;
  --brand-dark: #4A2080;
  --pink: #FDF0F5;
  --tint: #F1EEFB;
  --surface: #ffffff;
  --canvas: #F8F6FB;
  --line: #DAD6E8;
  --danger: #9e382d;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  color: var(--ink);
  background-image: url('/assets/bg_pattern.svg');
  background-repeat: repeat;
  background-size: 220px 220px;
  font-family: "Montserrat", "Segoe UI", sans-serif;
  font-optical-sizing: auto;
  font-weight: 400;
  font-style: normal;
}

button, input, select, textarea { font: inherit; }

.font-semibold { font-weight: 500; }

.shell {
  width: min(760px, calc(100% - 32px));
  margin: 0 auto;
  padding: 32px 0 56px;
}

.hero {
  position: relative;
  padding: 28px;
  color: #fff;
  border-radius: 24px;
  background: linear-gradient(135deg, var(--brand-dark), var(--brand));
  box-shadow: 0 18px 45px rgba(96, 96, 112, .18);
  font-family: "Fredoka", "Segoe UI", sans-serif;
  font-optical-sizing: auto;
}

.hero-logo {
  position: absolute;
  top: 8px;
  right: 8px;
  height: calc(100% - 16px);
  width: auto;
  display: block;
  object-fit: contain;
  opacity: .6;
  pointer-events: none;
}

.eyebrow {
  color: var(--brand);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
}

h1 { margin: 0; font-size: clamp(32px, 6vw, 50px); line-height: 1; }
.hero h1 { font-weight: 400; }
.hero p { margin: 0; }
.location { margin: 12px 0 0; color: rgba(255, 255, 255, .78); }
.hero .sub { font-family: "Great Vibes", cursive; font-size: clamp(18px, 4vw, 26px); margin-top: -5px; color: var(--pink); }

.card {
  margin-top: 18px;
  padding: 24px;
  border: 1px solid var(--line);
  border-radius: 20px;
  background: var(--surface);
}

.card h2 { margin: 0 0 18px; font-size: 20px; }

/* Google Maps embed — fixed-size iframe made responsive via the classic
   padding-hack aspect-ratio box, so it scales to the card width on mobile
   instead of overflowing at its original 600x450 intrinsic size. */
.map-embed {
  position: relative;
  width: 100%;
  padding-top: 75%;
  border-radius: 12px;
  overflow: hidden;
}

.map-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.card-header {
  display: flex;
  align-items: center;
  gap: 28px;
}
.card-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: 1;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  text-align: left;
  color: inherit;
}
/* Used where a sibling control (e.g. the queue's Refresh button) needs to
   sit right after the title instead of being pushed to the far right of
   the card — hugs its own content instead of stretching to fill the row. */
.card-toggle--compact { flex: none; justify-content: flex-start; }
.card-toggle h2 { margin: 0; }
.refresh-button { flex: none; padding: 8px 14px; }
.card-header:has(.card-toggle[aria-expanded="true"]) + .card-body { margin-top: 18px; }

label { display: block; margin-bottom: 8px; font-weight: 500; }

input:not([type="checkbox"]), select, textarea {
  width: 100%;
  min-width: 0;
  padding: 12px;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
}

/* Safari's native date-picker control has its own internal minimum content
   width that scales with font-size and does not shrink below it even when
   width:100% is set on a narrow container — the fixed, slightly smaller
   font-size (rather than inherit) keeps that minimum under a mobile card's
   available width. Confirmed to overflow both the standalone customer
   date field (not a grid item) and the admin walk-in date field, so this
   is a WebKit control quirk, not a grid/flex sizing bug.
   For input[type="date"] with a `min` attribute set specifically (see
   special-date/promo-start-date/promo-end-date/walkin-date/booking-date's
   JS), font-size alone isn't enough — on-device inspection confirmed it
   renders at a fixed ~372px regardless of which container/display type
   it's in (CSS Grid, flex row, flex column — width:100% is simply never
   honored for this exact input+attribute combination). max-width is a
   separate, later clamp in the box model that overrides even a stubborn
   preferred/intrinsic size, unlike width, so it still constrains this. */
input[type="date"], input[type="time"], input[type="month"] {
  font-size: 15px;
  padding: 12px 8px;
  max-width: 100%;
}

/* Houses the "Date" label plus the refresh-availability icon button — a
   visible gap (not just adjacent) so the refresh action doesn't read as
   part of the label text itself. */
.date-label-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 6px;
}
.date-label-row label { margin: 0; }

.date-nav {
  display: flex;
  align-items: center;
  gap: 8px;
}
.date-nav input[type="date"],
.date-nav input[type="month"],
.date-nav select { flex: 1; min-width: 0; }
.date-nav button {
  flex: none;
  padding: 12px 14px;
}

.slots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(145px, 1fr));
  gap: 10px;
  margin-top: 18px;
}

.slot {
  padding: 13px 10px;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
  cursor: pointer;
  text-align: left;
}

.slot:hover { border-color: var(--brand); }
.slot.selected { color: var(--pink); border-color: var(--brand); background: var(--brand-dark); }
.slot:disabled { color: #9aa49f; background: #f4f5f4; cursor: not-allowed; }
.slot strong, .slot span { display: block; }
.slot span { margin-top: 4px; color: var(--muted); font-size: 13px; }
.slot.selected span { color: var(--pink); opacity: .85; }

.summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

.booking-form {
  margin-top: 24px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}

.booking-form h2 { margin-bottom: 16px; }

.field-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  margin-bottom: 14px;
}

.field { min-width: 0; }
.field.full { grid-column: 1 / -1; }
.field-note { margin: 4px 0 0; font-size: 12px; }

/* Flexbox equivalent of pairing two .field-grid columns side by side —
   used specifically for a pair of native date inputs with a `min`
   attribute set (see special-date/promo-start-date/promo-end-date's JS).
   Real iPhone Safari/Chrome (confirmed via on-device Web Inspector, not
   reproducible in desktop DevTools emulation) renders such an input at
   its own intrinsic width instead of its actual CSS Grid track width —
   type="time" inputs with no min attribute don't show this, so it's
   specific to that combination, not native date/time inputs generally.
   .date-nav's own min-bearing date field (booking/walk-in date, also
   flexbox) is unaffected, so flex — not grid — is the safe container
   for these. */
.field-pair {
  display: flex;
  gap: 14px;
  margin-bottom: 14px;
}
.field-pair .field { flex: 1; min-width: 0; }

/* Admin-only custom-price fields on the walk-in form — a distinct tint
   (matching .override-button's purple) so it visually reads as a separate,
   deliberate override rather than blending into the rest of the form. */
.override-fields-group {
  display: flex;
  flex-direction: column;
  gap: 12px;
  background: #F1EEFB;
  border: 1px solid #DDD3F5;
  border-radius: 14px;
  padding: 14px;
}
.override-fields-group .field { margin: 0; }
.override-fields-group .contact-hint { margin: 0; }

.date-label-row:has(.walkin-date-label) { margin-top: 20px; }

/* Court rates: a 7-day-by-N-hour table. Wide by design (8 columns), so it
   scrolls inside its own container on mobile rather than the whole page —
   the outer card never overflows. */
.rate-table-scroll {
  margin-top: 16px;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: 12px;
}

.rate-table {
  border-collapse: collapse;
  width: 100%;
}

.rate-table th, .rate-table td {
  padding: 4px;
  text-align: center;
  border-bottom: 1px solid var(--line);
}

.rate-table th {
  background: var(--tint);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .02em;
  white-space: nowrap;
}

.rate-table th:first-child, .rate-table td:first-child {
  position: sticky;
  left: 0;
  background: var(--surface);
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
  white-space: nowrap;
  border-right: 1px solid var(--line);
}

.rate-table th:first-child { background: var(--tint); z-index: 1; }

.rate-table input[type="number"] {
  width: 64px;
  min-width: 64px;
  padding: 6px 4px;
  text-align: center;
  border-radius: 8px;
}

/* Hours a given day isn't open (per Weekly Hours) — cell stays visible so
   the table shape never changes between days, just non-interactive and
   visually muted. */
.rate-table input[type="number"]:disabled {
  background: var(--tint);
  color: var(--muted);
  border-color: transparent;
}

.rate-table-actions {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-top: 14px;
  flex-wrap: wrap;
}

/* Weekly hours: one row per day, no horizontal scroll needed (4 columns),
   but wrapped the same way for consistency and mobile safety. */
.hours-table-scroll {
  margin-top: 16px;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: 12px;
}

.hours-table {
  border-collapse: collapse;
  width: 100%;
}

.hours-table th, .hours-table td {
  padding: 8px 4px;
  text-align: center;
  border-bottom: 1px solid var(--line);
}

.hours-table th {
  background: var(--tint);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .02em;
  white-space: nowrap;
}

.hours-table td:first-child {
  text-align: left;
  font-weight: 700;
  white-space: nowrap;
}

.hours-table input[type="time"] {
  width: 88px;
  min-width: 0;
  padding: 8px 4px;
}

.hours-table input[type="time"]:disabled {
  background: var(--tint);
  color: var(--muted);
  border-color: transparent;
}

/* Generic read-only listing table (User management: admins, employees).
   One row per record with action buttons in the last column. Wrapped in a
   scroll container the same way the rate/hours tables are, for mobile. */
.data-table-scroll {
  margin: 12px 0 6px;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: 12px;
}

.data-table {
  border-collapse: collapse;
  width: 100%;
  font-size: 13px;
}

.data-table th,
.data-table td {
  padding: 8px 12px;
  text-align: left;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

.data-table th {
  background: var(--tint);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .02em;
}

.data-table tbody tr:last-child td { border-bottom: 0; }

.data-table td.muted-cell { color: var(--muted); }

.data-table td.data-table-actions { text-align: right; }
.data-table td.data-table-actions button { margin-left: 6px; }

.data-table .data-table-empty {
  text-align: center;
  color: var(--muted);
  padding: 14px 12px;
}

.data-table .access-password {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 13px;
  background: var(--tint);
  padding: 2px 6px;
  border-radius: 6px;
}

.data-table .access-password + .copy-icon-button {
  vertical-align: middle;
  margin-left: 6px;
}

.checkbox-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 18px 0;
  font-weight: 500;
}

.checkbox-row input { margin-top: 3px; }

.primary-button, .secondary-button, .danger-button, .override-button {
  padding: 8px 12px;
  border: 0;
  border-radius: 12px;
  font-weight: 800;
  font-size: 12.5px;
  cursor: pointer;
  font-family: "Fredoka", "Segoe UI", sans-serif;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.primary-button { color: #fff; background: var(--brand); }
.primary-button:hover:not(:disabled) { background: var(--brand-dark); }
.secondary-button { color: var(--brand-dark); background: var(--tint); font-weight: 600; }
.danger-button { color: #fff; background: var(--danger); }
/* Deliberately its own color (neither danger's red nor brand orange) — a
   manual status override bypasses every normal workflow guard, so it
   should visually stand apart from both routine actions and destructive
   ones. */
.override-button { color: #fff; background: #7c3aed; }
.override-button:hover:not(:disabled) { background: #6d28d9; }
button:disabled { opacity: .55; cursor: not-allowed; }

.back-button { margin-bottom: 18px; padding: 8px 14px; }
#submit-status { min-height: 22px; margin-bottom: 0; }

a { color: var(--brand); }

.stack { display: grid; gap: 14px; }
.actions { display: flex; flex-wrap: wrap; gap: 8px; }
.booking-item { padding: 16px; border: 1px solid var(--line); border-radius: 14px; }
.booking-item h3 { margin: 0 0 8px; }
.booking-item p { margin: 5px 0; }
.booking-item-details { margin-bottom: 10px; }
.booking-item-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.copy-icon-button {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--brand);
  cursor: pointer;
}
.copy-icon-button:hover { background: var(--tint); }
.copy-icon-button--copied { color: #2E7D32; }

/* Link-styled toggle for the info block above it (ref/name/schedule) —
   deliberately not another pill button, and a different color (teal vs.
   the buttons' purple/red) so it reads as "reveal more info" rather than
   an action alongside Approve/Reject/Void. */
.booking-item .details-toggle {
  display: inline-block;
  margin: 0 0 12px;
  padding: 0;
  border: 0;
  background: none;
  color: #2C7A7B;
  font-size: 13px;
  font-weight: 700;
  text-decoration: underline;
  text-transform: none;
  letter-spacing: normal;
  font-family: inherit;
  cursor: pointer;
}
.booking-item .details-toggle:hover { color: #1F5B5C; }

/* Booking-ticket action row: wraps onto additional rows on wider screens
   once there are more buttons than fit on one line (e.g. View Payment
   Proof + Upload Additional Proof + Flag for Reschedule + Void Booking),
   rather than clipping/scrolling the row off the edge of the card;
   stacks to full-width vertical buttons on narrow/mobile screens where
   the compact labels get too cramped. */
.booking-item .actions {
  flex-wrap: wrap;
}
.booking-item .actions button {
  flex-shrink: 0;
}
@media (max-width: 640px) {
  .booking-item .actions {
    flex-direction: column;
    overflow-x: visible;
  }
  .booking-item .actions button {
    width: 100%;
    white-space: normal;
  }
}

/* Inline hour-checkbox picker for flagging part of a multi-hour booking
   for reschedule (see reschedulePicker in admin/index.html). */
.reschedule-picker {
  margin-top: 12px;
  padding: 12px;
  border: 1px dashed var(--line);
  border-radius: 10px;
  background: var(--canvas);
}
.reschedule-picker .checkbox-row { margin: 8px 0; font-weight: 400; }
.reschedule-picker input[type="text"] { margin-top: 8px; }
.activity-log { margin: 10px 0; padding: 8px 10px; border-radius: 10px; background: rgba(0, 0, 0, .03); }
.activity-log-entry { margin: 3px 0 !important; font-size: 13px; }
.badge {
  display: inline-block;
  padding: 3px 8px;
  border-radius: 999px;
  color: var(--brand-dark);
  background: var(--tint);
  font-size: 12px;
  font-weight: 800;
}
#calendar-open-link { margin-left: auto; font-size: 13px; font-weight: 700; text-decoration: none; }
.calendar-view-toggle, .income-group-toggle { display: flex; gap: 8px; margin-top: 14px; }
.calendar-view-toggle .view-toggle-active, .income-group-toggle .view-toggle-active { background: var(--brand); color: #fff; }
/* Pushed to the right edge of the toggle row and given its own color
   (teal, vs. the Month/Week/Day buttons' purple) — it's a separate export
   action, not another view mode. */
.icon-action-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  padding: 0;
  color: #fff;
  background: #2C7A7B;
}
.icon-action-button:hover:not(:disabled) { background: #1F5B5C; }
.calendar-view-toggle .calendar-share-button { margin-left: auto; }
/* Same teal export color as .calendar-share-button, but sized for its
   "3-Day" text label instead of the fixed 34px icon-only square. Sits
   right after the share icon, both riding that button's margin-left:auto
   to the right edge of the toggle row. */
.calendar-share-3day-button { width: auto; padding: 0 12px; font-size: 13px; font-weight: 700; white-space: nowrap; }
.income-report-download-button { width: 44px; height: 38px; box-sizing: border-box; }
/* Sits inside a .date-nav row (see #income-month-nav/#income-year-nav) —
   width comes from that row's flex:1 rule, same as every other date/time
   field in the app, instead of a bespoke fixed pixel width. */
.income-month-input {
  display: block;
  margin: 10px 0 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  font-family: inherit;
  color: var(--ink);
  box-sizing: border-box;
}
.income-year-select { padding: 12px 8px; }
.calendar-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 16px 0;
}
.calendar-nav button { padding: 6px 14px; }
.calendar-month-label { font-weight: 800; min-width: 160px; text-align: center; }
input.calendar-month-label {
  border: 0;
  background: none;
  padding: 4px;
  cursor: pointer;
  color: inherit;
}
.calendar-weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  margin-bottom: 4px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-align: center;
  color: var(--muted);
}
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.calendar-day {
  /* <button> elements center their content by default in most browsers'
     UA stylesheets — override explicitly so the day number always lands
     top-left, not vertically centered in the cell. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  min-width: 0;
  min-height: 62px;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--canvas);
  font: inherit;
  color: inherit;
  text-align: left;
}
.calendar-day--clickable { cursor: pointer; }
.calendar-day--clickable:hover { border-color: var(--brand); }
.calendar-day--outside { visibility: hidden; }
/* Today: a thin pink ring on the normal canvas background — a marker, not
   a selection state. Selected (the active date filter): a solid brand-fill
   with white text — visually louder so the two never read as the same
   thing, even when a day is both today and selected at once (selected's
   fill wins since it's declared after and sets background+text color). */
.calendar-day--today { border-color: var(--brand); border-width: 2px; }
.calendar-day--selected {
  background: var(--brand);
  border-color: var(--brand-dark);
  border-width: 2px;
}
.calendar-day--selected .calendar-day-number { color: #fff; }
.calendar-day-number { font-size: 13px; font-weight: 700; }
.calendar-day-dots { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.calendar-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.calendar-hint { margin: 6px 0 0; font-size: 13px; }

/* Week view — only 7 cells on screen, so each gets real room for a short
   time-ordered list instead of month view's compact status dots. */
.calendar-grid--week { grid-template-columns: repeat(7, 1fr); }
.calendar-day--week { min-height: 160px; }
.calendar-week-bookings {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 8px;
  width: 100%;
  /* Flex items default to min-width: auto, so this list's un-wrapped chip
     text (white-space: nowrap below) would otherwise set a floor wider
     than the 1fr grid track on narrow screens, ballooning every week-view
     column and forcing the whole page to scroll horizontally. min-width: 0
     lets it actually shrink so the chip's own overflow/ellipsis can kick
     in instead. */
  min-width: 0;
}
.calendar-week-chip {
  padding: 3px 6px;
  border-left: 3px solid transparent;
  border-radius: 4px;
  background: var(--surface);
  color: var(--ink);
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.calendar-day--selected .calendar-week-chip { color: var(--ink); }

/* Day view — a full vertical schedule for one court/day, built from the
   same slot granularity as the customer booking page and the walk-in
   form, so admins can see vacant slots at a glance. */
.calendar-day-slots { display: flex; flex-direction: column; gap: 6px; }
.day-slot-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--canvas);
}
.day-slot-row--vacant { background: var(--surface); }
.day-slot-time { flex: none; width: 96px; font-weight: 800; font-size: 13px; }
.day-slot-label { font-size: 13px; }
.day-block-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; flex-wrap: wrap; }
#day-block-hint, #day-block-status { font-size: 13px; }
#day-block-toggle.view-toggle-active { background: var(--brand); color: #fff; }
.day-slot-row--selectable { cursor: pointer; }
.day-slot-row--selectable:hover { outline: 2px solid var(--brand-dark); outline-offset: -2px; }
.day-slot-row--block-selected { outline: 2px solid var(--brand-dark); outline-offset: -2px; }
.day-slot-row--blocked {
  cursor: pointer;
  background: repeating-linear-gradient(
    135deg,
    rgba(100, 116, 139, 0.16),
    rgba(100, 116, 139, 0.16) 8px,
    rgba(100, 116, 139, 0.28) 8px,
    rgba(100, 116, 139, 0.28) 16px
  );
  border-left: 4px solid #64748B;
}
.calendar-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-top: 18px;
  font-size: 13px;
  color: var(--muted);
}
.queue-filter-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
}
.queue-filter-row label, .queue-filter-row > span { margin: 0; font-weight: 700; font-size: 13px; }
.queue-filter-row select { width: auto; }
.status-multiselect { position: relative; }
.status-filter-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 20;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 10px 14px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 200px;
}
.status-filter-option { display: flex; align-items: center; gap: 8px; margin: 0; font-weight: 500; font-size: 14px; text-transform: none; }
.calendar-legend-item { display: flex; align-items: center; gap: 6px; }
.queue-subheading {
  margin: 22px 0 16px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  font-size: 16px;
}

/* Bookings heading block: title + total-collected on one row, search +
   status filter on the row below (search/filter used to share the title's
   row; moved down to make room for the total). */
.bookings-heading-block {
  margin: 22px 0 16px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.bookings-heading-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
}
.bookings-heading-row .bookings-heading {
  margin: 0;
  font-size: 16px;
}
.bookings-total-amount {
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
}
.bookings-heading-controls {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 12px;
}
.booking-search-input {
  width: 180px;
  max-width: 100%;
  padding: 8px 12px;
  font-size: 12.5px;
  border-radius: 12px;
}
/* Compact label + native date input pair, sized to its own content
   (not a fixed width like the search box) so it sits naturally beside
   the status filter instead of forcing a wrap. */
.booking-date-filter-group {
  display: flex;
  align-items: center;
  gap: 6px;
}
.booking-date-filter-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}
.booking-date-filter {
  padding: 8px 10px;
  font-size: 12.5px;
  border-radius: 12px;
}

@media (max-width: 600px) {
  .calendar-day { min-height: 44px; }
  .calendar-day--week { min-height: 100px; }
  .calendar-week-chip { font-size: 10px; }
}

.qr-code {
  display: block;
  width: 100%;
  max-width: 240px;
  border: 1px solid var(--line);
  border-radius: 16px;
}

.qr-code-button {
  display: block;
  margin: 4px auto 12px;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}

.qr-actions {
  display: flex;
  justify-content: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}

.payment-instructions { margin: 0 0 18px; color: var(--muted); }

#feedback-block {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

.feedback-intro { margin: 0 0 14px; }

#feedback-form {
  display: block;
  width: 100%;
  min-height: 500px;
  border: none;
}

.qr-lightbox {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(74, 32, 128, .82);
}

.qr-lightbox img {
  max-width: min(92vw, 480px);
  max-height: 85vh;
  border-radius: 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
}

.qr-lightbox-close {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  color: #fff;
  background: rgba(255, 255, 255, .18);
  font-size: 18px;
  cursor: pointer;
}

#account-card { margin: 14px 0 6px; }

.signin-row {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 12px;
  margin-left: 20px;
  margin-bottom: -12px;
}

.signin-row .error { color: var(--danger); font-weight: 600; }

/* Fixed hamburger + right-side drawer (admin page) — replaces the old
   always-in-the-header Sign out button, since the queue/calendar/income/
   rates sections can push it well below the fold. Stays anchored to the
   viewport (not the page) so it's reachable from anywhere on a long
   scroll.

   Two looks, swapped by JS (see updateMenuToggleTone) depending on
   what's actually behind the fixed button right now, not a fixed
   viewport size: over the hero, a translucent white button with dark
   bars — low-key, lets the banner show through. Scrolled down far enough
   that a plain section card is behind it instead (.over-section), a
   solid brand-purple button with white bars instead, since translucent
   white barely shows up against a white card. */
.menu-toggle {
  position: fixed;
  top: 18px;
  right: 18px;
  z-index: 90;
  width: 44px;
  height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border: 1px solid rgba(218, 214, 232, .55);
  border-radius: 12px;
  background: rgba(255, 255, 255, .55);
  box-shadow: 0 6px 14px rgba(74, 32, 128, .1);
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.menu-toggle:hover,
.menu-toggle:active,
.menu-toggle:focus-visible {
  background: var(--surface);
  border-color: var(--line);
  box-shadow: 0 6px 18px rgba(74, 32, 128, .16);
}
.menu-toggle-bar {
  width: 20px;
  height: 2px;
  border-radius: 2px;
  background: #4A2080;
  transition: background .15s ease;
}

.menu-toggle.over-section {
  border-color: transparent;
  background: #4A2080;
  box-shadow: 0 6px 18px rgba(74, 32, 128, .35);
}
.menu-toggle.over-section:hover,
.menu-toggle.over-section:active,
.menu-toggle.over-section:focus-visible { background: #5C2A9E; }
.menu-toggle.over-section .menu-toggle-bar { background: #fff; }

.menu-overlay {
  position: fixed;
  inset: 0;
  background: rgba(30, 16, 56, .45);
  z-index: 210;
  opacity: 0;
  transition: opacity .28s ease;
}
.menu-overlay.open { opacity: 1; }

/* Slides in from the right (translateX 100% -> 0), i.e. opens leftward
   into view — .open is added a frame after `hidden` is removed so the
   browser commits the off-screen starting position first. */
.menu-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 211;
  width: min(320px, 84vw);
  display: flex;
  flex-direction: column;
  padding: 22px;
  background: var(--surface);
  box-shadow: -14px 0 36px rgba(30, 16, 56, .2);
  transform: translateX(100%);
  transition: transform .28s cubic-bezier(.22, .8, .32, 1);
}
.menu-drawer.open { transform: translateX(0); }

.menu-drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.menu-drawer-header h2 { margin: 0; font-size: 17px; }
.menu-close {
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 10px;
  background: var(--tint);
  color: var(--brand-dark);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}

/* One section (Calendar by default) shows at a time — see showSection()
   — with the rest reachable via this nav instead of scrolling past every
   card. Scrolls independently if the list ever outgrows the drawer. */
.menu-nav {
  display: flex;
  flex-direction: column;
  gap: 4px;
  overflow-y: auto;
  min-height: 0;
}
.menu-nav-item {
  text-align: left;
  padding: 11px 12px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--ink);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 600;
  cursor: pointer;
}
.menu-nav-item:hover { background: var(--canvas); }
.menu-nav-item.active { background: var(--tint); color: var(--brand-dark); }

/* Pinned to the bottom of the drawer regardless of how little the nav
   list above it takes up. */
.menu-drawer-footer {
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.menu-signed-in-as { font-size: 13.5px; word-break: break-word; }

/* Desktop: the drawer becomes a permanent right-hand sidebar instead of a
   toggled overlay — no hamburger/backdrop needed, and there's enough
   width to give it its own column rather than covering content. .shell's
   own centering (width: min(760px, calc(100% - 32px)); margin: 0 auto)
   is untouched — reserving the sidebar's width as body padding just
   shrinks the box that centering happens within, on this page only. */
@media (min-width: 901px) {
  .admin-page { padding-right: 320px; }
  .admin-page .menu-toggle,
  .admin-page .menu-overlay { display: none; }
  .admin-page .menu-drawer {
    box-shadow: none;
    border-left: 1px solid var(--line);
  }
  .admin-page .menu-close { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .menu-overlay, .menu-drawer { transition: none; }
}

@media (max-width: 600px) {
  .shell { width: min(100% - 20px, 760px); padding-top: 12px; }
  .hero, .card { padding: 18px; border-radius: 16px; }
  .field-grid { grid-template-columns: minmax(0, 1fr); }
  .field.full { grid-column: auto; }
  /* .field-pair never had .field-grid's own single-column collapse — it's
     a flex row (see the CSS Grid+min-attribute bug it works around), so
     without this it stayed 2-up even below this breakpoint while every
     .field-grid sibling correctly stacked to one column, producing a
     narrower pair sitting next to full-width fields. */
  .field-pair { flex-direction: column; }
  .summary { align-items: flex-end; }
}

.total { font-size: 26px; font-weight: 800; }

/* Income tab summary — small stat tiles instead of one run-on sentence
   (a single "₱X total (N custom-price bookings, −₱Y discounted) · ₱Z
   collected (incl. ₱W forfeited)" line wraps into an unreadable wall of
   text on phone widths). Mirrors the same tile look already used in the
   downloadable PDF report's `.totals` div (same --tint background,
   --brand-dark value color) so the on-screen and exported numbers read as
   one consistent design instead of two different treatments. */
.income-summary { display: flex; flex-wrap: wrap; gap: 10px; margin: 4px 0 16px; }
.income-stat { background: var(--tint); border-radius: 10px; padding: 10px 14px; min-width: 110px; }
.income-stat span { display: block; font-size: 12px; color: var(--muted); margin-bottom: 2px; }
.income-stat strong { display: block; font-size: 20px; color: var(--brand-dark); }
.income-stat-primary strong { font-size: 26px; }

.muted, .status { color: var(--muted); }
.error { color: var(--danger); }

.loading {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 64px;
  color: var(--muted);
}

.spinner {
  width: 20px;
  height: 20px;
  border: 3px solid var(--line);
  border-top-color: var(--brand);
  border-radius: 50%;
  animation: spin .8s linear infinite;
}

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

/* Fills the gap between sign-in resolving and #admin-content becoming
   visible (the first getAdminDashboard round-trip) so the page doesn't
   look hung with nothing rendered on it. */
.admin-page-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 48px 0;
  color: var(--muted);
  font-size: 14px;
}

/* Loading overlay with bouncing ball + trail — shown during network calls
   that block the current step (submitting a booking, etc). */
.loading-overlay {
  position: fixed;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(122, 116, 152, .45);
  backdrop-filter: blur(2px);
  z-index: 50;
}

.loading-overlay.active { display: flex; }

/* Scoped loading overlay — covers only the section it's positioned
   inside (e.g. the admin Bookings list) rather than the whole viewport,
   so the rest of the page (calendar, filters) stays interactive/visible
   while a queue action (approve/reject/etc) is in flight. */
.booking-list-wrap { position: relative; }

/* Caps the Bookings list to roughly 5 tickets tall so a busy queue doesn't
   push the rest of the admin page (Weekly Hours, Court Rates, etc.) far
   down the screen — scroll within the list itself instead. */
#booking-list {
  max-height: 820px;
  overflow-y: auto;
  padding-right: 4px;
}

.section-loading-overlay {
  position: absolute;
  inset: 0;
  display: none;
  align-items: flex-start;
  justify-content: center;
  padding-top: 32px;
  background: rgba(255, 255, 255, .7);
  border-radius: 12px;
  z-index: 5;
}

.section-loading-overlay:not([hidden]) { display: flex; }

.ball-loader {
  position: relative;
  width: 56px;
  height: 80px;
}

.ball-loader .ball {
  position: absolute;
  top: 0;
  left: 4px;
  width: 48px;
  height: 48px;
  animation: ball-bounce 1.2s cubic-bezier(.5, 0, 1, .6) infinite;
}

.ball-loader .ball-trail-3 { opacity: .12; animation-delay: -.24s; }
.ball-loader .ball-trail-2 { opacity: .28; animation-delay: -.16s; }
.ball-loader .ball-trail-1 { opacity: .5; animation-delay: -.08s; }
.ball-loader .ball-main { opacity: 1; animation-delay: 0s; }

@keyframes ball-bounce {
  0% { transform: translateY(0) scale(1, 1); }
  12% { transform: translateY(0) scale(1.15, .82); }
  40% { transform: translateY(-38px) scale(1, 1); }
  68% { transform: translateY(0) scale(1.18, .8); }
  100% { transform: translateY(0) scale(1, 1); }
}

/* App-wide modal — replaces native alert()/confirm()/prompt() so dialogs
   match the site's look instead of the browser's default chrome. Built and
   torn down on demand by the dialog.js helper (window.geeDialog); markup
   for a single instance is injected once and reused for every call. */
.gee-modal-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(74, 32, 128, .45);
  backdrop-filter: blur(2px);
  z-index: 200;
}

.gee-modal {
  width: min(420px, 100%);
  padding: 22px;
  border-radius: 20px;
  background: var(--surface);
  box-shadow: 0 24px 60px rgba(74, 32, 128, .28);
}

.gee-modal-message {
  margin: 0 0 16px;
  color: var(--ink);
  font-size: 15px;
  line-height: 1.45;
  white-space: pre-line;
}

.gee-modal input[type="text"],
.gee-modal select,
.gee-modal textarea {
  display: block;
  width: 100%;
  margin-bottom: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  font-family: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--canvas);
}

.gee-modal label { margin-top: 4px; }

.gee-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

/* Payment-proof gallery/upload modal (admin page) — wider than the default
   .gee-modal since it needs to fit a grid of images, not just a message. */
.proof-modal {
  width: min(720px, 100%);
}

/* On phones, let the proof viewer take over most of the screen instead of
   sitting in a small centered card — the whole point of viewing/swiping a
   payment-proof photo is to actually see it. Targets the overlay (not
   just .proof-modal) via :has() so the surrounding inset also shrinks,
   same pattern already used elsewhere in this stylesheet. */
@media (max-width: 600px) {
  .gee-modal-overlay:has(.proof-modal) {
    padding: 8px;
  }
  .proof-modal {
    width: 100%;
    padding: 14px;
  }
  .proof-viewer-viewport {
    height: calc(100dvh - 210px);
  }
}

.proof-modal input[type="text"],
.proof-modal input[type="file"] {
  display: block;
  width: 100%;
  margin-bottom: 12px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  font-family: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--canvas);
}

/* Payment-proof viewer: always shows one enlarged image, with a top-center
   "n/total" counter and prev/next arrows (plus touch swipe) when there's
   more than one — see openProofGalleryModal for the JS. */
.proof-viewer {
  margin-bottom: 16px;
}

.proof-viewer-counter {
  text-align: center;
  font-weight: 600;
  color: var(--muted);
  margin: 0 0 8px;
}

/* prev-arrow / sliding-viewport / next-arrow, side by side — the arrows
   are flex siblings of the viewport, not overlaid on top of the image. */
.proof-viewer-stage {
  display: flex;
  align-items: center;
  gap: 8px;
}

.proof-viewer-viewport {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  height: 70vh;
  border-radius: 10px;
  border: 1px solid var(--line);
}

/* The track holds every proof side by side; openProofGalleryModal slides
   it with a transform so switching images is a smooth animated pan
   instead of an instant swap. */
.proof-viewer-track {
  display: flex;
  height: 100%;
  will-change: transform;
}

/* Each slide is a fixed-height box the size of the viewport, with the
   image centered inside it — so a shorter/narrower image sits centered
   instead of pinned to the top the way plain block flow would leave it. */
.proof-viewer-slide {
  flex: 0 0 100%;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.proof-viewer-slide img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
  cursor: zoom-in;
}

.proof-viewer-nav {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--canvas);
  color: var(--ink);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Full-screen single-image view (double-click/double-tap a proof) — a
   plain edge-to-edge overlay, not a Fullscreen-API element, since Safari
   on iOS doesn't support requestFullscreen() on arbitrary elements. No
   touch-action restriction on the image, so the browser's native
   pinch-zoom (already unblocked by this page's viewport meta tag) has
   the whole screen to zoom into. See openFullscreenProofImage.  */
.proof-fullscreen-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: rgba(10, 6, 20, .92);
  display: flex;
  align-items: center;
  justify-content: center;
}

.proof-fullscreen-overlay img {
  max-width: 100%;
  max-height: 100%;
}

.proof-fullscreen-close {
  position: fixed;
  top: 12px;
  right: 12px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, .15);
  color: #fff;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Income table's Overrides/Forfeited cells — click/tap (not hover-only, so
   it works the same on a phone as a desktop) to trace an amount back to
   the specific booking number(s) behind it. One shared floating panel
   (#income-ref-popover, built lazily) rather than a per-cell tooltip, so
   only one is ever open and it can hold several rows without the cell
   itself needing to grow. See renderIncomeTable/showIncomeRefPopover. */
.income-ref-cell { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px; }
.income-ref-cell:hover, .income-ref-cell:focus-visible { color: var(--brand-dark); }
.income-ref-popover {
  position: fixed;
  z-index: 600;
  max-width: 280px;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--canvas);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
  padding: 10px 12px;
  font-size: 13px;
}
.income-ref-popover-title { font-weight: 600; margin-bottom: 6px; color: var(--brand-dark); }
.income-ref-popover-row { padding: 5px 0; border-top: 1px solid var(--line); }
.income-ref-popover-row:first-of-type { border-top: none; }
.income-ref-popover-row-main { display: flex; justify-content: space-between; gap: 10px; }
.income-ref-popover-note { color: var(--muted); font-size: 12px; margin-top: 2px; }
