/*
 * Little Feast — front-end stylesheet.
 *
 * Structure mirrors the reference theme measured in the design plan: 1200px
 * container, 36px pill buttons, 24px cards, a 30px white panel floating on a
 * full-colour band, and a wave into a dark footer. The ink is the brand's own,
 * per Little_Feast_Color_Palette.md.
 *
 * Contents
 *   1  Tokens
 *   2  Reset and base
 *   3  Typography
 *   4  Layout helpers
 *   5  Buttons
 *   6  Section header block
 *   7  Announcement bar, header, navigation, drawer
 *   8  Hero and the card row
 *   9  The food
 *  10  Sangita
 *  11  How it works
 *  12  Halal band
 *  13  Plans
 *  14  For schools
 *  15  Closing and the form
 *  16  Wave and footer
 *  17  Mobile bottom bar
 *  18  Motion
 */

/* =========================================================================
   1  Tokens
   ========================================================================= */

:root {
	/* Ink — role names match the reference so the mapping stays checkable. */
	--main: #1c4b3a;
	--main-darker: #143829;
	--main-lighter: rgba(28, 75, 58, 0.5);
	--second: #a34a28;
	--second-darker: #8a3e21;
	--black: #1f2421;
	--black-light: rgba(31, 36, 33, 0.7);
	--gray: #f7f1e4;
	--gray-lighter: rgba(247, 241, 228, 0.5);
	--white: #fcfaf6;
	--white-pure: #ffffff;

	/* Support */
	--clay: #c15b33;
	--amber: #d89b3f;
	--muted: #5e6360;
	--rule: #e4ded1;
	--footer: #143829;
	--footer-text: #c7d2c8;

	/* Focus — two grounds, two values. Clay on forest is 1.69:1 and fails. */
	--focus: #a34a28;
	--focus-dark: #d89b3f;

	/* Faces */
	--font-main: 'Open Sans', system-ui, -apple-system, sans-serif;
	--font-headers: 'Fira Sans', system-ui, -apple-system, sans-serif;
	--font-subheaders: 'Sacramento', cursive;

	/* Fluid scale, 375px → 1440px */
	--fs-display: clamp(2.375rem, 1.4507rem + 3.944vw, 5rem);
	--fs-h2: clamp(1.875rem, 1.4789rem + 1.69vw, 3rem);
	--fs-h3: clamp(1.5rem, 1.368rem + 0.563vw, 1.875rem);
	--fs-h5: clamp(1.25rem, 1.162rem + 0.376vw, 1.5rem);
	--fs-script: clamp(1.75rem, 1.4859rem + 1.127vw, 2.5rem);
	--fs-lead: clamp(1.0625rem, 0.9965rem + 0.282vw, 1.25rem);
	--fs-body: 1rem;
	--fs-small: 0.875rem;

	/* Rhythm */
	--section-y: clamp(3.75rem, 2.4294rem + 5.634vw, 7.5rem);
	--container: 1440px;
	--prose: 760px;
	--gutter: 24px;

	/* Radii */
	--r-btn: 36px;
	--r-card: 24px;
	--r-panel: 30px;
	--r-sm: 8px;

	/* Elevation — used twice on the whole page, deliberately. */
	--shadow-header: 0 2px 16px rgba(31, 36, 33, 0.08);
	--shadow-panel: 0 20px 60px rgba(20, 56, 41, 0.18);

	/* Layers */
	--z-announce: 10;
	--z-header: 40;
	--z-bottombar: 50;
	--z-drawer: 100;

	--ease: cubic-bezier(0.22, 0.61, 0.36, 1);
	/* Expo-out. Nearly all of the distance is covered in the first third, so a
	   movement using it looks decided rather than gradual — which is most of
	   what separates an expensive-feeling hover from a sluggish one. */
	--ease-out: cubic-bezier(0.22, 1, 0.36, 1);

	color-scheme: light;
}

/* =========================================================================
   2  Reset and base
   ========================================================================= */

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

body,
h1, h2, h3, h4, h5, h6,
p, figure, blockquote, dl, dd, ul, ol {
	margin: 0;
}

ul[class],
ol[class] {
	padding: 0;
	list-style: none;
}

html {
	-webkit-text-size-adjust: 100%;
	/*
	 * No `scroll-behavior: smooth` here, deliberately, and it must not come back.
	 *
	 * ScrollTrigger measures by jumping the page to 0, reading every element's
	 * offset, and jumping back. With smooth scrolling on, those jumps ANIMATE:
	 * setting scrollTop to 0 and reading it on the next line still returns the
	 * old position, so every offset is recorded against a page that never moved.
	 *
	 * Measured: with the page at 5000px, one refresh moved every trigger 5000px
	 * earlier and the scrubbed animations stopped responding entirely. It does
	 * not show at load — the page is at the top then, so the error is zero — it
	 * shows after a resize, a rotation, or a phone browser hiding its own bar.
	 *
	 * The smooth scrolling itself is not lost: initSmoothAnchors() in
	 * assets/js/modules/anchors.js does it in JS, where it can be started and
	 * finished on demand rather than intercepting every programmatic scroll on
	 * the page. That module also honours prefers-reduced-motion, which this
	 * property could only do through the media query further down.
	 */
	/* Anchored sections must clear the sticky header — this still applies to
	   keyboard focus scrolling and to a page loaded on a #hash. */
	scroll-padding-top: 110px;
}

body {
	background: var(--white);
	color: var(--black);
	font-family: var(--font-main);
	font-size: var(--fs-body);
	line-height: 2;
	margin: 0;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	/* `clip`, not `hidden`, and the difference is not cosmetic.
	 *
	 * overflow-x: hidden makes the body a scroll container — the computed value
	 * becomes `hidden auto`, and a scroll container's own scrollTop is 0 no
	 * matter where the window is. ScrollTrigger reads that when it measures, so
	 * every start and end came out short by exactly the current scroll position:
	 * refreshing at 5000px moved every trigger on the page 5000px earlier, and
	 * the hero parallax, the card deal and the plans stagger all fired against
	 * offsets that no longer existed.
	 *
	 * `clip` cuts the overflow off without creating a scroll container, so the
	 * body stays unscrollable and the measurements stay right. Browsers too old
	 * to know it fall back to `visible`, which at worst permits a horizontal
	 * scrollbar — and there is nothing overflowing at any width to cause one. */
	overflow-x: clip;
}

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

button,
input,
select,
textarea {
	font: inherit;
	color: inherit;
}

a {
	color: var(--main);
	text-decoration-thickness: 1px;
	text-underline-offset: 3px;
}

a:hover {
	color: var(--second);
}

/* Focus. Never removed, only replaced — and it changes with the ground. */
:focus-visible {
	outline: 3px solid var(--focus);
	outline-offset: 2px;
	border-radius: 2px;
}

/* The hero is no longer on this list — it is a light ground now, so it takes
   the ordinary clay ring like the rest of the page. */
.lf-band :focus-visible,
.lf-footer :focus-visible,
.lf-announce :focus-visible {
	outline-color: var(--focus-dark);
}

/* Skip link — first focusable element on the page. */
.lf-skip {
	position: absolute;
	left: 50%;
	top: 0;
	transform: translate(-50%, -120%);
	z-index: 200;
	background: var(--main);
	color: var(--white-pure);
	padding: 14px 28px;
	border-radius: 0 0 var(--r-sm) var(--r-sm);
	font-family: var(--font-headers);
	font-weight: 600;
	text-decoration: none;
	transition: transform 0.2s var(--ease);
}

.lf-skip:focus {
	transform: translate(-50%, 0);
	color: var(--white-pure);
}

.lf-sr {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* =========================================================================
   3  Typography
   ========================================================================= */

h1, h2, h3, h4, h5, h6 {
	font-family: var(--font-headers);
	font-weight: 700;
	line-height: 1.3;
	color: var(--black);
}

p + p {
	margin-top: 1.1em;
}

strong {
	font-weight: 700;
}

.lf-lead {
	font-size: var(--fs-lead);
	line-height: 1.8;
	color: var(--muted);
}

.lf-small {
	font-size: var(--fs-small);
}

/* Prices, calories, per-lunch figures — no column jitter. */
.lf-num {
	font-variant-numeric: tabular-nums;
}

/* =========================================================================
   4  Layout helpers
   ========================================================================= */

.lf-container {
	width: 100%;
	max-width: calc(var(--container) + var(--gutter) * 2);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

.lf-prose {
	max-width: var(--prose);
	margin-inline: auto;
}

.lf-section {
	padding-block: var(--section-y);
	position: relative;
}

.lf-section--gray {
	background: var(--gray);
}

.lf-section--paper {
	background: var(--white);
}

/* A full-colour band: the reference's signature move. */
.lf-band {
	background-color: var(--main);
	color: var(--gray);
	position: relative;
	overflow: hidden;
}

.lf-band h1,
.lf-band h2,
.lf-band h3 {
	color: var(--white-pure);
}

.lf-band a {
	color: var(--gray);
}

.lf-band a:hover {
	color: var(--amber);
}

/* Low enough to read as texture rather than as a second layer of content —
   the prose on these bands is the most load-bearing on the page. */
.lf-band__pattern {
	position: absolute;
	inset: 0;
	opacity: 0.045;
	background-repeat: repeat;
	background-size: 300px;
	pointer-events: none;
}

.lf-band > .lf-container {
	position: relative;
}

/* A flat field of one green over a section this tall reads as a coloured box.
   Lifting the middle and dropping the corners by a few per cent gives it the
   depth of a lit wall instead — no visible gradient, just a ground that is not
   the same everywhere. */
.lf-band--vignette::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background:
		radial-gradient(120% 90% at 50% 30%, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0) 60%),
		radial-gradient(100% 80% at 50% 110%, rgba(0, 0, 0, 0.22), rgba(0, 0, 0, 0) 70%);
}

/* The white panel floating on a band or a tint. */
.lf-panel {
	background: var(--white-pure);
	border-radius: var(--r-panel);
	box-shadow: var(--shadow-panel);
	padding: clamp(2rem, 1rem + 4vw, 4.5rem) clamp(1.25rem, 0.4rem + 3.6vw, 4rem);
}

/* =========================================================================
   5  Buttons
   ========================================================================= */

.lf-btn {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	min-height: 48px;
	padding: 20px 40px;
	border: 2px solid transparent;
	border-radius: var(--r-btn);
	font-family: var(--font-headers);
	font-size: 1rem;
	font-weight: 700;
	line-height: 1;
	text-align: center;
	text-decoration: none;
	cursor: pointer;
	touch-action: manipulation;
	/* Contains the wash below, so a z-index of -1 sinks behind the label but
	   never behind the section. Also clips the wash to the pill. */
	isolation: isolate;
	overflow: hidden;
	transition:
		color 0.3s var(--ease),
		border-color 0.3s var(--ease),
		transform 0.4s var(--ease-out),
		box-shadow 0.4s var(--ease-out);
}

/* The wash.
 *
 * A darker panel that rises from the bottom on hover rather than the colour
 * simply swapping. Two reasons it is a pseudo-element and not a background
 * transition: a transform animates on the compositor where a background-color
 * does not, and a wipe has a direction — it reads as a response to the pointer
 * arriving, which a crossfade never does.
 *
 * 101% rather than 100%: at some zoom levels a whole number leaves a hairline of
 * the panel showing along the bottom edge. */
.lf-btn::before {
	content: "";
	position: absolute;
	inset: -2px;
	z-index: -1;
	border-radius: inherit;
	transform: translate3d(0, 101%, 0);
	transition: transform 0.5s var(--ease-out);
}

.lf-btn:hover::before,
.lf-btn:focus-visible::before {
	transform: translate3d(0, 0, 0);
}

/* Lifted a couple of pixels with the shadow to match. Small on purpose — this
   is a button, not a card being picked up. */
.lf-btn:hover,
.lf-btn:focus-visible {
	transform: translateY(-2px);
	box-shadow: 0 12px 26px rgba(31, 36, 33, 0.18);
}

/* Pressed puts it back down, and faster than it came up, so the click has a
   floor to land on. */
.lf-btn:active {
	transform: translateY(0);
	box-shadow: 0 4px 10px rgba(31, 36, 33, 0.16);
	transition-duration: 0.08s;
}

/* -- The travelling icon ------------------------------------------------- */

/* A fixed window with two copies of the glyph in it. The one in view leaves to
   the right, the one waiting off to the left takes its place — so the icon
   moves through the button rather than nudging and springing back. */
.lf-btn__icon {
	position: relative;
	display: inline-block;
	width: 18px;
	height: 18px;
	flex: none;
	overflow: hidden;
}

.lf-btn__icon svg {
	position: absolute;
	left: 0;
	top: 0;
	transition: transform 0.45s var(--ease-out);
}

.lf-btn__icon svg:last-child {
	transform: translate3d(-160%, 0, 0);
}

.lf-btn:hover .lf-btn__icon svg:first-child,
.lf-btn:focus-visible .lf-btn__icon svg:first-child {
	transform: translate3d(160%, 0, 0);
}

.lf-btn:hover .lf-btn__icon svg:last-child,
.lf-btn:focus-visible .lf-btn__icon svg:last-child {
	transform: translate3d(0, 0, 0);
}

/* Going back, so the hand-off runs the other way. */
.lf-btn--back .lf-btn__icon svg:last-child {
	transform: translate3d(160%, 0, 0);
}

.lf-btn--back:hover .lf-btn__icon svg:first-child,
.lf-btn--back:focus-visible .lf-btn__icon svg:first-child {
	transform: translate3d(-160%, 0, 0);
}

.lf-btn--back:hover .lf-btn__icon svg:last-child,
.lf-btn--back:focus-visible .lf-btn__icon svg:last-child {
	transform: translate3d(0, 0, 0);
}

/* -- Variants ------------------------------------------------------------ */

/* White on clay-deep is 5.89:1. The brighter clay fails at this size. */
.lf-btn--primary {
	background: var(--second);
	border-color: var(--second);
	color: var(--white-pure);
}

.lf-btn--primary::before {
	background: var(--second-darker);
}

.lf-btn--primary:hover,
.lf-btn--primary:focus-visible {
	border-color: var(--second-darker);
	color: var(--white-pure);
}

.lf-btn--accent {
	background: var(--main);
	border-color: var(--main);
	color: var(--white-pure);
}

.lf-btn--accent::before {
	background: var(--main-darker);
}

.lf-btn--accent:hover,
.lf-btn--accent:focus-visible {
	border-color: var(--main-darker);
	color: var(--white-pure);
}

.lf-btn--outline {
	background: transparent;
	border-color: var(--main);
	color: var(--main);
}

.lf-btn--outline::before {
	background: var(--main);
}

.lf-btn--outline:hover,
.lf-btn--outline:focus-visible {
	color: var(--white-pure);
}

.lf-band .lf-btn--outline {
	border-color: var(--gray);
	color: var(--gray);
}

.lf-band .lf-btn--outline::before {
	background: var(--gray);
}

.lf-band .lf-btn--outline:hover,
.lf-band .lf-btn--outline:focus-visible {
	color: var(--main);
}

.lf-btn--sm {
	padding: 12px 26px;
	font-weight: 600;
}

.lf-btn--sm .lf-btn__icon {
	width: 16px;
	height: 16px;
}

.lf-btn--sm .lf-btn__icon svg {
	width: 16px;
	height: 16px;
}

.lf-btn[disabled],
.lf-btn[aria-disabled='true'] {
	opacity: 0.55;
	cursor: not-allowed;
}

.lf-btns {
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
}

.lf-btns--center {
	justify-content: center;
}

/* =========================================================================
   6  Section header block
   ========================================================================= */

.lf-shead {
	margin-bottom: clamp(2.5rem, 1.5rem + 3vw, 4rem);
}

.lf-shead--center {
	text-align: center;
}

.lf-shead--center .lf-shead__lead {
	margin-inline: auto;
}

.lf-shead__script {
	display: block;
	font-family: var(--font-subheaders);
	font-size: var(--fs-script);
	font-weight: 400;
	line-height: 1.1;
	color: var(--main);
	/* Sits slightly into the headline's cap height, as the reference does. */
	margin-bottom: -0.1em;
}

.lf-band .lf-shead__script {
	color: var(--amber);
}

.lf-shead__title {
	font-size: var(--fs-h2);
}

.lf-shead__rule {
	width: 60px;
	height: 3px;
	background: var(--second);
	border: 0;
	margin: 20px 0 0;
	border-radius: 2px;
}

.lf-shead--center .lf-shead__rule {
	margin-inline: auto;
}

.lf-shead__lead {
	max-width: var(--prose);
	margin-top: 20px;
	font-size: var(--fs-lead);
	line-height: 1.8;
	color: var(--muted);
}

.lf-band .lf-shead__lead {
	color: var(--gray);
}

/* =========================================================================
   7  Announcement bar, header, navigation, drawer
   ========================================================================= */

.lf-announce {
	position: relative;
	z-index: var(--z-announce);
	background: var(--footer);
	color: var(--gray);
	font-size: var(--fs-small);
	line-height: 1.5;
}

.lf-announce__inner {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 16px;
	min-height: 44px;
	padding-block: 8px;
	text-align: center;
}

/* With contacts alongside it the message stops being centred on the page and
   becomes the left-hand item. Space is left at the right for the dismiss
   button, which is absolutely positioned over this row. */
.lf-announce__inner--split {
	justify-content: space-between;
	gap: 24px;
	text-align: left;
}

.lf-announce__contact {
	display: flex;
	align-items: center;
	gap: 22px;
	margin: 0;
	padding: 0;
	list-style: none;
	/* Stops the sentence at the left squeezing the details down to two
	   characters and an ellipsis on a mid-width screen. */
	flex: none;
}

