/**
 * TopLife Lodge — public site design system.
 *
 * The same token vocabulary the management application uses, so the two halves
 * of the product read as one thing. What differs is the register: the admin is
 * dense and utilitarian because somebody uses it forty times a day; this side is
 * open and photographic because somebody sees it once and decides whether to
 * trust the place with their holiday.
 *
 * @package TopLife
 */

/* ==========================================================================
   1. TOKENS
   ========================================================================== */

:root {
	--tl-neutral-0:   #ffffff;
	--tl-neutral-25:  #fdfcfa;
	--tl-neutral-50:  #f8f6f2;
	--tl-neutral-100: #f0ece5;
	--tl-neutral-200: #e3ddd3;
	--tl-neutral-300: #cec6b8;
	--tl-neutral-400: #a09788;
	--tl-neutral-500: #6f685c;
	--tl-neutral-600: #524c43;
	--tl-neutral-700: #3a352e;
	--tl-neutral-800: #262220;
	--tl-neutral-900: #171412;

	--tl-primary-50:  #eef6f6;
	--tl-primary-100: #d3e8e8;
	--tl-primary-200: #a7d1d2;
	--tl-primary-300: #6fb2b4;
	--tl-primary-400: #3d9093;
	--tl-primary-500: #1f7376;
	--tl-primary-600: #175c5f;
	--tl-primary-700: #124a4c;
	--tl-primary-800: #0e3a3c;
	--tl-primary-900: #0a2b2c;

	--tl-accent-100: #fdf0dc;
	--tl-accent-300: #f0c37a;
	--tl-accent-500: #d99b32;
	--tl-accent-600: #bf8324;
	--tl-accent-700: #a3701c;

	--tl-success-50: #e9f7ef;
	--tl-success-500: #1e874b;
	--tl-success-700: #146036;
	--tl-danger-50:  #fdedec;
	--tl-danger-100: #f9d6d3;
	--tl-danger-500: #c0392b;
	--tl-danger-700: #8f2a1f;
	--tl-warning-50: #fdf4e3;
	--tl-warning-500: #b87a0b;
	--tl-warning-700: #8a5a06;

	--tl-bg:      var(--tl-neutral-25);
	--tl-surface: var(--tl-neutral-0);
	--tl-border:  var(--tl-neutral-200);
	--tl-text:        var(--tl-neutral-800);
	--tl-text-muted:  var(--tl-neutral-500);
	--tl-text-subtle: var(--tl-neutral-400);

	--tl-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
	--tl-font-display: "Fraunces", "Iowan Old Style", Georgia, "Times New Roman", serif;

	--tl-space-1: 0.25rem;
	--tl-space-2: 0.5rem;
	--tl-space-3: 0.75rem;
	--tl-space-4: 1rem;
	--tl-space-5: 1.25rem;
	--tl-space-6: 1.5rem;
	--tl-space-8: 2rem;
	--tl-space-10: 2.5rem;
	--tl-space-12: 3rem;
	--tl-space-16: 4rem;
	--tl-space-20: 5rem;

	--tl-radius-sm: 4px;
	--tl-radius:    8px;
	--tl-radius-md: 12px;
	--tl-radius-lg: 18px;
	--tl-radius-full: 999px;

	--tl-shadow-xs: 0 1px 2px rgba(23, 20, 18, 0.04);
	--tl-shadow-sm: 0 1px 3px rgba(23, 20, 18, 0.06), 0 1px 2px rgba(23, 20, 18, 0.04);
	--tl-shadow-md: 0 4px 16px rgba(23, 20, 18, 0.08), 0 1px 4px rgba(23, 20, 18, 0.04);
	--tl-shadow-lg: 0 12px 36px rgba(23, 20, 18, 0.12), 0 2px 8px rgba(23, 20, 18, 0.05);

	--tl-ring: 0 0 0 3px rgba(31, 115, 118, 0.24);
	--tl-transition: 160ms cubic-bezier(0.4, 0, 0.2, 1);

	/*
	 * Measure and rhythm.
	 *
	 * 1180px was too narrow for a modern desktop: it left the booking grid at
	 * three cards with slack on both sides and made a 1920px screen look like a
	 * phone stretched out. 1320 fits four room cards, or three cards plus the
	 * booking basket, and still holds a comfortable line length for prose
	 * because prose is separately capped at its own measure.
	 */
	--tl-container: 1320px;
	--tl-measure-prose: 68ch;
	--tl-header-h: 76px;
	--tl-ticker-h: 40px;

	/*
	 * One section rhythm token instead of a 4rem constant. The old value put
	 * 64px above and below every band on a phone as well as a desktop, which is
	 * where most of the "too much empty space" came from.
	 */
	--tl-section-y: clamp(2.5rem, 4.5vw, 4rem);
	--tl-section-y-tight: clamp(1.75rem, 3vw, 2.5rem);

	/*
	 * Semantic aliases. Components should reach for these rather than for a
	 * numbered step, so the palette can be retuned in one place.
	 */
	--tl-primary:      var(--tl-primary-600);
	--tl-primary-dark: var(--tl-primary-800);
	--tl-accent:       var(--tl-accent-500);
	--tl-background:   var(--tl-bg);
	--tl-muted:        var(--tl-text-muted);
	--tl-success:      var(--tl-success-500);
	--tl-warning:      var(--tl-warning-500);
	--tl-danger:       var(--tl-danger-500);
}

/* ==========================================================================
   2. RESET
   ========================================================================== */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

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

body {
	margin: 0;
	background: var(--tl-bg);
	color: var(--tl-text);
	font-family: var(--tl-font);
	font-size: 1rem;
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

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

/*
 * Type scale.
 *
 * Deliberately smaller than it was. The old h1 topped out at 3.1rem — nearly
 * 50px — which made the word "Booking" the largest thing on a page whose actual
 * job is a form, and pushed the first useful control below the fold. A
 * hospitality page earns its presence from photography and spacing, not from
 * headline size. The display face is kept for headings that introduce a section;
 * everything else is the text face.
 */
h1, h2, h3, h4 { margin: 0 0 var(--tl-space-3); line-height: 1.22; letter-spacing: -0.015em; font-weight: 600; }
h1 { font-size: clamp(1.75rem, 3.2vw, 2.5rem); font-family: var(--tl-font-display); font-weight: 600; }
h2 { font-size: clamp(1.35rem, 2.2vw, 1.8rem); font-family: var(--tl-font-display); }
h3 { font-size: 1.08rem; }
h4 { font-size: 0.95rem; }
p { margin: 0 0 var(--tl-space-4); }
p:last-child { margin-bottom: 0; }

a { color: var(--tl-primary-600); text-underline-offset: 2px; }
a:hover { color: var(--tl-primary-700); }

:focus-visible { outline: 2px solid var(--tl-primary-500); outline-offset: 3px; border-radius: 3px; }

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

.tl-skip {
	position: absolute; left: -9999px; top: 0; z-index: 999;
	background: var(--tl-primary-700); color: #fff; padding: var(--tl-space-3) var(--tl-space-4);
	border-radius: 0 0 var(--tl-radius) 0; font-weight: 500;
}
.tl-skip:focus { left: 0; color: #fff; }

/* ==========================================================================
   3. LAYOUT
   ========================================================================== */

.tl-container {
	width: 100%;
	max-width: var(--tl-container);
	margin-inline: auto;
	padding-inline: var(--tl-space-5);
}

.tl-section { padding-block: var(--tl-section-y); }
.tl-section--tight { padding-block: var(--tl-section-y-tight); }
.tl-section--alt { background: var(--tl-neutral-50); }

/* A band that carries a rule rather than a fill — quieter than alternating
   background colours down the whole page, which reads as stripes. */
.tl-section--ruled { border-top: 1px solid var(--tl-border); }

.tl-section__head { max-width: 62ch; margin-bottom: var(--tl-space-6); }

/*
 * A clarifying line under a section's standfirst. Quieter than the paragraph
 * above it because it answers a question the reader has not asked yet, and
 * louder than nothing because the answer they would otherwise guess is wrong.
 */
.tl-section__note {
	margin: var(--tl-space-3) 0 0;
	font-size: 0.94rem;
	line-height: 1.6;
	color: var(--tl-text-muted);
}

/* A heading with something on the right — "Our rooms … See all rooms". */
.tl-section__head--split {
	max-width: none;
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: var(--tl-space-4);
}
.tl-section__head--split > div { max-width: 62ch; }
.tl-section__eyebrow {
	display: inline-block;
	margin-bottom: var(--tl-space-3);
	font-size: 0.75rem;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--tl-primary-500);
}
.tl-section__head p { color: var(--tl-text-muted); font-size: 1.05rem; margin-bottom: 0; }

/* ==========================================================================
   4. HEADER
   ========================================================================== */

.tl-header {
	position: sticky;
	top: 0;
	z-index: 60;
	background: rgba(255, 255, 255, 0.92);
	backdrop-filter: saturate(160%) blur(12px);
	border-bottom: 1px solid var(--tl-border);
	transition: box-shadow var(--tl-transition), background var(--tl-transition);
}

/*
 * Once the page has scrolled the bar is floating over content rather than
 * sitting on top of it, and it needs to say so. A hairline border alone
 * disappears against a photograph; a very soft shadow separates the two without
 * turning into a visible band.
 */
.tl-header.is-stuck {
	background: rgba(255, 255, 255, 0.97);
	box-shadow: 0 1px 0 var(--tl-border), 0 6px 20px rgba(23, 20, 18, 0.07);
}

/*
 * A fixed height rather than a minimum. The screenshots showed a header that
 * grew with whatever was inside it, which is how a 68px bar becomes a 110px
 * one and the page below starts too far down.
 */
.tl-header__inner {
	display: flex;
	align-items: center;
	gap: var(--tl-space-6);
	height: var(--tl-header-h);
}

/* THE GAP IS SMALLER THAN IT LOOKS, AND SO IS THE FIX.
 *
 * The owner reported too much air between the mark and the name. Two thirds of
 * it were CSS -- 12px, set when the mark was a tight 40px tile of two letters --
 * and one third is inside the logo file itself: the badge is a circle drawn in a
 * square canvas, so it carries its own transparent margin that no rule here can
 * reach. 8px against a mark with built-in padding reads as the single brand
 * block the owner asked for; going tighter starts to crowd the tile fallback,
 * which has no padding of its own.
 */
.tl-brand {
	display: flex;
	align-items: center;
	gap: var(--tl-space-2);
	text-decoration: none;
	color: var(--tl-text);
	font-weight: 600;
	flex: 0 0 auto;
}

/* 40px, not 44. On a 76px bar a 44px mark leaves 16px of air above and below
   and reads as a logo that has outgrown its header. */
.tl-brand__mark {
	display: grid;
	place-items: center;
	width: 40px; height: 40px;
	flex: 0 0 40px;
	border-radius: var(--tl-radius);
	background: linear-gradient(140deg, var(--tl-primary-400), var(--tl-primary-700));
	color: #fff;
	font-weight: 700;
	font-size: 0.8rem;
	letter-spacing: 0.04em;
}

/* THE UPLOADED LOGO, WHEN THERE IS ONE.
 *
 * `header.php` prefers the Site Identity logo and falls back to the monogram
 * above, so this rule is what stands between "a logo is set" and a header torn
 * open by a 1024px square. WordPress emits the <img> at its natural size and
 * the theme declares flex-width and flex-height, which means the CAP HAS TO BE
 * HERE -- there is nowhere else it can come from.
 *
 * Capped to the 40px the monogram occupies rather than to the 80px the theme
 * support declares, because the two marks sit in the same 76px bar and a header
 * that changes height when a logo is uploaded is a header that reflows the
 * whole page for one setting. `width: auto` lets a wordmark be wide; a square
 * badge stays square.
 */
/*
 * .custom-logo-link is gone with the anchor it styled. `the_custom_logo()`
 * wrapped the image in a second <a> inside .tl-brand, and nested anchors are
 * not representable in HTML: the parser closed .tl-brand and hoisted the logo
 * and the lodge's name out of the link. header.php now prints the bare <img>,
 * so there is no inner link left to style.
 */

.custom-logo {
	display: block;
	height: 40px;
	width: auto;
	max-width: 180px;
	object-fit: contain;
}

.tl-brand__name { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.tl-brand__name strong { font-size: 1.0625rem; font-weight: 600; letter-spacing: -0.012em; }
.tl-brand__name small { font-size: 0.6875rem; font-weight: 400; color: var(--tl-text-muted); letter-spacing: 0.07em; text-transform: uppercase; margin-top: 1px; }

.tl-nav { margin-left: auto; }
.tl-nav ul { display: flex; gap: var(--tl-space-1); list-style: none; margin: 0; padding: 0; }
.tl-nav a {
	display: block;
	padding: var(--tl-space-2) var(--tl-space-3);
	border-radius: var(--tl-radius-sm);
	color: var(--tl-neutral-600);
	text-decoration: none;
	font-size: 0.9375rem;
	font-weight: 500;
	white-space: nowrap;
	transition: color var(--tl-transition), background var(--tl-transition);
}
.tl-nav a:hover { color: var(--tl-text); background: var(--tl-neutral-100); }
.tl-nav .current-menu-item > a { color: var(--tl-primary-700); font-weight: 600; }

.tl-header__cta { flex: 0 0 auto; }

.tl-navtoggle {
	display: none;
	width: 42px; height: 42px;
	align-items: center; justify-content: center;
	border: 1px solid var(--tl-border);
	background: var(--tl-surface);
	border-radius: var(--tl-radius-sm);
	cursor: pointer;
}
.tl-navtoggle span { display: block; width: 18px; height: 2px; background: var(--tl-text); position: relative; }
.tl-navtoggle span::before, .tl-navtoggle span::after {
	content: ""; position: absolute; left: 0; width: 18px; height: 2px; background: var(--tl-text);
}
.tl-navtoggle span::before { top: -6px; }
.tl-navtoggle span::after { top: 6px; }

/* The nav drawer carries its own full-width 'Book a room' button. Above the
   drawer breakpoint the header shows .tl-header__cta instead, so this one
   must be hidden — otherwise it stretches across the desktop header as a
   full-width orange bar next to the very button it duplicates. */
.tl-nav__cta { display: none; }

/*
 * 320px is a real phone, not a hypothetical one. At that width the brand
 * lockup plus the menu button is wider than the 280px of content the container
 * leaves, so the secondary line goes and the mark shrinks. The name stays: it
 * is the one thing on the bar that has to be there.
 */
@media (max-width: 400px) {
	.tl-container { padding-inline: var(--tl-space-4); }
	.tl-header__inner { gap: var(--tl-space-3); height: 66px; }
	.tl-brand__mark { width: 36px; height: 36px; flex-basis: 36px; }
	/* Same step down as the monogram, for the same reason: they occupy one slot. */
	.custom-logo { height: 36px; max-width: 140px; }
	.tl-brand__name small { display: none; }
	.tl-brand__name strong { font-size: 0.9375rem; }
	.tl-navtoggle { width: 38px; height: 38px; flex: 0 0 38px; }
}

@media (max-width: 880px) {
	.tl-navtoggle { display: flex; margin-left: auto; }
	.tl-header__cta { display: none; }

	.tl-nav {
		position: fixed;
		inset: var(--tl-header-h) 0 auto 0;
		background: var(--tl-surface);
		border-bottom: 1px solid var(--tl-border);
		box-shadow: var(--tl-shadow-md);
		padding: var(--tl-space-4) var(--tl-space-5) var(--tl-space-6);
		margin: 0;
		display: none;
	}
	.tl-nav.is-open { display: block; }
	.tl-nav ul { flex-direction: column; gap: 2px; }
	.tl-nav a { padding: var(--tl-space-3); font-size: 1rem; }
	.tl-nav .tl-nav__cta { display: block; margin-top: var(--tl-space-3); }
}

/* ==========================================================================
   5. BUTTONS
   ========================================================================== */

.tl-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--tl-space-2);
	min-height: 44px;
	padding: 0 var(--tl-space-5);
	border-radius: var(--tl-radius-sm);
	border: 1px solid transparent;
	background: var(--tl-neutral-100);
	color: var(--tl-text);
	font: inherit;
	font-size: 0.9375rem;
	font-weight: 600;
	text-decoration: none;
	cursor: pointer;
	transition: background var(--tl-transition), color var(--tl-transition),
		box-shadow var(--tl-transition), transform var(--tl-transition);
	white-space: nowrap;
}

.tl-btn:hover { background: var(--tl-neutral-200); color: var(--tl-text); }
.tl-btn:active { transform: translateY(1px); }

.tl-btn--primary {
	background: var(--tl-primary-600);
	color: #fff;
	box-shadow: var(--tl-shadow-sm);
}
.tl-btn--primary:hover { background: var(--tl-primary-700); color: #fff; }

.tl-btn--accent {
	background: var(--tl-accent-500);
	color: var(--tl-neutral-900);
	box-shadow: var(--tl-shadow-sm);
}
.tl-btn--accent:hover { background: var(--tl-accent-600); color: var(--tl-neutral-900); }

.tl-btn--ghost { background: transparent; border-color: var(--tl-border); }
.tl-btn--ghost:hover { background: var(--tl-neutral-100); }

.tl-btn--sm { min-height: 36px; padding: 0 var(--tl-space-3); font-size: 0.85rem; }
.tl-btn--block { width: 100%; }

.tl-btn[disabled], .tl-btn.is-busy { opacity: 0.6; cursor: not-allowed; }

.tl-btn.is-busy::after {
	content: "";
	width: 14px; height: 14px;
	border-radius: 50%;
	border: 2px solid currentColor;
	border-right-color: transparent;
	animation: tl-spin 0.6s linear infinite;
}

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

/* ==========================================================================
   6. TICKER AND HERO
   ==========================================================================

   The hero carries no text at all. A headline over a photograph has to be made
   legible against whatever the photograph is doing behind it, and the only
   reliable way to do that is to darken the photograph — which is exactly the
   thing a lodge is selling. So the words moved to the section below and the
   pictures got the whole frame.
   ========================================================================== */

/* ==========================================================================
   THE ANNOUNCEMENT STRIP

   A continuous marquee, paused entirely in CSS.

   The whole set of messages slides past at one speed. Nothing appears or
   disappears, so a visitor who looks up halfway through a sentence can still
   read the rest of it, and a long message is ended by their reading speed
   rather than by a five-second timer.

   The loop is seamless because the track holds TWO identical copies of the
   list and travels exactly half its own width. At the moment the animation
   restarts, the second copy is sitting where the first one began, so there is
   no jump and no empty gap waiting for the beginning to come round again.
   ========================================================================== */

.tl-ticker {
	background: var(--tl-primary-900);
	color: rgba(255, 255, 255, 0.9);
	border-bottom: 1px solid rgba(255, 255, 255, 0.08);
	/* Full bleed: the strip is a band across the page, not a boxed element,
	   so it does not take the container's side padding. */
	overflow: hidden;
}

.tl-ticker__inner {
	display: flex;
	align-items: center;
	gap: var(--tl-space-4);
	height: 42px;
	padding-left: var(--tl-space-5);
}

.tl-ticker__badge {
	flex: 0 0 auto;
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--tl-accent-300);
}

