/* base.css — the platform's base sheet: the primitives every module's pages are built from
   (a page head, a button, a card, a form, a badge, the date field, the day strip and the
   time grid a booking is picked from, the ways-to-pay list), drawn ONLY in the site's
   tokens. A page links it BEFORE the site's /styles.css, so the site's tokens and rules win.

   The contract a site fulfils is four tokens — --bg, --text, --accent, --font — declared in
   its styles.css; everything else below has a default derived from those four, and a site
   overrides any of them by declaring it. See docs/WEBSITE.md, "The contract". */

/* Tokens: neutral defaults for the required four, so a module's page on a tenant with no
   site of its own still reads as a page (system type, white ground, one plain accent); a
   site's styles.css, linked after this sheet, overrides them --------------------------- */

:root {
	--bg: #FFFFFF;
	--text: #1B1F24;
	--accent: #1F6FEB;
	--font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

/* A PARAGRAPH READS IN ITS OWN LANGUAGE. A page carries the business's direction, but the
   words on it are whatever somebody wrote — a Hebrew club whose racket is called "Babolat
   Pure Drive", an English sentence left in a template. `plaintext` lets each block take its
   direction from its own first strong character, so a Latin sentence on a right-to-left page
   keeps its full stop at the end instead of having it dragged to the front. It changes
   nothing on a page whose text matches its direction, which is every page today. */
h1, h2, h3, h4, h5, h6, p, li, dd, dt, blockquote, figcaption, td, th, label, legend,
button, .button, .status {
	unicode-bidi: plaintext;
}

/* Tokens: the optional half, derived from the required four ------------------------ */

:root {
	--surface: color-mix(in srgb, var(--bg) 88%, white);
	--line: color-mix(in srgb, var(--text) 14%, var(--bg));
	--muted: color-mix(in srgb, var(--text) 64%, var(--bg));
	--accent-contrast: #FFFFFF;
	--accent-text: var(--accent);
	--font-display: var(--font);
	--radius: 14px;
	--radius-sm: 10px;
	--radius-control: 6px;
	--wrap: 72rem;
	--shadow: 0 1px 2px color-mix(in srgb, var(--text) 6%, transparent),
		0 10px 28px color-mix(in srgb, var(--text) 8%, transparent);
	--ring: 0 0 0 4px color-mix(in srgb, var(--accent) 24%, transparent);
	--ok: #1E7F3C;
	--warn: #B8791A;
	--bad: #B4311B;
}

/* Ground --------------------------------------------------------------------------- */

* { box-sizing: border-box; }

/* The hidden attribute wins over any display a class sets — a card a script hides is hidden. */
[hidden] { display: none !important; }

body {
	margin: 0;
	min-height: 100vh;
	display: flex;
	flex-direction: column;
	font-family: var(--font);
	font-size: 1.0625rem;
	line-height: 1.6;
	color: var(--text);
	background: var(--bg);
	-webkit-font-smoothing: antialiased;
}

/* The page's main content grows, so a short page still puts its footer at the bottom. */
main { flex: 1 0 auto; }

h1, h2, h3 { margin: 0 0 0.5em; font-family: var(--font-display); line-height: 1.1; }
p { margin: 0 0 1em; }
a { color: inherit; }

:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--radius-control); }

/* Layout --------------------------------------------------------------------------- */

/* As wide as it may be, up to --wrap: in the body's column (a flex column), auto margins
   alone would shrink it to what it holds, and the customers' app in it to nothing. */
.wrap {
	width: 100%;
	max-width: var(--wrap);
	margin-inline: auto;
	padding-inline: clamp(1.25rem, 4vw, 2.5rem);
}
.narrow { max-width: 44rem; }

.band { padding-block: clamp(2.5rem, 6vw, 4.5rem); }

/* A module page (one that opens with a page-head) is a working page, not a landing: it
   starts close under the header, whatever rhythm the site's template gives its bands. */
.band:has(> .wrap > .page-head) { padding-top: clamp(1.5rem, 3vw, 2.25rem); }

/* The chrome of a page that is TAKING MONEY: the business's mark, and nothing to click away
   with. It is what partials/brand.html draws when the site ships no brand of its own. */

.brandbar {
	padding-block: 1rem;
	border-bottom: 1px solid var(--line);
	background: var(--surface);
}
.brandbar-name {
	font-family: var(--font-display);
	font-size: 1.3rem;
	font-weight: 700;
	text-decoration: none;
}

