/* =========================================================================
   13 Taxi North Suburban
   Design system and components.

   Direction: "the meter". Black and orange straight off the logo artwork.
   The signature is the live fare meter in the booking card, so everything
   around it stays quiet and lets that one element carry the page.

   Palette is fully tokenised: the :root block is the only place colour is
   declared.
   ========================================================================= */

/* ---------- Tokens ---------- */

:root {
	/* Ink. Taken from the logo, which sets its orange on true black. A blue
	   tinted dark would fight the mark, so the neutrals stay neutral. */
	--ink:        #0B0B0D;
	--ink-soft:   #17171B;
	--ink-line:   #2B2B31;
	--ink-muted:  #9C9CA6;

	/* Paper. A hair off white so large light areas do not glare at 4am. */
	--paper:      #F6F6F4;
	--surface:    #FFFFFF;
	--line:       #E3E2DE;
	--line-soft:  #EFEEEB;
	--text:       #131316;
	--muted:      #5B5B66;

	/* Brand orange, sampled from the logo artwork. The only accent that
	   shouts, and always paired with black the way the logo pairs them. */
	--orange:      #FC7D03;
	--orange-hi:   #FF9526; /* Hover lift. */
	/* The brand orange fails WCAG AA as small text on a light background
	   (2.4:1), confirmed via Lighthouse, needs 4.5:1. Client approved
	   darkening it for that use 2026-08-27. Not used on the dark sections,
	   those already have enough contrast with the original brighter orange. */
	--orange-text: #B05702; /* Orange as text on a light background. */

	/* Vacant. The green light on a Melbourne roof means the cab is free.
	   Used only for live availability, never as decoration. */
	--vacant:     #12A15F;
	--alert:      #C4321F;

	/* Type */
	--display: "Archivo", "Helvetica Neue", Arial, sans-serif;
	--body:    "Public Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

	/* Fluid type scale */
	--t-xs:   0.8rem;
	--t-sm:   0.875rem;
	--t-base: 1rem;
	--t-lg:   1.125rem;
	/* Scaled down across the board, H1 most of all: proportions between the
	   tiers are kept the same, just the whole hierarchy sits smaller. */
	--t-xl:   clamp(1.15rem, 1.02rem + 0.5vw, 1.35rem);
	--t-2xl:  clamp(1.3rem, 1.1rem + 0.9vw, 1.75rem);
	--t-3xl:  clamp(1.7rem, 1.4rem + 1.5vw, 2.5rem);
	--t-4xl:  clamp(2.1rem, 1.65rem + 2.4vw, 3.4rem);

	/* Space */
	--s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
	--s-5: 24px;  --s-6: 32px;  --s-7: 48px;  --s-8: 64px;
	--s-9: 96px;  --s-10: 128px;

	--radius:    14px;
	--radius-sm: 9px;
	--radius-lg: 22px;

	--shadow-sm: 0 1px 2px rgba(11,11,13,.06), 0 2px 8px rgba(11,11,13,.05);
	--shadow:    0 4px 12px rgba(11,11,13,.08), 0 16px 40px rgba(11,11,13,.08);
	--shadow-lg: 0 8px 24px rgba(11,11,13,.12), 0 32px 64px rgba(11,11,13,.14);

	--wrap: 1200px;
	--ease: cubic-bezier(.22,.61,.36,1);
}

/* ---------- Reset ---------- */

*, *::before, *::after { box-sizing: border-box; }

/* A component display rule must never beat the hidden attribute. */
[hidden] { display: none !important; }

/* overflow-x here too, not just on body: a position:fixed element (the
   sticky action bar) is laid out against the viewport, not against body, so
   a body-only rule does not stop it from widening the page's own scrollable
   range. That is what let a real device's font metrics push the action bar
   a hair past 100% and leave the page shifted after the keyboard opened,
   even though nothing here ever showed a visible overflow in testing. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: hidden; }

@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	*, *::before, *::after {
		animation-duration: .01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .01ms !important;
	}
}

body {
	margin: 0;
	background: var(--paper);
	color: var(--text);
	font-family: var(--body);
	font-size: var(--t-base);
	line-height: 1.65;
	-webkit-font-smoothing: antialiased;
	overflow-x: hidden;
}

img, svg, video { max-width: 100%; height: auto; display: block; }

h1, h2, h3, h4 {
	font-family: var(--display);
	font-weight: 700;
	line-height: 1.1;
	letter-spacing: -0.02em;
	margin: 0 0 var(--s-4);
	color: var(--text);
	text-wrap: balance;
	text-transform: capitalize;
}

h1 { font-size: var(--t-4xl); letter-spacing: -0.035em; }
h2 { font-size: var(--t-3xl); letter-spacing: -0.03em; }
h3 { font-size: var(--t-xl); }

p { margin: 0 0 var(--s-4); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

a { color: inherit; }

:where(a, button, input, select, textarea, [tabindex]):focus-visible {
	outline: 3px solid var(--orange);
	outline-offset: 2px;
	border-radius: 4px;
}

.sr-only {
	position: absolute; width: 1px; height: 1px; padding: 0;
	margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

.skip-link {
	position: absolute; left: 0; top: 0; z-index: 999;
	transform: translateY(-120%);
	background: var(--ink); color: #fff; padding: var(--s-3) var(--s-5);
	font-weight: 600;
}
.skip-link:focus { transform: translateY(0); }

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

.wrap {
	width: 100%;
	max-width: var(--wrap);
	margin-inline: auto;
	padding-inline: var(--s-5);
}

.section { padding-block: clamp(48px, 7vw, 96px); }
.section--tight { padding-block: clamp(36px, 5vw, 64px); }
.section--ink { background: var(--ink); color: #fff; }
.section--ink h2, .section--ink h3 { color: #fff; }

.section__head { max-width: 62ch; margin-bottom: var(--s-6); }
.section__head--center { margin-inline: auto; text-align: center; }

.eyebrow {
	font-family: var(--display);
	font-size: var(--t-xs);
	font-weight: 700;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--orange-text);
	margin: 0 0 var(--s-3);
	display: block;
}
.section--ink .eyebrow, .hero .eyebrow { color: var(--orange); }

.lede { font-size: var(--t-lg); color: var(--muted); }
.section--ink .lede { color: var(--ink-muted); }

/* ---------- Checker rule ----------
   The taxi checker, used structurally rather than decoratively: it marks the
   seam between sections. Never used as background texture. */