.tl-ticker__viewport {
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	height: 100%;
	/* The strip runs off the right edge of the page rather than stopping at
	   the container, which is what makes it read as a band rather than as a
	   box with text in it. */
	mask-image: linear-gradient(to right, transparent 0, #000 24px);
	-webkit-mask-image: linear-gradient(to right, transparent 0, #000 24px);
}

/*
 * The viewport is focusable so a keyboard user can stop the strip, so it needs
 * a focus ring. Without one the only visible effect of tabbing to it would be
 * that the text stopped moving, which is not enough to tell somebody where they
 * are. `:focus-visible` rather than `:focus`, so clicking the band does not
 * draw a ring nobody asked for.
 */
.tl-ticker__viewport:focus { outline: none; }

.tl-ticker__viewport:focus-visible {
	outline: 2px solid var(--tl-accent-300);
	outline-offset: -2px;
}

.tl-ticker__track {
	display: flex;
	height: 100%;
	width: max-content;
	will-change: transform;
	animation: tl-ticker-run var(--tl-ticker-seconds, 40s) linear infinite;
}

@keyframes tl-ticker-run {
	from { transform: translate3d(0, 0, 0); }
	/* Exactly half, because the track is the list twice. */
	to   { transform: translate3d(-50%, 0, 0); }
}

/*
 * Pausing, without a button.
 *
 * Both states are here rather than in the script, so the strip stops on the
 * frame the pointer arrives instead of waiting for an event listener, and it
 * still stops if the script never runs. :focus-within covers the keyboard: a
 * link inside a moving strip is unusable, and tabbing to it stops the motion.
 */
.tl-ticker:hover .tl-ticker__track,
.tl-ticker:focus-within .tl-ticker__track {
	animation-play-state: paused;
}

.tl-ticker__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	align-items: center;
	height: 100%;
	flex: 0 0 auto;
}

.tl-ticker__item {
	display: flex;
	align-items: center;
	gap: var(--tl-space-2);
	font-size: 0.875rem;
	white-space: nowrap;
	padding-right: var(--tl-space-10);
}

.tl-ticker__kind {
	flex: 0 0 auto;
	font-size: 0.625rem;
	font-weight: 700;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	padding: 2px 7px;
	border-radius: var(--tl-radius-sm);
}

.tl-ticker__kind--offer     { background: rgba(217, 155,  50, 0.30); color: #f7dfae; }
.tl-ticker__kind--promotion { background: rgba(217, 155,  50, 0.30); color: #f7dfae; }
.tl-ticker__kind--event     { background: rgba(111, 178, 180, 0.28); color: #cfe9ea; }
.tl-ticker__kind--notice    { background: rgba(192,  57,  43, 0.30); color: #f4c9c3; }
.tl-ticker__kind--news      { background: rgba(255, 255, 255, 0.16); color: #fff; }
.tl-ticker__kind--welcome     { background: rgba( 30, 135,  75, 0.34); color: #c6ecd5; }
.tl-ticker__kind--information { background: rgba(111, 178, 180, 0.22); color: #d7edee; }

.tl-ticker__item a,
.tl-ticker__item span:not(.tl-ticker__kind) {
	color: rgba(255, 255, 255, 0.9);
	text-decoration: none;
}

.tl-ticker__item a { text-decoration: underline; text-underline-offset: 3px; }
.tl-ticker__item a:hover { color: #fff; }
.tl-ticker__item a:focus-visible { outline: 2px solid var(--tl-accent-300); outline-offset: 3px; }

/*
 * Reduced motion.
 *
 * Not a slower marquee: no marquee. The strip becomes an ordinary horizontal
 * scroller the reader moves themselves, and the duplicate copy is removed so
 * they do not scroll through the same five messages twice.
 */
@media (prefers-reduced-motion: reduce) {
	.tl-ticker__track {
		animation: none;
		width: auto;
	}

	.tl-ticker__viewport {
		overflow-x: auto;
		scrollbar-width: thin;
		mask-image: none;
		-webkit-mask-image: none;
	}

	.tl-ticker__list[data-tl-ticker-list="1"] { display: none; }
}

@media (max-width: 640px) {
	.tl-ticker__inner { height: 40px; gap: var(--tl-space-3); padding-left: var(--tl-space-4); }
	.tl-ticker__badge { display: none; }
	.tl-ticker__item { font-size: 0.8125rem; padding-right: var(--tl-space-8); }
}

/* -- The homepage hero slider ---------------------------------------------- */

/*
 * RESTORED, AND WHY IT VANISHED
 * -----------------------------
 * This whole block was deleted by accident in commit d8a6931. That commit's job
 * was to strip the play and pause controls out of the ticker and the room
 * slider, and the hero's rules sat immediately below the ticker's in this file.
 * The markup in inc/hero.php and the script in site.js were never touched, so
 * the section kept rendering: six slides, six dots, and no styling at all. An
 * unstyled .tl-hero has no height of its own, so the homepage showed a 51px
 * strip where a 620px photograph belongs, and it read as though the hero had
 * been removed.
 *
 * Nothing here is shared with .tl-slider, which is the rooms carousel further
 * down the page. Two separate components, two separate blocks, on purpose:
 * that is what stops one being edited into the other again. pms-test/css-check.py
 * now fails the build if any tl- class used in a template has no rule here.
 */

.tl-hero {
	position: relative;
	width: 100%;
	height: clamp(300px, 46vw, 620px);
	overflow: hidden;
	background: var(--tl-primary-900);
	isolation: isolate;
}

.tl-hero__track { position: absolute; inset: 0; }

.tl-hero__slide {
	position: absolute;
	inset: 0;
	opacity: 0;
	visibility: hidden;
	transition: opacity 700ms ease, visibility 700ms ease;
}

.tl-hero__slide.is-current { opacity: 1; visibility: visible; }

/*
 * Without JavaScript the first slide is the hero and the rest never appear. It
 * is still a full-bleed photograph, just not a moving one.
 */
.tl-hero:not([data-tl-hero-ready]) .tl-hero__slide:first-child {
	opacity: 1;
	visibility: visible;
}

.tl-hero__img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	display: block;
}

/*
 * THE BRANDED PANEL.
 *
 * This used to be a placeholder: a panel that announced a photograph had not
 * been uploaded yet. It is now the lodge's own hero design, and the difference
 * is not cosmetic. A placeholder tells every visitor the website is unfinished;
 * this tells them where they are. It is what the home page shows before the
 * photographs are taken, and it is a finished thing in its own right.
 *
 * It is built from the lodge's own greens, so it belongs to the brand rather
 * than standing in for it. Six tints rather than the three it had, because six
 * slides drawing from three variants visibly repeat.
 */
.tl-hero__panel {
	width: 100%;
	height: 100%;
	position: relative;
	overflow: hidden;
	background:
		radial-gradient(120% 90% at 22% 18%, rgba(255, 255, 255, 0.10) 0%, transparent 55%),
		radial-gradient(100% 80% at 82% 78%, rgba(255, 255, 255, 0.06) 0%, transparent 60%),
		linear-gradient(145deg, var(--tl-primary-700) 0%, var(--tl-primary-900) 100%);
}

.tl-hero__panel--1 {
	background:
		radial-gradient(120% 90% at 78% 22%, rgba(255, 255, 255, 0.10) 0%, transparent 55%),
		linear-gradient(145deg, var(--tl-primary-600) 0%, var(--tl-primary-800) 100%);
}

.tl-hero__panel--2 {
	background:
		radial-gradient(120% 90% at 50% 85%, rgba(255, 255, 255, 0.08) 0%, transparent 58%),
		linear-gradient(200deg, var(--tl-primary-800) 0%, #071f20 100%);
}

/*
 * Three, four and five lean on the brass accent rather than the green. Warm
 * light against the same deep ground, so the set reads as one lodge at
 * different hours rather than as five different sites.
 */
.tl-hero__panel--3 {
	background:
		radial-gradient(110% 85% at 18% 76%, rgba(255, 255, 255, 0.09) 0%, transparent 56%),
		radial-gradient(90% 70% at 70% 20%, var(--tl-accent-700) 0%, transparent 62%),
		linear-gradient(160deg, var(--tl-primary-800) 0%, var(--tl-primary-900) 100%);
}

.tl-hero__panel--4 {
	background:
		radial-gradient(130% 95% at 85% 60%, rgba(255, 255, 255, 0.07) 0%, transparent 54%),
		linear-gradient(120deg, var(--tl-primary-900) 0%, var(--tl-primary-700) 58%, var(--tl-accent-700) 130%);
}

.tl-hero__panel--5 {
	background:
		radial-gradient(100% 80% at 40% 12%, rgba(255, 255, 255, 0.11) 0%, transparent 52%),
		linear-gradient(215deg, var(--tl-primary-600) 0%, var(--tl-primary-900) 72%, #071f20 100%);
}

/*
 * Contour lines. The panel has to hold a 620px frame on its own, and a flat
 * wash at that size reads as a failed image rather than a deliberate one. These
 * are abstract by intent: evenly spaced level curves, the graphic language of a
 * map of the escarpment, not an attempt to look like a photograph of it. They
 * cost nothing, being two repeating gradients and no network request.
 */
.tl-hero__panel::before {
	content: "";
	position: absolute;
	inset: -20%;
	background-image:
		repeating-radial-gradient(
			ellipse 70% 46% at 28% 62%,
			rgba(255, 255, 255, 0.055) 0 1px,
			transparent 1px 34px
		),
		repeating-radial-gradient(
			ellipse 58% 40% at 78% 34%,
			rgba(255, 255, 255, 0.04) 0 1px,
			transparent 1px 28px
		);
	transform: rotate(-4deg);
}

.tl-hero__panel--1::before { transform: rotate(3deg) scale(1.1); }
.tl-hero__panel--2::before { transform: rotate(-8deg) scale(1.15); opacity: 0.8; }
.tl-hero__panel--3::before { transform: rotate(6deg) scale(1.05); opacity: 0.9; }
.tl-hero__panel--4::before { transform: rotate(-2deg) scale(1.2); opacity: 0.75; }
.tl-hero__panel--5::before { transform: rotate(9deg) scale(1.08); opacity: 0.85; }

/* A faint horizon, so the panel has a composition rather than being a wash. */
.tl-hero__panel::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	top: 62%;
	height: 1px;
	background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.18), transparent);
}

/*
 * The caption, and the scrim that makes it legible.
 *
 * THE SCRIM LIVES ON THE CAPTION, NOT ON THE SLIDE.
 * ------------------------------------------------
 * The whole argument at the top of `inc/hero.php` is that a lodge sells the
 * view and darkening it to make room for a sentence is the wrong trade. That
 * argument does not stop being true when somebody writes a sentence — it just
 * becomes their trade to make, on the slide they made it on. So this element
 * only exists on a slide that HAS words, and the gradient belongs to it. A
 * photograph with no heading is not dimmed to keep six slides consistent.
 *
 * The gradient is bottom-weighted rather than a flat wash, so the top of the
 * photograph — which is usually the sky, the roofline, the thing being sold —
 * stays as bright as it was.
 *
 * WHY THE TEXT SITS IN AN INNER BOX
 * ---------------------------------
 * `.tl-hero__caption` fills the slide so its gradient can; the inner box is
 * what carries the page's own gutter and its measure. Setting both on one
 * element means the gradient stops where the text stops, which draws a visible
 * rectangle over the photograph.
 */
.tl-hero__caption {
	position: absolute;
	inset: 0;
	z-index: 2;
	display: flex;
	align-items: flex-end;
	background: linear-gradient(
		to top,
		rgba(23, 20, 18, 0.72) 0%,
		rgba(23, 20, 18, 0.45) 32%,
		rgba(23, 20, 18, 0) 68%
	);
	pointer-events: none;
}

/*
 * The gradient does not take clicks — it covers the whole photograph, and a
 * transparent sheet over the arrows would swallow them. The text box takes them
 * back, because it holds a link.
 */
.tl-hero__captionbody {
	pointer-events: auto;
	width: 100%;
	max-width: var(--tl-measure-wide, 1200px);
	margin: 0 auto;
	padding: 0 var(--tl-space-5) calc(var(--tl-space-8) + var(--tl-space-5));
	color: #fff;
}

.tl-hero__title {
	margin: 0;
	font-family: var(--tl-font-display, var(--tl-font));
	font-size: clamp(1.6rem, 4.2vw, 3rem);
	line-height: 1.1;
	font-weight: 600;
	letter-spacing: -0.01em;
	text-wrap: balance;
	max-width: 18ch;
	text-shadow: 0 1px 12px rgba(23, 20, 18, 0.45);
}

.tl-hero__sub {
	margin: var(--tl-space-3) 0 0;
	font-size: clamp(0.95rem, 1.7vw, 1.15rem);
	line-height: 1.5;
	/*
	 * Not `color: rgba(255,255,255,.85)`. Over a photograph, a translucent
	 * white is a different colour on every slide; a solid near-white is the
	 * same sentence every time.
	 */
	color: #f2efec;
	max-width: 46ch;
	text-shadow: 0 1px 10px rgba(23, 20, 18, 0.45);
}

.tl-hero__cta {
	display: inline-block;
	margin: var(--tl-space-4) 0 0;
	padding: var(--tl-space-3) var(--tl-space-5);
	border-radius: var(--tl-radius);
	background: var(--tl-primary-500);
	color: #fff;
	font-weight: 600;
	font-size: 0.95rem;
	text-decoration: none;
	transition: background var(--tl-transition);
}

/*
 * A branded panel gets no scrim, whatever words it carries. The gradient is the
 * background; darkening a background to make text legible against it would mean
 * the background was the wrong one. Photographs still get theirs.
 */
.tl-hero__slide.is-panel .tl-hero__caption { background: none; }

/*
 * A single slide is not a carousel. Without arrows and dots the caption has no
 * furniture to clear, so it sits where it would on any full-bleed image.
 */
.tl-hero.is-single .tl-hero__captionbody { padding-bottom: var(--tl-space-8); }

.tl-hero__cta:hover,
.tl-hero__cta:focus { background: var(--tl-primary-700); color: #fff; }
.tl-hero__cta:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

/*
 * On a phone the dots sit at the bottom centre and the caption fills the
 * bottom, so the caption is lifted clear of them rather than printed over
 * them.
 */
@media (max-width: 560px) {
	.tl-hero__captionbody {
		padding-left: var(--tl-space-4);
		padding-right: var(--tl-space-4);
		padding-bottom: calc(var(--tl-space-8) + var(--tl-space-6));
	}

	.tl-hero__title { max-width: none; }
}

.tl-hero__arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 3;
	width: 44px;
	height: 44px;
	display: grid;
	place-items: center;
	border: 0;
	border-radius: var(--tl-radius-full);
	background: rgba(23, 20, 18, 0.34);
	color: #fff;
	cursor: pointer;
	backdrop-filter: blur(6px);
	transition: background var(--tl-transition);
}

.tl-hero__arrow:hover { background: rgba(23, 20, 18, 0.55); }
.tl-hero__arrow:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.tl-hero__arrow svg { width: 22px; height: 22px; }
.tl-hero__arrow--prev { left: var(--tl-space-5); }
.tl-hero__arrow--next { right: var(--tl-space-5); }

.tl-hero__dots {
	position: absolute;
	left: 0;
	right: 0;
	bottom: var(--tl-space-5);
	z-index: 3;
	display: flex;
	justify-content: center;
	gap: 0;
}

/*
 * The dot you can see is 8px. The button you can hit is 26px.
 *
 * An 8px control is fine for a mouse and useless for a thumb, and the
 * accessibility pass was flagging it as a very small target. Rather than draw a
 * bigger dot, which would put six 26px circles of furniture across a
 * photograph, the button carries the hit area and a pseudo-element carries the
 * dot. That keeps the indicators quiet, which is what the brief asks for.
 */
.tl-hero__dot {
	width: 26px;
	height: 26px;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
	display: grid;
	place-items: center;
}

.tl-hero__dot::before {
	content: "";
	display: block;
	width: 8px;
	height: 8px;
	border-radius: var(--tl-radius-full);
	background: rgba(255, 255, 255, 0.45);
	transition: background var(--tl-transition), width var(--tl-transition);
}

.tl-hero__dot:hover::before { background: rgba(255, 255, 255, 0.7); }
.tl-hero__dot:focus-visible { outline: 2px solid #fff; outline-offset: 2px; border-radius: var(--tl-radius-full); }

/* The current dot is wider as well as brighter: shape, not colour alone. */
.tl-hero__dot.is-current::before { background: #fff; width: 22px; }

@media (max-width: 880px) {
	.tl-hero { height: clamp(300px, 52vw, 520px); }
	.tl-hero__arrow { width: 38px; height: 38px; }
	.tl-hero__arrow--prev { left: var(--tl-space-3); }
	.tl-hero__arrow--next { right: var(--tl-space-3); }
}

/*
 * Below 560px the arrows go. A 38px control sitting over a 430px photograph on
 * a 375px screen covers a fifth of it, and the slider is swipeable and
 * autoplaying, so the arrows are the redundant way to reach a slide there. The
 * dots stay, and they remain the keyboard route to any slide directly.
 */
@media (max-width: 560px) {
	.tl-hero { height: clamp(300px, 62vw, 430px); }
	.tl-hero__arrow { display: none; }
}

@media (prefers-reduced-motion: reduce) {
	.tl-hero__slide { transition: none; }
}

/* -- Chips, icons and guest information ------------------------------------ */

/*
 * THE .tl-overview RULES WENT WITH THE MARKUP.
 *
 * They styled the lodge overview block on the front page -- a two-column grid,
 * its prose column, its button row, and the three-figure strip set against a
 * left rule. The block was removed from front-page.php as oversized and
 * repetitive, and nothing else on the site ever used these classes, so the
 * rules were left styling nothing.
 *
 * Deleted rather than kept in case the block returns: a stylesheet that
 * describes markup which no longer exists is a map of a building that has been
 * pulled down, and the next person reading it cannot tell which parts are still
 * standing. Git has both halves together if the block is ever wanted back.
 */

/*
 * ONE ICON, ONE DEFINITION
 *
 * Inline SVG inheriting currentColor, so an icon takes the colour of whatever
 * it sits inside and there is no second palette to keep in step.
 */
.tl-icon {
	width: 22px;
	height: 22px;
	display: block;
}

/*
 * What the lodge offers.
 *
 * The chip strip that used to live here printed the room_amenity taxonomy;
 * those terms were seeded by the demo importer and are no longer shown to
 * guests. These boxes carry longer, verified statements, so they are cards
 * rather than pills.
 *
 * `auto-fit` with a `min(100%, …)` floor: the floor is what stops a 240px
 * track overflowing a container narrower than 240px, which is a real case at
 * 375px once the container padding is taken off.
 */
/*
 * Flex-wrap rather than an auto-fit grid, and the reason is the orphan.
 *
 * The number of boxes is not fixed — it depends on which settings the lodge has
 * filled in — and an auto-fit grid gives every track the same width, so six
 * boxes in a five-track row leave one stranded on a line of its own with four
 * empty columns beside it. That reads as a layout that broke rather than one
 * that adapted. With flex the items on the final row grow to fill it, so the
 * section looks deliberate at four boxes, at five, and at seven.
 */
.tl-services {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-4);
}

.tl-service {
	/* Grow to fill the last row; the basis is the point at which a box stops
	   being readable and has to wrap to its own line. */
	flex: 1 1 240px;
	min-width: 0;
	padding: var(--tl-space-6);
	background: var(--tl-surface);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-md);
	box-shadow: var(--tl-shadow-xs);
	transition: box-shadow var(--tl-transition), border-color var(--tl-transition), transform var(--tl-transition);
}

.tl-service:hover {
	border-color: var(--tl-primary-200);
	box-shadow: var(--tl-shadow-md);
	transform: translateY(-2px);
}

/*
 * The head row: the icon, and a small piece of metadata pushed to the far
 * corner. That corner figure is what stops a row of these reading as four
 * paragraphs with pictures. Every one is counted or stored, never decorative.
 */
.tl-service__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tl-space-3);
	margin-bottom: var(--tl-space-4);
}

.tl-service__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 46px;
	height: 46px;
	flex: 0 0 auto;
	border-radius: var(--tl-radius);
	background: var(--tl-primary-50);
	color: var(--tl-primary-600);
	transition: background var(--tl-transition), color var(--tl-transition);
}

.tl-service:hover .tl-service__icon {
	background: var(--tl-primary-600);
	color: #fff;
}

.tl-service__meta {
	font-size: 0.68rem;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--tl-text-subtle);
	text-align: right;
	/* A long stored value (a phone number, an address line) must not push the
	   icon out of the card. */
	min-width: 0;
	overflow-wrap: anywhere;
}

.tl-service__title {
	margin: 0 0 var(--tl-space-2);
	font-family: var(--tl-font-display);
	font-size: 1.0625rem;
	line-height: 1.25;
	letter-spacing: -0.01em;
	color: var(--tl-text);
}

.tl-service__text {
	margin: 0;
	font-size: 0.9rem;
	line-height: 1.55;
	color: var(--tl-text-muted);
}

/* On a room page the same boxes sit in a narrower column and carry less air. */
.tl-services--compact { margin-top: var(--tl-space-4); }
.tl-services--compact .tl-service { padding: var(--tl-space-4); }
.tl-services--compact .tl-service__icon { width: 38px; height: 38px; }
.tl-services--compact .tl-service__head { margin-bottom: var(--tl-space-3); }
.tl-services--compact .tl-service__meta { display: none; }
.tl-services--compact .tl-service__text { font-size: 0.85rem; }

/*
 * Good to know.
 *
 * Check-in, check-out, where we are, how to book, how to reach somebody. Each
 * card exists only when the value behind it is stored, so the grid has to look
 * right at four cards and at seven — hence auto-fit rather than a fixed count.
 */
.tl-know {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-4);
}

.tl-know__card {
	position: relative;
	/* Same reason as the service boxes: the card count depends on what the
	   lodge has stored, so the last row grows to fill rather than stranding a
	   single card beside four empty columns. */
	/*
	 * 190px, not 210. The lodge's six cards need 6 × 190 + five gaps = 1220px,
	 * which fits the 1280px container — so they make one clean row and grow to
	 * fill it. At 210 the sixth card wrapped and stretched across the full
	 * width on its own, which looked like a mistake rather than a layout.
	 */
	flex: 1 1 190px;
	min-width: 0;
	padding: var(--tl-space-5);
	background: var(--tl-surface);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-md);
	box-shadow: var(--tl-shadow-xs);
	transition: box-shadow var(--tl-transition), border-color var(--tl-transition);
}

.tl-know__card:hover { border-color: var(--tl-primary-200); box-shadow: var(--tl-shadow-sm); }

.tl-know__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 34px;
	height: 34px;
	margin-bottom: var(--tl-space-3);
	border-radius: var(--tl-radius-full);
	background: var(--tl-accent-100);
	color: var(--tl-accent-700);
}