/* The business's logo (Settings → General) beside its name, in whatever bar names it. */
.brand:has(> .brand-logo),
.brandbar-name:has(> .brand-logo) {
	display: inline-flex;
	align-items: center;
	gap: 0.65rem;
}
.brand-logo {
	height: 2.5rem;
	width: auto;
	max-width: 10rem;
	object-fit: contain;
	flex-shrink: 0;
}

/* The head of a module page: its title and the one or two actions that belong beside it.
   Sized here so the site's display h1 (a hero size) never lands on a booking page. */

.page-head {
	display: flex;
	flex-wrap: wrap;
	align-items: end;
	justify-content: space-between;
	gap: 1rem 2rem;
	margin-bottom: clamp(1.1rem, 2vw, 1.5rem);
}
.page-head h1 { margin: 0; font-size: clamp(1.7rem, 2.8vw, 2.2rem); font-weight: 600; letter-spacing: -0.01em; }
.page-head .actions { margin: 0; }
/* An app's page names itself in the page-head's h1, so every h2 under it is a SECTION of that
   page. A template sizes its h2 for the home page's display headings; here that would put a
   section title above the page's own. */
.page-head ~ h2, .page-head ~ * h2 { font-size: 1.35rem; font-weight: 600; letter-spacing: -0.01em; line-height: 1.2; }

.lede { max-width: 40rem; margin: -0.5rem 0 1.75rem; font-size: 1.1rem; color: var(--muted); }

.eyebrow {
	margin: 0 0 0.6rem;
	font: 600 0.74rem/1.5 var(--font);
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--accent-text);
}

.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin: 1.5rem 0 0; }
/* One decision, full width, with a line under it — the shape a confirmation page ends on. */
.actions--stack { flex-direction: column; align-items: stretch; text-align: center; }
/* Buttons that share the line equally, and stack when it is too short for them. */
.actions--fill > * { flex: 1 1 10rem; justify-content: center; }
.button--wide { width: 100%; }

/* Prose: what the markdown filter prints — an article, a post — set to be READ -------- */

.prose { font-size: 1.05rem; line-height: 1.7; overflow-wrap: break-word; }
.prose h1 { font-size: clamp(1.7rem, 2.8vw, 2.2rem); font-weight: 600; letter-spacing: -0.01em; }
.prose h2 { margin: 2.25rem 0 0.6em; font-size: 1.35rem; font-weight: 600; letter-spacing: 0; }
.prose h3 { margin: 1.75rem 0 0.5em; font-size: 1.1rem; font-weight: 600; letter-spacing: 0; }
.prose p { margin: 0 0 1.1em; }
.prose ul, .prose ol { margin: 0 0 1.1em; padding-inline-start: 1.4rem; }
.prose li { margin-bottom: 0.4em; }
/* A BUTTON inside prose is a button, not a link: prose's link colour is more specific than
   the button's own, so it would paint the label over the button's background and leave it
   unreadable. */
.prose a:not(.button) { color: var(--accent-text); text-underline-offset: 0.15em; }
.prose hr { margin: 2.25rem 0; border: 0; border-top: 1px solid var(--line); }
.prose blockquote {
	margin: 1.5rem 0;
	padding: 0.1rem 1.25rem;
	border-inline-start: 3px solid var(--accent);
	color: var(--muted);
}

/* A screenshot gets the width of the column and a frame, so a picture of a screen does not
   read as part of the page it is shown on. */
.prose img {
	display: block;
	max-width: 100%;
	height: auto;
	margin: 1.5rem 0;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	box-shadow: var(--shadow);
}

.prose code {
	padding: 0.15em 0.4em;
	border-radius: 0.35rem;
	background: color-mix(in srgb, var(--text) 7%, var(--bg));
	font-size: 0.9em;
}
.prose pre {
	overflow-x: auto;
	margin: 1.5rem 0;
	padding: 1rem 1.15rem;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: color-mix(in srgb, var(--text) 4%, var(--bg));
	line-height: 1.5;
}
.prose pre code { padding: 0; background: none; }

.prose table {
	display: block;
	overflow-x: auto;
	width: 100%;
	margin: 1.5rem 0;
	border-collapse: collapse;
	font-size: 0.95rem;
}
.prose th, .prose td { padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--line); text-align: start; }
.prose th { font-weight: 600; }

/* Buttons -------------------------------------------------------------------------- */

.button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	padding: 0.7rem 1.4rem;
	border: 1.5px solid var(--text);
	border-radius: var(--radius-control);
	font: 600 0.95rem/1.2 var(--font);
	text-decoration: none;
	cursor: pointer;
	background: var(--text);
	color: var(--bg);
	transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}