.lf-announce__contact a {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	color: inherit;
	text-decoration: none;
	white-space: nowrap;
	/* The bar is 44px and these sit inside it, so the block itself cannot carry
	   a 44px target. The padding widens it as far as the strip allows. */
	padding-block: 6px;
	transition: color 0.2s var(--ease);
}

.lf-announce__contact a:hover {
	color: var(--white-pure);
}

.lf-announce__contact svg {
	width: 15px;
	height: 15px;
	flex: none;
	color: var(--amber);
}

/* A divider between the two, drawn rather than typed so it is not read out. */
.lf-announce__contact li + li {
	position: relative;
	padding-left: 22px;
}

.lf-announce__contact li + li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 50%;
	width: 1px;
	height: 14px;
	transform: translateY(-50%);
	background: rgba(255, 255, 255, 0.22);
}

.lf-announce__dismiss {
	position: absolute;
	right: 8px;
	top: 50%;
	transform: translateY(-50%);
	width: 44px;
	height: 44px;
	display: grid;
	place-items: center;
	background: none;
	border: 0;
	color: var(--gray);
	cursor: pointer;
	border-radius: 50%;
}

.lf-announce__dismiss:hover {
	background: rgba(255, 255, 255, 0.12);
}

/* The `hidden` attribute on this button does nothing without this rule.
 *
 * announce.js sets close.hidden = true above 991px, because the bar is only
 * dismissible on a phone by default. But `hidden` is just a UA stylesheet rule,
 * [hidden] { display: none }, and the `display: grid` above is an author rule —
 * so it won, and the control rendered on desktop regardless.
 *
 * It went unnoticed while the row carried 44px of padding-right reserving space
 * for it. With that padding gone the button sits on top of the last 8px of the
 * email address, and a click there dismissed the bar instead of opening mail.
 * (Same failure as the drawer panel further down, same one-line fix.) */
.lf-announce__dismiss[hidden] {
	display: none;
}

.lf-header {
	position: sticky;
	top: 0;
	z-index: var(--z-header);
	background: var(--white);
	transition: box-shadow 0.2s var(--ease), background-color 0.2s var(--ease);
}

.lf-header.is-stuck {
	box-shadow: var(--shadow-header);
}

.lf-header__inner {
	display: flex;
	align-items: center;
	gap: 24px;
	min-height: 100px;
	transition: min-height 0.2s var(--ease);
}

.lf-header.is-stuck .lf-header__inner {
	min-height: 72px;
}

.lf-logo {
	display: block;
	flex: none;
	line-height: 0;
}

.lf-logo svg,
.lf-logo img {
	width: 200px;
	height: auto;
}

/* Centred in the header, not pushed against the button.
 *
 * `flex: 1` on the nav with the logo and the button both `flex: none` gives the
 * nav all the leftover space, and centring inside it puts the middle of the menu
 * at the middle of that space. It is not the middle of the header — the logo and
 * the button are different widths, so it never could be both centred in the row
 * and centred between them. This centres it in the gap it actually occupies,
 * which is what reads as centred. */
.lf-nav {
	flex: 1;
	display: flex;
	justify-content: center;
	/* The row is 100px tall and the links are ~40px; without this they would sit
	   on the text baseline of a taller flex line. */
	align-items: center;
	min-width: 0;
}

.lf-nav__list {
	display: flex;
	align-items: center;
	gap: clamp(6px, 0.7vw, 14px);
	margin: 0;
	padding: 0;
	list-style: none;
}

.lf-nav__link {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 7px;
	padding: 9px 12px;
	border-radius: 999px;
	font-family: var(--font-headers);
	font-size: 1rem;
	font-weight: 600;
	line-height: 1.4;
	color: var(--black);
	text-decoration: none;
	white-space: nowrap;
	transition: color 0.25s var(--ease), background-color 0.25s var(--ease);
}

/* The icon is a marker to find a row by, not a second label. It stays quieter
   than the word beside it until the item is hovered or current, at which point
   it takes the accent — so the colour change reads as one movement across the
   whole item rather than the text changing and the icon sitting there. */
.lf-nav__icon {
	display: grid;
	place-items: center;
	width: 16px;
	height: 16px;
	flex: none;
	color: var(--muted);
	transition: color 0.25s var(--ease), transform 0.35s var(--ease-out);
}

.lf-nav__link:hover {
	color: var(--main);
	background: rgba(28, 75, 58, 0.07);
}

.lf-nav__link:hover .lf-nav__icon {
	color: var(--second);
	transform: translateY(-1px);
}

/* Current section.
 *
 * A filled pill rather than the underline this used to have. The underline sat
 * on the same line as the sticky header's own bottom edge and was easy to lose;
 * a ground behind the item cannot be. aria-current carries the same fact to a
 * screen reader, so this is drawing something already announced. */
.lf-nav__link[aria-current='true'] {
	color: var(--main);
	background: rgba(28, 75, 58, 0.1);
}

.lf-nav__link[aria-current='true'] .lf-nav__icon {
	color: var(--second);
}

/* A dot under the current item, drawn rather than typed. It is the part that
   survives when someone is hovering a different item at the same time — the
   hover ground and the current ground are close in weight, and without this
   there would be a moment where two items look equally selected. */
.lf-nav__link[aria-current='true']::after {
	content: "";
	position: absolute;
	left: 50%;
	bottom: 1px;
	width: 5px;
	height: 5px;
	border-radius: 50%;
	background: var(--second);
	transform: translateX(-50%);
}

.lf-header__cta {
	flex: none;
}

.lf-burger {
	display: none;
	width: 48px;
	height: 48px;
	margin-left: auto;
	padding: 0;
	background: none;
	border: 0;
	color: var(--black);
	cursor: pointer;
	place-items: center;
}

/* The dimmed page behind the panel. Kept just under the drawer's own layer so
   the panel sits on top of it and everything else sits under both. */
.lf-drawer__scrim {
	position: fixed;
	inset: 0;
	z-index: calc(var(--z-drawer) - 1);
	padding: 0;
	border: 0;
	background: rgba(20, 27, 23, 0.5);
	cursor: pointer;
	opacity: 0;
	transition: opacity 0.36s var(--ease-out);
}

.lf-drawer__scrim.is-open {
	opacity: 1;
}

/* A panel down the right-hand side, not a full-screen sheet.
 *
 * It comes in from the right because that is the side the burger is on — a menu
 * that opens away from the button that opened it reads as a different thing
 * arriving. The page stays visible beside it, which is what tells you what the
 * menu is covering and what closing it will give you back.
 *
 * Capped at 360px so it stays a panel on a large phone or a small tablet
 * instead of quietly becoming the full-screen sheet it replaced. */
.lf-drawer {
	position: fixed;
	top: 0;
	right: 0;
	bottom: 0;
	left: auto;
	z-index: var(--z-drawer);
	width: min(86vw, 360px);
	display: flex;
	flex-direction: column;
	background: var(--white);
	border-radius: var(--r-panel) 0 0 var(--r-panel);
	padding: 24px 20px 40px;
	overflow-y: auto;
	/* Deep and soft, thrown leftwards over the dimmed page — it is what makes
	   the panel read as being in front of the page rather than cut into it. */
	box-shadow: -24px 0 60px rgba(20, 27, 23, 0.28);
	transform: translateX(100%);

	/* visibility, because the `hidden` attribute on this element does nothing.
	 *
	 * The markup ships the panel with `hidden`, and drawer.js sets it back on
	 * after the slide out. Neither has any effect: `hidden` is only a UA
	 * stylesheet rule, `[hidden] { display: none }`, and the `display: flex`
	 * above is an author rule, so it wins every time. The panel is therefore in
	 * the layout at every width — parked off the right edge, invisible, and
	 * fully focusable. All eight controls in it sat in the tab order on every
	 * page, so a keyboard user tabbing out of the header fell into a menu that
	 * was not open and could not be seen.
	 *
	 * The scrim beside it is the proof of what was intended: it carries the same
	 * `hidden` attribute, sets no display of its own, and so is correctly
	 * display:none when closed.
	 *
	 * visibility rather than display, for two reasons: it takes the panel out of
	 * the tab order and out of the accessibility tree exactly as display:none
	 * would, and unlike display it can be transitioned — so the slide survives.
	 * The 0s duration with a 0.36s delay is what holds the panel visible for the
	 * length of the slide out and hides it the instant it lands. */
	visibility: hidden;
	transition:
		transform 0.36s var(--ease-out),
		visibility 0s linear 0.36s;
}

.lf-drawer.is-open {
	transform: translateX(0);
	/* No delay coming in: it has to be visible before it can slide. */
	visibility: visible;
	transition:
		transform 0.36s var(--ease-out),
		visibility 0s;
}

.lf-drawer__top {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 32px;
}

.lf-drawer__list {
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin: 0 0 32px;
	padding: 0;
	list-style: none;
}

.lf-drawer__list a {
	position: relative;
	display: flex;
	align-items: center;
	gap: 14px;
	padding: 14px 12px;
	border-bottom: 1px solid var(--rule);
	border-radius: var(--r-sm);
	font-family: var(--font-headers);
	font-size: 1.125rem;
	font-weight: 600;
	color: var(--black);
	text-decoration: none;
	transition: color 0.25s var(--ease), background-color 0.25s var(--ease);
}

/* A chip rather than a bare glyph. At this size the icons are the fastest thing
   to aim a thumb at, and a filled shape is a target where a 2px stroke is not. */
.lf-drawer__icon {
	display: grid;
	place-items: center;
	width: 38px;
	height: 38px;
	flex: none;
	border-radius: 12px;
	background: var(--gray);
	color: var(--main);
	transition: background-color 0.25s var(--ease), color 0.25s var(--ease);
}

.lf-drawer__list a:hover {
	color: var(--main);
}

/* Current section.
 *
 * A tinted row and a filled chip, plus a bar down the left edge. The bar is
 * what does the work: the drawer is a plain vertical list with no other colour
 * in it, and a row that is only slightly warmer than its neighbours is hard to
 * pick out at a glance on a phone held at arm's length. */
.lf-drawer__list a[aria-current='true'] {
	background: rgba(28, 75, 58, 0.08);
	color: var(--main);
}

.lf-drawer__list a[aria-current='true']::before {
	content: "";
	position: absolute;
	left: 0;
	top: 10px;
	bottom: 11px;
	width: 3px;
	border-radius: 0 3px 3px 0;
	background: var(--second);
}

.lf-drawer__list a:hover .lf-drawer__icon,
.lf-drawer__list a[aria-current='true'] .lf-drawer__icon {
	background: var(--main);
	color: var(--white-pure);
}

/* =========================================================================
   8  Hero and the card row
   ========================================================================= */

/* One screen, less the strip and the header above it — so the scroll cue at
   the foot of the hero actually lands inside the first viewport rather than
   just below it. */
.lf-hero {
	position: relative;
	min-height: min(calc(100vh - 150px), 830px);
	background: var(--white);
	color: var(--black);
	text-align: left;
	isolation: isolate;
	overflow: hidden;
}

.lf-hero__media {
	position: absolute;
	inset: 0;
	z-index: -2;
}

/* Centred rather than anchored to a corner.
 *
 * The reference pins its photograph top-right because its composition has one
 * plate up there and dead space elsewhere. Ours is a full-width field of
 * containers, and anchoring it threw away most of the left of the frame. The
 * hero is roughly 1.73:1 against the image's 1.78:1, so centred cover shows
 * very nearly the whole photograph — a few pixels off the sides, nothing off
 * the top or bottom. */
.lf-hero__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
}

/* One flat overlay across the whole section: white at 93%.
 *
 * Even coverage, so the photograph reads at the same strength everywhere
 * rather than being clear on one side and hidden on the other. */
.lf-hero__overlay {
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background: #ffffffed;
}

/* No photograph set: the uncovered corner gets the brand pattern on a warm
   ground so the composition still reads while waiting for artwork. */
.lf-hero__standin {
	position: absolute;
	inset: 0;
	display: block;
	background-color: var(--gray);
	background-repeat: repeat;
	background-size: 260px;
}

.lf-hero--noimage .lf-hero__standin::after {
	content: '';
	position: absolute;
	inset: 0;
	background: linear-gradient(135deg, rgba(28, 75, 58, 0.16), rgba(216, 155, 63, 0.22));
}

/* ---- The drift layer ---- */

/* Line icons moving slowly across the whole hero.
 *
 * z-index 0 puts them above the overlay — so they sit on the washed photograph
 * rather than under it, where they would be invisible — and the elements that
 * come after them in the markup, the plate at z-index 0 and the copy at 1, both
 * paint over the top. So nothing here can ever get between a reader and a word.
 *
 * inset:0 with overflow:hidden rather than letting the items escape: an icon
 * part-way through its travel would otherwise poke out past the section edge and
 * over the header. */
.lf-hero__drift {
	position: absolute;
	inset: 0;
	z-index: 0;
	overflow: hidden;
	pointer-events: none;
}

/* Each item is placed by percentage from the markup and given its own duration
   and a negative delay, so no two are in step.
 *
 * Faint on purpose. These pass behind a headline and a lead paragraph, and the
 * whole point is that the eye registers movement without ever resolving it into
 * something to look at — at a strength where you could read them, they would be
 * competing with the copy for the one thing the hero is for. */
.lf-hero__driftitem {
	position: absolute;
	display: block;
	line-height: 0;
	opacity: 0.38;
	animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1);
	animation-iteration-count: infinite;
	animation-fill-mode: both;
}

/* A heavier stroke than the icons carry elsewhere.
 *
 * They are printed at 26–40px from a 24px box, so the 2px stroke they are drawn
 * with scales DOWN in relative terms and comes out looking thin and washed at
 * this size. 2.6 puts the line back to the weight the eye reads as the same
 * family as the navigation icons. */
.lf-hero__driftitem svg {
	stroke-width: 2.6;
}

/* Four inks, all the brand's own — forest, amber, clay and clay-deep. Assigned
 * per item from the markup rather than by nth-child, so moving an icon in the
 * array takes its colour with it instead of reshuffling the whole sequence.
 *
 * The opacities differ per ink rather than being one number, because they do not
 * weigh the same against a near-white ground: amber is the palest of the four
 * and needs more to register, forest the darkest and needs less before it starts
 * competing with the headline. */
.lf-hero__driftitem--forest {
	color: var(--main);
	opacity: 0.42;
}

.lf-hero__driftitem--amber {
	color: var(--amber);
	opacity: 0.68;
}

.lf-hero__driftitem--clay {
	color: var(--clay);
	opacity: 0.55;
}

.lf-hero__driftitem--deep {
	color: var(--second);
	opacity: 0.46;
}

/* Four paths, assigned per item.
 *
 * Every one starts and ends on the identity transform. That is what makes the
 * loop seamless — and it is also what makes the reduced-motion case correct: the
 * global block further down collapses animation-duration to 0.001ms with one
 * iteration, which lands each icon on its 100% keyframe. Because 100% and 0% are
 * the same, they simply come to rest exactly where the markup placed them
 * instead of freezing mid-travel at an arbitrary offset. */
.lf-hero__driftitem--p1 {
	animation-name: lf-drift-1;
}

.lf-hero__driftitem--p2 {
	animation-name: lf-drift-2;
}

.lf-hero__driftitem--p3 {
	animation-name: lf-drift-3;
}

.lf-hero__driftitem--p4 {
	animation-name: lf-drift-4;
}

/* transform and opacity only — both composite on the GPU, so eight of these
   running at once cost no layout and no paint. Animating top/left here would
   reflow the hero on every frame. */
@keyframes lf-drift-1 {
	0%,
	100% {
		transform: translate3d(0, 0, 0) rotate(0deg);
	}

	25% {
		transform: translate3d(20px, -26px, 0) rotate(7deg);
	}

	50% {
		transform: translate3d(38px, 8px, 0) rotate(-5deg);
	}

	75% {
		transform: translate3d(14px, 28px, 0) rotate(6deg);
	}
}

@keyframes lf-drift-2 {
	0%,
	100% {
		transform: translate3d(0, 0, 0) rotate(0deg);
	}

	33% {
		transform: translate3d(-16px, -30px, 0) rotate(-8deg);
	}

	66% {
		transform: translate3d(10px, -14px, 0) rotate(5deg);
	}
}

@keyframes lf-drift-3 {
	0%,
	100% {
		transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
	}

	40% {
		transform: translate3d(-30px, 18px, 0) rotate(9deg) scale(1.08);
	}

	70% {
		transform: translate3d(-12px, 34px, 0) rotate(-6deg) scale(0.95);
	}
}

/* A slow orbit rather than a there-and-back, so at least one of the four does
   not visibly retrace its own path. */
@keyframes lf-drift-4 {
	0%,
	100% {
		transform: translate3d(0, 0, 0) rotate(0deg);
	}

	25% {
		transform: translate3d(24px, -18px, 0) rotate(90deg);
	}

	50% {
		transform: translate3d(0, -34px, 0) rotate(180deg);
	}

	75% {
		transform: translate3d(-24px, -18px, 0) rotate(270deg);
	}
}

/* The foreground plate.
 *
 * Sits above the overlay but below the copy, and straddles the overlay's edge
 * on purpose — an object crossing the boundary is what stops the two halves
 * reading as two separate panels stuck together.
 *
 * Sized against the viewport rather than the container so it keeps its
 * proportion to the photograph behind it at every width.
 *
 * Scoped to .lf-hero deliberately: this element used to carry Swiper's own
 * `swiper` class, which brought `.swiper { position: relative }` in at equal
 * specificity from a stylesheet enqueued after this one, and won. The class now
 * lives on the inner media element, but the extra ancestor stays — it costs
 * nothing and it keeps the positioning in this file's hands. */