.tl-know__label {
	margin: 0 0 3px;
	font-size: 0.7rem;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--tl-text-muted);
}

.tl-know__value {
	margin: 0;
	font-size: 0.9375rem;
	line-height: 1.45;
	color: var(--tl-text);
	/*
	 * An address or a URL somebody pasted has no spaces to break at, and a
	 * 210px card is not wide enough to absorb one. Break it rather than let it
	 * push the grid sideways.
	 */
	overflow-wrap: anywhere;
}

.tl-know__value a { color: var(--tl-primary-600); text-decoration: none; }
.tl-know__value a:hover { text-decoration: underline; }

/*
 * The whole card is the target, not just the four words of the link. The
 * anchor keeps its own focus ring — this is a hit area, not a focus style.
 */
.tl-know__value a::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
}

.tl-section__more { margin-top: var(--tl-space-8); text-align: center; }

/* The .tl-overview narrow-screen rules were removed here with the block itself;
   see the note where the base rules stood. */

/* ==========================================================================
   6b. QUICK AVAILABILITY SEARCH
   ==========================================================================

   The card that sits across the seam between the photographs and the page. It
   is a plain GET form aimed at the booking screen — no availability logic lives
   here, nothing is fetched, and it works with scripting switched off. Lifting it
   onto the hero is what makes the first thing a visitor can do the thing the
   lodge wants them to do.
   ========================================================================== */

.tl-quicksearch {
	position: relative;
	z-index: 5;
	/* Overlaps the hero by half the card, then gives the page below its own air. */
	margin-top: -44px;
	margin-bottom: var(--tl-section-y-tight);
}

/*
 * The brand line, under the hero and the search card.
 *
 * clamp() rather than a breakpoint: it is one line of display type and its job
 * is to be the largest thing on the page at every width without ever being wide
 * enough to wrap awkwardly at 375px.
 */
.tl-slogan {
	margin-bottom: var(--tl-section-y-tight);
	text-align: center;
}

.tl-slogan__text {
	margin: 0 auto;
	max-width: 24ch;
	font-family: var(--tl-font-display);
	font-size: clamp(1.5rem, 3.4vw, 2.5rem);
	line-height: 1.15;
	letter-spacing: -0.02em;
	color: var(--tl-text);
	text-wrap: balance;
}

.tl-quicksearch__card {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: var(--tl-space-4);
	padding: var(--tl-space-5) var(--tl-space-6);
	background: var(--tl-surface);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-md);
	box-shadow: var(--tl-shadow-lg);
}

.tl-quicksearch__lead {
	flex: 0 0 auto;
	display: flex;
	flex-direction: column;
	gap: 2px;
	padding-right: var(--tl-space-5);
	border-right: 1px solid var(--tl-border);
	align-self: stretch;
	justify-content: center;
}

.tl-quicksearch__eyebrow {
	font-size: 0.6875rem;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--tl-primary-500);
}

.tl-quicksearch__lead strong {
	font-family: var(--tl-font-display);
	font-size: 1.15rem;
	font-weight: 600;
	letter-spacing: -0.01em;
	white-space: nowrap;
}

.tl-quicksearch__field {
	flex: 1 1 150px;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 5px;
}

.tl-quicksearch__field--narrow { flex: 0 1 104px; }

.tl-quicksearch__field label {
	font-size: 0.72rem;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--tl-text-muted);
}

.tl-quicksearch__field input {
	width: 100%;
	min-width: 0;
	height: 44px;
	padding: 0 var(--tl-space-3);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-sm);
	background: var(--tl-surface);
	color: var(--tl-text);
	font: inherit;
	font-size: 0.9375rem;
}

.tl-quicksearch__field input:focus-visible {
	outline: none;
	border-color: var(--tl-primary-400);
	box-shadow: var(--tl-ring);
}

.tl-quicksearch__go { flex: 0 0 auto; min-height: 44px; padding-inline: var(--tl-space-6); }

@media (max-width: 900px) {
	/* Below this the card stops overlapping: half of it would sit off the
	   bottom of a short hero, and the seam looks broken rather than layered. */
	.tl-quicksearch { margin-top: var(--tl-space-5); }
	.tl-quicksearch__card { padding: var(--tl-space-4); gap: var(--tl-space-3); }
	.tl-quicksearch__lead {
		flex: 1 1 100%;
		border-right: 0;
		padding-right: 0;
		padding-bottom: var(--tl-space-2);
		border-bottom: 1px solid var(--tl-border);
	}
	.tl-quicksearch__field { flex: 1 1 130px; }
	.tl-quicksearch__field--narrow { flex: 1 1 90px; }
	.tl-quicksearch__go { flex: 1 1 100%; }
}

/* ==========================================================================
   7. CARDS / ROOM GRID
   ==========================================================================

   auto-fill at 280px gives four columns at 1320px, three at about 1080, two at
   740 and one on a phone — the 2–4 the brief asks for, without a breakpoint per
   step. `min(100%, …)` is what keeps the single column from overflowing at
   320px, where 280 + the container padding is wider than the screen.
   ========================================================================== */

.tl-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
	gap: var(--tl-space-5);
}

.tl-card {
	background: var(--tl-surface);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-md);
	overflow: hidden;
	display: flex;
	flex-direction: column;
	box-shadow: var(--tl-shadow-xs);
	transition: box-shadow var(--tl-transition), transform var(--tl-transition);
}

.tl-card:hover { box-shadow: var(--tl-shadow-md); transform: translateY(-2px); }

.tl-card__media {
	position: relative;
	aspect-ratio: 4 / 3;
	background: var(--tl-neutral-100);
	overflow: hidden;
}
.tl-card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform 400ms ease; }
.tl-card:hover .tl-card__media img { transform: scale(1.03); }

.tl-card__body { padding: var(--tl-space-5); display: flex; flex-direction: column; gap: var(--tl-space-2); flex: 1 1 auto; }
.tl-card__title { margin: 0; font-size: 1.15rem; font-weight: 600; }
.tl-card__title a { color: inherit; text-decoration: none; }
.tl-card__meta { display: flex; flex-wrap: wrap; gap: var(--tl-space-1) var(--tl-space-4); font-size: 0.82rem; color: var(--tl-text-muted); }
.tl-card__excerpt { color: var(--tl-text-muted); font-size: 0.92rem; margin: 0; }

/*
 * What the room includes. A tick per item rather than a comma list, because a
 * guest comparing two rooms is scanning for a difference, and a difference is
 * far easier to see down a column than along a sentence.
 *
 * The tick is a pseudo-element, so it is decoration and never reaches a screen
 * reader, which hears an ordinary list. It is also not a colour-only signal:
 * the item is present or it is absent from the list.
 */
.tl-card__amenities {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-1) var(--tl-space-4);
	margin: var(--tl-space-3) 0 0;
	padding: 0;
	list-style: none;
	font-size: 0.85rem;
	color: var(--tl-text);
}

.tl-card__amenities li { display: flex; align-items: baseline; gap: var(--tl-space-2); }

.tl-card__amenities li::before {
	content: "";
	flex: none;
	width: 0.42em;
	height: 0.72em;
	border: solid var(--tl-primary-500);
	border-width: 0 2px 2px 0;
	transform: translateY(-0.1em) rotate(45deg);
}

.tl-card__foot {
	margin-top: auto;
	padding-top: var(--tl-space-4);
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tl-space-3);
	border-top: 1px solid var(--tl-border);
}

.tl-price { font-size: 1.1rem; font-weight: 700; color: var(--tl-text); font-variant-numeric: tabular-nums; }
.tl-price small { display: block; font-size: 0.72rem; font-weight: 400; color: var(--tl-text-muted); }

/*
 * The amenity chip styles that used to live here are gone along with the
 * markup: the room_amenity terms were demo-importer output, identical on all
 * thirteen rooms and one of them untrue, so the public site no longer prints
 * that taxonomy at all. See .tl-services for what stands in its place.
 */

.tl-card__actions { display: flex; gap: var(--tl-space-2); flex: 0 0 auto; }

/* ==========================================================================
   THE ROOMS RAIL
   Four cards at a time on a desktop, sliding through all of them.
   ========================================================================== */

/*
 * Scroll-snap, not a JavaScript track.
 *
 * The browser does the paging, so this works before the script runs and keeps
 * working if it never does — swipe, trackpad, arrow keys and the scrollbar are
 * all native — and there is no transform to fall out of step with the layout
 * when a card's height changes.
 *
 * The card width is a percentage of the RAIL, not of the viewport, so the count
 * on screen is one number in one place: four here means four, whatever else is
 * on the page.
 */
.tl-slider {
	position: relative;
	--tl-slider-gap: var(--tl-space-5);
	--tl-slider-per: 1;
}

.tl-slider__rail {
	display: flex;
	gap: var(--tl-slider-gap);
	margin: 0;
	padding: 0 0 var(--tl-space-3);
	list-style: none;
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scroll-snap-type: x mandatory;
	/*
	 * No `scroll-behavior: smooth` here on purpose. The script asks for smooth
	 * per call via scrollTo(), and having it declared in CSS as well made the
	 * two fight: a programmatic scroll animated while the mandatory snap kept
	 * re-evaluating, and the rail snapped back to where it started.
	 */
	/* Containing block for what it clips: an absolutely positioned descendant
	   escapes a static scroller and takes the page sideways with it. */
	position: relative;
}

.tl-slider__rail:focus-visible {
	outline: 2px solid var(--tl-primary-600);
	outline-offset: 4px;
	border-radius: var(--tl-radius);
}

.tl-slider__item {
	flex: 0 0 calc(
		(100% - (var(--tl-slider-per) - 1) * var(--tl-slider-gap)) / var(--tl-slider-per)
	);
	min-width: 0;
	scroll-snap-align: start;
	display: flex;
}

.tl-slider__item > .tl-card { width: 100%; }

/* One card on a phone, two on a large phone or small tablet, three on a
   tablet, four from a laptop up. */
/*
 * HOW MANY ROOMS ARE ON SCREEN AT ONCE
 *
 * The brief asks for four on a desktop, three on a tablet, two on a large
 * phone and one on a small one, and 760px is where the tablet begins: an iPad
 * held upright is 768 wide, and it was landing in the two-up band and showing a
 * tablet the same number of rooms as a phone.
 *
 * Three at 768 leaves roughly 229px of card, measured, which holds the
 * photograph, the room's name, its rate and both actions. The actions stack
 * rather than sitting side by side at that width, which is what the 7rem basis
 * on .tl-card--room .tl-card__foot > * is for.
 */
@media (min-width: 560px)  { .tl-slider { --tl-slider-per: 2; } }
@media (min-width: 760px)  { .tl-slider { --tl-slider-per: 3; } }
@media (min-width: 1180px) { .tl-slider { --tl-slider-per: 4; } }

/* An author `display` outranks the browser's own `[hidden] { display: none }`,
   so these two would have been visible while hidden — announced to a screen
   reader as absent and drawn on the page anyway. The script hides them when
   every room already fits. */
.tl-slider__controls[hidden],
.tl-slider__dots[hidden] { display: none; }

.tl-slider__controls {
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: var(--tl-space-3);
	margin-top: var(--tl-space-3);
}


.tl-slider__arrow {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* 44px: a finger, not a mouse pointer. */
	width: 44px;
	height: 44px;
	padding: 0;
	border: 1px solid var(--tl-border-strong);
	border-radius: var(--tl-radius-full);
	background: var(--tl-surface);
	color: var(--tl-text);
	cursor: pointer;
	transition: background var(--tl-transition), border-color var(--tl-transition), opacity var(--tl-transition);
}

.tl-slider__arrow svg { width: 20px; height: 20px; }
.tl-slider__arrow:hover:not(:disabled) { background: var(--tl-neutral-100); border-color: var(--tl-neutral-400); }
.tl-slider__arrow:focus-visible { outline: 2px solid var(--tl-primary-600); outline-offset: 2px; }

/* Disabled rather than hidden: a control that vanishes at the end of the rail
   makes the row jump, and the jump is more confusing than the dimming. */
.tl-slider__arrow:disabled { opacity: 0.35; cursor: default; }

.tl-slider__count {
	margin: 0;
	font-size: 0.82rem;
	color: var(--tl-text-muted);
	font-variant-numeric: tabular-nums;
}

.tl-slider__dots {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--tl-space-2);
	margin-top: var(--tl-space-3);
}

.tl-slider__dot {
	/* A 26px hit area around a 8px mark: the target is what a finger needs,
	   the dot is what the eye needs, and they are not the same size. */
	position: relative;
	width: 26px;
	height: 26px;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

.tl-slider__dot::before {
	content: "";
	position: absolute;
	inset: 9px;
	border-radius: var(--tl-radius-full);
	background: var(--tl-neutral-300);
	transition: background var(--tl-transition), inset var(--tl-transition);
}

.tl-slider__dot.is-current::before {
	background: var(--tl-primary-600);
	inset: 8px 5px;
	border-radius: var(--tl-radius-full);
}

.tl-slider__dot:focus-visible { outline: 2px solid var(--tl-primary-600); outline-offset: 0; border-radius: var(--tl-radius-full); }

/* ==========================================================================
   ROOM CARD
   ========================================================================== */

/* Room number and price on one line, because they are the two things somebody
   is comparing across four cards, and the price must not be hunted for. */
.tl-card__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--tl-space-3);
	flex-wrap: wrap;
}

