/* dvh fallback added */
/* =============================================================================
   FitnessFinder — SHARED COMPONENT LAYER
   -----------------------------------------------------------------------------
   Loaded LAST on every page, after style.css and any page stylesheet, so it can
   unify components that grew their own bespoke rules over time.

   The site had accumulated ~20 separately-named modal variants (.svc-modal,
   .event-modal, .qual-modal, .rate-modal, .cancel-modal, .pwd-modal …), each
   with its own radius, padding, close button and header. This file gives them
   one shape language without touching their markup.

   Deliberately NOT changed here:
     * the semantic header tints (cancel = amber, rate = amber, dispute = red)
       still carry their own meaning, so only their structure is normalised
     * image lightboxes and the mobile nav scrim, which are not dialogs
   ========================================================================== */

/* -----------------------------------------------------------------------------
   1. OVERLAYS
   One scrim treatment: a cool dark wash plus a blur, so the page behind reads
   as pushed back rather than merely dimmed.
   -------------------------------------------------------------------------- */
.modal-overlay,
.svc-modal-overlay,
.event-modal-overlay,
.qual-modal-overlay,
.qual-overlay,
.rate-modal-overlay,
.cancel-modal-overlay,
.reviews-modal-overlay,
.review-overlay,
.pwd-overlay,
.dispute-overlay,
.payment-overlay,
.confirm-overlay,
.msg-thread-overlay,
.drawer-overlay {
  background: rgba(16, 24, 40, 0.55);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  padding: 20px;
}

/* The scrim fades in; without this the blur snaps on and feels abrupt. */
.modal-overlay.active,
.svc-modal-overlay.active,
.event-modal-overlay.active,
.qual-modal-overlay.active,
.rate-modal-overlay.active,
.cancel-modal-overlay.active,
.reviews-modal-overlay.active,
.review-overlay.active,
.pwd-overlay.active,
.dispute-overlay.active,
.payment-overlay.active,
.confirm-overlay.active {
  animation: ff-scrim-in 0.18s var(--ff-ease);
}

@keyframes ff-scrim-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* -----------------------------------------------------------------------------
   2. THE SHEET
   Rounder and more softly raised — the Deliveroo card feel, at dialog scale.
   A brand hairline across the top edge gives every dialog the same tell.
   -------------------------------------------------------------------------- */
.modal,
.svc-modal,
.event-modal,
.qual-modal,
.rate-modal,
.cancel-modal,
.reviews-modal,
.review-modal,
.pwd-modal,
.dispute-modal,
.payment-modal,
.confirm-modal {
  border-radius: var(--ff-r-card-lg);
  box-shadow: 0 24px 64px rgba(16, 24, 40, 0.22), 0 2px 8px rgba(16, 24, 40, 0.08);
  overflow: hidden;
  border: none;
  position: relative;
  animation: ff-modal-in 0.24s var(--ff-ease);
}

/* The shared brand rule along the top edge. */
.modal::before,
.svc-modal::before,
.event-modal::before,
.qual-modal::before,
.reviews-modal::before,
.review-modal::before,
.pwd-modal::before,
.payment-modal::before,
.confirm-modal::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4px;
  background: linear-gradient(90deg, var(--ff-brand), var(--ff-brand-soft));
  z-index: 5;
  pointer-events: none;
}

