@import url('https://fonts.googleapis.com/css2?family=Big+Shoulders+Display:wght@600;700;800&family=Public+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@500;600&family=Chivo:wght@500;700;900&family=Work+Sans:wght@400;500;600&display=swap');

/* ---- Theming: "Alpenglow" ----
   Grounded in the one moment every campsite looks unmistakably like itself: dusk light
   over the treeline. Spruce/Ember are the same roles the old pine/amber filled (and are
   still what a site's own branding colours override in Settings — see applyBranding in
   booking.js), just recoloured off the cream/serif look that reads as a generic
   AI-design default. Dusk is new: the palette's old pine+amber pairing never had a
   second, cooler hue to lean on for secondary accents and chart states. */
:root, [data-theme="light"] {
  /* Without this, native controls (date-picker icon, select dropdown, number spinners,
     scrollbars) keep the browser's default light chrome no matter what data-theme says —
     on a form-heavy page like the booking flow, that reads as dark mode "not working"
     even though every themed colour above is switching correctly. */
  color-scheme: light;
  --pine: #123524;           /* Spruce — headings, brand */
  --pine-light: #1F5738;
  --dusk: #33407A;           /* new: secondary accent, links, "awaiting" states */
  --dusk-soft: #5762A0;
  --clay: #A6532E;           /* fourth hue, alongside pine/dusk/amber — "present now" states */
  --bark: #4B5142;
  --on-bark: #FAFBF6;
  --canvas: #EFF2EA;
  --canvas-light: #FAFBF6;
  --amber: #FF6B35;          /* Ember — the one accent: buttons, highlights */
  --ember: #B23A55;          /* berry — overdue/cancelled/errors, kept apart from the accent */
  /* Text colour used wherever --pine/--amber are a BACKGROUND (nav highlights, buttons,
     chart headers) rather than text on the page's own canvas. These start as sane
     defaults for the stock palette, but applyBrandColours()/applyBranding() in JS
     recompute them live from whatever --pine/--amber actually resolve to — including a
     site's own custom brand colour — so text on top of them stays readable regardless of
     how light or dark that colour turns out to be, in either theme. */
  --on-pine: #FAFBF6;
  --on-dusk: #FAFBF6;
  --on-amber: #201C16;
  --ink: #171B12;
  --line: #D7DCCC;
  --radius: 4px;

  --muted: #5B6154;          /* secondary text */
  --surface: #FAFBF6;        /* cards */
  --surface-alt: #E4E8DC;    /* hover, table stripes */
  --surface-sunken: #EFF2EA;
  --tint-good: #DCEEE2;      /* soft green background */
  --tint-warn: #F5E7CC;      /* soft amber background */
  --tint-bad: #F6DEE4;       /* soft berry background */
  --on-accent: #23140A;      /* text on the (light) amber accent */
  --shadow: rgba(23,27,18,0.14);

  /* Site map illustration */
  --map-grass: #86AD68;
  --map-grass-edge: #9EC080;
  --map-path: #F7F4EC;
  --map-building: #C79E6B;
  --map-roof: #B08652;
  --map-tree-dark: #123524;
  --map-tree-mid: #1F5738;
  --map-tree-light: #4B7A54;
  --map-label: #171B12;
  --map-label-halo: #ffffff;

  /* The header is one fixed moment (dusk over the treeline), not a themed surface — it
     keeps its own colours in light and dark, the way a photograph wouldn't recolour
     itself for dark mode. See header.topbar below. */
  --hero-top: #123524;
  --hero-mid: #33407A;
  --hero-bottom: #FF6B35;
  --hero-ridge: #0B2115;
  --hero-on: #FBF6EC;

  /* Built-in "campsite" watermark — a small tent/bell-tent/campervan line-art tile,
     layered OVER whatever background colour is actually showing (a custom pick, or the
     theme's own default) rather than being an alternative to it — see applyAdminBranding()
     in dashboard.js, which picks whichever of these two tone variants actually contrasts
     against that colour (checked via colourLuminance(), the same helper already used for
     header/page text-colour contrast). A flat single tone each, not a multi-colour
     illustration — a watermark needs to read clearly against ANY colour someone picks, not
     just the handful this theme happens to use elsewhere. Defined once here, not redefined
     for [data-theme="dark"] below, since the choice between them is driven by the actual
     resolved colour's lightness, not by which theme is active — the same reasoning as
     --hero-* above, just for a different kind of "doesn't change with theme". */
  --watermark-dark: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'170'%20height%3D'140'%20viewBox%3D'0%200%20170%20140'%3E%20%3C!--%20Ridge%2FA-frame%20tent%20--%3E%20%3Cg%20transform%3D'translate(10%2C78)'%20opacity%3D'0.24'%3E%20%3Cpath%20d%3D'M0%2C34%20L22%2C3%20L44%2C34%20Z'%20fill%3D'none'%20stroke%3D'%23171B12'%20stroke-width%3D'2'%20stroke-linejoin%3D'round'%2F%3E%20%3Cline%20x1%3D'22'%20y1%3D'3'%20x2%3D'22'%20y2%3D'34'%20stroke%3D'%23171B12'%20stroke-width%3D'1.4'%2F%3E%20%3Cpath%20d%3D'M17%2C34%20L22%2C21%20L27%2C34'%20fill%3D'none'%20stroke%3D'%23171B12'%20stroke-width%3D'1.4'%20stroke-linejoin%3D'round'%2F%3E%20%3C%2Fg%3E%20%3C!--%20Bell%20tent%20with%20flag%20--%3E%20%3Cg%20transform%3D'translate(95%2C58)'%20opacity%3D'0.24'%3E%20%3Cpath%20d%3D'M3%2C37%20Q3%2C9%2022%2C7%20Q41%2C9%2041%2C37%20Z'%20fill%3D'none'%20stroke%3D'%23171B12'%20stroke-width%3D'2'%20stroke-linejoin%3D'round'%2F%3E%20%3Cline%20x1%3D'22'%20y1%3D'7'%20x2%3D'22'%20y2%3D'0'%20stroke%3D'%23171B12'%20stroke-width%3D'1.4'%2F%3E%20%3Cpath%20d%3D'M22%2C0%20L29%2C2.5%20L22%2C5%20Z'%20fill%3D'%23171B12'%20stroke%3D'none'%2F%3E%20%3C%2Fg%3E%20%3C!--%20Campervan%20--%3E%20%3Cg%20transform%3D'translate(43%2C10)'%20opacity%3D'0.24'%3E%20%3Cpath%20d%3D'M1%2C30%20L1%2C15%20Q1%2C7%2011%2C7%20L37%2C7%20Q47%2C7%2047%2C17%20L47%2C30%20Z'%20fill%3D'none'%20stroke%3D'%23171B12'%20stroke-width%3D'2'%20stroke-linejoin%3D'round'%2F%3E%20%3Cline%20x1%3D'16'%20y1%3D'7'%20x2%3D'12'%20y2%3D'30'%20stroke%3D'%23171B12'%20stroke-width%3D'1.4'%2F%3E%20%3Crect%20x%3D'4'%20y%3D'12'%20width%3D'8'%20height%3D'8'%20rx%3D'1.5'%20fill%3D'none'%20stroke%3D'%23171B12'%20stroke-width%3D'1.3'%2F%3E%20%3Crect%20x%3D'19'%20y%3D'12'%20width%3D'24'%20height%3D'8'%20rx%3D'1.5'%20fill%3D'none'%20stroke%3D'%23171B12'%20stroke-width%3D'1.3'%2F%3E%20%3Ccircle%20cx%3D'13'%20cy%3D'30'%20r%3D'4.5'%20fill%3D'none'%20stroke%3D'%23171B12'%20stroke-width%3D'1.4'%2F%3E%20%3Ccircle%20cx%3D'37'%20cy%3D'30'%20r%3D'4.5'%20fill%3D'none'%20stroke%3D'%23171B12'%20stroke-width%3D'1.4'%2F%3E%20%3C%2Fg%3E%20%3C%2Fsvg%3E");
  --watermark-light: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'170'%20height%3D'140'%20viewBox%3D'0%200%20170%20140'%3E%20%3C!--%20Ridge%2FA-frame%20tent%20--%3E%20%3Cg%20transform%3D'translate(10%2C78)'%20opacity%3D'0.36'%3E%20%3Cpath%20d%3D'M0%2C34%20L22%2C3%20L44%2C34%20Z'%20fill%3D'none'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'2'%20stroke-linejoin%3D'round'%2F%3E%20%3Cline%20x1%3D'22'%20y1%3D'3'%20x2%3D'22'%20y2%3D'34'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'1.4'%2F%3E%20%3Cpath%20d%3D'M17%2C34%20L22%2C21%20L27%2C34'%20fill%3D'none'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'1.4'%20stroke-linejoin%3D'round'%2F%3E%20%3C%2Fg%3E%20%3C!--%20Bell%20tent%20with%20flag%20--%3E%20%3Cg%20transform%3D'translate(95%2C58)'%20opacity%3D'0.36'%3E%20%3Cpath%20d%3D'M3%2C37%20Q3%2C9%2022%2C7%20Q41%2C9%2041%2C37%20Z'%20fill%3D'none'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'2'%20stroke-linejoin%3D'round'%2F%3E%20%3Cline%20x1%3D'22'%20y1%3D'7'%20x2%3D'22'%20y2%3D'0'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'1.4'%2F%3E%20%3Cpath%20d%3D'M22%2C0%20L29%2C2.5%20L22%2C5%20Z'%20fill%3D'%23FAFBF6'%20stroke%3D'none'%2F%3E%20%3C%2Fg%3E%20%3C!--%20Campervan%20--%3E%20%3Cg%20transform%3D'translate(43%2C10)'%20opacity%3D'0.36'%3E%20%3Cpath%20d%3D'M1%2C30%20L1%2C15%20Q1%2C7%2011%2C7%20L37%2C7%20Q47%2C7%2047%2C17%20L47%2C30%20Z'%20fill%3D'none'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'2'%20stroke-linejoin%3D'round'%2F%3E%20%3Cline%20x1%3D'16'%20y1%3D'7'%20x2%3D'12'%20y2%3D'30'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'1.4'%2F%3E%20%3Crect%20x%3D'4'%20y%3D'12'%20width%3D'8'%20height%3D'8'%20rx%3D'1.5'%20fill%3D'none'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'1.3'%2F%3E%20%3Crect%20x%3D'19'%20y%3D'12'%20width%3D'24'%20height%3D'8'%20rx%3D'1.5'%20fill%3D'none'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'1.3'%2F%3E%20%3Ccircle%20cx%3D'13'%20cy%3D'30'%20r%3D'4.5'%20fill%3D'none'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'1.4'%2F%3E%20%3Ccircle%20cx%3D'37'%20cy%3D'30'%20r%3D'4.5'%20fill%3D'none'%20stroke%3D'%23FAFBF6'%20stroke-width%3D'1.4'%2F%3E%20%3C%2Fg%3E%20%3C%2Fsvg%3E");

  --field-bg: #ffffff;       /* form fields — brighter than the card so they read as inputs */

  /* Chart bar colours — must stay clearly distinct from each other in both themes */
  --chart-paid: #1F5738;
  --chart-owing: #C9861A;
  --chart-overdue: #B23A55;
  /* Deeper, more saturated versions of the two above — a confirmed booking with
     literally nothing paid toward it is a different situation from one that's just
     waiting on the final balance, so it earns a visually heavier colour, not the same
     amber/berry as everything else that owes something. */
  --chart-owing-all: #A8460A;
  --chart-overdue-all: #7A1F3A;
  --chart-unconfirmed: #7A8B99;
  --chart-pending: #5762A0;
  --chart-external: #33407A;
  --chart-blocked-a: #D6D2C4;
  --chart-blocked-b: #C4BFAF;
  --chart-text: #ffffff;
}

/* Dark theme. Deliberately warm charcoal rather than pure black — this gets used at a
   reception desk at night, and pure black on a bright screen is harsh to read for long. */
[data-theme="dark"] {
  color-scheme: dark;
  --pine: #85DEAB;           /* headings/accents must be light in dark mode to stay legible */
  --pine-light: #61BC8D;
  --dusk: #8D97DA;
  --dusk-soft: #6B76B8;
  --clay: #E0916A;
  --bark: #A6AC9A;
  --on-bark: #1A2016;
  --canvas: #11150D;
  --canvas-light: #1A2016;
  --amber: #FF9B62;
  --on-pine: #1A2016;
  --on-dusk: #1A2016;
  --on-amber: #201C16;
  --ember: #D9557E;          /* balances two jobs: legible as text on dark canvas, and
                                 dark enough to still hold white text as a button fill */
  --ink: #EAEEE3;
  --line: #2B3226;

  --muted: #A6AC9A;
  --surface: #1A2016;
  --surface-alt: #242B1E;
  --surface-sunken: #161B11;
  --tint-good: #1D3527;
  --tint-warn: #3A2E14;
  --tint-bad: #3A1F26;
  --on-accent: #1A0D06;
  --shadow: rgba(0,0,0,0.5);

  --map-grass: #2F4033;
  --map-grass-edge: #364A38;
  --map-path: #4A4639;
  --map-building: #6E5940;
  --map-roof: #5A4733;
  --map-tree-dark: #14261C;
  --map-tree-mid: #1B3527;
  --map-tree-light: #295038;
  --map-label: #EAEEE3;
  --map-label-halo: #10140D;

  /* Fixed, same as light — the dusk moment doesn't invert for dark mode. */
  --hero-top: #123524;
  --hero-mid: #33407A;
  --hero-bottom: #FF6B35;
  --hero-ridge: #0B2115;
  --hero-on: #FBF6EC;

  --field-bg: #242B1E;       /* lighter than the card, same "stands out" affordance as light mode */

  /* Lighter, less saturated versions — full-strength colours glow uncomfortably on dark */
  --chart-paid: #3E8F65;
  --chart-owing: #D99A2E;
  --chart-overdue: #C4527A;
  --chart-owing-all: #E06A1A;
  --chart-overdue-all: #B0446E;
  --chart-unconfirmed: #8FA0AD;
  --chart-pending: #6F79B8;
  --chart-external: #4A5590;
  --chart-blocked-a: #2B3226;
  --chart-blocked-b: #242B1E;
  --chart-text: #F5F1E6;
}

/* ---- Theming: "Command Deck" — admin dashboard only ----
   A separate identity from Alpenglow above, scoped to body.admin-page so it never reaches
   the guest-facing pages (public/index.html, portal.html, pay.html, etc.), which keep
   Alpenglow and their own per-site branding colours untouched. Deliberately fixed, not
   overridable per site (see applyAdminBranding() in dashboard.js, which no longer applies
   a site's custom colours to the dashboard) — the tape chart's arriving/departing/
   overdue/settled colour-coding needs to mean the same thing on every site's dashboard,
   not clash with whatever brand colour that site picked for its own booking page.
   Reuses the SAME variable names as Alpenglow (--pine, --amber, --surface, etc.) rather
   than introducing new ones, since every existing rule in this file and every var(--...)
   reference in dashboard.js already reads those — a full reskin becomes a matter of
   redefining values, not rewriting every selector that uses them. A rule that matches
   body.admin-page directly always wins over whatever's inherited from <html> (including
   any leftover inline override), so this doesn't depend on the JS having been updated
   too — it is, but this alone would already be enough. */
body.admin-page, body.admin-page[data-theme="light"] {
  color-scheme: light;
  --pine: #1F5F52;
  --pine-light: #2C7C6A;
  --dusk: #2F5FA0;
  --dusk-soft: #5B85BE;
  --clay: #B5651D;
  --bark: #57645F;
  --on-bark: #F8FAF9;
  --canvas: #EEF1F0;
  --canvas-light: #FFFFFF;
  --amber: #C97A1E;
  --ember: #B4433A;
  --on-pine: #F8FAF9;
  --on-dusk: #F8FAF9;
  --on-amber: #FFFFFF;
  --ink: #16211F;
  --line: #D7DEDC;
  --muted: #55645F;
  --surface: #FFFFFF;
  --surface-alt: #E7ECEA;
  --surface-sunken: #EFF3F1;
  --tint-good: #DCEFE7;
  --tint-warn: #F6E6CE;
  --tint-bad: #F7DEDC;
  --on-accent: #FFFFFF;
  --shadow: rgba(20,30,28,0.10);
  --field-bg: #FFFFFF;
  --chart-paid: #1F6B4E;
  --chart-owing: #C9861A;
  --chart-overdue: #B4433A;
  --chart-owing-all: #A8460A;
  --chart-overdue-all: #7A2620;
  --chart-unconfirmed: #6B7F8C;
  --chart-pending: #3F62A0;
  --chart-external: #2F5FA0;
  --chart-blocked-a: #D6D9D8;
  --chart-blocked-b: #C7CBC9;
  --chart-text: #FFFFFF;
}
/* Higher specificity than the bare [data-theme="dark"] rule above (which only matches
   <html>) so this wins for everything inside body.admin-page regardless of source order. */
[data-theme="dark"] body.admin-page {
  color-scheme: dark;
  --pine: #4C9A78;
  --pine-light: #6BB596;
  --dusk: #5B8FD9;
  --dusk-soft: #7FA8E2;
  --clay: #E3A23C;
  --bark: #8FA09C;
  --on-bark: #0E1516;
  --canvas: #111A1D;
  --canvas-light: #1B2529;
  --amber: #E3A23C;
  --ember: #D9564A;
  --on-pine: #0E1516;
  --on-dusk: #0E1516;
  --on-amber: #14100A;
  --ink: #E8EDEA;
  --line: #2C3A3F;
  --muted: #8FA09C;
  --surface: #1B2529;
  --surface-alt: #223035;
  --surface-sunken: #0D1416;
  --tint-good: #1B3226;
  --tint-warn: #332616;
  --tint-bad: #33201C;
  --on-accent: #14100A;
  --shadow: rgba(0,0,0,0.45);
  --field-bg: #1F2A2E;
  --chart-paid: #4C9A78;
  --chart-owing: #E3A23C;
  --chart-overdue: #D9564A;
  --chart-owing-all: #E8843C;
  --chart-overdue-all: #C4433A;
  --chart-unconfirmed: #7E96A0;
  --chart-pending: #5B8FD9;
  --chart-external: #6F8FC4;
  --chart-blocked-a: #26312F;
  --chart-blocked-b: #1F2A28;
  --chart-text: #F5F1E6;
}
body.admin-page {
  font-family: 'Work Sans', -apple-system, sans-serif;
}
body.admin-page h1, body.admin-page h2, body.admin-page h3 {
  font-family: 'Chivo', 'Arial Narrow', sans-serif;
  letter-spacing: 0;
}
/* The dusk-over-treeline header stays as-is deliberately — it's Alpenglow's one fixed,
   theme-independent signature moment (see header.topbar below), not part of the token
   system this reskin recolours. Left in place rather than reworked in the same pass that
   just fixed the dashboard's own palette, so it doesn't get lost as a separate decision. */

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: 'Public Sans', -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.5;
}