.tl-price--card {
	display: flex;
	align-items: baseline;
	gap: 4px;
	white-space: nowrap;
	font-variant-numeric: tabular-nums;
}

.tl-price--card small { font-size: 0.7rem; color: var(--tl-text-muted); font-weight: 400; }

/* Said on the picture itself, where the eye already is when it notices the
   picture is not a photograph. */
.tl-card__nophoto {
	position: absolute;
	left: var(--tl-space-3);
	bottom: var(--tl-space-3);
	margin: 0;
	padding: 3px var(--tl-space-2);
	border-radius: var(--tl-radius-sm);
	background: rgba(0, 0, 0, 0.55);
	color: #fff;
	font-size: 0.68rem;
	letter-spacing: 0.02em;
}

/*
 * Room cards sit four across in the slider and in a grid on the archive, so
 * they have to be the height of their row rather than the height of their own
 * copy. Otherwise a room with a two-line description leaves a step in the row.
 */
.tl-card--room { height: 100%; }

.tl-card--room .tl-card__foot {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-2);
}

/*
 * Three controls now — View photos, View room, Book now — in a card that is
 * only about 250px of content wide once four of them share a 1280px row.
 *
 * The basis is what decides how they break, and it was measured rather than
 * guessed. At 9rem all three wrapped onto separate lines, giving every card a
 * 141px stack of three identical full-width buttons; at 7rem two fit side by
 * side and "Book now" takes the line below on its own — which is the right
 * answer anyway, because it is the primary action and now looks like it.
 * Checked at 375 through 1920: the break is the same at every width.
 */
.tl-card--room .tl-card__foot > * { flex: 1 1 7rem; text-align: center; justify-content: center; }

.tl-card__photos-count {
	margin-left: 4px;
	padding: 0 5px;
	border-radius: var(--tl-radius-full);
	background: var(--tl-neutral-100);
	font-size: 0.68rem;
	color: var(--tl-neutral-600);
	font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   ROOM GALLERY
   ========================================================================== */

.tl-gallery {
	position: fixed;
	inset: 0;
	z-index: 200;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--tl-space-4);
}

/* An author `display` beats the browser's own [hidden] rule, so the attribute
   stops working the moment this block sets one. Stated here rather than
   discovered later. */
.tl-gallery[hidden] { display: none; }

.tl-gallery__backdrop {
	position: absolute;
	inset: 0;
	background: rgba(6, 20, 20, 0.82);
}

.tl-gallery__panel {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--tl-space-3);
	width: min(100%, 1040px);
	max-height: 92vh;
	padding: var(--tl-space-4);
	border-radius: var(--tl-radius);
	background: var(--tl-surface);
	box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);
	overflow-y: auto;
}

.tl-gallery__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tl-space-3);
}

.tl-gallery__title { margin: 0; font-weight: 600; font-size: 1.05rem; }

.tl-gallery__close {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-full);
	background: var(--tl-surface);
	color: var(--tl-text);
	cursor: pointer;
}

.tl-gallery__close svg { width: 20px; height: 20px; }
.tl-gallery__close:hover { background: var(--tl-neutral-100); }
.tl-gallery__close:focus-visible { outline: 2px solid var(--tl-primary-600); outline-offset: 2px; }

.tl-gallery__stage {
	position: relative;
	background: var(--tl-neutral-100);
	border-radius: var(--tl-radius-sm);
	overflow: hidden;
}

.tl-gallery__figure { margin: 0; }
.tl-gallery__figure[hidden] { display: none; }

.tl-gallery__figure img {
	display: block;
	width: 100%;
	max-height: 64vh;
	object-fit: contain;
	background: var(--tl-neutral-100);
}

.tl-gallery__arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	border: 0;
	border-radius: var(--tl-radius-full);
	background: rgba(255, 255, 255, 0.92);
	color: var(--tl-text);
	cursor: pointer;
}

.tl-gallery__arrow svg { width: 22px; height: 22px; }
.tl-gallery__arrow--prev { left: var(--tl-space-3); }
.tl-gallery__arrow--next { right: var(--tl-space-3); }
.tl-gallery__arrow:hover { background: #fff; }
.tl-gallery__arrow:focus-visible { outline: 2px solid var(--tl-primary-600); outline-offset: 2px; }

.tl-gallery__count {
	margin: 0;
	text-align: center;
	font-size: 0.82rem;
	color: var(--tl-text-muted);
	font-variant-numeric: tabular-nums;
}

.tl-gallery__thumbs {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-2);
	justify-content: center;
}

.tl-gallery__thumb {
	width: 68px;
	height: 52px;
	padding: 0;
	border: 2px solid transparent;
	border-radius: var(--tl-radius-sm);
	background: var(--tl-neutral-100);
	overflow: hidden;
	cursor: pointer;
}

.tl-gallery__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tl-gallery__thumb.is-current { border-color: var(--tl-primary-600); }
.tl-gallery__thumb:focus-visible { outline: 2px solid var(--tl-primary-600); outline-offset: 2px; }

.tl-gallery__empty {
	padding: var(--tl-space-6) var(--tl-space-4);
	text-align: center;
	border: 1px dashed var(--tl-border-strong);
	border-radius: var(--tl-radius-sm);
	color: var(--tl-text-muted);
}

.tl-gallery__empty-title {
	margin: 0 0 var(--tl-space-2);
	font-weight: 600;
	color: var(--tl-text);
}

.tl-gallery__empty p { margin: 0; max-width: 46ch; margin-inline: auto; }

/* The page behind a full-screen dialogue must not scroll. On a phone that is
   the difference between a gallery and a trap. */
body.tl-noscroll { overflow: hidden; }

/* ==========================================================================
   ROOM PAGE
   ========================================================================== */

.tl-room__title { margin-bottom: var(--tl-space-4); }

.tl-room__gallerybar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tl-space-3);
	margin-top: var(--tl-space-3);
}

.tl-room__gallerynote { margin: 0; font-size: 0.86rem; color: var(--tl-text-muted); }

.tl-room__subhead {
	margin: var(--tl-space-6) 0 var(--tl-space-3);
	font-size: 1.05rem;
	font-weight: 600;
}

.tl-room__nodesc {
	margin-top: var(--tl-space-5);
	padding: var(--tl-space-4);
	border: 1px dashed var(--tl-border);
	border-radius: var(--tl-radius-sm);
	color: var(--tl-text-muted);
	font-size: 0.9rem;
}

.tl-panel__note { font-size: 0.86rem; color: var(--tl-text-muted); margin-top: var(--tl-space-4); }

/* The foot wraps rather than squeezing the price against two buttons when the
   card is at its narrowest. */
@media (max-width: 380px) {
	.tl-card__foot { flex-wrap: wrap; }
	.tl-card__actions { width: 100%; }
	.tl-card__actions .tl-btn { flex: 1 1 auto; }
}

/* ==========================================================================
   8. BOOKING FLOW
   ========================================================================== */

/*
 * Wide enough for three room cards and the summary column beside them:
 * 3 × 232 + 2 gaps + the 320px basket and its gap. Below this the grid drops to
 * two cards and then to one, which is the behaviour the auto-fill is for.
 */
/*
 * The booking application gets the full container, not a narrower one.
 *
 * 1120px was a compromise sized around three 232px cards. With the room cards
 * now on the same 280px auto-fill grid as the rest of the site, the flow wants
 * the whole measure: four cards and the summary at 1320, three and the summary
 * at about 1100, and so on down. Capping it below the container was what left
 * the wide slack on either side of the screenshots.
 */
.tl-booking { max-width: none; margin-inline: auto; }

.tl-steps {
	display: flex;
	gap: var(--tl-space-2);
	list-style: none;
	margin: 0 0 var(--tl-space-8);
	padding: 0;
	/*
	 * Grid children default to min-width:auto, which is what made the previous
	 * booking header overflow at 320px no matter what max-width was set. Flex
	 * plus an explicit min-width of 0 is the fix, not a smaller font.
	 */
	flex-wrap: wrap;
}

.tl-step {
	flex: 1 1 0;
	min-width: 0;
	display: flex;
	align-items: center;
	gap: var(--tl-space-2);
	padding: var(--tl-space-3);
	border-radius: var(--tl-radius);
	background: var(--tl-surface);
	border: 1px solid var(--tl-border);
	font-size: 0.82rem;
	color: var(--tl-text-muted);
}

.tl-step__num {
	display: grid;
	place-items: center;
	width: 24px; height: 24px;
	flex: 0 0 24px;
	border-radius: 50%;
	background: var(--tl-neutral-100);
	color: var(--tl-neutral-600);
	font-size: 0.75rem;
	font-weight: 700;
}

.tl-step__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tl-step.is-current { border-color: var(--tl-primary-400); background: var(--tl-primary-50); color: var(--tl-primary-700); font-weight: 600; }
.tl-step.is-current .tl-step__num { background: var(--tl-primary-600); color: #fff; }
.tl-step.is-done .tl-step__num { background: var(--tl-success-500); color: #fff; }
.tl-step.is-done .tl-step__num::after { content: "✓"; }
.tl-step.is-done .tl-step__num { font-size: 0.7rem; }

/*
 * On a phone, four equal steps left every label a single letter and an ellipsis
 * — "1 D…  2 R…  3 Y…  4 C…" — which tells the guest nothing. Below this width
 * only the step you are on is named; the rest keep their numbers, and their
 * labels are moved off-screen rather than removed, so a screen reader still
 * announces "Rooms" and "Confirm".
 */
@media ( max-width: 560px ) {
	.tl-step { flex: 0 0 auto; }

	.tl-step:not( .is-current ) .tl-step__label {
		position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
		overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
	}

	.tl-step.is-current { flex: 1 1 auto; }
}

.tl-panel {
	background: var(--tl-surface);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-md);
	padding: var(--tl-space-6);
	box-shadow: var(--tl-shadow-xs);
}

.tl-panel + .tl-panel { margin-top: var(--tl-space-5); }
.tl-panel__title { margin: 0 0 var(--tl-space-1); font-size: 1.1rem; }
.tl-panel__hint { color: var(--tl-text-muted); font-size: 0.88rem; margin-bottom: var(--tl-space-5); }

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

.tl-fields {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
	gap: var(--tl-space-4);
}

.tl-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.tl-field--full { grid-column: 1 / -1; }
.tl-field > label { font-size: 0.8rem; font-weight: 600; color: var(--tl-neutral-600); }

.tl-field input, .tl-field select, .tl-field textarea {
	width: 100%;
	min-height: 46px;
	padding: 10px var(--tl-space-3);
	border: 1px solid var(--tl-neutral-300);
	border-radius: var(--tl-radius-sm);
	background: var(--tl-surface);
	font: inherit;
	font-size: 0.95rem;
	color: var(--tl-text);
	transition: border-color var(--tl-transition), box-shadow var(--tl-transition);
}

.tl-field textarea { min-height: 96px; resize: vertical; }

.tl-field input:focus, .tl-field select:focus, .tl-field textarea:focus {
	border-color: var(--tl-primary-500);
	box-shadow: var(--tl-ring);
	outline: none;
}

.tl-field input[aria-invalid="true"], .tl-field select[aria-invalid="true"] { border-color: var(--tl-danger-500); }
.tl-field__error { font-size: 0.78rem; color: var(--tl-danger-700); font-weight: 500; }

/* -- Room results ---------------------------------------------------------- */

/*
 * Room cards, not room rows.
 *
 * The previous layout was one full-width row per room inside a column that was
 * already narrowed by the sticky basket, so every room got a 64px thumbnail and
 * a line of text — the same amount of space a table row gets, for the decision
 * the whole page exists to support. Cards give the photograph a real frame and
 * fit two or three across at desktop widths.
 */
.tl-results {
	display: grid;
	/* 212px is the width at which three cards fit the 726px column left beside
	   the summary. A larger minimum drops it to two and wastes a third of the
	   row; a smaller one lets four crowd in and the photograph stops working. */
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 248px), 1fr));
	gap: var(--tl-space-4);
	align-items: start;
}

.tl-roomcard {
	display: flex;
	flex-direction: column;
	background: var(--tl-surface);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-md);
	overflow: hidden;
	transition: box-shadow var(--tl-transition), border-color var(--tl-transition);
	min-width: 0;
}

.tl-roomcard:hover { box-shadow: var(--tl-shadow-md); }

.tl-roomcard.is-selected {
	border-color: var(--tl-primary-500);
	box-shadow: 0 0 0 2px var(--tl-primary-100), var(--tl-shadow-sm);
}

/*
 * A fixed 16:10 frame. Photography arrives at whatever proportion the camera
 * happened to be held at, and a grid of cards whose images are each a different
 * height reads as broken rather than as varied. object-fit does the cropping.
 */
.tl-roomcard__media {
	position: relative;
	aspect-ratio: 16 / 10;
	background: var(--tl-neutral-100);
	overflow: hidden;
}

.tl-roomcard__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	display: block;
}

.tl-roomcard__flag {
	position: absolute;
	top: var(--tl-space-2);
	left: var(--tl-space-2);
	font-size: 0.625rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	padding: 3px 8px;
	border-radius: var(--tl-radius-full);
	background: var(--tl-primary-700);
	color: #fff;
}

.tl-roomcard__body {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: var(--tl-space-4);
	flex: 1 1 auto;
	min-width: 0;
}

.tl-roomcard__name {
	margin: 0;
	font-size: 1.0625rem;
	font-weight: 600;
	letter-spacing: -0.01em;
}

.tl-roomcard__meta {
	margin: 0;
	font-size: 0.8125rem;
	color: var(--tl-text-muted);
	line-height: 1.45;
}

.tl-roomcard__amenities {
	margin: 2px 0 0;
	font-size: 0.75rem;
	color: var(--tl-text-subtle);
	line-height: 1.45;
}

.tl-roomcard__foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tl-space-3);
	padding: var(--tl-space-3) var(--tl-space-4) var(--tl-space-4);
	border-top: 1px solid var(--tl-border);
	margin-top: auto;
}

.tl-roomcard__price {
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	line-height: 1.15;
	min-width: 0;
}

.tl-roomcard__price small {
	display: block;
	font-weight: 400;
	font-size: 0.7rem;
	color: var(--tl-text-muted);
}

.tl-roomcard__foot .tl-btn { flex: 0 0 auto; }

/* The generated stand-in for a room with no photograph. Named, so it is
   obvious which room it belongs to, and tinted from the room id so a grid of
   them is not one flat colour repeated. */
.tl-roomcard__placeholder {
	width: 100%;
	height: 100%;
	display: grid;
	place-items: center;
	padding: var(--tl-space-4);
	text-align: center;
	background: linear-gradient(145deg, var(--tl-primary-100), var(--tl-primary-200));
	color: var(--tl-primary-800);
	font-size: 0.75rem;
	font-weight: 600;
	letter-spacing: 0.04em;
}