@keyframes ff-modal-in {
  from { opacity: 0; transform: translateY(12px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

/* Dialogs with no scrolling body of their own. The shared sheet rule above sets
   overflow:hidden to clip the rounded corners, so on a short screen their content
   was either pushed off the top (no max-height) or silently clipped (max-height,
   nothing scrollable). Cap them to the viewport and let the panel itself scroll.
   Declared here, after the shared rule, because overflow:hidden would otherwise win.

   .svc-modal, .qual-modal and .modal are left alone: each has a real scrolling body
   (a flex child with min-height:0 and overflow-y:auto), so the panel itself must not
   scroll as well.

   .event-modal, .payment-modal and .review-modal were previously listed as having one
   too. They do not — .event-modal-body sets no overflow, and there is no rule at all for
   the other two — so they inherited overflow:hidden and silently clipped. On a phone that
   put the event booking total and its Book button below the cut with no way to reach them. */
.pwd-modal,
.dispute-modal,
.cancel-modal,
.rate-modal,
.event-modal,
.payment-modal,
.review-modal {
  max-height: 90vh;
  max-height: 90dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* -----------------------------------------------------------------------------
   3. HEADERS
   The generic dialogs get a soft brand-tint block — a light colour band rather
   than a bare white strip. Semantic headers (cancel/rate/dispute) keep their own
   background and only inherit the spacing and type.
   -------------------------------------------------------------------------- */
.modal-header,
.svc-modal-header,
.event-modal-header,
.qual-modal-header,
.reviews-modal-header,
.review-modal-header,
.pwd-modal-header,
.payment-modal-header,
.cancel-modal-header,
.rate-modal-header,
.dispute-modal-header {
  padding: 22px 26px 20px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-shrink: 0;
}

/* Colour band — generic dialogs only. */
.modal-header,
.svc-modal-header,
.event-modal-header,
.qual-modal-header,
.reviews-modal-header,
.review-modal-header,
.pwd-modal-header,
.payment-modal-header {
  background: var(--ff-brand-tint);
  border-bottom: 1px solid var(--ff-brand-a18);
}

/* Titles: the condensed display face, sized to sit calmly at dialog scale. */
.modal-header h2, .modal-header h3, .modal-header h4,
.svc-modal-header h2, .svc-modal-header h3, .svc-modal-title,
.event-modal-header h2, .event-modal-header h3,
.qual-modal-header h2, .qual-modal-header h3,
.reviews-modal-header h2, .reviews-modal-header h3, .reviews-modal-title,
.review-modal-header h2, .review-modal-header h3,
.pwd-modal-header h2, .pwd-modal-header h3,
.payment-modal-header h2, .payment-modal-header h3,
.cancel-modal-header h3, .rate-modal-header h3, .dispute-modal-header h3 {
  font-family: var(--ff-font-display);
  font-weight: var(--ff-display-fw);
  text-transform: uppercase;
  letter-spacing: var(--ff-display-ls);
  font-size: 1.15rem;
  line-height: 1.1;
  margin-bottom: 5px;
}

/* Generic dialog titles sit on the tint, so they take the ink colour. */
.modal-header h2, .modal-header h3, .modal-header h4,
.svc-modal-header h3, .svc-modal-title,
.event-modal-header h3,
.qual-modal-header h3,
.reviews-modal-header h3, .reviews-modal-title,
.review-modal-header h3,
.pwd-modal-header h3,
.payment-modal-header h3 {
  color: var(--ff-ink);
}

/* The supporting line under a dialog title becomes a mono micro-label. */
.svc-modal-sub,
.modal-header p,
.svc-modal-header p,
.event-modal-header p,
.qual-modal-header p,
.reviews-modal-header p,
.review-modal-header p,
.pwd-modal-header p,
.payment-modal-header p {
  font-family: var(--ff-font-mono);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ff-brand-deep);
}

/* -----------------------------------------------------------------------------
   4. CLOSE BUTTON
   One circular tile everywhere, instead of the mix of bare glyphs and dark
   translucent circles the pages had grown.
   -------------------------------------------------------------------------- */
.modal-close,
.svc-modal-close,
.event-modal-close,
.qual-modal-close,
.rate-modal-close,
.cancel-modal-close,
.reviews-modal-close,
.review-modal-close,
.pwd-modal-close {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--ff-surface);
  border: 1px solid var(--ff-line-field);
  color: var(--ff-muted-2);
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  transition: background var(--ff-t-fast), color var(--ff-t-fast),
              border-color var(--ff-t-fast), transform var(--ff-t-fast);
}

.modal-close:hover,
.svc-modal-close:hover,
.event-modal-close:hover,
.qual-modal-close:hover,
.rate-modal-close:hover,
.cancel-modal-close:hover,
.reviews-modal-close:hover,
.review-modal-close:hover,
.pwd-modal-close:hover {
  background: var(--ff-ink);
  border-color: var(--ff-ink);
  color: var(--ff-surface);
  transform: rotate(90deg);
}

/* -----------------------------------------------------------------------------
   5. FOOTERS
   A quiet tray at the bottom, clearly separated from the body, with the actions
   pushed right and rendered as pills.
   -------------------------------------------------------------------------- */
.modal-footer,
.svc-modal-footer,
.event-modal-footer,
.qual-modal-footer,
.rate-modal-footer,
.cancel-modal-footer,
.review-modal-footer,
.pwd-modal-footer,
.payment-modal-footer {
  padding: 16px 26px;
  background: var(--ff-surface-2);
  border-top: 1px solid var(--ff-line-soft);
  gap: 10px;
  flex-shrink: 0;
}

/* Every button inside a dialog footer becomes a pill with the same rhythm. */
.modal-footer button,
.svc-modal-footer button,
.event-modal-footer button,
.qual-modal-footer button,
.rate-modal-footer button,
.cancel-modal-footer button,
.review-modal-footer button,
.pwd-modal-footer button,
.payment-modal-footer button {
  border-radius: var(--ff-r-pill);
  font-family: var(--ff-font-body);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 11px 22px;
  cursor: pointer;
  transition: background var(--ff-t-fast), box-shadow var(--ff-t-fast),
              border-color var(--ff-t-fast), transform var(--ff-t-fast);
}

.modal-footer button:hover,
.svc-modal-footer button:hover,
.event-modal-footer button:hover,
.qual-modal-footer button:hover,
.rate-modal-footer button:hover,
.cancel-modal-footer button:hover,
.review-modal-footer button:hover,
.pwd-modal-footer button:hover,
.payment-modal-footer button:hover {
  transform: translateY(-1px);
}
.modal-footer button:active,
.svc-modal-footer button:active,
.event-modal-footer button:active,
.qual-modal-footer button:active,
.rate-modal-footer button:active,
.cancel-modal-footer button:active,
.review-modal-footer button:active,
.pwd-modal-footer button:active,
.payment-modal-footer button:active {
  transform: translateY(0);
}

/* -----------------------------------------------------------------------------
   6. SIDE DRAWER
   Not a centred dialog, so it keeps its own geometry — it only borrows the
   header band and the close-button tile.
   -------------------------------------------------------------------------- */
.drawer-header {
  background: var(--ff-brand-tint);
  border-bottom: 1px solid var(--ff-brand-a18);
}
.drawer-header h2, .drawer-header h3 {
  font-family: var(--ff-font-display);
  font-weight: var(--ff-display-fw);
  text-transform: uppercase;
  letter-spacing: var(--ff-display-ls);
  color: var(--ff-ink);
}
.drawer-close {
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--ff-surface);
  border: 1px solid var(--ff-line-field);
  color: var(--ff-muted-2);
  cursor: pointer;
  transition: background var(--ff-t-fast), color var(--ff-t-fast),
              border-color var(--ff-t-fast), transform var(--ff-t-fast);
}
.drawer-close:hover {
  background: var(--ff-ink);
  border-color: var(--ff-ink);
  color: var(--ff-surface);
  transform: rotate(90deg);
}

/* -----------------------------------------------------------------------------
   7. COLOUR BLOCK
   The Deliveroo device: a solid brand panel that interrupts a run of white
   cards. Used on the homepage; available to any page that wants one.
   -------------------------------------------------------------------------- */
.ff-band {
  display: flex;
  align-items: center;
  gap: 32px;
  flex-wrap: wrap;
  /* Depth rather than a flat fill, and deliberately deepest on the LEFT where
     the copy sits — flat --ff-brand behind white text is only ~2.1:1. Every
     stop is an existing palette value; nothing has been recoloured. */
  background:
    repeating-linear-gradient(
      115deg,
      rgba(255,255,255,0.05) 0 2px,
      transparent 2px 14px
    ),
    linear-gradient(115deg, var(--ff-brand-deep) 0%, var(--ff-brand) 52%, #00d9ff 100%);
  color: var(--ff-on-brand);
  border-radius: var(--ff-r-card-lg);
  padding: 42px 44px;
  position: relative;
  overflow: hidden;
  box-shadow: 0 18px 44px rgba(0, 153, 187, 0.30);
}
/* Inner top highlight, so the block reads as a raised surface. */
.ff-band::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.35);
  pointer-events: none;
}
.ff-band::after {
  content: '';
  position: absolute;
  top: -55%;
  right: -6%;
  width: 340px;
  height: 340px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255,255,255,0.26), transparent 70%);
  pointer-events: none;
}