.checker {
	height: 20px;
	width: 100%;
	background-image:
		linear-gradient(45deg, var(--ink) 25%, transparent 25%, transparent 75%, var(--ink) 75%),
		linear-gradient(45deg, var(--ink) 25%, transparent 25%, transparent 75%, var(--ink) 75%);
	background-size: 20px 20px;
	background-position: 0 0, 10px 10px;
	background-color: var(--orange);
}
.checker--thin { height: 12px; background-size: 12px 12px; background-position: 0 0, 6px 6px; }

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

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--s-2);
	font-family: var(--display);
	font-weight: 700;
	font-size: var(--t-base);
	letter-spacing: 0;
	word-spacing: .06em;
	padding: 14px 24px;
	border-radius: var(--radius-sm);
	border: 2px solid transparent;
	cursor: pointer;
	text-decoration: none;
	text-transform: capitalize;
	transition: transform .15s var(--ease), background-color .15s var(--ease), border-color .15s var(--ease);
	line-height: 1.2;
	text-align: center;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--primary { background: var(--orange); color: #fff; }
.btn--primary:hover { background: var(--orange-hi); }

.btn--ink { background: var(--ink); color: #fff; }
.btn--ink:hover { background: var(--ink-soft); }

.btn--ghost { background: transparent; color: var(--text); border-color: var(--line); }
.btn--ghost:hover { border-color: var(--text); }

.section--ink .btn--ghost { color: #fff; border-color: var(--ink-line); }
.section--ink .btn--ghost:hover { border-color: #fff; }

/* Pre footer close section. Orange, not black, per the client. Body copy
   stays dark for genuine readability (a full sentence needs more contrast
   than a heading can get away with); the heading follows the same white on
   orange the client chose for buttons elsewhere. The primary button switches
   to .btn--ink here specifically, since an orange button would disappear on
   an orange section. */
.section--orange { background: var(--orange); color: var(--ink); }
.section--orange h2, .section--orange h3 { color: #fff; }
.section--orange .lede { color: var(--ink); }
/* Solid white, not just an outline: next to the solid black primary button,
   a thin dark-on-orange outline read as the weaker, secondary option even
   though calling and booking online are equally valid choices here. Matching
   solid weight on both puts them on equal footing. */
.section--orange .btn--ghost {
	background: #fff;
	color: var(--ink);
	border-color: #fff;
}
.section--orange .btn--ghost:hover { background: var(--paper); border-color: var(--paper); }

.btn--lg { padding: 17px 30px; font-size: var(--t-lg); }
.btn--sm { padding: 10px 16px; font-size: var(--t-sm); }
.btn--block { width: 100%; min-height: 48px; }

/* ---------- Header ---------- */

.site-header {
	position: sticky;
	top: 0;
	z-index: 100;
	background: var(--ink);
	color: #fff;
}

.site-header__bar {
	display: flex;
	align-items: center;
	gap: var(--s-5);
	min-height: 72px;
}

/* Brand lockup. Two artwork variants, one for dark surfaces and one for
   light, each with the outer background already knocked out. */
.brand { display: inline-flex; align-items: center; text-decoration: none; color: #fff; flex-shrink: 0; }
.brand img { height: 42px; width: auto; }
@media (max-width: 620px) { .brand img { height: 34px; } }
.brand--sm img { height: 34px; }

.nav { margin-left: auto; }
.nav__list { display: flex; align-items: center; gap: var(--s-5); list-style: none; margin: 0; padding: 0; }
.nav__list a {
	text-decoration: none;
	font-size: var(--t-sm);
	font-weight: 500;
	color: #fff;
	opacity: .82;
	white-space: nowrap;
	text-transform: capitalize;
	transition: opacity .15s var(--ease);
}
.nav__list a:hover { opacity: 1; }

.header__actions { display: flex; align-items: center; gap: var(--s-3); flex-shrink: 0; }

/* Dropdowns. Opened on hover for a mouse and on click for everything else, so
   the same markup serves both without a hover trap on touch. Kept in the DOM
   rather than built on open, so the links are crawlable. */
.nav__has-sub { position: relative; }
.nav__subtoggle {
	display: inline-flex; align-items: center; gap: 5px;
	background: none; border: 0; padding: 0; cursor: pointer;
	font-family: inherit; font-size: var(--t-sm); font-weight: 500;
	color: #fff; opacity: .82; white-space: nowrap;
	text-transform: capitalize;
	transition: opacity .15s var(--ease);
}
.nav__subtoggle:hover { opacity: 1; }
.nav__subtoggle svg { width: 15px; height: 15px; transition: transform .18s var(--ease); }
.nav__has-sub:hover .nav__subtoggle svg,
.nav__subtoggle[aria-expanded="true"] svg { transform: rotate(180deg); }

.nav__sub {
	position: absolute; top: calc(100% + 14px); left: 50%;
	transform: translateX(-50%) translateY(-6px);
	min-width: 232px;
	list-style: none; margin: 0; padding: 8px;
	background: var(--ink-2, #16161A);
	border: 1px solid var(--ink-line);
	border-radius: var(--radius-sm);
	box-shadow: 0 18px 40px rgba(0,0,0,.45);
	opacity: 0; visibility: hidden; pointer-events: none;
	transition: opacity .16s var(--ease), transform .16s var(--ease), visibility .16s;
	z-index: 60;
}
/* A gap between the button and the panel would drop the hover, so the panel
   reaches back up to the bar with a transparent pad. */
.nav__sub::before { content: ""; position: absolute; inset: -14px 0 100% 0; }
.nav__has-sub:hover .nav__sub,
.nav__has-sub:focus-within .nav__sub,
.nav__subtoggle[aria-expanded="true"] + .nav__sub {
	opacity: 1; visibility: visible; pointer-events: auto;
	transform: translateX(-50%) translateY(0);
}
.nav__sub a {
	display: block; padding: 9px 12px; border-radius: 7px;
	opacity: .85; white-space: normal;
	text-transform: capitalize;
}
.nav__sub a:hover { opacity: 1; background: rgba(255,255,255,.06); }



/* Phone is a first class action, not a footer detail. Most taxi bookings
   still start with a call. */
.btn--footer-whatsapp { background: #25D366; color: #fff; }
.btn--footer-whatsapp:hover { background: #1EBE5A; }
.callnow { display: inline-flex; align-items: center; gap: 9px; text-decoration: none; color: #fff; }
.callnow__icon {
	width: 38px; height: 38px; border-radius: 50%;
	background: rgba(252,125,3,.14);
	border: 1px solid rgba(252,125,3,.3);
	display: grid; place-items: center; color: var(--orange); flex-shrink: 0;
}
.callnow__txt { display: flex; flex-direction: column; line-height: 1.15; }
.callnow__txt small { font-size: 11px; color: var(--ink-muted); letter-spacing: .06em; text-transform: uppercase; }
.callnow__txt b { font-family: var(--display); font-size: 15.5px; font-weight: 700; font-variant-numeric: tabular-nums; }

.navtoggle {
	display: none;
	background: transparent; border: 1px solid var(--ink-line);
	color: #fff; width: 44px; height: 44px; border-radius: 10px;
	cursor: pointer; align-items: center; justify-content: center;
}

/* ---------- Hero ---------- */

.hero {
	background: var(--ink);
	color: #fff;
	position: relative;
	overflow: hidden;
	padding-block: clamp(36px, 5vw, 68px) clamp(48px, 7vw, 88px);
}

/* A single soft amber wash, like a streetlight. One atmospheric move, not a
   pile of gradients. */
.hero::before {
	content: "";
	position: absolute;
	top: -20%; right: -10%;
	width: 720px; height: 720px;
	background: radial-gradient(circle, rgba(252,125,3,.16) 0%, transparent 62%);
	pointer-events: none;
}

.hero__grid {
	position: relative;
	display: grid;
	grid-template-columns: 1fr minmax(400px, 480px);
	gap: clamp(32px, 5vw, 72px);
	align-items: start;
}

.hero__copy { padding-top: var(--s-5); }

.hero h1 { color: #fff; margin-bottom: var(--s-5); }
.hero h1 em {
	font-style: normal;
	color: var(--orange);
}

.hero__lede { font-size: var(--t-lg); color: var(--ink-muted); max-width: 46ch; margin-bottom: var(--s-6); }

/* Trust row. Facts only, and only facts we can stand behind. White text: only
   ever used on a dark hero, never on the plain page background. */
.trustrow { display: flex; flex-wrap: wrap; gap: var(--s-5) var(--s-6); padding: 0; margin: 0; list-style: none; }
.trustrow li { display: flex; align-items: center; gap: 10px; font-size: var(--t-sm); color: #fff; }
.trustrow svg { color: var(--orange); flex-shrink: 0; }

/* Suburb photo, its own section right after the hero. Only renders once a
   featured image is actually set, so a suburb still waiting on its photo
   brief keeps the layout it has now rather than showing a broken image.
   No forced crop: these are delivered as complete designed banners (logo,
   headline and contact details already placed in the image), so cropping to
   a fixed ratio risks cutting off real content rather than just trimming
   empty background the way it would on a plain photo. Shown at its own
   natural size instead. */
.suburb-photo-section { padding-block: clamp(28px, 4vw, 44px); }
.suburb-photo { margin: 0; }
.suburb-photo__img {
	display: block; width: 100%; height: auto;
	border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
}

/* Same short icon led line, for a sidebar sitting on the plain page
   background rather than a dark hero, so it needs the page's own text
   colour instead of trustrow's white. */
.pointlist { display: grid; gap: 14px; padding: 0; margin: 0 0 20px; list-style: none; }
.pointlist li { display: flex; align-items: center; gap: 10px; font-size: var(--t-sm); color: var(--text); }
.pointlist svg { color: var(--orange); flex-shrink: 0; }

/* The Cabcharge badge sits top-left, clear of the card-and-terminal action
   the photo is actually about, and scales down with the photo instead of
   staying a fixed size that overwhelms it once the photo goes short on
   mobile. */
.cabchargephoto { position: relative; }
.cabchargephoto__badge {
	position: absolute;
	top: clamp(12px, 3vw, 18px);
	left: clamp(12px, 3vw, 18px);
	background: #fff;
	border-radius: var(--radius-sm);
	padding: clamp(10px, 2.2vw, 16px) clamp(16px, 3.5vw, 24px);
	box-shadow: 0 4px 14px rgba(11,11,13,.18);
}
.cabchargephoto__badge img { display: block; height: clamp(24px, 6vw, 36px); width: auto; }

.tacphoto { position: relative; }
.tacphoto__badge {
	position: absolute;
	top: clamp(12px, 3vw, 18px);
	left: clamp(12px, 3vw, 18px);
	background: var(--ink-2, #16161A);
	border-radius: var(--radius-sm);
	padding: clamp(10px, 2.2vw, 16px) clamp(16px, 3.5vw, 24px);
	box-shadow: 0 4px 14px rgba(11,11,13,.18);
}
.tacphoto__badge img { display: block; height: clamp(22px, 5.5vw, 32px); width: auto; }

.livedot {
	width: 8px; height: 8px; border-radius: 50%;
	background: var(--vacant);
	box-shadow: 0 0 0 4px rgba(18,161,95,.18);
	flex-shrink: 0;
}
@media (prefers-reduced-motion: no-preference) {
	.livedot { animation: pulse 2.4s var(--ease) infinite; }
	@keyframes pulse {
		0%, 100% { box-shadow: 0 0 0 4px rgba(18,161,95,.18); }
		50%      { box-shadow: 0 0 0 8px rgba(18,161,95,.06); }
	}
}

/* ---------- Booking card ---------- */

.bookcard {
	background: var(--surface);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-lg);
	overflow: hidden;
	color: var(--text);
}
/* On the booking page the form is the page, so it does not need a card around
   it. The panel treatment is for the hero, where the form is an object sitting
   on a photo backdrop and has to lift off it. */
.bookcard--plain {
	background: transparent;
	border-radius: 0;
	box-shadow: none;
	overflow: visible;
}
.bookcard--plain .bookcard__body { padding: 0; }

/* Now or later. A segmented control rather than a tab strip: this sits inside
   the form as a choice between two states, not as navigation between two
   panes, and the pill makes the selected one unmistakable at a glance. */
.bookcard__tabs {
	display: flex;
	gap: 4px;
	padding: 4px;
	background: var(--surface);
	border: 1.5px solid var(--line);
	border-radius: 11px;
}
.bookcard__tab {
	flex: 1;
	appearance: none;
	background: transparent;
	border: 0;
	border-radius: 8px;
	padding: 9px 10px;
	min-height: 44px;
	font-family: var(--display);
	font-weight: 600;
	font-size: var(--t-sm);
	color: var(--muted);
	cursor: pointer;
	/* Deliberately allowed to wrap. "As soon as possible" does not fit on one
	   line inside the card at 320px, and nowrap pushed the whole page 12px
	   wide rather than taking a second line. */
	line-height: 1.25;
	transition: background-color .16s var(--ease), color .16s var(--ease), box-shadow .16s var(--ease);
}
.bookcard__tab:hover { color: var(--text); }
.bookcard__tab[aria-selected="true"] {
	background: var(--orange);
	color: #fff;
	font-weight: 700;
	box-shadow: 0 1px 3px rgba(11,11,13,.12);
}
.bookcard__tab:focus-visible { outline: 2px solid var(--orange); outline-offset: 1px; }

.bookcard__body { padding: var(--s-4); }

/* Address suggestions. Anchored to the field rather than the input, so it
   clears the locate button on the pickup row too. */
.acmenu {
	position: absolute;
	top: 100%; left: 0; right: 0;
	z-index: 40;
	margin: 4px 0 0;
	padding: 5px;
	list-style: none;
	max-height: 264px;
	overflow-y: auto;
	background: var(--paper);
	border: 1.5px solid var(--line);
	border-radius: 10px;
	box-shadow: 0 14px 34px rgba(11,11,13,.16);
}
.acmenu[hidden] { display: none !important; }
.acmenu--up {
	top: auto;
	bottom: 100%;
	margin: 0 0 4px;
	box-shadow: 0 -14px 34px rgba(11,11,13,.16);
}
.acitem {
	padding: 11px 12px;
	min-height: 44px;
	display: flex;
	align-items: center;
	border-radius: 7px;
	font-size: var(--t-sm);
	color: var(--text);
	cursor: pointer;
}
.acitem:hover,
.acitem.is-active { background: rgba(252,125,3,.12); }

/* ---------- Fields ---------- */

.field { margin-bottom: var(--s-3); position: relative; }
.field:last-child { margin-bottom: 0; }

.field > label,
.fieldset > legend {
	display: block;
	font-size: var(--t-sm);
	font-weight: 600;
	margin-bottom: 5px;
	color: var(--text);
	text-transform: capitalize;
}
.field__optional { font-weight: 400; color: var(--muted); }



.field select { text-transform: capitalize; }
.field select option { text-transform: capitalize; }

.field input[type="text"],
.field input[type="tel"],
.field input[type="email"],
.field input[type="date"],
.field input[type="time"],
.field input[type="number"],
.field select,
.field textarea {
	width: 100%;
	font-family: var(--body);
	font-size: var(--t-base);
	/* 16px is the floor: anything smaller and iOS Safari zooms the page on
	   focus. The padding takes the height instead. min-height is the real
	   guarantee though: padding alone measured 41px in practice, 3px under
	   the touch target floor, since form control line-height varies by
	   browser rather than following the padding math exactly. */
	padding: 10px 13px;
	min-height: 44px;
	border: 1.5px solid var(--line);
	border-radius: var(--radius-sm);
	background: var(--surface);
	color: var(--text);
	transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.field input:hover, .field select:hover { border-color: #CFC9BB; }
.field input:focus, .field select:focus, .field textarea:focus {
	outline: none;
	border-color: var(--orange);
	box-shadow: 0 0 0 3px rgba(252,125,3,.22);
}

/* Country code and mobile number as one connected control, the standard
   phone-input pattern. The two pieces sit flush (no gap, no border between
   them, only the outer edges rounded) so they read as a single field despite
   being two form elements. Placed after the global .field input/select rule,
   and repeats its hover and focus states with 'none', because both blocks
   match input[type="tel"] and select with the same specificity: without this,
   the global rule's hover and focus borders win by source order and stack a
   second border and a second focus ring on top of the wrapper's own. */
.phonegroup {
	display: flex;
	border: 1.5px solid var(--line);
	border-radius: var(--radius-sm);
	background: var(--surface);
	transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.phonegroup:hover { border-color: #CFC9BB; }
.phonegroup:focus-within {
	border-color: var(--orange);
	box-shadow: 0 0 0 3px rgba(252,125,3,.15);
}
.phonegroup input[type="tel"] {
	flex: 1;
	min-width: 0;
	border: 0;
	border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.phonegroup input[type="tel"]:hover,
.phonegroup input[type="tel"]:focus {
	border: 0;
	box-shadow: none;
}
/* The sitewide :focus-visible rule already outlines whichever piece is
   actually tabbed to; nothing here needs to fight or replace it. */

/* Country code picker: a real listbox built by hand, not a native <select>.
   Flag emoji inside a native select's option list render as blank boxes on
   Windows (regional indicator pairs are drawn as two letter codes there by
   design, not flag glyphs) and on any environment without a colour emoji
   font, confirmed missing even outside Windows during testing. Real SVG
   flags here render identically everywhere since nothing depends on the
   OS's own emoji support. */
.ccpick { position: relative; }
.ccpick__btn {
	flex: 0 0 auto;
	display: flex; align-items: center; gap: 6px;
	border: 0;
	border-right: 1.5px solid var(--line);
	border-radius: var(--radius-sm) 0 0 var(--radius-sm);
	background: transparent;
	padding: 10px 10px 10px 12px;
	font-family: var(--body);
	font-size: var(--t-base);
	color: var(--text);
	cursor: pointer;
}
.ccpick__btn:hover, .ccpick__btn[aria-expanded="true"] { background: var(--paper); }
.ccpick__btn img { width: 20px; height: 15px; border-radius: 2px; flex-shrink: 0; object-fit: cover; }
.ccpick__btn svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--muted); }
.ccpick__menu {
	position: absolute;
	top: 100%; left: 0;
	z-index: 40;
	margin: 4px 0 0;
	padding: 5px;
	list-style: none;
	width: 260px;
	max-width: 90vw;
	max-height: 280px;
	overflow-y: auto;
	background: var(--paper);
	border: 1.5px solid var(--line);
	border-radius: 10px;
	box-shadow: 0 14px 34px rgba(11,11,13,.16);
}
.ccpick__menu[hidden] { display: none !important; }
.ccpick__item {
	display: flex; align-items: center; gap: 10px;
	padding: 9px 10px;
	min-height: 44px;
	border-radius: 7px;
	font-size: var(--t-sm);
	color: var(--text);
	cursor: pointer;
}
.ccpick__item img { width: 20px; height: 15px; border-radius: 2px; flex-shrink: 0; object-fit: cover; }
.ccpick__name { flex: 1; min-width: 0; }
.ccpick__dial { color: var(--muted); flex-shrink: 0; }
.ccpick__item:hover, .ccpick__item.is-active { background: rgba(252,125,3,.12); }
.ccpick__item[aria-selected="true"] { font-weight: 600; }


.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); }
/* A date and its time belong side by side. They read as one answer, and both
   inputs are narrow enough to hold two columns even on the smallest phone,
   unlike the name and phone pair which does stack. */
.field-row--split { grid-template-columns: 1fr 1fr; }
/* Grid children default to min-width:auto, and a native date input reports a
   wider intrinsic width than a time input, so 1fr 1fr silently comes out
   uneven. This makes the two halves actually equal. */
.field-row--split > .field { min-width: 0; }
.field-row--split input { width: 100%; }

.field__with-btn { position: relative; }
.field__with-btn input { padding-right: 46px; }
.locate {
	position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
	width: 34px; height: 34px; border-radius: 50%;
	background: rgba(252,125,3,.1); border: 0;
	color: var(--orange); cursor: pointer;
	display: grid; place-items: center;
}
.locate:hover { background: rgba(252,125,3,.18); }
.locate:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }

/* A live pulse reads as "this finds you", the way a map's blue dot does.
   Two rings expand and fade behind the pin so it doesn't look like it is
   simply blinking on and off, which reads as an error state, not a location
   button. Runs continuously and costs nothing while idle. */
.locate::before,
.locate::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
	background: var(--orange);
	opacity: 0;
	animation: mt13-locate-pulse 2.2s ease-out infinite;
	pointer-events: none;
}
.locate::after { animation-delay: 1.1s; }
@keyframes mt13-locate-pulse {
	0%   { transform: scale(.65); opacity: .55; }
	70%  { transform: scale(1.55); opacity: 0; }
	100% { transform: scale(1.55); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
	.locate::before, .locate::after { animation: none; content: none; }
}

/* Mid fetch: the rings stop (a spinner is the honest state for "working",
   a pulse implies "idle, tap me") and the icon itself pulses in place. */
.locate:disabled {
	cursor: wait;
	opacity: .75;
}
.locate:disabled::before,
.locate:disabled::after { animation: none; content: none; }
.locate:disabled svg { animation: mt13-locate-spin 1s linear infinite; }
@keyframes mt13-locate-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
	.locate:disabled svg { animation: none; }
}

/* Found: a brief solid flash confirms the tap worked, distinct from the
   ongoing idle pulse. */
.locate.is-done {
	background: var(--orange);
	color: #fff;
	animation: mt13-locate-found .5s var(--ease);
}
@keyframes mt13-locate-found {
	0% { transform: translateY(-50%) scale(1); }
	40% { transform: translateY(-50%) scale(1.18); }
	100% { transform: translateY(-50%) scale(1); }
}

/* ---------- Stops ----------
   Between pickup and drop off, in the order the trip actually runs. Starts
   empty: most trips have none, so there is nothing to look at until "Add a
   stop" is pressed. */

.field--stops { margin-bottom: var(--s-3); }
.stoplist:not(:empty) { display: flex; flex-direction: column; gap: var(--s-3); margin-bottom: var(--s-3); }

.stopremove {
	position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
	width: 34px; height: 34px; border-radius: 50%;
	background: var(--paper); border: 0;
	color: var(--muted); cursor: pointer;
	display: grid; place-items: center;
}
.stopremove:hover { background: rgba(196,50,31,.12); color: var(--alert); }
.stopremove:focus-visible { outline: 2px solid var(--alert); outline-offset: 2px; }

/* Its own small control, not the heavy page-level .btn: this sits inline in
   a form as a minor "add another field" action, not a call to action, so it
   stays light, text sized, and sentence case rather than bold and capitalised
   like a primary button. */
.stopadd {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	padding: 7px 14px;
	border: 1.5px solid var(--line);
	border-radius: 999px;
	background: transparent;
	color: var(--orange);
	font-family: var(--body);
	font-weight: 600;
	font-size: var(--t-sm);
	cursor: pointer;
	transition: border-color .15s var(--ease), background-color .15s var(--ease);
}
.stopadd svg { width: 15px; height: 15px; }
.stopadd:hover { border-color: var(--orange); background: rgba(252,125,3,.06); }
.stopadd:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
.stopadd[hidden] { display: none; }

/* ---------- The meter ----------
   The signature. A real taxi meter reads out in tabular figures and only ever
   moves up. This does the same as the trip takes shape, which is the whole
   point: the passenger sees the price before they commit, not after. */

.meter {
	margin-top: var(--s-3);
	background: var(--ink);
	border-radius: var(--radius);
	padding: var(--s-3) var(--s-4);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-4);
	position: relative;
	overflow: hidden;
}
.meter::after {
	content: "";
	position: absolute; inset: 0;
	background: radial-gradient(circle at 88% 50%, rgba(252,125,3,.18) 0%, transparent 58%);
	pointer-events: none;
}

/* The booking form's meter is a <details> now, so the card itself has to
   stack (summary, then the breakdown below it) rather than stay a flex row;
   .meter__summary carries the exact row layout the plain div version above
   still uses directly, so the fleet page's own static .meter (no accordion,
   no summary) is untouched. */
.meter.js-meter {
	display: block;
	padding: 0;
}
.meter__summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-4);
	padding: var(--s-3) var(--s-4);
	cursor: pointer;
	list-style: none;
}
.meter__summary::-webkit-details-marker { display: none; }
.meter__chevron {
	position: relative; z-index: 1;
	flex-shrink: 0;
	width: 9px; height: 9px;
	border-right: 2px solid rgba(255,255,255,.5);
	border-bottom: 2px solid rgba(255,255,255,.5);
	transform: rotate(45deg);
	transition: transform .2s var(--ease);
}
.meter[open] .meter__chevron { transform: rotate(-135deg); }
/* Nothing to expand yet: the chevron only implies content once there is a
   price to check, not on an empty card before both addresses are typed. */
.meter:has(.farebreak[hidden]) .meter__summary { cursor: default; }
.meter:has(.farebreak[hidden]) .meter__chevron { display: none; }

/* The itemised rows, now inside the dark card instead of sitting on the
   page background underneath it: .farebreak's own colours are the light
   background version still used by the fares page estimator, so this
   overrides just the piece that changes for a dark card rather than
   touching the shared rule both places rely on. */
.meter .farebreak {
	position: relative; z-index: 1;
	margin-top: 0;
	padding: 0 var(--s-4) var(--s-3);
	border-top: 1px solid rgba(255,255,255,.14);
}
.meter .farebreak__row {
	color: rgba(255,255,255,.7);
	border-bottom-color: rgba(255,255,255,.12);
}
.meter .farebreak__row b { color: #fff; }
.meter .farebreak__row--head {
	color: #fff;
	border-top-color: rgba(255,255,255,.14);
}

.meter__label { position: relative; z-index: 1; }
.meter__label small {
	display: block;
	font-size: 11px;
	letter-spacing: .16em;
	text-transform: uppercase;
	color: var(--ink-muted);
	margin-bottom: 3px;
}
.meter__label span { font-size: var(--t-sm); color: #fff; }

.meter__value {
	position: relative; z-index: 1;
	font-family: var(--display);
	font-weight: 800;
	font-size: clamp(1.9rem, 1.5rem + 1.6vw, 2.5rem);
	line-height: 1;
	letter-spacing: -0.04em;
	color: var(--orange);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}
.meter__value[data-idle="true"] { color: var(--ink-muted); }

.meter__note {
	font-size: var(--t-xs);
	color: var(--muted);
	margin-top: var(--s-3);
	text-align: center;
}

/* ---------- Wizard ---------- */

/* One page form. The client did not want a wizard, so the grouping has to do
   the work the steps used to: named sections with a rule between them, so a
   long form still reads as three short ones. */
.formsec + .formsec {
	margin-top: var(--s-5);
}
.formsec__title {
	font-size: 0.95rem;
	margin-bottom: var(--s-3);
}

.fieldset { border: 0; padding: 0; margin: 0 0 var(--s-4); min-width: 0; }
.fieldset--last { margin-bottom: 0; }
.field--last { margin-bottom: 0; }

.meter__note--left { text-align: left; }

.formsec--pay .js-submit { margin-top: var(--s-5); }

.paymenticons { display: flex; align-items: center; justify-content: center; flex-wrap: nowrap; gap: clamp(4px, 1.6vw, 14px); margin: var(--s-5) 0 0; }
.paymenticons__badge { flex: 1 1 0; min-width: 0; display: flex; justify-content: center; }
.paymenticons__badge img { display: block; height: auto; max-height: 32px; max-width: 100%; width: auto; opacity: .85; }
.paymenticons__badge--framed img { opacity: 1; }

/* ---------- Vehicle picker ---------- */

.vehgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr)); gap: var(--s-3); }

.vehopt { position: relative; }
.vehopt input { position: absolute; opacity: 0; pointer-events: none; }
.vehopt__box {
	display: block;
	background: var(--surface);
	border: 1.5px solid var(--line);
	border-radius: var(--radius);
	padding: var(--s-3);
	cursor: pointer;
	transition: border-color .15s var(--ease), background-color .15s var(--ease);
	height: 100%;
}
.vehopt__box:hover { border-color: #CFC9BB; }
.vehopt input:checked + .vehopt__box {
	border-color: var(--orange);
	background: var(--surface);
	box-shadow: 0 0 0 1px var(--orange) inset;
}
.vehopt input:focus-visible + .vehopt__box { outline: 3px solid var(--orange); outline-offset: 2px; }
.vehopt__box img { width: 100%; max-width: 108px; margin: 0 auto var(--s-2); }

/* Name and price share a row instead of stacking, so the surcharge reads as
   a badge next to the label rather than competing with it for the same
   line's weight. A divider then separates that header from the spec list,
   which carries a small icon per line instead of two lines of plain text
   with nothing to anchor a quick scan. */
.vehopt__head {
	display: flex; align-items: flex-start; justify-content: space-between;
	gap: 6px;
	padding-bottom: 8px;
	margin-bottom: 8px;
	border-bottom: 1px solid var(--line);
}
.vehopt__head b { font-family: var(--display); font-size: var(--t-sm); font-weight: 700; }
.vehopt__surcharge {
	flex-shrink: 0;
	font-family: var(--body);
	font-size: 11px;
	font-weight: 700;
	color: var(--orange);
	background: rgba(252,125,3,.12);
	border-radius: 999px;
	padding: 2px 8px;
	line-height: 1.5;
}
.vehopt__specs { display: grid; gap: 4px; }
.vehopt__spec {
	display: flex; align-items: flex-start; gap: 6px;
	font-size: var(--t-xs);
	line-height: 1.4;
	color: var(--muted);
}
/* Top aligned against the row, not centred against it: centring looks right
   for "4 seats" on one line, but tips the icon visibly high once "2 large
   and 2 small bags" wraps to two. A small nudge lines the icon up with the
   cap height of that first line instead of the row's literal top edge. */
.vehopt__spec svg { width: 14px; height: 14px; flex-shrink: 0; margin-top: 2px; color: var(--muted); }

/* ---------- Checkable list (extras, notes) ---------- */

/* One line answers. A boxed card with a hint under it is the right shape for
   choosing between things, and the wrong shape for a plain yes or no. */
.checklines { display: grid; gap: 0; }
.checkline {
	display: flex; align-items: center; gap: var(--s-3);
	min-height: 36px;
	padding: 1px 0;
	cursor: pointer;
	font-size: var(--t-sm);
	text-transform: capitalize;
}
.checkline input {
	accent-color: var(--orange);
	width: 18px; height: 18px;
	flex-shrink: 0;
	margin: 0;
}
.checkline span { min-width: 0; }

.checklist { display: grid; gap: var(--s-2); }
.checkitem {
	display: flex; gap: var(--s-3); align-items: flex-start;
	background: var(--surface);
	border: 1.5px solid var(--line);
	border-radius: var(--radius-sm);
	padding: 12px 14px;
	cursor: pointer;
	transition: border-color .15s var(--ease), background-color .15s var(--ease);
}
.checkitem:hover { border-color: #CFC9BB; }
/* Native checkboxes with accent-color render inconsistently across mobile
   browsers, some pad a large touch halo around the box itself, which threw
   the alignment off against the label text. Drawn by hand instead so the
   box is exactly 18px everywhere. */
.checkitem input {
	appearance: none; -webkit-appearance: none;
	margin: 3px 0 0; width: 18px; height: 18px; flex-shrink: 0;
	border: 1.5px solid var(--line); border-radius: 5px;
	background: var(--surface);
	position: relative;
	cursor: pointer;
}
.checkitem input:checked { background: var(--orange); border-color: var(--orange); }
.checkitem input:checked::after {
	content: ""; position: absolute; left: 5px; top: 1px;
	width: 5px; height: 9px;
	border: solid #fff; border-width: 0 2px 2px 0;
	transform: rotate(45deg);
}
.checkitem input:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
.checkitem:has(input:checked) { border-color: var(--orange); background: var(--surface); }
.checkitem b { display: block; font-size: var(--t-sm); font-weight: 600; }
.checkitem small { display: block; font-size: var(--t-xs); color: var(--muted); }
/* One line of text, not a stacked title and description: the checkbox
   centres with it instead of pinning to the top like it does for the
   two-line version. */
.checkitem--inline { align-items: center; }
.checkitem--inline input { margin: 0; }

/* Trip overview map, under the fare breakdown. Both ends and the route
   between them, with a floating card carrying the real distance and time,
   the same figures the fare itself is built from. */
.tripmap {
	position: relative;
	border-radius: var(--radius-sm);
	overflow: hidden;
	margin: var(--s-4) 0;
	border: 1.5px solid var(--line);
}
.tripmap[hidden] { display: none !important; }
.tripmap__map { width: 100%; height: 220px; background: var(--line-soft); }
.tripmap__card {
	position: absolute; left: 12px; bottom: 12px;
	background: var(--surface);
	border-radius: var(--radius-sm);
	box-shadow: 0 4px 14px rgba(11,11,13,.2);
	padding: 10px 14px;
	display: flex; align-items: baseline; gap: 8px;
}
.tripmap__card b { font-family: var(--display); font-size: var(--t-lg); }
.tripmap__card span { font-size: var(--t-xs); color: var(--muted); }

/* FAQ list on every page that is not the dedicated /faq/ page. One shared
   bordered group, not a stack of individually boxed cards: taken literally
   after "group of accordion, not separate accordion" kept coming back even
   once the single column and the centred heading were both already fixed.
   The dedicated /faq/ page's own .qa usage is untouched, since it sits
   inside .faqbody, not .faqgrid--single, and was never part of this ask. */
.faqgrid--single {
	grid-template-columns: 1fr;
	gap: 0;
	max-width: 760px;
	margin-inline: auto;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface);
	overflow: hidden;
}
.faqgrid--single .qa {
	border: 0;
	border-radius: 0;
	margin-bottom: 0;
	border-bottom: 1px solid var(--line);
}
.faqgrid--single .qa:last-child { border-bottom: 0; }

/* Reviews marquee. A continuous CSS animation, not JS: cheaper, and it
   cannot get stuck if a script fails to load. The track is exactly two
   copies of the review list side by side; sliding it left by precisely one
   copy's width (50%, since there are two equal copies) and jumping back to
   0% at the same instant is what makes the loop invisible. */
.reviewmarquee { overflow: hidden; margin-inline: calc(var(--s-5) * -1); }
.reviewmarquee__track {
	display: flex;
	gap: var(--s-4);
	width: max-content;
	padding-inline: var(--s-5);
	animation: mt13-marquee 34s linear infinite;
}
.reviewmarquee:hover .reviewmarquee__track,
.reviewmarquee__track:has(:focus-visible) {
	animation-play-state: paused;
}
.reviewmarquee .reviewcard { flex: 0 0 300px; }
@keyframes mt13-marquee {
	to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
	.reviewmarquee__track { animation: none; }
	.reviewmarquee { overflow-x: auto; }
}

/* Review score line: G mark, the number, five stars, then the count. */
.reviewscore {
	display: flex; align-items: center; justify-content: center; gap: 8px;
	margin-top: var(--s-3);
	font-size: var(--t-base);
}
.reviewscore b { font-size: var(--t-lg); font-family: var(--display); }
.reviewscore__stars { display: inline-flex; gap: 2px; color: #F5A623; }
.reviewscore__stars svg { width: 16px; height: 16px; fill: currentColor; stroke: none; }

.reviewcard { text-decoration: none; color: var(--text); }
.reviewcard__stars { display: flex; gap: 2px; color: #F5A623; margin-bottom: var(--s-3); }
.reviewcard__stars svg { width: 15px; height: 15px; fill: currentColor; stroke: none; }
.reviewcard p { color: var(--text); }
.reviewcard b { display: flex; align-items: center; gap: 6px; color: var(--muted); font-weight: 600; }

/* Per page reviews (template-parts/page-reviews.php). Full review text, so a
   long review would stretch every card in the flex track to its height; let
   each card size to its own review instead. */
.reviewmarquee--page .reviewmarquee__track { align-items: flex-start; }

/* What makes this review relevant to the page: the suburb the reviewer named
   or the topic they wrote about. Only ever set from the review's own words. */
.reviewcard__tag {
	display: inline-block;
	margin: 0 0 var(--s-3);
	padding: 2px 9px;
	border-radius: 999px;
	background: rgba(252, 125, 3, .08);
	color: var(--orange-text);
	font-size: .74rem;
	font-weight: 700;
	line-height: 1.5;
}

/* ---------- Cards and grids ---------- */

/* Wide content scrolls inside its own box, never the page. */
.tbl-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }

.grid { display: grid; gap: var(--s-5); }
/* Grid and flex children default to min-width:auto and will not shrink below
   their content, which is how one long word or a wide table drags the whole
   page sideways. */
.grid > *, .booklayout > *, .contactlayout > *, .hero__grid > * { min-width: 0; }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
/* 260px, not 220px: at tablet widths (roughly 700-900px wrap) 220px let a
   4 item grid squeeze 3 columns into the row, orphaning the 4th item alone
   on its own row with two empty column tracks beside it. 260 forces a clean
   2x2 at that width instead, while still fitting all 4 across on desktop. */
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }

.card {
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	padding: var(--s-5);
	transition: border-color .18s var(--ease), transform .18s var(--ease), box-shadow .18s var(--ease);
}
.card:hover { border-color: #D6D0C2; transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.card h3 { margin-bottom: var(--s-2); }
.card__media {
	aspect-ratio: 16 / 9;
	width: 100%;
	object-fit: cover;
	border-radius: var(--radius-sm);
	background: var(--line-soft);
	margin-bottom: var(--s-4);
}
/* Vehicle photos, not blog thumbnails: cover crops whatever does not fit the
   box, and on a car that is the front bumper and roofline, exactly what
   matters for "which car is this". The source photos run 1.95:1 to 2.36:1,
   wider than the 16:9 box, so this widens it and shows the full car with a
   little padding either side rather than cropping it. */
.card__media--fit {
	/* The delivered photos are now tightly cropped around the car (2026-08-22
	   fix), running close to 1.9:1 to 2.2:1, so this box is set to the same
	   ratio and the photo covers it edge to edge like the base .card__media
	   rule does. contain plus internal padding was the right call when the
	   source photos still carried a lot of baked in white margin, since it
	   guaranteed nothing was cropped; now that margin is gone, contain only
	   adds letterboxing on top of an already tight photo and makes the car
	   look smaller than the card, which is the "images are too small"
	   complaint. cover crops at most a sliver off one edge now, never the
	   bumper or roofline. */
	aspect-ratio: 2 / 1;
	object-fit: cover;
}

.card--veh__blurb { color: var(--muted); margin-bottom: var(--s-3); min-height: 2.6em; }
.card p { font-size: var(--t-sm); color: var(--muted); margin: 0; }

/* Four across on desktop (there are always exactly four vehicles), a
   horizontal scroll carousel below tablet width instead of a cramped single
   column stack. */
@media (max-width: 768px) {
	.fleetgrid {
		display: flex;
		grid-template-columns: none;
		overflow-x: auto;
		scroll-snap-type: x proximity;
		-webkit-overflow-scrolling: touch;
		padding-bottom: 6px;
		margin: 0 -4px;
	}
	.fleetgrid .card--veh {
		flex: 0 0 clamp(220px, 70vw, 280px);
		scroll-snap-align: start;
	}
}

.card--link { text-decoration: none; display: block; }

.cardicon {
	width: 44px; height: 44px; border-radius: 11px;
	background: rgba(252,125,3,.14);
	color: var(--orange);
	display: grid; place-items: center;
	margin-bottom: var(--s-4);
}

/* ---------- Footer ---------- */

.site-footer { background: var(--ink); color: var(--ink-muted); padding-block: var(--s-8) var(--s-5); }
.site-footer h3 {
	font-family: var(--display); font-size: var(--t-sm); font-weight: 700;
	color: #fff; letter-spacing: .04em; margin: 0 0 var(--s-4);
}
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
/* Scoped past the header's own .brand img rule: bigger here only, the header
   logo is untouched. */
.site-footer .brand img { height: 56px; }
/* :not(.btn) matters. Without it this rule repaints the phone button's label
   grey on orange, which is unreadable and is exactly how the FAQ call to
   action ended up white on white. */
.site-footer a:not(.btn) { color: var(--ink-muted); text-decoration: none; font-size: var(--t-sm); }
.site-footer a:not(.btn):hover { color: #fff; }
.footer__grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--s-6); }
.footer__bottom {
	margin-top: var(--s-7); padding-top: var(--s-5);
	border-top: 1px solid var(--ink-line);
	display: flex; flex-wrap: wrap; gap: 6px var(--s-3); justify-content: center;
	text-align: center;
	font-size: var(--t-xs);
}

/* ---------- Sticky mobile action bar ----------
   On a phone the two things that matter are call and book. They stay put. */

.actionbar {
	display: none;
	position: fixed; bottom: 0; left: 0; right: 0; z-index: 90;
	align-items: stretch;
	padding-bottom: env(safe-area-inset-bottom);
	box-shadow: 0 -4px 20px rgba(11,11,13,.1);
	overflow: visible;
}
/* Two flush halves, one bar. No gap, no radius between them, only the outer
   corners rounded, so the two colours read as one connected control rather
   than two buttons sitting side by side. */
.actionbar__half {
	flex: 1;
	display: flex; align-items: center; justify-content: center;
	min-height: 56px;
	font-family: var(--display); font-weight: 700; font-size: .92rem;
	color: #fff; text-decoration: none;
	white-space: nowrap;
	text-transform: uppercase;
}
.actionbar__half--book { background: var(--orange); padding-right: 34px; }
.actionbar__half--call { background: var(--ink); padding-left: 34px; }
/* The phone itself, raised on the seam. This is the one action a passenger
   in a hurry reaches for first, so it gets the visual weight, not just a
   label like the other two. */
.actionbar__call {
	position: absolute; left: 50%; top: -18px;
	transform: translateX(-50%);
	width: 52px; height: 52px; border-radius: 50%;
	background: #fff; color: var(--orange);
	display: grid; place-items: center;
	box-shadow: 0 4px 14px rgba(11,11,13,.25);
	border: 3px solid var(--paper);
}
.actionbar__call svg { width: 22px; height: 22px; }

/* Multi segment version. Kept the two-half-plus-circle rules above rather
   than delete them, since this exact component has flipped back and forth
   more than once already and re-adding them from scratch would be pure
   churn if it changes again. */
.actionbar--four { align-items: stretch; }
.actionbar__third {
	flex: 1;
	min-width: 0;
	display: flex; align-items: center; justify-content: center; gap: 5px;
	min-height: 56px;
	padding-inline: 3px;
	font-family: var(--display); font-weight: 700;
	/* A fixed .82rem sat at an exact three-way fit in testing, with no
	   margin for a real device's font metrics to differ even slightly.
	   Fluid sizing keeps a floor so it can give up a little width instead
	   of forcing the row a hair past 100%. A fourth segment (SMS) tightens
	   this further, the clamp's floor is what actually keeps four columns
	   from wrapping rather than any change to the rule itself. */
	font-size: clamp(.66rem, 2.6vw, .82rem);
	color: #fff; text-decoration: none;
	white-space: nowrap;
	text-transform: uppercase;
}
.actionbar__third svg { width: 17px; height: 17px; flex-shrink: 0; }
.actionbar__third--book     { background: var(--orange); }
.actionbar__third--call     { background: var(--ink); }
.actionbar__third--sms      { background: #1E6DAD; }
.actionbar__third--whatsapp { background: #25D366; }

/* Two flat halves with the Google mark as a real circle sitting inline
   between them, at the bar's own height, not raised above it like the
   earlier version. A normal flex item, not position:absolute. */
.actionbar--pair {
	display: flex;
	align-items: stretch;
	background: var(--ink);
}
/* .actionbar__half is reused by the older two-half-plus-raised-circle rules
   above (kept, unused, in case this component flips again), which is exactly
   why the book/call halves here use their own unique classes rather than
   --book/--call modifiers: those modifier names already exist up there with
   a padding-left/right meant to clear space for a circle that overlapped
   from the edge in that version. Reusing them silently carried that padding
   into this version too, since a later rule only overrides properties it
   explicitly redeclares, not ones it never mentions. That is exactly the bug
   that shifted "Call" off centre here before this was split out. */
.actionbar__half {
	flex: 1;
	display: flex; align-items: center; justify-content: center;
	min-height: 56px;
	font-family: var(--display); font-weight: 700; font-size: .92rem;
	color: #fff; text-decoration: none;
	gap: 6px;
	white-space: nowrap;
}
.actionbar__pairbook { background: var(--orange); }
.actionbar__paircall { background: var(--ink); }
.actionbar__half svg { width: 17px; height: 17px; flex-shrink: 0; }
.actionbar__g {
	flex: 0 0 auto;
	width: 44px; height: 44px; margin: auto 8px;
	border-radius: 50%;
	background: #fff;
	display: grid; place-items: center;
	box-shadow: 0 1px 4px rgba(11,11,13,.25);
}
.actionbar__g svg { width: 22px; height: 22px; }
/* WhatsApp's own glyph is a solid white mark, meant to sit on their brand
   green, not the white-circle treatment the Google G needed to show its own
   colours correctly. */
.actionbar__g--whatsapp { background: #25D366; color: #fff; }

/* ---------- Responsive ---------- */

@media (max-width: 1080px) {
	.nav__list { gap: var(--s-4); }
	.nav__list a { font-size: 13.5px; }
	.hero__grid { grid-template-columns: 1fr minmax(360px, 420px); }
}

@media (max-width: 1100px) {
	.nav { display: none; }
	.navtoggle { display: inline-flex; }
	.callnow__txt { display: none; }
	/* The nav carries the margin-left:auto that pushes this group right, so
	   with the nav hidden the actions have to claim that space themselves. */
	.header__actions { margin-left: auto; }

	/* On a phone the booking card is the job, so it comes early. It does not
	   come first though: landing on a bare pair of address fields, with no
	   headline above them, gives no clue what the site even is. The heading
	   leads, the card follows straight after it, and the supporting copy sits
	   underneath. display:contents lifts the copy block's children up into the
	   grid so they can be ordered around the card without duplicating markup. */
	.hero__grid { grid-template-columns: 1fr; gap: 0; }
	.hero__copy { display: contents; }
	.hero__copy > * { min-width: 0; }
	.hero__copy > .eyebrow { order: 1; }
	.hero__copy > h1 { order: 2; margin: 10px 0 22px; }
	.hero__form { order: 3; margin-bottom: 26px; }
	.hero__copy > .hero__lede { order: 4; margin-bottom: 20px; }
	.hero__copy > .trustrow { order: 5; }

	.footer__grid { grid-template-columns: 1fr 1fr; gap: var(--s-5); }
}

@media (max-width: 660px) {
	/* A percentage flex-basis here is what caused the actual bug: the track
	   is width:max-content (it has to be, for the marquee animation's own
	   math to work), so a child asking for a percentage of that has nothing
	   real to resolve against and blew out to several thousand pixels wide,
	   the card rendering as unstyled overflowing text with no visible border.
	   A fixed width has a real number to be a percentage, or rather, be
	   fixed, of. */
	.reviewmarquee .reviewcard { flex-basis: 260px; }
	/* The sticky action bar carries booking on a phone, so the header button
	   comes out rather than being squeezed. */
	.header__actions .btn--primary { display: none; }
	.site-header__bar { gap: var(--s-3); }
}

/* Touch targets. Keyed to the pointer rather than the width, because a tablet
   is a wide screen you still operate with a finger. Hit areas grow with
   padding, so nothing changes visually, the target just stops being fiddly.
   The width branch carries phones and tablets, which is also where the mobile
   nav takes over, and the pointer branch picks up touch laptops that are wider
   than that. Left alone on a mouse driven desktop, where a 16px link is
   perfectly clickable and padding every footer link out to 44px would only
   double the footer's height for nothing. */
@media (max-width: 1100px), (pointer: coarse) {
	.locate { width: 44px; height: 44px; }
	/* The icon grew, so the text has to clear it by more than it did before. */
	.field__with-btn input { padding-right: 58px; }
	.callnow__icon { width: 44px; height: 44px; }
	.brand { padding-block: 6px; min-height: 44px; display: inline-flex; align-items: center; }
	.nav__list a { display: flex; align-items: center; min-height: 44px; }
	.site-footer ul { gap: 0; }
	.site-footer ul a {
		display: flex; align-items: center;
		min-height: 38px; padding-block: 6px;
	}
}

@media (max-width: 620px) {
	/* Two short lines stacked and left aligned reads clean; space-between
	   with only two items and not much width to work with was wrapping them
	   unevenly, one hugging the right edge on its own line. */
	.footer__bottom { flex-direction: column; align-items: center; gap: var(--s-2); }

	.wrap { padding-inline: var(--s-4); }
	/* Matches .wrap's narrower padding at this width, or the review row's
	   edges stop lining up with the rest of the page's content. */
	.reviewmarquee { margin-inline: calc(var(--s-4) * -1); }
	.reviewmarquee__track { padding-inline: var(--s-4); }
	.bookcard__body { padding: var(--s-4); }
	.field-row { grid-template-columns: 1fr; }
	.field-row--split { grid-template-columns: 1fr 1fr; gap: var(--s-2); }
	.meter { flex-direction: column; align-items: flex-start; gap: var(--s-2); }
	.footer__grid { grid-template-columns: 1fr; }
	.actionbar { display: flex; }
	/* On the footer itself, not body: body's own background is the light
	   paper tone, so padding reserved there for the fixed bar showed up as a
	   mismatched pale gap between the black footer and the bar. The footer's
	   own black background fills the same reserved space correctly instead.
	   Also right sized now: 56px bar height, not the 92px this held over from
	   the earlier version where the phone circle was raised 18px above the
	   bar rather than sitting inline within it. */
	.site-footer { padding-bottom: calc(var(--s-5) + 56px); }
	.trustrow { gap: var(--s-3) var(--s-5); }
}

/* ---------- Nav drawer (mobile) ---------- */

.drawer {
	position: fixed; inset: 0; z-index: 200;
	background: var(--ink);
	padding: var(--s-5);
	overflow-y: auto;
	display: none;
}
.drawer.is-open { display: block; }
.drawer__top { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--s-6); }
.drawer ul { list-style: none; margin: 0 0 var(--s-6); padding: 0; display: grid; gap: 2px; }
.drawer__nav ul { list-style: none; margin: 0; padding: 0; }
.drawer__group summary {
	display: flex; align-items: center; justify-content: space-between;
	min-height: 48px; padding: 13px 0;
	border-bottom: 1px solid var(--ink-line);
	color: #fff; cursor: pointer; list-style: none;
	font-family: var(--display); font-weight: 600; font-size: var(--t-lg);
}
.drawer__group summary::-webkit-details-marker { display: none; }
.drawer__group summary svg { width: 20px; height: 20px; transition: transform .18s var(--ease); }
.drawer__group[open] summary svg { transform: rotate(180deg); }
/* The children sit indented under their heading so the grouping reads at a
   glance, rather than looking like eleven equal items. */
.drawer__group > ul { padding-left: var(--s-4); }
.drawer__group > ul a {
	font-size: var(--t-base);
	font-weight: 500;
	opacity: .82;
}

.drawer ul a {
	display: flex; align-items: center; min-height: 48px;
	color: #fff; text-decoration: none;
	font-family: var(--display); font-weight: 600; font-size: var(--t-lg);
	padding: 13px 0; border-bottom: 1px solid var(--ink-line);
}

/* ---------- Suburb and route page components ---------- */

/* A page hero is shorter than the home hero: the headline has already been
   earned by the search result that brought them here. */
.hero--page { padding-block: clamp(28px, 4vw, 52px) clamp(40px, 6vw, 72px); }
.hero--page h1 { font-size: var(--t-3xl); }

/* A service page's own photo, sitting between the hero and the first content
   section. Kept out of hero__grid entirely rather than added as a third
   column: that grid already reorders on mobile via display:contents, and a
   photo dropped into it would have to be threaded through that same order
   logic. A separate block underneath has no such entanglement. */
.svcphoto-wrap { padding-block: clamp(28px, 4vw, 48px); }
.svcphoto { display: block; width: 100%; height: clamp(200px, 30vw, 400px); object-fit: cover; border-radius: var(--radius-lg); }

/* Fare tables. Tabular figures so the dollar columns line up down the page. */
.fares { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.fares th, .fares td { padding: 13px 14px; text-align: left; }
.fares thead th {
	font-family: var(--display);
	font-size: var(--t-xs);
	text-transform: uppercase;
	letter-spacing: .1em;
	color: var(--muted);
	border-bottom: 2px solid var(--line);
	white-space: nowrap;
}
.fares tbody th { font-weight: 700; }
.fares a { display: inline-block; padding: 6px 0; min-height: 32px; }
@media (max-width: 768px) {
	.fares a { padding: 11px 0; min-height: 44px; }
}
.fares tbody tr { border-bottom: 1px solid var(--line-soft); }
.fares tbody tr:last-child { border-bottom: 0; }
.fares tbody tr:hover { background: var(--paper); }

/* Neighbour links. Real distances, so the row is information rather than a
   wall of keyword links. */
.svccarousel {
	display: flex;
	gap: var(--s-5);
	overflow-x: auto;
	scroll-snap-type: x proximity;
	-webkit-overflow-scrolling: touch;
	padding-bottom: 6px;
	margin: 0 -4px;
}
.svccarousel__card {
	flex: 0 0 clamp(240px, 27vw, 300px);
	scroll-snap-align: start;
	display: flex;
	flex-direction: column;
	gap: 12px;
	text-decoration: none;
	color: var(--text);
	background: var(--surface);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	padding: var(--s-4);
	transition: border-color .18s var(--ease), transform .18s var(--ease), box-shadow .18s var(--ease);
}
.svccarousel__card:hover { border-color: #D6D0C2; transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.svccarousel__media {
	width: 100%;
	height: clamp(150px, 14vw, 190px);
	object-fit: cover;
	border-radius: var(--radius-sm);
	display: block;
}
.svccarousel__icon {
	width: 100%;
	height: clamp(150px, 14vw, 190px);
	border-radius: var(--radius-sm);
	background: var(--line-soft);
	display: grid;
	place-items: center;
	color: var(--orange);
}
.svccarousel__title { font-weight: 700; font-size: var(--t-base); line-height: 1.3; }
.svccarousel__card:hover .svccarousel__title { color: var(--orange); }
.svccarousel__copy { font-size: var(--t-sm); color: var(--muted); line-height: 1.45; margin-top: -4px; }

.pillrow { display: flex; flex-wrap: wrap; gap: 10px; }
.pill {
	display: inline-flex;
	align-items: baseline;
	gap: 8px;
	padding: 12px 18px;
	min-height: 44px;
	border: 1.5px solid var(--line);
	border-radius: 999px;
	text-decoration: none;
	font-size: var(--t-sm);
	font-weight: 600;
	transition: border-color .15s var(--ease), background-color .15s var(--ease);
}
.pill span { font-weight: 400; color: var(--muted); font-size: var(--t-xs); }
.pill:hover { border-color: var(--orange); background: rgba(252,125,3,.06); }

@media (max-width: 620px) {
	.fares th, .fares td { padding: 11px 10px; }
}

/* ---------- Service areas hub ---------- */

.arealist {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
	gap: 2px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	overflow: hidden;
	background: var(--line);
}
.areaitem {
	display: block;
	background: var(--surface);
	padding: 14px 18px;
	min-height: 56px;
	text-decoration: none;
	transition: background-color .15s var(--ease);
}
.areaitem:hover { background: rgba(252,125,3,.07); }
.areaitem b { display: block; font-size: var(--t-sm); font-weight: 700; }
.areaitem span { display: block; font-size: var(--t-xs); color: var(--muted); margin-top: 2px; }

/* ---------- Booking page ---------- */

.booklayout { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--s-6); align-items: start; }

.steps { list-style: none; margin: 0; padding: 0; counter-reset: step; display: grid; gap: 14px; }
.steps li { counter-increment: step; padding-left: 38px; position: relative; }
.steps li::before {
	content: counter(step);
	position: absolute; left: 0; top: 0;
	width: 26px; height: 26px; border-radius: 50%;
	background: var(--orange); color: var(--ink);
	font-family: var(--display); font-weight: 800; font-size: 13px;
	display: grid; place-items: center;
}
.steps b { display: block; font-size: var(--t-sm); }
.steps span { display: block; font-size: var(--t-xs); color: var(--muted); }

.ticks { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.ticks li { padding-left: 26px; position: relative; font-size: var(--t-sm); color: var(--muted); }
.ticks li::before {
	content: "";
	position: absolute; left: 0; top: 7px;
	width: 14px; height: 8px;
	border-left: 2.5px solid var(--vacant);
	border-bottom: 2.5px solid var(--vacant);
	transform: rotate(-45deg);
}

@media (max-width: 900px) {
	.booklayout { grid-template-columns: 1fr; }
	/* The reassurance panel belongs under the form on a phone, not above it. */
	.booklayout__side { order: 2; }
	.booklayout__form { order: 1; }
}

/* ---------- Fares page ---------- */

.rate {
	font-family: var(--display);
	font-weight: 800;
	font-size: 1.6rem;
	letter-spacing: -0.03em;
	color: var(--orange);
	font-variant-numeric: tabular-nums;
	margin-bottom: 6px;
}

/* The estimator's working, shown above the total. */
.farebreak {
	margin-top: var(--s-4);
	border-top: 1px solid var(--line);
	padding-top: var(--s-3);
}
/* Review step summary. The label must not shrink: flex items default to
   min-width:auto, so a long value squeezes the label until it wraps one
   character per line, which is how "Return" rendered as Re / tur / n. */
.summary { display: grid; gap: 8px; margin: 0; font-size: var(--t-sm); }
.summary__row {
	display: flex; gap: var(--s-4); justify-content: space-between;
	border-bottom: 1px solid var(--line-soft);
	padding-bottom: 6px;
}
.summary__row dt { color: var(--muted); flex-shrink: 0; white-space: nowrap; }
.summary__row dd {
	margin: 0; font-weight: 600; text-align: right;
	min-width: 0; overflow-wrap: break-word;
}

.farebreak__row {
	display: flex;
	justify-content: space-between;
	gap: var(--s-4);
	padding: 7px 0;
	font-size: var(--t-sm);
	color: var(--muted);
	border-bottom: 1px solid var(--line-soft);
}
/* Leg headings in a return quote. Without them the breakdown reads as one long
   list of charges and the total looks wrong. */
.farebreak__row--head {
	font-weight: 700;
	color: var(--text);
	border-top: 1px solid var(--line);
	margin-top: 4px;
	padding-top: 8px;
}

.farebreak__row:last-child { border-bottom: 0; }
.farebreak__row b { color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- Fleet specs ---------- */

.spec { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: var(--t-xs); color: var(--muted); }
.spec b { color: var(--text); font-weight: 700; }
.spec span { display: flex; align-items: center; gap: 6px; }
.spec svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--muted); }

/* ---------- FAQ page ---------- */

.faqlayout { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: var(--s-7); align-items: start; }
.faqlayout > * { min-width: 0; }

.faqnav { position: sticky; top: 100px; }
.faqnav b { display: block; font-family: var(--display); font-size: var(--t-xs); letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin-bottom: var(--s-3); }
.faqnav ul { list-style: none; margin: 0 0 var(--s-5); padding: 0; display: grid; gap: 2px; }
.faqnav ul a { display: flex; align-items: center; min-height: 44px; text-decoration: none; font-size: var(--t-sm); padding: 9px 12px; border-radius: 8px; color: var(--muted); }
.faqnav ul a:hover { background: var(--surface); color: var(--text); }

.faqgroup { margin-bottom: var(--s-7); }
.faqgroup:last-child { margin-bottom: 0; }
.faqgroup h2 { margin-bottom: var(--s-4); }

.qa {
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface);
	margin-bottom: 10px;
	overflow: hidden;
}
.qa summary {
	cursor: pointer;
	padding: 15px 18px;
	font-weight: 600;
	font-size: var(--t-base);
	list-style: none;
	display: flex;
	justify-content: space-between;
	gap: var(--s-4);
	align-items: flex-start;
}
.qa summary::-webkit-details-marker { display: none; }
.qa summary::after {
	content: "";
	flex-shrink: 0;
	width: 9px; height: 9px;
	margin-top: 6px;
	border-right: 2px solid var(--muted);
	border-bottom: 2px solid var(--muted);
	transform: rotate(45deg);
	transition: transform .2s var(--ease);
}
.qa[open] summary::after { transform: rotate(-135deg); }
.qa p { padding: 0 18px 16px; color: var(--muted); font-size: var(--t-sm); margin: 0; }

/* ---------- Contact ---------- */

.contactlayout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: var(--s-6); align-items: start; }

.contactcard {
	display: flex;
	align-items: center;
	gap: var(--s-4);
	padding: 18px 20px;
	border: 1.5px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface);
	text-decoration: none;
	margin-bottom: 12px;
	transition: border-color .15s var(--ease), transform .15s var(--ease);
}
.contactcard:hover { border-color: var(--orange); transform: translateY(-1px); }
.contactcard__icon {
	width: 46px; height: 46px; border-radius: 12px; flex-shrink: 0;
	background: rgba(252,125,3,.12); color: var(--orange);
	display: grid; place-items: center;
}
.contactcard small { display: block; font-size: var(--t-xs); color: var(--muted); }
.contactcard b { font-family: var(--display); font-size: var(--t-lg); letter-spacing: -0.02em; word-break: break-word; }
.contactcard--primary { background: var(--ink); border-color: var(--ink); }
.contactcard--primary small { color: var(--ink-muted); }
.contactcard__icon--whatsapp { background: rgba(37,211,102,.14); color: #25D366; }
.contactcard--primary b { color: #fff; }
.contactcard--primary .contactcard__icon { background: rgba(252,125,3,.2); color: var(--orange); }

/* Honeypot. Off screen rather than display:none, which some bots skip. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

@media (max-width: 900px) {
	.faqlayout { grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
	.faqnav { position: static; min-width: 0; }
	/* The chip row scrolls inside itself. Without the explicit min-width:0 the
	   track grows to fit every chip and takes the page with it. */
	.faqnav ul {
		display: flex;
		flex-wrap: nowrap;
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
		gap: 6px;
		padding-bottom: 6px;
		min-width: 0;
		scrollbar-width: thin;
	}
	.faqnav li { flex: 0 0 auto; }
	.faqnav ul a { white-space: nowrap; border: 1px solid var(--line); background: var(--surface); }
	.contactlayout { grid-template-columns: 1fr; }
}

/* ---------- Blog ---------- */

.postcard time { display: block; font-size: var(--t-xs); color: var(--muted); margin-bottom: 6px; }
.postcard h2 { font-size: 1.05rem; line-height: 1.3; margin-bottom: 8px; letter-spacing: -0.01em; }

.pagination { display: flex; justify-content: center; gap: 6px; margin-top: var(--s-6); flex-wrap: wrap; }
.pagination .page-numbers {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: 42px; height: 42px; padding: 0 14px;
	border: 1px solid var(--line); border-radius: var(--radius-sm);
	background: var(--surface); text-decoration: none; font-size: var(--t-sm); font-weight: 600;
}
.pagination .page-numbers:hover { border-color: var(--text); }
.pagination .page-numbers.current { background: var(--orange); border-color: var(--orange); color: var(--ink); }

/* ---------- Article ---------- */

.article { max-width: 760px; }
.backlink { display: inline-block; font-size: var(--t-sm); color: var(--muted); text-decoration: none; margin-bottom: var(--s-4); padding: 10px 0; min-height: 44px; }
.backlink:hover { color: var(--text); }
.articlemeta { font-size: var(--t-sm); color: var(--muted); margin-bottom: var(--s-6); }
.article__hero { margin: 0 0 var(--s-6); }
.article__img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--radius); background: var(--paper, #F6F6F4); }

.entry { font-size: 1.05rem; line-height: 1.8; }
.entry h2 { font-size: var(--t-2xl); margin-top: var(--s-7); }
.entry h3 { font-size: var(--t-xl); margin-top: var(--s-6); }
.entry p { margin-bottom: var(--s-4); }
.entry ul, .entry ol { margin: 0 0 var(--s-4); padding-left: 1.3em; }
.entry li { margin-bottom: 8px; }
.entry img { border-radius: var(--radius); margin: var(--s-5) 0; }
.entry a:not(.btn) { color: var(--orange); text-underline-offset: 3px; }
/* Inline links inside a sentence are deliberately left at line height. WCAG
   2.5.8 exempts them, and an enlarged overlay would sit on top of the lines
   above and below and steal their taps, which is worse than the thing it
   claims to fix. */
.entry blockquote {
	margin: var(--s-5) 0; padding: var(--s-4) var(--s-5);
	border-left: 3px solid var(--orange); background: var(--surface);
	border-radius: 0 var(--radius) var(--radius) 0;
}
.entry table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.entry th, .entry td { padding: 10px 12px; border-bottom: 1px solid var(--line); text-align: left; }

.articlecta {
	margin-top: var(--s-7);
	padding: var(--s-5);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface);
}

/* ---------- Layout utilities ----------
   These exist so templates stop reaching for inline styles. An inline style
   cannot be overridden by a media query, so a hardcoded 48px gap stays 48px on
   a 360px phone. Anything spacing related belongs here instead. */

.grid--roomy { gap: clamp(28px, 5vw, 48px); }
.grid--middle { align-items: center; }
.grid--top { align-items: start; }

.narrow { max-width: 820px; }
.narrower { max-width: 640px; }

/* Button rows. Wrap, and go full width on the smallest screens so two buttons
   never end up as awkward half-width stubs. */
.btnrow { display: flex; gap: 12px; flex-wrap: wrap; }
.btnrow--center { justify-content: center; }
/* Both buttons the same width, the wider of the two decides it. Scoped, not
   applied to .btnrow generally, since most CTA pairs on the site are fine
   sized to their own content and forcing that everywhere would make short
   labels look padded and odd. */
/* min-width:0 matters: flex items default to min-width:auto, which refuses
   to shrink below content size regardless of flex-shrink, and that is why
   these two wrapped onto separate lines on a narrow phone instead of sharing
   the row like flex:1 1 0 alone should have guaranteed. */
.btnrow--even { flex-wrap: nowrap; }
.btnrow--even .btn {
	flex: 1 1 0;
	min-width: 0;
	white-space: nowrap;
	/* Same width does not mean same content length: "Book a taxi" and a full
	   phone number are different widths of text, so the phone button's own
	   font size flexes down fluidly to whatever fits its equal share of the
	   row, rather than either wrapping onto two lines or overflowing it. */
	font-size: clamp(.85rem, 3.6vw, 1.05rem);
}
/* The icon has no flex-shrink protection anywhere else in .btn, since this
   is the first place a button with an icon sits in a width this tight. Left
   unset it shrank to 0x0 rather than the text, which is backwards: the
   number is what needs to stay legible. */
.btnrow--even .btn svg { flex-shrink: 0; width: 18px; height: 18px; }
@media (min-width: 420px) {
	.btnrow--even .btn { flex: 0 1 220px; }
}

@media (max-width: 420px) {
	.btnrow { flex-direction: column; align-items: stretch; }
	.btnrow .btn { width: 100%; }
	/* .btnrow--even opts back out: its whole point is staying side by side
	   at equal width, which the stacked default above would undo. */
	.btnrow--even { flex-direction: row; }
	.btnrow--even .btn { width: auto; }
}

.meter--sm .meter__value { font-size: clamp(1.35rem, 1.1rem + 1vw, 1.6rem); }

/* Long unbroken strings (an address, an email) must never widen the page. */
.card, .qa, .entry, .contactcard { overflow-wrap: anywhere; }
/* meter__label only ever holds short, plain-English text ("Estimated fare,
   sedan"), never a long unbroken token, so it keeps ordinary word wrapping.
   overflow-wrap: anywhere was breaking words mid-letter (e.g. "ESTIMATED" ->
   "ESTIMA" / "TED") the moment the value beside it (a big "$114-$140") left
   the label a narrow column, which happens on any two column card grid
   around tablet width. Removing it from the shared rule above was not
   enough on its own: overflow-wrap inherits, and this label sits inside
   .card, which is still "anywhere" for the addresses it actually needs
   that on, so it has to be reset explicitly here rather than relying on
   .meter__label just not being in that list. */
.meter__label { min-width: 0; flex-shrink: 1; overflow-wrap: normal; }

/* ---------- Search ---------- */

.searchwrap { margin-top: var(--s-5); max-width: 520px; }

.searchform { display: flex; gap: 8px; }
.searchform input[type="search"] {
	flex: 1;
	min-width: 0;
	font-family: var(--body);
	font-size: var(--t-base);
	padding: 13px 14px;
	border: 1.5px solid var(--ink-line);
	border-radius: var(--radius-sm);
	background: var(--ink-soft);
	color: #fff;
}
.searchform input[type="search"]::placeholder { color: var(--ink-muted); }
.searchform input[type="search"]:focus {
	outline: none;
	border-color: var(--orange);
	box-shadow: 0 0 0 3px rgba(252,125,3,.25);
}
.searchform .btn { flex: 0 0 auto; }

@media (max-width: 480px) {
	.searchform { flex-direction: column; }
	.searchform .btn { width: 100%; }
}

/* Airport page: fare tables that stack into labelled cards on a phone rather
   than scroll sideways, and the numbered arrival steps. */
.airsteps { margin: 0; padding-left: 1.4em; display: grid; gap: var(--s-3); max-width: 70ch; }
.airsteps li { padding-left: .2em; }
@media (max-width: 620px) {
	.fares--stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
	.fares--stack, .fares--stack tbody, .fares--stack tr, .fares--stack th, .fares--stack td { display: block; width: 100%; }
	.fares--stack tr { padding: 10px 0; }
	.fares--stack tbody th { padding: 4px 0; font-size: var(--t-base); }
	.fares--stack td { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; text-align: right !important; }
	.fares--stack td::before { content: attr(data-label); font-weight: 400; color: var(--muted); text-align: left; }
}
/* The six column vehicle table needs the stacked layout up to tablet width. */
@media (min-width: 621px) and (max-width: 900px) {
	.fares--stack-md thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
	.fares--stack-md, .fares--stack-md tbody { display: block; width: 100%; }
	.fares--stack-md tr { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px 24px; padding: 14px 0; }
	.fares--stack-md tbody th { grid-column: 1 / -1; padding: 0 0 4px; font-size: var(--t-base); }
	.fares--stack-md td { display: flex; flex-direction: column; padding: 4px 0; text-align: left !important; }
	.fares--stack-md td::before { content: attr(data-label); font-weight: 400; color: var(--muted); font-size: var(--t-xs); }
}

/* ---------- Home page revision 2026-10-14 ---------- */

/* Form section labels are plain text now, not headings, so they no longer
   inherit the h1 to h4 base. Same look as before. */
.formsec__title {
	display: block;
	font-family: var(--display);
	font-weight: 700;
	line-height: 1.1;
	letter-spacing: -0.02em;
	color: var(--text);
	text-transform: capitalize;
}
.formsec__legal { margin: var(--s-3) 0 0; font-size: var(--t-xs); color: var(--muted); text-align: center; }
.formsec__legal a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* Fares notes and how it works. */
.homefares__notes { margin-top: var(--s-6); }
.homefares__notes h3 { font-size: var(--t-lg); margin-bottom: var(--s-3); }
.homefares__notes p, .homesteps__alt { font-size: var(--t-sm); color: var(--text); }
.homefares__notes a, .homesteps__alt a { color: var(--orange-deep, #B85300); text-underline-offset: 3px; }
.homesteps { margin: var(--s-4) 0 var(--s-5); }
.homesteps b { font-size: var(--t-base); }
.homesteps span { font-size: var(--t-sm); }
.homesteps__btn { margin-top: var(--s-3); }

/* Home reviews: printed once in the HTML. main.js clones the cards (aria
   hidden) and adds .is-looping, and only then does the marquee animate.
   Without that the row is a plain horizontal scroller. */
.reviewmarquee--js:not(.is-looping) .reviewmarquee__track { animation: none; }
.reviewmarquee--js:not(.is-looping) { overflow-x: auto; scroll-snap-type: x proximity; }
.reviewmarquee--js:not(.is-looping) .reviewcard { scroll-snap-align: start; }

/* Footer operator details. */
.footer__contact { font-size: .875rem; margin-top: var(--s-3); line-height: 1.7; }

/* Question headings in post content stay in sentence case (see the
   the_content filter in functions.php that adds .is-q). */
h2.is-q, h3.is-q, h4.is-q { text-transform: none; }

/* Free Silver Service upgrade: the note under the car choice and the fare
   line that shows when it applies. Green is the "vacant" colour, used here
   because it marks something the passenger gains, not a charge. */
.upgradenote {
	display: flex; gap: var(--s-2); align-items: flex-start;
	margin: var(--s-3) 0 0; padding: var(--s-3);
	border: 1px solid rgba(18,161,95,.3); border-radius: 10px;
	background: rgba(18,161,95,.07);
	font-size: var(--t-sm); line-height: 1.45; color: var(--text);
}
.upgradenote svg { color: var(--vacant); flex-shrink: 0; width: 18px; height: 18px; margin-top: 1px; }
.farebreak__row--free b { color: var(--vacant); }
.meter .farebreak__row--free b { color: #3DDC97; }

/* ---------- Local hero (13 Taxi North Suburban) ----------
   Same headline and booking card as the base theme, set over a photo of the
   north, with a drawn map of the suburbs served and the Google rating under
   the headline. The photo sits under a dark wash so the text keeps its
   contrast; the wash is heaviest on the left where the copy sits. */
.hero--local {
	background:
		linear-gradient(90deg, rgba(11,11,13,.92) 0%, rgba(11,11,13,.75) 40%, rgba(11,11,13,.25) 75%, rgba(11,11,13,.1) 100%),
		var(--hero-photo) center bottom / cover no-repeat,
		var(--ink);
}
.hero--local::before { display: none; }
.hero--local .hero__lede { color: rgba(255,255,255,.8); }

.tripmap--hero {
	margin: 0 0 var(--s-5);
	max-width: 560px;
	border: 1px solid rgba(255,255,255,.14);
	border-radius: 16px;
	overflow: hidden;
}
.tripmap--hero { margin: 0 0 var(--s-5); }
.tripmap--hero .tripmap__map {
	height: 320px;
	/* Until Google Maps paints over it: a quiet street grid, not a grey void. */
	background:
		linear-gradient(rgba(255,255,255,.05) 1px, transparent 1px) 0 0 / 32px 32px,
		linear-gradient(90deg, rgba(255,255,255,.05) 1px, transparent 1px) 0 0 / 32px 32px,
		#17171B;
}

.hero__rating {
	display: inline-flex; align-items: center; gap: 10px; flex-wrap: wrap;
	padding: 10px 16px 10px 10px;
	border-radius: 999px;
	background: #fff; color: var(--text);
	text-decoration: none;
	box-shadow: 0 8px 24px rgba(0,0,0,.25);
}
.hero__rating:hover { box-shadow: 0 10px 28px rgba(0,0,0,.35); }
.hero__g {
	width: 30px; height: 30px; border-radius: 50%;
	display: grid; place-items: center;
	background: #fff; border: 1px solid var(--line);
	font: 800 16px var(--display); color: #4285F4;
}
.hero__score { font: 800 18px var(--display); }
.hero__stars { color: #FBBC04; display: inline-flex; gap: 1px; }
.hero__rtext { font-size: var(--t-sm); font-weight: 600; }

@media (max-width: 900px) {
	.hero--local { background: linear-gradient(180deg, rgba(11,11,13,.88) 0%, rgba(11,11,13,.8) 55%, rgba(11,11,13,.35) 85%, rgba(11,11,13,.15) 100%), var(--hero-photo) 75% bottom / auto 70% no-repeat, var(--ink); padding-bottom: 62vw; }
	.hero__copy > .tripmap--hero { order: 6; }
	.hero__copy > .hero__rating { order: 7; justify-self: start; }
}
.tripmap--hero .tripmap__card { color: var(--text); }
@media (max-width: 520px) { .hero__rating { border-radius: 16px; } }

/* ---------- Booking form in steps ---------- */
.bsteps {
	list-style: none; margin: 0 0 var(--s-4); padding: 0;
	display: grid; grid-template-columns: repeat(4, 1fr);
	border-bottom: 1px solid var(--line);
}
.bsteps__btn {
	width: 100%; background: none; border: 0; cursor: pointer;
	display: flex; flex-direction: column; align-items: center; gap: 4px;
	padding: 6px 2px 12px; margin-bottom: -1px;
	font: 600 13px var(--body); color: var(--muted);
	border-bottom: 3px solid transparent;
}
.bsteps__btn span {
	width: 26px; height: 26px; border-radius: 50%;
	display: grid; place-items: center;
	border: 1.5px solid var(--line); background: #fff;
	font: 700 13px var(--display); color: var(--muted);
}
.bsteps__btn.is-current { color: var(--text); border-bottom-color: var(--orange); }
.bsteps__btn.is-current span { background: var(--ink); border-color: var(--ink); color: #fff; }
.bsteps__btn.is-done span { background: var(--orange); border-color: var(--orange); color: #fff; }
.bsteps__btn:disabled { cursor: default; opacity: .55; }
.bstep__title { font: 800 var(--t-xl)/1.2 var(--display); margin: 0 0 var(--s-3); }

/* Without the script every step shows, one after another. */
.bookform--steps:not(.is-stepped) .bsteps,
.bookform--steps:not(.is-stepped) .bstepbar { display: none; }
.bookform--steps.is-stepped .bstep:not(.is-active) { display: none; }

.bstepbar {
	position: sticky; bottom: 0; z-index: 3;
	display: flex; align-items: center; justify-content: space-between; gap: 12px;
	margin: var(--s-4) calc(-1 * var(--s-4)) calc(-1 * var(--s-4));
	padding: 12px var(--s-4);
	background: #fff; border-top: 1px solid var(--line);
	border-radius: 0 0 var(--radius) var(--radius);
}
.minimeter { display: flex; flex-direction: column; min-width: 0; }
.minimeter__label { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 160px; }
.minimeter .meter__value { font: 800 22px var(--display); color: var(--text); font-variant-numeric: tabular-nums; }
.bstepbar__btns { display: flex; gap: 8px; flex-shrink: 0; }
.bstepbar .btn { min-height: 46px; padding-inline: 20px; }
.bookform--steps.is-last-step .bstepbar .js-bstep-next { display: none; }
@media (max-width: 900px) {
	.bstepbar { bottom: 64px; } /* sits above the phone action bar */
}

.bsummary {
	display: grid; grid-template-columns: auto 1fr; gap: 8px 16px;
	margin: 0 0 var(--s-4); padding: var(--s-3) var(--s-4);
	background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius-sm);
	font-size: var(--t-sm);
}
.bsummary:empty { display: none; }
.bsummary dt { color: var(--muted); font-weight: 600; }
.bsummary dd { margin: 0; color: var(--text); overflow-wrap: anywhere; }

/* Hero reviews: real Google reviews scrolling under the map. */
.reviewmarquee--hero { margin: 0; max-width: 560px; border-radius: 14px;
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
	mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.reviewmarquee--hero .reviewcard { flex: 0 0 260px; padding: 14px 16px; }
.reviewcard--hero p { font-size: .85rem; line-height: 1.45; margin: 0 0 8px; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
.reviewcard--hero b { font-size: .8rem; display: inline-flex; gap: 6px; align-items: center; }
@media (max-width: 900px) { .hero__copy > .reviewmarquee--hero { order: 7; } }

/* ---------- Services: image cards (homepage) ----------
   Light, so it reads as a clear break after the dark hero. */
.svctiles { background: var(--paper); }
.svctiles h2 { max-width: 20ch; }
.svctiles__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.svctile {
	display: flex; flex-direction: column; gap: 6px;
	padding: 10px 10px 18px; border-radius: 18px; text-decoration: none; color: var(--text);
	background: #fff; border: 1px solid var(--line);
	box-shadow: 0 6px 20px rgba(11,11,13,.05);
	transition: border-color .15s var(--ease), box-shadow .15s var(--ease), transform .15s var(--ease);
}
.svctile:hover, .svctile:focus-visible { border-color: var(--orange); box-shadow: 0 14px 30px rgba(11,11,13,.1); transform: translateY(-2px); }
.svctile__media { position: relative; display: block; margin-bottom: 24px; }
.svctile__media img { display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: cover; border-radius: 12px; }
.svctile__icon {
	position: absolute; left: 12px; bottom: -20px;
	width: 44px; height: 44px; border-radius: 50%;
	background: var(--orange); color: #fff; display: grid; place-items: center;
	border: 3px solid #fff;
}
.svctile__icon svg { width: 20px; height: 20px; }
.svctile__title, .svctile__copy, .svctile__more { padding-inline: 8px; }
.svctile__title { font: 700 17px/1.25 var(--display); }
.svctile__copy { color: var(--muted); font-size: 14px; line-height: 1.45; flex: 1; }
.svctile__more { color: var(--orange-text); font-size: 13px; font-weight: 600; margin-top: 8px; }
@media (max-width: 1000px) { .svctiles__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 420px) { .svctiles__grid { gap: 12px; } .svctile__copy { font-size: 13px; } }

/* ---------- Fares board (homepage) ---------- */
.fareboard-sec { background: #fff; border-top: 1px solid var(--line); }
.fareboard { background: #111114; border-radius: 18px; padding: 8px 24px 16px; color: #e8e8e8; box-shadow: 0 30px 60px rgba(11,11,13,.18); }
.fareboard__top { display: flex; justify-content: space-between; align-items: center; padding: 14px 0 8px; font: 700 14px var(--display); color: #fff; letter-spacing: .04em; text-transform: uppercase; }
.fareboard__top i { font-style: normal; color: #3DDC97; font: 600 13px var(--body); text-transform: none; letter-spacing: 0; }
.fareboard__row { display: grid; grid-template-columns: 1.2fr 1.4fr .7fr .8fr 1fr; gap: 12px; padding: 14px 0; border-bottom: 1px solid #26262c; align-items: center; color: #e8e8e8; text-decoration: none; }
a.fareboard__row:hover .fareboard__from { color: var(--orange); }
.fareboard__row--head { color: var(--orange); font: 700 12px var(--body); letter-spacing: .14em; text-transform: uppercase; }
.fareboard__row--head span:last-child { text-align: right; }
.fareboard__from { font-weight: 600; font-size: 16px; transition: color .15s var(--ease); }
.fareboard__fare { color: var(--orange); font: 800 20px var(--display); font-variant-numeric: tabular-nums; text-align: right; }
.fareboard__note { color: var(--muted); margin-top: 22px; max-width: 78ch; line-height: 1.6; }
@media (max-width: 760px) {
	.fareboard { padding: 4px 16px 10px; }
	.fareboard__row--head { display: none; }
	.fareboard__row { grid-template-columns: 1fr auto; gap: 2px 10px; }
	.fareboard__to { grid-row: 2; color: #9c9ca6; font-size: 14px; }
	.fareboard__km, .fareboard__min { display: none; }
	.fareboard__fare { grid-row: 1 / 3; grid-column: 2; font-size: 18px; }
}

/* ---------- How it works + coverage (homepage) ---------- */
.howbook { background: var(--paper); }
.howbook__steps { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; position: relative; }
.howbook__steps::before { content: ""; position: absolute; top: 30px; left: 16%; right: 16%; border-top: 3px dashed #F5B97A; }
.howbook__step { text-align: center; position: relative; }
.howbook__n { width: 60px; height: 60px; border-radius: 50%; background: var(--orange); color: #fff; display: grid; place-items: center; font: 800 24px var(--display); margin: 0 auto 16px; box-shadow: 0 0 0 8px var(--paper); }
.howbook__step b { font: 700 19px var(--display); display: block; margin-bottom: 6px; }
.howbook__step p { color: var(--muted); line-height: 1.5; max-width: 30ch; margin: 0 auto; }
.howbook__alt { text-align: center; margin-top: 26px; color: var(--muted); }
.howbook__alt a { color: var(--text); font-weight: 700; }
.howbook__cov { margin-top: 56px; background: #fff; border: 1px solid var(--line); border-radius: 22px; padding: 30px; display: grid; grid-template-columns: 1fr 1.4fr; gap: 30px; align-items: center; }
.howbook__cov h3 { font: 800 26px var(--display); margin: 0 0 8px; }
.howbook__cov p { color: var(--muted); line-height: 1.6; margin-bottom: 16px; }
.howbook__chips { display: flex; flex-wrap: wrap; gap: 8px; }
.howbook__chips a, .howbook__chips span { background: var(--paper); border: 1px solid var(--line); border-radius: 999px; padding: 8px 14px; font-size: 14px; font-weight: 500; color: var(--text); text-decoration: none; }
.howbook__chips a:hover { border-color: var(--orange); color: var(--orange-text); }
@media (max-width: 860px) {
	.howbook__steps { grid-template-columns: 1fr; }
	.howbook__steps::before { display: none; }
	.howbook__cov { grid-template-columns: 1fr; padding: 22px; }
}

/* ---------- Fixed price or meter (confirm step) ---------- */
.faretype { border: 0; padding: 0; margin: 0 0 var(--s-4); display: grid; gap: 10px; }
.faretype legend { font-weight: 700; margin-bottom: 8px; }
.faretype__opt { display: block; cursor: pointer; }
.faretype__opt input { position: absolute; opacity: 0; pointer-events: none; }
.faretype__box { display: block; border: 1.5px solid var(--line); border-radius: 14px; padding: 14px 16px; transition: border-color .15s var(--ease), background-color .15s var(--ease); }
.faretype__opt input:checked + .faretype__box { border-color: var(--orange); background: #FFF6EC; }
.faretype__opt input:focus-visible + .faretype__box { box-shadow: 0 0 0 3px rgba(252,125,3,.3); }
.faretype__head { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
.faretype__head b { font-family: var(--display); font-size: 17px; }
.faretype__price { color: var(--orange-text); font-variant-numeric: tabular-nums; }
.faretype small { display: block; color: var(--muted); font-size: 13px; line-height: 1.45; margin-top: 4px; }

.paynotice { border: 1.5px solid var(--line); border-radius: 16px; padding: 20px 22px; background: #fff; }
.paynotice--ok { border-color: rgba(18,161,95,.4); background: rgba(18,161,95,.06); }
.paynotice h2 { font-size: var(--t-xl); margin: 0 0 6px; }
.paynotice p { margin: 0; color: var(--muted); }