.tl-roomcard__placeholder--1 { background: linear-gradient(145deg, #e7efe6, #cadfd4); color: #24503c; }
.tl-roomcard__placeholder--2 { background: linear-gradient(145deg, var(--tl-accent-100), #f2dcb4); color: #7a5312; }
.tl-roomcard__placeholder--3 { background: linear-gradient(145deg, var(--tl-neutral-100), var(--tl-neutral-200)); color: var(--tl-neutral-700); }

@media (max-width: 520px) {
	.tl-results { grid-template-columns: minmax(0, 1fr); }
}

.tl-basket {
	position: sticky;
	top: calc(var(--tl-header-h) + var(--tl-space-4));
	align-self: start;
}

.tl-basket__list { list-style: none; margin: 0 0 var(--tl-space-4); padding: 0; display: flex; flex-direction: column; gap: var(--tl-space-2); }

.tl-basket__item {
	display: grid;
	grid-template-columns: 1fr auto auto;
	gap: var(--tl-space-3);
	align-items: center;
	padding: var(--tl-space-3);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-sm);
	background: var(--tl-neutral-25);
	font-size: 0.9rem;
}

.tl-basket__name { font-weight: 600; min-width: 0; }
.tl-basket__name small { display: block; font-weight: 400; font-size: 0.76rem; color: var(--tl-text-muted); }
.tl-basket__price { font-variant-numeric: tabular-nums; white-space: nowrap; }

.tl-basket__remove {
	border: 0;
	background: none;
	color: var(--tl-danger-500);
	font-size: 0.82rem;
	font-weight: 600;
	cursor: pointer;
	padding: 4px 6px;
	border-radius: var(--tl-radius-sm);
}
.tl-basket__remove:hover { background: var(--tl-danger-50); }

.tl-total { border-top: 2px solid var(--tl-neutral-200); padding-top: var(--tl-space-4); }
.tl-total__row { display: flex; justify-content: space-between; gap: var(--tl-space-4); font-size: 0.9rem; color: var(--tl-text-muted); margin-bottom: 6px; }
.tl-total__row b { color: var(--tl-text); font-variant-numeric: tabular-nums; }
.tl-total__row--grand { font-size: 1.15rem; font-weight: 700; color: var(--tl-text); margin-top: var(--tl-space-3); padding-top: var(--tl-space-3); border-top: 1px solid var(--tl-border); }

/* Stands in for the grand total until there is one. Deliberately quiet: it is an
   instruction, not a figure, and must not be mistaken for a price. */
.tl-total__prompt {
	margin: var(--tl-space-3) 0 0;
	padding-top: var(--tl-space-3);
	border-top: 1px solid var(--tl-border);
	font-size: 0.875rem;
	color: var(--tl-text-muted);
}

/*
 * Rooms on the left, the running total on the right. 340px is the narrowest the
 * summary can be and still show a room name, its per-night rate and its line
 * total on one row without wrapping mid-figure.
 */
.tl-booking__layout {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 340px;
	gap: var(--tl-space-6);
	align-items: start;
}

/* Without this the room grid cannot shrink below its widest card and the whole
   booking page scrolls sideways on a narrow screen. */
.tl-booking__layout > * { min-width: 0; }

/*
 * On step one there is nothing to summarise, so the basket panel carries the
 * `hidden` attribute — and the 340px track it would have occupied stayed
 * reserved and empty, leaving the date form in a column three quarters of the
 * page wide with a gap beside it. `:has()` lets the grid react to that without
 * a class toggled from JavaScript, which would be a second source of truth for
 * a fact the markup already states.
 */
.tl-booking__layout:has( [data-tl-basket-panel][hidden] ) {
	grid-template-columns: minmax(0, 1fr);
}

@media (max-width: 900px) {
	.tl-booking__layout { grid-template-columns: minmax(0, 1fr); }

	/*
	 * On a phone the summary moves under the rooms and sticks to the bottom of
	 * the viewport instead of the top, because that is where a thumb is and
	 * because the total is what somebody checks before tapping Continue.
	 */
	.tl-basket {
		position: sticky;
		top: auto;
		bottom: 0;
		z-index: 40;
		margin-inline: calc(var(--tl-space-5) * -1);
		border-radius: var(--tl-radius-md) var(--tl-radius-md) 0 0;
		box-shadow: 0 -4px 18px rgba(23, 20, 18, 0.10);
		max-height: 52vh;
		overflow-y: auto;
	}
}

/* ==========================================================================
   9. FEEDBACK
   ========================================================================== */

/* `display: flex` on the base class outranks the browser's `[hidden]` rule, so
   an alert marked hidden would still paint — the booking wizard's empty error
   box appeared as a red bar on first load. The class must honour the attribute
   it is given. */
.tl-alert[hidden] { display: none; }

.tl-alert {
	display: flex;
	gap: var(--tl-space-3);
	padding: var(--tl-space-3) var(--tl-space-4);
	border-radius: var(--tl-radius);
	border: 1px solid var(--tl-border);
	border-left-width: 3px;
	font-size: 0.92rem;
	margin-bottom: var(--tl-space-4);
	line-height: 1.5;
}

/* The text colour here is the -ink token, not -700: -700 is guaranteed
   readable on white, and this text is not on white, it is on the -50 tint.
   The fallback keeps the stylesheet correct on its own, before the generated
   design block loads. See toplife_design_pair_css(). */
.tl-alert--error   { background: var(--tl-danger-50);  border-color: var(--tl-danger-100);  border-left-color: var(--tl-danger-500);  color: var(--tl-danger-ink, var(--tl-danger-700)); }
.tl-alert--success { background: var(--tl-success-50); border-left-color: var(--tl-success-500); color: var(--tl-success-ink, var(--tl-success-700)); }
.tl-alert--warning { background: var(--tl-warning-50); border-left-color: var(--tl-warning-500); color: var(--tl-warning-ink, var(--tl-warning-700)); }
.tl-alert--info    { background: var(--tl-primary-50); border-left-color: var(--tl-primary-500); color: var(--tl-primary-700); }

.tl-empty {
	text-align: center;
	padding: var(--tl-space-12) var(--tl-space-5);
	border: 1px dashed var(--tl-neutral-300);
	border-radius: var(--tl-radius-md);
	color: var(--tl-text-muted);
	background: var(--tl-neutral-25);
}
.tl-empty strong { display: block; color: var(--tl-text); font-size: 1.05rem; margin-bottom: var(--tl-space-2); }

/* A skeleton row, so "searching" looks like something arriving rather than
   like nothing happening. */
.tl-skeleton {
	height: 88px;
	border-radius: var(--tl-radius);
	background: linear-gradient(90deg, var(--tl-neutral-100) 25%, var(--tl-neutral-50) 37%, var(--tl-neutral-100) 63%);
	background-size: 400% 100%;
	animation: tl-shimmer 1.3s ease-in-out infinite;
}

@keyframes tl-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }

/* ==========================================================================
   10. FOOTER
   ========================================================================== */

.tl-footer {
	background: var(--tl-neutral-900);
	color: rgba(255, 255, 255, 0.72);
	padding: var(--tl-space-16) 0 var(--tl-space-8);
	margin-top: var(--tl-section-y);
}

/*
 * FOUR COLUMNS, AND THE FIRST ONE IS WIDER
 *
 * The brand column carries a name, a slogan and a sentence; the other three
 * carry short lines. Equal columns would leave the brand text cramped and the
 * link lists swimming, so the first track is given half again as much room.
 *
 * `minmax(0, …)` on every track: a grid track's default minimum is its content,
 * and one long unbroken address is enough to push the whole footer wider than
 * the page without it.
 */
/*
 * FIVE COLUMNS, AND THE BRAND ONE IS WIDER
 *
 * The brand column carries a paragraph and the other four carry lists, so they
 * are not the same shape and should not be the same width. Below 1024px the
 * grid drops to two columns with the brand spanning both, and below 560px to
 * one, which is the only arrangement in which five headings and their contents
 * all stay readable on a phone.
 */
.tl-footer__cols {
	display: grid;
	grid-template-columns: minmax(0, 1.6fr) repeat(4, minmax(0, 1fr));
	gap: var(--tl-space-10) var(--tl-space-8);
	padding-bottom: var(--tl-space-8);
	border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

@media (max-width: 1200px) {
	.tl-footer__cols { grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr)); }
	.tl-footer__col--book { grid-column: 2 / -1; }
}

@media (max-width: 1024px) {
	.tl-footer__cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--tl-space-8); }
	.tl-footer__col--brand { grid-column: 1 / -1; }
	.tl-footer__col--book { grid-column: 1 / -1; }
}

@media (max-width: 560px) {
	.tl-footer__cols { grid-template-columns: minmax(0, 1fr); }
	.tl-footer { padding-top: var(--tl-space-12); }
}

.tl-footer__booknote {
	margin: 0 0 var(--tl-space-4);
	font-size: 0.9rem;
	line-height: 1.6;
	color: rgba(255, 255, 255, 0.72);
}

.tl-footer__bookgo { margin: 0; }

.tl-footer__bookgo .tl-btn { width: 100%; justify-content: center; }

@media (min-width: 1025px) {
	.tl-footer__bookgo .tl-btn { width: auto; }
}

.tl-footer__col { min-width: 0; }

.tl-footer__heading {
	margin: 0 0 var(--tl-space-4);
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, 0.55);
}

/* The brand column's heading is the lodge's name, so it is set as a name. */
.tl-footer__col--brand .tl-footer__heading {
	font-family: var(--tl-font-display);
	font-size: 1.35rem;
	font-weight: 600;
	letter-spacing: -0.02em;
	text-transform: none;
	color: #fff;
	margin-bottom: var(--tl-space-3);
}

.tl-footer__slogan {
	margin: 0 0 var(--tl-space-4);
	font-family: var(--tl-font-display);
	font-size: 1rem;
	line-height: 1.4;
	color: var(--tl-accent-300);
}

.tl-footer__about {
	margin: 0;
	max-width: 34ch;
	font-size: 0.9rem;
	line-height: 1.6;
	color: rgba(255, 255, 255, 0.6);
}

.tl-footer__links,
.tl-footer__facts {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--tl-space-3);
	font-size: 0.9rem;
}

.tl-footer__links a {
	color: rgba(255, 255, 255, 0.78);
	text-decoration: none;
	transition: color var(--tl-transition);
}

.tl-footer__links a:hover { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
.tl-footer__links a:focus-visible { outline: 2px solid var(--tl-accent-300); outline-offset: 3px; border-radius: 2px; }

.tl-footer__address,
.tl-footer__hours { color: rgba(255, 255, 255, 0.55); overflow-wrap: anywhere; }

/*
 * The guest-information column is a definition list in spirit: a label above
 * its value, so the five policies scan as five answers rather than as prose.
 */
.tl-footer__facts li { display: flex; flex-direction: column; gap: 2px; }

.tl-footer__factlabel {
	font-size: 0.7rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, 0.42);
}

.tl-footer__factvalue { color: rgba(255, 255, 255, 0.82); line-height: 1.4; }

.tl-footer__base {
	padding-top: var(--tl-space-5);
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-3);
	justify-content: space-between;
	font-size: 0.82rem;
	color: rgba(255, 255, 255, 0.45);
}

/*
 * The developer credit. Quieter than everything beside it on purpose: it is
 * true, it belongs on the page, and it is the last thing a guest needs. The
 * link only separates itself from the sentence on hover and on focus, so it
 * reads as a credit rather than as a fifth thing to click.
 */
.tl-footer__credit {
	color: rgba(255, 255, 255, 0.32);
}

.tl-footer__credit a {
	color: inherit;
	text-decoration: none;
	border-bottom: 1px solid rgba(255, 255, 255, 0.18);
}

.tl-footer__credit a:hover,
.tl-footer__credit a:focus-visible {
	color: rgba(255, 255, 255, 0.75);
	border-bottom-color: currentColor;
}

/*
 * The legal line. Set at the same weight as the copyright beside it, because
 * these are three links a guest opens once and they should not compete with
 * the four columns above that they came for.
 */
.tl-footer__legal {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-2) var(--tl-space-5);
	margin: 0;
	padding: 0;
	list-style: none;
}

.tl-footer__legal a {
	color: inherit;
	text-decoration: none;
}

.tl-footer__legal a:hover { color: rgba(255, 255, 255, 0.8); text-decoration: underline; }
.tl-footer__legal a:focus-visible { outline: 2px solid var(--tl-accent-300); outline-offset: 3px; }

/* ==========================================================================
   11. SINGLE ROOM
   ========================================================================== */

.tl-room__layout {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 340px;
	gap: var(--tl-space-8);
	align-items: start;
}

@media (max-width: 900px) { .tl-room__layout { grid-template-columns: minmax(0, 1fr); } }

.tl-room__gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr)); gap: var(--tl-space-3); margin-top: var(--tl-space-5); }
.tl-room__gallery img { border-radius: var(--tl-radius); aspect-ratio: 4/3; object-fit: cover; width: 100%; }

.tl-room__hero { border-radius: var(--tl-radius-md); overflow: hidden; aspect-ratio: 16/9; background: var(--tl-neutral-100); }
.tl-room__hero img { width: 100%; height: 100%; object-fit: cover; }

.tl-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: var(--tl-space-4); margin: var(--tl-space-6) 0; padding: var(--tl-space-4); border: 1px solid var(--tl-border); border-radius: var(--tl-radius); background: var(--tl-surface); }
.tl-facts div { min-width: 0; }
.tl-facts dt { font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--tl-text-muted); margin-bottom: 2px; }
.tl-facts dd { margin: 0; font-weight: 600; }

/* ==========================================================================
   12. MISC
   ========================================================================== */

.tl-badge {
	display: inline-flex; align-items: center; gap: 5px;
	padding: 2px 9px 2px 7px;
	border-radius: var(--tl-radius-full);
	font-size: 0.74rem; font-weight: 600;
	background: var(--tl-neutral-100); color: var(--tl-neutral-700);
}
.tl-badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: 0.75; }
.tl-badge--available { background: var(--tl-success-50); color: var(--tl-success-ink, var(--tl-success-700)); }
.tl-badge--soldout { background: var(--tl-danger-50); color: var(--tl-danger-ink, var(--tl-danger-700)); }

/* A reading measure for prose. Not for the booking application — see page.php. */
.tl-prose { max-width: var(--tl-measure-prose); }

/* The full width of the container, for a screen that is an application rather
   than an article. */
.tl-wide { max-width: none; }
.tl-prose h2 { margin-top: var(--tl-space-8); }
.tl-prose img { border-radius: var(--tl-radius); margin-block: var(--tl-space-5); }

.tl-pagination { display: flex; gap: var(--tl-space-2); justify-content: center; margin-top: var(--tl-space-10); flex-wrap: wrap; }
.tl-pagination .page-numbers {
	display: grid; place-items: center; min-width: 40px; height: 40px; padding: 0 var(--tl-space-2);
	border: 1px solid var(--tl-border); border-radius: var(--tl-radius-sm);
	background: var(--tl-surface); text-decoration: none; color: var(--tl-text); font-size: 0.9rem;
}
.tl-pagination .page-numbers.current { background: var(--tl-primary-600); border-color: var(--tl-primary-600); color: #fff; font-weight: 600; }

/* ==========================================================================
   13. PHOTOGRAPH PLACEHOLDER
   ==========================================================================
   Shown where a room has no featured image. The point is that it should look
   chosen, not missing: a tinted panel carrying the room's own name, in the
   same family of colours as the rest of the site. */

.tl-placeholder {
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
	padding: var(--tl-space-4);
	text-align: center;
	background: linear-gradient(145deg, var(--tl-primary-700), var(--tl-primary-900));
	color: rgba(255, 255, 255, 0.94);
}

/* A faint diagonal texture, so a large placeholder does not read as a flat
   error state. */
.tl-placeholder::after {
	content: "";
	position: absolute;
	inset: 0;
	background-image: repeating-linear-gradient(
		135deg,
		rgba(255, 255, 255, 0.05) 0 1px,
		transparent 1px 14px
	);
}

.tl-placeholder span {
	position: relative;
	z-index: 1;
	font-family: var(--tl-font-display);
	font-size: clamp(1.15rem, 3.4vw, 1.6rem);
	font-weight: 600;
	letter-spacing: 0.01em;
	line-height: 1.2;
	text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

.tl-placeholder--1 { background: linear-gradient(145deg, var(--tl-primary-600), var(--tl-primary-800)); }
.tl-placeholder--2 { background: linear-gradient(145deg, #3c4a3a, #1e2a1d); }
.tl-placeholder--3 { background: linear-gradient(145deg, var(--tl-accent-700), #5f3f0d); }

/* On a room's own page the placeholder fills the hero, which has no absolutely
   positioned parent of its own without this. */
.tl-room__hero { position: relative; }

/* ==========================================================================
   CONTENT BLOCKS

   The parts of the site the lodge edits itself, from Website Content in the
   management system.

   THE RESPONSIVE MODEL, IN ONE PLACE
   ----------------------------------
   Each block carries three values per dimension as custom properties — mobile,
   tablet and desktop — and this is where the cascade picks between them.
   Mobile is the base and the other two are overrides, in that order, so a
   block that only sets a mobile value simply keeps it all the way up.

   The alternative — writing the sizes into each block's style attribute — puts
   the desktop padding on a phone, because an inline declaration outranks a
   media query. Custom properties are the way an inline value can still lose an
   argument it should lose.
   ========================================================================== */

.tl-block {
	padding-top: var(--b-pt-m, 40px);
	padding-bottom: var(--b-pb-m, 40px);
	min-height: var(--b-mh-m, 0);
	margin-top: var(--b-mt-m, 0);
	margin-bottom: var(--b-mb-m, 0);
}

/*
 * A ceiling on height, and what happens when the content exceeds it.
 *
 * The block only becomes a scroller when a ceiling was actually chosen, which
 * is what the 0px guard is for: max-height:0 would collapse every block that
 * never set one. Content taller than the ceiling scrolls inside the block
 * rather than being cropped, because a paragraph losing its last line to a
 * number somebody typed in a settings box is not a layout choice, it is a bug
 * that looks like one.
 */
.tl-block[style*="--b-xh-m"] { max-height: var(--b-xh-m, none); overflow-y: auto; }

.tl-block__inner {
	width: min(100%, var(--tl-container));
	margin-inline: auto;
	padding-inline: var(--tl-space-5);
}

/* Widths. Named, not typed: a content editor picks "narrow", not a pixel. */
.tl-block--w-narrow .tl-block__inner { width: min(100%, 760px); }
.tl-block--w-wide .tl-block__inner   { width: min(100%, 1560px); }
.tl-block--w-full .tl-block__inner   { width: 100%; padding-inline: 0; }

/*
 * Fractional widths.
 *
 * A share of the container, and only from a desktop up. A block set to a
 * quarter is a quarter of a 1320px page, which is 330px: readable on a laptop
 * and unusable on a phone, where it would be about 90px wide. Below the
 * breakpoint every fraction is full width, which is what every responsive
 * grid does and what somebody choosing "25%" actually expects to happen when
 * they turn their phone.
 *
 * The fractions are named keys rather than numbers written into a style
 * attribute, so nothing a content editor types can reach the page as CSS.
 */
@media (min-width: 900px) {
	.tl-block--w-quarter .tl-block__inner       { width: min(100%, calc(var(--tl-container) * 0.25)); }
	.tl-block--w-third .tl-block__inner         { width: min(100%, calc(var(--tl-container) / 3)); }
	.tl-block--w-half .tl-block__inner          { width: min(100%, calc(var(--tl-container) * 0.5)); }
	.tl-block--w-twothirds .tl-block__inner     { width: min(100%, calc(var(--tl-container) * 2 / 3)); }
	.tl-block--w-threequarters .tl-block__inner { width: min(100%, calc(var(--tl-container) * 0.75)); }
}

.tl-block--align-center { text-align: center; }
.tl-block--align-right  { text-align: right; }

/* Centring the text should centre the buttons under it too, or the block
   reads as two different alignments arguing with each other. */
.tl-block--align-center .tl-block__actions { justify-content: center; }
.tl-block--align-right .tl-block__actions  { justify-content: flex-end; }

/*
 * Backgrounds.
 *
 * Six named tokens, each shipping its own text colour. That pairing is the
 * whole reason there is no colour picker on the editor: a free choice of
 * background with a fixed text colour is how somebody ends up with grey on
 * pale gold, and nothing in the system would have stopped them.
 */
.tl-block--bg-surface { background: var(--tl-surface); }
.tl-block--bg-alt     { background: var(--tl-neutral-50); }

.tl-block--bg-primary { background: var(--tl-primary-700); color: #fff; }
.tl-block--bg-accent  { background: var(--tl-accent-100); color: var(--tl-neutral-800); }
.tl-block--bg-dark    { background: var(--tl-neutral-900); color: #fff; }

.tl-block--bg-primary .tl-block__title,
.tl-block--bg-dark .tl-block__title,
.tl-block--bg-primary .tl-block__cardtitle,
.tl-block--bg-dark .tl-block__cardtitle { color: inherit; }

.tl-block--bg-primary .tl-block__sub,
.tl-block--bg-dark .tl-block__sub,
.tl-block--bg-primary .tl-block__cardtext,
.tl-block--bg-dark .tl-block__cardtext { color: rgba(255, 255, 255, 0.78); }

.tl-block--bg-primary .tl-section__eyebrow,
.tl-block--bg-dark .tl-section__eyebrow { color: var(--tl-accent-300); }

.tl-block--bg-primary .tl-block__card,
.tl-block--bg-dark .tl-block__card {
	background: rgba(255, 255, 255, 0.06);
	border-color: rgba(255, 255, 255, 0.14);
}

.tl-block__head { margin-bottom: var(--tl-space-6); }

.tl-block__title {
	margin: 0;
	font-family: var(--tl-font-display);
	font-size: clamp(1.5rem, 2.6vw, 2.125rem);
	line-height: 1.15;
	letter-spacing: -0.02em;
}

.tl-block__sub {
	margin: var(--tl-space-2) 0 0;
	color: var(--tl-text-muted);
	font-size: 1rem;
	line-height: 1.6;
}

.tl-block__prose { max-width: var(--tl-measure-prose); }
.tl-block--align-center .tl-block__prose { margin-inline: auto; }

.tl-block__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-3);
	margin-top: var(--tl-space-5);
}

/*
 * The item grid.
 *
 * The column count is the editor's, per breakpoint. `minmax(0, 1fr)` rather
 * than `1fr`: a grid track's default minimum is its content, so one long
 * unbroken word in a card would otherwise push the whole row wider than the
 * page — which is exactly the overflow class of bug this project has already
 * been bitten by four times.
 */
.tl-block__grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(var(--b-cols-m, 1), minmax(0, 1fr));
	gap: var(--b-gap-m, 16px);
}

.tl-block__card {
	padding: var(--tl-space-5);
	background: var(--tl-surface);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-md);
	box-shadow: var(--tl-shadow-xs);
	min-width: 0;
}

.tl-block__cardtitle {
	margin: 0 0 var(--tl-space-2);
	font-family: var(--tl-font-display);
	font-size: 1.05rem;
	line-height: 1.25;
}

.tl-block__cardtitle a { color: inherit; text-decoration: none; }
.tl-block__cardtitle a:hover { text-decoration: underline; }

.tl-block__cardtext {
	margin: 0;
	font-size: 0.9rem;
	line-height: 1.55;
	color: var(--tl-text-muted);
	overflow-wrap: anywhere;
}

.tl-block__gallery img {
	width: 100%;
	height: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border-radius: var(--tl-radius);
	display: block;
}

.tl-block__stats li { text-align: center; }
.tl-block__stats strong {
	display: block;
	font-family: var(--tl-font-display);
	font-size: clamp(1.75rem, 4vw, 2.75rem);
	line-height: 1;
	letter-spacing: -0.03em;
	font-variant-numeric: tabular-nums;
}
.tl-block__stats span { font-size: 0.85rem; color: var(--tl-text-muted); }

.tl-block__steps {
	margin: 0;
	padding: 0 0 0 var(--tl-space-6);
	display: grid;
	gap: var(--tl-space-4);
}
.tl-block__steps li { padding-left: var(--tl-space-2); }
.tl-block__steps strong { display: block; }
.tl-block__steps span { color: var(--tl-text-muted); font-size: 0.92rem; }

.tl-block__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--tl-space-3); }
.tl-block__list li { padding-left: var(--tl-space-5); position: relative; }
.tl-block__list li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.55em;
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--tl-primary-400);
}
.tl-block__listnote { display: block; color: var(--tl-text-muted); font-size: 0.88rem; }

