/**
 * Booking UI: the GHL modal and the docked scheduling CTA.
 *
 * On-site container for the GoHighLevel booking widget opened from the Locations
 * finder, plus the site-wide docked Schedule bar. Enqueued directly (not via
 * theme.scss) so it applies on a plain file sync without a SCSS recompile.
 * The modal (z-index 100000) sits above the docked CTA (99990).
 */

html.booking-modal-open {
	overflow: hidden;
}

.booking-modal {
	position: fixed;
	inset: 0;
	z-index: 100000;
	display: flex;
	align-items: stretch;
	justify-content: flex-end;
	box-sizing: border-box;
}

/* [hidden] alone loses to display:flex, so hide explicitly. */
.booking-modal[hidden] {
	display: none;
}

.booking-modal__overlay {
	position: absolute;
	inset: 0;
	background: rgba(0, 0, 0, 0.6);
	cursor: pointer;
}

/* A drawer, not a centered card: 720px is the widest the dialog ever was, and full viewport
   height is the whole point — the GHL widget's md variant stacks at this width and runs
   700-850px tall, so every pixel of height counts. Rounded only on the open edge. */
.booking-modal__dialog {
	position: relative;
	display: flex;
	flex-direction: column;
	width: min(720px, 100%);
	height: 100%;
	background: var(--ab-white, #FFFFFF);
	border-radius: 16px 0 0 16px;
	box-shadow: -24px 0 60px rgba(28, 52, 84, 0.35); /* token ink */
	overflow: hidden;
}

.booking-modal__close {
	position: absolute;
	top: 10px;
	right: 10px;
	z-index: 1;
	width: 40px;
	height: 40px;
	padding: 0;
	border: none;
	border-radius: 50%;
	background: rgba(0, 0, 0, 0.06);
	color: var(--ab-ink);
	font-size: 26px; /* no token — deliberate */
	line-height: 1;
	cursor: pointer;
	transition: background 0.2s ease, color 0.2s ease;
}

.booking-modal__close:hover,
.booking-modal__close:focus-visible {
	background: var(--ab-b600, #4981C2);
	color: var(--ab-white, #FFFFFF);
}

/* Was `outline: none`, substituting a background change for the focus ring — the one place in
   the library that removed it. offset 2 rather than the house 3: the button sits 10px from the
   dialog's clipping edge. */
.booking-modal__close:focus-visible {
	outline: 3px solid var(--ab-b700, #3A6DA6);
	outline-offset: 2px;
}

.booking-modal__body {
	flex: 1 1 auto;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
	/* The list scrolls, the page behind it does not. */
	overscroll-behavior: contain;
}

/* The frame fills whatever the header leaves and scrolls inside (scrolling="yes"), so the drawer
   never shows dead space under the widget and the visitor has one scrollbar, not two.

   The !important lines are deliberate, and each undoes something GHL's form_embed.js writes
   inline. `height`: the script sizes the frame from its own measurement of the widget, and that
   measurement stops short of the form, which left a blank block under Continue and a second
   scrollbar (QA Sep 3, card 55). The other five: on load the script hides every frame already
   pointing at its domain (opacity 0, visibility hidden, parked at left:-9999px) and waits for a
   popup trigger an in-page embed never sends, so the FIRST clinic picked in a session opened
   as an empty panel (measured Sep 4 on local and staging; a second pick mounts after the
   script has loaded and was left alone, which is why it always worked). Inline styles lose to
   !important, so the drawer stops depending on the script's timing while the script keeps
   doing what it is loaded for: answering the widget's query-param and sticky-contact messages. */
.booking-modal__body iframe,
.booking-modal__frame iframe {
	display: block !important;
	position: static !important;
	left: auto !important;
	width: 100%;
	height: 100% !important;
	min-height: 0;
	border: 0;
	opacity: 1 !important;
	visibility: visible !important;
	pointer-events: auto !important;
}

/* The inline block's frame gets the same immunity (Sep 6): shortcodes/blocks/booking-embed.php
   now prints form_embed.js after the iframe so GHL receives the page's UTMs, and the script
   parks any frame it claims with the same inline styles. Height is left to the block (its
   `height` parameter) or to the script's auto-resize, whichever writes last. */
.ab .ab-booking__frame {
	display: block !important;
	position: static !important;
	left: auto !important;
	opacity: 1 !important;
	visibility: visible !important;
	pointer-events: auto !important;
}

@media (max-width: 600px) {
	.booking-modal__dialog {
		width: 100%;
		border-radius: 0;
	}

}

/* -------------------------------------------------------------------------
   Docked scheduling CTA
   Client decision 2026-08-19: keep the persistent Schedule button, but docked
   — it must never cover content or controls. Below 1024 it is a full-width
   bar on the bottom edge and the body reserves its height, so even the
   footer's last line clears it. From 1024 up it is a corner pill on the house
   button recipe: Blue 600 to Blue 700 on hover, one step up the shadow
   ladder, light instead of movement. The var() fallbacks keep the bar on
   brand if ability-tokens.css ever fails to load ahead of this sheet.
   ---------------------------------------------------------------------- */

:root {
	--ability-dock-h: 56px;
}

.ability-cta-dock {
	position: fixed;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 99990;
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: calc(var(--ability-dock-h) + env(safe-area-inset-bottom, 0px));
	padding: 0 var(--ab-gutter, 18px) env(safe-area-inset-bottom, 0px);
	/* Blue 700, not 600. The label is 13px/700, which WCAG counts as normal text and so
	   needs 4.5:1; white on Blue 600 measures 4.03:1 and fails, Blue 700 gives 5.36:1.
	   Same fix as the review ribbon, same reason. */
	background: var(--ab-b700, #3A6DA6);
	/* The ring is what keeps this thing findable. It is Blue 700 and it floats over a footer
	   that is also Blue 700 — a 1:1 boundary — so on the last screen of every page the pill
	   simply stopped existing and left its label hanging in the footer. --ab-ring-on-dark is
	   the design system's answer to that ground and carries the arithmetic; the same token
	   rings ability_button's on-dark variant, so the docked CTA and an authored button on a
	   dark band cannot end up wearing two different edges.
	   An inset shadow rather than a border because this element sizes by height/min-height
	   with no border to colour: a real border would grow it by 4px, move the mobile bar's
	   reserved strip out of step, and unpick the pill's 52px match with .ab-btn.

	   Transparent at rest, and declared here rather than only on .is-over-dark so the shadow
	   LIST never changes shape: box-shadow interpolates only between lists of the same length,
	   so a rule that added the ring instead of recolouring it would snap it on. Only the
	   colour moves, which is what makes the fade possible at all.

	   The ring is off over light ground on purpose. It exists to draw a boundary the ground
	   cannot; over white or Blue 100 the fill already is the boundary, and a permanent light
	   edge there reads as an inner bevel nobody asked for. booking.js turns it on ~120px
	   before the dock reaches a dark band, so it has finished arriving by the time it is
	   needed. */
	--ability-dock-ring: transparent;
	box-shadow: inset 0 0 0 2px var(--ability-dock-ring);
	color: var(--ab-white, #FFFFFF);
	font-family: var(--ab-font, 'Inter', sans-serif);
	font-size: var(--ab-t-button, 13px);
	font-weight: 700;
	line-height: 1;
	letter-spacing: .06em;
	text-decoration: none;
	text-transform: uppercase;
	visibility: hidden;
	transform: translateY(100%);
	/* The ring is the one thing here that must NOT use --ab-ease-out.
	 *
	 * That curve is the house entrance — cubic-bezier(.22, 1, .32, 1) — and it is shaped to
	 * get something on screen fast and settle it slowly. Measured on this element at .28s it
	 * put the ring at 37% opacity after 40ms and 96% after 90ms: the whole fade happened in
	 * the first third of its own duration and read as a hard switch, which is exactly what it
	 * was supposed to avoid. An entrance curve is right for a thing arriving from somewhere
	 * and wrong for a colour becoming another colour.
	 *
	 * Linear, because a fade between two colours has no arrival to shape — every frame should
	 * carry the same amount of change or the eye finds the fast part and calls it a snap. And
	 * .4s rather than .28s so the change has enough frames to be seen at all. The fills keep
	 * their .16s: those are pointer feedback and want to feel immediate. */
	transition: transform .3s var(--ab-ease-out), visibility .3s, background .16s ease, box-shadow .4s linear;
}

.ability-cta-dock.is-on {
	visibility: visible;
	transform: none;
}

/* `is-over-dark` is set by booking.js 280px before a dark band arrives. Nothing reads it any
   more: it existed for the desktop corner pill's ring, and there is no desktop pill (see the
   1024-up block below). The class still ships because the lead is cheap and a future on-dark
   desktop treatment would want it; the mobile button answers to `is-on-dark` instead, which
   booking.js sets with no lead at all. See the block at the end of this file. */

.ability-cta-dock:hover,
.ability-cta-dock:focus-visible {
	background: var(--ab-b700, #3A6DA6);
	/* The base is already Blue 700 and the system has no Blue 800, so hover darkens the
	   ground with a black wash instead of another token. It sits under the label rather
	   than over it, so the contrast ratio only ever improves on hover.
	   The ring is restated first: inset shadows paint in source order with the first on top,
	   so listing it ahead of the 100vmax wash keeps the edge out from under it. It stays on
	   the custom property, so hovering over light ground does not conjure an edge that the
	   scroll position says should not be there. */
	box-shadow:
		inset 0 0 0 2px var(--ability-dock-ring),
		inset 0 0 0 100vmax rgba(0, 0, 0, .10);
	color: var(--ab-white, #FFFFFF);
	text-decoration: none;
}

/* Over a dark band the ring goes solid under the pointer — 4.7:1 on Blue 700, so the edge
   sharpens rather than being dimmed by the very wash that signals the hover. Two classes and
   a pseudo (0,3,0) over .is-over-dark's (0,2,0), so this wins where it applies and nowhere
   else. */
.ability-cta-dock.is-over-dark:hover,
.ability-cta-dock.is-over-dark:focus-visible {
	--ability-dock-ring: var(--ab-white, #FFFFFF);
}

/* The reserved strip — the bar may cover this padding and nothing else. Scoped to the class
   functions.php sets from the same predicate that decides whether the bar is emitted at all: a
   page with its own scheduler gets neither, and reserving a strip for a bar that is not there
   would leave 56px of dead white under the footer. */
@media (max-width: 1023.98px) {
	body.ability-has-dock {
		padding-bottom: calc(var(--ability-dock-h) + env(safe-area-inset-bottom, 0px));
	}

	/* TheGem's scroll-top button sits near the bottom edge; lift it clear of
	   the bar — "docked" means covering no controls, ours or the theme's.
	   !important matches the existing left/right override in theme.scss. */
	body.ability-has-dock .scroll-top-button {
		bottom: calc(var(--ability-dock-h) + env(safe-area-inset-bottom, 0px) + 12px) !important;
	}
}

/* From 1024 up there is no dock at all.
 *
 * The header carries "Schedule Appointment" on desktop and it never leaves the screen, so a
 * corner pill is the same action twice, forty pixels from the edge, competing with the one
 * the visitor already knows. Below 1024 the header collapses into a hamburger and that
 * Schedule goes with it — which is the whole reason this element exists.
 *
 * This replaces the corner pill that used to be authored here. Two things went with it, and
 * both were only ever the pill's problems: the on-dark ring (a floating pill over a Blue 700
 * footer leaves a 1:1 boundary and needs an edge drawn for it — a bar pinned to the viewport
 * edge does not), and the `--wide-off` suppression for pages with their own scheduler (the
 * argument was always about a pill competing with a chooser the size of the page, and at
 * phone widths it floats over nothing). The class still ships from functions.php; below 1024
 * it is inert, which is where the bar belongs.
 *
 * Everything positional in the base rule is edge-to-edge (`left: 0; right: 0`), so hiding it
 * here is also what keeps a full-width blue band off the bottom of every desktop page. */
@media (min-width: 1024px) {
	.ability-cta-dock {
		display: none;
	}

	/* No bar, nothing to reserve, and TheGem's scroll-top button gets its corner back. */
	body.ability-has-dock {
		padding-bottom: 0;
	}

	body.ability-has-dock .scroll-top-button {
		bottom: var(--ab-gutter, 40px) !important;
	}
}


/* -------------------------------------------------------------------------
   Booking modal — the clinic picker
   Every GoHighLevel calendar belongs to one clinic, so "Schedule" from a page
   that does not name one is not yet an answerable action. The picker asks,
   then loads that clinic's real calendar in the same dialog: the question the
   /locations/ redirect used to ask by navigating away.
   This is the library's third clinic-list anatomy, after the finder's cards
   and the location switcher's rows. It earns the separate name by being a
   different thing — no status pill, no distance, no address, and it lives
   outside any .ab root, so .ab-lsw__* rules cannot reach it. The chevron path
   data is copied from the switcher verbatim so the two cannot drift.
   ---------------------------------------------------------------------- */

.booking-modal__view {
	display: flex;
	flex-direction: column;
	min-height: 0;
}

/* [hidden] loses to display:flex, same as the modal root above. */
.booking-modal__view[hidden] {
	display: none;
}

/* The 56px clears .booking-modal__close, which is absolutely positioned 10px from the top right
   at 40px wide. Without it the title sits under the button. */
.booking-modal__head {
	padding: 24px 56px 12px 24px;
}

/* The element is named because this dialog lives outside .ab: TheGem styles bare h2, and
   theme.scss:95 locks heading margins with !important, so only !important wins here (same
   neutralisation ability-blocks.css applies for .ab headings). Without it the 25px bottom
   margin survives and, in the drawer's centered flex head, floats the title above the back
   pill. All spacing here is padding for the same reason. */
.booking-modal h2.booking-modal__title {
	margin: 0 !important;
	color: var(--ab-ink, #333333);
	font-family: var(--ab-font, 'Inter', sans-serif);
	font-size: 22px;
	font-weight: 700;
	line-height: 1.25;
	letter-spacing: 0;
	text-transform: none;
}

.booking-modal__sub {
	margin: 0;
	padding-top: 6px;
	color: var(--ab-ink2, #5F5F5F);
	font-family: var(--ab-font, 'Inter', sans-serif);
	font-size: 14px;
	line-height: 1.5;
}

.booking-modal__list {
	padding: 0 24px 24px;
}

.booking-modal__group {
	margin: 0;
	padding: 20px 0 6px;
	color: var(--ab-b600, #4981C2);
	font-family: var(--ab-font, 'Inter', sans-serif);
	font-size: var(--ab-t-eyebrow, 11px);
	font-weight: 700;
	letter-spacing: .16em;
	line-height: 1.2;
	text-transform: uppercase;
}

.booking-modal__opt {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	width: 100%;
	min-height: 64px;
	padding: 12px 4px;
	border: 0;
	border-bottom: 1px solid var(--ab-bd, #DDE5EC);
	background: none;
	color: var(--ab-ink, #333333);
	cursor: pointer;
	font-family: var(--ab-font, 'Inter', sans-serif);
	text-align: left;
	text-decoration: none;
	transition: background .16s ease;
}

.booking-modal__opt:hover {
	background: var(--ab-b50, #F7FAFD);
	color: var(--ab-ink, #333333);
	text-decoration: none;
}

/* Inside, not outside: the dialog is overflow:hidden, so a ring drawn outward from a row at the
   top or bottom edge of the list is clipped away. */
.booking-modal__opt:focus-visible {
	outline: 3px solid var(--ab-b700, #3A6DA6);
	outline-offset: -3px;
}

.booking-modal__opt-body {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.booking-modal__opt-name {
	font-size: 16px;
	font-weight: 700;
	line-height: 1.3;
}

/* The finder's Coming Soon pair, duplicated on purpose: booking.css is enqueued
   independently of ability-blocks.css (see the drawer animation note below). */
.booking-modal__soon {
	display: inline-flex;
	align-items: center;
	margin-left: 10px;
	padding: 3px 9px;
	border-radius: 999px;
	background: #FBE7D6;
	color: #96430F;
	font-size: 10.5px;
	font-weight: 800;
	letter-spacing: 0.02em;
	white-space: nowrap;
	vertical-align: middle;
}

.booking-modal__opt-meta {
	color: var(--ab-ink3, #7B8794);
	font-size: 13px;
	line-height: 1.3;
}

.booking-modal__go {
	flex: 0 0 auto;
	width: 16px;
	height: 16px;
	color: var(--ab-b600, #4981C2);
}

/* A clinic with no calendar is a link to its page, and has to read as one before the click
   rather than after it. */
.booking-modal__opt--page .booking-modal__go {
	color: var(--ab-ink3, #7B8794);
}

.booking-modal__empty {
	margin: 0;
	padding: 0 24px 24px;
	color: var(--ab-ink2, #5F5F5F);
	font-family: var(--ab-font, 'Inter', sans-serif);
	font-size: 14px;
}

.booking-modal__head--cal {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 16px 56px 12px 16px;
}

.booking-modal__back {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 8px 14px 8px 10px;
	border: 0;
	border-radius: var(--ab-r-pill, 999px);
	background: var(--ab-b100, #EEF4FA);
	color: var(--ab-b700, #3A6DA6);
	cursor: pointer;
	font-family: var(--ab-font, 'Inter', sans-serif);
	font-size: 13px;
	font-weight: 700;
	line-height: 1;
}

.booking-modal__back:focus-visible {
	outline: 3px solid var(--ab-b700, #3A6DA6);
	outline-offset: 2px;
}

.booking-modal__back-chv {
	width: 14px;
	height: 14px;
}

/* Opened straight onto one clinic's calendar — the finder's buttons and the Find Us CTA — so
   there is no list behind it to go back to. */
.booking-modal--single .booking-modal__back {
	display: none;
}

.booking-modal--single .booking-modal__head--cal {
	padding-right: 56px;
}

/* Move 3 of the three-move vocabulary: the control is instant, the incoming view rises in. A
   modal answers a tap, which is exactly what that move is for. `animation` and not `transition`
   because [hidden] -> display:flex offers no transitionable start state.
   Duplicated rather than shared with ability-blocks.css's equivalent because booking.css is
   enqueued independently of it — the same reason location-finder.css carries its own copy. If
   the move changes, all three change. */
@keyframes booking-view-in {
	from {
		opacity: 0;
		transform: translateY(6px);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

@keyframes booking-scrim-in {
	from {
		opacity: 0;
	}

	to {
		opacity: 1;
	}
}

.booking-modal__view.is-in {
	animation: booking-view-in .26s cubic-bezier(.2, .8, .2, 1) both;
}

/* The 260ms scrim against the panel's own slide is the location switcher's pairing, not a new
   one. */
.booking-modal:not([hidden]) .booking-modal__overlay {
	animation: booking-scrim-in 260ms var(--ab-ease-out, cubic-bezier(.22, 1, .32, 1)) both;
}

/* The drawer slides, it does not rise: 340ms is the location switcher sheet's timing, reused so
   the two panels move as one family. The exit mirrors it; js/booking.js waits for animationend
   (with a timeout fallback) before tearing the dialog down, so `both` on the way out is what
   keeps the panel offscreen during that handshake. */
.booking-modal:not([hidden]) .booking-modal__dialog {
	animation: booking-drawer-in 340ms cubic-bezier(.2, .8, .2, 1) both;
}

.booking-modal.is-closing .booking-modal__dialog {
	animation: booking-drawer-out 340ms cubic-bezier(.2, .8, .2, 1) both;
}

.booking-modal.is-closing .booking-modal__overlay {
	animation: booking-scrim-out 260ms var(--ab-ease-out, cubic-bezier(.22, 1, .32, 1)) both;
}

@keyframes booking-drawer-in {
	from {
		transform: translateX(100%);
	}

	to {
		transform: none;
	}
}

@keyframes booking-drawer-out {
	from {
		transform: none;
	}

	to {
		transform: translateX(100%);
	}
}

@keyframes booking-scrim-out {
	from {
		opacity: 1;
	}

	to {
		opacity: 0;
	}
}

/* -------------------------------------------------------------------------
   The modal's waiting state.

   Same shape and same reasoning as .ab-booking__wait in ability-blocks.css, and duplicated on
   purpose rather than shared: booking.css is enqueued independently of that sheet, which is the
   rule already recorded above for the keyframes. If the placeholder changes, both change.

   Still, never a shimmer — DESIGN-SYSTEM.md's vocabulary is three moves and a looping gradient is
   not one of them. It leaves on move 3, the same view switch the dialog itself enters on.
   ---------------------------------------------------------------------- */

/* The view and the host both stretch so the frame can fill the drawer: the body is the scroll
   container with a definite height, the view matches it, and the frame grows into whatever the
   header leaves. A definite chain all the way down is what lets the iframe's height:100% resolve
   (see the iframe rule above for why the frame no longer trusts form_embed.js with its height). */
.booking-modal__view--cal {
	height: 100%;
}

.booking-modal__frame {
	position: relative;
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	min-height: 0;
}

/* The host has no height of its own until the iframe lays out, so an inset:0 placeholder inside it
   collapsed to 64px — a stripe, not a form. Matches the iframe's own min-height so the panel does
   not resize under the visitor the moment the widget arrives. */
.booking-modal__frame.is-waiting {
	min-height: 70vh;
}

.booking-modal__wait {
	position: absolute;
	inset: 0;
	z-index: 1;
	display: flex;
	flex-direction: column;
	gap: 20px;
	padding: 32px 24px 24px;
	background: var(--ab-white, #FFFFFF);
	pointer-events: none;
	overflow: hidden;
}

.booking-modal__frame.is-ready .booking-modal__wait {
	opacity: 0;
	transition: opacity .26s cubic-bezier(.2, .8, .2, 1);
}

/* The silhouette copies the widget's real first paint — the "Enter details" step, not the
   calendar the old 7-column grid promised: a left-aligned branding block (logo, two title
   lines, three step lines), then five labelled fields and a Continue button pinned bottom-right.
   At this width GHL's md variant stacks branding over form and ranges it left (verified against
   the live widget at 720px), so one silhouette serves every viewport the drawer sees. */
.booking-modal__wait-logo,
.booking-modal__wait-title,
.booking-modal__wait-line,
.booking-modal__wait-heading,
.booking-modal__wait-label,
.booking-modal__wait-input,
.booking-modal__wait-btn {
	display: block;
	border-radius: var(--ab-r-sm, 8px);
	/* Blue 50 rather than a neutral grey: grey on white reads as a disabled form. */
	background: var(--ab-b50, #F7FAFD);
}

.booking-modal__wait-brand {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 10px;
	padding: 8px 0 12px;
}

.booking-modal__wait-logo {
	width: 56px;
	height: 56px;
	margin-bottom: 6px;
}

.booking-modal__wait-title {
	width: min(75%, 340px);
	height: 20px;
}

.booking-modal__wait-title--2 {
	width: min(45%, 200px);
}

.booking-modal__wait-line {
	width: min(60%, 280px);
	height: 11px;
}

.booking-modal__wait-form {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.booking-modal__wait-heading {
	width: min(35%, 150px);
	height: 18px;
	margin-bottom: 8px;
}

.booking-modal__wait-label {
	width: 90px;
	height: 11px;
	margin-top: 10px;
}

.booking-modal__wait-input {
	width: 100%;
	height: 48px;
}

.booking-modal__wait-btn {
	width: 120px;
	height: 44px;
	margin-top: auto;
	align-self: flex-end;
}

@media (prefers-reduced-motion: reduce) {
	.ability-cta-dock,
	.booking-modal__close,
	.booking-modal__opt {
		transition: none;
	}

	/* The modal used to be absent from this block, which was harmless only while it had no
	   entrance. It has one now, and DESIGN-SYSTEM.md's rule is that every animated selector is
	   hardened — the dialog still opens and every row is still reachable, only the motion goes. */
	.booking-modal__view,
	.booking-modal__dialog,
	.booking-modal__overlay {
		animation: none;
	}

	.booking-modal__wait {
		transition: none;
	}
}


/* -------------------------------------------------------------------------
   The bar holds a button, below the split

   Full-width white text on a blue band is a banner, not a control: the report was that it
   does not read as a button, and the comparison offered was the footer's own pill three
   centimetres above it — which is right, they were always meant to be the same object.

   So on a phone the fixed element stops BEING the button and becomes an invisible strip that
   carries one, inset by the gutter so it never bleeds into the screen edge. Invisible is the
   point: a band of any colour is a second surface laid over the page, and every version that
   had one — white, then blue — read as chrome bolted to the bottom rather than as a control.
   There is no band. There is a button, floating, with the design system's card shadow to lift
   it off whatever is behind it.

   Over the footer the shadow has nothing to do, because a Blue 700 fill on a Blue 700 ground
   is a 1:1 boundary and no amount of shadow rescues it. There the pill takes a white outline
   instead and keeps its fill — opaque, so the footer's links do not read through it, and
   invisible, because it is the same blue as the ground. js/booking.js reports the ground.

   And the ground under the button at the end of a page is the footer's, not the body's: see
   the reserved strip at the bottom of this block.

   The anchor is still the whole bar, so the tap target remains the entire bottom edge — the
   easiest target a phone has. The pill is what the eye is offered, not what the thumb has to
   find.

   Above 1024 none of this applies: there the element is the corner pill and already looks
   like one.
   ---------------------------------------------------------------------- */
.ability-cta-dock__btn {
	display: none;
}

@media (max-width: 1023.98px) {
	.ability-cta-dock {
		padding: 8px var(--ab-gutter, 18px) calc(8px + env(safe-area-inset-bottom, 0px));
		font-size: 0;
		letter-spacing: 0;
		/* Same argument the ring makes forty lines up, applied to the ground: a colour becoming
		   another colour has no arrival to shape, so linear, and long enough to be seen. The
		   strip and its shadow have to change together or the white lingers under the blue. */
		transition: transform .3s var(--ab-ease-out), visibility .3s, background .4s linear, box-shadow .4s linear;
	}

	/* The strip has no ground and never gets one. Hover and focus are restated alongside it
	   because the desktop rule paints a Blue 700 wash on both, and down here the strip is not a
	   control — it is the area the thumb may land in. The pill answers the pointer. */
	.ability-cta-dock,
	.ability-cta-dock:hover,
	.ability-cta-dock:focus-visible {
		background: transparent;
		box-shadow: none;
	}

	.ability-cta-dock__btn {
		display: flex;
		align-items: center;
		justify-content: center;
		gap: 10px;
		width: 100%;
		height: 48px;
		/* Declared transparent rather than absent so the ghost state only recolours it. A border
		   that appears would add 4px to the pill and put it out of step with the reserved strip
		   at exactly the moment the ground changes. */
		border: 2px solid transparent;
		border-radius: var(--ab-r-pill, 999px);
		background: var(--ab-b700, #3A6DA6);
		/* The system's card shadow, unmodified. This is the one thing standing between the
		   button and the page it floats over, so it is the same lift every raised surface in
		   the library gets rather than a value invented here. */
		box-shadow: var(--ab-shadow-card, 0 6px 22px rgba(28, 52, 84, .12));
		color: var(--ab-white, #FFFFFF);
		font-family: var(--ab-font, 'Inter', sans-serif);
		font-size: var(--ab-t-button, 13px);
		font-weight: 700;
		letter-spacing: .06em;
		line-height: 1;
		text-transform: uppercase;
		box-sizing: border-box;
		transition: background .4s linear, border-color .4s linear, color .4s linear;
	}

	/* is-on-dark says a dark band is behind the strip RIGHT NOW — not the 280px lead
	   is-over-dark carries for the desktop ring, which would flip the button while white page
	   is still under it.

	   The outline arrives; the fill stays. A ghost button is transparent because it sits on a
	   band and has nothing to hide, but this one is scrolled over: the footer's own links ran
	   straight through it, so "Ohio" and "Back Pain" read across the label. Keeping the Blue 700
	   makes it opaque without making it visible — an identical fill on an identical ground is
	   nothing to the eye and everything to what is behind it, and the white outline is what
	   draws the button either way. The shadow goes, because on that ground it has nothing to
	   separate the pill from. */
	.ability-cta-dock.is-on-dark .ability-cta-dock__btn {
		border-color: var(--ab-white, #FFFFFF);
		box-shadow: none;
	}

	/* Filled darkens, ghost inverts — each the feedback its own variant gives everywhere else
	   in the library. */
	.ability-cta-dock:hover .ability-cta-dock__btn,
	.ability-cta-dock:focus-visible .ability-cta-dock__btn {
		box-shadow:
			var(--ab-shadow-card, 0 6px 22px rgba(28, 52, 84, .12)),
			inset 0 0 0 100vmax rgba(0, 0, 0, .10);
	}

	.ability-cta-dock.is-on-dark:hover .ability-cta-dock__btn,
	.ability-cta-dock.is-on-dark:focus-visible .ability-cta-dock__btn {
		box-shadow: none;
		background: var(--ab-white, #FFFFFF);
		color: var(--ab-b700, #3A6DA6);
	}

	/* The footer's own Schedule goes. It was the only permanent booking route a phone had, which
	   is why it was left in place while this bar was being built; now there are two of the same
	   button within a thumb's reach of each other, and the client asked for it to come out as
	   soon as the permanent one existed. Mobile only — on desktop the footer button is still the
	   end of the page and the corner pill is a different shape in a different place. */
	body.ability-has-dock .ab .ab-footer__cta {
		display: none;
	}

	.ability-cta-dock__arrow {
		width: 18px;
		height: 18px;
		flex: 0 0 auto;
	}

	/* The strip is 48 of button plus 8 above and 8 below, and the reserved space has to say the
	   same number or the last line of the footer ends up underneath it.

	   That space belongs to the FOOTER, not to the body. As body padding it painted the page's
	   own white below the footer, so every page ended in a white band with the button sitting
	   on it — and the moment the bar scrolled away, the band was all that was left. Handing the
	   footer the same 64px puts its own Blue 700 under the button instead, at rest and while
	   the bar is gone, and the page is exactly as tall as it was.

	   The body keeps the reserve on any page with no ab-footer, which is why this is a :has()
	   and not a deletion. */
	body.ability-has-dock {
		padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px));
	}

	body.ability-has-dock:has(.ab-footer) {
		padding-bottom: 0;
	}

	body.ability-has-dock .ab-footer {
		padding-bottom: calc(34px + 64px + env(safe-area-inset-bottom, 0px));
	}
}
