/**
 * Spots shipper app — booking flow layer
 * css/booking-shared.css
 *
 * Loads AFTER css/stylesheet.css and consumes the tokens declared in
 * css/variables-blue-red.css. Per css/design_handoff_shipper_portal
 * ("Booking Shell.dc.html") — the shared shell behind the whole booking flow:
 * startbook.php → productsp.php → showcart.php → checkout-pay.php →
 * checkout-complete.php.
 *
 * productsp owns steps 1 and 2 only; steps 3 and 4 are rendered into
 * #cargoCatSelect by ltl.php / ltl_lcl.php, so the carrier-price family the
 * handoff uses there (.book-rate-*) lands when those two fragments are
 * migrated — this file carries no rules nothing renders.
 *
 * checkout-pay.php and the three checkout-complete* pages share one order
 * summary: action/checkout_fetch.php echoes .book-item cards (section 6) into
 * both, so the same delivery reads identically either side of payment.
 *
 * Relationship to .info-section
 * -----------------------------
 * The booking pages keep the shared .info-section wrapper (css/stylesheet.css
 * section 8) untouched — 13 pages depend on its float/width/padding. The app
 * screens add the .info-section--app modifier for the tighter rhythm and the
 * cool surface. Modifier only: nothing here re-declares a base .info-section
 * rule.
 *
 * Scoping rule
 * ------------
 * Global: the page-shell classes only —
 *     .info-section--app  .book-flow  .book-lede  .book-layout  .book-main
 *     .book-panel  .book-rail  .book-add
 * Scoped under .book-panel: the cart item family and everything else.
 * Scoped under .rail-card / .book-rail: the rail internals.
 * Scoped under .upload-modal / .terms-modal: showcart.php's two dialogs
 * (upload_modal.php, the #termsModal include). .book-note is the one family
 * that crosses both worlds — section 4's selectors read
 * :is(.book-panel, .modal-body) .book-note so a note looks identical whether
 * it sits in a panel or a dialog, without a second copy of the rules.
 * html/building_type.html and html/job_services.html are shared with
 * productsp_ftl / _int / _lcl / _sg and instantquote*, which still render the
 * legacy layout, so the hooks added to those two files must stay inert until a
 * page opts in by wrapping them in a .book-panel. functions.php session() is
 * shared with those same pages plus checkout-pay.php and products.php, so its
 * hooks are scoped to .book-rail, which none of them render.
 *
 * Contents:
 *   1. Page shell       (.info-section--app, .book-flow, .book-layout)
 *   2. Panel            (.book-panel)
 *   3. Fields           (.book-fieldset, .book-field, .book-grid)
 *   4. Notes            (.book-note)
 *   5. Actions          (.book-actions)
 *   6. Cart items       (.book-item, .book-price, .book-add, .book-empty)
 *   7. Summary rail     (.book-rail, .rail-card, .book-rail-card)
 *   8. Shared includes  (building type, service charge)
 *   9. Cargo items      (.book-cargo-list, .book-prod, .book-wait)
 *  10. Carrier price    (.book-rate)
 *  11. Dialogs          (.upload-modal, .terms-modal)
 *  12. Confirmation     (.book-confirm, .book-todo)
 *  13. Responsive
 */

/* ========== 1. Page shell ========== */

.info-section--app {
	background-color: var(--surface-alt);
	padding: var(--space-10) 0 var(--space-20);
}

.info-section--app h1 {
	font-size: var(--text-4xl);
	color: var(--blue-ink);
	margin: 0 0 var(--space-2);
}

.info-section--app .book-lede {
	font-size: var(--text-lg);
	line-height: var(--lh-snug);
	color: var(--slate);
	margin: 0 0 var(--space-8);
}

/*-- Step rail --
   Spans all three booking screens; the step numbers come from a counter, so
   each page only marks .is-done / .is-current. No icon font — a completed step
   keeps its number on a blue wash rather than depending on a glyph codepoint. */
.book-flow {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-4);
	counter-reset: book-step;
	list-style: none;
	margin: 0 0 var(--space-6);
	padding: 0;
}

.book-flow li {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	counter-increment: book-step;
	font-size: var(--text-sm);
	color: var(--fog);
}

.book-flow li::before {
	content: counter(book-step);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 22px;
	height: 22px;
	border: 1px solid var(--border);
	border-radius: var(--radius-full);
	background: var(--paper-white);
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	color: var(--fog);
}

.book-flow li:not(:last-child)::after {
	content: "";
	width: 24px;
	height: 1px;
	margin-left: var(--space-2);
	background: var(--border);
}

.book-flow .is-current {
	color: var(--blue-ink);
	font-weight: var(--fw-semibold);
}

.book-flow .is-current::before {
	border-color: var(--spots-blue);
	background: var(--spots-blue);
	color: var(--paper-white);
}

.book-flow .is-done {
	color: var(--slate);
}

.book-flow .is-done::before {
	border-color: var(--blue-200);
	background: var(--blue-50);
	color: var(--spots-blue);
}

/*-- Layout --
   One column by default. --rail-end puts the 320px summary rail after the main
   column (showcart); --rail-start puts productsp's 300px pickup rail before it,
   --rail-wide checkout-pay's 380px payment rail. All collapse to one column
   below the lg breakpoint, in DOM order. */
.book-layout {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: var(--space-6);
	align-items: start;
}

.book-layout--single {
	max-width: 940px;
}

.book-layout--rail-end {
	grid-template-columns: minmax(0, 1fr) 320px;
}

/* Rail-first in the DOM as well as the grid: when the columns collapse, the
   pickup address it summarises should still come before the delivery form
   that answers it. */
.book-layout--rail-start {
	grid-template-columns: 300px minmax(0, 1fr);
}

/* checkout-pay.php. Same rail-first arrangement as --rail-start, wider: that
   rail is the payment method picker rather than a passive summary, and three
   rows carrying a title, a description and a chevron read cramped at 300px. */
.book-layout--rail-wide {
	grid-template-columns: 380px minmax(0, 1fr);
}

/* Also the counter root for the numbered panel badges — see .book-step below */
.book-main {
	min-width: 0;
	counter-reset: book-panel-step;
}

/* The rail is shorter than the cart it summarises — pin it under the fixed
   navbar (body carries padding-top: 80px) so the total and the CTA stay in
   view while a long list scrolls. Released at lg in section 9. */
.book-rail {
	display: grid;
	gap: var(--space-4);
	align-content: start;
	min-width: 0;
	position: sticky;
	top: 96px;
}


/* ========== 2. Panel ========== */

.book-panel {
	background: var(--paper-white);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-sm);
}

.book-panel + .book-panel {
	margin-top: var(--space-6);
}

.book-panel-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	padding: var(--space-4) var(--space-6);
	background: var(--blue-50);
	border-bottom: 1px solid var(--color-border);
	border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

/* Equal specificity to `.info-section h2`, and this file loads later — the
   panel heading is a component label, not the page's 4xl section heading. */
.book-panel-head h2 {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	margin: 0;
	font-size: var(--text-xl);
	font-weight: var(--fw-semibold);
	color: var(--spots-blue);
}

.book-panel-head h2 i {
	font-size: var(--text-base);
	color: var(--spots-blue);
}

/* A second head inside one panel (checkout-pay stacks billing over pickup):
   it divides rather than caps, so it loses the rounded top corners the first
   head draws and takes its rule on the leading edge instead. */
.book-panel-head--stacked {
	border-top: 1px solid var(--color-border);
	border-bottom: 0;
	border-radius: 0;
}

/*-- Collapsible panel --
   productsp keeps the legacy Bootstrap collapse, so the head is the trigger and
   .book-panel-body sits inside the .collapse. The body is still the divider
   rule's parent, so the fieldsets separate exactly as they do on a flat panel. */
.book-panel-head[data-toggle="collapse"] {
	cursor: pointer;
}

.book-panel-head[data-toggle="collapse"]:hover h2,
.book-panel-head[data-toggle="collapse"]:hover .book-panel-caret {
	color: var(--blue-500);
}

/* Nothing renders under a closed head, so it must not draw a rule beneath
   itself — and it carries the panel's full radius rather than just the top */
.book-panel-head[aria-expanded="false"] {
	border-bottom-color: transparent;
	border-radius: var(--radius-lg);
}