.tl-block__split {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: var(--b-gap-m, 16px);
	align-items: center;
}

/*
 * The text column of a split block. min-width:0 is the load-bearing line: a
 * grid item defaults to min-content width, so one long unbroken word in a
 * lodge's own copy would widen the column past the container and push the
 * page sideways.
 */
.tl-block__splittext {
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: var(--tl-space-4);
	justify-content: center;
}

/* A grid item in a split block. Without min-width:0 a long unbroken word, or an
   image the editor sized past the column, widens the track and pushes the page
   sideways. */
.tl-block__media { min-width: 0; }

.tl-block__media img { width: 100%; height: auto; border-radius: var(--tl-radius-md); display: block; }
.tl-block__caption { margin: var(--tl-space-3) 0 0; font-size: 0.88rem; color: var(--tl-text-muted); }

.tl-block__quote { margin: 0; }
.tl-block__quote blockquote {
	margin: 0;
	padding-left: var(--tl-space-5);
	border-left: 3px solid var(--tl-accent-500);
	font-family: var(--tl-font-display);
	font-size: clamp(1.125rem, 2.2vw, 1.5rem);
	line-height: 1.4;
}
.tl-block__quote figcaption { margin-top: var(--tl-space-3); color: var(--tl-text-muted); font-size: 0.9rem; }
.tl-block--align-center .tl-block__quote blockquote { border-left: 0; padding-left: 0; }

.tl-block__faq { display: grid; gap: var(--tl-space-2); }

.tl-block__faqitem {
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius);
	background: var(--tl-surface);
	padding: var(--tl-space-4);
}

.tl-block__faqitem summary { cursor: pointer; font-weight: 600; }
.tl-block__faqitem summary:focus-visible { outline: 2px solid var(--tl-primary-600); outline-offset: 3px; }
.tl-block__faqitem p { margin: var(--tl-space-3) 0 0; color: var(--tl-text-muted); }

.tl-block__notice {
	display: flex;
	gap: var(--tl-space-4);
	padding: var(--tl-space-5);
	border: 1px solid var(--tl-accent-300);
	border-left-width: 4px;
	border-radius: var(--tl-radius);
	background: var(--tl-accent-100);
	color: var(--tl-neutral-800);
	text-align: left;
}

.tl-block__noticeicon { flex: 0 0 auto; color: var(--tl-accent-700); }
.tl-block__noticetitle { margin: 0 0 var(--tl-space-2); font-weight: 700; }

/*
 * The wrapper around whatever oEmbed returned. That markup is not ours, so the
 * container is what stops a provider's own fixed width escaping the column.
 */
.tl-block__embed { min-width: 0; overflow: hidden; }

.tl-block__embed iframe,
.tl-block__embed video {
	width: 100%;
	aspect-ratio: 16 / 9;
	height: auto;
	border: 0;
	border-radius: var(--tl-radius-md);
	display: block;
}

/* A spacer with a background gets a line so it reads as a divider rather than
   as a gap somebody left by accident. */
.tl-block--spacer.tl-block--bg-none .tl-block__inner { border-top: 1px solid var(--tl-border); }

/*
 * Per-breakpoint visibility.
 *
 * Written as three separate ranges rather than as min-width overrides,
 * because "hidden on tablet" has to mean hidden on tablet and visible on both
 * sides of it — a min-width rule would leak the hiding upwards onto desktop.
 */
@media (max-width: 767px) {
	.tl-block--hide-m { display: none; }
}

@media (min-width: 768px) and (max-width: 1023px) {
	.tl-block--hide-t { display: none; }
}

@media (min-width: 1024px) {
	.tl-block--hide-d { display: none; }
}

@media (min-width: 768px) {
	.tl-block {
		padding-top: var(--b-pt-t, 56px);
		padding-bottom: var(--b-pb-t, 56px);
		min-height: var(--b-mh-t, 0);
		margin-top: var(--b-mt-t, 0);
		margin-bottom: var(--b-mb-t, 0);
	}

	.tl-block[style*="--b-xh-t"] { max-height: var(--b-xh-t, none); }

	.tl-block__grid { grid-template-columns: repeat(var(--b-cols-t, 2), minmax(0, 1fr)); gap: var(--b-gap-t, 20px); }
	.tl-block__split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--b-gap-t, 20px); }
}

@media (min-width: 1024px) {
	.tl-block {
		padding-top: var(--b-pt-d, 72px);
		padding-bottom: var(--b-pb-d, 72px);
		min-height: var(--b-mh-d, 0);
		margin-top: var(--b-mt-d, 0);
		margin-bottom: var(--b-mb-d, 0);
	}

	.tl-block[style*="--b-xh-d"] { max-height: var(--b-xh-d, none); }

	.tl-block__grid { grid-template-columns: repeat(var(--b-cols-d, 3), minmax(0, 1fr)); gap: var(--b-gap-d, 24px); }
	.tl-block__split { gap: var(--b-gap-d, 24px); }
}

/*
 * The booking rules strip.
 *
 * The two policies and the identification requirement, shown where the guest
 * is about to choose dates rather than only in the footer. Laid out as a row
 * of short statements: a paragraph here would be read by nobody, and these are
 * the four things somebody arrives at the desk having got wrong.
 */
.tl-rules {
	list-style: none;
	margin: 0 0 var(--tl-space-6);
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
	gap: var(--tl-space-4);
}

.tl-rule {
	display: flex;
	align-items: flex-start;
	gap: var(--tl-space-3);
	padding: var(--tl-space-4);
	min-width: 0;
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius);
	background: var(--tl-surface);
	font-size: 0.88rem;
	line-height: 1.5;
	color: var(--tl-text-muted);
}

.tl-rule__icon {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	border-radius: var(--tl-radius-full);
	background: var(--tl-primary-50);
	color: var(--tl-primary-600);
}

/* ==========================================================================
   PAYMENT METHODS
   ========================================================================== */

/*
 * The recommended option is one card, wider and warmer than the rest, and the
 * others are a quiet grid underneath. That hierarchy is the whole design: a
 * guest should be able to tell in one glance which way the lodge would rather
 * they paid, without every option shouting.
 */

.tl-pay {
	display: flex;
	flex-direction: column;
	gap: var(--tl-space-6);
	min-width: 0;
}

.tl-pay__lead {
	position: relative;
	padding: var(--tl-space-6);
	border: 1px solid var(--tl-accent-300);
	border-radius: var(--tl-radius-md);
	background: var(--tl-accent-100);
	box-shadow: var(--tl-shadow-xs);
}

.tl-pay__badge {
	display: inline-block;
	margin: 0 0 var(--tl-space-3);
	padding: 0.22em 0.7em;
	border-radius: var(--tl-radius-full);
	background: var(--tl-accent-700);
	color: #fff;
	font-size: 0.68rem;
	font-weight: 700;
	letter-spacing: 0.1em;
	text-transform: uppercase;
}

.tl-pay__leadtitle {
	display: flex;
	align-items: center;
	gap: var(--tl-space-3);
	margin: 0 0 var(--tl-space-2);
	font-size: 1.3rem;
	line-height: 1.2;
}

.tl-pay__leadsub {
	margin: 0 0 var(--tl-space-3);
	font-size: 0.9rem;
	color: var(--tl-text-muted);
}

.tl-pay__leadtext {
	margin: 0;
	max-width: 56ch;
}

.tl-pay__via {
	margin: var(--tl-space-3) 0 0;
	font-size: 0.85rem;
	color: var(--tl-text-muted);
}

.tl-pay__go { margin: var(--tl-space-5) 0 0; }

.tl-pay__other {
	margin: 0;
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--tl-text-muted);
}

/*
 * auto-fit rather than a fixed column count. The lodge takes three methods
 * today and may take two tomorrow, and a three-column grid holding two cards
 * leaves a hole that looks like something failed to load.
 */
.tl-pay__grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
	gap: var(--tl-space-4);
	margin: 0;
	padding: 0;
	list-style: none;
}

.tl-pay__card {
	min-width: 0;
	padding: var(--tl-space-5);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-md);
	background: var(--tl-surface);
	transition: border-color var(--tl-transition), box-shadow var(--tl-transition);
}

.tl-pay__card:hover {
	border-color: var(--tl-border-strong);
	box-shadow: var(--tl-shadow-xs);
}

.tl-pay__icon {
	display: inline-flex;
	color: var(--tl-primary-700);
}

.tl-pay__icon .tl-icon { width: 22px; height: 22px; }

.tl-pay__name {
	margin: var(--tl-space-3) 0 var(--tl-space-1);
	font-weight: 700;
}

.tl-pay__note {
	margin: 0;
	font-size: 0.88rem;
	color: var(--tl-text-muted);
}

/*
 * Shown only when no gateway is connected. Set as a plain note rather than a
 * warning: a lodge that takes payment at the desk is not in an error state.
 */
.tl-pay__pending {
	margin: 0;
	padding: var(--tl-space-4) var(--tl-space-5);
	border-left: 3px solid var(--tl-border-strong);
	background: var(--tl-neutral-50, #f7f6f3);
	border-radius: 0 var(--tl-radius-sm) var(--tl-radius-sm) 0;
	font-size: 0.92rem;
	color: var(--tl-text-muted);
	max-width: 62ch;
}

@media (max-width: 560px) {
	.tl-pay__lead { padding: var(--tl-space-5); }
	.tl-pay__leadtitle { font-size: 1.15rem; }
}

/* -- Where a photograph is not yet ------------------------------------------ */

/*
 * Flat, drawn, and obviously not a photograph. See toplife_block_placeholder():
 * the whole point of this panel is that a guest can tell at a glance that the
 * lodge has not put a picture here, rather than being shown somebody else's.
 */
.tl-block__media--pending { min-width: 0; }

.tl-pending {
	position: relative;
	display: block;
	width: 100%;
	aspect-ratio: 8 / 5;
	overflow: hidden;
	border-radius: var(--tl-radius-md);
	border: 1px solid var(--tl-border);
	background: var(--tl-neutral-50);
}

.tl-pending svg { width: 100%; height: 100%; display: block; }

.tl-pending__ground { fill: var(--tl-primary-900); }
.tl-pending__screen { fill: var(--tl-accent-300); }
.tl-pending__stand  { fill: var(--tl-primary-600); }
.tl-pending__seat   { fill: var(--tl-primary-700); }
.tl-pending__line   { fill: var(--tl-primary-600); }

/*
 * The label is the honest part, so it is legible rather than decorative. On the
 * lounge panel it sits over the drawing and needs its own ground to read
 * against; on a plain panel it is the only thing there and is centred.
 */
.tl-pending__label {
	position: absolute;
	left: var(--tl-space-4);
	bottom: var(--tl-space-4);
	padding: 0.3em 0.7em;
	border-radius: var(--tl-radius-full);
	background: rgba(255, 255, 255, 0.92);
	color: var(--tl-neutral-800);
	font-size: 0.72rem;
	font-weight: 600;
	letter-spacing: 0.04em;
}

.tl-pending--plain {
	display: grid;
	place-items: center;
	background:
		repeating-linear-gradient(
			135deg,
			var(--tl-neutral-100) 0 10px,
			var(--tl-neutral-50) 10px 20px
		);
}

.tl-pending--plain .tl-pending__label {
	position: static;
	background: var(--tl-surface);
	border: 1px solid var(--tl-border);
}

/*
 * The optional lead-in on a ticker message. Weight rather than a separator: a
 * message without one must not be left carrying a dangling colon.
 */
.tl-ticker__lead {
	color: #fff;
	font-weight: 700;
	margin-right: 0.4em;
}

/* The amenity list on a room's own page, given room to breathe. */
.tl-room__amenities { margin-top: var(--tl-space-4); font-size: 0.94rem; }

/* ==========================================================================
   Upendo Hostel
   --------------------------------------------------------------------------
   A second business on the same site, so it uses the same tokens, the same
   buttons, the same section rhythm and the same placeholder treatment as the
   lodge. Nothing here introduces a colour, a scale step or a shadow that the
   rest of the site does not already have: two visual systems on one domain is
   how a site starts looking like two sites badly glued together.

   Everything below is either a layout the lodge had no use for (the section
   navigation, the qualities row) or a variation on one it already has.
   ========================================================================== */

.tl-hostel-hero {
	background: var(--tl-neutral-50);
	border-bottom: 1px solid var(--tl-border);
	padding-block: var(--tl-section-y);
}

.tl-hostel-hero__inner {
	display: grid;
	grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
	gap: var(--tl-space-6);
	align-items: center;
}

.tl-hostel-hero__title {
	margin: 0 0 var(--tl-space-3);
	/* Clamped rather than stepped, so the name never wraps awkwardly at a
	   width nobody thought to test. */
	font-size: clamp(2rem, 1.4rem + 2.6vw, 3.1rem);
	line-height: 1.1;
}

.tl-hostel-hero__lead {
	margin: 0 0 var(--tl-space-3);
	max-width: 46ch;
	font-size: 1.1rem;
	color: var(--tl-text-muted);
}

/* A grid column that is allowed to shrink. Without min-width:0 a long word,
   or the hostel's name at a small viewport, forces the column wider than its
   track and pushes the placeholder off the screen. */
.tl-hostel-hero__text { min-width: 0; }

.tl-hostel-hero__pin {
	display: inline-flex;
	color: var(--tl-primary-300);
}

.tl-hostel-hero__where {
	display: flex;
	align-items: center;
	gap: var(--tl-space-2);
	margin: 0 0 var(--tl-space-5);
	color: var(--tl-text-muted);
}

.tl-hostel-hero__pin svg { width: 20px; height: 20px; display: block; }

.tl-hostel-hero__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-3);
}

.tl-hostel-hero__media { min-width: 0; }

.tl-hostel-hero__pending {
	aspect-ratio: 4 / 3;
	border-radius: var(--tl-radius-lg);
}

/* The section navigation. Sticky on anything tall enough for it to help, and
   horizontally scrollable rather than wrapped on a phone: nine items wrapped
   to three lines pushes the first section off the screen. */
.tl-hostel-nav {
	position: sticky;
	/* BELOW THE SITE HEADER, NOT UNDER IT.
	   Written first as top:0, which is what "stick to the top" means until you
	   remember the site header is also sticky at top:0 and 76px tall with a
	   higher z-index. The section navigation stuck perfectly, entirely hidden
	   behind it, and nothing looked broken: the page simply had no section
	   navigation. Seen by measuring the deployed page, not by reading the CSS. */
	top: var(--tl-header-h);
	z-index: 20;
	background: var(--tl-surface);
	border-bottom: 1px solid var(--tl-border);
}

.tl-hostel-nav__list {
	display: flex;
	gap: var(--tl-space-1);
	margin: 0;
	padding: 0;
	list-style: none;
	overflow-x: auto;
	/* The scroll is the point on a narrow screen, but the bar itself is noise
	   on a wide one where nothing overflows. */
	scrollbar-width: thin;
	-webkit-overflow-scrolling: touch;
}

.tl-hostel-nav__link {
	display: inline-flex;
	align-items: center;
	/* 44px, which is the smallest target a thumb reliably hits. */
	min-height: 44px;
	padding: 0 var(--tl-space-3);
	white-space: nowrap;
	color: var(--tl-text-muted);
	text-decoration: none;
	border-bottom: 2px solid transparent;
	font-size: 0.95rem;
}

.tl-hostel-nav__link:hover,
.tl-hostel-nav__link:focus-visible {
	color: var(--tl-text);
	border-bottom-color: var(--tl-primary-300);
}

/* An anchor under two stacked sticky bars lands with its heading hidden behind
   both unless the browser is told to stop short of them. The header, the
   section navigation, and a little air. */
.tl-hostel-anchor { scroll-margin-top: calc(var(--tl-header-h) + 45px + var(--tl-space-4)); }

.tl-hostel-subhead {
	margin: var(--tl-space-6) 0 var(--tl-space-4);
	font-size: 1.15rem;
}