.lf-hero .lf-hero__plate {
	--plate-rim: clamp(6px, 0.7vw, 11px);

	position: absolute;
	/* Hard against the right edge of the section. It used to be inset by
	   clamp(1.5rem, 2vw + 1rem, 7rem); the frame now starts at the very right
	   and the hero's own overflow:hidden trims it there. */
	right: 0;
	top: 50%;
	z-index: 0;
	/* Width and height set independently, so aspect-ratio is gone.
	 *
	 * It was aspect-ratio: 1 with a single width, which meant the two could only
	 * ever move together — widening the frame made it taller by the same amount.
	 * The height is right as it is, so the two are now stated separately: the
	 * height keeps exactly the 48vw/700px it had, and only the width grows.
	 *
	 * 52vw is not a round number picked by eye. The copy column ends 664px from
	 * the left at this container width, and the frame is flush to the right, so
	 * anything past about 53vw starts running under the lead paragraph — and the
	 * copy sits ABOVE the frame in the stack, so that shows up as grey text on a
	 * photograph rather than as a tidy overlap. 52vw leaves roughly 12px of
	 * clearance. Do not raise it without re-measuring against .lf-hero__lead. */
	width: clamp(320px, 52vw, 900px);
	height: clamp(320px, 48vw, 770px);
	transform: translateY(-50%);
	/* Rounded on the left, square on the right. It sits hard against the right
	   edge of the section now, so the two corners over there are off the page —
	   rounding them would put a pair of cream notches against the viewport edge
	   where the eye reads a cut-off, not a curve. */
	border-radius: var(--r-panel) 0 0 var(--r-panel);
	/* No overflow:hidden here. The rim is drawn on this element and would be
	   clipped by it; only .lf-hero__plate-media clips. */
	pointer-events: none;

	/* The rim. Three layers doing three jobs:
	   an outer cream ring wide enough to read as a frame edge rather than a
	   hairline border, a hairline of amber just inside it to catch the light,
	   and the drop shadow — a framed object casts one, a flat square looks like
	   a hole cut in the page. */
	box-shadow:
		0 0 0 var(--plate-rim) rgba(252, 250, 246, 0.94),
		0 0 0 calc(var(--plate-rim) + 1px) rgba(216, 155, 63, 0.55),
		0 30px 70px rgba(31, 36, 33, 0.26);
}

/* The clipped photograph. */
.lf-hero__plate-media {
	width: 100%;
	height: 100%;
	border-radius: inherit;
	overflow: hidden;
	/* Safari repaints the fade against the rounded corner badly without this. */
	isolation: isolate;
}

/* Swiper sets its own sizing on the wrapper and slides; the circle is the
   frame, so both have to fill it completely or the fade shows a seam. */
.lf-hero__plate-media .swiper-wrapper,
.lf-hero__plate-media .swiper-slide,
.lf-hero__plate-media > div {
	width: 100%;
	height: 100%;
}

.lf-hero__plate img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
}

/* ---- The plate's controls ---- */

/* The control layer sits inside the frame, along its bottom edge: navigation at
   the left, the loader at the right. Inside rather than below, because the frame
   is absolutely positioned over the hero and anything hung underneath it would
   be floating in the middle of the section attached to nothing. */
.lf-hero__plate-ui {
	position: absolute;
	z-index: 2;
	left: 0;
	right: 0;
	bottom: 0;
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 12px;
	padding: clamp(0.75rem, 1.2vw, 1.25rem);
	/* The frame sets pointer-events:none so it never swallows a click meant for
	   the copy behind it. The controls are the one part that must take them. */
	pointer-events: auto;
	/* A wash under the controls only. The photograph beneath them is whatever
	   was uploaded, and white glyphs on an unknown image is a coin toss. */
	background: linear-gradient(to top, rgba(20, 56, 41, 0.62), rgba(20, 56, 41, 0));
	/* Matches the frame: bottom-left only, because the bottom-right corner of
	   the frame is square now. A rounded corner here against a square one there
	   leaves a sliver of photograph showing under the wash. */
	border-radius: 0 0 0 calc(var(--r-panel) - 1px);
}

/* The copy layer has to stop swallowing clicks meant for the plate.
 *
 * .lf-hero__inner sits at z-index 1 and spans the whole hero; the plate and its
 * controls sit at z-index 0 underneath it. So every press on prev, next or
 * play/pause landed on .lf-hero__inner instead — document.elementFromPoint at
 * the centre of the next button returned DIV.lf-hero__inner, which is precisely
 * how the browser dispatches a real click. The controls looked fine and did
 * nothing.
 *
 * It did not show up in testing because a scripted el.click() dispatches
 * straight at the node and never consults the hit-test, so the buttons passed
 * every check while being unusable with a mouse.
 *
 * Raising the plate above the copy would fix the clicks and break something
 * worse — where the two overlap at narrower widths the photograph would then
 * paint over the lead paragraph. So the layer stays where it is and simply stops
 * intercepting: it is transparent to the pointer, and the things inside it that
 * are actually interactive opt back in. Nothing moves, nothing repaints. */
.lf-hero__inner {
	pointer-events: none;
}

.lf-hero__content,
.lf-hero__social {
	pointer-events: auto;
}

.lf-hero__plate-nav {
	display: flex;
	align-items: center;
	gap: 8px;
}

/* 44px, so these clear the touch minimum on their own rather than relying on
   the frame around them. */
.lf-hero__plate-btn {
	display: grid;
	place-items: center;
	width: 44px;
	height: 44px;
	padding: 0;
	border: 1px solid rgba(252, 250, 246, 0.34);
	border-radius: 50%;
	/* 0.72, and the number is derived rather than chosen by eye.
	 *
	 * These discs sit on whatever photograph the editor uploads, so the only
	 * safe assumption is the worst one: a white plate on a pale worktop filling
	 * the corner. White glyphs on a 42% forest disc over pure white computes to
	 * about 2.8:1 — the counter inside needs 4.5:1 as small text, and it would
	 * fail silently the day somebody swapped the picture. At 0.72 the same
	 * worst case computes to 5.4:1, so it holds whatever the photograph does. */
	background: rgba(20, 56, 41, 0.72);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
	color: var(--white-pure);
	cursor: pointer;
	transition:
		background-color 0.25s var(--ease),
		border-color 0.25s var(--ease),
		color 0.25s var(--ease),
		transform 0.25s var(--ease-out);
}

.lf-hero__plate-btn svg {
	width: 19px;
	height: 19px;
}

.lf-hero__plate-btn:hover {
	background: var(--second);
	border-color: var(--second);
	color: var(--white-pure);
	transform: translateY(-2px);
}

.lf-hero__plate-btn:active {
	transform: translateY(0);
}

/* The play/pause pair. One button, two glyphs, only one drawn at a time — the
   class is written by the JS alongside the accessible name, so what is shown and
   what is announced cannot disagree. */
.lf-hero__plate-resume {
	display: none;
}

.lf-hero__plate-btn--play.is-paused .lf-hero__plate-pause {
	display: none;
}

.lf-hero__plate-btn--play.is-paused .lf-hero__plate-resume {
	display: block;
}

/* ---- The circular loader ---- */

/* A disc with the countdown drawn around its edge and the position printed in
   the middle. It replaces the ring that used to run around the whole plate: at
   this size the arc is legible as a countdown, where a 0.6px stroke on a 600px
   circle was a hairline nobody could see moving. */
.lf-hero__plate-timer {
	position: relative;
	display: grid;
	place-items: center;
	width: 58px;
	height: 58px;
	flex: none;
	border-radius: 50%;
	/* 0.72, and the number is derived rather than chosen by eye.
	 *
	 * These discs sit on whatever photograph the editor uploads, so the only
	 * safe assumption is the worst one: a white plate on a pale worktop filling
	 * the corner. White glyphs on a 42% forest disc over pure white computes to
	 * about 2.8:1 — the counter inside needs 4.5:1 as small text, and it would
	 * fail silently the day somebody swapped the picture. At 0.72 the same
	 * worst case computes to 5.4:1, so it holds whatever the photograph does. */
	background: rgba(20, 56, 41, 0.72);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
	border: 1px solid rgba(252, 250, 246, 0.28);
}

/* Rotated so the arc starts at twelve o'clock, which is where an eye looks for
   the start of a circle. */
.lf-hero__plate-ring {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	transform: rotate(-90deg);
	overflow: visible;
	pointer-events: none;
}

/* Stroke width is left in user units rather than pinned with
   non-scaling-stroke: the dash pattern is measured in user units too, so mixing
   the two coordinate systems would put the arc length out of step with the
   circumference the JS is working from. */
.lf-hero__plate-ring circle {
	fill: none;
	stroke-width: 5;
	/* Round caps so the growing end of the arc is a soft dot rather than a
	   guillotined edge. */
	stroke-linecap: round;
}

.lf-hero__plate-track {
	stroke: rgba(252, 250, 246, 0.2);
}

.lf-hero__plate-arc {
	stroke: var(--amber);
	/* dasharray and dashoffset are both written by the JS, computed from this
	   circle's own r — so the radius can be changed here without leaving the
	   arc out of step with its own geometry. The values below are only the
	   resting state for a page whose script has not run. */
	stroke-dasharray: 282.74;
	stroke-dashoffset: 282.74;
	transition: stroke 0.4s ease;
}

/* The position, in the middle of the ring. Tabular figures so 1/3 and 2/3 do
   not shift the glyphs a pixel sideways as it counts. */
.lf-hero__plate-count {
	position: relative;
	margin: 0;
	font-family: var(--font-headers);
	font-size: 0.8125rem;
	font-weight: 700;
	line-height: 1;
	letter-spacing: 0.02em;
	font-variant-numeric: tabular-nums;
	color: var(--white-pure);
}

.lf-hero__plate-of {
	opacity: 0.6;
	margin-inline: 1px;
}

.lf-hero__inner {
	position: relative;
	z-index: 1;
	display: flex;
	align-items: center;
	min-height: min(calc(100vh - 150px), 830px);
	padding-block: clamp(3rem, 2rem + 4vw, 5rem) clamp(6rem, 4rem + 6vw, 8rem);
}

.lf-hero__content {
	max-width: 620px;
}

/* The copy now sits on the shape, not on the photograph, so it is dark type on
   the page ground — no scrim and no text shadow needed anywhere. */
/* Smaller than the script line elsewhere on the page: this is the longest of
   them and it has a column rather than the full width to sit in, so at the
   section size it broke after "across" and left "Toronto" stranded. */
.lf-hero__eyebrow {
	display: block;
	font-family: var(--font-subheaders);
	font-size: clamp(1.5rem, 1.15rem + 1.4vw, 2.125rem);
	line-height: 1.15;
	color: var(--main);
	margin-bottom: -0.04em;
}

/* Set against the column, not the viewport.
   The page's display size tops out at 80px, which in a 620px column wraps this
   headline onto three lines and leaves "actually eat." alone on the last.
   Capping it at 58px — the size the reference layout uses — gives two. */
.lf-hero__title {
	font-size: clamp(2.25rem, 1.5rem + 3vw, 3.625rem);
	line-height: 1.14;
	/* Display type at this size sets loose without it. */
	letter-spacing: -0.02em;
	color: var(--black);
	margin-bottom: 20px;
	text-wrap: balance;
}

/* ---- The typed headline ---- */

/* visibility, not opacity or display.
 *
 * A hidden character has to keep its box, because the line breaks are worked out
 * once from the full sentence and must not change as the letters arrive.
 * display:none would collapse them and re-flow the headline on every frame;
 * opacity would work but paints thirty-odd composited layers for no reason.
 * visibility keeps the space, costs nothing, and is what makes this reflow-free.
 *
 * The class is added by JS. Without it every character is visible, so a page
 * whose script never ran shows the finished headline rather than an empty h1. */
.lf-hero__title.is-typing .lf-type__ch {
	visibility: hidden;
}

.lf-hero__title.is-typing .lf-type__ch.is-in {
	visibility: visible;
}

/* The caret rides on whichever character was last revealed. Absolutely
   positioned so it adds no width — a border on the letter would push every
   character after it sideways by 2px as the caret moved along the line. */
.lf-type__ch {
	position: relative;
}

.lf-hero__title.is-typing .lf-type__ch.is-cursor::after {
	content: "";
	position: absolute;
	right: -0.07em;
	top: 0.1em;
	bottom: 0.14em;
	width: 0.055em;
	background: var(--second);
	visibility: visible;
	/* Reuses the footer watermark's caret keyframes rather than declaring a
	   second set — one blink rhythm across the site. */
	animation: lf-caret 0.9s steps(1) infinite;
}

/* Once the sentence has landed the caret goes, the way a real one stops. */
.lf-hero__title.is-typed .lf-type__ch.is-cursor::after {
	animation: none;
	opacity: 0;
}

.lf-hero__lead {
	/* Runs the full width of the copy column rather than the 30rem it used to
	   be held to. At 30rem this paragraph broke over four lines; the column's
	   620px sets it in three, at roughly 62 characters a line — still inside the
	   60–75 that reads comfortably, and one line shorter under the headline. */
	max-width: 100%;
	margin-bottom: 36px;
	font-size: var(--fs-lead);
	line-height: 1.85;
	color: var(--muted);
}

/* Button and its qualifying line as one unit, side by side.
 *
 * The reference centres its line under its button, which works there because
 * the phone number is narrower than the button. Ours is a full sentence, so it
 * sits beside the button instead — the two read as one row, and the note is
 * level with the thing it qualifies rather than trailing under it.
 *
 * wrap is load-bearing rather than defensive. The note is 54 characters and the
 * copy column is 620px; once the button has taken its share there is roughly
 * 340px left, so the sentence sets on two lines at this width and drops to its
 * own line below the button as the column narrows. Centring is still avoided —
 * both items align left, which is where the rest of the hero starts. */
.lf-hero__cta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	/* Row gap first, so a wrapped note keeps clear air above it. */
	gap: 14px 20px;
}

.lf-hero__note {
	/* Was 16px, which is what stacked it. The row gap above owns the spacing
	   now, in both directions. */
	margin-top: 0;

	/* Two lines, deliberately.
	 *
	 * Measured rather than guessed: the sentence sets 366px wide at this size,
	 * which is 46ch, and the button leaves it 366px of the 620px column — so
	 * left alone it lands on exactly one long line and the row reads as a
	 * button with a caption running off the side of it. Capping it near half
	 * its natural width turns it into a two-line block the same height as the
	 * button beside it, which is what makes the pair read as one unit.
	 *
	 * 28ch rather than 23: at exactly half the words break where they fall.
	 * The extra few characters give text-wrap:balance room to choose, and it
	 * takes the comma — "Opening in Scarborough," / "North York and Etobicoke
	 * first." That break matters. At a tighter cap the line ends on "North" and
	 * splits a place name across two lines. */
	max-width: 28ch;
	text-wrap: balance;

	font-size: var(--fs-small);
	line-height: 1.6;
	color: var(--muted);
}

/* ---- Foot of the hero ---- */