.button:hover { transform: translateY(-1px); box-shadow: var(--shadow); }
.button:active { transform: translateY(0); }
.button[disabled] { opacity: 0.55; cursor: default; transform: none; box-shadow: none; }

.button--accent {
	background: var(--accent);
	border-color: var(--accent);
	color: var(--accent-contrast);
}

.button--ghost {
	background: transparent;
	/* drawn off the ink, not --line: it must still show on a card a shade off the ground */
	border-color: color-mix(in srgb, var(--text) 28%, transparent);
	color: var(--text);
}
.button--ghost:hover { border-color: var(--text); }

.button--ghost-danger {
	background: transparent;
	border-color: color-mix(in srgb, var(--bad) 40%, var(--bg));
	color: var(--bad);
}
.button--ghost-danger:hover { border-color: var(--bad); }

.linklike {
	padding: 0;
	border: 0;
	background: none;
	font: inherit;
	font-weight: 600;
	color: var(--accent-text);
	cursor: pointer;
	text-decoration: underline;
}

/* Cards ---------------------------------------------------------------------------- */

.card {
	padding: clamp(1.4rem, 3vw, 2.2rem);
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	box-shadow: var(--shadow);
}

/* A title INSIDE a card is a content title, never the site's display size: the site's own
   h2 is a band headline (a hero scale), and a module's page must not inherit it for "your
   booking is confirmed". Sized above the site's bare selectors by specificity. */
.card h2 { font-size: 1.3rem; font-weight: 600; letter-spacing: 0; margin: 0 0 0.35em; }
.card h3 { font-size: 1.05rem; font-weight: 600; letter-spacing: 0; margin: 0 0 0.35em; }
.card p:last-child { margin-bottom: 0; }
/* The card's padding is the space above its first line: a status or a row of actions that
   opens a card keeps the gap it takes after other content to itself. */
.card > :first-child { margin-top: 0; }

/* Forms ---------------------------------------------------------------------------- */

/* A form is a column of ordinary-sized fields, never a wall of them: a field is as tall as
   its text needs, and fills its container — the PAGE bounds the container (a card, a
   narrow wrap, a max-width on its own form) so fields never sit ragged inside a wider box. */
.form { display: grid; gap: 0.15rem; align-content: start; }
.form label { font-weight: 600; font-size: 0.85rem; margin-top: 0.7rem; }
.form label:first-child { margin-top: 0; }

.form input,
.form textarea,
.form select {
	width: 100%;
	padding: 0.5rem 0.7rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-control);
	font: inherit;
	font-size: 0.95rem;
	line-height: 1.3;
	color: inherit;
	background: var(--surface);
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.form input::placeholder, .form textarea::placeholder { color: var(--muted); opacity: 0.7; }
.form input:hover, .form textarea:hover, .form select:hover { border-color: var(--muted); }
.form input:focus-visible, .form textarea:focus-visible, .form select:focus-visible {
	border-color: var(--accent);
	box-shadow: var(--ring);
	outline: none;
}

/* A yes/no choice is the box and its words on one line, the words part of the click: a label
   wrapping its checkbox ("Keep me signed in"). */
.form .check {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-weight: 400;
	font-size: 0.9rem;
	cursor: pointer;
}
.form .check input {
	width: auto;
	padding: 0;
	margin: 0;
	accent-color: var(--accent);
	box-shadow: none;
}

/* A form's own submit, when the page did not dress it as one of the buttons above: the
   same shape as .button--accent (padding, radius, type), so a submit and a .button beside
   it — the sign-in card's two ways in — read as one family. */