/* Big Shoulders Display is a condensed, signage-like face — bold and unmistakable at
   page-title size, but shouting in caps at every small section header throughout a
   dense admin screen would fatigue fast. So h1 (one per page) gets the full treatment;
   h2/h3 keep the same face's structure without going uppercase. */
h1, h2, h3 { font-family: 'Big Shoulders Display', 'Arial Narrow', sans-serif; font-weight: 700; letter-spacing: 0.01em; margin: 0 0 0.4em; color: var(--pine); }
h1 { font-size: 2.2rem; font-weight: 800; text-transform: uppercase; }
h2 { font-size: 1.5rem; }
h3 { font-size: 1.2rem; }

.mono { font-family: 'IBM Plex Mono', monospace; white-space: nowrap; }

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

/* The header is the one signature moment: dusk light over a treeline, the thing every
   campsite actually looks like at the hour most guests arrive. It's a fixed image, not a
   themed surface (--hero-* vars are the same in light and dark — see above), and it's
   spent nowhere else in the interface. A site's own branding colours (Settings) still
   override --pine/--amber everywhere else; the hero deliberately sits outside that so
   one guest-uploaded colour can't turn dusk into daylight. */
header.topbar {
  background: linear-gradient(165deg, var(--hero-top) 0%, var(--hero-mid) 65%, var(--hero-bottom) 130%);
  color: var(--hero-on);
  padding: 0.8rem 1.3rem 1.3rem 1.3rem;
  /* Pinned to the top on every device — sticky rather than fixed, so it keeps reserving
     its own space in normal flow (nothing below needs manual padding to avoid sliding
     under it) right up until the page actually scrolls past it. Still works as the
     containing block for the absolutely-positioned mark/ridge below, same as relative
     did. z-index keeps scrolled page content from painting over it once stuck; still
     comfortably below the mobile drawer (200), its backdrop (199) and the hamburger
     that needs to sit above the drawer (201) — see .nav-toggle. */
  position: sticky;
  top: 0;
  z-index: 100;
  overflow: hidden;
  box-shadow: 0 2px 14px var(--shadow);
}
/* Holds the operator's own logo (inserted by applyBranding() in booking.js) beside their
   site name, rather than the logo stacking above it as its own line. */
.site-brand-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.site-logo { height: 30px; width: auto; display: block; border-radius: 4px; }
/* Only appears once a website_url is set (see applyBranding() in booking.js) — wraps the
   existing logo+name rather than restyling them, so a tenant with no website on file sees
   no change at all. */
.site-brand-link { display: flex; align-items: center; gap: 0.6rem; text-decoration: none; color: inherit; }

.site-footer {
  text-align: center;
  padding: 1.75rem 1rem 2.5rem;
  font-size: 0.8rem;
  color: var(--muted);
}
.site-footer a { color: var(--muted); text-decoration: underline; text-decoration-color: var(--line); }
.site-footer a:hover { color: var(--pine); }
header.topbar h1 {
  color: var(--hero-on);
  font-size: 1.3rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin: 0;
}
/* "Logged in as X (role)" used to sit here always — genuinely useful once, skippable on
   every subsequent visit, and it now also lives in Settings > Account, so dropping it
   here isn't a loss, just a header that stops repeating itself on every page load. */
header.topbar .subtitle { display: none; }
/* A low ridgeline silhouette, not a decorative rule — the horizon the gradient is lit
   against. Height scales with the smaller header above; the polygon itself is already
   in percentages, so it doesn't need its own adjustment. */
header.topbar::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 13px;
  background: var(--hero-ridge);
  clip-path: polygon(
    0% 100%, 0% 62%, 5% 35%, 11% 58%, 17% 20%, 24% 52%, 31% 28%, 38% 56%,
    45% 18%, 53% 48%, 61% 10%, 69% 44%, 77% 24%, 85% 54%, 93% 22%, 100% 40%, 100% 100%
  );
}

/* Scales with the viewport instead of sitting at a fixed width — a fixed 960px looked
   the same on a 13" laptop as on a 32" monitor, wasting most of the screen on anything
   larger. The cap keeps text-heavy steps (dates, details) from stretching into
   unreadably long lines on an ultrawide display. */
.wrap { max-width: min(92vw, 1400px); margin: 0 auto; padding: 1.5rem; }

/* A coloured left-border on a rounded card is a generic default nearly every AI-styled
   dashboard reaches for. A dashed inset line reads as a stitched seam instead — which is
   where this app's canvas motif actually came from, applied on purpose here rather than
   out of habit. */
.card {
  background: var(--canvas-light);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin-bottom: 1rem;
  box-shadow: 0 1px 4px var(--shadow);
  position: relative;
}
.card::before {
  content: '';
  position: absolute;
  inset: 6px;
  border: 1px dashed color-mix(in srgb, var(--pine) 35%, transparent);
  border-radius: 2px;
  pointer-events: none;
}

/* Pitches grouped into their real sections (A, B, C, D…, matching sites.section) rather
   than one undifferentiated grid — a guest at a real site thinks "which row is this in?",
   not just "which number?". Chips, not bordered boxes: a previous version packed the name,
   surface and capacity into a tiny bordered card and ellipsis-truncated whatever didn't
   fit, which hid text outright and read poorly in dark mode (a 1px border in --line is low
   contrast against --canvas-light there). A pill with a solid fill reads clearly in both
   themes and never truncates — it just wraps onto the next line. */
.pitch-section { margin-bottom: 1.25rem; }
.pitch-section:last-child { margin-bottom: 0; }
.pitch-section-title {
  font-family: 'Big Shoulders Display', 'Arial Narrow', sans-serif;
  font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase;
  font-size: 0.85rem; color: var(--pine); margin: 0 0 0.5rem;
}
.pitch-chip-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.pitch-chip {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  row-gap: 0.3rem;
  gap: 0.45rem;
  max-width: 100%;
  border: 1px solid transparent;
  border-radius: 100px;
  background: var(--surface-alt);
  color: var(--ink);
  padding: 0.5rem 1rem;
  font-family: 'Public Sans', -apple-system, sans-serif;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.pitch-chip:hover { border-color: var(--amber); }
/* Electric hookup pitches get their own colour rather than amber, since amber already
   means "selected" — a blue tint reads as a distinct fact about the pitch instead of
   looking like it's already picked. Selecting one still shows the normal amber fill (the
   rule below wins), since which state you're in matters more once you've actually chosen. */
.pitch-chip-electric { border-color: var(--dusk); background: color-mix(in srgb, var(--dusk) 14%, var(--surface-alt)); }
.pitch-chip-electric:hover { border-color: var(--dusk); }
.pitch-chip.selected { background: var(--amber); color: var(--on-amber); }
.pitch-chip .pc-meta { font-weight: 400; font-size: 0.78rem; opacity: 0.75; white-space: nowrap; }
.pitch-chip .pc-price { font-weight: 700; font-size: 0.85rem; white-space: nowrap; }
/* A visible reason a pitch is cheaper, not just a lower number next to it — its own colour
   (distinct from the amber "selected" state) so it reads as "why", not as another meta tag. */
.pitch-chip .pc-discount {
  font-weight: 600; font-size: 0.72rem; white-space: nowrap;
  background: var(--tint-good); color: var(--pine-light);
  border-radius: 100px; padding: 0.15rem 0.6rem;
}
.pitch-chip.selected .pc-discount { background: rgba(255,255,255,0.35); color: var(--on-amber); }

/* Photo strip for whichever pitch type is currently selected, above the pitch grid — the
   category's own photos (Sites > Categories > Edit / photos), not any one specific pitch. */
.category-photo-strip { display: flex; gap: 0.5rem; overflow-x: auto; margin-bottom: 0.75rem; padding-bottom: 0.2rem; }
.category-photo-strip img {
  height: 110px; width: auto; border-radius: var(--radius); object-fit: cover;
  cursor: zoom-in; flex-shrink: 0;
}

/* Step 2's "what type of pitch?" cards — bigger and fewer than a pitch chip (a handful of
   categories, not dozens of numbered pitches), each carrying a photo, description and a
   real from-price so this step earns being its own step rather than a bare dropdown. Photo
   sits beside the text, not stacked above it — a full-width banner photo pushed the price
   and description down where a quick side-by-side glance reads faster. A category with no
   photo uploaded yet just loses that column rather than leaving an empty gap. */
.category-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1rem; }
.category-cards > * { min-width: 0; }
.category-card {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
  background: var(--canvas-light);
  box-shadow: 0 1px 4px var(--shadow);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.category-card:hover { border-color: var(--amber); box-shadow: 0 4px 14px var(--shadow); transform: translateY(-2px); }
.category-card.selected { border-color: var(--amber); box-shadow: 0 0 0 2px var(--amber) inset; }
.category-card.unavailable { opacity: 0.55; cursor: default; }
.category-card.unavailable:hover { transform: none; box-shadow: 0 1px 4px var(--shadow); }
.category-card img { width: 130px; flex-shrink: 0; object-fit: cover; display: block; }
.category-card .cc-body { padding: 0.9rem 1.1rem; flex: 1; min-width: 0; }
.category-card h3 { margin: 0 0 0.3rem; font-size: 1.15rem; }
.category-card .cc-desc { font-size: 0.84rem; color: var(--muted); margin: 0 0 0.6rem; }
.category-card .cc-price { font-weight: 700; color: var(--pine); font-size: 1.05rem; margin: 0; }
.category-card .cc-select {
  margin: 0.55rem 0 0;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--amber);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.category-card.selected .cc-select { color: var(--pine); }
.category-card .cc-unavailable { color: var(--muted); font-size: 0.82rem; margin: 0; }

/* Site map sits above the pitch sections, full width. When the operator has confirmed pitch
   positions (see pitch_map_interactive), clicking the map IS the picker and the chip list
   below shrinks to just a summary of what's selected — so the map gets to be the main event
   rather than a small preview crowded out by a long list, hence the much taller cap than a
   "just for reference" image would need. */
.site-picker-layout { display: flex; flex-direction: column; gap: 1rem; }
#pitch-map-panel { width: 100%; }
#pitch-map-image-wrap { position: relative; line-height: 0; }
#pitch-map-image { width: 100%; max-height: 720px; object-fit: contain; background: var(--surface-alt); border-radius: var(--radius); border: 1px solid var(--line); display: block; }
.pitch-map-hint { font-size: 0.9rem; color: var(--muted); text-align: center; padding: 0.5rem 0; }
.pc-remove {
  border: none; background: none; color: inherit; opacity: 0.7; cursor: pointer;
  font-size: 0.9rem; line-height: 1; padding: 0 0 0 0.2rem;
}
.pc-remove:hover { opacity: 1; }

/* Click-to-enlarge overlay for any pitch/category photo — one shared lightbox rather than
   per-photo modals, since only one can be open at a time anyway. */
.photo-lightbox {
  position: fixed; inset: 0; background: rgba(10,10,10,0.86); z-index: 1000;
  display: flex; align-items: center; justify-content: center; padding: 2rem; cursor: zoom-out;
}
.photo-lightbox img { max-width: 100%; max-height: 100%; border-radius: 6px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); }
.lightbox-nav {
  position: fixed; top: 50%; transform: translateY(-50%); z-index: 1001;
  background: rgba(255,255,255,0.15); color: #fff; border: none; border-radius: 100px;
  width: 2.75rem; height: 2.75rem; font-size: 1.6rem; line-height: 1; cursor: pointer;
}
.lightbox-nav:hover { background: rgba(255,255,255,0.3); }
.lightbox-prev { left: 1rem; }
.lightbox-next { right: 1rem; }
.lightbox-count {
  position: fixed; bottom: 1.25rem; left: 50%; transform: translateX(-50%);
  color: #fff; font-size: 0.8rem; background: rgba(0,0,0,0.4); padding: 0.2rem 0.7rem; border-radius: 100px;
}

/* Thumbnail on each pitch chip in step 3 — the pitch's own photo, so pitches of the same
   type are visually distinct rather than all reading as interchangeable text. */
.pc-thumb {
  width: 2.2rem; height: 2.2rem; border-radius: 6px; object-fit: cover;
  cursor: zoom-in; flex-shrink: 0; align-self: center;
  margin: -0.25rem 0 -0.25rem -0.4rem;
}

/* The view from the chosen pitch (sites.view_photo_url), under "Pitch selected" in step 3. */
.pitch-view { margin: 0.5rem 0 0; }
.pitch-view img { display: block; width: 100%; max-width: 480px; aspect-ratio: 16 / 10; object-fit: cover; border-radius: var(--radius); border: 1px solid var(--line); cursor: zoom-in; }
.pitch-view figcaption { font-size: 0.9rem; color: var(--muted); margin-top: 0.3rem; }

label { display: block; font-size: 0.85rem; font-weight: 500; margin: 0.75rem 0 0.3rem; color: var(--bark); }
input, select, textarea {
  width: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-family: 'Public Sans', -apple-system, sans-serif;
  font-size: 0.95rem;
  background: var(--field-bg);
  color: var(--ink);
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--amber); outline-offset: 1px; }

button.btn, a.btn {
  background: var(--amber);
  /* var(--on-amber), not var(--ink) — the button is amber in both themes (and can now be
     a custom accent colour too), so text needs to track what --amber actually resolves to,
     not the page's own theme. See --on-amber above. */
  color: var(--on-amber);
  border: none;
  padding: 0.7rem 1.3rem;
  border-radius: var(--radius);
  font-weight: 600;
  cursor: pointer;
  font-size: 0.95rem;
  display: inline-block;
  text-decoration: none;
}
button.btn:hover, a.btn:hover { background: var(--amber); }
button.btn:disabled { background: var(--line); color: var(--muted); cursor: not-allowed; }
/* A visible fill even at rest, not just a border — same reasoning as .subtab above: an
   outline-only button on a dark or custom-coloured card can read as barely-there, especially
   in dark mode where the border colour and card background sit close in tone. */
button.btn.secondary, a.btn.secondary { background: var(--surface-alt); border: 1px solid var(--pine); color: var(--pine); }
button.btn.danger, a.btn.danger { background: var(--ember); color: #fff; }

.price-summary { border-top: 1px dashed var(--line); margin-top: 1rem; padding-top: 0.75rem; }
.price-summary .row { display: flex; justify-content: space-between; font-family: 'IBM Plex Mono', monospace; font-size: 0.9rem; margin-bottom: 0.3rem; }
.price-summary .row.total { font-weight: 600; font-size: 1.05rem; color: var(--pine); }

.status-tag { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; padding: 0.2rem 0.55rem; border-radius: 100px; font-weight: 600; }
.status-confirmed { background: var(--tint-good); color: var(--pine-light); }
.status-pending_payment { background: var(--tint-warn); color: var(--amber); }
.status-cancelled { background: var(--tint-bad); color: var(--ember); }
.status-completed { background: var(--surface-alt); color: var(--bark); }

/* A literal little indicator light — check-in/check-out state at a glance on the Diary,
   without reading text, the way a physical panel would show it. Lit uses a glow (box-shadow),
   not just a colour swap, so "on" reads as genuinely lit rather than just a different dot. */
.led { display: inline-block; width: 9px; height: 9px; border-radius: 50%; vertical-align: middle; cursor: pointer; }
.led.on { background: var(--chart-paid); box-shadow: 0 0 4px 1px var(--chart-paid); }
.led.off { background: var(--line); }
.led:hover:not(.led-disabled) { box-shadow: 0 0 0 3px var(--tint-good); }
.led-disabled { cursor: default; opacity: 0.5; }

/* The dot itself is tiny — this is the actual click target around it (see
   checkStatusLedCell() in dashboard.js), padded well past the dot so a near-miss still
   toggles the light instead of falling through to the row underneath and opening the
   booking by mistake. */
.led-cell { text-align: center; padding: 0.5rem 0.7rem !important; cursor: pointer; }
.led-cell:has(.led-disabled) { cursor: default; }
.led-cell:hover { background: color-mix(in srgb, var(--pine) 8%, var(--canvas-light)); }

/* Capped rather than left to grow with the note's length — an unbounded note on the Diary
   tab widened its row enough to push the In/Out check-in controls off the visible edge of
   the screen. The full text is still reachable via the title tooltip, and one tap away
   regardless through the row's own click-through to the booking detail. */
.diary-notes-cell { max-width: 160px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.error-msg { color: var(--ember); font-size: 0.9rem; margin-top: 0.5rem; }
.success-msg { color: var(--pine-light); font-size: 0.9rem; margin-top: 0.5rem; }

table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
table th, table td { text-align: left; padding: 0.6rem 0.5rem; border-bottom: 1px solid var(--line); }
table th { font-family: 'IBM Plex Mono', monospace; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--bark); }

/* ---- Shared data-table chrome: toolbar, sortable headers, columns picker, totals row,
   pagination — see dtRenderTable() in dashboard.js. ---- */
.dt-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 0.6rem; }
.dt-selection { font-size: 0.82rem; color: var(--muted); }
.dt-toolbar-actions { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }

table th.dt-sortable { cursor: pointer; user-select: none; }
table th.dt-sortable:hover { color: var(--pine); }

/* Opt-in per table via opts.onRowClick (Bookings only, so far) — the row itself is
   clickable to open its detail view, same affordance as a sortable header, while the
   buttons inside the actions cell keep working normally (see dtRenderTable's own
   event.target.closest carve-out) rather than every click double-firing. */
tr.dt-row-clickable { cursor: pointer; }
tr.dt-row-clickable:hover td { background: color-mix(in srgb, var(--pine) 8%, var(--canvas-light)); }

/* An ANPR-matched arrival (see anpr_highlight_arrivals) — the car has actually been seen
   arriving today, so this row is worth picking out from everyone else still expected. */