.lf-hero__social {
	position: absolute;
	left: 0;
	bottom: clamp(2rem, 1rem + 3vw, 3.5rem);
	display: flex;
	gap: 14px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.lf-hero__social a {
	display: grid;
	place-items: center;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	background: var(--main);
	color: var(--gray);
	transition: background-color 0.25s var(--ease), transform 0.25s var(--ease);
}

.lf-hero__social a:hover {
	background: var(--second);
	color: var(--white-pure);
	transform: translateY(-3px);
}

.lf-hero__social svg {
	width: 19px;
	height: 19px;
}

/* The scroll cue: a barrel with a wheel that drifts down it. */
.lf-hero__scroll {
	position: absolute;
	left: 50%;
	bottom: clamp(2rem, 1rem + 3vw, 3.5rem);
	transform: translateX(-50%);
	display: block;
	width: 26px;
	height: 40px;
	border: 2px solid var(--main);
	border-radius: 999px;
	opacity: 0.55;
}

.lf-hero__wheel {
	position: absolute;
	left: 50%;
	top: 8px;
	width: 4px;
	height: 8px;
	margin-left: -2px;
	border-radius: 2px;
	background: var(--main);
	animation: lf-wheel 1.8s var(--ease) infinite;
}

@keyframes lf-wheel {
	0% {
		transform: translateY(0);
		opacity: 0;
	}

	25% {
		opacity: 1;
	}

	75% {
		transform: translateY(14px);
		opacity: 0;
	}

	100% {
		transform: translateY(14px);
		opacity: 0;
	}
}

/* The cards sit below the hero rather than riding up over it.
 *
 * On the bounds: clamp() returns max(MIN, min(VAL, MAX)), so with a MIN above
 * the MAX the middle term can never be reached and the whole expression is just
 * MIN. Here that is 3rem, so this resolves to a flat 48px at every width and the
 * -6vw and -6rem are inert. The same was true of the value this replaced —
 * clamp(-3rem, -6vw, -6rem) computed to -48px everywhere, never -6vw — so the
 * row has never actually been fluid. Written out as given; `margin-top: 3rem`
 * is the same declaration with nothing dead in it. */
.lf-cards {
	position: relative;
	z-index: 2;
	margin-top: clamp(3rem, -6vw, -6rem);
}

/* Above the slider breakpoint these are a plain grid. Swiper's own classes are
   in the markup permanently, so the display rules have to be reset here or the
   flex wrapper would collapse the row on desktop. */
.lf-cards__grid {
	overflow: visible;
}

.lf-cards__grid .swiper-wrapper {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 24px;
	transform: none !important;
}

.lf-cards__dots {
	display: none;
}

/* A card being dealt out of the deck.
 *
 * The JS takes .lf-reveal off these cards before adding this class, so there is
 * no transition left to fight: the deck writes transform every frame from the
 * scroll position, and any transition on transform would render each of those
 * frames late. With the deck running, the deal IS the entrance — the fade is
 * not wanted twice. The transition:none here is a belt-and-braces guard for the
 * frame between the stylesheet loading and the module running. */
.lf-card--deck {
	opacity: 1;
	transition: none;
	will-change: transform;
	/* Stops the neighbouring card's edge showing through the corner radius
	   while the three are piled up. */
	backface-visibility: hidden;
}

/* The card.
 *
 * White with a warm hairline and a two-stop shadow rather than a flat cream
 * panel. The cream was the same colour as half the page, so the three of them
 * read as areas of background with words on rather than as objects — and an
 * object is what the deal-out animation above needs them to be.
 *
 * The shadow is two shadows: a tight one at 4% that draws the edge, and a wide
 * soft one lifted off the bottom that puts it above the page. A single blurred
 * shadow gives you one or the other.
 *
 * Scoped through .lf-cards__grid because this element is also a Swiper slide,
 * and `.swiper-slide { display: block }` matches at the same specificity from a
 * stylesheet enqueued after this one — which quietly turned the row into a
 * stack. The ancestor wins it back. */
.lf-cards__grid .lf-card {
	position: relative;
	display: flex;
	align-items: center;
	gap: 20px;
	min-height: 180px;
	padding: 24px 28px 24px 0;
	background: var(--white-pure);
	border: 1px solid var(--rule);
	border-radius: var(--r-card);
	overflow: hidden;
	/* The `translate` property, not `transform`. GSAP owns this element's
	   inline transform for the deal-out, and the two compose instead of
	   fighting — a transform-based hover would simply be overwritten on the
	   next scroll frame. */
	translate: 0 0;
	transition:
		translate 0.45s var(--ease-out),
		box-shadow 0.45s var(--ease-out),
		border-color 0.45s var(--ease);
	box-shadow:
		0 1px 2px rgba(31, 36, 33, 0.04),
		0 10px 26px -10px rgba(31, 36, 33, 0.12);
}

/* A wash of the brand cream bled into the top-right corner. Keeps the warmth
   the flat fill used to carry without flattening the card back out, and gives
   the eye something to travel to after the title. */
.lf-card::after {
	content: "";
	position: absolute;
	right: -30%;
	top: -60%;
	z-index: 0;
	width: 75%;
	aspect-ratio: 1;
	border-radius: 50%;
	background: radial-gradient(circle, var(--gray) 0%, rgba(247, 241, 228, 0) 70%);
	opacity: 0.9;
	pointer-events: none;
	transition: opacity 0.45s var(--ease);
}

.lf-card:hover {
	translate: 0 -6px;
	border-color: rgba(216, 155, 63, 0.5);
	box-shadow:
		0 2px 4px rgba(31, 36, 33, 0.05),
		0 26px 50px -14px rgba(31, 36, 33, 0.22);
}

.lf-card__media {
	position: relative;
	z-index: 1;
	flex: none;
	width: 130px;
	height: 130px;
	margin-left: -26px;
	border-radius: 50%;
	overflow: hidden;
	background: var(--rule);
	/* The same rim as the hero plate, at the scale this circle asks for. It is
	   what turns the photograph from a hole in the card into a thing sitting on
	   it — and it ties the two circles on the page together. */
	box-shadow:
		0 0 0 5px var(--white-pure),
		0 0 0 6px rgba(216, 155, 63, 0.45),
		0 10px 22px -8px rgba(31, 36, 33, 0.3);
}

.lf-card__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.6s var(--ease-out);
}

/* Slow and slight. A photograph that leaps at the pointer looks like a banner
   advert; 4% over six-tenths of a second just looks alive. */
.lf-card:hover .lf-card__media img {
	transform: scale(1.04);
}

.lf-card__body {
	position: relative;
	z-index: 1;
}

.lf-card__title {
	font-size: var(--fs-h5);
	margin-bottom: 6px;
	transition: color 0.3s var(--ease);
}

.lf-card:hover .lf-card__title {
	color: var(--main);
}

/* The meta line gets a short amber rule above it. It is the only thing on the
   card that changes width, so it is what carries the hover — the rest of the
   movement is depth, and depth alone can read as accidental. */
.lf-card__meta {
	position: relative;
	padding-top: 12px;
	font-size: var(--fs-small);
	font-weight: 700;
	line-height: 1.5;
	color: var(--main);
}

.lf-card__meta::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0;
	width: 24px;
	height: 2px;
	border-radius: 2px;
	background: var(--amber);
	transition: width 0.45s var(--ease-out);
}

.lf-card:hover .lf-card__meta::before {
	width: 52px;
}

/* =========================================================================
   9  The food
   ========================================================================= */

.lf-chip {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 8px 20px;
	border: 2px solid var(--second);
	border-radius: var(--r-btn);
	font-family: var(--font-headers);
	font-size: var(--fs-small);
	font-weight: 700;
	line-height: 1.4;
	color: var(--second);
}

/* A rule across the container with the sample-menu label set into it. */
.lf-week__marker {
	display: flex;
	align-items: center;
	gap: 20px;
	margin-top: 8px;
}

.lf-week__marker::after {
	content: '';
	flex: 1;
	height: 1px;
	background: var(--rule);
}

.lf-week__count {
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--muted);
	white-space: nowrap;
	order: 3;
}

/* The week is a Swiper track: three days at a time on a wide screen, looping.
   Padding on the sides so a card lifting on hover is not clipped by the
   overflow the slider needs. */
.lf-week {
	position: relative;
	margin-top: 32px;
	padding: 12px 4px 4px;
}

.lf-week .swiper-wrapper {
	align-items: stretch;
	/* Swiper writes transition-duration inline from its `speed` option but takes
	   the curve from CSS, where the default `ease` is symmetrical — it leaves as
	   slowly as it arrives, which over 900ms reads as drag. Expo-out spends
	   almost all of the distance up front and settles, so the track looks like
	   it was released rather than pushed the whole way. */
	transition-timing-function: var(--ease-out);
}

.lf-day {
	display: flex;
	flex-direction: column;
	/* Slides are equal height regardless of how much copy each day carries. */
	height: auto;
	background: var(--white-pure);
	border: 1px solid var(--rule);
	border-radius: 20px;
	overflow: hidden;
	transition: transform 0.35s var(--ease), box-shadow 0.35s var(--ease), border-color 0.35s var(--ease);
}

@media (hover: hover) {
	.lf-day:hover {
		transform: translateY(-6px);
		border-color: transparent;
		box-shadow: 0 18px 44px rgba(31, 36, 33, 0.13);
	}

	.lf-day:hover .lf-day__media img {
		transform: scale(1.05);
	}
}

.lf-day__media {
	position: relative;
	aspect-ratio: 1 / 1;
	background: var(--rule);
	overflow: hidden;
}

.lf-day__media img,
.lf-day__media .lf-placeholder {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.6s var(--ease);
}

.lf-day__zoom {
	display: block;
	width: 100%;
	height: 100%;
}

/* The affordance for "this opens bigger". Appears on hover, but is always
   visible to the keyboard and on touch, where there is no hover to rely on. */
.lf-day__zoomicon {
	position: absolute;
	top: 10px;
	right: 10px;
	z-index: 2;
	display: grid;
	place-items: center;
	width: 34px;
	height: 34px;
	border-radius: 50%;
	background: rgba(252, 250, 246, 0.92);
	color: var(--main);
	opacity: 0;
	transform: scale(0.85);
	transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
}

.lf-day__zoomicon svg {
	width: 17px;
	height: 17px;
}

.lf-day:hover .lf-day__zoomicon,
.lf-day__zoom:focus-visible .lf-day__zoomicon {
	opacity: 1;
	transform: scale(1);
}

@media (hover: none) {
	.lf-day__zoomicon {
		opacity: 1;
		transform: scale(1);
	}
}

/* The day sits on the photograph, not above the ingredient list — it labels
   the picture, and it buys the card a piece of type with some presence. */
.lf-day__scrim {
	position: absolute;
	inset: 0;
	background: linear-gradient(180deg, rgba(20, 56, 41, 0) 42%, rgba(20, 56, 41, 0.78) 100%);
}

/* The same shadow .lf-day__index carries, and for the same reason.
 *
 * White on the scrim measures 5.14:1 to 11.76:1 across the five photographs
 * currently in place — every one of them passes AA. Thursday is the thin one,
 * and it has only about 17% of headroom in backdrop luminance before it drops
 * under 4.5:1.
 *
 * These photographs are placeholders (see doc/Image_Credits.md) and every one
 * of them will be replaced. A brighter plate at the foot of the frame — a white
 * bowl, a pale worktop — takes the day name below AA, and it would do it
 * silently, because nothing about the CSS would have changed. The shadow costs
 * nothing on a dark photograph and holds the floor on a light one. */
.lf-day__name {
	position: absolute;
	left: 16px;
	bottom: 12px;
	font-family: var(--font-headers);
	font-size: 1.0625rem;
	font-weight: 700;
	line-height: 1.2;
	color: var(--white-pure);
	text-shadow: 0 1px 6px rgba(20, 56, 41, 0.5);
}

.lf-day__index {
	position: absolute;
	top: 12px;
	left: 16px;
	font-family: var(--font-headers);
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.14em;
	color: rgba(255, 255, 255, 0.82);
	text-shadow: 0 1px 6px rgba(20, 56, 41, 0.5);
}

.lf-day__body {
	flex: 1;
	padding: 6px 18px 20px;
}

/* Two columns: the icon chip, then the label stacked over the value. The chip
   spans both text rows and centres against them, so the three rows line up down
   the card whether or not a value wraps to a second line. */
.lf-day__row {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	column-gap: 12px;
	align-items: center;
	padding-block: 13px;
	border-bottom: 1px solid var(--rule);
}

.lf-day__row:last-child {
	border-bottom: 0;
	padding-bottom: 0;
}

.lf-day__icon {
	grid-row: 1 / span 2;
	display: grid;
	place-items: center;
	width: 32px;
	height: 32px;
	border-radius: 50%;
	background: var(--gray);
	color: var(--main);
	transition: background-color 0.35s var(--ease), color 0.35s var(--ease);
}

.lf-day__icon svg {
	width: 16px;
	height: 16px;
}

/* The chips warm up together when the card is hovered — one movement for the
   whole card rather than three separate ones chasing the pointer down it. */
.lf-day:hover .lf-day__icon {
	background: var(--second);
	color: var(--white-pure);
}

.lf-day__label {
	grid-column: 2;
	display: block;
	margin-bottom: 3px;
	font-size: 0.625rem;
	font-weight: 700;
	letter-spacing: 0.16em;
	line-height: 1.4;
	text-transform: uppercase;
	color: var(--second);
}

.lf-day__value {
	grid-column: 2;
	display: block;
	font-size: 0.9375rem;
	line-height: 1.55;
	color: var(--black);
}

/* ---- Week slider controls ---- */

/* In the marker row, after the day count. */
.lf-week__nav {
	order: 4;
	display: flex;
	gap: 10px;
	margin-left: 4px;
}

.lf-week__arrow {
	display: grid;
	place-items: center;
	width: 48px;
	height: 48px;
	padding: 0;
	border: 2px solid var(--rule);
	border-radius: 50%;
	background: var(--white-pure);
	color: var(--main);
	cursor: pointer;
	transition: background-color 0.25s var(--ease), border-color 0.25s var(--ease), color 0.25s var(--ease);
}

.lf-week__arrow svg {
	width: 20px;
	height: 20px;
}

.lf-week__arrow:hover {
	background: var(--main);
	border-color: var(--main);
	color: var(--white-pure);
}

/* The track loops, so neither arrow is ever a dead end and neither is ever
   disabled — but Swiper still marks them, so the lock state is styled rather
   than left looking identical to the live one. */
.lf-week__arrow.swiper-button-lock {
	display: none;
}

.lf-week__dots {
	display: flex;
	justify-content: center;
	gap: 7px;
	margin-top: 26px;
}

.lf-week__dots .swiper-pagination-bullet {
	width: 8px;
	height: 8px;
	margin: 0;
	background: var(--main);
	opacity: 0.22;
	transition: width 0.25s var(--ease), background-color 0.25s var(--ease), opacity 0.25s var(--ease);
}

.lf-week__dots .swiper-pagination-bullet-active {
	width: 26px;
	border-radius: 4px;
	background: var(--second);
	opacity: 1;
}

.lf-food__note {
	max-width: var(--prose);
	margin: 36px auto 0;
	text-align: center;
}

/* A spec sheet, set in a panel of its own. */
.lf-spec {
	position: relative;
	margin-top: clamp(2.5rem, 1.6rem + 2.8vw, 4rem);
	padding: clamp(2rem, 1.2rem + 2.6vw, 3.25rem) clamp(1.5rem, 0.8rem + 2.6vw, 3.25rem);
	background: var(--gray);
	border-radius: var(--r-panel);
}

.lf-spec__title {
	display: block;
	margin-bottom: 28px;
	font-family: var(--font-headers);
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--second);
}