.book-panel-caret {
	flex-shrink: 0;
	font-size: 12px;
	color: var(--spots-blue);
	transition: transform var(--transition-fast);
}

/* Bootstrap flips aria-expanded on the trigger as the region opens */
.book-panel-head[aria-expanded="true"] .book-panel-caret {
	transform: rotate(180deg);
}

/*-- Numbered step badge --
   The number is positional rather than authored. ltl.php renders the same two
   panels as steps 3 and 4 on productsp (which has a service-charge step) and as
   2 and 3 on the cross-border and instant-quote pages, so a shared fragment
   cannot know its own number — the counter derives it from DOM order, and the
   span is left empty in the markup.

   Counter root is .book-main. Named book-panel-step, not book-step, because
   .book-flow (section 1) already runs a counter of that name for the page rail.
   A display:none element generates no box and so does not increment, which is
   exactly right while #cargoCatSelect is still hidden. */
.book-step {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	counter-increment: book-panel-step;
	border-radius: var(--radius-full);
	background: var(--spots-blue);
	font-size: var(--text-sm);
	color: var(--paper-white);
}

.book-step::before {
	content: counter(book-panel-step);
}

.book-panel-body {
	padding: var(--space-6);
}

/* Blocks inside a panel body are divided the way the legacy form used <hr>.
   Opt out with .book-flush. A hidden block (#job_services before a building
   type is picked) is display:none, so it draws no rule.

   The rule is a pseudo-element sitting in the margin, not a border or an inset
   shadow on the block itself, because two blocks in this layer break both: a
   <fieldset> lets its <legend> cut the top edge, notching a border AND an inset
   shadow around the label and dropping the block-start padding under the legend
   rather than above it; and .book-note carries its own border and wash, which
   an inset shadow lands inside of, invisibly. A line in the margin has neither
   problem, so every block divides the same way whatever box it draws for
   itself.

   It closes each block rather than opening the next one for the same reason: a
   rendered <legend> also shifts the fieldset's positioning origin below itself,
   so a rule anchored to the top edge lands back on the label. The bottom edge
   carries neither the notch nor the shift.

   :has() rather than :not(:last-child) because the last child is often not the
   last rendered block — pickup.php and html/job_services.html both close with a
   <script>, which would otherwise leave a trailing rule under the block above
   it. "Has a later sibling that renders" is the actual condition. */
.book-panel-body > *:has(~ *:not(script):not(style)):not(.book-flush),
.book-panel-body > form > *:has(~ *:not(script):not(style)):not(.book-flush) {
	position: relative;
	margin-bottom: var(--space-12);
}

.book-panel-body > *:has(~ *:not(script):not(style)):not(.book-flush)::after,
.book-panel-body > form > *:has(~ *:not(script):not(style)):not(.book-flush)::after {
	content: "";
	position: absolute;
	right: 0;
	bottom: calc(-1 * var(--space-6));
	left: 0;
	height: 1px;
	background: var(--color-border);
}

/* ========== 3. Fields ========== */

.book-panel .book-fieldset {
	min-width: 0;
	margin: 0;
	padding: 0;
	border: 0;
}

/* Reboot floats legends and forces them full width — undo both */
.book-panel .book-fieldset > legend {
	float: none;
	width: auto;
	margin-bottom: var(--space-4);
	padding: 0;
	font-family: var(--font-heading);
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--slate);
}

.book-panel .book-field {
	min-width: 0;
}

.book-panel .book-field > label {
	display: block;
	margin-bottom: 6px;
	font-size: var(--text-sm);
	font-weight: var(--fw-medium);
	color: var(--blue-ink);
}

.book-panel .book-hint {
	display: block;
	margin-top: 6px;
	font-size: 12px;
	color: var(--slate);
}

/* Validation messages are written into these spans by js/validate.js */
.book-panel .book-field > .text-danger {
	display: block;
	margin-top: 6px;
	font-size: 12px;
}

.book-panel .book-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	gap: var(--space-4);
}

.book-panel .book-grid--halves {
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	gap: var(--space-6);
}

/*-- Compact grid --
   The dimension row on a cargo item: many small numeric fields on one line,
   with uppercase micro-labels so the row reads as a single unit rather than a
   stack of separate questions. */
.book-panel .book-grid--compact {
	grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
	gap: var(--space-3);
}

.book-panel .book-grid--compact .book-field > label {
	margin-bottom: 4px;
	font-family: var(--font-heading);
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--slate);
}

.book-panel .book-grid--compact .form-control,
.book-panel .book-grid--compact .custom-select {
	padding: 8px 10px;
}

/* Validation and hints that belong to the whole row, not one field */
.book-panel .book-grid-note {
	margin: 6px 0 0;
	font-size: 12px;
}

.book-panel .book-grid-note > span + span {
	margin-left: var(--space-3);
}

/*-- Derived field --
   Total volume / total weight: computed by volWeight(), never typed into. It
   holds a field slot in the grid so the row stays aligned, but reads as an
   answer rather than an input. */
.book-panel .book-field-derived > .book-derived-value {
	display: block;
	margin: 0;
	padding: 8px 0;
	font-family: var(--font-heading);
	font-size: var(--text-sm);
	font-weight: var(--fw-semibold);
	line-height: var(--lh-snug);
	color: var(--spots-blue);
}

/* Inputs run one size down inside a panel — a booking form is dense */
.book-panel .form-control,
.book-panel .custom-select {
	height: auto;
	padding: 10px 12px;
	font-size: var(--text-sm);
}

.book-panel textarea.form-control {
	min-height: 88px;
}

.book-panel .input-group-text {
	padding: 10px 12px;
	font-size: var(--text-sm);
	color: var(--slate);
	background-color: var(--surface-alt);
	border-color: var(--color-border);
}

/* select2 (#add, #postcode) resolves its width from .form-control — pin it so
   a long postcode label can't push the grid column open */
.book-panel .select2-container {
	width: 100% !important;
}


/* ========== 4. Notes ========== */
/* :is(.book-panel, .modal-body) — a note reads the same in a cart panel and
   in the two dialogs of section 11, so both scope roots share one ruleset. */

:is(.book-panel, .modal-body) .book-note {
	display: flex;
	gap: var(--space-3);
	padding: var(--space-4);
	border: 1px solid var(--color-border);
	border-left: 3px solid var(--spots-blue);
	border-radius: var(--radius-md);
	background: var(--blue-50);
}

:is(.book-panel, .modal-body) .book-note > i {
	flex-shrink: 0;
	margin-top: 2px;
	font-size: 15px;
	color: var(--spots-blue);
}

:is(.book-panel, .modal-body) .book-note p {
	margin: 0;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--blue-ink);
}

:is(.book-panel, .modal-body) .book-note p + p {
	margin-top: 6px;
}

:is(.book-panel, .modal-body) .book-note-title {
	font-weight: var(--fw-semibold);
}

/* action/complete_fetch.php returns the uploaded payment slips as a bare <ul>,
   and printed instructions arrive as one too */
:is(.book-panel, .modal-body) .book-note ul {
	margin: 6px 0 0;
	padding-left: 1.1rem;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--blue-ink);
}

:is(.book-panel, .modal-body) .book-note li + li {
	margin-top: 2px;
}

:is(.book-panel, .modal-body) .book-note--attention {
	border-left-color: var(--spots-red);
	background: var(--red-50);
}

:is(.book-panel, .modal-body) .book-note--attention > i {
	color: var(--spots-red);
}

/* The warning copy in html/building_type.html carries .text-danger for the
   legacy pages; inside the wash it reads as body text, not an error */
:is(.book-panel, .modal-body) .book-note--attention p.text-danger {
	color: var(--blue-ink) !important;
}

/* Buttons that answer the note — "Upload invoice" on the customs warning */
:is(.book-panel, .modal-body) .book-note-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin-top: var(--space-3);
}


/* ========== 5. Actions ========== */

.book-panel .book-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-3);
}

.book-panel .book-actions-end {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: flex-end;
	gap: var(--space-3);
	margin-left: auto;
}

.book-panel .book-actions .btn-link {
	padding-left: 0;
	padding-right: 0;
}

/* The row that closes a cart item, tighter than the row that closes a panel */
.book-panel .book-actions--tight {
	margin-top: var(--space-4);
	padding-top: var(--space-3);
	box-shadow: inset 0 1px 0 var(--color-border);
}