/* The six words the hostel used. Presented as a row of plain markers, not as
   badges with ticks: a tick reads as a guarantee, and these are qualities the
   hostel's arrangements are intended to support. */
.tl-hostel-qualities {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-2);
	margin: var(--tl-space-5) 0 0;
	padding: 0;
	list-style: none;
}

.tl-hostel-quality {
	padding: var(--tl-space-2) var(--tl-space-4);
	border: 1px solid var(--tl-border);
	border-radius: 999px;
	background: var(--tl-surface);
	color: var(--tl-text);
	font-size: 0.95rem;
}

/* Held to the same reading measure as the prose above it, so the address and
   the two actions sit under the heading rather than stretching to the full
   width of a desktop page. */
.tl-hostel-contact { max-width: 62ch; }

.tl-hostel-contact__name {
	margin: 0 0 var(--tl-space-1);
	font-size: 1.25rem;
	font-weight: 600;
	/* The hostel writes its name in capitals. Done here rather than stored
	   twice, so there is still one name in the database. */
	text-transform: uppercase;
	letter-spacing: 0.02em;
}

.tl-hostel-contact__where {
	margin: 0 0 var(--tl-space-5);
	color: var(--tl-text-muted);
}

.tl-hostel-contact__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-3);
}

.tl-hostel-contact__action {
	gap: var(--tl-space-2);
	/* Big enough to tap without aiming, because on a phone this is the whole
	   purpose of the page. */
	min-height: 48px;
	padding-inline: var(--tl-space-5);
}

.tl-hostel-contact__action svg { width: 20px; height: 20px; }

@media (max-width: 880px) {
	.tl-hostel-hero__inner {
		grid-template-columns: minmax(0, 1fr);
	}

	/* The placeholder goes below the words on a narrow screen, and shrinks:
	   a 4:3 box above the heading pushes the hostel's name off the first
	   screenful, which is the one thing the hero has to get right. */
	.tl-hostel-hero__media { order: 2; }
	.tl-hostel-hero__pending { aspect-ratio: 16 / 9; }
}

@media (max-width: 560px) {
	.tl-hostel-contact__action {
		/* One per row, full width. Two half-width buttons at 375px leaves each
		   about 150px, and a phone number does not fit in 150px. */
		width: 100%;
		justify-content: center;
	}
}

/* --------------------------------------------------------------- hostel payments

   Three facts, kept visually separate because they are three: what the method
   is, where it stands among the options, and whether it can take a payment
   right now. Run together they read as "you can pay online today", which is not
   yet true.

   The rule line is bordered rather than merely coloured, so it survives a
   monochrome printout and a reader who cannot separate the hues. "We do not
   take cash" is exactly the sort of thing somebody arrives at the door having
   got wrong.
*/
.tl-hostel-payment {
	max-width: 62ch;
	padding: var(--tl-space-5);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-lg);
	background: var(--tl-surface);
}

.tl-hostel-payment__head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tl-space-2);
	margin: 0 0 var(--tl-space-3);
}

.tl-hostel-payment__method {
	margin: 0;
	font-size: 1.25rem;
	line-height: 1.2;
}

.tl-badge--recommended {
	background: var(--tl-primary-50);
	color: var(--tl-primary-700);
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
}

/* Amber, and worded as a state rather than as a warning: nothing is broken,
   the hostel simply has not finished setting this up. */
.tl-badge--pending {
	background: var(--tl-warning-50);
	color: var(--tl-warning-ink, var(--tl-warning-700));
}

.tl-badge--ready {
	background: var(--tl-success-50);
	color: var(--tl-success-ink, var(--tl-success-700));
}

.tl-hostel-payment__rule {
	display: inline-block;
	margin: 0 0 var(--tl-space-4);
	padding: var(--tl-space-2) var(--tl-space-4);
	border: 2px solid var(--tl-primary-600);
	border-radius: var(--tl-radius-lg);
	background: var(--tl-surface);
	color: var(--tl-primary-700);
	font-weight: 700;
	letter-spacing: 0.02em;
	text-wrap: balance;
}

/*
 * The account itself. Label and value, stacked on a phone, with the number in
 * tabular figures and free to wrap anywhere: it is read a digit at a time and
 * copied by hand into a banking app, so it must never be re-flowed into
 * something a student could mis-transcribe.
 */