.lf-spec__list {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 22px 48px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.lf-spec__list li {
	display: flex;
	align-items: flex-start;
	gap: 16px;
	font-size: 1rem;
	line-height: 1.7;
	color: var(--black);
}

.lf-spec__mark {
	flex: none;
	width: 28px;
	height: 28px;
	margin-top: 2px;
	color: var(--main);
}

/* =========================================================================
   10  Sangita
   ========================================================================= */

.lf-sangita__grid {
	display: grid;
	grid-template-columns: 5fr 6fr;
	gap: clamp(2rem, 1rem + 4vw, 5rem);
	align-items: start;
}

.lf-sangita__figure {
	position: relative;
	margin: 0;
	/* Contains the amber block's z-index: -1 so it cannot escape past this
	   figure and slide behind the section itself. */
	isolation: isolate;
	/* `translate`, not `transform`. This figure carries .lf-reveal, whose
	   entrance owns the transform property — the two compose instead of one
	   overwriting the other on the next frame. Same reason the hero cards and
	   the plan cards use it. */
	translate: 0 0;
	transition: translate 0.55s var(--ease-out);
}

/* The portrait, squared and mounted.
 *
 * Square corners rather than the 24px card radius it had: this is the one
 * photograph on the page of a person rather than of food, and a mounted
 * rectangle reads as a portrait where a soft-cornered card reads as another
 * content tile. --r-sm keeps it on the theme's radius scale while being square
 * at this size.
 *
 * The mount is three box-shadow rings doing what a picture frame does — a wide
 * paper-white mat, a hairline of amber just outside it to catch the light, then
 * the drop shadow that lifts the whole thing off the cream ground. It is the
 * same construction as the hero plate's rim and the card media's, at the scale a
 * 574px portrait asks for, so the three read as one family.
 *
 * No border property: a border would sit inside the element and eat into the
 * photograph. Spread shadows sit outside it and cost no layout. */
/* The mount is drawn on this wrapper, not on the img.
 *
 * It was on the image itself, which was fine while the figure held nothing else.
 * With a caption inside the figure the mount's 13px outward spread would run
 * underneath the text — the shadow is outside the img's box, and the caption sits
 * in the flow immediately after it. Moving the rings up one level keeps them
 * around the photograph and lets the caption clear them. */
.lf-sangita__frame {
	/*
	 * position without z-index, deliberately.
	 *
	 * A z-index here would make this a stacking context, and the amber block
	 * below is a descendant at z-index -1 — inside a stacking context it would
	 * be trapped, painting on top of this element's own background and mount
	 * instead of behind them. With z-index left at auto it escapes to the
	 * figure's context and sits behind the whole mounted print, which is where
	 * it belongs.
	 */
	position: relative;
	display: block;
	/* This is the figure element now, and a figure carries `1em 40px` of UA
	   margin — 40px of it on each side, which would pull the mount in off the
	   grid column and misalign it with the amber block behind it. */
	margin: 0;
	/* Truly square — no radius at all. A mounted photograph has sharp corners. */
	border-radius: 0;
	background: var(--white-pure);
	box-shadow:
		0 0 0 12px var(--white-pure),
		0 0 0 13px rgba(216, 155, 63, 0.5),
		0 26px 56px -22px rgba(31, 36, 33, 0.4);
	transition: box-shadow 0.55s var(--ease-out);
}

.lf-sangita__figure img {
	display: block;
	width: 100%;
	border-radius: 0;
}

/* The name, under the print.
 *
 * Clay small-caps at the tracking the theme uses for every other label of this
 * kind — the day rows in the week, the plan meta, the spec title. It is a
 * caption, so it is set as one: quieter than the prose beside it, and clearly
 * attached to the photograph above rather than floating between the two.
 *
 * It sits on the mount now rather than below it, so the space around it is
 * padding inside the white board instead of a margin outside it. The photograph
 * runs flush to the board's edges; the caption is inset from them, which is what
 * makes the strip below the print read as mat rather than as a second panel. */
.lf-sangita__caption {
	display: block;
	/* flex-shrink: 0 — when the mount is stretched to the prose column and the
	   photograph is flexing to absorb the difference, the caption must not be
	   what gives. Text squeezed to fit is worse than a photograph cropped by a
	   few more pixels. */
	flex: 0 0 auto;
	padding: 22px 26px 24px;
	font-family: var(--font-headers);
}

/* The name. */
.lf-sangita__capname {
	display: block;
	font-size: 0.8125rem;
	font-weight: 700;
	letter-spacing: 0.16em;
	line-height: 1.5;
	text-transform: uppercase;
	color: var(--second);
}

/* Who she is, under it.
 *
 * Deliberately the quieter of the two: sentence case, no tracking, forest at
 * reduced opacity. Two lines set identically would read as a two-line title and
 * compete with the headline in the column beside it. */
.lf-sangita__caprole {
	display: block;
	margin-top: 7px;
	font-size: 0.8125rem;
	font-weight: 500;
	letter-spacing: 0.01em;
	line-height: 1.5;
	color: var(--forest);
	opacity: 0.72;
}

/* A short amber rule above the name, matching the one under the section heads.
   It is what stops the caption reading as the first line of a new paragraph. */
.lf-sangita__caption::before {
	content: "";
	display: block;
	width: 28px;
	height: 2px;
	margin-bottom: 12px;
	border-radius: 2px;
	background: var(--amber);
	transition: width 0.55s var(--ease-out);
}

.lf-sangita__figure:hover .lf-sangita__caption::before {
	width: 56px;
}

/* Hover: the portrait lifts, its shadow deepens and lengthens, and the amber
   hairline comes up to full strength. Three changes on one gesture rather than a
   single crude scale — and nothing is scaled, because enlarging a photograph of
   a person on hover is the sort of movement that draws attention to the
   interaction rather than to her. */
.lf-sangita__figure:hover .lf-sangita__frame {
	box-shadow:
		0 0 0 12px var(--white-pure),
		0 0 0 13px var(--amber),
		0 38px 70px -24px rgba(31, 36, 33, 0.5);
}

.lf-sangita__figure:hover {
	translate: 0 -8px;
}

/* An amber compartment shape behind the portrait — from the mark, not decoration.
 *
 * Anchored to the FRAME, not to the figure. It used to hang off the figure, and
 * the moment a caption was added underneath, two things broke: its 46% height
 * was measured against a figure that had just grown by the height of the caption
 * so the block ballooned, and its bottom edge dropped past the photograph and
 * covered the caption text outright — the caption is static, this is positioned,
 * so it painted on top. Measured against the frame, its size and position follow
 * the photograph and ignore anything below it.
 *
 * Squared with the frame in front of it. A rounded block behind a sharp-cornered
 * photograph reads as two unrelated shapes rather than one composition. */
.lf-sangita__frame::before {
	content: '';
	position: absolute;
	left: -18px;
	bottom: -18px;
	width: 62%;
	height: 46%;
	background: var(--amber);
	border-radius: 0;
	z-index: -1;
	transition: transform 0.55s var(--ease-out);
}

/* It slides further out as the portrait rises, so the two move as one object
   opening rather than a card sliding over a static block. */
.lf-sangita__figure:hover .lf-sangita__frame::before {
	transform: translate(-8px, 8px);
}

/* Two columns of the same height.
 *
 * The mount is taller than the prose beside it — a 4:5 portrait at this column
 * width runs about 125px past the last line, so the section ends on a ragged
 * step. Squaring the two off is not a matter of `align-items: stretch` on its
 * own: stretch makes both items the height of the ROW, and the row is sized by
 * the tallest item, which is the photograph. Everything would still be 797px
 * tall and the prose column would simply grow a band of empty space.
 *
 * So the mount is taken out of flow. An absolutely positioned item contributes
 * nothing to the row's height, which leaves the prose to size it alone; the
 * figure then stretches to that height and the mount fills the figure. The
 * photograph flexes into whatever is left once the caption has taken its share,
 * and covers it — cropped, never distorted.
 *
 * Only above the breakpoint where the two columns exist. Below it the grid is a
 * single column and the mount goes back into flow, where the photograph is free
 * to be its natural height and there is nothing to match it to. */
@media (min-width: 992px) {
	.lf-sangita__grid {
		align-items: stretch;
	}

	.lf-sangita__frame {
		position: absolute;
		inset: 0;
		display: flex;
		flex-direction: column;
	}

	.lf-sangita__frame img {
		flex: 1 1 auto;
		/* Without this the image's intrinsic height becomes its floor and it
		   refuses to flex down — the default min-height of a flex item is auto,
		   which for a replaced element resolves to its own content size. */
		min-height: 0;
		height: auto;
		object-fit: cover;
		/* Above centre. When the crop has to take from somewhere it should take
		   from below the shoulders, not off the top of a head. */
		object-position: 50% 30%;
	}
}

.lf-sangita__body {
	max-width: var(--prose);
}

.lf-sangita__body p {
	line-height: 2;
}

/* The one typographic emphasis in the section: the sixty hours line. */
.lf-sangita__body p:last-child {
	margin-top: 1.6em;
	font-size: var(--fs-lead);
	line-height: 1.75;
	font-weight: 600;
	color: var(--main);
}

/* =========================================================================
   11  How it works
   ========================================================================= */

.lf-steps {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 32px;
}

.lf-step {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 22px;
	align-items: start;
}

/* The drawing and the numeral share one square: the line art sits behind, the
   numeral rides on a disc at its corner. */
.lf-step__art {
	position: relative;
	flex: none;
	display: grid;
	place-items: center;
	width: 84px;
	height: 84px;
	border-radius: 22px;
	background: var(--gray);
	color: var(--main);
}

.lf-step__art svg {
	width: 46px;
	height: 46px;
}

/* Drawn in rather than faded in.
   1200 is comfortably longer than any path here, so one value covers every
   shape without measuring each one in JavaScript. */
.lf-step__art svg path,
.lf-step__art svg rect,
.lf-step__art svg circle {
	stroke-dasharray: 1200;
	stroke-dashoffset: 1200;
}

.lf-step.is-drawn .lf-step__art svg path,
.lf-step.is-drawn .lf-step__art svg rect,
.lf-step.is-drawn .lf-step__art svg circle {
	animation: lf-draw 1.15s var(--ease) forwards;
}

.lf-step.is-drawn .lf-step__art svg > :nth-child(2) {
	animation-delay: 0.12s;
}

.lf-step.is-drawn .lf-step__art svg > :nth-child(3) {
	animation-delay: 0.24s;
}

.lf-step.is-drawn .lf-step__art svg > :nth-child(4) {
	animation-delay: 0.36s;
}

@keyframes lf-draw {
	to {
		stroke-dashoffset: 0;
	}
}

.lf-step__num {
	position: absolute;
	right: -8px;
	bottom: -8px;
	display: grid;
	place-items: center;
	width: 34px;
	height: 34px;
	background: var(--second);
	color: var(--white-pure);
	border: 3px solid var(--white);
	border-radius: 50%;
	font-family: var(--font-headers);
	font-size: 0.9375rem;
	font-weight: 700;
	line-height: 1;
}

.lf-step__title {
	font-size: var(--fs-h5);
	margin-bottom: 8px;
}

/* Step 3 carries the freezer instruction — the one thing the parent must
   remember. The copy says so, so the design says so. */
.lf-step__reminder {
	margin-top: 18px;
	padding: 20px 24px;
	background: var(--gray);
	border-left: 4px solid var(--second);
	border-radius: 0 var(--r-sm) var(--r-sm) 0;
	line-height: 1.8;
}

/* =========================================================================
   12  Halal band
   ========================================================================= */

.lf-halal__grid {
	display: grid;
	grid-template-columns: 5fr 7fr;
	gap: clamp(2.5rem, 1rem + 5vw, 6rem);
	align-items: start;
}

.lf-halal__aside {
	position: sticky;
	top: 130px;
}

.lf-halal__title {
	font-size: var(--fs-h2);
	color: var(--white-pure);
	text-wrap: balance;
}

.lf-halal__rule {
	display: block;
	width: 64px;
	height: 3px;
	margin-top: 28px;
	background: var(--amber);
	border-radius: 2px;
	/* Drawn left-to-right by GSAP when the section arrives. Set here rather than
	   in the tween so the rule is never missing if the script does not run. */
	transform-origin: left center;
}

/* The kitchen photograph.
 *
 * A frame that crops and a photograph that moves inside it. The frame is what
 * you see — fixed, cleanly cornered, with a cream edge picking up the plate and
 * the day cards — and the drift happens behind it. */
.lf-halal__frame {
	position: relative;
	margin: 44px 0 0;
	aspect-ratio: 4 / 3;
	border-radius: var(--r-panel);
	overflow: hidden;
	/* On a dark ground a shadow alone does nothing; the light edge is what
	   separates the photograph from the green behind it. */
	box-shadow:
		0 0 0 1px rgba(247, 241, 228, 0.16),
		0 30px 60px -20px rgba(0, 0, 0, 0.55);
}

.lf-halal__frame img {
	display: block;
	width: 100%;
	/* Taller than the frame on purpose. This overhang is the room the parallax
	   travels through — at exactly 100% the drift would pull the frame's own
	   background into view at the top or bottom of its range. */
	height: 112%;
	margin-top: -6%;
	object-fit: cover;
	object-position: center;
}

/* The photograph is lit grey and the band is deep green, so without this the
   frame reads as something pasted onto the section. Washing the bottom back
   into the band's own colour settles it into the ground — and it puts the
   darkest part of the picture directly under the amber edge, which is where
   that line needs contrast to be seen at all. */
.lf-halal__frame::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: linear-gradient(to top, rgba(28, 75, 58, 0.55), rgba(28, 75, 58, 0) 58%);
}

/* A hairline of amber along the bottom of the frame, matching the rule above
   the photograph and the plate's rim in the hero. */
.lf-halal__frameedge {
	z-index: 1;
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 3px;
	background: linear-gradient(90deg, var(--amber), rgba(216, 155, 63, 0));
	transform-origin: left center;
}

.lf-halal__body {
	max-width: 620px;
}

.lf-halal__body p {
	font-size: 1.0625rem;
	line-height: 1.95;
	color: rgba(247, 241, 228, 0.9);
}

.lf-halal__body p + p {
	margin-top: 1.35em;
}

/* "Until then we're not putting a badge on this page."
   The one sentence on the page that declines to claim something, so it is set
   apart rather than left as the fourth of four equal blocks. There is no badge,
   seal or certificate graphic in this section, and there must not be one until
   the certificate exists. */
.lf-halal__final {
	margin-top: 2.2em !important;
	padding: 4px 0 4px 26px;
	border-left: 3px solid var(--amber);
	font-size: var(--fs-lead) !important;
	line-height: 1.75 !important;
	color: var(--white-pure) !important;
}

.lf-halal__link {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	margin-top: 34px;
	padding-bottom: 4px;
	border-bottom: 1px solid rgba(247, 241, 228, 0.35);
	font-family: var(--font-headers);
	font-size: 1rem;
	font-weight: 600;
	color: var(--gray);
	text-decoration: none;
	transition: border-color 0.3s var(--ease), color 0.3s var(--ease);
}

.lf-halal__link svg {
	transition: transform 0.3s var(--ease);
}

.lf-halal__link:hover {
	color: var(--amber);
	border-bottom-color: var(--amber);
}

.lf-halal__link:hover svg {
	transform: translateX(4px);
}

/* No page behind it yet, so it is not a link and must not look like one. */
.lf-halal__link.is-pending {
	border-bottom-style: dashed;
	color: rgba(247, 241, 228, 0.72);
}

.lf-halal__link.is-pending svg {
	display: none;
}

/* =========================================================================
   13  Plans
   ========================================================================= */

.lf-plans__grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 24px;
}

/* Room for the stagger to move through.
 *
 * Added by the scroll module and only while that animation is running, so the
 * resting layout is unchanged everywhere the animation is not: below 992px, with
 * reduced motion on, or if GSAP fails to load. Without it the raised first card
 * climbs into the headline and the dropped last one lands on the tax line. */
.lf-plans__grid.is-staggered {
	padding-block: 34px;
}

.lf-plan--stagger {
	will-change: transform;
}

.lf-plan {
	position: relative;
	display: flex;
	flex-direction: column;
	padding: 36px 32px;
	background: var(--gray);
	border: 1px solid transparent;
	border-radius: var(--r-card);
	text-align: center;
	translate: 0 0;
	transition:
		translate 0.45s var(--ease-out),
		box-shadow 0.45s var(--ease-out),
		border-color 0.45s var(--ease);
}

/* One ground per tier, and each one is a tint of a colour already in the logo:
 * the forest of the mark's outline, the clay of its top compartment, the amber
 * of its bottom one. Nothing new is introduced — the three cards are the three
 * colours of the logo, let down to about a tenth strength.
 *
 * The point is to tell three cards apart at a glance, which colour does faster
 * than reading three headings. It is not to rank them. The copy does not
 * recommend a plan, so nothing here may look like the recommended one: all
 * three sit at the same lightness and the same strength, so the eye reads them
 * as three of a kind rather than as a scale.
 *
 * Each carries a hairline of its own colour rather than a grey one, which would
 * put a cold outline around a warm tint and flatten it. */
.lf-plan--tone1 {
	background: #e9f0ea;
	border-color: rgba(28, 75, 58, 0.16);
}

.lf-plan--tone2 {
	background: #f8eae2;
	border-color: rgba(193, 91, 51, 0.2);
}

.lf-plan--tone3 {
	background: #faf0df;
	border-color: rgba(216, 155, 63, 0.32);
}

.lf-plan:hover {
	translate: 0 -6px;
	box-shadow:
		0 2px 4px rgba(31, 36, 33, 0.04),
		0 24px 46px -16px rgba(31, 36, 33, 0.2);
}

.lf-plan--tone1:hover {
	border-color: rgba(28, 75, 58, 0.34);
}

.lf-plan--tone2:hover {
	border-color: rgba(193, 91, 51, 0.42);
}

.lf-plan--tone3:hover {
	border-color: rgba(216, 155, 63, 0.55);
}

.lf-plan__name {
	font-size: var(--fs-h5);
	margin-bottom: 14px;
}

.lf-plan__meta {
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.14em;
	line-height: 1.6;
	text-transform: uppercase;
	color: var(--muted);
	margin-bottom: 20px;
}

.lf-plan__price {
	font-family: var(--font-headers);
	font-size: clamp(2.25rem, 1.8rem + 1.9vw, 3rem);
	font-weight: 700;
	line-height: 1.1;
	color: var(--second);
}

.lf-plan__period {
	font-family: var(--font-main);
	font-size: 1rem;
	font-weight: 700;
	color: var(--black);
}

.lf-plan__each {
	margin-top: 6px;
	font-size: var(--fs-small);
	color: var(--muted);
}

.lf-plans__more {
	max-width: var(--prose);
	margin: 32px auto 0;
	text-align: center;
}

/* Sits directly under the cards at 16px. Not 11px, not in the footer. */
.lf-plans__tax {
	margin-top: 20px;
	text-align: center;
	font-size: 1rem;
	color: var(--muted);
}

.lf-assure {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 20px;
	margin-top: clamp(2.5rem, 1.5rem + 3vw, 4rem);
	padding: 0;
	list-style: none;
}

/* A card each, not four columns of loose text.
 *
 * These four sat as unbounded blocks of different lengths with a tick floating
 * above each — which reads as one paragraph chopped into pieces rather than as
 * four separate promises. An edge, a floor and equal heights are what make them
 * count as four things.
 *
 * They sit on the white plans panel, so the card cannot also be white: the
 * cream ground is what gives it a shape, and the border does the rest. */
.lf-assure__item {
	display: flex;
	flex-direction: column;
	gap: 16px;
	height: 100%;
	padding: 26px 24px 28px;
	background: var(--gray);
	border: 1px solid var(--rule);
	border-radius: var(--r-card);
	line-height: 1.8;
	/* `translate`, not `transform` — same reason as the hero cards: it composes
	   with anything else that touches transform instead of overwriting it. */
	translate: 0 0;
	transition:
		translate 0.45s var(--ease-out),
		box-shadow 0.45s var(--ease-out),
		border-color 0.45s var(--ease),
		background-color 0.45s var(--ease);
}

.lf-assure__item:hover {
	translate: 0 -5px;
	background: var(--white-pure);
	border-color: rgba(216, 155, 63, 0.55);
	box-shadow:
		0 2px 4px rgba(31, 36, 33, 0.04),
		0 22px 44px -16px rgba(31, 36, 33, 0.22);
}

/* The separator, between the chip and the words.
 *
 * A pseudo-element on a flex container is itself a flex item, so it lands
 * first — before the chip — unless it is ordered. Hence the explicit order on
 * all three: chip, rule, text.
 *
 * It is one element doing two jobs. The background COLOUR is the full-width
 * hairline; the background IMAGE is a short amber-to-clay segment sitting on
 * top of it, and hover grows that segment from 28px to the full width. Two
 * stacked elements would do the same thing with twice the markup, and animating
 * background-size on a 2px strip costs nothing. */
.lf-assure__item::before {
	content: "";
	order: 1;
	height: 2px;
	border-radius: 2px;
	background-color: var(--rule);
	background-image: linear-gradient(90deg, var(--amber), var(--second));
	background-repeat: no-repeat;
	background-position: left center;
	background-size: 28px 100%;
	transition: background-size 0.55s var(--ease-out);
}

.lf-assure__item:hover::before {
	background-size: 100% 100%;
}

.lf-assure__text {
	order: 2;
}

/* The chip. Square with a generous radius rather than a circle, so it does not
   compete with the round icon chips in the week cards two sections up. */
.lf-assure__mark {
	order: 0;
	display: grid;
	place-items: center;
	width: 46px;
	height: 46px;
	flex: none;
	border-radius: 14px;
	background: var(--white-pure);
	border: 1px solid var(--rule);
	color: var(--main);
	transition:
		background-color 0.45s var(--ease),
		border-color 0.45s var(--ease),
		color 0.45s var(--ease);
}