tr.diary-anpr-matched td { background: var(--tint-good); }
tr.diary-anpr-matched.dt-row-clickable:hover td { background: color-mix(in srgb, var(--tint-good) 70%, var(--pine) 12%); }

.dt-totals-row td { font-weight: 600; border-top: 2px solid var(--line); border-bottom: none; font-family: 'IBM Plex Mono', monospace; }

.dt-columns-picker { position: relative; }
.dt-columns-menu {
  position: absolute; right: 0; top: calc(100% + 0.3rem); z-index: 20;
  background: var(--canvas-light); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 4px 16px var(--shadow); padding: 0.6rem 0.8rem; min-width: 160px;
}
.dt-columns-menu label { display: flex; align-items: center; gap: 0.4rem; font-size: 0.85rem; padding: 0.2rem 0; white-space: nowrap; }
.dt-columns-menu input[type="checkbox"] { width: auto; }

/* Scrollable table body: a visible (not native-invisible) scrollbar so overflow reads as
   "more to scroll" rather than content silently cut off at the edge. A min-width on the
   table itself matters just as much as the scrollbar — without it, `table { width:100% }`
   just keeps shrinking every column's content (wrapping text, squashing buttons) to fit
   the container instead of ever triggering real overflow, which is the actual mechanism
   behind the table looking "squeezed" rather than scrollable on a narrow screen. */
.dt-scroll { overflow-x: auto; scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
.dt-scroll table { width: max-content; min-width: 100%; }
.dt-scroll::-webkit-scrollbar { height: 8px; }
.dt-scroll::-webkit-scrollbar-track { background: transparent; }
.dt-scroll::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }

/* Columns marked `secondary` (BOOKINGS_COLUMNS etc. in dashboard.js) fold away below the
   width they'd otherwise force the table past, so the essential columns and the actions
   stay reachable without scrolling on ordinary laptop widths. The columns picker can still
   bring one back on demand. */
@media (max-width: 1300px) {
  table th.dt-col-secondary, table td.dt-col-secondary { display: none; }
}

/* An actions cell with several stacked buttons (view/mark paid/refund/cancel) gets its own
   compact sizing so it doesn't dominate the table's width. Buttons stack via display:block,
   not a flex column — a flex formatting context nested inside a table cell measures its
   intrinsic size inconsistently between Chrome's table column-width pass and its final
   paint pass, which let this cell render past the table's own right edge instead of
   scrolling with it. Plain block-stacked buttons don't have that problem. */
.dt-actions-cell .btn { display: block; width: fit-content; margin-bottom: 0.3rem; padding: 0.35rem 0.6rem; font-size: 0.78rem; white-space: nowrap; }
.dt-actions-cell .btn:last-child { margin-bottom: 0; }

.dt-pager-wrap { margin-top: 0.75rem; }
.dt-pager { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.dt-pager-buttons { display: flex; gap: 0.35rem; flex-wrap: wrap; }
.dt-pager-buttons .btn { padding: 0.3rem 0.65rem; font-size: 0.82rem; }
.dt-pager-buttons .btn.secondary.active { background: var(--pine); color: var(--on-pine); }
.dt-pager-info { display: flex; align-items: center; gap: 0.6rem; font-size: 0.82rem; color: var(--muted); }
.dt-pager-info select { width: auto; padding: 0.2rem 0.4rem; }
.dt-pager-info label { display: flex; align-items: center; gap: 0.4rem; }

/* ---- Admin navigation ----
   24 sections is far too many for a row of pills across the top — it wrapped onto three
   lines and pushed the real content down the page. A grouped sidebar instead, which also
   makes it obvious which section you're in. */
/* Same dynamic idea as the base .wrap, just a bigger cap — the dashboard has a sidebar
   plus wide tables to fit, so it earns more width than the guest booking flow. */
body.admin-page .wrap { max-width: min(97vw, 1800px); }
.admin-shell { display: flex; gap: 1.25rem; align-items: flex-start; }

nav.tabs {
  flex: 0 0 190px;
  /* Sticks below the header now that the header itself is pinned (position:sticky,
     see header.topbar) rather than scrolling away — offset by its rendered height
     (~65px) plus a little headroom, not just 1rem, or the sidebar would tuck in
     underneath and start out partly hidden. */
  position: sticky; top: 4.75rem;
  max-height: calc(100vh - 5.75rem);
  display: flex; flex-direction: column;
  background: var(--canvas-light);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.6rem;
  box-shadow: 0 1px 4px var(--shadow);
}
/* The tab groups scroll on their own; the footer below stays pinned in view regardless of
   how long the tab list gets or how short the viewport is — logging out should never
   require scrolling to find. */
.nav-inner { flex: 1; min-height: 0; overflow-y: auto; }
.nav-footer {
  flex-shrink: 0;
  margin-top: 0.6rem; padding-top: 0.6rem;
  border-top: 1px solid var(--line);
}
.nav-footer .btn { width: 100%; }
.nav-group {
  margin-bottom: 0.9rem;
  padding-left: 0.5rem;
  border-left: 3px solid var(--line);
}
.nav-group:last-child { margin-bottom: 0; }
/* Each section gets its own colour, cycling through the brand palette, so the sidebar
   reads as distinct areas rather than one long flat list. The heading's own background is
   a soft tint of that same colour — not just the left border — so the section reads as one
   coloured block at a glance instead of the heading looking like plain grey text sitting
   above an unrelated coloured line. */
.nav-group:nth-child(1) { border-left-color: var(--pine); }
.nav-group:nth-child(1) .nav-group-title { background: color-mix(in srgb, var(--pine) 16%, transparent); color: var(--pine); }
.nav-group:nth-child(2) { border-left-color: var(--amber); }
.nav-group:nth-child(2) .nav-group-title { background: color-mix(in srgb, var(--amber) 16%, transparent); color: var(--amber); }
.nav-group:nth-child(3) { border-left-color: var(--ember); }
.nav-group:nth-child(3) .nav-group-title { background: color-mix(in srgb, var(--ember) 16%, transparent); color: var(--ember); }
.nav-group:nth-child(4) { border-left-color: var(--pine-light); }
.nav-group:nth-child(4) .nav-group-title { background: color-mix(in srgb, var(--pine-light) 16%, transparent); color: var(--pine-light); }
.nav-group:nth-child(5) { border-left-color: var(--bark); }
.nav-group:nth-child(5) .nav-group-title { background: color-mix(in srgb, var(--bark) 16%, transparent); color: var(--bark); }
.nav-group:nth-child(6) { border-left-color: var(--dusk); }
.nav-group:nth-child(6) .nav-group-title { background: color-mix(in srgb, var(--dusk) 16%, transparent); color: var(--dusk); }
.nav-group-title {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.09em;
  font-weight: 600;
  padding: 0.25rem 0.4rem;
  border-radius: 4px;
  margin-bottom: 0.3rem;
}
nav.tabs button {
  display: block; width: 100%; text-align: left;
  background: transparent; border: 0;
  padding: 0.4rem 0.5rem; margin-bottom: 1px;
  border-radius: 5px; cursor: pointer;
  font-size: 0.83rem; color: var(--ink);
  font-family: inherit;
}
nav.tabs button:hover { background: var(--surface-alt); }
nav.tabs button.active { background: var(--pine); color: var(--on-pine); font-weight: 600; }

/* min-width:0 stops the wide booking tables from squeezing the sidebar off screen */
.admin-content { flex: 1; min-width: 0; }

.nav-toggle { display: none; }
/* Always visible, unlike .nav-toggle above (mobile-only) — same hamburger-button treatment
   (size, colour, radius), just for the Help & Assistant drawer rather than the tab sidebar,
   since it's now the only way to reach either. */
.assistant-toggle {
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  width: 44px; height: 44px;
  background: var(--pine); color: var(--on-pine);
  border-radius: 8px; padding: 0;
  font-size: 1.15rem; min-height: 44px;
  position: relative; z-index: 201;
}

/* Help & Assistant drawer — mirrors nav.tabs' off-canvas pattern (.open + shared
   .nav-backdrop) but slides in from the right and is available at any viewport width, not
   just the mobile breakpoint. */
.side-drawer {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: 420px; max-width: 100vw;
  background: var(--surface);
  z-index: 200;
  transform: translateX(100%);
  transition: transform 0.2s ease;
  box-shadow: -2px 0 12px rgba(0,0,0,0.2);
  display: flex; flex-direction: column;
}
.side-drawer.open { transform: translateX(0); }
/* Unlike .nav-backdrop (mobile-only, since the left drawer only exists there), this drawer
   is available at every width, so its dimming overlay needs to apply everywhere too — not
   scoped to a media query. */
.drawer-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.35); z-index: 199; }
.side-drawer-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 1rem 1.25rem; border-bottom: 1px solid var(--line); flex-shrink: 0;
}
.side-drawer-header h2 { margin: 0; }
.side-drawer-header button {
  width: auto; background: none; border: none; font-size: 1.6rem; line-height: 1;
  cursor: pointer; color: var(--muted); padding: 0.25rem 0.5rem;
}
.side-drawer-header button:hover { color: var(--ink); }
.side-drawer-subtabs { display: flex; gap: 0.5rem; padding: 0.75rem 1.25rem 0; flex-shrink: 0; }
.side-drawer-subtab {
  width: auto; background: var(--surface-alt); border: 1px solid var(--line);
  border-radius: 999px; padding: 0.35rem 0.9rem; font-size: 0.82rem; cursor: pointer;
  color: var(--muted);
}
.side-drawer-subtab.active { background: var(--pine); border-color: var(--pine); color: var(--on-pine); font-weight: 600; }
.side-drawer-body { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 1.25rem; }
.side-drawer-view { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow-y: auto; }
#assistant-drawer-chat #assistant-chat { display: flex; flex-direction: column; flex: 1; min-height: 0; }
#assistant-messages {
  flex: 1; overflow-y: auto; min-height: 150px;
  border: 1px solid var(--line); border-radius: 6px; padding: 0.75rem; background: var(--surface);
}
@media (max-width: 480px) { .side-drawer { width: 100vw; } }

/* Between the full sidebar and the drawer breakpoint, a laptop-width screen still gets a
   permanent sidebar but can't spare 190px for it once a wide table is on screen — narrow it
   and drop its label size a touch to give the content back some room. */
@media (max-width: 1150px) and (min-width: 901px) {
  nav.tabs { flex-basis: 155px; }
  nav.tabs button { font-size: 0.79rem; padding: 0.35rem 0.4rem; }
}

@media (max-width: 900px) {
  .admin-shell { display: block; }
  /* Slide-out drawer on small screens — a permanent column costs too much width */
  nav.tabs {
    position: fixed; top: 0; left: 0; bottom: 0;
    width: 230px; z-index: 200;
    border-radius: 0;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: 2px 0 12px rgba(0,0,0,0.2);
    max-height: 100vh;
  }
  nav.tabs.open { transform: translateX(0); }
  /* Sits inline in the header's toolbar row now (alongside search — see admin/index.html
     and .header-toolbar below), not floating fixed over the title. That freed up the
     header's top-left corner for the site logo and name, and means nothing needs to
     compensate with extra header/content padding to avoid it anymore. Only the drawer
     it opens (nav.tabs, above) still needs position:fixed, to lift it above the page. */
  .nav-toggle {
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    /* relative + z-index, not fixed — stays a normal flex item in the toolbar row (see
       admin/index.html), but still needs to sit above the open drawer (z-index 200) and
       its backdrop (199), since the drawer is position:fixed from top:0 and would
       otherwise cover this button, leaving no way to tap it again to close. */
    position: relative; z-index: 201;
    width: 44px; height: 44px;
    background: var(--pine); color: var(--on-pine);
    border-radius: 8px; padding: 0;
    font-size: 1.1rem; min-height: 44px;
  }
  .nav-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.35); z-index: 199; }
}

.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
/* Grid items default to min-width:auto, not 0 — meaning a child with content that
   resists wrapping (a table cell with a long label, in practice) can force the whole
   grid, and everything containing it up to the page itself, wider than the viewport,
   since nothing in that chain clips overflow. Found live: "Booking revenue (last 30
   days)" in the Money-at-a-glance table did exactly this on a phone screen. This lets
   a card shrink and wrap its own content instead. */
.two-col > * { min-width: 0; }
@media (max-width: 640px) { .two-col { grid-template-columns: 1fr; } }

/* Manual booking form: stay details and guest details side by side instead of one long
   narrow column — the form has enough fields that stacked-and-capped-at-480px meant a lot
   of scrolling on a screen with plenty of spare width either side. */
.mb-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 0 2rem; align-items: start; }
.mb-columns > * { min-width: 0; }
@media (max-width: 900px) { .mb-columns { grid-template-columns: 1fr; gap: 0; } }

/* Staff New booking: live price and the compulsory payment choice. */
.mb-price { margin-top: 1rem; padding: 0.75rem 0.9rem; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-alt); font-variant-numeric: tabular-nums; }
.mb-price-empty { margin: 0; font-size: 0.85rem; color: var(--muted); }
.mb-price-row { display: flex; justify-content: space-between; gap: 1rem; padding: 0.15rem 0; font-size: 0.9rem; }
.mb-price-row.discount { color: var(--pine-light); }
.mb-price-row.total { margin-top: 0.35rem; padding-top: 0.45rem; border-top: 1px solid var(--line); font-weight: 700; font-size: 1rem; }
.mb-price-row.sub { font-size: 0.82rem; color: var(--muted); }
.mb-price-warn { margin: 0.4rem 0 0; font-size: 0.82rem; color: var(--ember); }
.mb-payment { margin-top: 1.25rem; padding: 0.75rem 0.9rem 0.9rem; border: 1px solid var(--line); border-radius: 6px; }
.mb-payment legend { padding: 0 0.3rem; font-weight: 600; }
.mb-payment.needs-choice { border-color: var(--ember); }
.mb-pay-plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; }
@media (max-width: 900px) { .mb-pay-plans { grid-template-columns: 1fr; } }
.mb-pay-plan { display: flex; gap: 0.5rem; align-items: flex-start; margin: 0; padding: 0.55rem 0.65rem; border: 1px solid var(--line); border-radius: 6px; font-weight: 400; cursor: pointer; }
.mb-pay-plan input { width: auto; margin-top: 0.2rem; }
.mb-pay-plan span { display: flex; flex-direction: column; gap: 0.1rem; }
.mb-pay-plan small { color: var(--muted); font-size: 0.78rem; line-height: 1.3; }
.mb-pay-plan:has(input:checked) { border-color: var(--pine-light); box-shadow: inset 0 0 0 1px var(--pine-light); }
.mb-block { margin: 0.75rem 0; padding: 0.7rem 0.85rem; border-radius: 6px; border: 1px solid var(--ember); background: var(--tint-bad); font-size: 0.88rem; }
.mb-block.warn { border-color: #B7791F; background: var(--tint-warn); }
.mb-block strong { display: block; margin-bottom: 0.2rem; }
.mb-block .mb-override { margin-top: 0.55rem; padding-top: 0.55rem; border-top: 1px solid var(--line); }
.mb-block .mb-override label { display: flex; align-items: center; gap: 0.45rem; font-weight: 600; margin: 0; }
.mb-block .mb-override input[type=checkbox] { width: auto; }
.mb-suggest { margin: 0.6rem 0; padding: 0.6rem 0.75rem; border: 1px solid var(--line); border-radius: 6px; font-size: 0.86rem; }
.mb-suggest-row { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; padding: 0.3rem 0; border-top: 1px solid var(--line); }
.mb-suggest-row:first-of-type { border-top: 0; }
.mb-suggest-row small { color: var(--muted); display: block; }
.mb-pay-preset.selected { box-shadow: inset 0 0 0 2px var(--pine-light); font-weight: 700; }
.mb-pay-plan:focus-within { outline: 2px solid var(--pine-light); outline-offset: 1px; }

/* A settings screen made of several distinct topic cards (e.g. Booking policy) — side by
   side on a wide screen instead of one long capped-width column stacking every topic under
   the last, which made a form with this many fields a lot of scrolling to just scan. */
.settings-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; align-items: start; }
.settings-columns > .card { max-width: none; margin-bottom: 0; }
@media (max-width: 900px) { .settings-columns { grid-template-columns: 1fr; } }

/* Staff-only, drag-to-correct pitch dot (Settings > Sites > Site map) — marks where a pitch
   sits on the uploaded photo, and can be dragged to fix it when the two don't quite line up.
   The label (site name) stays hidden until hover/drag so 88 of these at once don't turn into
   solid text — see .map-position-dot-label below and placeMapPositionDots() in dashboard.js. */
.map-position-dot {
  position: absolute; width: 16px; height: 16px; margin: -8px 0 0 -8px;
  background: #E0393B; border: 2px solid #fff; border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0,0,0,0.4); cursor: grab; touch-action: none; z-index: 1;
}
.map-position-dot:hover, .map-position-dot.dragging { z-index: 2; transform: scale(1.15); }
.map-position-dot.dragging { cursor: grabbing; background: var(--amber); }
.map-position-dot.moved { background: var(--pine); }
.map-position-dot-label {
  position: absolute; bottom: 130%; left: 50%; transform: translateX(-50%);
  background: #16241D; color: #fff; font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem; font-weight: 600; padding: 0.15rem 0.4rem; border-radius: 4px;
  white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 0.1s ease;
}
.map-position-dot:hover .map-position-dot-label, .map-position-dot.dragging .map-position-dot-label { opacity: 1; }

/* Guest-facing clickable pitch marker, positioned as a % of the map image (see
   #pitch-map-image-wrap, already position:relative — see line ~488) using the same
   sites.map_x/map_y this staff preview checks. Only rendered when
   booking_settings.pitch_map_interactive is on — see renderPitchMap()/placePitchMapMarkers().
   Deliberately just a small dot, not a text pill: the map image already has the pitch number
   printed on it, so a label sitting on top of every single one just doubled it up and, on a
   phone where 88 of these are packed into a few hundred pixels, turned the whole map into an
   unreadable wall of pills. The button itself is a much bigger invisible square than the dot
   looks (see width/height vs .pitch-map-marker-dot) — a small, accurate tap target without a
   correspondingly large, obscuring visual mark. */