.tl-hostel-payment__account {
	display: grid;
	gap: 0;
	margin: 0 0 var(--tl-space-4);
	border: 1px solid var(--tl-border, #e2e5ea);
	border-radius: var(--tl-radius, 8px);
	overflow: hidden;
	background: var(--tl-surface, #fff);
	max-width: 100%;
}

.tl-hostel-payment__row {
	display: grid;
	grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
	gap: var(--tl-space-3);
	padding: var(--tl-space-2) var(--tl-space-3);
	border-bottom: 1px solid var(--tl-border, #e2e5ea);
}

.tl-hostel-payment__row:last-child { border-bottom: 0; }

.tl-hostel-payment__row dt {
	margin: 0;
	font-size: 0.85rem;
	color: var(--tl-muted, #5b6472);
}

.tl-hostel-payment__row dd {
	margin: 0;
	min-width: 0;
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	overflow-wrap: anywhere;
}

/* What to do, in the order it has to be done in. */
.tl-hostel-payment__steps {
	margin: 0 0 var(--tl-space-4);
	padding-inline-start: var(--tl-space-5);
	max-width: 62ch;
	line-height: 1.65;
}

.tl-hostel-payment__steps li { margin: 0 0 6px; }

/*
 * The sentence that stops a student arriving expecting a bed. It is the most
 * important line in the block and is set apart so it is not read as a footnote.
 */
.tl-hostel-payment__warn {
	margin: 0 0 var(--tl-space-4);
	padding: var(--tl-space-3);
	border-left: 4px solid var(--tl-accent, #4257a5);
	background: var(--tl-surface-alt, #f5f6f8);
	max-width: 62ch;
	line-height: 1.6;
}

/*
 * The way to the full rent page.
 *
 * Its own rule rather than a bare button, because this is the end of a long
 * block of account numbers and steps: the link needs air above it so a resident
 * scanning for "what do I actually do now" finds it, rather than reading it as
 * the last line of the warning it follows.
 */
.tl-hostel-payment__cta {
	margin: var(--tl-space-5) 0 0;
}

.tl-hostel-payment__instructions {
	font-weight: 600;
	color: var(--tl-text);
}

.tl-hostel-payment__note {
	max-width: 62ch;
	color: var(--tl-text-muted);
}

@media screen and (max-width: 600px) {
	.tl-hostel-payment { padding: var(--tl-space-4); }
	.tl-hostel-payment__rule { display: block; }
	/* Label above value: an 11rem label column is most of a phone. */
	.tl-hostel-payment__row { grid-template-columns: minmax(0, 1fr); gap: 2px; }
}

@media (forced-colors: active) {
	.tl-hostel-payment { border: 1px solid CanvasText; }
	.tl-badge--recommended,
	.tl-badge--pending,
	.tl-badge--ready { border: 1px solid CanvasText; }
}

/* ------------------------------------------------------------ hostel rooms

   Fourteen rooms, each with its bed count and its price. A grid rather than a
   table because the useful comparison is between rooms of similar size, which
   a reader makes by scanning across, and because a fourteen-row table on a
   phone is a fourteen-screen scroll.
*/
.tl-hostel-rooms {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
	gap: var(--tl-space-3);
	margin: var(--tl-space-5) 0;
}

.tl-hostel-room {
	padding: var(--tl-space-4);
	border: 1px solid var(--tl-border);
	border-radius: var(--tl-radius-lg);
	background: var(--tl-surface);
}

.tl-hostel-room__code {
	margin: 0 0 var(--tl-space-1);
	font-size: 1.05rem;
	line-height: 1.2;
}

.tl-hostel-room__beds {
	margin: 0 0 var(--tl-space-2);
	color: var(--tl-text-muted);
	font-size: 0.9rem;
}

.tl-hostel-room__price {
	margin: 0;
	font-weight: 700;
	color: var(--tl-primary-700);
}

.tl-hostel-room__price span {
	display: block;
	font-weight: 400;
	font-size: 0.8rem;
	color: var(--tl-text-muted);
}

.tl-hostel-rooms__note { color: var(--tl-text-muted); }

/* ------------------------------------------------------- how it works

   Numbered, because the order is the information. The number is drawn rather
   than left to the list marker so it survives the flex layout and stays
   aligned with the first line of a step that wraps to three.
*/
.tl-hostel-steps {
	display: grid;
	gap: var(--tl-space-4);
	max-width: 46rem;
	margin: var(--tl-space-5) 0 0;
	padding: 0;
	list-style: none;
	counter-reset: none;
}

.tl-hostel-step {
	display: flex;
	align-items: flex-start;
	gap: var(--tl-space-3);
}

.tl-hostel-step__number {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	border-radius: 999px;
	background: var(--tl-primary-600);
	color: #fff;
	font-weight: 700;
	font-size: 0.9rem;
}

.tl-hostel-step__body { display: block; }
.tl-hostel-step__title { display: block; }
.tl-hostel-step__text { display: block; color: var(--tl-text-muted); }

.tl-hostel-apply {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tl-space-3);
	margin-top: var(--tl-space-4);
}

@media screen and (max-width: 600px) {
	.tl-hostel-rooms { grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); }
	.tl-hostel-apply .tl-btn { width: 100%; text-align: center; }
}

@media (forced-colors: active) {
	.tl-hostel-room { border: 1px solid CanvasText; }
	.tl-hostel-step__number { border: 1px solid CanvasText; }
}

/*
 * The payment link the theme adds to the primary menu when the site owner has
 * not placed it themselves. It carries a class so it can be told apart in the
 * markup — it takes no styling of its own, because an item that looked
 * different from the ones beside it would look like a mistake rather than like
 * part of the menu.
 */
.tl-nav__added { /* intentionally identical to its siblings */ }

/* ==========================================================================
   BOOKING, STEP 2: ROOM SELECTION

   The photograph was removed from this step. Twelve of the thirteen rooms had
   no photograph and rendered a coloured panel with the room number printed
   inside it, which is decoration standing where booking information should be.
   The card now opens on the room number.

   Nothing here changes what is selected, what it costs or how it is counted.
   `.is-selected` is the state booking.js already sets; this only makes it
   visible.
   ========================================================================== */

.tl-results .tl-roomcard__media { display: none; }

.tl-results {
display: grid;
grid-template-columns: repeat( auto-fit, minmax( 17.5rem, 1fr ) );
gap: .875rem;
align-items: start;
}

.tl-roomcard {
display: flex;
flex-direction: column;
gap: .75rem;
height: auto;
min-height: 0;
padding: 1rem 1.125rem 1.125rem;
background: var( --tl-surface, #fff );
border: 1px solid var( --tl-border, #dfe5e0 );
border-radius: var( --tl-radius, .625rem );
box-shadow: none;
overflow: visible;
transition: border-color .15s ease, background-color .15s ease;
}

.tl-roomcard:hover { border-color: var( --tl-primary, #0b5d45 ); }

.tl-roomcard__body {
display: flex;
flex-direction: column;
gap: .2rem;
padding: 0;
}

.tl-roomcard__name {
margin: 0;
font-size: clamp( 1.125rem, 1rem + .5vw, 1.3125rem );
font-weight: 700;
line-height: 1.15;
letter-spacing: -.01em;
color: var( --tl-text, #1e2422 );
}

.tl-roomcard__meta {
margin: 0;
font-size: .8125rem;
font-weight: 600;
color: var( --tl-primary, #0b5d45 );
}

.tl-roomcard__amenities {
margin: .3rem 0 0;
font-size: .8125rem;
line-height: 1.5;
color: var( --tl-muted, #686c6b );
display: block;
max-height: none;
overflow: visible;
text-overflow: clip;
overflow-wrap: anywhere;
}

.tl-roomcard__foot {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
justify-content: space-between;
gap: .625rem;
margin-top: auto;
padding: .8rem 0 0;
border-top: 1px solid var( --tl-border, #dfe5e0 );
}

.tl-roomcard__price {
display: flex;
flex-direction: column;
gap: .1rem;
font-size: 1.0625rem;
font-weight: 700;
line-height: 1.15;
color: var( --tl-text, #1e2422 );
}

.tl-roomcard__price small {
font-size: .6875rem;
font-weight: 500;
letter-spacing: .01em;
color: var( --tl-muted, #686c6b );
}

.tl-roomcard__foot .tl-btn {
flex: 0 0 auto;
min-height: 2.75rem;
padding-inline: 1.125rem;
}

.tl-roomcard.is-selected {
border-color: var( --tl-primary, #0b5d45 );
box-shadow: inset 0 0 0 1px var( --tl-primary, #0b5d45 );
background: #f5faf7;
}

.tl-roomcard.is-selected .tl-roomcard__name::after {
content: "Selected";
display: inline-block;
margin-left: .5rem;
padding: .2em .55em;
font-size: .5625rem;
font-weight: 700;
letter-spacing: .07em;
text-transform: uppercase;
color: #fff;
background: var( --tl-primary, #0b5d45 );
border-radius: 999px;
vertical-align: middle;
}

.tl-roomcard .tl-btn:focus-visible {
outline: 2px solid var( --tl-primary, #0b5d45 );
outline-offset: 2px;
}

.tl-basket { align-self: start; }
.tl-basket__list { margin: 0; padding: 0; list-style: none; }

.tl-total__row {
display: flex;
justify-content: space-between;
gap: 1rem;
padding: .22rem 0;
font-size: .875rem;
}

@media ( min-width: 61.25em ) {
.tl-basket {
position: sticky;
top: 1.25rem;
max-height: none;
overflow: visible;
}
}

@media ( max-width: 37.5em ) {
.tl-results { grid-template-columns: 1fr; gap: .75rem; }
.tl-roomcard { padding: .875rem 1rem 1rem; gap: .625rem; }
.tl-roomcard__foot { align-items: center; }
.tl-roomcard__foot .tl-btn { flex: 1 1 8rem; }
}

/* ==========================================================================
   BOOKING UI v2

   A hospitality booking selector, not a form. Everything below is scoped to
   .tl-booking so no other page inherits it. No markup, no engine, no pricing
   and no selection logic is involved: this file only changes how the existing
   elements look. The step states (.is-done / .is-current) and the selected
   state (.is-selected) are set by booking.js already; this gives them a voice.
   ========================================================================== */

.tl-booking {
--bk-1: .5rem;   --bk-2: .75rem;  --bk-3: 1rem;    --bk-4: 1.5rem;
--bk-5: 2rem;    --bk-6: 2.5rem;  --bk-7: 3rem;    --bk-8: 4rem;
--bk-line: #e7ece8;
--bk-ink: #11201b;
--bk-soft: #6b7370;
--bk-green: var( --tl-primary, #0b5d45 );
--bk-wash: #f3f8f5;
max-width: 75rem;
margin-inline: auto;
}

.tl-booking__layout {
display: grid;
grid-template-columns: minmax( 0, 1fr ) 21.25rem;
gap: var( --bk-5 );
align-items: start;
}

/* Panels stop being boxes. The step is the page; it does not need a frame. */
.tl-booking .tl-panel {
background: none;
border: 0;
border-radius: 0;
box-shadow: none;
padding: 0;
}

.tl-booking .tl-panel__title {
margin: 0 0 var( --bk-2 );
font-size: clamp( 1.5rem, 1.15rem + 1.1vw, 2rem );
font-weight: 500;
line-height: 1.12;
letter-spacing: -.02em;
color: var( --bk-ink );
}

.tl-booking .tl-panel__hint {
margin: 0 0 var( --bk-5 );
max-width: 46ch;
font-size: .9375rem;
line-height: 1.6;
color: var( --bk-soft );
}

/* ---------------------------------------------------------------- progress */

.tl-booking .tl-steps {
display: grid;
grid-auto-flow: column;
grid-auto-columns: 1fr;
gap: 0;
margin: 0 0 var( --bk-6 );
padding: 0;
list-style: none;
border: 0;
background: none;
}

.tl-booking .tl-step {
position: relative;
display: flex;
flex-direction: column;
gap: .35rem;
padding: 0 0 var( --bk-2 );
background: none;
border: 0;
border-radius: 0;
border-bottom: 2px solid var( --bk-line );
color: var( --bk-soft );
}

.tl-booking .tl-step.is-done { border-bottom-color: var( --bk-green ); }

.tl-booking .tl-step.is-current {
border-bottom-color: var( --bk-green );
color: var( --bk-ink );
}

.tl-booking .tl-step__num {
font-size: .6875rem;
font-weight: 700;
letter-spacing: .14em;
text-transform: uppercase;
color: var( --bk-soft );
background: none;
border: 0;
border-radius: 0;
width: auto;
height: auto;
line-height: 1;
display: block;
}

.tl-booking .tl-step.is-done .tl-step__num,
.tl-booking .tl-step.is-current .tl-step__num { color: var( --bk-green ); }

.tl-booking .tl-step__num::before { content: "0"; }

.tl-booking .tl-step__label {
font-size: .9375rem;
font-weight: 500;
letter-spacing: -.01em;
line-height: 1.25;
}

.tl-booking .tl-step.is-current .tl-step__label { font-weight: 600; }

.tl-booking .tl-step.is-done .tl-step__label { color: var( --bk-soft ); }

/* On a phone four columns is unreadable, so only the current step is shown,
   with the rail behind it carrying the progress. */
@media ( max-width: 46.875em ) {
.tl-booking .tl-steps {
grid-auto-flow: row;
grid-auto-columns: auto;
position: relative;
border-bottom: 2px solid var( --bk-line );
padding-bottom: var( --bk-2 );
}

.tl-booking .tl-step { display: none; border-bottom: 0; padding-bottom: 0; }
.tl-booking .tl-step.is-current { display: flex; }

.tl-booking .tl-step.is-current .tl-step__num::after { content: " of 04"; }

.tl-booking .tl-steps::after {
content: "";
position: absolute;
left: 0;
bottom: -2px;
height: 2px;
background: var( --bk-green );
width: 25%;
transition: width .25s ease;
}

.tl-booking .tl-steps:has( li:nth-child(2).is-current )::after { width: 50%; }
.tl-booking .tl-steps:has( li:nth-child(3).is-current )::after { width: 75%; }
.tl-booking .tl-steps:has( li:nth-child(4).is-current )::after { width: 100%; }
}

/* ------------------------------------------------------------- room option */

.tl-booking .tl-results {
display: grid;
grid-template-columns: 1fr;
gap: 0;
align-items: stretch;
border-top: 1px solid var( --bk-line );
}

.tl-booking .tl-roomcard {
display: grid;
grid-template-columns: minmax( 0, 1fr ) auto;
grid-template-areas: "body side";
align-items: start;
column-gap: var( --bk-5 );
row-gap: var( --bk-3 );
padding: var( --bk-4 ) var( --bk-2 );
background: none;
border: 0;
border-bottom: 1px solid var( --bk-line );
border-radius: 0;
box-shadow: none;
height: auto;
min-height: 0;
overflow: visible;
transition: background-color .18s ease;
}

.tl-booking .tl-roomcard:hover { background: #fbfdfc; }

.tl-booking .tl-roomcard__body {
grid-area: body;
display: flex;
flex-direction: column;
gap: .3rem;
padding: 0;
min-width: 0;
}

.tl-booking .tl-roomcard__name {
margin: 0;
font-size: 1.25rem;
font-weight: 500;
line-height: 1.15;
letter-spacing: -.015em;
color: var( --bk-ink );
}

.tl-booking .tl-roomcard__name::before {
content: "Room";
display: block;
margin-bottom: .3rem;
font-family: var( --tl-font );
font-size: .625rem;
font-weight: 700;
letter-spacing: .18em;
text-transform: uppercase;
color: var( --bk-green );
}

.tl-booking .tl-roomcard__meta {
margin: 0;
font-size: .8125rem;
font-weight: 500;
color: var( --bk-soft );
}

.tl-booking .tl-roomcard__amenities {
margin: .45rem 0 0;
font-size: .8125rem;
line-height: 1.55;
color: var( --bk-soft );
display: block;
max-height: none;
overflow: visible;
overflow-wrap: anywhere;
}

.tl-booking .tl-roomcard__foot {
grid-area: side;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: flex-start;
gap: var( --bk-2 );
margin: 0;
padding: 0;
border: 0;
text-align: right;
}

.tl-booking .tl-roomcard__price {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: .1rem;
font-size: 1.25rem;
font-weight: 600;
line-height: 1.1;
letter-spacing: -.015em;
color: var( --bk-ink );
white-space: nowrap;
}

.tl-booking .tl-roomcard__price small {
font-size: .6875rem;
font-weight: 400;
letter-spacing: .01em;
color: var( --bk-soft );
white-space: nowrap;
}

.tl-booking .tl-roomcard__foot .tl-btn {
min-height: 2.75rem;
padding-inline: 1.25rem;
font-size: .8125rem;
letter-spacing: .02em;
}

.tl-booking .tl-roomcard.is-selected { background: var( --bk-wash ); }

.tl-booking .tl-roomcard.is-selected .tl-roomcard__name::before {
content: "Room  Selected";
}

.tl-booking .tl-roomcard .tl-btn:focus-visible {
outline: 2px solid var( --bk-green );
outline-offset: 2px;
}

/* ----------------------------------------------------------- your stay */

.tl-booking .tl-basket {
align-self: start;
padding: var( --bk-4 );
background: var( --tl-surface, #fff );
border: 1px solid var( --bk-line );
border-radius: .5rem;
box-shadow: none;
}

.tl-booking .tl-basket .tl-panel__title {
margin: 0 0 .15rem;
font-family: var( --tl-font );
font-size: .6875rem;
font-weight: 700;
letter-spacing: .18em;
text-transform: uppercase;
color: var( --bk-green );
}

.tl-booking .tl-basket .tl-panel__hint {
margin: 0 0 var( --bk-3 );
font-size: .8125rem;
color: var( --bk-soft );
}

.tl-booking .tl-basket__list {
margin: 0;
padding: 0;
list-style: none;
}

.tl-booking .tl-basket__item {
display: grid;
grid-template-columns: minmax( 0, 1fr ) auto;
gap: .15rem var( --bk-2 );
padding: var( --bk-2 ) 0;
border-top: 1px solid var( --bk-line );
font-size: .8125rem;
}

.tl-booking .tl-basket__name { font-weight: 600; color: var( --bk-ink ); }
.tl-booking .tl-basket__price { font-weight: 600; text-align: right; white-space: nowrap; }

.tl-booking .tl-total {
margin-top: var( --bk-3 );
padding-top: var( --bk-2 );
border-top: 1px solid var( --bk-line );
}

.tl-booking .tl-total__row {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var( --bk-3 );
padding: .22rem 0;
font-size: .8125rem;
color: var( --bk-soft );
}

.tl-booking .tl-total__row b { color: var( --bk-ink ); font-weight: 600; }

.tl-booking .tl-total__row--grand {
margin-top: var( --bk-2 );
padding-top: var( --bk-2 );
border-top: 1px solid var( --bk-line );
font-size: .6875rem;
font-weight: 700;
letter-spacing: .16em;
text-transform: uppercase;
color: var( --bk-soft );
}

.tl-booking .tl-total__row--grand b {
font-size: 1.375rem;
font-weight: 600;
letter-spacing: -.02em;
color: var( --bk-green );
}

.tl-booking .tl-total__prompt {
margin: var( --bk-2 ) 0 0;
font-size: .75rem;
line-height: 1.5;
color: var( --bk-soft );
}

@media ( min-width: 61.25em ) {
.tl-booking .tl-basket { position: sticky; top: 1.5rem; max-height: none; overflow: visible; }
}

/* ------------------------------------------------- details, confirm, buttons */

.tl-booking .tl-fields {
display: grid;
grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
gap: var( --bk-4 ) var( --bk-4 );
max-width: 46rem;
}

.tl-booking .tl-field { display: flex; flex-direction: column; gap: .4rem; min-width: 0; }
.tl-booking .tl-field--full { grid-column: 1 / -1; }

.tl-booking .tl-field label {
font-size: .6875rem;
font-weight: 700;
letter-spacing: .12em;
text-transform: uppercase;
color: var( --bk-soft );
}

.tl-booking .tl-field input,
.tl-booking .tl-field select,
.tl-booking .tl-field textarea {
width: 100%;
min-height: 2.875rem;
padding: .6rem .85rem;
font-family: inherit;
font-size: .9375rem;
color: var( --bk-ink );
background: var( --tl-surface, #fff );
border: 1px solid var( --bk-line );
border-radius: .375rem;
box-shadow: none;
transition: border-color .15s ease, box-shadow .15s ease;
}

.tl-booking .tl-field textarea { min-height: 6rem; resize: vertical; }

.tl-booking .tl-field input:focus,
.tl-booking .tl-field select:focus,
.tl-booking .tl-field textarea:focus {
outline: none;
border-color: var( --bk-green );
box-shadow: 0 0 0 3px rgba( 11, 93, 69, .12 );
}

.tl-booking .tl-field input:required:user-invalid { border-color: #b4553f; }

/* Step 4 reads as a review, not a receipt dump. */
.tl-booking [data-tl-pane="4"] > div > p:first-child {
margin: 0 0 var( --bk-3 );
padding-bottom: var( --bk-3 );
border-bottom: 1px solid var( --bk-line );
font-size: 1rem;
font-weight: 500;
color: var( --bk-ink );
}

.tl-booking [data-tl-pane="4"] > div {
max-width: 34rem;
padding: var( --bk-4 );
background: var( --tl-surface, #fff );
border: 1px solid var( --bk-line );
border-radius: .5rem;
}

.tl-booking [data-tl-pane="4"] > div > p:last-of-type {
margin: var( --bk-3 ) 0 0;
padding-top: var( --bk-3 );
border-top: 1px solid var( --bk-line );
font-size: .8125rem;
color: var( --bk-soft );
}

/* Buttons: one confident primary, everything else quieter. */
.tl-booking .tl-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 2.875rem;
padding-inline: 1.4rem;
font-size: .9375rem;
font-weight: 600;
letter-spacing: .01em;
border-radius: .375rem;
box-shadow: none;
transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}

.tl-booking .tl-btn--ghost {
background: none;
border: 1px solid var( --bk-line );
color: var( --bk-ink );
}

.tl-booking .tl-btn--ghost:hover { border-color: var( --bk-green ); color: var( --bk-green ); }
.tl-booking .tl-btn--primary:hover { background: #0a4e3a; }
.tl-booking .tl-btn:focus-visible { outline: 2px solid var( --bk-green ); outline-offset: 2px; }

/* ------------------------------------------------------------- responsive */

@media ( max-width: 68.75em ) {
.tl-booking__layout { grid-template-columns: minmax( 0, 1fr ); gap: var( --bk-5 ); }
.tl-booking .tl-basket { position: static; }
}

@media ( max-width: 46.875em ) {
.tl-booking .tl-fields { grid-template-columns: minmax( 0, 1fr ); gap: var( --bk-3 ); }
.tl-booking .tl-roomcard {
grid-template-columns: minmax( 0, 1fr );
grid-template-areas: "body" "side";
padding: var( --bk-3 ) 0;
row-gap: var( --bk-2 );
}
.tl-booking .tl-roomcard__foot {
flex-direction: row;
align-items: center;
justify-content: space-between;
text-align: left;
width: 100%;
}
.tl-booking .tl-roomcard__price { align-items: flex-start; }
.tl-booking .tl-roomcard__foot .tl-btn { flex: 0 0 auto; min-width: 7.5rem; }
.tl-booking [data-tl-pane="4"] > div { padding: var( --bk-3 ); }
.tl-booking .tl-panel p .tl-btn { flex: 1 1 10rem; }
}

/* --------------------------------------------------------------- v2 fixes */

/* booking.js moves focus to the step heading when the step changes, which is
   right for a screen reader and wrong for a mouse: it painted a ring round the
   title. Keyboard users still get the ring. */
.tl-booking .tl-panel__title:focus:not( :focus-visible ) { outline: none; }

/* The theme styles the step number as a filled disc, and its state selectors
   out-rank the reset above, so the disc survived and swallowed the numeral. */
.tl-booking .tl-step .tl-step__num,
.tl-booking .tl-step.is-done .tl-step__num,
.tl-booking .tl-step.is-current .tl-step__num {
display: block;
width: auto;
height: auto;
min-width: 0;
padding: 0;
background: none;
border: 0;
border-radius: 0;
box-shadow: none;
line-height: 1;
font-size: .6875rem;
font-weight: 700;
letter-spacing: .14em;
color: var( --bk-soft );
}

.tl-booking .tl-step.is-done .tl-step__num,
.tl-booking .tl-step.is-current .tl-step__num { color: var( --bk-green ); }

/* The step heading carries tabindex="-1" and is focused by script purely to
   move the screen reader to the new step. It cannot be reached with Tab, so
   removing its ring costs no keyboard operability, and every real control
   below keeps its focus ring. */
.tl-booking .tl-panel__title[tabindex="-1"]:focus { outline: none; }

/* One selected indicator, not two: the v1 pill stays, the eyebrow stops
   repeating it. */
.tl-booking .tl-roomcard.is-selected .tl-roomcard__name::before { content: "Room"; }

/* ---- Booking v2, round three ----------------------------------------- */

/* The remove link belongs under the room it removes, not centred in the row. */
.tl-booking .tl-basket__item .tl-basket__remove { justify-self: start; text-align: left; }

/* On Confirm the review is the summary, so the sticky rail stands down rather
   than printing the same rooms and the same total a second time. */
.tl-booking__layout:has( > div > .tl-panel:nth-child( 4 ):not( [hidden] ) ) { grid-template-columns: minmax( 0, 1fr ); }
.tl-booking__layout:has( > div > .tl-panel:nth-child( 4 ):not( [hidden] ) ) .tl-basket { display: none; }
.tl-booking .tl-panel:nth-child( 4 ) > div { max-width: 40rem; }

/* Required is now carried by a quiet word in the label rather than by colouring
   every empty field like a mistake. The required attribute still does the work
   for assistive technology; this is only the visible cue. */
.tl-booking .tl-field:has( > input:required, > select:required, > textarea:required ) > label::after {
content: " required";
margin-left: .375rem;
font-size: .5625rem;
font-weight: 500;
letter-spacing: .12em;
color: var( --bk-soft );
}
.tl-booking .tl-field > label::after { text-transform: none; letter-spacing: 0; font-size: .625rem; }

/* One page, one scrollbar. The summary had a capped height with its own
   overflow at narrow widths, which put a second scrollport inside the page.
   Cleared at every width, not only on desktop. */
.tl-booking .tl-basket { max-height: none; overflow: visible; }

/* The summary was bleeding 20px either side against a 16px page gutter, which
   pushed horizontal scroll onto phones and left the summary wider than the
   room list on tablets. It now lines up with the room list at every width
   below the two-column breakpoint. */
@media ( max-width: 61.25em ) {
.tl-booking .tl-basket { margin-inline: 0; }
}

/* Two columns from 980px rather than 1088px, so a 1024 tablet in landscape
   gets the room list and the summary side by side instead of the phone stack.
   The rail narrows with the viewport and stops at the desktop width. */
@media ( min-width: 61.25em ) {
.tl-booking .tl-booking__layout { grid-template-columns: minmax( 0, 1fr ) clamp( 18rem, 28vw, 21.25rem ); }
}

/* ==========================================================================
   Booking V3
   The summary on the right carries the stay, the money and the action for
   whichever step is open, so the primary control sits beside the total rather
   than below a long room list. On phones that same block becomes the bar at
   the bottom of the screen. One set of buttons throughout: the nodes are the
   originals, moved in the template, so their handlers are untouched.
   ========================================================================== */

@media ( min-width: 61.25em ) {
.tl-booking .tl-booking__layout { grid-template-columns: minmax( 0, 1fr ) clamp( 19rem, 30vw, 22rem ); }
}

/* ---- room card ---------------------------------------------------------- */

.tl-booking .tl-roomcard { padding: 1.25rem var( --bk-2 ); row-gap: .125rem; }
.tl-booking .tl-roomcard__type { margin: .1875rem 0 0; font-size: .9375rem; font-weight: 500; color: var( --bk-ink ); }
.tl-booking .tl-roomcard__meta { margin: .0625rem 0 0; font-size: .8125rem; color: var( --bk-soft ); }
.tl-booking .tl-roomcard__amenities { margin: .5rem 0 0; font-size: .8125rem; color: var( --bk-soft ); }

/* ---- the stay line ------------------------------------------------------ */

.tl-booking .tl-basket__dates {
margin: .75rem 0 0;
padding-top: .75rem;
border-top: 1px solid var( --bk-line );
font-size: .8125rem;
color: var( --bk-soft );
font-variant-numeric: tabular-nums;
}
.tl-booking .tl-basket__dates:empty { display: none; }

/* ---- the action block --------------------------------------------------- */

.tl-booking .tl-basket__actions { margin-top: 1.25rem; }

/* Only the pair belonging to the open step is shown. */
.tl-booking .tl-basket__actions [data-tl-act] { display: none; }
.tl-booking__layout:has( [data-tl-pane="2"]:not( [hidden] ) ) .tl-basket__actions [data-tl-act="2"],
.tl-booking__layout:has( [data-tl-pane="3"]:not( [hidden] ) ) .tl-basket__actions [data-tl-act="3"],
.tl-booking__layout:has( [data-tl-pane="4"]:not( [hidden] ) ) .tl-basket__actions [data-tl-act="4"] { display: flex; }

.tl-booking .tl-basket__actions .tl-btn--primary,
.tl-booking .tl-basket__actions .tl-btn--accent {
width: 100%;
min-height: 46px;
margin: 0;
justify-content: center;
}

.tl-booking .tl-basket__actions .tl-btn--ghost {
width: 100%;
min-height: 44px;
margin: .5rem 0 0;
padding: 0;
justify-content: center;
background: none;
border: 0;
box-shadow: none;
font-size: .8125rem;
font-weight: 400;
color: var( --bk-soft );
text-decoration: underline;
text-underline-offset: 3px;
}
.tl-booking .tl-basket__actions .tl-btn--ghost:hover { color: var( --bk-green ); background: none; }

/* The compact facts line belongs to the phone bar only. */
.tl-booking .tl-basket__facts { display: none; margin: 0; }

/* ---- phones and small tablets: the actions become the bottom bar --------- */

@media ( max-width: 61.25em ) {
.tl-booking .tl-basket__actions {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 40;
margin: 0;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: .5rem .75rem;
padding: .75rem 1rem;
padding-bottom: calc( .75rem + env( safe-area-inset-bottom, 0px ) );
background: #fff;
border-top: 1px solid var( --bk-line );
box-shadow: 0 -2px 12px rgba( 17, 32, 27, .07 );
}

.tl-booking .tl-basket__facts {
display: block;
flex: 1 1 100%;
font-size: .8125rem;
color: var( --bk-soft );
font-variant-numeric: tabular-nums;
}

.tl-booking .tl-basket__actions .tl-btn--primary,
.tl-booking .tl-basket__actions .tl-btn--accent { flex: 1 1 58%; width: auto; }

.tl-booking .tl-basket__actions .tl-btn--ghost { flex: 0 1 auto; width: auto; margin: 0; padding: 0 .5rem; }

/* the room list clears the bar */
.tl-booking { padding-bottom: 9rem; }
}


/* V3 STICKY BAND REPAIR - restore the sticky summary between 61.25em and 68.75em,
   where a V2 rule still forces position: static. */
@media ( min-width: 61.25em ) {
	.tl-booking .tl-basket { position: sticky; top: 1.5rem; max-height: none; overflow: visible; }
}


/* V3 CONFIRM STEP ACTION REPAIR - the step-4 actions now live inside the summary,
   so the rail must not be display:none on Confirm. Keep only the actions. */
.tl-booking__layout:has( > div > .tl-panel:nth-child( 4 ):not( [hidden] ) ) .tl-basket {
	display: block;
	position: static;
	max-width: 40rem;
	margin: 0;
	padding: 0;
	background: none;
	border: 0;
	box-shadow: none;
}
.tl-booking__layout:has( > div > .tl-panel:nth-child( 4 ):not( [hidden] ) ) .tl-basket > :not( .tl-basket__actions ) { display: none; }
@media ( min-width: 61.25em ) {
	.tl-booking__layout:has( > div > .tl-panel:nth-child( 4 ):not( [hidden] ) ) .tl-basket__actions { margin-top: 1.75rem; }
}


/* V3 TOUCH TARGET FLOOR - keep the secondary action at least 44px wide in the mobile bar. */
@media ( max-width: 61.25em ) {
	.tl-booking .tl-basket__actions .tl-btn--ghost { min-width: 44px; padding: 0 .75rem; }
}