.lf-assure__item:hover .lf-assure__mark {
	background: var(--main);
	border-color: var(--main);
	color: var(--white-pure);
}

.lf-assure__text {
	margin: 0;
	font-size: 0.9375rem;
	line-height: 1.75;
	color: var(--muted);
}

.lf-assure__text strong {
	color: var(--black);
}

/* =========================================================================
   14  For schools
   ========================================================================= */

.lf-schools__panel {
	display: grid;
	grid-template-columns: 1fr auto;
	gap: clamp(1.5rem, 1rem + 3vw, 4rem);
	align-items: center;
	background: var(--gray);
	border-radius: var(--r-panel);
	padding: clamp(2rem, 1.2rem + 3.4vw, 4rem);
}

.lf-schools__body {
	max-width: 720px;
}

/* =========================================================================
   15  Closing and the form
   ========================================================================= */

/* =========================================================================
   15b  The photo collage
   ========================================================================= */

.lf-gal {
	background: var(--gray);
}

/* Three columns, bottom-aligned.
 *
 * flex-end is the whole mechanism. The columns are different heights, so
 * sharing one baseline is what produces the stepped top edge — there is no
 * masonry library here, nothing is absolutely positioned and nothing is
 * transformed. Take the flex-end away and it becomes three tidy columns. */
.lf-gal__grid {
	display: flex;
	align-items: flex-end;
	justify-content: center;
	gap: 24px;
}

.lf-gal__col {
	display: flex;
	flex-direction: column;
	gap: 24px;
	flex: 1 1 0;
	min-width: 0;
}

/* The middle column is the tall one, and it holds the largest photograph. */
.lf-gal__col--mid {
	flex-grow: 1.05;
}

/* Two half-width tiles side by side, at the top of the middle column. */
.lf-gal__pair {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 24px;
}

/* A sub-column: a tile and a figure card stacked, sharing the outer gap. */
.lf-gal__stack {
	display: flex;
	flex-direction: column;
	gap: 24px;
}

.lf-gal__col--left,
.lf-gal__col--right {
	flex-direction: row;
	align-items: flex-end;
}

.lf-gal__col--left > *,
.lf-gal__col--right > * {
	flex: 1 1 0;
	min-width: 0;
}

.lf-gal__tile {
	overflow: hidden;
	background: var(--rule);
	border-radius: var(--r-card);
}

.lf-gal__tile img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	transition: transform 0.7s var(--ease-out);
}

.lf-gal__tile:hover img {
	transform: scale(1.05);
}

/* Aspect ratios do the shaping. Every tile is as wide as its column allows and
   as tall as its ratio makes it — which is where the unequal column heights
   that the flex-end alignment needs actually come from. */
.lf-gal__col--left > .lf-gal__tile,
.lf-gal__col--right > .lf-gal__tile {
	aspect-ratio: 180 / 332;
}

.lf-gal__pair .lf-gal__tile {
	aspect-ratio: 180 / 147;
}

.lf-gal__col--mid > .lf-gal__tile {
	aspect-ratio: 384 / 373;
}

.lf-gal__stack .lf-gal__tile {
	aspect-ratio: 180 / 201;
}

/* -- The figure cards ---------------------------------------------------- */

.lf-gal__stat {
	position: relative;
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 10px;
	padding: 30px 22px;
	border-radius: var(--r-card);
	aspect-ratio: 180 / 234;
	overflow: hidden;
	/* `translate`, not `transform`. The reveal tween owns this element's
	   transform and would overwrite a transform-based hover on its next frame;
	   the two properties compose instead of fighting. */
	translate: 0 0;
	transition:
		translate 0.45s var(--ease-out),
		box-shadow 0.45s var(--ease-out);
}

/* A sheen that sweeps across on hover.
 *
 * These two cards are the only things in the collage that are not photographs,
 * and the photographs already answer a hover by zooming. Without something the
 * figures feel dead by comparison — but they cannot zoom, because scaling a
 * number just makes it briefly blurry. Light moving over a flat surface is the
 * version of that gesture a solid card can do. */
.lf-gal__stat::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: linear-gradient(
		100deg,
		rgba(255, 255, 255, 0) 38%,
		rgba(255, 255, 255, 0.16) 50%,
		rgba(255, 255, 255, 0) 62%
	);
	transform: translateX(-100%);
	transition: transform 0.75s var(--ease-out);
}

/* !important, and it is not laziness.
 *
 * The reveal tween animates this card's transform, and when GSAP takes over an
 * element's transform it writes `translate: none; rotate: none; scale: none`
 * INLINE to stop the independent properties fighting its matrix. An inline
 * declaration beats any external rule at any specificity, so the lift silently
 * did nothing — the shadow and the sheen appeared, the card never moved.
 * !important is the only thing that outranks a non-important inline style. */
.lf-gal__stat:hover {
	translate: 0 -6px !important;
	box-shadow: 0 26px 50px -18px rgba(31, 36, 33, 0.34);
}

.lf-gal__stat:hover::after {
	transform: translateX(100%);
}

/* The amber card's sheen is white on yellow, which barely registers — it gets
   the forest ink instead, so the sweep reads as a shadow passing rather than a
   highlight. */
.lf-gal__stat--amber::after {
	background: linear-gradient(
		100deg,
		rgba(28, 75, 58, 0) 38%,
		rgba(28, 75, 58, 0.12) 50%,
		rgba(28, 75, 58, 0) 62%
	);
}

.lf-gal__stat--dark {
	background: var(--footer);
	color: var(--white-pure);
}

.lf-gal__stat--amber {
	background: var(--amber);
	color: var(--footer);
}

/* flex-end, not baseline.
 *
 * The digit windows are blocks with overflow:hidden, and such a box does not
 * report the baseline of the text inside it — browsers synthesise one from an
 * edge instead. Aligning to it put "yrs" up at the top of the number like a
 * footnote marker. Aligning the bottoms is unambiguous and is what a unit
 * suffix wants anyway. */
.lf-gal__figure {
	display: flex;
	align-items: flex-end;
	margin: 0;
	font-family: var(--font-headers);
	font-size: clamp(2rem, 1.4rem + 1.6vw, 2.75rem);
	font-weight: 700;
	line-height: 1.1;
	letter-spacing: -0.02em;
}

.lf-gal__digits {
	display: flex;
	align-items: flex-end;
}

/* The rolling window. One per digit, exactly one numeral tall, with the strip
   of ten inside it clipped to that height. */
.lf-gal__reel {
	display: block;
	height: 1.1em;
	overflow: hidden;
}

.lf-gal__strip {
	display: flex;
	flex-direction: column;
	/* Parked on the last cell — a copy of the final digit — so the number is
	   simply correct if the script never runs. The JS winds it back to the top
	   and lets it roll down to here.
	 *
	 * margin, not transform. GSAP reads an existing transform as the element's
	 * current position and adds its own on top: with translateY here the strip
	 * ended up at -968px instead of -484px, exactly twice as far, and every
	 * window showed blank space above the digits. Parking it with margin leaves
	 * the transform entirely to GSAP, and resting at yPercent 0 then means
	 * resting exactly where this puts it. */
	margin-top: calc(-1.1em * 10);
}

.lf-gal__num {
	display: block;
	height: 1.1em;
	line-height: 1.1;
	font-variant-numeric: tabular-nums;
}

.lf-gal__unit,
.lf-gal__char {
	font-size: 0.5em;
	font-weight: 700;
	line-height: 1.1;
	/* Lifted clear of the numerals' descender space so the unit sits on the
	   number's own baseline rather than under it. */
	margin: 0 0 0.16em 0.16em;
}

.lf-gal__statlabel {
	font-size: 0.9375rem;
	font-weight: 600;
	line-height: 1.45;
	color: inherit;
	opacity: 0.88;
}

/* The waitlist section.
 *
 * A light ground rather than the forest band it used to be. It sits directly
 * above the dark footer, and a plain field between the busy sections above and
 * the near-black below reads as a pause before the ask — which is the whole
 * point of putting the form here rather than anywhere else. */
.lf-closing {
	position: relative;
	isolation: isolate;
	background-color: var(--white);
	background-image: url("../img/cta-bg.avif");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}


/* Deck on the left, everything else on the right.
 *
 * The picture is the smaller half deliberately. It is decoration; the form is
 * the job, and giving the words and the fields the larger share is what stops
 * this reading as an image section that happens to have a form in it. */
.lf-closing__wrap {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: clamp(2.5rem, 1rem + 5vw, 5rem);
}

.lf-closing__body {
	flex: 1 1 auto;
	min-width: 0;
	max-width: 700px;
	text-align: left;
}

.lf-closing__lead {
	max-width: 54ch;
}

/* -- The deck ------------------------------------------------------------ */

/* Swiper's cards effect owns the transforms here — the fan, the depth and the
   shadows are all its. This only has to give it a box of the right shape and
   stop it being wider than the column.

   overflow stays visible: the rotated cards behind the top one sit outside the
   slider's own box, and clipping them would cut the fan off square. */
.lf-closing__deck {
	flex: none;
	width: 300px;
	height: 375px;
	overflow: visible;
}

.lf-closing__card {
	border-radius: var(--r-card);
	overflow: hidden;
	background: var(--gray);
	box-shadow: 0 24px 48px -18px rgba(31, 36, 33, 0.4);
}

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

/* -- The form ------------------------------------------------------------ */

.lf-form {
	margin-top: 36px;
}

/* Three fields and a button.
 *
 * The reference has one field with the button tucked inside it. Three fields
 * cannot do that, so the pattern is kept at the level that matters: the fields
 * sit in one bordered shell that reads as a single control, with the button
 * inset inside it rather than butted on the end. */
.lf-form__row {
	display: grid;
	/* Email gets the most: it is the longest thing anyone types here and the
	   only one where seeing what you typed matters. The postal code is seven
	   characters and the kid count is a select, so both can be narrow. */
	grid-template-columns: 0.78fr 1.5fr 0.82fr auto;
	gap: 10px;
	align-items: end;
	padding: 10px;
	background: var(--white-pure);
	border: 1px solid var(--rule);
	border-radius: calc(var(--r-card) + 4px);
	box-shadow: 0 10px 30px -18px rgba(31, 36, 33, 0.3);
	transition: border-color 0.3s var(--ease), box-shadow 0.3s var(--ease);
}

/* The shell lights up when anything inside it has focus, so the group reads as
   one control taking focus rather than four separate ones. */
.lf-form__row:focus-within {
	border-color: rgba(163, 74, 40, 0.45);
	box-shadow: 0 14px 34px -18px rgba(31, 36, 33, 0.38);
}

/* Inside the shell the individual fields lose their own boxes — two nested
   borders 10px apart is a box in a box. */
.lf-form__row .lf-field input,
.lf-form__row .lf-field select {
	background: transparent;
	border: 0;
	border-radius: var(--r-sm);
}

.lf-form__row .lf-field + .lf-field {
	border-left: 1px solid var(--rule);
	padding-left: 10px;
}

/* Colours for a light ground.
 *
 * Everything in this form was set for the forest band the section used to sit
 * on — cream labels, cream note, cream status line. On the new ground those
 * measured 1.13:1 and 1.08:1 against the field shell, which is not "hard to
 * read", it is invisible. These are the same roles restated in the dark inks. */
.lf-closing .lf-field__label {
	color: var(--second);
	/* One line each. "How many kids" wrapped to two and pushed its field lower
	   than the two beside it, which broke the row the shell is meant to be. */
	white-space: nowrap;
}

/* The button is inside the shell, so it does not need a button's own outer
   spacing — that padding was sized for a button standing on its own. */
.lf-closing .lf-form__row .lf-btn {
	padding-inline: 26px;
}

.lf-closing .lf-field input,
.lf-closing .lf-field select {
	color: var(--black);
}

.lf-closing .lf-field input::placeholder {
	color: var(--muted);
	opacity: 1;
}

.lf-closing .lf-form__note,
.lf-closing .lf-form__status {
	color: var(--muted);
}

.lf-closing .lf-closing__lead {
	color: var(--muted);
}

.lf-closing .lf-form__consent label {
	color: var(--muted);
}

.lf-closing .lf-form__success {
	background: var(--white-pure);
	border: 1px solid var(--rule);
	box-shadow: 0 20px 44px -24px rgba(31, 36, 33, 0.3);
}

.lf-closing .lf-form__success h3 {
	color: var(--black);
}

.lf-closing .lf-form__success p {
	color: var(--muted);
}

.lf-field {
	text-align: left;
}

.lf-field__label {
	display: block;
	margin-bottom: 8px;
	font-family: var(--font-headers);
	font-size: var(--fs-small);
	font-weight: 600;
	line-height: 1.4;
	color: var(--gray);
}

.lf-field__control {
	width: 100%;
	min-height: 56px;
	padding: 14px 18px;
	background: var(--white-pure);
	border: 2px solid transparent;
	border-radius: var(--r-sm);
	color: var(--black);
	line-height: 1.5;
}

.lf-field__control::placeholder {
	color: var(--muted);
	opacity: 1;
}

.lf-field__control:focus-visible {
	outline: 3px solid var(--focus-dark);
	outline-offset: 2px;
}

.lf-field__error {
	display: none;
	align-items: flex-start;
	gap: 8px;
	margin-top: 8px;
	font-size: var(--fs-small);
	line-height: 1.5;
	color: var(--amber);
}

.lf-field.has-error .lf-field__error {
	display: flex;
}

.lf-field.has-error .lf-field__control {
	border-color: var(--amber);
}

.lf-field__error svg {
	flex: none;
	width: 18px;
	height: 18px;
	margin-top: 3px;
}

.lf-form__consent {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	max-width: var(--prose);
	margin: 22px auto 0;
	text-align: left;
	font-size: var(--fs-small);
	line-height: 1.7;
	color: var(--gray);
}

.lf-form__consent input {
	flex: none;
	width: 22px;
	height: 22px;
	margin-top: 6px;
	accent-color: var(--amber);
}

.lf-form__note {
	margin-top: 20px;
	font-size: var(--fs-small);
	color: rgba(247, 241, 228, 0.85);
}

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

.lf-form__status {
	margin-top: 20px;
	font-size: var(--fs-small);
	color: var(--amber);
	min-height: 1.5em;
}

.lf-form__success {
	max-width: var(--prose);
	margin: 0 auto;
	padding: 40px 32px;
	background: rgba(255, 255, 255, 0.08);
	border-radius: var(--r-panel);
	text-align: center;
}

.lf-form__success h3 {
	color: var(--white-pure);
	margin-bottom: 12px;
}

.lf-btn__busy,
.lf-btn.is-busy .lf-btn__idle {
	display: none;
}

.lf-btn.is-busy .lf-btn__busy {
	display: inline;
}

/* The idle span carries the label and the icon together, so it has to lay them
   out the way the button itself would. It cannot inherit that: the button's own
   flex row sees this span as a single child. */
.lf-btn__idle {
	display: inline-flex;
	align-items: center;
	gap: 10px;
}

/* =========================================================================
   16  Footer
   ========================================================================= */

.lf-footer {
	position: relative;
	background: var(--footer);
	color: var(--footer-text);
	/* Bottom only. The closing bar no longer carries its own padding, so without
	   this the copyright line would render on the final pixel of the document.
	   The top spacing is left to .lf-footer__masthead. */
	padding-bottom: 15px;
	overflow: hidden;
}

/* The speck texture. Tiled small and left at full opacity — the dots are
   already only half-opaque amber in the artwork itself, and dimming the layer
   as well would put them below the threshold where they do anything. */
.lf-footer__pattern {
	position: absolute;
	inset: 0;
	background-repeat: repeat;
	background-size: 220px;
	opacity: 0.5;
	pointer-events: none;
}

/* -- The masthead band -------------------------------------------------- */

/* Phone at the left, mark centred, rider at the right.
 *
 * The centre column is the one that has to be centred in the CONTAINER, not
 * between its neighbours — the phone block and the rider are different widths,
 * so a three-column flex row would push the mark off-centre. Equal outer
 * columns with the middle sized to its content fixes the mark to the middle of
 * the page regardless of what sits either side of it. */
.lf-footer__masthead {
	position: relative;
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: 24px;
}

.lf-footer__call {
	display: flex;
	align-items: center;
	gap: 16px;
}

.lf-footer__callicon {
	display: grid;
	place-items: center;
	width: 52px;
	height: 52px;
	flex: none;
	border-radius: 50%;
	background: var(--amber);
	color: var(--footer);
}

.lf-footer__callicon svg {
	width: 22px;
	height: 22px;
}