/* ========== 6. Cart items ========== */
/* The delivery cards on showcart.php. action/showcart_fetch.php echoes these
   into #showCart, which IS the .book-panel-body — so each .book-item is a
   direct child of the body and the section 2 divider rule separates them. The
   detail region is filled lazily by action/showcart_gen.php on first expand. */

/*-- Item header --
   A Bootstrap collapse trigger: an <a class="open" data-toggle="collapse">
   whose id is the lorder id. Anchor styling has to be neutralised, and the
   whole row stays clickable, so nothing interactive may be nested inside it. */
.book-panel .book-item-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--space-4);
	color: inherit;
}

.book-panel a.book-item-head:hover,
.book-panel a.book-item-head:focus {
	color: inherit;
	text-decoration: none;
}

.book-panel a.book-item-head:hover .book-item-title {
	color: var(--spots-blue);
}

.book-panel .book-item-media {
	display: flex;
	gap: var(--space-3);
	min-width: 0;
}

.book-panel .book-item-thumb {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 40px;
	height: 40px;
	border-radius: var(--radius-md);
	background: var(--blue-50);
	font-size: 16px;
	color: var(--spots-blue);
}

.book-panel .book-item-index {
	margin: 0 0 2px;
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--fog);
}

/* Two classes, so this beats `.info-section h3` (one class, one element) —
   an item title is a card label, not a section heading */
.book-panel .book-item-title {
	margin: 0;
	font-size: var(--text-base);
	font-weight: var(--fw-semibold);
	line-height: var(--lh-snug);
	color: var(--blue-ink);
	transition: color var(--transition-fast);
}

.book-panel .book-item-desc {
	margin: 4px 0 0;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

/*-- Item price / carrier --
   .book-price-meta wraps the transpref() helper's output verbatim: an
   .img-thumbnail logo pinned to 50px inline, then a .small trading name. */
.book-panel .book-item-aside {
	flex-shrink: 0;
	text-align: right;
}

.book-panel .book-price {
	font-family: var(--font-heading);
	font-size: var(--text-xl);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	color: var(--blue-ink);
}

.book-panel .book-price-meta {
	margin-bottom: var(--space-2);
	font-size: 12px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

/* transpref() wraps the trading name in .small — 80% of an already-12px
   parent is unreadable, so it holds the meta size instead */
.book-panel .book-price-meta .small {
	font-size: 100%;
}

.book-panel .book-price-meta .img-thumbnail {
	padding: 2px;
	border-color: var(--color-border);
	border-radius: var(--radius-sm);
	background-color: var(--paper-white);
}

.book-panel .book-item-toggle {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin-top: var(--space-2);
	font-size: 12px;
	font-weight: var(--fw-semibold);
	color: var(--spots-blue);
}

.book-panel .book-item-toggle i {
	font-size: 10px;
	transition: transform var(--transition-fast);
}

/* Bootstrap flips aria-expanded on the trigger as the region opens */
.book-panel .book-item-head[aria-expanded="true"] .book-item-toggle i {
	transform: rotate(180deg);
}

/*-- Item detail --
   Addresses, cargo and the per-delivery actions. Three columns on desktop,
   stacked below lg where the rail has already taken the width. */
.book-panel .book-item-detail {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, 200px);
	gap: var(--space-5);
	margin-top: var(--space-4);
	padding-top: var(--space-4);
	box-shadow: inset 0 1px 0 var(--color-border);
}

.book-panel .book-detail-block + .book-detail-block {
	margin-top: var(--space-4);
}

.book-panel .book-detail-label {
	margin: 0 0 var(--space-2);
	font-family: var(--font-heading);
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--slate);
}

/* Excluding the label is required, not tidiness: `.book-detail-block p` carries
   one more element than `.book-detail-label`, so it would win against it */
.book-panel .book-detail-block p:not(.book-detail-label),
.book-panel .book-detail-value {
	display: block;
	margin: 0;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--blue-ink);
}

/* The cargo lines and charge list arrive as a .table-striped and a <ul> from
   showcart_gen.php — flatten both onto the card surface */
.book-panel .book-item-detail .table {
	margin-bottom: 0;
	font-size: 13px;
	color: var(--blue-ink);
}

.book-panel .book-item-detail .table td {
	padding: 8px 0;
	border-top-color: var(--color-border);
	vertical-align: top;
}

.book-panel .book-item-detail .table td:last-child {
	text-align: right;
	white-space: nowrap;
}

.book-panel .book-item-detail .table-striped tbody tr:nth-of-type(odd) {
	background-color: transparent;
}

.book-panel .book-item-detail ul {
	margin: var(--space-2) 0 0;
	padding-left: 1.1rem;
	font-size: 12px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

/* Stacked full-width buttons in the actions column */
.book-panel .book-item-detail .btn-block + .btn-block {
	margin-top: var(--space-2);
}

/*-- Facts grid --
   Label-over-value pairs: the pickup address panel, and any summary row that
   wants the same rhythm. */
.book-panel .book-item-facts {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
	gap: var(--space-3) var(--space-4);
	margin: 0;
}

.book-panel .book-item-facts dt {
	margin-bottom: 2px;
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--slate);
}

.book-panel .book-item-facts dd {
	margin: 0;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--blue-ink);
}

/*-- Tracking number --
   Leads a card on the confirmation pages, where the tracking number IS the
   identity of the delivery rather than the company it is going to. Tabular
   figures so a column of them lines up digit for digit. */
.book-panel .book-track {
	margin: 0;
	font-family: var(--font-heading);
	font-size: var(--text-lg);
	font-weight: var(--fw-bold);
	font-variant-numeric: tabular-nums;
	line-height: var(--lh-snug);
	letter-spacing: 0.01em;
	color: var(--spots-blue);
}

/*-- Datalist --
   Label-beside-value rows: cargo, charges and lead time on an order-summary
   card. The sibling of .book-item-facts above — facts stacks label OVER value
   in a responsive grid for short strings like a phone number, this one keeps
   them side by side because the values are sentences of wildly uneven length
   and a grid of them would leave ragged holes.

   Flat dt/dd pairs rather than a wrapping <div> per pair, so the two columns
   are declared once here and every row aligns to the same axis regardless of
   how many lines its value runs to. */
.book-panel .book-datalist {
	display: grid;
	grid-template-columns: minmax(120px, max-content) minmax(0, 1fr);
	gap: var(--space-2) var(--space-4);
	margin: 0;
}

.book-panel .book-datalist dt {
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	line-height: var(--lh-normal);
	color: var(--slate);
	/* The label is uppercase micro-type and the value is 13px sentence text —
	   without this nudge the cap-height of the label rides above the value's
	   x-height and the row reads as two unrelated lines */
	padding-top: 1px;
}

.book-panel .book-datalist dd {
	margin: 0;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--blue-ink);
}

/* Dimensions under a cargo description, the charge_calc lines under a charge:
   the same row, one step quieter */
.book-panel .book-datalist .book-datalist-sub {
	display: block;
	color: var(--slate);
}

/* The charge list arrives from lorder_charges as a <ul> — flatten it onto the
   row rather than indenting a bulleted list inside a value */
.book-panel .book-datalist dd > ul {
	margin: 0;
	padding: 0;
	list-style: none;
}

/*-- Empty cart --
   action/showcart_fetch.php renders this into the same panel body when the
   order has no deliveries yet. */
.book-panel .book-empty {
	padding: var(--space-6) 0;
	text-align: center;
}

.book-panel .book-empty i {
	font-size: 28px;
	color: var(--blue-200);
}

.book-panel .book-empty p {
	margin: var(--space-3) 0 0;
	font-size: var(--text-sm);
	color: var(--slate);
}

/*-- Add another delivery --
   Sits outside the panels, closing the main column. Global by necessity. */
.book-add {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	margin-top: var(--space-6);
	padding: var(--space-4);
	border: 1px dashed var(--border);
	border-radius: var(--radius-lg);
	background: var(--paper-white);
	font-size: var(--text-sm);
	font-weight: var(--fw-semibold);
	color: var(--spots-blue);
	transition: var(--transition-fast);
}

.book-add:hover,
.book-add:focus {
	border-color: var(--spots-blue);
	background: var(--blue-50);
	color: var(--spots-blue);
	text-decoration: none;
}