/* Oversized watermark glyph bleeding off the trailing edge. Drawn with the
   Font Awesome font directly, the same trick the header auth pills use, so it
   needs no extra markup. */
.ff-band-mark {
  position: absolute;
  right: -18px;
  bottom: -46px;
  font-family: 'Font Awesome 6 Free';
  font-weight: 900;
  font-style: normal;
  font-size: 12rem;
  line-height: 1;
  color: rgba(255,255,255,0.13);
  transform: rotate(-18deg);
  pointer-events: none;
  user-select: none;
}
.ff-band-mark::before { content: '\f44b'; }

.ff-band-text { flex: 1 1 340px; min-width: 0; position: relative; z-index: 1; }

.ff-band-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--ff-font-mono);
  font-size: var(--ff-label-fs);
  font-weight: 600;
  letter-spacing: var(--ff-label-ls);
  text-transform: uppercase;
  color: var(--ff-on-brand);
  background: rgba(255,255,255,0.18);
  border: 1px solid rgba(255,255,255,0.30);
  padding: 6px 14px;
  border-radius: var(--ff-r-pill);
  margin-bottom: 16px;
}
.ff-band-eyebrow i { font-size: 0.72rem; }

.ff-band h2 {
  font-family: var(--ff-font-display);
  font-weight: var(--ff-display-fw);
  text-transform: uppercase;
  letter-spacing: var(--ff-display-ls);
  font-size: clamp(1.7rem, 3.6vw, 2.4rem);
  line-height: 1.02;
  color: var(--ff-on-brand);
  text-shadow: 0 1px 12px rgba(0, 92, 115, 0.22);
}
.ff-band p {
  margin-top: 12px;
  font-size: 1rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.94);
  max-width: 46ch;
}