.form button[type="submit"]:not(.button) {
	justify-self: start;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	margin-top: 0.9rem;
	padding: 0.7rem 1.4rem;
	border: 1.5px solid var(--accent);
	border-radius: var(--radius-control);
	background: var(--accent);
	color: var(--accent-contrast);
	font: 600 0.95rem/1.2 var(--font);
	cursor: pointer;
	transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.form button[type="submit"]:not(.button):hover { transform: translateY(-1px); box-shadow: var(--shadow); }
.form button[type="submit"]:not(.button)[disabled] { opacity: 0.55; transform: none; cursor: default; }
/* A submit the page already dressed as one of the buttons: it is still the form's last row,
   and the grid's own gap is the space between a label and its field, never between a field
   and the button that sends it. */
.form button[type="submit"].button { margin-top: 0.9rem; }

/* Where a payment gateway draws its own card field, and what it draws around it. The box
   is the gateway's — its iframe, its validation — so the page cannot style what is inside
   it; the page gives it an input's shape and some space. */
.gatewayslot { display: grid; gap: 0.75rem; }
.cardfield {
	padding: 0.65rem 0.7rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-control);
	background: var(--surface);
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.cardfield.is-focus { border-color: var(--accent); box-shadow: var(--ring); }
.cardrow { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }

/* A field with a control living inside it — the password eye. */
.field-affix { position: relative; }
.field-affix input { padding-inline-end: 3rem; }
.field-affix .affix {
	position: absolute;
	inset-inline-end: 0.6rem;
	top: 50%;
	transform: translateY(-50%);
	display: inline-flex;
	padding: 0.4rem;
	border: 0;
	border-radius: 8px;
	background: none;
	color: var(--muted);
	cursor: pointer;
}
.field-affix .affix:hover { color: var(--text); background: var(--bg); }
.field-affix .affix svg { width: 1.15rem; height: 1.15rem; }

.status { min-height: 1.6em; margin: 0.9rem 0 0; color: var(--muted); font-size: 0.95rem; }
.status.error { color: var(--bad); }

/* The answer a form gives when it lands: the fields give way to the confirmation, in the
   same box the person was looking at. A line of grey text under a form that has just
   emptied itself does not tell anybody whether they sent something. */

.form-done {
	display: grid;
	justify-items: center;
	text-align: center;
	gap: 0.45rem;
	padding: 0.6rem 0 0.2rem;
}

.form-done-mark {
	display: grid;
	place-items: center;
	width: 3rem;
	height: 3rem;
	margin-bottom: 0.35rem;
	border-radius: 50%;
	background: color-mix(in srgb, var(--ok) 14%, var(--surface));
	color: var(--ok);
	font-size: 1.45rem;
	line-height: 1;
}

.form-done h2 { margin: 0; font-size: 1.25rem; font-weight: 600; letter-spacing: 0; }
.form-done p { margin: 0; max-width: 26rem; color: var(--muted); }
.form-done .linklike { margin-top: 0.7rem; }

/* A button that is waiting says so where the person pressed, not in a line below it. */
.is-busy::before {
	content: "";
	flex: none;
	width: 0.9em;
	height: 0.9em;
	border: 2px solid currentColor;
	border-top-color: transparent;
	border-radius: 50%;
	animation: spin 0.7s linear infinite;
}

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

@media (prefers-reduced-motion: reduce) {
	.is-busy::before { animation: none; }
}

/* Badges and notices: a state, coloured by what it means --------------------------- */

.badge {
	display: inline-block;
	padding: 0.3rem 0.75rem;
	border-radius: 999px;
	font-size: 0.8rem;
	font-weight: 600;
	white-space: nowrap;
	background: color-mix(in srgb, var(--text) 8%, var(--surface));
	color: var(--muted);
}
.badge.is-ok { background: color-mix(in srgb, var(--ok) 14%, var(--surface)); color: var(--ok); }
.badge.is-pending { background: color-mix(in srgb, var(--warn) 16%, var(--surface)); color: var(--warn); }
.badge.is-cancelled { background: color-mix(in srgb, var(--bad) 12%, var(--surface)); color: var(--bad); }

.notice {
	margin: 0;
	padding: 0.95rem 1.2rem;
	border-radius: var(--radius-sm);
	font-weight: 600;
	background: color-mix(in srgb, var(--text) 8%, var(--surface));
}
.notice.is-ok { background: color-mix(in srgb, var(--ok) 14%, var(--surface)); color: var(--ok); }
.notice.is-pending { background: color-mix(in srgb, var(--warn) 16%, var(--surface)); color: var(--warn); }
.notice.is-cancelled { background: color-mix(in srgb, var(--bad) 12%, var(--surface)); color: var(--bad); }
.notice.is-error { background: color-mix(in srgb, var(--bad) 12%, var(--surface)); color: var(--bad); }

/* Modal: the one thing a page interrupts you with — an act it will not undo -------- */

.modal {
	width: min(30rem, calc(100vw - 2rem));
	/* The reset zeroes every margin; a dialog centers itself only through margin: auto. */
	margin: auto;
	padding: 0;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface);
	color: var(--text);
	box-shadow: var(--shadow);
}
.modal::backdrop { background: color-mix(in srgb, var(--text) 45%, transparent); }