/* ========== 7. Summary rail ========== */
/* .book-rail (section 1) holds these cards: order summary + checkout CTA,
   promo code, and the help card. */

.rail-card {
	padding: var(--space-5);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	background: var(--paper-white);
	box-shadow: var(--shadow-sm);
}

/* Equal specificity to `.info-section p`, and this file loads later */
.rail-card p {
	margin: 0;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

.rail-card p + p {
	margin-top: 6px;
}

.rail-card .rail-label {
	margin: 0 0 var(--space-3);
	font-family: var(--font-heading);
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--slate);
}

.rail-card .rail-line {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-3);
	padding: 5px 0;
	font-size: var(--text-sm);
	color: var(--blue-ink);
}

/* A promotion reads as money coming off, so it carries the accent */
.rail-card .rail-line--discount {
	color: var(--spots-red);
}

.rail-card .rail-line--muted {
	color: var(--fog);
}

.rail-card .rail-total {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-3);
	margin: var(--space-3) 0 0;
	padding-top: var(--space-3);
	box-shadow: inset 0 1px 0 var(--color-border);
}

.rail-card .rail-total dt {
	font-size: var(--text-sm);
	font-weight: var(--fw-semibold);
	color: var(--blue-ink);
}

.rail-card .rail-total dd {
	margin: 0;
	font-family: var(--font-heading);
	font-size: var(--text-2xl);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	color: var(--spots-blue);
}

.rail-card .rail-fine {
	font-size: 12px;
	color: var(--fog);
}

/* The promo lookup writes the campaign name here, or its error string */
.rail-card .rail-promo-name {
	margin-top: var(--space-3);
	font-size: var(--text-sm);
	font-weight: var(--fw-semibold);
	color: var(--blue-ink);
}

.rail-card .rail-promo-name:empty,
.rail-card .rail-fine:empty {
	display: none;
}

.rail-card .form-control {
	height: auto;
	padding: 10px 12px;
	font-size: var(--text-sm);
}

.rail-card .btn-block + .btn-block,
.rail-card .btn-block + p {
	margin-top: var(--space-2);
}

/*-- Method picker (checkout-pay.php) --
   Three neutral choices, not CTAs — the page's single red button lives in the
   footer of the dialog they open. Each row is a <button> so it is keyboard
   reachable and announces itself as an action rather than a link to nowhere.
   Disabled until action/checkout_fetch.php has returned #finalCost, since
   every method needs the amount before it can hand off. */
.rail-card .book-choice-list {
	display: grid;
	gap: var(--space-2);
}

.rail-card .book-choice {
	display: block;
	width: 100%;
	padding: var(--space-3) var(--space-4);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
	background: var(--paper-white);
	cursor: pointer;
	transition: border-color var(--transition-fast),
	            background-color var(--transition-fast);
}

.rail-card .book-choice:hover,
.rail-card .book-choice:focus {
	border-color: var(--spots-blue);
	background: var(--blue-50);
	outline: none;
}

.rail-card .book-choice[disabled] {
	opacity: 0.55;
	cursor: not-allowed;
}

.rail-card .book-choice[disabled]:hover,
.rail-card .book-choice[disabled]:focus {
	border-color: var(--color-border);
	background: var(--paper-white);
}

.rail-card .book-choice-main {
	display: flex;
	align-items: center;
	gap: var(--space-3);
}

.rail-card .book-choice-main > i {
	flex-shrink: 0;
	width: 20px;
	font-size: 18px;
	color: var(--spots-blue);
	text-align: center;
}

.rail-card .book-choice-body {
	flex: 1 1 auto;
	min-width: 0;
	text-align: left;
}

/* Both are <p>, so they need to outrank `.rail-card p` above */
.rail-card .book-choice-title {
	margin: 0;
	font-size: var(--text-sm);
	font-weight: var(--fw-semibold);
	color: var(--blue-ink);
}

.rail-card .book-choice-desc {
	margin: 2px 0 0;
	font-size: 12px;
	line-height: var(--lh-snug);
	color: var(--slate);
}

.rail-card .book-choice-aside {
	display: flex;
	flex-shrink: 0;
	align-items: center;
	gap: var(--space-2);
}

.rail-card .book-choice-aside > i {
	font-size: 12px;
	color: var(--fog);
}

/* "Fastest" is a fact about the method, not a promotion — it stays blue so it
   does not compete with the red button inside the dialog */
.rail-card .book-flag {
	padding: 2px 6px;
	border-radius: var(--radius-sm);
	background: var(--blue-50);
	font-size: 10px;
	font-weight: var(--fw-semibold);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--spots-blue);
	white-space: nowrap;
}

/* The same spinner-and-line the panel uses (section 9), sized for the rail:
   the shared .spinner-loader is a 50px page-level spinner and reads as a hero
   beside one line of text */
.rail-card .book-wait {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	margin-bottom: var(--space-3);
}

/* checkOut() dismisses this one with the hidden attribute rather than the
   inline display the ltl.php spinners use, and a display declaration outranks
   [hidden]'s UA rule — without this the spinner never goes away */
.rail-card .book-wait[hidden] {
	display: none;
}

.rail-card .book-wait p {
	margin: 0;
	font-size: 13px;
	font-weight: var(--fw-semibold);
	color: var(--spots-blue);
}

.rail-card .book-wait .spinner-loader {
	flex-shrink: 0;
	width: 20px;
	height: 20px;
	border-width: 3px;
}

/*-- Legacy rail cards (functions.php session()) --
   session() echoes a Bootstrap .card for the pickup summary and one for the
   pickup-charge toggle. It is shared with productsp_ftl / _int / _lcl / _sg,
   checkout-pay.php and products.php, so the hooks in that function are
   additive class names only and everything below is scoped to .book-rail,
   which none of those pages render. The .card keeps its own .card-body, so the
   padding lands there rather than on the card as it does for .rail-card. */
.book-rail .book-rail-card {
	margin: 0 !important;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	background: var(--paper-white);
	box-shadow: var(--shadow-sm);
}

.book-rail .book-rail-card > .card-body {
	padding: var(--space-5);
}

.book-rail .book-rail-card p {
	margin: 0;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

.book-rail .book-rail-card p + p {
	margin-top: 6px;
}

/* The include's headings carry .card-text plus .text-dark / .text-primary,
   the latter a Bootstrap !important colour — hence the !important here */
.book-rail .book-rail-card .book-rail-label {
	margin: 0 0 var(--space-3) !important;
	font-family: var(--font-heading);
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--slate) !important;
}

/* "PICKUP ADDRESS" is wrapped in <b> in the include — the label weight owns it */
.book-rail .book-rail-card .book-rail-label b {
	font-weight: inherit;
}

/* The company name leads the address block, the rest is metadata */
.book-rail .book-rail-card .book-rail-address b {
	display: block;
	margin-bottom: 2px;
	font-size: var(--text-sm);
	font-weight: var(--fw-semibold);
	color: var(--blue-ink);
}

.book-rail .book-rail-card .btn {
	width: 100%;
	margin-top: var(--space-4);
}

/*-- Pickup charges --
   #waiting_charge_pickup is priced by costGen() in ltl.php, so the checkbox
   keeps its name, id and disabled-when-the-cart-is-loaded state. Only the row
   around it is restyled. */
.book-rail .book-rail-card .form-group {
	margin: 0;
}

.book-rail .book-rail-card .checkbox {
	margin: 0 0 var(--space-2) !important;
}

.book-rail .book-rail-card .checkbox label {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	margin: 0;
	font-size: var(--text-sm);
	font-weight: var(--fw-medium);
	color: var(--blue-ink);
	cursor: pointer;
}

.book-rail .book-rail-card .checkbox input[type="checkbox"] {
	flex-shrink: 0;
	margin: 0;
}

/* session() locks the toggle once the cart holds a delivery — the label wraps
   the input, so the whole row reads as locked */
.book-rail .book-rail-card .checkbox label:has(input:disabled) {
	color: var(--fog);
	cursor: not-allowed;
}


/* ========== 8. Shared includes ========== */
/* html/building_type.html and html/job_services.html still render their legacy
   Bootstrap rows on productsp* / instantquote*. Everything below is scoped to
   .book-panel so those pages are untouched. */