/* The two standing promises, pulled out of the paragraph so they scan. */
.ff-band-points {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 20px 0 0;
  padding: 0;
}
.ff-band-points li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 15px;
  border-radius: var(--ff-r-pill);
  background: rgba(255,255,255,0.16);
  border: 1px solid rgba(255,255,255,0.26);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ff-on-brand);
  white-space: nowrap;
}
.ff-band-points i { font-size: 0.7rem; }

.ff-band-cta {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  /* White on cyan reads brighter than the old near-black pill, and puts the
     label on a 15:1 ground instead of a 2.1:1 one. */
  background: var(--ff-surface);
  color: var(--ff-ink);
  text-decoration: none;
  padding: 17px 32px;
  border-radius: var(--ff-r-pill);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  box-shadow: 0 8px 22px rgba(0, 60, 80, 0.22);
  transition: transform var(--ff-t-fast), box-shadow var(--ff-t-fast);
}
.ff-band-cta i {
  color: var(--ff-brand-deep);
  transition: transform var(--ff-t-fast);
}
.ff-band-cta:hover {
  transform: translateY(-3px);
  box-shadow: 0 16px 34px rgba(0, 60, 80, 0.30);
}
.ff-band-cta:hover i { transform: translateX(4px); }
.ff-band-cta:active { transform: translateY(-1px); }