.modal-body { padding: clamp(1.4rem, 3vw, 2rem) clamp(1.4rem, 3vw, 2rem) 0; }
.modal-body h2 { margin: 0 0 0.6rem; font-size: 1.25rem; font-weight: 600; }
.modal-body p { margin: 0; color: var(--muted); }
.modal-input {
	width: 100%;
	margin-top: 1rem;
	padding: 0.5rem 0.7rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-control);
	font: inherit;
	font-size: 0.95rem;
	color: inherit;
	background: var(--surface);
}

/* The decision lives at the bottom, where a reader finishes and where every OS puts it:
   the primary last, Cancel beside it. */
.modal-actions {
	display: flex;
	flex-wrap: wrap;
	justify-content: flex-end;
	gap: 0.6rem;
	padding: clamp(1.4rem, 3vw, 2rem);
}

/* Tabs and chips: one choice among a few ------------------------------------------- */

.tabs { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem; }

.tab {
	display: inline-flex;
	align-items: center;
	gap: 0.6rem;
	padding: 0.55rem 1.3rem;
	border: 1.5px solid var(--line);
	border-radius: var(--radius-sm);
	background: var(--surface);
	font: 600 0.95rem/1.2 var(--font);
	color: var(--text);
	cursor: pointer;
	transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.tab:hover { transform: translateY(-1px); box-shadow: var(--shadow); }
.tab.is-active { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast); }
a.tab { text-decoration: none; }

/* The date field (widgets.js) ------------------------------------------------------ */

.datefield {
	display: inline-flex;
	align-items: center;
	gap: 0.6rem;
	padding: 0.7rem 1.2rem;
	border: 1.5px solid var(--line);
	border-radius: var(--radius-sm);
	background: var(--surface);
	font: 600 1rem/1.2 var(--font);
	color: var(--text);
	cursor: pointer;
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.datefield:hover { border-color: var(--muted); box-shadow: var(--shadow); }
.datefield svg { width: 1.1rem; height: 1.1rem; color: var(--accent-text); }
.datefield-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

.datepop {
	position: absolute;
	z-index: 20;
	margin-top: 0.5rem;
	padding: 1rem;
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	box-shadow: var(--shadow);
	animation: pop 0.16s ease;
}

.datepop-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	margin-bottom: 0.7rem;
}

.datepop-nav {
	width: 2.1rem;
	height: 2.1rem;
	border: 0;
	border-radius: 50%;
	background: var(--bg);
	color: var(--text);
	font-size: 1.05rem;
	cursor: pointer;
}
.datepop-nav:hover { background: var(--line); }

.datepop-grid {
	display: grid;
	grid-template-columns: repeat(7, 2.3rem);
	gap: 2px;
	justify-items: stretch;
}

.datepop-dow {
	font: 600 0.7rem/2 var(--font);
	text-transform: uppercase;
	text-align: center;
	color: var(--muted);
}

.datepop-day {
	height: 2.3rem;
	border: 0;
	border-radius: 50%;
	background: none;
	font: 500 0.92rem/1 var(--font);
	color: var(--text);
	cursor: pointer;
}
.datepop-day:hover { background: var(--bg); }
.datepop-day.is-today { box-shadow: inset 0 0 0 1.5px var(--line); }
.datepop-day.is-selected { background: var(--accent); color: var(--accent-contrast); font-weight: 700; }
.datepop-day:disabled { color: var(--line); cursor: default; background: none; }

@keyframes pop {
	from { opacity: 0; transform: translateY(-4px); }
}

/* Picking a moment: a strip of days, then the free times of the chosen one -------------
   The same two pieces wherever something is booked — a court, a class, a meeting — so a
   visitor who has taken one thing on the site already knows how to take the next. The
   strip sits at the top of its card (padding 0), the grid in the card's body. */

.daystrip {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	padding: 0.6rem 0.9rem;
	border-bottom: 1px solid var(--line);
	background: color-mix(in srgb, var(--text) 3%, var(--surface));
}

.daynav {
	flex: none;
	width: 2rem;
	height: 2rem;
	border: 1px solid var(--line);
	border-radius: 50%;
	background: var(--surface);
	color: var(--text);
	font-size: 1.1rem;
	cursor: pointer;
	transition: border-color 0.15s ease;
}
.daynav:hover { border-color: var(--text); }
.daynav[disabled] { opacity: 0.35; cursor: default; }

.days {
	display: grid;
	grid-template-columns: repeat(7, 1fr);
	gap: 0.4rem;
	flex: 1;
	min-width: 0;
}