/*-- .book-only --
   Markup that exists only for this layer — the note icons and note titles
   added to the shared includes. Hidden everywhere else, so the legacy pages
   render exactly the copy they rendered before. */
.book-only {
	display: none !important;
}

.book-panel .book-only {
	display: block !important;
}

.book-panel i.book-only {
	display: inline-block !important;
}

/*-- Building type (html/building_type.html) --
   Two halves, like the legacy .row it replaces: the selects in one, the "why
   we ask" and "carriers move the cargo" notes in the other. The include puts
   the copy first in the DOM, so `order` flips them — fields lead on desktop,
   and when the halves collapse below ~580px the fields still come first. */
.book-panel .book-build {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	gap: var(--space-5);
	align-items: start;
	margin: 0;
}

.book-panel .book-build-fields,
.book-panel .book-build-copy {
	flex: none;
	width: 100%;
	max-width: none;
	padding: 0;
}

.book-panel .book-build-fields {
	order: 1;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: var(--space-4);
	align-content: start;
}

/* The two notes stay stacked inside their own half; the grid gap above
   separates the halves, so no margin of its own */
.book-panel .book-build-copy {
	order: 2;
	display: grid;
	gap: var(--space-3);
	align-content: start;
}

/* The include's h6 sits inside the copy half, so it would label the notes
   rather than the block. The wrapping .book-fieldset in pickup.php supplies
   the <legend> instead — the h6 is suppressed here, untouched elsewhere. */
.book-panel .book-build-title {
	display: none !important;
}

/* The selects reveal themselves through fadeIn(), which writes an inline
   display — margin only, no layout claim */
.book-panel .book-build-fields > select {
	margin: 0 !important;
}

/* The reminder spans and #building_catErr sit under the full width of the
   select grid, not in a column of their own */
.book-panel .book-build-fields > span {
	grid-column: 1 / -1;
	font-size: 12px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

.book-panel .book-build-fields > span:empty {
	display: none;
}

/*-- Service charge (html/job_services.html) --
   Legacy: a bordered card with a 5x clock over centred radios. Here: a flat
   two-column block, question left, explanation as a note right. */
/* #job_services is a .row — drop the gutter negative margins so it lines up
   with the fieldsets above it, and take it off flex. It is revealed by
   fadeIn(), which clears the inline display:none rather than writing one, so
   the element falls back to the stylesheet and .row's display:flex would
   otherwise apply, leaving .book-svc-card sized as a flex item. */
.book-panel .book-svc {
	display: block;
	margin-left: 0;
	margin-right: 0;
}

.book-panel .book-svc-card {
	margin: 0 !important;
	border: 0;
	border-radius: 0;
	background: none;
}

.book-panel .book-svc-card > .card-body {
	padding: 0;
}

.book-panel .book-svc-row {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
	gap: var(--space-5);
	margin: 0;
}

.book-panel .book-svc-ask,
.book-panel .book-svc-copy {
	flex: none;
	width: 100%;
	max-width: none;
	padding: 0;
}

/* The 5x clock belongs to the legacy card; the panel head carries the icon */
.book-panel .book-svc-icon {
	display: none;
}

.book-panel .book-svc-ask .form-group {
	margin: 0;
	text-align: left !important;
}

.book-panel .book-svc-ask h6 {
	margin: 0 0 var(--space-3) !important;
	text-transform: none;
	letter-spacing: var(--ls-normal);
	font-size: var(--text-sm);
	color: var(--blue-ink) !important;
}

.book-panel .book-svc-ask h6 label {
	margin: 0;
	font-weight: var(--fw-medium);
}

.book-panel .book-svc-ask .form-check-inline {
	margin-right: var(--space-5);
}

.book-panel .book-svc-ask .form-check-label {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	font-size: var(--text-sm);
	font-weight: var(--fw-medium);
	color: var(--blue-ink);
	cursor: pointer;
}

/* .input-radio-primary (css/stylesheet.css section 6) paints these as filled
   squares, which read as checkboxes on a two-option question — round them
   back into radios inside the panel only */
.book-panel .book-svc-ask .form-check-input[type="radio"] {
	position: static;
	flex-shrink: 0;
	width: 16px;
	height: 16px;
	margin: 0;
	padding: 0;
	border: 1px solid var(--border);
	border-radius: var(--radius-full);
	background-color: var(--paper-white);
	cursor: pointer;
}

.book-panel .book-svc-ask .form-check-input[type="radio"]:checked {
	border-color: var(--spots-blue);
	background-color: var(--spots-blue);
	box-shadow: inset 0 0 0 3px var(--paper-white);
}

.book-panel .book-svc-err {
	margin: var(--space-3) 0 0;
	text-align: left !important;
	font-size: 12px;
}

/* The explanation reuses the note treatment without the include having to
   know about .book-note */
.book-panel .book-svc-copy {
	padding: var(--space-4) !important;
	border: 1px solid var(--color-border);
	border-left: 3px solid var(--spots-blue);
	border-radius: var(--radius-md);
	background: var(--blue-50);
}

.book-panel .book-svc-copy h6 {
	margin: 0 0 6px !important;
	text-transform: none;
	letter-spacing: var(--ls-normal);
	font-size: 13px;
	font-weight: var(--fw-semibold);
	color: var(--blue-ink) !important;
}

.book-panel .book-svc-copy h6 i {
	margin-right: 6px;
	color: var(--spots-blue);
}

.book-panel .book-svc-copy p {
	margin: 0;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--blue-ink);
}

.book-panel .book-svc-copy p + p {
	margin-top: 6px;
}


/* ========== 9. Cargo items ========== */
/* Step 3 on productsp. ltl.php renders the panel; action/ltlcargo_action.php
   echoes one .book-item per cargo line into #cargoDetails, which is nested two
   levels inside the panel body — so the section 2 divider rule does not reach
   them and .book-cargo-list separates them instead. Each item holds two states:
   the product tiles, and the dimensions form once a product is chosen. */

.book-panel .book-cargo-list > .book-item + .book-item {
	margin-top: var(--space-6);
	padding-top: var(--space-6);
	box-shadow: inset 0 1px 0 var(--color-border);
}

/* Blocks inside one item are closer than the items are to each other */
.book-panel .book-item > * + * {
	margin-top: var(--space-4);
}

/*-- Product tiles --
   The "what are you shipping?" choices. Buttons, not links: picking one swaps
   the state of this item, it does not navigate. */
.book-panel .book-prod {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
	gap: var(--space-3);
}

.book-panel .book-prod-tile {
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
	width: 100%;
	margin: 0;
	padding: var(--space-4);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	background: var(--paper-white);
	text-align: left;
	transition: var(--transition-fast);
}

.book-panel button.book-prod-tile:hover,
.book-panel button.book-prod-tile:focus {
	border-color: var(--spots-blue);
	background: var(--blue-50);
	outline: none;
}

.book-panel .book-prod-body {
	display: block;
	min-width: 0;
}

.book-panel .book-prod-name {
	display: block;
	font-size: var(--text-sm);
	font-weight: var(--fw-semibold);
	line-height: var(--lh-snug);
	color: var(--blue-ink);
}