.pitch-map-marker {
  position: absolute; transform: translate(-50%, -50%);
  width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
  background: transparent; border: none; padding: 0; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.pitch-map-marker-dot {
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--pine); border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,0.5); transition: transform 0.1s ease, background 0.1s ease;
}
.pitch-map-marker:hover .pitch-map-marker-dot, .pitch-map-marker:focus-visible .pitch-map-marker-dot {
  transform: scale(1.4);
}
/* Same amber .pitch-chip.selected already uses, so a pitch reads as selected identically
   whether picked from the map or the summary chip below it. Slightly bigger too, so which
   pitch is currently chosen still reads at a glance once the hover/tap label (below) fades. */
.pitch-map-marker.selected .pitch-map-marker-dot {
  background: var(--amber); width: 14px; height: 14px;
}
/* Only ever the pitch's own name (no price/etc — that's what the summary chip below is for),
   shown on hover for a mouse, and briefly on tap for touch (see the .tapped class toggled in
   placePitchMapMarkers()) since a phone has no hover to reveal it otherwise. */
.pitch-map-marker-label {
  position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: 2px;
  background: #16241D; color: #fff; font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem; font-weight: 600; padding: 0.15rem 0.4rem; border-radius: 4px;
  white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 0.1s ease;
}
.pitch-map-marker:hover .pitch-map-marker-label,
.pitch-map-marker:focus-visible .pitch-map-marker-label,
.pitch-map-marker.tapped .pitch-map-marker-label { opacity: 1; }

/* Theme switch on the public booking page — sits on the dark banner, so it needs its
   own contrast rather than the button styles meant for the light card background. */
.theme-toggle-btn {
  position: absolute;
  top: 1.1rem; right: 1.2rem;
  background: rgba(255,255,255,0.12);
  color: var(--hero-on);
  border: 1px solid rgba(255,255,255,0.35);
  border-radius: 100px;
  padding: 0.35rem 0.85rem;
  font-size: 0.8rem;
  font-family: 'Public Sans', -apple-system, sans-serif;
  cursor: pointer;
}
.theme-toggle-btn:hover { background: rgba(255,255,255,0.22); }
@media (max-width: 480px) {
  .theme-toggle-btn { top: 0.6rem; right: 0.6rem; padding: 0.3rem 0.65rem; font-size: 0.72rem; }
}

/* Nimbosoft's own mark, small and quiet, top-right of every page's hero banner — this is
   the platform provider's watermark, deliberately subtle so it never competes with an
   operator's own branding (Settings > Branding) on guest-facing pages. Where a page
   already carries the mark prominently next to its own title (platform.html, setup.html —
   pages that ARE Nimbosoft, not a tenant's site), this is skipped rather than doubled up. */
.topbar-brand-mark {
  position: absolute;
  top: 0.65rem; right: 0.9rem;
  height: 18px;
  width: auto;
  opacity: 0.85;
}
@media (max-width: 480px) {
  .topbar-brand-mark { top: 0.5rem; right: 0.55rem; height: 15px; }
}

/* public/index.html is the one page that carries BOTH the mark and the theme toggle —
   grouped in one flex row instead of two independently-guessed absolute offsets, so they
   sit side by side with a real gap regardless of the toggle's text width or screen size
   (the previous fixed-offset approach left them overlapping). */
.topbar-actions {
  position: absolute;
  top: 0.65rem; right: 0.9rem;
  display: flex; align-items: center; gap: 0.5rem;
}
.topbar-actions .topbar-brand-mark { position: static; top: auto; right: auto; }
.topbar-actions .theme-toggle-btn { position: static; top: auto; right: auto; }
@media (max-width: 480px) {
  .topbar-actions { top: 0.5rem; right: 0.55rem; gap: 0.35rem; }
  .topbar-actions .topbar-brand-mark { height: 15px; }
}

/* Electric hookup — a plain checkbox got lost next to the pitch-type dropdown, and it's
   one of the questions most likely to change which pitches are even offered, so it gets
   a full-width, unmissable toggle of its own instead. */
.electric-toggle {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  width: 100%;
  margin-top: 0.75rem;
  padding: 0.75rem 1rem;
  background: var(--surface-alt);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  font-family: 'Public Sans', -apple-system, sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
  text-align: left;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.electric-toggle:hover { border-color: var(--amber); }
.electric-toggle-icon { font-size: 1.3rem; line-height: 1; }
.electric-toggle-text { flex: 1; }
.electric-toggle-state {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 700;
  padding: 0.25rem 0.65rem;
  border-radius: 100px;
  background: var(--surface);
  color: var(--muted);
}
.electric-toggle[aria-pressed="true"] { background: var(--tint-warn); border-color: var(--amber); }
.electric-toggle[aria-pressed="true"] .electric-toggle-state { background: var(--amber); color: var(--on-amber); }

/* Custom calendar date-range picker — see renderCalendar() in booking.js. Replaces two
   native date inputs specifically because those can't grey out individual closed days. */
.date-range-field { position: relative; margin-bottom: 1rem; }
.drp-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
  width: 100%; text-align: left;
  background: var(--field-bg, var(--surface)); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 0.6rem 0.8rem; font-size: 0.95rem;
  cursor: pointer; color: var(--ink);
}
.drp-trigger:hover, .drp-trigger[aria-expanded="true"] { border-color: var(--amber); }
.drp-nights-badge {
  flex-shrink: 0; font-size: 0.78rem; font-weight: 600; color: var(--amber);
  background: color-mix(in srgb, var(--amber) 14%, transparent);
  border-radius: 999px; padding: 0.2rem 0.6rem; white-space: nowrap;
}
.drp-popover {
  position: absolute; z-index: 20; top: calc(100% + 0.4rem); left: 0;
  width: min(340px, 100%);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 6px 20px var(--shadow); padding: 0.9rem;
}
.drp-nav { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.6rem; }
.drp-nav button {
  width: 32px; height: 32px; padding: 0; border-radius: 6px;
  background: var(--surface-alt); border: 1px solid var(--line); cursor: pointer; font-size: 1.1rem;
}
.drp-nav button:disabled { opacity: 0.35; cursor: default; }
.drp-month-label { font-family: 'Fraunces', serif; font-weight: 600; font-size: 1rem; }
.drp-weekdays, .drp-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.drp-weekdays span {
  text-align: center; font-size: 0.7rem; color: var(--muted); text-transform: uppercase;
  font-family: 'IBM Plex Mono', monospace; padding-bottom: 0.3rem;
}
.drp-day {
  aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 6px; background: transparent; color: var(--ink);
  font-size: 0.85rem; cursor: pointer; font-family: 'IBM Plex Mono', monospace;
}
.drp-day:hover:not(:disabled) { background: var(--surface-alt); }
.drp-day:disabled { color: var(--line); cursor: default; text-decoration: line-through; }
.drp-day.drp-empty { visibility: hidden; }
.drp-day.drp-today { box-shadow: inset 0 0 0 1px var(--amber); }
.drp-day.drp-in-range { background: var(--tint-warn); border-radius: 0; }
.drp-day.drp-range-start { background: var(--amber); color: var(--on-amber); font-weight: 600; border-radius: 6px 0 0 6px; }
.drp-day.drp-range-end { background: var(--amber); color: var(--on-amber); font-weight: 600; border-radius: 0 6px 6px 0; }
.drp-day.drp-range-start.drp-range-end { border-radius: 6px; }
.drp-hint { font-size: 0.78rem; color: var(--muted); margin: 0.6rem 0 0; }
@media (max-width: 480px) { .drp-popover { width: min(300px, 92vw); } }

/* A real, unmissable call-out for returning guests — sits above the booking flow rather
   than as a small link inside the header, which is easy to skim past entirely. */
.manage-booking-banner {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: 0.6rem;
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.75rem 1rem;
  margin-bottom: 1.25rem;
  font-size: 0.92rem;
}
.manage-booking-banner .btn { padding: 0.4rem 0.9rem; font-size: 0.85rem; }

/* Booking steps — one screen at a time so a first-time guest isn't faced with the whole
   form (pitch, extras, questions, payment) all at once. */
.stepper { display: flex; gap: 0.4rem; margin-bottom: 1.25rem; }
.step-dot {
  flex: 1;
  text-align: center;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  padding-bottom: 0.5rem;
  border-bottom: 3px solid var(--line);
}
.step-dot .num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--surface-alt);
  color: var(--bark);
  margin-right: 0.35rem;
  font-weight: 700;
}
.step-dot.active { color: var(--pine); border-bottom-color: var(--amber); }
.step-dot.active .num { background: var(--amber); color: var(--on-amber); }
.step-dot.done { color: var(--pine-light); border-bottom-color: var(--pine-light); }
.step-dot.done .num { background: var(--pine-light); color: #fff; }
@media (max-width: 640px) {
  .step-dot .label { display: none; }
  .step-dot { padding-bottom: 0.4rem; }
}

.step-nav { display: flex; justify-content: space-between; align-items: center; margin-top: 1.5rem; gap: 0.75rem; }

/* Calendar grid */
table.cal-table { border-collapse: collapse; font-size: 0.78rem; }
table.cal-table th, table.cal-table td { border: 1px solid var(--line); padding: 0.25rem 0.4rem; text-align: center; white-space: nowrap; }
table.cal-table th.cal-pitch-col, table.cal-table td.cal-pitch-col {
  position: sticky; left: 0; background: var(--canvas-light); text-align: left; z-index: 1; min-width: 110px;
}
table.cal-table td.cal-booked { background: var(--tint-good); color: var(--pine-light); font-weight: 500; cursor: grab; }
table.cal-table td.cal-booked:active { cursor: grabbing; }
table.cal-table td.cal-booked.cal-pending { background: var(--tint-warn); color: var(--amber); }
table.cal-table td.cal-free { background: var(--canvas-light); color: var(--line); }
table.cal-table th.cal-weekend { background: var(--surface-alt); }

/* Dashboard stat cards — headline figure plus tomorrow/weekend/week breakdown,
   mirroring the four-across layout of a commercial system's main dashboard. */
.dash-stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1rem;
  margin-bottom: 1rem;
}
.dash-stat-grid > * { min-width: 0; } /* same grid-item overflow trap as .two-col above */
.dash-stat {
  background: var(--canvas-light);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
}
/* Opens today's printable list for that category (see statCard() in dashboard.js) — only
   the four with somewhere to actually go get the pointer/hover treatment. */
.dash-stat[role="button"] { cursor: pointer; transition: box-shadow 0.15s ease, transform 0.15s ease; }
.dash-stat[role="button"]:hover { box-shadow: 0 3px 10px var(--shadow); transform: translateY(-1px); }
.dash-stat[role="button"]:focus-visible { outline: 2px solid var(--pine); outline-offset: 2px; }
.dash-stat .label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bark);
}
.dash-stat .headline {
  font-family: 'Big Shoulders Display', 'Arial Narrow', sans-serif;
  font-size: 2.1rem;
  font-weight: 700;
  color: var(--pine);
  line-height: 1.1;
  margin: 0.2rem 0 0.5rem;
}
.dash-stat .breakdown {
  display: flex;
  gap: 0.9rem;
  border-top: 1px dashed var(--line);
  padding-top: 0.5rem;
}
.dash-stat .breakdown div { font-size: 0.72rem; color: var(--bark); }
.dash-stat .breakdown strong {
  display: block;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.95rem;
  color: var(--ink);
  font-weight: 600;
}

/* Each of the four headline stats gets its own tint from the existing palette (not a
   new accent), so the row reads at a glance rather than as four identical cards —
   the same tinted-fill language already used for status tags/rows, not a new pattern. */
.dash-stat.arrivals {
  background: color-mix(in srgb, var(--pine) 12%, var(--canvas-light));
  border-color: color-mix(in srgb, var(--pine) 30%, var(--line));
}
.dash-stat.arrivals .headline { color: var(--pine); }
.dash-stat.arrivals .breakdown { border-top-color: color-mix(in srgb, var(--pine) 30%, var(--line)); }

.dash-stat.departures {
  background: color-mix(in srgb, var(--dusk) 12%, var(--canvas-light));
  border-color: color-mix(in srgb, var(--dusk) 30%, var(--line));
}
.dash-stat.departures .headline { color: var(--dusk); }
.dash-stat.departures .breakdown { border-top-color: color-mix(in srgb, var(--dusk) 30%, var(--line)); }

.dash-stat.onsite {
  background: color-mix(in srgb, var(--clay) 14%, var(--canvas-light));
  border-color: color-mix(in srgb, var(--clay) 32%, var(--line));
}
.dash-stat.onsite .headline { color: var(--clay); }
.dash-stat.onsite .breakdown { border-top-color: color-mix(in srgb, var(--clay) 32%, var(--line)); }

.dash-stat.occupancy {
  background: color-mix(in srgb, var(--amber) 14%, var(--canvas-light));
  border-color: color-mix(in srgb, var(--amber) 32%, var(--line));
}
.dash-stat.occupancy .headline { color: var(--amber); }
.dash-stat.occupancy .breakdown { border-top-color: color-mix(in srgb, var(--amber) 32%, var(--line)); }

.dash-stat.owed {
  background: color-mix(in srgb, var(--ember) 12%, var(--canvas-light));
  border-color: color-mix(in srgb, var(--ember) 30%, var(--line));
  height: 100%;
}
.dash-stat.owed .headline { color: var(--ember); }
.dash-stat.owed .breakdown { border-top-color: color-mix(in srgb, var(--ember) 30%, var(--line)); }
/* Nothing owed: an all-clear, not a warning. */
.dash-stat.owed.all-clear { background: var(--tint-good); border-color: color-mix(in srgb, var(--pine-light) 30%, var(--line)); }
.dash-stat.owed.all-clear .headline { color: var(--pine-light); }
.dash-stat.owed.all-clear .breakdown { border-top-color: var(--line); }

/* Settings > Overview — launcher tiles for every settings screen, both the ones with their
   own sub-tab here (Branding, Booking policy, Guest WiFi, Staff & access, Your account) and
   the low-frequency ones folded out of the main sidebar (Statics, Channels, Plates, Reviews,
   Import/Export, System, Setup). */
.settings-tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 0.75rem;
  margin-top: 0.75rem;
}
/* Groups the tile grid above into labelled sections (Property, Guest experience, etc.) so a
   long flat list of everything the app configures reads as a few short, findable lists instead. */
.settings-section-title {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.09em;
  font-weight: 600; color: var(--muted);
  margin: 1.25rem 0 0;
}
.settings-section-title:first-of-type { margin-top: 0.75rem; }
.settings-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.25rem;
  text-align: left;
  background: var(--canvas);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
  cursor: pointer;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
  width: auto;
}
.settings-tile:hover { box-shadow: 0 3px 10px var(--shadow); transform: translateY(-1px); }
.settings-tile:focus-visible { outline: 2px solid var(--pine); outline-offset: 2px; }
.settings-tile strong { font-family: 'Fraunces', serif; font-size: 0.98rem; }
.settings-tile span { font-size: 0.78rem; color: var(--muted); }

/* Today's customizable dashboard widgets — a 2-column grid; each widget spans 1 or 2
   columns per its data-width, reorderable/resizable while .editing (see
   initDashboardCustomize() in dashboard.js). */
.dash-widget-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  align-items: stretch; /* a pair in the same row matches height, not each growing on its own */
}
.dash-widget { grid-column: span 1; min-width: 0; display: flex; flex-direction: column; }
.dash-widget[data-width="2"] { grid-column: span 2; }
.dash-widget > .card,
.dash-widget > [id$="-card"] {
  margin-bottom: 0; /* the grid's own gap replaces .card's margin-bottom */
  width: 100%;
  flex: 1; /* fills the row's stretched height, so a shorter card still matches its taller row-mate */
}

/* A widget with a lot to show (a long list, a full week of arrivals) scrolls inside its
   own card once it hits a sensible height, rather than growing the card — and its
   row-mate's height along with it — down the page. "See more" is a click into the full
   page (Bookings, Diary, the booking itself), same as Outstanding balances and Latest
   bookings already do; the dashboard box only ever needs to show a preview. */
#dash-coming-days, #dash-money, #dash-latest, #dash-recent-emails,
#arrivals-list, #departures-list {
  max-height: 360px;
  overflow-y: auto;
}

.dash-widget-toolbar { display: none; }
.dash-widget-grid.editing .dash-widget {
  outline: 1px dashed color-mix(in srgb, var(--pine) 40%, transparent);
  outline-offset: 4px;
  border-radius: var(--radius);
}
.dash-widget-grid.editing .dash-widget-toolbar {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  margin-bottom: 0.4rem;
}
.dash-widget-toolbar button {
  width: auto;
  padding: 0.2rem 0.55rem;
  font-size: 0.76rem;
  background: var(--canvas-light);
  border: 1px solid var(--line);
  border-radius: 4px;
  cursor: pointer;
  color: var(--bark);
  font-family: 'IBM Plex Mono', monospace;
}
.dash-widget-toolbar button:hover { border-color: var(--pine); color: var(--pine); }
.dash-widget-toolbar button:disabled { opacity: 0.35; cursor: default; }
.dash-widget-toolbar button:disabled:hover { border-color: var(--line); color: var(--bark); }

@media (max-width: 720px) {
  .dash-widget-grid { grid-template-columns: 1fr; }
  .dash-widget[data-width="2"] { grid-column: span 1; }
}

/* Occupancy bar used in the coming-days strip */
.occ-bar {
  height: 6px;
  background: var(--surface-alt);
  border-radius: 100px;
  overflow: hidden;
  margin-top: 3px;
}
.occ-bar span { display: block; height: 100%; background: var(--pine-light); border-radius: 100px; }
.occ-bar span.high { background: var(--ember); }
.occ-bar span.mid { background: var(--amber); }

/* Blocked pitches — deliberately taken out of use by staff (maintenance, owner use, etc).
   Visually distinct from "booked" so it's obvious at a glance which is which. */
table.cal-table td.cal-blocked {
  background: repeating-linear-gradient(45deg, var(--ink) 0 6px, color-mix(in srgb, var(--ink) 72%, var(--canvas)) 6px 12px);
  color: var(--canvas);
  font-weight: 700;
  cursor: pointer;
}
/* ---- Mobile / tablet ----
   The dashboard is used on a phone at the gate as much as on a desktop in the office,
   so tables scroll horizontally rather than squashing, tap targets grow, and the
   multi-column layouts collapse to single column. */