.lf-footer__callbody {
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.lf-footer__calllabel {
	font-size: 0.875rem;
	line-height: 1.4;
	color: var(--footer-text);
}

/* The largest type in the footer, deliberately. It is the only thing down here
   that somebody in a hurry actually wants. */
.lf-footer__callnumber {
	font-family: var(--font-headers);
	font-size: clamp(1.375rem, 1.1rem + 1vw, 1.75rem);
	font-weight: 700;
	line-height: 1.2;
	color: var(--white-pure);
	text-decoration: none;
	transition: color 0.25s var(--ease);
}

.lf-footer__callnumber:hover {
	color: var(--amber);
}

.lf-footer__logo {
	display: block;
	line-height: 0;
}

.lf-footer__logo svg,
.lf-footer__logo img {
	width: clamp(180px, 16vw, 230px);
	height: auto;
}

/* The rider sits at the right end of the band and is allowed to overhang the
   container's right gutter a little, so it reads as arriving from off the page
   rather than parked inside a box. */
.lf-footer__rider {
	justify-self: end;
	width: clamp(150px, 15vw, 210px);
	margin-right: clamp(-2rem, -2vw, 0rem);
	line-height: 0;
	pointer-events: none;
}

.lf-footer__rider img {
	display: block;
	width: 100%;
	height: auto;
}

/* The inset hairline between the two bands. Inset rather than full-bleed,
   matching the reference — a rule that runs to both edges reads as a border on
   the section, not as a divider inside it. */
.lf-footer__rule {
	position: relative;
	display: block;
	height: 1px;
	margin-bottom: clamp(2.25rem, 1.5rem + 3vw, 3.75rem);
	background: rgba(247, 241, 228, 0.14);
}

/* -- The link columns ---------------------------------------------------- */

.lf-footer__grid {
	position: relative;
	display: grid;
	/* minmax(0,1fr) rather than 1fr: without it a long label like
	   "Delivery, Skip & Cancellation Policy" sets the column's minimum width
	   and squeezes the others.
	   Five columns, not the reference's four — the brand cell has moved up into
	   the masthead, but this site has one more list than that one does and
	   dropping a column of real links to match a layout is not a trade worth
	   making. The contact column is widest because its values do not wrap. */
	grid-template-columns: repeat(4, minmax(0, 1fr)) 1.2fr;
	gap: 36px 30px;
}

/* A hairline of the brand's third ink across the very top of the footer.
   It is the join between the page and the plinth it sits on. */
.lf-footer__edge {
	position: absolute;
	inset: 0 0 auto 0;
	height: 2px;
	background: linear-gradient(90deg, transparent, var(--amber) 22%, var(--clay) 50%, var(--amber) 78%, transparent);
	opacity: 0.55;
}

/* Opens the contact column, where the reference has "Contact us any time for
   getting" above its email address. */
.lf-footer__tagline {
	max-width: 30ch;
	margin-bottom: 18px;
	font-size: 0.9375rem;
	line-height: 1.8;
	color: var(--footer-text);
}

/* There are three of these now, not one — the sign-off is a block of lines, and
   at 18px apart they read as three unrelated sentences rather than one address
   block. Tightened between, with the 18px kept under the last of them so the
   contact rows below still clear it. */
.lf-footer__tagline + .lf-footer__tagline {
	margin-top: -12px;
}


/* Small tracked caps in white, as the reference sets them.
 *
 * These used to be large and amber. Five of those across the width read as five
 * headlines competing with each other, and the amber then had to be spent again
 * on the links' chevrons and once more on the email — by which point the accent
 * marks nothing in particular. Small caps put the structure in the typography
 * and leave the colour free to mean "this is the thing to press". */
.lf-footer__col h2 {
	/* Two lines' worth of space whether the title needs it or not, so a heading
	   that wraps — "For Organisations" — does not push its list below the
	   lists beside it. */
	position: relative;
	display: flex;
	align-items: flex-start;
	min-height: 2.4em;
	margin-bottom: 18px;
	padding-bottom: 14px;
	font-family: var(--font-headers);
	font-size: 0.875rem;
	font-weight: 700;
	line-height: 1.4;
	letter-spacing: 0.15em;
	text-transform: uppercase;
	color: var(--white-pure);
	text-wrap: balance;
	/* Drawn, not text-decoration. Under caps tracked at 0.15em an underline
	   runs on past the last letter by a whole space, and it would sit tight
	   against the descender-free caps rather than clear of them. A border sits
	   where the padding puts it and stops at the column edge. */
	border-bottom: 1px solid rgba(247, 241, 228, 0.14);
}

/* The heading's button, styled back to being text.
 *
 * This exists for the phone accordion. At every other width it must be
 * indistinguishable from the plain heading it replaced, so it inherits
 * everything and contributes nothing: no background, no border, no padding, and
 * the default cursor because on a wide screen it does not do anything. */
.lf-footer__toggle {
	display: block;
	width: 100%;
	margin: 0;
	padding: 0;
	background: none;
	border: 0;
	font: inherit;
	letter-spacing: inherit;
	color: inherit;
	text-align: left;
	text-transform: inherit;
	cursor: default;
}

/* The chevron only exists on a phone. */
.lf-footer__chev {
	display: none;
}

/* No wrapper behaviour above the breakpoint — the panel is just a box the list
   sits in, so the column measures exactly as it did before it existed. */
.lf-footer__panel {
	display: block;
}

/* An amber segment over the left of that hairline, so the underline belongs to
   the heading rather than reading as a divider the column happens to have. */
.lf-footer__col h2::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: -1px;
	width: 28px;
	height: 1px;
	background: var(--amber);
}

.lf-footer__list {
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Chevron bullets, always present, as the reference sets them. The link slides
   right on hover rather than the marker appearing — the list keeps a steady
   left edge either way. */
.lf-footer__list a,
.lf-footer__pending {
	display: inline-flex;
	align-items: baseline;
	gap: 10px;
	padding-block: 8px;
	font-size: 0.9375rem;
	line-height: 1.6;
	text-decoration: none;
	transition: color 0.25s var(--ease), transform 0.25s var(--ease);
}

.lf-footer__list a {
	color: var(--white-pure);
}

/* The marker window. Fixed size and clipped, so the two glyphs inside can pass
   through it without the label beside them shifting by a pixel. */
.lf-footer__marker {
	position: relative;
	display: inline-block;
	width: 12px;
	height: 12px;
	flex: none;
	overflow: hidden;
	transform: translateY(1px);
}

.lf-footer__list a svg,
.lf-footer__pending svg {
	position: absolute;
	left: 0;
	top: 0;
	width: 12px;
	height: 12px;
	color: var(--amber);
	transition: transform 0.4s var(--ease-out);
}

/* The arrow waits off to the left until the row is hovered. */
.lf-footer__list a svg:last-child {
	transform: translate3d(-150%, 0, 0);
}

.lf-footer__list a:hover svg:first-child,
.lf-footer__list a:focus-visible svg:first-child {
	transform: translate3d(150%, 0, 0);
}

.lf-footer__list a:hover svg:last-child,
.lf-footer__list a:focus-visible svg:last-child {
	transform: translate3d(0, 0, 0);
}

.lf-footer__list a:hover {
	color: var(--white-pure);
	transform: translateX(4px);
}

/* A label with no page behind it yet.
 *
 * Set in the same white as the real links now, so the lists read as one thing —
 * these used to be dimmed to say "not a link", and that left the columns
 * looking like half the items had failed to load. What still separates them is
 * behaviour: no underline, no colour change, no slide, and cursor:default. */
.lf-footer__pending {
	color: var(--white-pure);
	cursor: default;
}

.lf-footer__pending svg {
	color: rgba(216, 155, 63, 0.55);
}

/* ---- Social row ---- */

.lf-footer__social {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	margin: 26px 0 0;
	padding: 0;
	list-style: none;
}

/* Flat discs a shade lighter than the ground, as the reference has them — soft
   circles rather than outlined buttons, so they sit in the dark rather than on
   it. They only earn a colour once the pointer is on them. */
.lf-footer__social a {
	display: grid;
	place-items: center;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	background: rgba(247, 241, 228, 0.08);
	color: var(--footer-text);
	transition: background-color 0.25s var(--ease), color 0.25s var(--ease), transform 0.25s var(--ease);
}

.lf-footer__social a:hover {
	background: var(--second);
	color: var(--white-pure);
	transform: translateY(-3px);
}

.lf-footer__social svg {
	width: 19px;
	height: 19px;
}

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

.lf-footer__contact {
	margin: 0;
	padding: 0;
	list-style: none;
}

.lf-footer__contact li {
	display: flex;
	align-items: flex-start;
	gap: 14px;
	padding-block: 9px;
}

.lf-footer__cicon {
	flex: none;
	display: grid;
	place-items: center;
	width: 34px;
	height: 34px;
	border-radius: 50%;
	background: rgba(216, 155, 63, 0.14);
	color: var(--amber);
}

.lf-footer__cicon svg {
	width: 17px;
	height: 17px;
}

.lf-footer__cvalue {
	display: block;
	font-size: 0.9375rem;
	line-height: 1.6;
	color: var(--footer-text);
	text-decoration: none;
	word-break: break-word;
	transition: color 0.25s var(--ease);
}

/* The underline is gone, so the colour has to carry the affordance instead —
   otherwise these three rows are indistinguishable from the plain text above
   them and nothing says they can be pressed. */
a.lf-footer__cvalue:hover,
a.lf-footer__cvalue:focus-visible {
	color: var(--amber);
}

/* The closing block. */
.lf-footer__legal {
	position: relative;
	margin-top: clamp(2rem, 1.5rem + 2vw, 3rem);
	text-align: center;
}

/* The company name as a watermark: sized to the width of the container, set in
   the ground colour at a few per cent, and clipped so it can be oversized
   without widening the page.
 *
 * cqi would be neater but the container is not a container-query container, so
 * the size is driven off the viewport with a cap — at 1440 it lands just inside
 * the gutters, and it stops growing after that rather than running off the ends
 * on an ultrawide screen. */
.lf-footer__company {
	position: relative;
	margin: 0;
	/* 8.6vw, measured rather than guessed: the name sets 1057px wide at 100px in
	   this face, against 1337px of container at 1440 — so it fits at 126px, and
	   8.6vw lands just under that. At 9.2vw it clipped the final "C." off. */
	width: 100%;
	overflow: hidden;
	font-family: var(--font-headers);
	font-size: clamp(2.5rem, 8.6vw, 7.75rem);
	font-weight: 700;
	line-height: 1;
	letter-spacing: -0.02em;
	white-space: nowrap;
	text-transform: uppercase;
	/* Low enough to read as texture rather than as a heading somebody forgot to
	   style. It should be findable, not legible at a glance. */
	color: rgba(247, 241, 228, 0.055);
	user-select: none;
}

/* Typed out on arrival.
 *
 * The default above is the finished state, so the name is simply there if the
 * script never runs. JS adds .is-typing to wind it back to nothing — with no
 * transition, so that reset is instant rather than a backwards erase — and then
 * .is-typed to play it forwards. The step count is set from the actual string
 * length in JS, because the name is editable and a hard-coded steps() would
 * type at the wrong rhythm for anything but this one. */
.lf-footer__company.is-typing {
	width: 0;
	transition: none;
}

.lf-footer__company.is-typed {
	width: 100%;
	transition: width 1.9s steps(23);
}

/* The caret. Brighter than the letters — at 5.5% it would be invisible, and a
   typewriter with no cursor is just a wipe. It stops blinking once the line has
   finished, the way a real one would. */
.lf-footer__company.is-typing::after,
.lf-footer__company.is-typed::after {
	content: "";
	position: absolute;
	right: 0;
	top: 12%;
	bottom: 12%;
	width: 3px;
	background: rgba(216, 155, 63, 0.5);
	animation: lf-caret 0.9s steps(1) infinite;
}

.lf-footer__company.is-done::after {
	animation: none;
	opacity: 0;
}

@keyframes lf-caret {
	0%,
	50% {
		opacity: 1;
	}

	51%,
	100% {
		opacity: 0;
	}
}

.lf-footer__bottom {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-top: 14px;
	/* Same weight and colour as the rule under the masthead, so the footer is
	   bracketed by one hairline top and bottom rather than two different lines.
	   The padding is what keeps the text off it — with margin-top alone the
	   border would sit against the line above. */
	padding-top: 18px;
	border-top: 1px solid rgba(247, 241, 228, 0.14);
	font-size: 0.8125rem;
	line-height: 1.9;
	text-align: center;
	color: rgba(247, 241, 228, 0.5);
}

/* =========================================================================
   17  Mobile bottom bar
   ========================================================================= */

/* =========================================================================
   17  Mobile bottom bar
   =========================================================================

   Below the drawer breakpoint the page behaves like an application: a
   persistent bar at the thumb, four destinations, and the one action the page
   exists for raised in the middle where it is easiest to reach one-handed.

   Hidden above 991px entirely — on a desktop the sticky header already does
   this job and a floating bar would just be in the way. */

.lf-appbar {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: var(--z-bottombar);
	display: none;
	padding-bottom: env(safe-area-inset-bottom);
	background: rgba(252, 250, 246, 0.92);
	backdrop-filter: saturate(180%) blur(18px);
	-webkit-backdrop-filter: saturate(180%) blur(18px);
	box-shadow: 0 -1px 0 var(--rule), 0 -12px 32px rgba(31, 36, 33, 0.08);
	transition: transform 0.3s var(--ease), opacity 0.25s var(--ease);
}

.lf-appbar.is-hidden,
.lf-appbar.is-tucked {
	transform: translateY(105%);
	opacity: 0;
	pointer-events: none;
}

.lf-appbar__list {
	display: grid;
	grid-template-columns: repeat(5, 1fr);
	align-items: end;
	margin: 0;
	padding: 0;
	list-style: none;
}

.lf-appbar__slot {
	display: flex;
	justify-content: center;
}

.lf-appbar__link {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 5px;
	width: 100%;
	min-height: 58px;
	padding: 9px 2px 10px;
	color: var(--muted);
	text-decoration: none;
	transition: color 0.22s var(--ease);
	-webkit-tap-highlight-color: transparent;
}

.lf-appbar__icon {
	position: relative;
	display: grid;
	place-items: center;
	width: 26px;
	height: 26px;
}

.lf-appbar__icon svg {
	width: 22px;
	height: 22px;
}

.lf-appbar__label {
	font-family: var(--font-headers);
	font-size: 0.625rem;
	font-weight: 600;
	letter-spacing: 0.02em;
	line-height: 1;
	white-space: nowrap;
}

.lf-appbar__link[aria-current='true'] {
	color: var(--main);
}

/* A dot rather than a filled pill: it marks position without shouting, and it
   survives at 10px where a background change would just look like a smudge. */
.lf-appbar__link[aria-current='true'] .lf-appbar__icon::after {
	content: '';
	position: absolute;
	bottom: -7px;
	width: 4px;
	height: 4px;
	border-radius: 50%;
	background: var(--second);
}

/* The raised action. */
.lf-appbar__cta {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 5px;
	width: 100%;
	padding-bottom: 10px;
	color: var(--main);
	text-decoration: none;
	-webkit-tap-highlight-color: transparent;
}

.lf-appbar__disc {
	display: grid;
	place-items: center;
	width: 54px;
	height: 54px;
	margin-top: -20px;
	border: 4px solid var(--white);
	border-radius: 50%;
	background: var(--second);
	color: var(--white-pure);
	box-shadow: 0 8px 20px rgba(163, 74, 40, 0.34);
	transition: transform 0.22s var(--ease), background-color 0.22s var(--ease);
}

.lf-appbar__cta:active .lf-appbar__disc {
	transform: scale(0.94);
	background: var(--second-darker);
}

.lf-appbar__cta .lf-appbar__label {
	font-weight: 700;
	color: var(--second);
	/* The real button label is longer than the four destination labels, so it
	   is allowed to shrink rather than push its neighbours out of the grid. */
	font-size: 0.5938rem;
	letter-spacing: 0;
}

/* =========================================================================
   18  Motion
   ========================================================================= */

/* Hidden only until the scroll module takes over. GSAP adds `is-in` to every
   one of these before it animates anything, so if the script never runs — or
   reduced motion is on — the content is visible rather than stuck at zero
   opacity. That failure mode is the reason this is a class and not a style. */
.lf-reveal {
	opacity: 0;
	transform: translateY(24px);
}

.lf-reveal.is-in {
	opacity: 1;
	transform: none;
	transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}

/* Reading progress */
.lf-progress {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: 120;
	height: 3px;
	pointer-events: none;
}

.lf-progress span {
	display: block;
	height: 100%;
	background: linear-gradient(90deg, var(--main), var(--second) 60%, var(--amber));
	transform: scaleX(0);
	transform-origin: left center;
}

@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}

	*,
	*::before,
	*::after {
		animation-duration: 0.001ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.001ms !important;
	}

	.lf-reveal {
		opacity: 1;
		transform: none;
	}

	/* The step drawings finish instantly rather than not drawing at all —
	   otherwise the icons would sit permanently invisible. */
	.lf-step__art svg path,
	.lf-step__art svg rect,
	.lf-step__art svg circle {
		stroke-dashoffset: 0;
		animation: none !important;
	}

	.lf-progress {
		display: none;
	}

	/* The wash and the icon hand-off survive — with the durations collapsed
	   above they simply arrive rather than travel, and the hover state stays
	   just as legible. The lift does not: two pixels with no time to cross them
	   is a twitch, which is the thing this setting exists to stop. */
	.lf-btn:hover,
	.lf-btn:focus-visible,
	.lf-btn:active {
		transform: none;
	}

	/* The scroll cue stops rather than disappearing — it still marks the foot
	   of the hero, it just no longer moves. */
	.lf-hero__wheel {
		animation: none;
		opacity: 1;
	}
}

/* =========================================================================
   Responsive — 1200 / 991 / 768 / 600, the reference's own breakpoints
   ========================================================================= */

/* A 1440px container on a 1440px screen leaves the copy hard against the
   viewport edge. Widening the gutter above that width gives the page a margin
   without moving anything relative to anything else — the header, the hero and
   every section below stay on the same left edge. */
@media (min-width: 1200px) {
	:root {
		--gutter: 44px;
	}
}

@media (max-width: 1280px) {
	.lf-footer__grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: 40px 32px;
	}

	.lf-gal__grid {
		gap: 16px;
	}

	.lf-gal__col,
	.lf-gal__stack,
	.lf-gal__pair {
		gap: 16px;
	}
}