.book-panel .book-prod-desc {
	display: block;
	margin-top: 4px;
	font-size: 12px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

/* The full-truckload tile carries a size select rather than a single product */
.book-panel .book-prod-tile--select .form-control {
	margin-top: var(--space-3);
}

.book-panel .book-prod-thumb {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 56px;
	height: 56px;
	border-radius: var(--radius-md);
	background: var(--blue-50);
	font-size: 20px;
	color: var(--spots-blue);
	overflow: hidden;
}

/* Product photos are not square — cover the tile rather than distort */
.book-panel .book-thumb-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/*-- Chosen product --
   #prodChoice_<i> is written into the item head by type=product, so the media
   block arrives as a child of it rather than of .book-item-head itself. */
.book-panel .book-item-head > div > .book-item-media {
	min-width: 0;
}

.book-panel .book-item-head .book-item-desc {
	max-width: 60ch;
}

/* Sits under the product description, so it needs to read as an action rather
   than as the last line of the copy */
.book-panel .book-prod-change {
	display: inline-block;
	margin-top: var(--space-2);
	font-size: 13px;
	font-weight: var(--fw-semibold);
}

/* Per-item carrier and price, filled by the .book handler once a carrier is
   picked. They hold no space until then — the app clears them to " ", not to
   empty, so :empty cannot be relied on. */
.book-panel .book-item-aside .carrier,
.book-panel .book-item-aside .cost {
	margin: 0;
}

.book-panel .book-item-aside .carrier:not(:first-child) {
	margin-top: 2px;
}

/* Cold chain only. A three-option select does not need the panel's full width,
   and jQuery .show() writes display:block so the cap has to come from here. */
.book-panel .book-temp {
	max-width: 340px;
}

.book-panel .book-temp > label i {
	margin-right: 6px;
	color: var(--cold-ice);
}

/* "Others" reveals a free-text unit under the handling-unit select */
.book-panel .book-unit-other {
	margin: 0;
}

.book-panel .book-unit-other:not(:empty) {
	margin-top: var(--space-2);
}

.book-panel .book-unit-other .form-control {
	padding: 8px 10px;
	font-size: var(--text-sm);
}

/*-- Loading --
   #wait_cargo and #wait_price, shown while a route is priced. .spinner-loader
   is the app's own spinner (css/stylesheet.css). */
.book-panel .book-wait {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding: var(--space-4) 0;
}

.book-panel .book-wait p {
	margin: 0;
	font-size: var(--text-sm);
	color: var(--spots-blue);
}

/*-- Cargo footer --
   "Add another item" sits in the actions row rather than closing the column, so
   it loses the block margin .book-add carries on showcart. */
.book-panel .book-actions .book-add {
	margin-top: 0;
	padding: var(--space-2) var(--space-4);
	cursor: pointer;
}

.book-panel .book-cargo-remark {
	margin: var(--space-3) 0 0;
	text-align: right;
}

/* The running total, and the booking submit that costgen_action.php injects
   alongside it into #totalCost */
.book-panel .book-cargo-total {
	margin-top: var(--space-4);
	text-align: right;
}

/* The rule belongs to the total, not to the block that holds it — the block is
   always present (it carries #totalRemark, #formErr and the quote-id inputs)
   and would otherwise dangle under the actions row before any quote exists.
   :has(*) rather than :empty because the app clears #totalCost to " ". */
.book-panel .book-total:has(*) {
	padding-top: var(--space-4);
	box-shadow: inset 0 1px 0 var(--color-border);
}

.book-panel .book-total h4 {
	margin: 0;
	font-family: var(--font-heading);
	font-size: var(--text-2xl);
	font-weight: var(--fw-bold);
	line-height: var(--lh-tight);
	color: var(--spots-blue);
}

/* .btn-block would stretch the submit across the panel; it closes the total */
.book-panel .book-total .btn {
	display: inline-block;
	width: auto;
	margin-top: var(--space-3);
}

.book-panel .book-cargo-total > p {
	margin: var(--space-2) 0 0;
}


/* ========== 10. Carrier price ========== */
/* Step 4. cost_display() in action/functions/functions.php writes one .book-rate
   per carrier into #price, cheapest first. The rows are a list of choices, not a
   form, so the panel body runs them flush and each row carries its own padding
   and divider — see .book-panel-body--flush. */

.book-panel-body--flush {
	padding: 0;
}

.book-panel-body--flush > .book-wait--inset {
	padding: var(--space-5) var(--space-6);
}

.book-panel .book-rate {
	display: flex;
	align-items: flex-start;
	gap: var(--space-4);
	padding: var(--space-5) var(--space-6);
}

.book-panel .book-rate + .book-rate {
	box-shadow: inset 0 1px 0 var(--color-border);
}

.book-panel .book-rate:hover {
	background: var(--surface-alt);
}

/* Logo + carrier name stack in their own column so the name reads as a
   caption under the mark, rather than as a heading beside it. */
.book-panel .book-rate-brand {
	display: flex;
	flex-direction: column;
	align-items: center;
	flex: 0 0 auto;
	width: 96px;
	gap: var(--space-2);
	text-align: center;
}

/* Carrier marks are wildly different shapes — contain rather than cover, so a
   wide wordmark is not cropped down to its middle */
.book-panel .book-rate-logo {
	width: 48px;
	height: 48px;
	background: var(--paper-white);
	border: 1px solid var(--color-border);
	padding: 4px;
}

.book-panel .book-rate-logo .book-thumb-img {
	width: 100%;
	height: 100%;
	object-fit: contain;
}

.book-panel .book-rate-body {
	flex: 1 1 auto;
	min-width: 0;
}

.book-panel .book-rate-name {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin: 0;
	font-size: 13px;
	font-weight: var(--fw-semibold);
	line-height: var(--lh-snug);
	color: var(--blue-ink);
}

/* Sea / Air, on FCL quotes only */
.book-panel .book-rate-mode {
	padding: 2px var(--space-2);
	border-radius: var(--radius-sm);
	background: var(--blue-50);
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--spots-blue);
}

/*-- Terms --
   Liability, lead time and any service charges, in one list. The legacy markup
   painted these green, which read as reassurance on what is mostly a liability
   disclaimer. */
.book-panel .book-rate-terms {
	margin: var(--space-2) 0 0;
	padding-left: 1.1rem;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

.book-panel .book-rate-terms li + li {
	margin-top: 2px;
}

.book-panel .book-rate-toggle {
	margin-top: var(--space-2);
	font-size: 13px;
	font-weight: var(--fw-semibold);
}

.book-panel .book-rate-toggle i {
	font-size: 10px;
	transition: transform var(--transition-fast);
}

.book-panel .book-rate-toggle[aria-expanded="true"] i {
	transform: rotate(180deg);
}

/*-- Pricing details --
   The collapse holds #priceDetail_<id>, the form the .book handler serialises
   to turn a carrier into per-item costs. */
.book-panel .book-rate-detail {
	margin-top: var(--space-3);
	padding: var(--space-4);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
	background: var(--surface-alt);
}

.book-panel .book-rate-detail p {
	margin: 0 0 var(--space-2);
	font-size: 12px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

.book-panel .book-rate-minimum {
	font-weight: var(--fw-semibold);
	color: var(--blue-ink) !important;
}

.book-panel .book-rate-lines {
	margin: 0 0 var(--space-3);
	padding-left: 1.1rem;
	font-size: 12px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

/* The per-item table, flattened onto the card the way showcart flattens its
   cargo table */
.book-panel .book-rate-table {
	margin: 0;
	font-size: 12px;
	color: var(--blue-ink);
}

.book-panel .book-rate-table th {
	padding: 0 0 6px;
	border: 0;
	font-family: var(--font-heading);
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--slate);
}

.book-panel .book-rate-table td {
	padding: 6px 0;
	border-top: 1px solid var(--color-border);
	vertical-align: top;
}

.book-panel .book-rate-table td:last-child {
	white-space: nowrap;
}

/*-- Price and CTA --
   .book-price (section 6) carries the figure itself. */
.book-panel .book-rate-aside {
	flex-shrink: 0;
	width: 180px;
	text-align: right;
}

.book-panel .book-rate-aside .btn {
	width: 100%;
	margin-top: var(--space-2);
}

/* Freight / documentation split, where customs charges apply */
.book-panel .book-rate-breakdown {
	margin: 0 0 var(--space-2);
}

.book-panel .book-rate-breakdown > div {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-3);
}

.book-panel .book-rate-breakdown dt {
	font-size: 12px;
	font-weight: var(--fw-regular);
	color: var(--slate);
}

.book-panel .book-rate-breakdown dd {
	margin: 0;
	font-size: 12px;
	color: var(--blue-ink);
}

.book-panel .book-rate-help {
	margin: var(--space-2) 0 0;
	font-size: 12px;
	color: var(--slate);
}

.book-panel .book-rate-help i {
	color: var(--on-time-green);
}

/* Inherits the global red anchor colour otherwise, which puts an accent-red
   link in every row alongside the one CTA that is meant to carry it */
.book-panel .book-rate-help a {
	color: var(--slate);
}

.book-panel .book-rate-help a:hover,
.book-panel .book-rate-help a:focus {
	color: var(--spots-blue);
}

/* The panel head's right-hand caption, beside the caret */
.book-panel-head .book-panel-note {
	margin-left: auto;
	font-size: 12px;
	color: var(--slate);
}


/* ========== 11. Dialogs ========== */
/* showcart.php's two Bootstrap modals. The global .modal-header / .modal-title
   rule (css/stylesheet.css section 9) already paints every modal's header
   blue-50 / brand-blue — these two classes only add what's specific to each
   dialog's body. .book-note above covers the informational callouts in both.
   checkout-pay.php's three payment dialogs are deliberately not on this layer:
   each owns a live integration (Stripe Elements, Dropzone, the credit check)
   and keeps its existing markup and flow. */

/*-- Upload (upload_modal.php) --
   One Dropzone drop target, then the two "your documents" galleries
   action/fileupload_action.php fills with raw .col-md-2 + <embed> markup —
   only the section wrapper and the thumbnail chrome are styled here. */
.upload-modal .upload-dropzone {
	display: flex !important;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 2px;
	padding: var(--space-8) var(--space-5) !important;
	border: 1px dashed var(--border) !important;
	border-radius: var(--radius-lg) !important;
	background: var(--surface-alt) !important;
	text-align: center;
	cursor: pointer;
	transition: var(--transition-fast);
}

/* .dz-drag-hover is Dropzone's own class, added while a file is dragged over —
   loads after this file (see the CDN link in upload_modal.php's head), so the
   background/border need !important to hold against the library's defaults */
.upload-modal .upload-dropzone:hover,
.upload-modal .upload-dropzone.dz-drag-hover {
	border-color: var(--spots-blue) !important;
	background: var(--blue-50) !important;
}

.upload-modal .upload-dropzone .dz-message i {
	font-size: 28px;
	color: var(--spots-blue);
}

.upload-modal .upload-dropzone-title {
	margin-top: var(--space-2);
	font-size: var(--text-sm);
	font-weight: var(--fw-semibold);
	color: var(--blue-ink);
}

.upload-modal .upload-dropzone small {
	color: var(--slate);
}

/* Files staged before Submit render inside the same card, not as a floating
   preview strip */
.upload-modal .upload-dropzone .dz-preview {
	margin: var(--space-3) 0 0;
}

.upload-modal .upload-doc-section {
	margin-top: var(--space-6);
	padding-top: var(--space-6);
	box-shadow: inset 0 1px 0 var(--color-border);
}

.upload-modal .upload-doc-label {
	margin: 0 0 var(--space-3);
	font-family: var(--font-heading);
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--slate);
}