.day {
	display: grid;
	justify-items: center;
	gap: 0.05rem;
	padding: 0.4rem 0.25rem;
	border: 1px solid transparent;
	border-radius: var(--radius-sm);
	background: transparent;
	color: var(--text);
	font: inherit;
	line-height: 1.2;
	cursor: pointer;
	transition: background 0.15s ease, border-color 0.15s ease;
}
.day:hover { border-color: var(--line); background: var(--surface); }
.day-name { font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.day-num { font-size: 1.05rem; font-weight: 600; }
.day.is-active { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast); }
.day.is-active .day-name { color: var(--accent-contrast); opacity: 0.8; }

@media (max-width: 48rem) {
	.day-name { font-size: 0.7rem; }
}

.timegrid {
	display: grid;
	gap: 0.45rem;
	grid-template-columns: repeat(auto-fill, minmax(5.25rem, 1fr));
}

.time {
	padding: 0.5rem 0.4rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-sm);
	background: var(--surface);
	color: var(--text);
	font: 600 0.95rem/1.2 var(--font);
	cursor: pointer;
	transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}
.time:hover { border-color: var(--accent); background: var(--accent); color: var(--accent-contrast); }

/* A chip says its own price only when the day holds more than one, so the clock keeps the
   grid's rhythm on every ordinary day. */
.time-when { display: block; }
.time-price {
	display: block;
	margin-top: 0.1rem;
	font-size: 0.78rem;
	font-weight: 500;
	color: var(--muted);
}
.time:hover .time-price { color: inherit; opacity: 0.85; }

/* The card the two pieces sit in, and the SERVICE column beside them -------------------
   The strip sits flush at the top (padding 0, clipped corners) and the body is what the
   chosen day offers: the things that can be taken on the left, their free times on the
   right. A page with one thing to take (a meeting) writes its own body and uses the label
   alone. */

/* One area is no choice, so the picker above the card is not drawn. */
.areas { margin-bottom: 1rem; }
.areas:has(> :only-child) { display: none; }

.booking { padding: 0; overflow: hidden; }

.booking-label {
	margin: 0 0 0.7rem;
	font-size: 0.75rem;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--muted);
}

.booking-body {
	display: grid;
	grid-template-columns: minmax(14rem, 17rem) 1fr;
}
.services { padding: 1rem 1rem 1.25rem; border-inline-end: 1px solid var(--line); }
.times { padding: 1rem 1rem 1.25rem; }

.service {
	display: flex;
	align-items: center;
	gap: 0.85rem;
	width: 100%;
	padding: 0.6rem 0.85rem;
	margin-bottom: 0.35rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-sm);
	background: var(--surface);
	color: var(--text);
	font: inherit;
	text-align: start;
	cursor: pointer;
	transition: border-color 0.15s ease, background 0.15s ease;
}
.service:hover { border-color: var(--text); }
.service.is-active { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.service.is-empty { opacity: 0.5; }
.service-name { font-weight: 600; }
.service-meta { display: block; font-size: 0.85rem; color: var(--muted); }
.service-price { margin-inline-start: auto; font-weight: 600; white-space: nowrap; }

.times .status { margin: 0.2rem 0 0; }

@media (max-width: 48rem) {
	.booking-body { grid-template-columns: 1fr; }
	.services { border-inline-end: 0; border-bottom: 1px solid var(--line); }
}

/* A summary and its total: what is being taken, one line each, and the sum under a rule -- */

.summary-line {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	margin: 0.3rem 0 0;
	color: var(--muted);
}

.summary-total {
	display: flex;
	justify-content: space-between;
	margin: 0.9rem 0 0;
	padding-top: 0.9rem;
	border-top: 1px solid var(--line);
	font-size: 1.1rem;
}

/* The ways to pay: one radio row per method or kept card ------------------------------- */

.paymethods { display: grid; gap: 0.5rem; }

.paymethod {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.9rem 1.1rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-sm);
	background: var(--surface);
	cursor: pointer;
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.paymethod:hover { border-color: var(--muted); }
.paymethod:has(input:checked) { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
/* The tick is INK, so it is the accent's ink token: a palette whose accent is a pale
   yellow draws a checked radio nobody can tell from an empty one. */
.paymethod input { width: 1.05rem; height: 1.05rem; accent-color: var(--accent-text); }

/* The card brands a merchant is required to show: monochrome-neutral, small, in the foot. */
/* The card brands a merchant shows, each on a small white card of its own: the marks are
   drawn for a light ground and must read on a dark footer too. */
.paybrands, .pay-brands { display: inline-flex; align-items: center; gap: 0.5rem; }
.paybrands img, .pay-brands img {
	display: block;
	box-sizing: content-box;
	height: 0.95rem;
	width: auto;
	padding: 0.3rem 0.45rem;
	border-radius: 0.3rem;
	background: white;
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--text) 12%, transparent);
}