@media (max-width: 820px) {
  body { font-size: 15px; }
  .wrap { padding: 0.75rem; }
  .card { padding: 0.9rem; }
  .two-col { grid-template-columns: 1fr !important; }
  .dash-stat-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
  .dash-stat .headline { font-size: 1.7rem; }

  /* Tables scroll sideways instead of crushing columns to unreadable widths */
  .card table { display: block; overflow-x: auto; white-space: nowrap; }

  /* Bigger tap targets — 44px is the usual minimum for reliable finger taps */
  button.btn { padding: 0.75rem 1.1rem; min-height: 44px; }
  input, select, textarea { padding: 0.7rem; font-size: 16px; } /* 16px stops iOS zooming on focus */
  nav.tabs { gap: 0.4rem; }
  nav.tabs button { padding: 0.55rem 0.85rem; min-height: 40px; }

  header.topbar { padding: 0.55rem 0.7rem 1rem 0.7rem; }
  header.topbar h1 { font-size: 0.92rem; }
  .site-logo { height: 20px; }
  .site-brand-row { gap: 0.4rem; }

  /* Charts on a phone: the pitch column shrinks further so more days fit without
     scrolling, while day cells stay close to their desktop size — they're what's
     actually being tapped and read. */
  .chart-pitch { width: 42px; max-width: 42px; padding: 0 0.25rem; }
  .chart-day { width: 30px; min-width: 30px; height: 28px; }
  .chart-bar { font-size: 0.64rem; line-height: 24px; }
}

@media (max-width: 480px) {
  .dash-stat-grid { grid-template-columns: 1fr 1fr; }
  .dash-stat { padding: 0.75rem; }
  .dash-stat .headline { font-size: 1.5rem; }
  .dash-stat .breakdown { gap: 0.5rem; }
  .dash-stat .breakdown div { font-size: 0.66rem; }
}

/* Admin header: title on the left, search + quick actions on the right, one row — keeps
   the header the same height it always was rather than growing a second row underneath. */
.admin-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.admin-header-title { min-width: 0; } /* lets the title/subtitle truncate instead of forcing the row to wrap */

.header-toolbar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  /* Pulled in from the edge so it doesn't crowd the Nimbosoft mark, which sits absolutely
     positioned in the header's top-right corner (see .topbar-brand-mark) rather than in
     this flex row. Reset to 0 once the toolbar wraps onto its own row below the title,
     where the mark is no longer beside it. */
  margin-right: 2.2rem;
}
.header-search { position: relative; flex: 1 1 160px; max-width: 300px; }
.header-search input {
  width: 100%;
  background: rgba(255,255,255,0.14);
  border: 1px solid rgba(255,255,255,0.4);
  color: var(--hero-on);
  border-radius: 100px;
  padding: 0.4rem 0.9rem;
  font-family: 'Public Sans', -apple-system, sans-serif;
  font-size: 0.85rem;
}
.header-search input::placeholder { color: var(--hero-on); opacity: 0.68; }
.header-search input:focus { outline: 2px solid rgba(255,255,255,0.6); outline-offset: 1px; }
.header-search .autocomplete-results { color: var(--ink); }
/* Smaller than the site-wide default — this button sits in a header that's now
   deliberately compact, and the full-size button was the one element in it that hadn't
   shrunk to match. */
.header-toolbar .btn { padding: 0.4rem 0.9rem; font-size: 0.85rem; }
@media (max-width: 640px) {
  .admin-header-row { justify-content: flex-start; }
  /* Kept as one row with the search bar (not wrapped onto its own full-width row below) —
     the icon-only button next to it is small enough that there's no need to give up the
     row, and stacking cost more header height than a phone screen can spare. */
  .header-toolbar { width: 100%; margin-top: 0.45rem; margin-right: 0; flex-wrap: nowrap; }
  .header-search { flex: 1 1 auto; max-width: none; }
  /* Icon-only: the label would either force a second row or truncate unreadably at this
     width. The tooltip/aria-label still carry the label for anyone who needs it. */
  #header-new-booking-btn .btn-label { display: none; }
  #header-new-booking-btn { flex-shrink: 0; padding: 0.6rem 0.85rem; min-width: 44px; }
}

/* Autocomplete dropdown for finding existing customers when taking a booking */
.autocomplete-results {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 50;
  background: var(--canvas-light);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  max-height: 260px;
  overflow-y: auto;
}
.autocomplete-results .ac-item {
  padding: 0.55rem 0.7rem;
  cursor: pointer;
  border-bottom: 1px solid var(--line);
}
.autocomplete-results .ac-item:last-child { border-bottom: none; }
.autocomplete-results .ac-item:hover,
.autocomplete-results .ac-item.active { background: var(--surface-alt); }
.autocomplete-results .ac-name { font-weight: 600; }
.autocomplete-results .ac-meta { font-size: 0.78rem; color: var(--bark); }
.linked-customer {
  background: var(--tint-good);
  border: 1px solid var(--pine-light);
  border-radius: var(--radius);
  padding: 0.6rem 0.75rem;
  margin-top: 0.5rem;
  font-size: 0.85rem;
}

table.cal-table td.cal-owing {
  background: var(--tint-warn);
  color: var(--amber);
  font-weight: 700;
}
table.cal-table td.cal-overdue {
  background: var(--tint-bad);
  color: var(--ember);
  font-weight: 700;
}
/* Solid fill rather than a soft tint — nothing paid at all reads as visually heavier
   than "just the balance left", the same escalation the charts use. */
table.cal-table td.cal-owing-all {
  background: var(--chart-owing-all);
  color: var(--chart-text);
  font-weight: 700;
}
table.cal-table td.cal-overdue-all {
  background: var(--chart-overdue-all);
  color: var(--chart-text);
  font-weight: 700;
}


/* ---- Charts: whole-site booking grid ----
   A wide scrolling grid: pitch rows down the side, days across the top, booking bars
   spanning their dates. Sticky headers so the pitch name and date stay visible while
   scrolling a full year. */
.chart-table { border-collapse: separate; border-spacing: 0; font-size: 0.72rem; }
.chart-table th, .chart-table td { border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }

.chart-table thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--canvas-light);
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 600; font-size: 0.66rem;
  padding: 2px 0; text-align: center; white-space: nowrap;
}
.chart-table thead tr:first-child th { top: 0; }
.chart-table thead tr:nth-child(2) th { top: 20px; }

/* Pitch name column stays put when scrolling sideways. Fixed narrow width (rather than
   sizing to the longest name) so the booking bars — the thing actually being read — get
   the space instead; long names still reach the reader via the title tooltip. */
.chart-pitch {
  position: sticky; left: 0; z-index: 2;
  background: var(--canvas-light);
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 600;
  padding: 0 0.35rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  border-right: 2px solid var(--line);
  width: 56px; max-width: 56px;
}
.chart-table thead .chart-pitch { z-index: 4; }

/* --bark rather than --pine: pine is the same green family as --chart-paid, which sits in
   this same table — bark is a distinct muted hue in every theme, so it reads as a structural
   divider rather than looking like another (or the same) booking state. */
.chart-category-row td {
  background: var(--bark); color: var(--on-bark);
  font-family: 'Big Shoulders Display', 'Arial Narrow', sans-serif; font-weight: 700;
  padding: 0.25rem 0.5rem; position: sticky; left: 0;
}

.chart-day { width: 34px; min-width: 34px; height: 26px; padding: 0; position: relative; }
.chart-day.chart-weekend { background: rgba(44, 85, 64, 0.06); }
.chart-day.chart-today { box-shadow: inset 2px 0 0 var(--ember); }
.chart-day:hover { background: rgba(198, 142, 23, 0.15); cursor: crosshair; }

/* Booking bars sit on top of the day cells */
.chart-bar {
  position: absolute; top: 2px; bottom: 2px; left: 1px;
  border-radius: 3px; padding: 0 4px;
  font-size: 0.7rem; line-height: 22px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: grab; z-index: 1; color: #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
}
.chart-bar:hover { filter: brightness(1.1); z-index: 5; }
.chart-bar:active { cursor: grabbing; }
.chart-bar.dragging { opacity: 0.45; }

/* The release ("✕") button on a held/unconfirmed bar only appears on hover — hidden the rest
   of the time so a normal chart full of holds doesn't look cluttered with buttons. */
.chart-bar-label { pointer-events: none; }
.chart-bar-release {
  display: none;
  position: absolute; top: 1px; right: 1px; bottom: 1px;
  width: 16px; padding: 0; margin: 0;
  border: 0; border-radius: 3px;
  background: rgba(0,0,0,0.35); color: #fff;
  font-size: 0.7rem; line-height: 1; cursor: pointer;
}
.chart-bar-release:hover { background: rgba(0,0,0,0.6); }
.chart-unconfirmed:hover .chart-bar-release { display: block; }