.upload-modal .upload-doc-grid .img-thumbnail {
	border-color: var(--color-border);
	border-radius: var(--radius-md);
}

.upload-modal .upload-doc-grid .img_Inv {
	display: block;
	margin-top: 6px;
	font-size: 12px;
	font-weight: var(--fw-semibold);
	color: var(--spots-blue);
	text-align: center;
}

.upload-modal .upload-doc-grid p.text-info {
	margin: 0;
	font-size: var(--text-sm);
	color: var(--slate);
}

/*-- Terms & conditions (showcart.php #termsModal) --
   Legacy: a photo column beside a bg-light card. Here: one column, the
   liability / cancellation copy read as two notes instead of danger-red lists,
   and the minimum-pickup interstitial reuses the same note before the terms
   are shown. */
.terms-modal .terms-illustration {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--space-6);
	background: var(--blue-50);
}

.terms-modal .terms-illustration img {
	max-width: 100%;
	height: auto;
}

.terms-modal .terms-body {
	padding: var(--space-6);
}

/* Scoped to .terms-body, not .terms-modal — the dialog's own .modal-title is
   an h5 too, and must keep the global brand-blue heading treatment */
.terms-modal .terms-body h5 {
	margin: 0 0 var(--space-4);
	font-size: var(--text-lg);
	color: var(--blue-ink);
}

.terms-modal .book-note + .book-note {
	margin-top: var(--space-3);
}

.terms-modal .terms-agree {
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
	margin-top: var(--space-6);
	padding-top: var(--space-5);
	box-shadow: inset 0 1px 0 var(--color-border);
}

.terms-modal .terms-agree input[type="checkbox"] {
	flex-shrink: 0;
	width: 16px;
	height: 16px;
	margin-top: 3px;
}

.terms-modal .terms-agree label {
	margin: 0;
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

.terms-modal .terms-actions {
	margin-top: var(--space-5);
}

@media (max-width: 767.98px) {
	.terms-modal .terms-illustration {
		display: none;
	}
}

/*-- Payment hand-offs (checkout-pay.php, billing_pay.php) --
   html/upload_payment_modal.php and its _credit twin are the upload dialog
   above with a fact list in front of the drop target — the account to bank
   into. html/credit_payment_modal.php has no upload at all, but states the
   same kind of figures, so both share one list treatment and the two hand-offs
   read as the same step. The Stripe dialog is deliberately not on this layer:
   it renders Stripe's own Payment Element and keeps its existing markup. */
:is(.upload-modal, .credit-modal, .stripe-modal) .modal-facts {
	margin: 0 0 var(--space-4);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
}

:is(.upload-modal, .credit-modal, .stripe-modal) .modal-facts > div {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	padding: 10px var(--space-4);
}

:is(.upload-modal, .credit-modal, .stripe-modal) .modal-facts > div + div {
	box-shadow: inset 0 1px 0 var(--color-border);
}

:is(.upload-modal, .credit-modal, .stripe-modal) .modal-facts dt {
	font-size: 13px;
	font-weight: var(--fw-regular);
	color: var(--slate);
}

/* The figure is what the customer is checking — it carries the weight */
:is(.upload-modal, .credit-modal, .stripe-modal) .modal-facts dd {
	margin: 0;
	font-size: 13px;
	font-weight: var(--fw-semibold);
	text-align: right;
	color: var(--blue-ink);
}

/* action/credit_fetch.php fills the figures when the dialog opens, and
   action/payment_credit_action.php runs again on submit */
.credit-modal .credit-working {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	margin-top: var(--space-4);
}

.credit-modal .credit-working p {
	margin: 0;
	font-size: 13px;
	font-weight: var(--fw-semibold);
	color: var(--spots-blue);
}

/* The shared .spinner-loader is a 50px page-level spinner — inline beside one
   line of text it needs to be a glyph, not a hero */
.credit-modal .credit-working .spinner-loader {
	flex-shrink: 0;
	width: 20px;
	height: 20px;
	border-width: 3px;
}

/* Empty until the credit check reports a reason the order cannot be billed */
.credit-modal .credit-error {
	margin: var(--space-3) 0 0;
	font-size: 13px;
	line-height: var(--lh-normal);
}

/*-- Stripe (html/stripe_payment_modal.php and its _credit twin) --
   Stripe renders the fields itself inside an iframe, so this styles the frame
   around them: the mount point, the button, the error line and the fine print.

   css/stripe-css.css is the Stripe sample stylesheet and still loads with both
   dialogs, on a page it was never scoped for. Dropping its .stripe wrapper
   from the markup is what disarms the worst of it — `.stripe form` pins a form
   to 30vw / min-width 500px, which does not fit a dialog, and `.stripe button`
   paints Stripe purple. What is left leaks by element or id and is corrected
   below rather than by editing a vendor file three pages depend on. */
.stripe-modal .stripe-mount {
	margin-bottom: var(--space-4);
}

/* #card-element (the older Card Element the _credit dialog uses) is given a
   44px box with a 4px 4px 0 0 radius by stripe-css.css, squared off at the
   bottom because the sample glues its button directly underneath. The button
   is its own block here, so the box closes properly and picks up the app's
   border rather than Stripe's rgba slate. */
.stripe-modal #card-element {
	height: auto;
	padding: 12px;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
}

/* stripe-css.css paints this slate grey — it is the card decline message, and
   a declined card is an error */
.stripe-modal #card-error {
	margin-top: var(--space-3);
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--urgent-red);
}

.stripe-modal #card-error:empty {
	margin-top: 0;
}

/* The button spinner is stripe-css.css's, and its two masks are painted the
   sample's purple so they disappear against that button. Repaint them to the
   button underneath them here. */
.stripe-modal .spinner:before,
.stripe-modal .spinner:after {
	background: var(--spots-blue);
}

.stripe-modal .stripe-terms {
	margin: var(--space-3) 0 0;
	font-size: 12px;
	line-height: var(--lh-normal);
	color: var(--fog);
}

.stripe-modal .stripe-terms a {
	font-weight: var(--fw-semibold);
	color: var(--spots-blue);
}