/* The tab bar -----------------------------------------------------------------------
   A template hides its menu links on a phone; site.js gives them back in a tab bar floating
   at the bottom, the account as the last tab (docs/WEBSITE.md, "The bar"). A header that
   lays its menu out for a phone itself never sees it. */

.tabbar {
	position: fixed;
	inset-inline: 0.75rem;
	bottom: calc(0.75rem + env(safe-area-inset-bottom));
	z-index: 40;
	display: flex;
	gap: 0.15rem;
	max-width: 32rem;
	margin-inline: auto;
	padding: 0.35rem;
	border-radius: 2rem;
	background: color-mix(in srgb, var(--bg) 82%, transparent);
	-webkit-backdrop-filter: saturate(1.8) blur(18px);
	backdrop-filter: saturate(1.8) blur(18px);
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--text) 6%, transparent),
		0 10px 30px color-mix(in srgb, var(--text) 16%, transparent);
}
.tabbar-tab {
	flex: 1 1 0;
	min-width: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.15rem;
	padding: 0.45rem 0.2rem 0.35rem;
	border: 0;
	border-radius: 1.6rem;
	background: none;
	color: var(--text);
	font: 600 0.6875rem/1.2 var(--font);
	text-decoration: none;
	cursor: pointer;
}
.tabbar-tab[aria-current="page"], .tabbar-tab[aria-expanded="true"] {
	background: color-mix(in srgb, var(--text) 7%, transparent);
	color: var(--accent-text);
}
/* A label of two words ("Book a court") takes two lines rather than losing its end. */
.tabbar-label {
	max-width: 100%;
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	overflow: hidden;
	text-align: center;
	overflow-wrap: break-word;
	text-wrap: balance;
}
.tabbar-icon {
	width: 1.5rem;
	height: 1.5rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.9;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.tabbar-sheet {
	position: fixed;
	inset-inline: 0.75rem;
	bottom: calc(5.5rem + env(safe-area-inset-bottom));
	z-index: 40;
	display: grid;
	max-width: 32rem;
	margin-inline: auto;
	padding: 0.4rem;
	border-radius: var(--radius);
	background: var(--bg);
	box-shadow: var(--shadow), 0 0 0 1px color-mix(in srgb, var(--text) 6%, transparent);
}
.tabbar-sheet-link {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.75rem 0.8rem;
	border-radius: var(--radius-sm);
	color: var(--text);
	font: 500 1rem/1.3 var(--font);
	text-decoration: none;
}
.tabbar-sheet-link[aria-current="page"] { color: var(--accent-text); }
.tabbar-sheet-link:hover { background: color-mix(in srgb, var(--text) 6%, transparent); }
.tabbar-sheet-langs { display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0.6rem 0.8rem; }
.tabbar-sheet-langs a { color: var(--muted); font-weight: 600; text-decoration: none; }

/* While the tab bar shows, the page ends above it instead of under it. */
.has-tabbar body { padding-bottom: calc(5.5rem + env(safe-area-inset-bottom)); }

/* The account corner: who is signed in, and the two things they do about it ---------
   The header carries [data-account]; the platform's site.js fills it with the member's
   avatar (a photo, or the initial of their name) and a small menu — My account, Sign out —
   once it knows somebody is signed in. A stranger keeps the plain Sign in link. */

.account { position: relative; display: inline-flex; align-items: center; }
.account-link { text-decoration: none; color: var(--muted); font-weight: 500; }
.account-link:hover { color: var(--text); }

.avatar {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	padding: 0;
	border: 1px solid var(--line);
	border-radius: 50%;
	background: color-mix(in srgb, var(--accent) 16%, var(--surface));
	color: var(--accent-text);
	font: 700 0.9rem/1 var(--font);
	cursor: pointer;
	overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar:hover { border-color: var(--muted); }

/* Fixed and hung off the body (site.js), so no rule the site writes about its own header
   can hide what is in it; the script puts it under the avatar. */
.account-pop {
	position: fixed;
	z-index: 900;
	min-width: 14rem;
	padding: 0.5rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-sm);
	background: var(--surface);
	box-shadow: var(--shadow);
	animation: pop 0.12s ease;
}
.account-who { display: grid; padding: 0.4rem 0.6rem 0.6rem; border-bottom: 1px solid var(--line); margin-bottom: 0.3rem; }
.account-who strong { font-size: 0.95rem; }
.account-who span { font-size: 0.82rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; }
.account-pop a, .account-pop button {
	display: block;
	width: 100%;
	padding: 0.45rem 0.6rem;
	border: 0;
	border-radius: 6px;
	background: none;
	color: var(--text);
	font: 500 0.92rem/1.3 var(--font);
	text-align: start;
	text-decoration: none;
	cursor: pointer;
}
.account-pop a:hover, .account-pop button:hover { background: var(--bg); }

/* Language is a visible header control, outside the navigation that folds into a drawer —
   ONE button beside the account, which site.js opens onto the list. The markup ships plain
   links, so a page with no JavaScript still switches language; there they read as a row of
   autonyms, which keep the way out legible even to a visitor who cannot read the page. */
.site-languages {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.25rem;
	flex-shrink: 0;
	font-size: 0.8125rem;
	line-height: 1.2;
}
.site-languages a {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding: 0.5rem;
	color: inherit;
	text-decoration: none;
	white-space: nowrap;
	border-radius: var(--radius-control);
}
.site-languages a:hover { text-decoration: underline; text-underline-offset: 0.3em; }
.site-languages a[aria-current] { font-weight: 750; text-decoration: underline; text-underline-offset: 0.3em; }

.lang-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	padding: 0;
	border: 0;
	border-radius: var(--radius-control);
	background: none;
	color: inherit;
	cursor: pointer;
}
.lang-button:hover { background: color-mix(in srgb, var(--text) 8%, transparent); }
.lang-pop { min-width: 11rem; }
.lang-pop a[aria-current] { font-weight: 750; }