.chart-paid       { background: var(--chart-paid); color: var(--chart-text); }
.chart-owing      { background: var(--chart-owing); color: #201C16; }
.chart-owing-all  { background: var(--chart-owing-all); color: var(--chart-text); }
.chart-overdue    { background: var(--chart-overdue); color: var(--chart-text); }
.chart-overdue-all{ background: var(--chart-overdue-all); color: var(--chart-text); }
.chart-unconfirmed{ background: var(--chart-unconfirmed); color: var(--chart-text); }
.chart-pending    { background: var(--chart-pending); color: var(--chart-text); }
/* Blocked: bold dark stripes (light ones in dark mode) so a held-back pitch stands out from every booking colour. */
.chart-blocked    { background: repeating-linear-gradient(45deg, var(--ink) 0 6px, color-mix(in srgb, var(--ink) 72%, var(--canvas)) 6px 12px); color: var(--canvas); font-weight: 700; box-shadow: 0 0 0 2px var(--ink); }
.chart-external   { background: var(--chart-external); color: var(--chart-text); }
/* Members' club bookings (see /charts in routes/admin.js) — purple, since blue already means
   awaiting approval / other channel. Only paid-up or balance-not-yet-due bookings take it;
   anything overdue keeps its warning colour. */
.chart-member-paid  { background: var(--chart-member, #6A4C9C); color: #FFFFFF; }
.chart-member-owing { background: var(--chart-member-soft, #B6A5DB); color: #24173F; }
.chart-member::before { content: '★ '; }
.chart-key.chart-member-paid::before, .chart-key.chart-member-owing::before { content: none; }

.chart-key {
  display: inline-block; width: 14px; height: 14px;
  border-radius: 3px; vertical-align: middle; margin-right: 3px;
}

/* Where a booking can be dropped */
.chart-day.drop-ok { background: rgba(44, 85, 64, 0.3); }
.chart-day.drop-bad { background: rgba(181, 72, 44, 0.3); }

/* Charts on a large monitor: there's room to spare, so give it to the booking bars —
   wider, taller cells make guest names and date boundaries easier to read at a glance
   instead of squinting at a grid sized for a laptop. */
@media (min-width: 1400px) {
  .chart-pitch { width: 76px; max-width: 76px; }
  .chart-day { width: 46px; min-width: 46px; height: 32px; }
  .chart-bar { font-size: 0.78rem; line-height: 28px; padding: 0 6px; }
  .chart-table { font-size: 0.78rem; }
  .chart-table thead th { font-size: 0.7rem; }
}

/* --- Seasons: paint-the-year calendar, Rules table, rate grid --- */
/* Same pill-toggle look reused generically (.subtabs/.subtab) wherever a tab has too many
   unrelated settings/cards to show at once — Settings and Setup below both use the generic
   version and its shared click handler; Seasons keeps its own bespoke one since switching
   sub-view there also re-renders the Rules table. */
.season-subtabs, .subtabs { display: flex; gap: 0.25rem; flex-wrap: wrap; margin-bottom: 1rem; }
.season-subtab, .subtab {
  /* A visible fill even at rest, not just a border — an outline-only button can blend
     into a custom page background the theme-contrast check elsewhere can't fully guard
     against for every combination, and it shouldn't take clicking a button to see it. */
  background: var(--surface-alt); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 0.35rem 0.8rem; font-family: 'IBM Plex Mono', monospace; font-size: 0.78rem;
  color: var(--muted); cursor: pointer;
}
.season-subtab.active, .subtab.active { background: var(--pine); color: var(--on-pine); border-color: var(--pine); font-weight: 600; }

.season-pill {
  --pill-color: #5B6154;
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: var(--surface); border: 2px solid var(--line); border-radius: 999px;
  padding: 0.3rem 0.7rem 0.3rem 0.5rem; font-size: 0.82rem; font-weight: 600; color: var(--ink);
  cursor: pointer;
}
.season-pill.armed { border-color: var(--pill-color); box-shadow: 0 0 0 2px color-mix(in srgb, var(--pill-color) 30%, transparent); }
.season-pill-swatch { width: 14px; height: 14px; border-radius: 50%; background: var(--pill-color); display: inline-block; }
.season-pill-edit { opacity: 0.5; font-size: 0.9rem; padding: 0 0.1rem; }
.season-pill-edit:hover { opacity: 1; }
.season-pill.eraser { --pill-color: transparent; color: var(--muted); }
.season-pill.eraser .season-pill-swatch { background: var(--surface-alt); border: 1px dashed var(--line); }

.season-tag {
  display: inline-block; padding: 0.15rem 0.55rem; border-radius: 999px;
  color: #fff; font-size: 0.75rem; font-weight: 600;
}

table.season-calendar { border-collapse: separate; border-spacing: 0; font-size: 0.72rem; }
table.season-calendar th, table.season-calendar td { border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.cal-month-label {
  position: sticky; left: 0; z-index: 2; background: var(--canvas-light);
  font-family: 'IBM Plex Mono', monospace; font-size: 0.7rem; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--bark); padding: 0 0.6rem; white-space: nowrap; text-align: left;
}
.cal-dow {
  position: sticky; top: 0; z-index: 1; background: var(--canvas-light);
  font-family: 'IBM Plex Mono', monospace; font-size: 0.68rem; color: var(--muted);
  width: 22px; min-width: 22px; height: 22px; text-align: center; font-weight: 500;
}
.cal-day {
  width: 22px; min-width: 22px; height: 24px; text-align: center; font-size: 0.68rem;
  cursor: pointer; user-select: none; color: var(--ink);
}
.cal-day:hover { outline: 2px solid var(--amber); outline-offset: -2px; }
.cal-day-empty { background: var(--surface-sunken); }

.day-toggle-row { display: flex; gap: 2px; }
.day-toggle {
  width: 28px; height: 24px; border-radius: 3px; border: 1px solid var(--line);
  background: var(--surface); color: var(--muted); font-size: 0.66rem; cursor: pointer; padding: 0;
}
/* Arrival (pine/green) vs departure (dusk/blue) — same colour split the Today dashboard
   already uses for arrivals/departures, so the two day-toggle rows read as distinct
   controls even before anything's selected, not one ambiguous block shown twice. */
.day-toggle.arrival:hover { border-color: var(--pine); }
.day-toggle.arrival.active { background: var(--pine); border-color: var(--pine); color: var(--on-pine); font-weight: 600; }
.day-toggle.departure:hover { border-color: var(--dusk); }
.day-toggle.departure.active { background: var(--dusk); border-color: var(--dusk); color: var(--on-dusk); font-weight: 600; }
.day-toggle-actions { display: flex; gap: 0.5rem; margin-top: 0.25rem; }
.day-toggle-actions a { font-size: 0.72rem; color: var(--muted); text-decoration: underline; cursor: pointer; }
.day-toggle-actions a:hover { color: var(--ink); }

table.rate-grid-table { border-collapse: collapse; width: 100%; }
table.rate-grid-table th, table.rate-grid-table td { padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--line); text-align: left; }
table.rate-grid-table input[type="number"] { width: 6rem; }

/* ---- "Match my website" layout (Settings > Branding) --------------------------------
   Guest pages wearing the operator's own website header, footer and typography instead
   of this platform's dusk header — see applySiteChrome() in branding.js, which builds the
   nav/footer and sets --site-bg/--site-mud/--site-on-mud from the operator's colours.
   Mirrors the Nimbosoft website template (Fraunces headings, Karla body, IBM Plex Mono
   labels/buttons). Everything is scoped under .layout-website: off, nothing here applies. */
.layout-website { background: var(--site-bg, var(--canvas)); }
.layout-website header.topbar { display: none; }
.layout-website body { font-family: 'Karla', -apple-system, sans-serif; background-color: var(--site-bg, var(--canvas)); }
.layout-website h1, .layout-website h2, .layout-website h3,
.layout-website .pitch-section-title {
  font-family: 'Fraunces', Georgia, serif; font-weight: 600; letter-spacing: -0.01em;
  text-transform: none; color: var(--ink);
}
.layout-website input, .layout-website select, .layout-website textarea,
.layout-website .pitch-chip, .layout-website .electric-toggle { font-family: 'Karla', -apple-system, sans-serif; }

/* Panels, not stitched cards — the website's flat, softly rounded boxes. */
.layout-website .card { border-radius: 12px; box-shadow: none; padding: 1.5rem 1.6rem; }
.layout-website .card::before { display: none; }

.layout-website button.btn, .layout-website a.btn {
  font-family: 'IBM Plex Mono', monospace; font-size: 0.8rem; font-weight: 500;
  letter-spacing: 0.05em; text-transform: uppercase; border-radius: 2px; padding: 0.85rem 1.4rem;
}
.layout-website button.btn.secondary, .layout-website a.btn.secondary {
  background: transparent; border: 1px solid var(--ink); color: var(--ink);
}

.site-nav {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem 1.6rem;
  padding: 1rem clamp(1.25rem, 4vw, 3rem); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 100; background: var(--site-bg, var(--canvas));
  max-width: 1440px; margin-inline: auto;
}
.site-nav-mark {
  font-family: 'Fraunces', Georgia, serif; font-weight: 700; font-size: 1.05rem; text-decoration: none;
  color: var(--ink); display: flex; align-items: center; gap: 0.6rem; white-space: nowrap;
}
.site-nav-mark-icon { width: 32px; height: 32px; border-radius: 8px; flex-shrink: 0; }
.site-nav-links { display: flex; gap: 1.6rem; align-items: center; }
.site-nav-links a { color: var(--ink); text-decoration: none; font-size: 0.92rem; }
.site-nav-links a:hover { color: var(--amber); }
.site-nav-right { display: flex; align-items: center; gap: 0.6rem; }
.site-nav-cta {
  font-family: 'IBM Plex Mono', monospace; font-size: 0.76rem; letter-spacing: 0.05em; text-transform: uppercase;
  background: var(--site-mud, #16302A); color: var(--site-on-mud, #F7F1DF);
  padding: 0.65rem 1.15rem; border-radius: 2px; text-decoration: none; white-space: nowrap;
}
.site-icon { display: flex; }
.site-theme-toggle {
  display: flex; align-items: center; justify-content: center; width: 38px; height: 38px;
  background: none; border: 1px solid var(--line); border-radius: 50%; cursor: pointer; color: var(--ink); padding: 0;
}
.site-theme-toggle:hover { border-color: var(--ink); }
.site-theme-toggle .icon-sun { display: none; }
.site-theme-toggle[data-mode="dark"] .icon-sun { display: block; }
.site-theme-toggle[data-mode="dark"] .icon-moon { display: none; }
.site-menu-toggle {
  display: none; background: none; border: none; cursor: pointer; padding: 0.4rem; color: var(--ink);
  align-items: center; justify-content: center;
}
.site-nav :focus-visible { outline: 2px solid var(--pine); outline-offset: 2px; }
@media (max-width: 720px) {
  .site-menu-toggle { display: flex; }
  .site-nav-links {
    order: 3; flex-basis: 100%; flex-direction: column; align-items: flex-start; gap: 0;
    max-height: 0; overflow: hidden; opacity: 0; transition: max-height 0.25s ease, opacity 0.2s ease;
  }
  .site-nav-links a { padding: 0.8rem 0; width: 100%; border-bottom: 1px solid var(--line); }
  .site-nav-links.open { max-height: 320px; opacity: 1; margin-top: 0.4rem; }
}

.layout-website .site-footer.site-footer-website {
  background: var(--site-mud, #16302A); color: var(--site-on-mud, #F7F1DF);
  padding: 2rem clamp(1.25rem, 4vw, 3rem); margin: 2.5rem auto 0; max-width: 1440px;
  display: flex; justify-content: space-between; align-items: flex-start; gap: 1.5rem 1rem;
  flex-wrap: wrap; font-size: 0.85rem; text-align: left;
}
.site-footer-brand { display: flex; align-items: center; gap: 0.9rem; }
.site-footer-icon { width: 38px; height: 38px; object-fit: contain; flex-shrink: 0; }
.site-footer-name { display: block; font-family: 'Fraunces', Georgia, serif; font-weight: 700; }
.site-footer-tagline { display: block; opacity: 0.75; font-size: 0.82rem; margin-top: 0.15rem; }
.site-footer-links { display: flex; flex-direction: column; align-items: flex-end; gap: 0.5rem; text-align: right; margin-left: auto; }
.layout-website .site-footer-website a { color: var(--site-on-mud, #F7F1DF); text-decoration: none; }
.layout-website .site-footer-website a:hover { text-decoration: underline; }
.layout-website .site-footer-website .site-footer-credit { opacity: 0.6; font-size: 0.78rem; }
/* Company name, number and registered office — small print, full width under everything. */
.site-footer-legal { margin: 0.6rem auto 0; max-width: 60rem; font-size: 0.72rem; line-height: 1.5; opacity: 0.8; }
.layout-website .site-footer-website .site-footer-legal {
  flex-basis: 100%; max-width: none; margin: 0; padding-top: 1rem; opacity: 0.55;
  border-top: 1px solid color-mix(in srgb, var(--site-on-mud, #F7F1DF) 15%, transparent);
}

/* Dashboard search results — a big panel under the header (see runGlobalSearch() in
   dashboard.js), fixed to the viewport so the header's overflow can't clip it. */
.search-panel {
  position: fixed; left: 50%; transform: translateX(-50%); z-index: 150;
  width: min(1000px, calc(100vw - 2rem)); overflow-y: auto;
  background: var(--canvas-light); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(0,0,0,0.22); padding: 0.4rem 0.9rem 0.9rem;
}
.search-panel-head {
  position: sticky; top: -0.4rem; background: var(--canvas-light); z-index: 1;
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  padding: 0.6rem 0 0.5rem; border-bottom: 1px solid var(--line); font-size: 0.85rem; color: var(--muted);
}
.search-panel-close { background: none; border: 1px solid var(--line); border-radius: 6px; color: var(--ink); cursor: pointer; padding: 0.2rem 0.55rem; }
.search-panel-group {
  font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted);
  margin: 1rem 0 0.35rem; font-family: 'IBM Plex Mono', monospace; font-weight: 600;
}
.search-panel-more { text-transform: none; letter-spacing: 0; font-weight: 400; margin-left: 0.4rem; }
.search-panel-empty { color: var(--muted); padding: 1rem 0.2rem; margin: 0; }
.search-row {
  display: block; width: 100%; text-align: left; font: inherit; color: inherit; cursor: pointer;
  background: none; border: none; border-bottom: 1px solid var(--line); padding: 0.6rem 0.4rem; border-radius: 4px;
}
.search-row:hover, .search-row:focus-visible { background: var(--surface-alt); outline: none; }
.search-row-title { font-weight: 600; display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; }
/* Announcement bar (Settings > Branding & website): the booking pages' own copy and the
   settings preview. The text is in the track twice so sliding it by half loops seamlessly. */
.announce-bar { display: block; overflow: hidden; background: var(--pine); color: var(--on-pine); font-size: 0.9rem; font-weight: 600; text-decoration: none; }
a.announce-bar:hover, a.announce-bar:focus-visible { text-decoration: underline; }
a.announce-bar:focus-visible { outline: 2px solid var(--on-pine); outline-offset: -4px; }
.announce-track { display: inline-flex; white-space: nowrap; padding: 0.5rem 0; animation: announce-scroll var(--announce-duration, 22s) linear infinite; }
/* Each copy is at least the full bar wide with the text centred in it, so the message
   scrolls in from the right edge, through the middle and off the left — not just across
   the left-hand side, which is all a short message covered when the copies sat end to end. */
.announce-track span { flex: 0 0 auto; box-sizing: border-box; min-width: 100vw; text-align: center; padding: 0 3rem; }
.announce-bar:hover .announce-track, .announce-bar:focus-within .announce-track { animation-play-state: paused; }
@keyframes announce-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  .announce-track { animation: none; white-space: normal; display: block; text-align: center; padding: 0.5rem 1rem; }
  .announce-track span { min-width: 0; padding: 0; }
  .announce-track span + span { display: none; }
}
.eq-bulk { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; padding: 0.6rem 0; border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--surface); z-index: 1; }
.eq-row { display: flex; gap: 0.6rem; align-items: flex-start; }
.eq-row > input[type=checkbox] { width: auto; margin-top: 0.3rem; }
.no-electric-note { margin: 0.75rem 0 0; padding: 0.6rem 0.8rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-sunken); font-size: 0.9rem; }
.settings-search { margin: 0.25rem 0 1.25rem; }
.settings-search input { font-size: 1rem; }
.settings-search-results { margin-top: 0.4rem; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
.settings-search-empty { margin: 0; padding: 0.7rem 0.9rem; font-size: 0.88rem; color: var(--muted); }
.settings-hit { animation: settings-hit 2.4s ease-out; border-radius: 6px; }
@keyframes settings-hit { 0%, 35% { box-shadow: 0 0 0 3px var(--pine-light); background: var(--surface-alt); } 100% { box-shadow: 0 0 0 3px transparent; } }
@media (prefers-reduced-motion: reduce) { .settings-hit { animation: none; box-shadow: 0 0 0 3px var(--pine-light); } }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.search-row-sub { font-weight: 400; font-size: 0.82rem; color: var(--muted); }
.search-row-status { font-size: 0.7rem; font-weight: 600; text-transform: capitalize; padding: 0.1rem 0.5rem; border-radius: 100px; background: var(--surface-alt); }
.search-row-meta { font-size: 0.84rem; color: var(--muted); margin-top: 0.15rem; }
.search-row-match { font-size: 0.82rem; margin-top: 0.25rem; }
.search-panel mark { background: color-mix(in srgb, var(--amber) 35%, transparent); color: inherit; border-radius: 2px; padding: 0 1px; }

/* --- Team: My time, Cleaning, Messages, Timesheets (admin/js/team.js) --- */
.nav-count { display: inline-block; min-width: 1.3em; padding: 0 0.4em; margin-left: 0.35rem; border-radius: 100px; background: var(--amber); color: var(--on-amber); font-size: 0.72rem; font-weight: 700; line-height: 1.5; text-align: center; }
.nav-count[hidden] { display: none; }
.team-urgent { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-bottom: 1rem; padding: 0.7rem 0.9rem; border-radius: var(--radius); background: var(--tint-bad); color: var(--ink); border: 1px solid var(--ember); }
.team-urgent[hidden] { display: none; }
.team-urgent span { flex: 1 1 14rem; }
.btn.btn-sm { padding: 0.3rem 0.65rem; font-size: 0.8rem; }
.mt-small { font-size: 0.82rem; color: var(--muted); }
.mt-small.flag { color: var(--ember); }
.team-h3 { font-size: 0.95rem; margin: 1.2rem 0 0.3rem; }
.team-check { display: inline-flex; align-items: center; gap: 0.45rem; font-weight: 400; font-size: 0.88rem; }
.team-check input { width: auto; margin: 0; }
.team-form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 0.6rem 0.9rem; }
.team-form-grid .span-2 { grid-column: span 2; }
@media (max-width: 480px) { .team-form-grid .span-2 { grid-column: auto; } }
.mt-inline { display: flex; flex-wrap: wrap; gap: 0.5rem 0.75rem; align-items: center; margin-top: 0.6rem; }
.mt-inline[hidden] { display: none; }
.mt-inline input { width: auto; }
.mt-inline label { margin: 0; }
.team-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.3rem; }
.team-chip { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.75rem; padding: 0.15rem 0.55rem; border-radius: 100px; background: var(--surface-alt); color: var(--ink); white-space: nowrap; }
.team-chip.good { background: var(--tint-good); color: var(--pine-light); }
.team-chip.warn { background: var(--tint-warn); color: var(--ink); }
.team-chip.bad { background: var(--tint-bad); color: var(--ember); }
.team-table-wrap { overflow-x: auto; }
.team-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.team-table th, .team-table td { padding: 0.45rem 0.5rem; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
.team-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.team-table tfoot th { border-bottom: 0; border-top: 2px solid var(--ink); }

/* My time: a status line and a grid of big job buttons, thumb-sized on a phone. */
.mt-status { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; font-size: 1.15rem; padding: 0.8rem 1rem; border-radius: var(--radius); background: var(--surface-sunken); border: 1px solid var(--line); }
.mt-status.on { background: var(--tint-good); border-color: var(--pine-light); }
.mt-dot { width: 0.7rem; height: 0.7rem; border-radius: 50%; background: var(--pine-light); flex: none; }
.mt-dot.off { background: var(--muted); opacity: 0.5; }
.mt-elapsed { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 700; }
.mt-hint { font-size: 0.85rem; color: var(--muted); margin: 0.9rem 0 0.5rem; }
.mt-types { display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: 0.6rem; }
.mt-type { display: flex; flex-direction: column; align-items: flex-start; gap: 0.2rem; min-height: 4.2rem; padding: 0.75rem 0.85rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--field-bg); color: var(--ink); font: inherit; font-weight: 600; font-size: 1rem; text-align: left; cursor: pointer; }
.mt-type small { font-weight: 400; font-size: 0.75rem; color: var(--muted); }
.mt-type:hover { border-color: var(--pine-light); }
.mt-type:focus-visible { outline: 3px solid var(--amber); outline-offset: 2px; }
.mt-type.current { background: var(--pine); color: var(--on-pine); border-color: var(--pine); }
.mt-type.current small { color: var(--on-pine); opacity: 0.85; }
.mt-type:disabled { opacity: 0.6; cursor: progress; }
.mt-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.9rem; }
.mt-forgotten { margin-top: 0.75rem; padding: 0.75rem 0.9rem; border-radius: var(--radius); background: var(--tint-warn); }
.mt-forgotten p { margin: 0; }
.mt-weektotal { margin: 0; }

/* Cleaning board */
.cl-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 0.6rem; }
.cl-head h2 { margin: 0; }
.cl-datenav { display: flex; gap: 0.35rem; align-items: center; max-width: 100%; }
.cl-datenav input { width: auto; min-width: 0; flex: 1 1 8rem; }
@media (max-width: 480px) { .cl-datenav { width: 100%; } .cl-datenav .btn { padding-inline: 0.6rem; } }
.cl-progress { display: flex; align-items: center; gap: 0.75rem; margin: 0.9rem 0 0.6rem; font-size: 0.9rem; }
.cl-progress:empty { display: none; }
.cl-bar { flex: 1; height: 0.5rem; border-radius: 100px; background: var(--surface-alt); overflow: hidden; }
.cl-bar span { display: block; height: 100%; background: var(--pine-light); }
.cl-job { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.6rem; padding: 0.75rem 0; border-top: 1px solid var(--line); }
.cl-job-main { flex: 1 1 14rem; min-width: 0; }
.cl-job-title { font-weight: 600; }
.cl-job.priority .cl-job-title::before { content: "Priority · "; color: var(--amber); }
.cl-job.status-done .cl-job-title { text-decoration: line-through; color: var(--muted); }
.cl-job.status-in_progress .cl-job-title::after { content: " · in progress"; font-weight: 400; color: var(--pine-light); }
.cl-job-actions { display: flex; gap: 0.4rem; align-items: flex-start; flex-wrap: wrap; }
.cl-issues { margin-top: 0.75rem; }
.cl-issue { display: flex; justify-content: space-between; gap: 0.6rem; align-items: flex-start; padding: 0.6rem 0; border-top: 1px solid var(--line); font-size: 0.9rem; }
.cl-issue.fixed { color: var(--muted); }
.cl-area { display: flex; flex-wrap: wrap; gap: 0.45rem; align-items: center; padding: 0.4rem 0; border-top: 1px solid var(--line); }
.cl-area input[type="text"] { width: auto; flex: 1 1 9rem; }
.cl-sites { display: flex; flex-direction: column; gap: 0.5rem; }
.cl-section { border: 1px solid var(--line); border-radius: var(--radius); padding: 0.5rem 0.75rem; display: grid; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: 0.3rem 0.75rem; }
.cl-section legend { font-weight: 600; font-size: 0.85rem; padding: 0 0.3rem; }

/* Messages */
.msg-compose-row { display: flex; flex-wrap: wrap; gap: 0.6rem 1rem; align-items: flex-end; margin-top: 0.6rem; }
.msg-compose-row select { width: auto; }
.msg { padding: 0.75rem 0; border-top: 1px solid var(--line); }
.msg:first-child { border-top: 0; }
.msg.pinned { background: var(--surface-sunken); padding-inline: 0.75rem; border-radius: var(--radius); }
.msg.urgent .msg-body { font-weight: 600; }
.msg-head { display: flex; flex-wrap: wrap; gap: 0.4rem 0.6rem; align-items: center; }
.msg-body { margin: 0.35rem 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.msg-foot { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }

/* Timesheets */
.tsh-periods { display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; }
.tsh-periods select { width: auto; }
.tsh-period { padding: 0.35rem 0.75rem; border: 1px solid var(--line); border-radius: 100px; background: var(--field-bg); color: var(--ink); font: inherit; font-size: 0.85rem; cursor: pointer; }
.tsh-period.active { background: var(--pine); border-color: var(--pine); color: var(--on-pine); }
.tsh-period:focus-visible { outline: 3px solid var(--amber); outline-offset: 2px; }
.tsh-rangebar { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 0.6rem; margin: 1rem 0 0.6rem; }
.tsh-rangebar-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.tsh-on-now { display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; margin-bottom: 0.75rem; }
.linkbtn { border: 0; background: none; padding: 0; color: inherit; text-decoration: underline; font: inherit; cursor: pointer; }
.tsh-detail > td { background: var(--surface-sunken); }
.tsh-lines, .tsh-entries { margin: 0.4rem 0 0.8rem; background: var(--surface); }
.tsh-daystart td { border-top: 2px solid var(--line); }
.tsh-worker { border-top: 1px solid var(--line); padding: 0.55rem 0; }
.tsh-worker summary { cursor: pointer; display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.tsh-worker[open] summary { margin-bottom: 0.6rem; }
.tsh-rate { margin-left: auto; font-variant-numeric: tabular-nums; }
.tsh-special { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: 0.4rem 0.75rem; }
.tsh-special label { font-weight: 400; font-size: 0.82rem; display: flex; flex-direction: column; gap: 0.2rem; }

.te-dialog { width: min(34rem, calc(100vw - 32px)); border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); color: var(--ink); padding: 1.1rem; }
.te-dialog::backdrop { background: rgba(0, 0, 0, 0.45); }
.te-dialog h2 { margin-top: 0; }
.te-dialog [hidden] { display: none; }
.te-buttons { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.9rem; }

/* Booking page while online booking is switched off */
.cc-price-note { margin: 0.1rem 0 0; font-size: 0.8rem; color: var(--muted); }
.bookings-closed[hidden] { display: none; }
.bookings-closed { text-align: center; }
.bookings-closed p { max-width: 36rem; margin-inline: auto; }

/* Platform admin: Website visitors card (see loadVisitorStats() in js/platform.js) */
.vs-periods { display: flex; gap: 0.3rem; flex-wrap: wrap; }
.vs-periods .btn { padding: 0.3rem 0.65rem; font-size: 0.8rem; }
.vs-periods .btn[aria-pressed="true"] { background: var(--pine); color: var(--on-pine); border-color: var(--pine); }
.vs-row { cursor: pointer; }
.vs-row:hover, .vs-row:focus-visible, .vs-row.open { background: var(--surface-alt); }
.vs-detail { border: 1px solid var(--line); border-radius: var(--radius); padding: 1rem; background: var(--surface-sunken); }
.vs-detail h4 { margin: 1rem 0 0.4rem; }
.vs-step { display: grid; grid-template-columns: minmax(9rem, 15rem) 1fr auto; gap: 0.6rem; align-items: center; margin: 0.3rem 0; font-size: 0.85rem; }
.vs-bar { height: 0.8rem; background: var(--surface-alt); border-radius: var(--radius); overflow: hidden; }
.vs-bar > span { display: block; height: 100%; background: var(--dusk); }
.vs-step-n { min-width: 5.5rem; text-align: right; }
.vs-chart { margin-top: 1rem; }
.vs-chart svg { width: 100%; height: 140px; display: block; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); }
.vs-bar-v { fill: var(--dusk-soft); }
.vs-bar-p { fill: var(--amber); }
.vs-chart-axis { display: flex; justify-content: space-between; font-size: 0.75rem; color: var(--muted); margin-top: 0.2rem; }
.vs-legend { font-size: 0.75rem; color: var(--muted); display: flex; align-items: center; gap: 0.3rem; }
.vs-key { display: inline-block; width: 0.7rem; height: 0.7rem; border-radius: 2px; margin-left: 0.4rem; }
.vs-key-v { background: var(--dusk-soft); }
.vs-key-p { background: var(--amber); }
.vs-mini { width: 100%; font-size: 0.85rem; }
.vs-mini td { word-break: break-all; }
@media (max-width: 600px) {
  .vs-step { grid-template-columns: 1fr auto; }
  .vs-step .vs-bar { grid-column: 1 / -1; grid-row: 2; }
}

/* ANPR: is this car meant to be here today? (admin/js/dashboard.js plateWho) */
.anpr-chip { display: inline-block; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; padding: 0.15rem 0.55rem; border-radius: 100px; }
.anpr-chip.in { background: var(--tint-good); color: var(--pine-light); }
.anpr-chip.out { background: var(--tint-warn); color: var(--ink); }
.anpr-chip.blocked { background: var(--ember); color: #fff; }
.anpr-detail { font-size: 0.82rem; color: var(--muted); margin: 0.15rem 0 0.25rem; }
.anpr-detail strong { color: var(--ink); }

/* Today dashboard: list boxes stop at a set height and scroll inside, so one busy day never
   pushes everything else off the page. The shadows at top/bottom only appear when there's
   more to scroll that way (backgrounds that scroll with the content cover them at the ends). */
.dash-scroll {
  max-height: 22rem; overflow-y: auto; overscroll-behavior: contain;
  background:
    linear-gradient(var(--surface) 30%, transparent) top / 100% 2.5rem no-repeat local,
    linear-gradient(transparent, var(--surface) 70%) bottom / 100% 2.5rem no-repeat local,
    radial-gradient(farthest-side at 50% 0, var(--shadow), transparent) top / 100% 0.6rem no-repeat scroll,
    radial-gradient(farthest-side at 50% 100%, var(--shadow), transparent) bottom / 100% 0.6rem no-repeat scroll;
}
@media (max-width: 700px) { .dash-scroll { max-height: 18rem; } }

/* Booking admin's Analytics: headline visitor figures above the shared funnel/chart (.vs-*) */
.vs-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); gap: 0.6rem; margin-top: 0.75rem; }
.vs-tile { border: 1px solid var(--line); border-radius: var(--radius); padding: 0.7rem 0.85rem; background: var(--surface); }
.vs-tile-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.vs-tile-n { font-size: 1.7rem; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.2; }
.vs-tile-sub { font-size: 0.78rem; color: var(--muted); }
.vs-tile.warn .vs-tile-n { color: var(--ember); }

/* Privacy notice (/privacy) and the one-line link under the booking form's pay button */
.privacy-notice { max-width: 70ch; line-height: 1.6; }
.privacy-notice h2 { margin-top: 1.6rem; font-size: 1.1rem; }
.privacy-notice ul { padding-left: 1.2rem; margin: 0.4rem 0 0.8rem; }
.privacy-notice li { margin-bottom: 0.35rem; }
.privacy-line { font-size: 0.8rem; color: var(--muted); margin-top: 0.6rem; }
.site-footer-privacy { margin: 0.4rem 0 0; font-size: 0.8rem; }

/* Getting started checklist for a new site (see renderOnboardingChecklist in dashboard.js) */
.ob-bar { height: 8px; border-radius: 4px; background: var(--surface-alt); overflow: hidden; margin: 0.4rem 0 0.8rem; }
.ob-bar > span { display: block; height: 100%; background: var(--pine-light); transition: width .3s; }
.ob-list { list-style: none; margin: 0; padding: 0; }
.ob-step { display: grid; grid-template-columns: 1.6rem 1fr auto; gap: 0.75rem; align-items: start; padding: 0.7rem 0; border-top: 1px solid var(--line); }
.ob-tick { display: inline-grid; place-items: center; width: 1.4rem; height: 1.4rem; border-radius: 50%; border: 2px solid var(--line); font-weight: 700; font-size: 0.85rem; color: var(--on-pine); }
.ob-step.done .ob-tick { background: var(--pine-light); border-color: var(--pine-light); }
.ob-step.done strong { color: var(--muted); }
.ob-why { font-size: 0.85rem; color: var(--muted); margin-top: 0.15rem; }
.ob-opt { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.ob-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; justify-content: flex-end; }
.ob-sub { margin: 1rem 0 0.2rem; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.ob-golive { display: flex; align-items: center; gap: 0.8rem; flex-wrap: wrap; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--line); }
@media (max-width: 600px) { .ob-step { grid-template-columns: 1.6rem 1fr; } .ob-actions { grid-column: 2; justify-content: flex-start; } }

/* --- Import from any system (admin/js/setup-tools.js) --- */
.si-summary { margin-top: 1rem; }
.si-notes { font-size: 0.85rem; color: var(--muted); margin: 0.3rem 0 0.6rem; }
.si-map-table { width: 100%; border-collapse: collapse; margin: 0.6rem 0 1rem; font-size: 0.88rem; }
.si-map-table th, .si-map-table td { text-align: left; padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--line); vertical-align: middle; }
.si-map-table td select { margin: 0.15rem 0; }
.si-map-table td:first-child { width: 42%; }
.si-req { font-size: 0.72rem; color: var(--muted); background: var(--surface-alt); padding: 0.05rem 0.4rem; border-radius: 999px; margin-left: 0.3rem; }
.si-preview { overflow-x: auto; }
.si-preview .si-map-table td:first-child { width: auto; }

/* --- Fill in from your current website (Getting started) --- */
.pf-card { border: 1px solid var(--line); background: var(--surface-sunken); border-radius: 8px; padding: 0.9rem 1rem; margin: 0.8rem 0; }
.pf-card.pf-done summary { cursor: pointer; font-size: 0.88rem; }
.pf-lead { margin: 0 0 0.6rem; font-size: 0.9rem; }
.pf-row { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
.pf-row input { flex: 1 1 240px; margin: 0; }
.pf-check { background: var(--tint-warn); border-radius: 6px; padding: 0.5rem 0.8rem; font-size: 0.85rem; margin-bottom: 0.6rem; }
.pf-check ul { margin: 0.2rem 0 0; padding-left: 1.1rem; }
.pf-sections { display: grid; gap: 0.5rem; }
.pf-section { display: flex; gap: 0.6rem; align-items: flex-start; background: var(--surface); border: 1px solid var(--line); border-radius: 6px; padding: 0.6rem 0.75rem; font-weight: 400; cursor: pointer; }
.pf-section input { width: auto; margin-top: 0.25rem; }
.pf-section.pf-off { opacity: 0.6; cursor: default; }
.pf-body { font-size: 0.85rem; color: var(--muted); margin-top: 0.2rem; }
.pf-body ul { margin: 0.2rem 0 0; padding-left: 1.1rem; }
.pf-note { font-size: 0.78rem; color: var(--muted); }
.pf-logo { max-height: 48px; max-width: 160px; background: #fff; padding: 4px; border-radius: 4px; vertical-align: middle; margin-right: 0.5rem; }
.pf-swatch { display: inline-block; width: 26px; height: 26px; border-radius: 50%; border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--line); vertical-align: middle; margin-right: 0.3rem; }
.pf-photos { display: flex; gap: 0.35rem; flex-wrap: wrap; margin-top: 0.3rem; }
.pf-photos img { width: 84px; height: 60px; object-fit: cover; border-radius: 4px; }
.pf-spin { display: inline-block; width: 0.9em; height: 0.9em; border: 2px solid var(--line); border-top-color: var(--pine); border-radius: 50%; animation: pf-spin 0.8s linear infinite; vertical-align: -0.1em; margin-right: 0.3rem; }
@keyframes pf-spin { to { transform: rotate(360deg); } }

/* --- Website builder (admin/js/website.js) --- */
.ws-status-row { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; align-items: flex-start; }
.ws-status-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.ws-status-line { margin: 0.35rem 0 0; }
.ws-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--muted); margin-right: 0.4rem; vertical-align: 0.05em; }
.ws-dot.ws-live { background: #2f9e5b; }
.ws-small { font-size: 0.82rem; color: var(--muted); }
.ws-help { margin-top: 0.6rem; border-top: 1px solid var(--line); padding-top: 0.6rem; }
.ws-help summary { cursor: pointer; font-weight: 600; font-size: 0.9rem; }
.ws-locked { border-left: 4px solid var(--dusk); }
.ws-tip { background: var(--tint-good); border-radius: 6px; padding: 0.6rem 0.8rem; font-size: 0.85rem; margin: 0.8rem 0; }
.ws-layout { display: grid; grid-template-columns: minmax(340px, 440px) 1fr; gap: 1rem; align-items: start; }
@media (max-width: 1100px) { .ws-layout { grid-template-columns: 1fr; } }
.ws-side { background: var(--surface); border: 1px solid var(--line); border-radius: 8px; padding: 0.8rem 1rem 1.2rem; }
.ws-tabs { flex-wrap: wrap; }
.ws-panel { border: 0; padding: 0; margin: 0; min-width: 0; }
.ws-panel h3 { margin: 1rem 0 0.4rem; }
.ws-panel h4 { margin: 1rem 0 0.4rem; font-size: 0.9rem; }
.ws-preview { position: sticky; top: 0.5rem; min-width: 0; }
.ws-preview-bar { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; margin-bottom: 0.5rem; }
.ws-preview-bar select { width: auto; margin: 0; }
.ws-device { display: flex; gap: 0.25rem; }
.ws-device .btn[aria-pressed="true"] { background: var(--pine); color: var(--on-pine); border-color: var(--pine); }
.ws-save-state { font-size: 0.8rem; color: var(--muted); margin-left: auto; }
.ws-frame-wrap { border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: #fff; }
.ws-frame-wrap iframe { display: block; border: 0; width: 100%; height: 78vh; }
.ws-frame-wrap.ws-phone { width: 390px; max-width: 100%; margin: 0 auto; border-radius: 22px; border-width: 8px; border-color: #222; }
.ws-designs { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.6rem; }
.ws-design { text-align: left; background: var(--surface); border: 2px solid var(--line); border-radius: 8px; padding: 0.4rem 0.4rem 0.6rem; cursor: pointer; display: flex; flex-direction: column; gap: 0.2rem; font: inherit; color: inherit; }
.ws-design span:last-child { font-size: 0.75rem; color: var(--muted); line-height: 1.35; }
.ws-design.selected { border-color: var(--amber); box-shadow: 0 0 0 2px var(--amber); }
.ws-thumb { display: block; position: relative; width: 100%; aspect-ratio: 16 / 11; overflow: hidden; border-radius: 5px; background: var(--surface-alt); pointer-events: none; }
.ws-thumb iframe { position: absolute; top: 0; left: 0; width: 1280px; height: 880px; border: 0; transform: scale(0.15); transform-origin: 0 0; }
.ws-palettes { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.ws-palette { display: inline-flex; align-items: center; gap: 0.25rem; padding: 0.35rem 0.6rem; border: 2px solid var(--line); border-radius: 999px; background: var(--surface); cursor: pointer; font: inherit; font-size: 0.82rem; color: inherit; }
.ws-palette.selected { border-color: var(--amber); }
.ws-sw { width: 16px; height: 16px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15); }
.ws-palette .ws-sw:last-of-type { margin-right: 0.25rem; }
.ws-custom-colours { display: flex; gap: 1rem; margin: 0.6rem 0; flex-wrap: wrap; }
.ws-custom-colours label { display: flex; align-items: center; gap: 0.4rem; font-weight: 400; }
.ws-custom-colours input { width: 44px; height: 32px; padding: 0; border: 0; }
.ws-check { display: flex; align-items: center; gap: 0.5rem; font-weight: 400; margin: 0.4rem 0; }
.ws-check input { width: auto; margin: 0; }
.ws-pages { list-style: none; padding: 0; margin: 0 0 0.5rem; }
.ws-pages li { display: flex; gap: 0.5rem; align-items: center; padding: 0.3rem 0.4rem; border-radius: 6px; }
.ws-pages li.current { background: var(--surface-alt); }
.ws-row-actions { margin-left: auto; display: flex; gap: 0.25rem; flex-wrap: wrap; }
.ws-link { background: none; border: 0; padding: 0; font: inherit; font-weight: 600; color: inherit; cursor: pointer; text-align: left; }
.ws-page-edit { border-top: 1px solid var(--line); margin-top: 0.8rem; }
.ws-slug { display: flex; align-items: center; gap: 0.2rem; }
.ws-slug input { margin: 0; }
.ws-section { border: 1px solid var(--line); border-radius: 6px; margin-bottom: 0.4rem; background: var(--surface); }
.ws-section.is-hidden { opacity: 0.6; }
.ws-section-head { display: flex; gap: 0.4rem; align-items: center; padding: 0.45rem 0.55rem; flex-wrap: wrap; }
.ws-section-body { padding: 0 0.7rem 0.8rem; border-top: 1px solid var(--line); }
.ws-add-section { display: flex; gap: 0.4rem; margin-top: 0.6rem; flex-wrap: wrap; }
.ws-add-section select { flex: 1; margin: 0; }
.ws-image { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; }
.ws-image img { width: 120px; height: 80px; object-fit: cover; border-radius: 4px; }
.ws-noimg { width: 120px; height: 80px; border: 2px dashed var(--line); border-radius: 4px; display: grid; place-items: center; font-size: 0.75rem; color: var(--muted); text-align: center; padding: 0.3rem; }
.ws-image-actions { display: flex; gap: 0.3rem; flex-wrap: wrap; margin-top: 0.3rem; }
.ws-images { display: grid; gap: 0.4rem; }
.ws-img-item { display: flex; gap: 0.4rem; align-items: center; }
.ws-img-item img { width: 70px; height: 50px; object-fit: cover; border-radius: 4px; flex: none; }
.ws-img-item input { margin: 0; }
.ws-list { display: grid; gap: 0.35rem; margin-bottom: 0.35rem; }
.ws-list-row { display: flex; gap: 0.35rem; align-items: flex-start; }
.ws-list-row input, .ws-list-row textarea { margin: 0; }
.ws-list-multi { flex-wrap: wrap; border-left: 3px solid var(--line); padding-left: 0.4rem; }
.ws-list-multi input, .ws-list-multi textarea { flex: 1 1 100%; }
dialog.ws-library { width: min(760px, 94vw); border: 1px solid var(--line); border-radius: 8px; background: var(--surface); color: var(--ink); }
.ws-lib-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 0.5rem; max-height: 60vh; overflow: auto; }
.ws-lib-item { position: relative; font-weight: 400; cursor: pointer; }
.ws-lib-item input { position: absolute; top: 6px; left: 6px; width: auto; }
.ws-lib-item img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 4px; border: 3px solid transparent; }
.ws-lib-item input:checked + img { border-color: var(--amber); }

/* Settings › Features: extras switched off are hidden everywhere (admin/js/dashboard.js). */
.feature-off { display: none !important; }
.staff-off { display: none !important; }
.of-list { display: grid; gap: 0.4rem; }
.of-row { display: flex; gap: 0.7rem; align-items: flex-start; padding: 0.6rem 0.75rem; border: 1px solid var(--line); border-radius: 6px; font-weight: 400; cursor: pointer; background: var(--surface); }
.of-row input { width: auto; margin-top: 0.25rem; }
.of-row strong { display: block; }
.of-blurb { display: block; font-size: 0.82rem; color: var(--muted); }

/* --- Simpler admin (2026-10) ---------------------------------------------------------- */
/* Switcher along the top of screens grouped under one sidebar entry (NAV_HUBS in
   dashboard.js) — e.g. Payments | Allocations | Invoices | VAT export. */
.page-switch { display: flex; gap: 0.25rem; flex-wrap: wrap; margin-bottom: 1rem; border-bottom: 1px solid var(--line); }
.page-switch[hidden] { display: none; }
.page-switch button {
  background: none; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  padding: 0.5rem 0.85rem; font: inherit; font-size: 0.9rem; color: var(--muted); cursor: pointer;
}
.page-switch button:hover { color: var(--ink); }
.page-switch button.active { color: var(--ink); border-bottom-color: var(--pine); font-weight: 600; }
.page-switch .nav-count { margin-left: 0.25rem; }
/* On a section's own front page there's nowhere to go "back" to. */
.subtabs.on-overview { display: none; }

.settings-hint { font-size: 0.85rem; color: var(--muted); background: var(--surface-alt); border: 1px solid var(--line); border-radius: var(--radius); padding: 0.55rem 0.75rem; margin: 0.75rem 0 0; }

/* "More options" folds: the everyday settings stay in view, fine-tuning is one click away. */
details.more-options { margin-top: 1rem; border-top: 1px dashed var(--line); padding-top: 0.6rem; }
details.more-options > summary, details.settings-more > summary, details.fold-card > summary {
  cursor: pointer; font-size: 0.88rem; font-weight: 600; color: var(--pine); list-style: none;
}
details.more-options > summary::-webkit-details-marker, details.settings-more > summary::-webkit-details-marker, details.fold-card > summary::-webkit-details-marker { display: none; }
details.more-options > summary::before, details.settings-more > summary::before, details.fold-card > summary::before {
  content: '▸'; display: inline-block; width: 1em; transition: transform 0.15s;
}
details[open].more-options > summary::before, details[open].settings-more > summary::before, details[open].fold-card > summary::before { transform: rotate(90deg); }
details[open].more-options > summary { margin-bottom: 0.5rem; }
.more-options-note { font-size: 0.8rem; color: var(--muted); margin: 0 0 0.5rem; }
details.settings-more { margin-top: 1.5rem; }
details.settings-more > summary { font-family: 'IBM Plex Mono', monospace; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.09em; color: var(--muted); }
details.fold-card > summary { color: var(--ink); font-size: 0.95rem; }
details.fold-card > summary .fold-card-sub { font-weight: 400; color: var(--muted); font-size: 0.85rem; }
details[open].fold-card > summary { margin-bottom: 0.75rem; }
@media (max-width: 600px) {
  .page-switch { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .page-switch button { white-space: nowrap; padding: 0.5rem 0.6rem; }
}
/* Booking page: the deposit rule and cancellation terms, just above the card form. */
.booking-terms-note { margin-top: 0.75rem; padding: 0.7rem 0.9rem; border: 1px solid var(--line); border-left: 3px solid var(--pine); border-radius: var(--radius); background: var(--surface-alt); font-size: 0.85rem; }
.booking-terms-note p { margin: 0 0 0.35rem; }
.booking-terms-note p:last-child { margin-bottom: 0; }
/* My bookings: the "Book a new stay" card at the top once logged in. */
.book-again-card { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.book-again-card .btn { width: auto; }

/* Members' club (club.js) — the booking page banner and the "save £x with the club" note. */
.club-banner[hidden] { display: none; }
.club-banner { border-left: 4px solid var(--pine-light, #2f6b4f); }
.club-banner p { margin: 0.25rem 0; }
.club-banner .club-small { font-size: 0.88rem; color: var(--muted); }
.club-offer { margin-top: 0.75rem; padding: 0.7rem 0.85rem; border-radius: 10px; background: color-mix(in srgb, var(--pine-light, #2f6b4f) 10%, transparent); font-size: 0.9rem; line-height: 1.4; }
.club-offer-pay[hidden] { display: none; }
.club-offer-pay { border: 2px solid var(--pine-light, #2f6b4f); }
.club-offer-pay .club-offer-head { margin: 0 0 0.4rem; }
.club-offer-pay .club-offer-lines .row { display: flex; justify-content: space-between; gap: 1rem; padding: 0.15rem 0; }
.club-offer-pay .club-offer-lines .row.total { border-top: 1px solid var(--line, #ccc); margin-top: 0.3rem; padding-top: 0.35rem; font-weight: 700; }
.club-offer-pay .club-offer-foot { margin: 0.5rem 0 0; font-size: 0.88rem; }

/* Settings › Branding › Dashboard look — picks booking_settings.admin_theme. */
.admin-look-options { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.75rem; margin-top: 0.5rem; }
.admin-look-option {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem; text-align: left;
  background: var(--surface); border: 2px solid var(--line); border-radius: 12px; padding: 0.75rem;
  cursor: pointer; font: inherit; color: var(--ink);
}
.admin-look-option:hover { border-color: var(--pine-light); }
.admin-look-option[aria-checked="true"] { border-color: var(--pine); box-shadow: 0 0 0 3px color-mix(in srgb, var(--pine) 20%, transparent); }
.admin-look-option span:not(.admin-look-swatch) { font-size: 0.82rem; color: var(--muted); }
.admin-look-swatch { display: flex; align-items: flex-end; gap: 6px; width: 100%; height: 54px; border-radius: 8px; padding: 8px; margin-bottom: 0.4rem; box-sizing: border-box; border: 1px solid var(--line); }
.admin-look-swatch i { display: block; height: 100%; border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,.1); }
.admin-look-swatch i:first-child { flex: 3; }
.admin-look-swatch i:last-child { flex: 1; height: 40%; }

/* Takings (admin/js/takings.js): big tap-target category buttons, totals as chips. */
.tk-cats { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.25rem; }
.tk-cat {
  min-height: 48px; padding: 0.5rem 1.1rem; border-radius: 12px; cursor: pointer;
  border: 2px solid var(--line); background: var(--surface); color: var(--ink); font: inherit; font-weight: 600;
}
.tk-cat:hover { border-color: var(--pine-light); }
.tk-cat.picked { border-color: var(--pine); background: color-mix(in srgb, var(--pine) 12%, var(--surface)); color: var(--pine); font-weight: 800; }
.tk-amount { font-size: 1.3rem !important; font-weight: 700; }
.tk-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.75rem; }
.tk-chip { background: var(--surface-alt); border-radius: 999px; padding: 0.3rem 0.8rem; font-size: 0.88rem; }
.tk-cat-row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; margin-top: 0.4rem; }
.tk-cat-row input { flex: 1 1 180px; width: auto; }
.tk-cat-row select { width: auto; }

/* Platform admin › Members' club: settings for all sites, or one site on its own. */
.club-site-pick { background: var(--surface-alt); border-radius: 12px; padding: 0.75rem 0.9rem; margin-bottom: 1rem; }
.club-site-pick label { margin-top: 0; }
.club-site-pick select { max-width: 360px; }
.club-site-note { margin-top: 0.5rem; font-size: 0.88rem; }
.club-hint { font-size: 0.82rem; color: var(--muted); margin: 0.35rem 0 0; }
.loy-season { display: flex; align-items: center; gap: 0.75rem; margin: 0.3rem 0; flex-wrap: wrap; }
.loy-season span { min-width: 140px; font-weight: 600; }
.loy-season select { width: auto; }

/* The dashboard header's light/dark switch (admin/index.html #theme-toggle, theme.js). */
.admin-theme-toggle {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 38px; height: 38px; padding: 0; border-radius: 50%; cursor: pointer;
  background: transparent; color: var(--ink); border: 1px solid var(--line);
}
.admin-theme-toggle:hover { border-color: var(--ink); }
.admin-theme-toggle:focus-visible { outline: 2px solid var(--pine); outline-offset: 2px; }
.admin-theme-toggle .icon-sun { display: none; }
.admin-theme-toggle[data-mode="dark"] .icon-sun { display: block; }
.admin-theme-toggle[data-mode="dark"] .icon-moon { display: none; }

/* Sidebar (2026-10-19): the group headings (Daily, Bookings, Money…) aren't buttons, and
   "Bookings" above the Bookings button read as the same thing twice — so only the buttons
   show; each group keeps its coloured line and the gap between groups. */
.nav-group-title { display: none !important; }
.nav-group + .nav-group { margin-top: 0.6rem; }

/* Blocked-pitches list (Calendar › Block pitches and the Booking chart's block panel):
   grouped as they were made, tick boxes to unblock several at once. */
.pb-head { display: flex; align-items: center; gap: 0.75rem 1rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
.pb-head h3 { margin: 0; }
.pb-all { display: flex; align-items: center; gap: 0.4rem; font-weight: 400; margin: 0; }
.pb-head .btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.pb-count { display: inline-block; min-width: 1.5em; padding: 0 0.45em; border-radius: 99px; background: var(--ink); color: var(--canvas); font-size: 0.8em; font-weight: 700; text-align: center; }
.pb-group { border: 1px solid var(--line); border-left: 6px solid var(--ink); border-radius: 6px; padding: 0.6rem 0.75rem; margin-bottom: 0.6rem; }
.pb-group-head { display: flex; align-items: flex-start; gap: 0.5rem; font-weight: 400; margin: 0 0 0.5rem; cursor: pointer; }
.pb-meta { display: block; color: var(--muted); font-size: 0.85rem; }
.pb-pitches { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.pb-chip { display: inline-flex; align-items: center; gap: 0.35rem; margin: 0; padding: 0.3rem 0.65rem; border: 1px solid var(--line); border-radius: 99px; font-weight: 600; cursor: pointer; }
.pb-chip:has(input:checked) { background: var(--ink); color: var(--canvas) !important; border-color: var(--ink); }
.pb-head input, .pb-group-head input, .pb-chip input { width: auto; margin: 0; }

/* Diary: camera check-in / check-out switches (anpr-auto.js). */
.diary-camera { display: flex; gap: 0.5rem; flex-wrap: wrap; padding-bottom: 0.45rem; }
.diary-cam-switch { border: 1px solid var(--line); background: transparent; color: var(--ink); border-radius: 99px; padding: 0.4rem 0.9rem; font: inherit; font-size: 0.9rem; cursor: pointer; }
.diary-cam-switch b { margin-left: 0.25rem; }
.diary-cam-switch small { color: inherit; opacity: 0.8; margin-left: 0.25rem; }
.diary-cam-switch[aria-checked="true"] { background: var(--pine); border-color: var(--pine); color: var(--on-pine, #fff); }
.diary-cam-switch:focus-visible { outline: 2px solid var(--pine); outline-offset: 2px; }

/* Notes on a booking (bookingNotesHTML in dashboard.js): Diary, Bookings list, top of a booking. */
.bk-notes { display: flex; flex-direction: column; gap: 0.25rem; margin-top: 0.35rem; max-width: 38rem; }
.bk-note { font-size: 0.85rem; line-height: 1.35; padding: 0.3rem 0.55rem; border-radius: 6px; border-left: 4px solid var(--dusk, #4a6fa5);
  background: color-mix(in srgb, var(--dusk, #4a6fa5) 16%, transparent); color: var(--ink); white-space: normal; overflow-wrap: anywhere; }
.bk-note b { font-weight: 700; }
.bk-note { display: flex; gap: 0.4rem; align-items: flex-start; }
.bk-note > span { flex: 1; min-width: 0; }
.bk-note-rm { flex: none; border: 0; background: transparent; color: var(--muted); font-size: 1.1rem; line-height: 1; padding: 0 0.2rem; cursor: pointer; border-radius: 4px; }
.bk-note-rm:hover, .bk-note-rm:focus-visible { color: var(--ink); background: color-mix(in srgb, var(--ink) 12%, transparent); outline: none; }
.bk-note-customer { border-left-color: var(--ember); background: color-mix(in srgb, var(--ember) 12%, transparent); }
.bk-note-staff { border-left-color: var(--pine); background: color-mix(in srgb, var(--pine) 12%, transparent); }
.bk-notes-short .bk-note { font-size: 0.78rem; padding: 0.2rem 0.45rem; }
#booking-detail > .bk-notes { margin: 0 0 1rem; max-width: none; }
#booking-detail > .bk-notes .bk-note { font-size: 0.95rem; padding: 0.5rem 0.75rem; }
td.diary-guest-cell { min-width: 14rem; white-space: normal; }

/* Invoices screen (dashboard.js loadInvoices / New invoice). */
.inv-summary { display: flex; gap: 1.25rem; flex-wrap: wrap; margin: 0.25rem 0 0.75rem; font-size: 0.95rem; }
.inv-filters { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
.inv-filters button { border: 1px solid var(--line); background: transparent; color: var(--ink); border-radius: 99px; padding: 0.35rem 0.9rem; font: inherit; font-size: 0.9rem; cursor: pointer; }
.inv-filters button[aria-pressed="true"] { background: var(--pine); border-color: var(--pine); color: var(--on-pine, #fff); }
.inv-h3 { margin: 1.25rem 0 0.25rem; font-size: 1rem; }
.inv-edit { width: 100%; }
.inv-edit td { padding: 0.25rem 0.35rem 0.25rem 0; vertical-align: middle; }
.inv-edit td:first-child { min-width: 16rem; }
.inv-total-line { font-size: 1.05rem; margin: 0.75rem 0; }
.pill { display: inline-block; padding: 0 0.55rem; border-radius: 99px; font-size: 0.75rem; font-weight: 700; background: color-mix(in srgb, var(--pine) 16%, transparent); color: var(--ink); vertical-align: middle; }
.pill-good { background: color-mix(in srgb, var(--pine) 22%, transparent); }

/* Booking panel, option C (openBookingDetail / bdFold in dashboard.js): a summary at the
   top, then colour-coded folds — green stay, amber money, blue guest, purple extras, grey history. */
.bd-summary { border: 1px solid var(--line); border-radius: 12px; padding: 0.8rem 0.95rem; margin-bottom: 0.75rem;
  background: color-mix(in srgb, var(--pine) 10%, transparent); }
.bd-summary-top { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }
.bd-who { font-size: 1.25rem; font-weight: 800; color: var(--ink); }
.bd-facts { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; margin-top: 0.25rem; font-size: 0.95rem; color: var(--ink); }
.bd-facts .bd-unit { width: auto; padding: 0.1rem 0.4rem; font-size: inherit; display: inline-block; }
.bd-balance { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; flex-wrap: wrap;
  margin-top: 0.6rem; padding-top: 0.6rem; border-top: 1px dashed var(--line); font-size: 1rem; }
.bd-quick { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; }
.bd-fold { --fc: var(--pine); border: 1px solid var(--line); border-radius: 10px; margin-top: 0.6rem; overflow: hidden; }
.bd-fold > summary { list-style: none; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 0.75rem;
  padding: 0.6rem 0.85rem; border-left: 5px solid var(--fc); background: color-mix(in srgb, var(--fc) 13%, transparent); }
.bd-fold > summary::-webkit-details-marker { display: none; }
.bd-fold > summary::after { content: '▸'; color: var(--muted); transition: transform 0.15s; }
.bd-fold[open] > summary::after { transform: rotate(90deg); }
.bd-fold > summary:focus-visible { outline: 2px solid var(--fc); outline-offset: -2px; }
.bd-fold-title { font-weight: 800; font-size: 1rem; color: var(--ink); white-space: nowrap; }
.bd-fold-bar { flex: 1; text-align: right; font-size: 0.85rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.bd-fold-body { padding: 0.6rem 0.85rem 0.85rem; border-left: 5px solid color-mix(in srgb, var(--fc) 45%, transparent); }
.bd-fold-stay { --fc: #2f9a5f; }
.bd-fold-money { --fc: #d08a12; }
.bd-fold-guest { --fc: #4a7fd0; }
.bd-fold-items { --fc: #8a63c9; }
.bd-fold-hist { --fc: #7d8790; }
.bd-fold-others { --fc: #3e8fa0; }
.bd-sub { margin: 1rem 0 0.4rem; font-size: 0.95rem; }
@media (prefers-reduced-motion: reduce) { .bd-fold > summary::after { transition: none; } }
/* Customer window folds (same look as the booking panel). */
.bd-fold-details { --fc: #4a7fd0; }
.bd-fold-club { --fc: #8a63c9; }
.bd-fold-stays { --fc: #2f9a5f; }
.bd-fold-email { --fc: #3e8fa0; }
.bd-fold-records { --fc: #7d8790; }

/* Guest portal: adding to a booking (portal.js openAddToStay). */
.add-stay { margin-top: 0.75rem; padding: 0.85rem; border: 1px solid var(--line); border-radius: 10px; }
.add-choice { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.6rem; }
.add-choice button { font: inherit; padding: 0.55rem 0.9rem; border-radius: 99px; border: 1px solid var(--line); background: transparent; color: var(--ink); cursor: pointer; }
.add-choice button[aria-checked="true"] { background: var(--pine); border-color: var(--pine); color: var(--on-pine, #fff); font-weight: 700; }
.add-quote { font-size: 1rem; margin: 0.75rem 0 0.5rem; }

/* Block pitches: the live "3 nights blocked…" line under the dates (dashboard.js showBlockNights). */
.block-nights { margin: 0.35rem 0 0.25rem; font-size: 0.95rem; }
.block-nights:empty { display: none; }

/* ---- Settings › Your look (admin, 2026-10-24) ---- */
.look-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.6rem; margin-top: 0.6rem; }
@media (max-width: 860px) { .look-grid { grid-template-columns: 1fr; } }
.look-logo { display: flex; gap: 1rem; align-items: center; }
#br-logo-preview img, .look-logo-empty { width: 76px; height: 76px; border-radius: 16px; border: 1px solid var(--line); object-fit: contain; background: #fff; display: flex; align-items: center; justify-content: center; font-size: 0.72rem; color: var(--muted); text-align: center; }
.look-choices { display: flex; flex-direction: column; gap: 0.5rem; }
.look-choice { display: flex; align-items: center; gap: 0.7rem; border: 1px solid var(--line); border-radius: 12px; padding: 0.7rem 0.85rem; font-weight: 500; cursor: pointer; flex-wrap: wrap; }
.look-choice[hidden] { display: none; }
.look-choice input[type=radio] { width: auto; margin: 0; accent-color: var(--pine-light); }
.look-choice:has(input:checked) { border: 2px solid var(--pine-light); background: color-mix(in srgb, var(--pine-light) 6%, transparent); }
.look-sw, .look-presets, .look-own { margin-left: auto; display: flex; gap: 0.35rem; align-items: center; }
.look-sw i { width: 22px; height: 22px; border-radius: 6px; display: block; box-shadow: 0 0 0 1px var(--line); }
.look-presets .theme-swatch { width: 28px; height: 28px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px var(--line); cursor: pointer; padding: 0; }
.look-presets .theme-swatch.on { box-shadow: 0 0 0 2px var(--pine-light); }
.look-own input[type=color] { width: 34px; height: 30px; padding: 0; border-radius: 6px; }
.look-font-sample { font-size: 1.6rem; margin-top: 0.6rem; line-height: 1.2; }
.look-font-sample small { font-size: 0.85rem; color: var(--muted); margin-left: 0.5rem; }
.look-preview { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.7rem; }
.look-tile { border: 1px solid var(--line); border-radius: 12px; overflow: hidden; font-size: 11px; background: #fff; }
.lp-cap { padding: 0.45rem 0.6rem; color: var(--muted); font-weight: 600; border-top: 1px solid var(--line); }
.lp-book, .lp-mail, .lp-voucher { height: 118px; position: relative; color: #222; }
.lp-hd { display: flex; align-items: center; gap: 6px; padding: 6px 8px; font-weight: 600; font-size: 11px; line-height: 1.2; }
.lp-hd img, .lp-hd b { width: 18px; height: 18px; border-radius: 5px; flex-shrink: 0; background: rgba(255,255,255,.3); object-fit: contain; }
.lp-lines { padding: 10px 8px; } .lp-lines i { display: block; height: 8px; border-radius: 3px; background: rgba(0,0,0,.08); margin-bottom: 7px; width: 80%; }
.lp-btn { position: absolute; left: 8px; bottom: 10px; padding: 5px 10px; font-weight: 700; font-size: 10px; }
.lp-mail { padding: 8px; }
.lp-title { font-size: 14px; padding: 7px 8px 0; line-height: 1.15; }
.lp-row { margin: 6px 8px; height: 20px; border: 1px solid rgba(0,0,0,.1); background: #fff; }
.lp-voucher { display: grid; grid-template-columns: 1.6fr 1fr; }
.lp-v-main { color: #fff; padding: 8px; font-size: 12px; line-height: 1.2; }
.lp-v-stub { display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 22px; font-weight: 600; }
.lp-v-stub small { font-size: 8px; letter-spacing: 2px; font-weight: 700; }
.look-foot { display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.look-foot .btn { width: auto; }