/* Scheme names as type, not logo files: the legacy dialog shipped two PNGs for
   this and they never lined up with each other */
.stripe-modal .stripe-marks {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
	margin-top: var(--space-4);
}

.stripe-modal .stripe-mark {
	padding: 3px 8px;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-sm);
	font-size: 11px;
	font-weight: var(--fw-semibold);
	color: var(--slate);
}

.stripe-modal .stripe-mark--note {
	padding-left: 0;
	border: 0;
	font-weight: var(--fw-regular);
	color: var(--fog);
}


/* ========== 12. Confirmation ========== */
/* checkout-complete.php and its two siblings — the Stripe/PayPal return, the
   bank-slip return (checkout-complete-1.php) and the credit-terms return
   (checkout-complete-credit.php). All three render the same shell and differ
   only in copy and in the pending-verification note action/complete_fetch.php
   fills, so everything below is shared by the three of them.

   The deliveries themselves are .book-item cards from action/checkout_fetch.php
   — the same fragment checkout-pay.php renders one step earlier, so a customer
   sees the identical card before and after paying. Nothing here re-styles it. */

/*-- Confirmation banner --
   Replaces the legacy stock photo + green "CONFIRMATION" heading. Sits above
   the layout, so it is global like .book-add rather than panel-scoped. */
.book-confirm {
	display: flex;
	align-items: center;
	gap: var(--space-5);
	margin-bottom: var(--space-8);
	padding: var(--space-6);
	border: 1px solid var(--color-border);
	border-left: 3px solid var(--on-time-green);
	border-radius: var(--radius-lg);
	background: var(--paper-white);
	box-shadow: var(--shadow-sm);
}

/* The one green thing on the page. Green is not in the booking palette — it is
   reserved for exactly this: the state that has actually completed. */
.book-confirm-mark {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 52px;
	height: 52px;
	border-radius: var(--radius-full);
	background: var(--on-time-green);
	font-size: 24px;
	color: var(--paper-white);
}

/*-- Pending --
   billing-complete-credit.php reaches this banner in two states: the payment
   cleared (green, as above), or a bank-in slip is waiting on approval. Green
   is reserved for what has actually completed, so a pending settlement takes
   the brand blue instead — the same "in progress, nothing wrong" reading the
   step rail gives a current step. */
.book-confirm--pending {
	border-left-color: var(--spots-blue);
}

.book-confirm--pending .book-confirm-mark {
	background: var(--spots-blue);
}

.book-confirm-body {
	flex: 1 1 auto;
	min-width: 0;
}

/* The page's h1 lives in here rather than above the banner, so it loses the
   bottom margin `.info-section--app h1` gives it */
.book-confirm-body h1 {
	margin-bottom: var(--space-1) !important;
	font-size: var(--text-3xl);
}

.book-confirm-body p {
	margin: 0;
	font-size: var(--text-sm);
	line-height: var(--lh-normal);
	color: var(--slate);
}

/*-- Booking reference --
   The number the customer quotes back to us, so it is set apart from the copy
   rather than run into it as the legacy two-column row did. */
.book-confirm-ref {
	flex-shrink: 0;
	margin: 0;
	padding-left: var(--space-5);
	border-left: 1px solid var(--color-border);
	text-align: right;
}

.book-confirm-ref dt {
	margin-bottom: 2px;
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--slate);
}

.book-confirm-ref dd {
	margin: 0;
	font-family: var(--font-heading);
	font-size: var(--text-2xl);
	font-weight: var(--fw-bold);
	font-variant-numeric: tabular-nums;
	line-height: var(--lh-tight);
	color: var(--spots-blue);
}

/*-- Pending verification --
   checkout-complete-1.php and -credit.php only. action/complete_fetch.php
   returns an empty status once the payment has been reconciled, so the page
   hides the whole note rather than rendering an empty callout. */
.book-confirm-pending[hidden] {
	display: none;
}

/*-- Modal confirmation --
   Small action modals (save pickup/delivery address, etc.) swap their form
   for this centered checkmark + message rather than closing outright.
   Same "completed" green as .book-confirm-mark, scaled for a modal body. */
.modal-confirm {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: var(--space-4);
	padding: var(--space-8) 0;
}

.modal-confirm-mark {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 88px;
	height: 88px;
	border-radius: var(--radius-full);
	background: var(--on-time-green);
	font-size: 40px;
	color: var(--paper-white);
}

.modal-confirm p {
	margin: 0;
	font-size: var(--text-base);
	color: var(--slate);
}

/*-- What happens next --
   The numbered list in the rail. Replaces the legacy alert-info block of the
   same three instructions, which sat in the far column above the fold and
   repeated the modal word for word. */
.rail-card .book-todo {
	counter-reset: book-todo;
	list-style: none;
	margin: 0;
	padding: 0;
}

.rail-card .book-todo li {
	position: relative;
	counter-increment: book-todo;
	padding-left: var(--space-8);
	font-size: 13px;
	line-height: var(--lh-normal);
	color: var(--slate);
}

.rail-card .book-todo li + li {
	margin-top: var(--space-4);
}

.rail-card .book-todo li::before {
	content: counter(book-todo);
	position: absolute;
	top: 0;
	left: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 22px;
	height: 22px;
	border: 1px solid var(--blue-200);
	border-radius: var(--radius-full);
	background: var(--blue-50);
	font-size: var(--text-xs);
	font-weight: var(--fw-semibold);
	color: var(--spots-blue);
}

/* Each step leads with what to do, then explains it on the line below */
.rail-card .book-todo strong {
	display: block;
	margin-bottom: 2px;
	font-size: var(--text-sm);
	font-weight: var(--fw-semibold);
	color: var(--blue-ink);
}

/* The onward actions close the rail — stacked, full width, evenly spaced */
.rail-card .rail-actions {
	display: grid;
	gap: var(--space-2);
}

.rail-card .rail-actions .btn {
	width: 100%;
	margin: 0;
}


/* ========== 13. Responsive ========== */

@media (max-width: 991.98px) {
	/* The rail stacks with the main column and stops being sticky — a summary
	   pinned to the top of a stacked page would cover the list it summarises */
	.book-layout--rail-end,
	.book-layout--rail-start,
	.book-layout--rail-wide {
		grid-template-columns: minmax(0, 1fr);
	}

	.book-rail {
		position: static;
		top: auto;
	}

	.book-panel .book-item-detail {
		grid-template-columns: minmax(0, 1fr);
	}
}

@media (max-width: 767.98px) {
	.info-section--app {
		padding: var(--space-6) 0 var(--space-12);
	}

	.info-section--app h1 {
		font-size: var(--text-3xl);
	}

	.book-panel-head,
	.book-panel-body {
		padding: var(--space-4);
	}

	/* Check mark, copy and booking number stack. The reference loses its rule
	   and gains one above it instead — it is closing the block now, not
	   sitting beside it. */
	.book-confirm {
		flex-direction: column;
		align-items: flex-start;
		gap: var(--space-4);
		padding: var(--space-5);
	}

	.book-confirm-ref {
		width: 100%;
		padding: var(--space-4) 0 0;
		border-left: 0;
		border-top: 1px solid var(--color-border);
		text-align: left;
	}
}

@media (max-width: 575.98px) {
	/* Three labels plus connectors overflow a phone — the numbers and labels
	   stay, the connecting rules go */
	.book-flow li:not(:last-child)::after {
		display: none;
	}

	.book-panel .book-actions-end {
		width: 100%;
		margin-left: 0;
	}

	.book-panel .book-actions-end .btn {
		width: 100%;
	}

	/* Carrier logo, price and toggle stop competing with the address for
	   width — the header stacks and the price aligns left with the title */
	.book-panel .book-item-head {
		flex-direction: column;
		gap: var(--space-3);
	}

	.book-panel .book-item-aside {
		text-align: left;
	}

	/* A 180px price column plus the terms is too much for a phone — logo and
	   name take the full first line, and the aside wraps onto its own line
	   below. It keeps half that line's width and sits flush right, so the
	   price column still reads as distinct from the carrier's terms rather
	   than bleeding across the full row; the Select button keeps the
	   full-width tap target from the base rule above. */
	.book-panel .book-rate {
		flex-wrap: wrap;
	}

	.book-panel .book-rate-aside {
		width: 50%;
		margin-left: auto;
	}
}