@media (max-width: 900px) {
  .ff-band { padding: 34px 32px; }
  .ff-band-mark { font-size: 9rem; bottom: -34px; right: -22px; }
}

@media (max-width: 640px) {
  .ff-band { padding: 28px 24px; gap: 22px; }
  .ff-band-cta { width: 100%; justify-content: center; }
  .ff-band-points li { font-size: 0.78rem; padding: 7px 13px; }
  /* No room for it beside the copy at this width. */
  .ff-band-mark { display: none; }
}

/* =============================================================================
   8. TABS
   Used where a page had several parallel sections stacked vertically — three
   near-identical blocks that the reader had to scroll past to compare. As tabs
   they occupy one screen and switch in place.

   Markup contract (see components.js):
     <div data-ff-tabs>
       <div role="tablist"> <button role="tab" aria-controls="panelId"> …
       <section role="tabpanel" id="panelId"> …
   ========================================================================== */
.ff-tablist {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  padding: 0 20px;
}

.ff-tab {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 12px 24px;
  border-radius: var(--ff-r-pill);
  border: 1.5px solid var(--ff-line);
  background: var(--ff-surface);
  color: var(--ff-muted);
  font-family: var(--ff-font-body);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--ff-t-fast), color var(--ff-t-fast),
              border-color var(--ff-t-fast), box-shadow var(--ff-t-fast),
              transform var(--ff-t-fast);
}
.ff-tab i { font-size: 0.82rem; color: var(--ff-muted-4); transition: color var(--ff-t-fast); }

.ff-tab:hover {
  border-color: var(--ff-brand-a40);
  color: var(--ff-brand-deep);
  transform: translateY(-1px);
}
.ff-tab:hover i { color: var(--ff-brand); }

.ff-tab[aria-selected="true"] {
  background: var(--ff-brand);
  border-color: var(--ff-brand);
  color: var(--ff-on-brand);
  box-shadow: 0 4px 16px var(--ff-brand-a28);
}
.ff-tab[aria-selected="true"] i { color: var(--ff-on-brand); }

/* Panels are toggled with the `hidden` attribute so the markup stays
   meaningful with CSS or JS unavailable — without JS every panel simply shows,
   which is the old stacked page rather than a broken one. */
.ff-tabpanel[hidden] { display: none; }

.ff-tabpanel { animation: ff-panel-in 0.26s var(--ff-ease); }

@keyframes ff-panel-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 560px) {
  .ff-tablist { gap: 8px; }
  .ff-tab { padding: 11px 16px; font-size: 0.72rem; }
}


/* ── Add/Edit service & event modal on a phone ─────────────────────────────
   dashboard.css defines a bottom-sheet for this modal under @media (max-width: 768px),
   but the .svc-modal-overlay rule above sets padding: 20px with no media query and loads
   after it — so the sheet never applied and the modal rendered as a floating desktop card
   on a phone. These rules live here, after that, so they hold.

   Measured at 375px before this: all 33 controls under 16px, ten under 40px tall, and the
   modal using 335px of the screen. */
@media (max-width: 700px) {
  .svc-modal-overlay {
    padding: 0;
    align-items: flex-end;
  }
  .svc-modal {
    width: 100%;
    max-width: 100%;
    border-radius: 20px 20px 0 0;
    max-height: 94vh;
    max-height: 94dvh;
  }

  /* iOS Safari zooms the page when a focused control is under 16px and does not zoom back
     out. The controls carry inline font sizes, so this has to outrank them. */
  .svc-modal input,
  .svc-modal select,
  .svc-modal textarea {
    font-size: 16px !important;
    min-height: 44px;
  }
  .svc-modal textarea { min-height: 88px; }

  /* Day / Month / Year sharing the width instead of three cramped boxes. */
  .svc-modal select[id$="Day"],
  .svc-modal select[id$="Month"],
  .svc-modal select[id$="Year"] {
    flex: 1 1 0;
    min-width: 0;
  }

  .svc-modal-footer { gap: 10px; }
  .svc-modal-footer button { min-height: 46px; }
}