/* THE ACCOUNT IS ALWAYS THE LAST THING IN THE BAR — every template, every width
   (docs/WEBSITE.md). Wherever a header writes it, site.js moves it to the end of the bar
   and this keeps it there; the name holds the start — whatever the header calls it, .brand
   or .logo, because both are written and a bar that names it otherwise would silently stop
   holding anything — and the menu, the language and the account travel together at the other
   end. */
.topbar-inner > .account,
.topbar-inner > :has(.account) {
	order: 99;
}
.topbar-inner:has(> .account) { justify-content: flex-start; }
.topbar-inner:has(> .account) > .brand,
.topbar-inner:has(> .account) > .logo { margin-inline-end: auto; }

/* A background slideshow leaves the heading and booking action still. */
.hero-slides { position: absolute; inset: 0; z-index: -2; overflow: hidden; }
.hero-slides [data-slide] { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 1.2s ease; }
.hero-slides [data-slide].is-active { opacity: 1; }
.slideshow-controls { display: flex; align-items: center; gap: 0.35rem; color: inherit; }
.slideshow-controls[hidden] { display: none; }
.slideshow-controls [data-slide-dots] { display: flex; align-items: center; }
.slideshow-controls button { display: grid; place-items: center; width: 28px; height: 32px; padding: 0; border: 0; border-radius: 999px; background: transparent; color: inherit; cursor: pointer; }
.slideshow-controls .slideshow-dot::before { content: ""; width: 6px; height: 6px; border-radius: 999px; background: currentColor; opacity: 0.45; transition: width 0.25s, opacity 0.25s; }
.slideshow-controls .slideshow-dot[aria-pressed="true"] { width: 36px; }
.slideshow-controls .slideshow-dot[aria-pressed="true"]::before { width: 22px; opacity: 1; }
.slideshow-controls button:hover { background: #FFFFFF18; }
.slideshow-controls button:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
.slideshow-controls [data-slide-pause] { width: 32px; font-size: 0.8rem; opacity: 0.7; }
.slideshow-controls [data-slide-pause]:hover { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .hero-slides [data-slide] { transition: none; } }

/* The end of the bar: the language and the account, last. site.js makes one when a header
   has none. */
.topbar-end { display: flex; align-items: center; gap: 1rem; }

/* The bar's account link with somebody signed in: a circle with their photo or initials, which
   opens their menu (client/assets/site.js, mountAccount). */
.account-link.is-signed-in { display: inline-flex; padding: 0; border: 0; border-radius: 50%; }
.account-link .account-photo,
.account-link .account-initials { width: 36px; height: 36px; flex-shrink: 0; border-radius: 50%; }
.account-link .account-photo { object-fit: cover; }
.account-link .account-initials { display: inline-grid; place-items: center; background: var(--accent); color: var(--accent-contrast, #fff); font-size: 0.85rem; font-weight: 650; }
.account-error { margin: 0.3rem 0.6rem 0; color: var(--danger, #b42318); font-size: 0.85rem; }