@media (max-width: 900px) {
	/* Two columns, not three.
	 *
	 * At this width three columns put the small tiles under 130px, which is too
	 * small to see what the food is — and a gallery whose point is "look at the
	 * actual lunches" cannot afford tiles you have to squint at. The middle
	 * column drops below the other two and keeps its large photograph. */
	.lf-gal__grid {
		flex-wrap: wrap;
	}

	.lf-gal__col--left,
	.lf-gal__col--right {
		/* Matches the 16px gap set above. At calc(50% - 12px) the two columns
		   plus the gap came to more than the container and the page scrolled
		   sideways. */
		flex: 1 1 calc(50% - 8px);
	}

	.lf-gal__col--mid {
		flex: 1 1 100%;
		order: 1;
		flex-direction: row;
		align-items: flex-end;
	}

	.lf-gal__col--mid > * {
		flex: 1 1 0;
		min-width: 0;
	}

	/* The big tile is no longer alone in a column, so it stops being the tall
	   one and matches the pair beside it. */
	.lf-gal__col--mid > .lf-gal__tile {
		aspect-ratio: 180 / 201;
	}
}

@media (max-width: 991px) {
	.lf-nav,
	.lf-header__cta {
		display: none;
	}

	.lf-burger {
		display: grid;
	}

	/* Below 992px Swiper takes the row over, so the grid rules are handed back.
	   The ancestor is load-bearing: `.swiper { padding: 0 }` matches this element
	   at the same specificity from a stylesheet enqueued later, so a bare
	   `.lf-cards__grid` lost its padding-bottom and the pagination dots ended up
	   sitting on top of the last card instead of below it. */
	.lf-cards .lf-cards__grid {
		overflow: hidden;
		padding-bottom: 44px;
		/* Swiper's own variable rather than a `bottom` override on the dots.
		   Its default of 8px sits the row 8px up from the padding box, which put
		   it 6px inside the last card; overriding it by selector would mean
		   out-specifying `.swiper-horizontal > .swiper-pagination-bullets`, and
		   the variable is the supported way in. */
		--swiper-pagination-bottom: 4px;
	}

	.lf-cards__grid .swiper-wrapper {
		display: flex;
		gap: 0;
		transform: translate3d(0, 0, 0);
	}

	.lf-cards__grid .swiper-wrapper {
		transform: none;
	}

	.lf-cards__grid.swiper-initialized .swiper-wrapper {
		transform: translate3d(0, 0, 0);
	}

	.lf-card {
		height: auto;
	}

	.lf-cards__dots {
		display: block;
	}

	.lf-cards__dots .swiper-pagination-bullet {
		width: 7px;
		height: 7px;
		background: var(--main);
		opacity: 0.25;
	}

	.lf-cards__dots .swiper-pagination-bullet-active {
		width: 22px;
		border-radius: 4px;
		background: var(--second);
		opacity: 1;
	}

	.lf-cards {
		margin-top: clamp(-2rem, -4vw, -3rem);
	}

	.lf-sangita__grid,
	.lf-steps,
	.lf-schools__panel {
		grid-template-columns: 1fr;
	}

	/* The sticky headline has nothing to stick against once the columns stack. */
	.lf-halal__grid {
		grid-template-columns: 1fr;
		gap: 2rem;
	}

	.lf-halal__aside {
		position: static;
	}

	.lf-halal__body {
		max-width: none;
	}

	.lf-assure {
		grid-template-columns: 1fr 1fr;
	}

	.lf-plans__grid {
		grid-template-columns: 1fr;
		max-width: 520px;
		margin-inline: auto;
	}

	/* The shell stops being a row and becomes a stack, so the borders that
	   divided the fields horizontally have to divide them vertically instead. */
	.lf-form__row {
		grid-template-columns: 1fr;
		gap: 4px;
	}

	.lf-form__row .lf-field + .lf-field {
		border-left: 0;
		border-top: 1px solid var(--rule);
		padding-left: 0;
		padding-top: 10px;
	}

	.lf-form__row .lf-btn {
		width: 100%;
		margin-top: 6px;
	}

	/* Deck above the words. It is 400px of picture at the top of a section
	   somebody has scrolled a long way to reach, so it is scaled down and the
	   column order is left alone — the form is what they came for. */
	.lf-closing__wrap {
		flex-direction: column;
		align-items: stretch;
		gap: 2rem;
	}

	.lf-closing__deck {
		align-self: center;
		width: 268px;
		height: 300px;
	}

	.lf-closing__card {
		width: 220px;
		height: 262px;
	}

	.lf-closing__body {
		max-width: none;
	}

	/* No mobile override any more — the overlay is a flat wash at every width,
	   and at 93% it is heavier than the graded version was at its lightest, so
	   the copy holds here regardless of what photograph gets uploaded. */

	/* Nothing is absolutely positioned in the hero at this width, so the tall
	   bottom padding that reserved room for the social row and the scroll cue
	   is no longer needed — it would just be a gap above the plate. */
	.lf-hero__inner {
		min-height: 0;
		padding-block: clamp(2rem, 1rem + 4vw, 3.5rem) 2rem;
	}

	.lf-hero__content {
		max-width: none;
	}

	.lf-hero__cta {
		display: flex;
		width: 100%;
	}

	.lf-hero__cta .lf-btn {
		width: 100%;
	}

	.lf-hero__scroll {
		display: none;
	}

	/* Back into the flow at the end of the copy rather than pinned to a corner
	   that no longer exists. */
	.lf-hero__social {
		position: static;
		justify-content: center;
		margin-top: 2.25rem;
		transform: none;
	}

	/* The plate has nowhere to sit once the columns collapse — absolutely
	   positioned it would land on top of the headline. It returns to the flow
	   under the copy, centred, at a size that does not add half a screen of
	   scrolling to get past it. */
	/* Icons only. The sentence beside them already wraps to two lines at this
	   width; adding a phone number and an email address in full would take it to
	   four and push the hero off the fold. The aria-label carries the name. */
	/* The announcement bar does not appear on a phone at all.
	 *
	 * Vertical space above the fold is the scarcest thing on this layout, and the
	 * strip was costing 44px of it before the header had even started. Hidden
	 * rather than dismissible: the dismiss control only ever bought the reader
	 * that space back after they had already been pushed down the page once.
	 *
	 * Note that this makes the announce rules below inert at this width — they
	 * are left in place so the bar comes back correctly styled if it is ever
	 * shown here again, but nothing in them renders today. The line itself is
	 * still on the page above 991px, where there is room for it. */
	.lf-announce {
		display: none;
	}

	.lf-announce__contact span {
		display: none;
	}

	.lf-announce__contact {
		gap: 4px;
	}

	.lf-announce__contact li + li {
		padding-left: 12px;
	}

	.lf-announce__contact a {
		padding: 6px 4px;
	}

	.lf-announce__contact svg {
		width: 17px;
		height: 17px;
	}

	.lf-announce__inner--split {
		gap: 14px;
		padding-right: 40px;
	}

	.lf-hero .lf-hero__plate {
		/* Relative, not static: the controls are absolutely positioned against
		   this element, and a static frame would hand them to the section
		   instead — putting them somewhere near the headline. The offsets have
		   to be cleared by hand, since relative still honours them. */
		position: relative;
		right: auto;
		top: auto;
		width: min(64vw, 330px);
		/* Square again, and the height has to be handed back explicitly.
		   The desktop rule now sets width and height separately; left alone the
		   height would keep its clamp here and the frame would come out taller
		   than it is wide — 240 x 320 at 375px. */
		height: auto;
		aspect-ratio: 1;
		margin: 0 auto 3rem;
		transform: none;
		/* Rounded all round again. The square right-hand corners exist because
		   that side runs off the edge of the page at desktop widths; here the
		   frame is centred in the flow with air on both sides, and two square
		   corners on one side of a centred card read as a mistake. */
		border-radius: var(--r-panel);
	}

	.lf-hero__plate-ui {
		border-radius: 0 0 calc(var(--r-panel) - 1px) calc(var(--r-panel) - 1px);
	}

	.lf-appbar {
		display: block;
	}

	/* Clear the bar. 58px of bar plus the raised disc's overhang. */
	body.lf-front {
		padding-bottom: calc(70px + env(safe-area-inset-bottom));
	}
}

@media (max-width: 768px) {
	:root {
		--gutter: 20px;
	}

	/* One column per row. The lists collapse into an accordion below, so the
	   footer is four headings tall whether there is one column across or two —
	   and one full-width row gives the longer labels room not to wrap. */
	.lf-footer__grid {
		grid-template-columns: 1fr;
		gap: 0;
	}

	/* The masthead is the mark alone on a phone.
	   The number and the rider both come off: the number is already the middle
	   item of the contact block a few hundred pixels below, and repeating it
	   twice in one footer is repetition, not emphasis. The rider is 200px of
	   decoration at the top of the longest scroll on the page, and its animation
	   is desktop-only anyway — on a phone it never moves, so it is a large
	   static picture and nothing else. */
	.lf-footer__masthead {
		grid-template-columns: 1fr;
		justify-items: center;
		gap: 22px;
		padding-top: 34px;
		text-align: center;
	}

	.lf-footer__call,
	.lf-footer__rider {
		display: none;
	}

	/* With the number and the rider gone the mark is the whole band, so it needs
	   its own space under it before the first accordion row. */
	.lf-footer__logo {
		margin-bottom: 15px;
	}

	/* -- The accordion --------------------------------------------------- */

	/* The heading becomes the control: full width, with the chevron pushed to
	   the far end so the whole row is the target rather than the words. */
	.lf-footer__toggle {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 16px;
		min-height: 44px;
		cursor: pointer;
	}

	.lf-footer__chev {
		display: grid;
		place-items: center;
		width: 20px;
		height: 20px;
		flex: none;
		color: var(--amber);
		transition: transform 0.32s var(--ease-out);
	}

	.lf-footer__chev svg {
		width: 16px;
		height: 16px;
	}

	.lf-footer__col.is-open .lf-footer__chev {
		transform: rotate(180deg);
	}

	/* The heading's own hairline now separates one accordion row from the next,
	   so it runs the full width and the amber segment goes — a 28px accent on
	   every row of a four-row stack reads as a repeating tick, not an accent. */
	.lf-footer__col h2 {
		min-height: 0;
		margin: 0;
		padding-bottom: 0;
		border-bottom: 0;
	}

	.lf-footer__col h2::after {
		display: none;
	}

	.lf-footer__col {
		border-bottom: 1px solid rgba(247, 241, 228, 0.12);
	}

	/* grid-template-rows 0fr -> 1fr, not max-height.
	   max-height needs a guessed ceiling: too low and a long column is clipped,
	   too high and the close runs at the wrong speed because most of the
	   transition is spent collapsing empty space. This animates to the content's
	   real height with no number in it at all. */
	.lf-footer__panel {
		display: grid;
		grid-template-rows: 0fr;
		transition: grid-template-rows 0.34s var(--ease-out);
	}

	.lf-footer__panel > * {
		overflow: hidden;
	}

	.lf-footer__col.is-open .lf-footer__panel {
		grid-template-rows: 1fr;
	}

	/* Only closed while it is actually a disclosure. The JS puts this class on
	   when it takes the columns over; without it — if the script never runs, or
	   the viewport is above the breakpoint — every panel is open and the footer
	   degrades to the plain list it has always been. */
	.lf-footer__col:not(.lf-footer__col--acc) .lf-footer__panel {
		grid-template-rows: 1fr;
	}

	.lf-footer__list {
		padding-bottom: 10px;
	}

	/* Contact is not an accordion: it keeps its own underlined heading and its
	   rule, and sits open at the foot of the stack. */
	.lf-footer__col--contact {
		padding-top: 26px;
		border-bottom: 0;
	}

	.lf-footer__col--contact h2 {
		margin-bottom: 18px;
		padding-bottom: 14px;
		border-bottom: 1px solid rgba(247, 241, 228, 0.14);
	}

	.lf-footer__col--contact h2::after {
		display: block;
	}

	.lf-spec__list {
		grid-template-columns: 1fr;
		gap: 18px;
	}

	/* Footer links are the densest set of targets on the page. At the desktop
	   line-height they measure 32px, under the 44px minimum, and they sit
	   directly above one another — which is where mis-taps actually happen. */
	/* Top-aligned, not centred.
	   Labels wrap to two and three lines in these narrower columns, and centring
	   put the marker halfway down the block — beside the second line of
	   "Allergen & Ingredient Statement" rather than beside its beginning. The
	   marker belongs to the first line, so it aligns to it and the offset below
	   drops it onto that line's x-height. */
	.lf-footer__list a,
	.lf-footer__pending {
		display: flex;
		align-items: flex-start;
		min-height: 44px;
	}

	/* The contact column got missed when the rule above was written.
	 *
	 * Those are the four link columns; these three are the phone, the email and
	 * the WhatsApp number, which are the rows down here somebody in a hurry
	 * actually presses — and they measured 24px, well under the same minimum.
	 *
	 * Padding out and pulling back by the same amount, rather than min-height:
	 * the row is a flex line with a top-aligned icon chip beside it, so growing
	 * the anchor would drop the text off the icon it belongs to. This grows only
	 * the hit area — 24px of text becomes a 44px target and nothing moves. */
	.lf-footer__cvalue {
		padding-block: 10px;
		margin-block: -10px;
	}

	.lf-footer__marker {
		margin-top: 5px;
	}

	.lf-footer__list li + li {
		margin-top: 0;
	}
}

@media (max-width: 600px) {
	/* Two columns and nothing nested inside them.
	 *
	 * The wider layout subdivides each outer column into two again, which at
	 * 390px left tiles 72px across — small enough that you cannot tell one lunch
	 * from another, in a section whose whole point is showing the food. Every
	 * inner row becomes a stack, so the only division left is the two outer
	 * columns and each tile gets the full width of one. */
	.lf-gal__col--left,
	.lf-gal__col--right,
	.lf-gal__col--mid,
	.lf-gal__pair {
		display: flex;
		flex-direction: column;
	}

	/* flex: none on the children, and it is load-bearing.
	 *
	 * The wider layout gives them `flex: 1 1 0` to share a row. Turned into a
	 * column that zero basis becomes a zero HEIGHT — and a box with a zero
	 * height and an aspect-ratio computes a zero width to match, so three tiles
	 * vanished entirely. Sized by their own ratio instead, off the width the
	 * column stretches them to. */
	.lf-gal__col--left > *,
	.lf-gal__col--right > *,
	.lf-gal__col--mid > * {
		flex: none;
	}

	/* Every tile takes the same shape once they are all one across. The varied
	   ratios existed to give the columns unequal heights, and with the middle
	   column stacked underneath there are no unequal heights left to make. */
	.lf-gal__col > .lf-gal__tile,
	.lf-gal__pair .lf-gal__tile,
	.lf-gal__stack .lf-gal__tile,
	.lf-gal__col--mid > .lf-gal__tile,
	.lf-gal__stat {
		aspect-ratio: 4 / 5;
	}

	/* No room for the rule and the arrows on one line at this width, so the
	   marker row wraps and the arrows sit under it. */
	.lf-week__marker {
		flex-wrap: wrap;
		gap: 14px;
	}

	.lf-week__arrow {
		width: 44px;
		height: 44px;
	}

	.lf-week__count {
		display: none;
	}

	/* Scoped to match the desktop rule's specificity — that one had to gain an
	   ancestor to beat Swiper's `.swiper-slide { display: block }`, and these
	   overrides have to keep up with it or they lose to the wider layout. */
	.lf-cards__grid .lf-card {
		flex-direction: column;
		align-items: flex-start;
		text-align: left;
		padding: 0 24px 28px;
	}

	/* A banner across the top rather than a circle beside the text, so the ring
	   goes too: it is there to make a circle read as a plate, and on a
	   full-width rectangle it would just look like a mistake. */
	.lf-cards__grid .lf-card__media {
		width: calc(100% + 48px);
		height: 190px;
		margin: 0 -24px 20px;
		border-radius: var(--r-card) var(--r-card) 0 0;
		box-shadow: none;
	}

	.lf-assure {
		grid-template-columns: 1fr;
	}

	/* The clamp floor was 2.5rem, which beat the vw term at this width and set
	   the name at 40px — about 90px wider than the container, so it typed itself
	   out and then sat clipped in the middle of a word. Sized off the viewport
	   here instead: at 390 this is 29.6px, and the name measures 318px against
	   335px of container. */
	.lf-footer__company {
		font-size: 7.6vw;
	}

	/* Two lines side by side need about 620px between them. Below that the row
	   wraps and centres rather than crushing both to two words a line — the
	   space-between above still applies, it just has one item per line to
	   distribute. */
	.lf-footer__bottom {
		flex-wrap: wrap;
		justify-content: center;
		gap: 4px 24px;
	}

	/* Follows the amber block to its new home on the frame. Left on the old
	   selector this stopped matching anything and the block reappeared on a
	   phone, where the column is too narrow to carry it. */
	.lf-sangita__frame::before {
		display: none;
	}

	/* An app screen, not a document: tighter gutters, a shorter header, and
	   headings that start at the left edge like every native list screen. */
	.lf-header__inner {
		min-height: 66px;
	}

	.lf-logo svg,
	.lf-logo img {
		width: 158px;
	}

	.lf-shead--center {
		text-align: left;
	}

	.lf-shead--center .lf-shead__lead,
	.lf-shead--center .lf-shead__rule {
		margin-inline: 0;
	}

	.lf-food__note,
	.lf-plans__more,
	.lf-plans__tax {
		text-align: left;
	}

	.lf-halal__body,
	.lf-closing__body {
		text-align: left;
	}

	.lf-btns--center {
		justify-content: stretch;
	}

	.lf-btns--center .lf-btn {
		flex: 1;
	}
}
