/*
 * Knaus Berry Farm — compiled stylesheet.
 *
 * Authored here in assets/dist/ and resolved through assets/dist/manifest.json
 * by kbf_asset() in functions.php. When the asset pipeline lands it should
 * take over emitting a fingerprinted file + manifest; the theme already reads
 * whatever the manifest points at, so nothing in PHP changes when it does.
 *
 * Design tokens below are the Figma variables for the "Homepage - CLIENT
 * APPROVED" frame — colours, type, spacing and radii. Treat them as the single
 * source of truth: reference the custom properties, do not re-type the values.
 */

:root {
	/* Colour */
	--kbf-surface-primary: #fbf8f3;   /* roof / cream page ground */
	--kbf-surface-secondary: #efe9e4;  /* dark roof / footer */
	--kbf-surface-secondary-30: rgba(239, 233, 228, 0.3); /* the same, at 30% */
	--kbf-surface-tertiary: #46443e;   /* chalkboard */
	--kbf-text-primary: #46443e;
	--kbf-text-secondary: #fbf8f3;
	--kbf-barn-red: #bd545f;
	--kbf-barn-red-dark: #a8474f;      /* pressed / hover state of barn red */
	--kbf-chalkboard: #46443e;
	--kbf-chalkboard-80: rgba(70, 68, 62, 0.8);
	/* The preorder frames set body copy at three distinct strengths — clause
	   text, summary labels and fine print — so each is a token rather than an
	   opacity on the element, which would drag the barn-red emphasis inside it
	   down too. */
	--kbf-chalkboard-75: rgba(70, 68, 62, 0.75);
	--kbf-chalkboard-65: rgba(70, 68, 62, 0.65);
	--kbf-chalkboard-60: rgba(70, 68, 62, 0.6);
	--kbf-roof-60: rgba(251, 248, 243, 0.6); /* cream on chalkboard, de-emphasised */
	--kbf-border-dark: rgba(70, 68, 62, 0.2);
	/* Figma border/border-tertiary. Same value as surface-secondary, kept apart
	   because the design uses them as different things: a field's edge and a
	   card's ground. */
	--kbf-border-tertiary: #efe9e4;
	--kbf-hairline: rgba(0, 0, 0, 0.1);

	/* Type */
	--kbf-font-title: "Playfair Display", Georgia, "Times New Roman", serif;
	--kbf-font-body: "Josefin Sans", system-ui, -apple-system, "Segoe UI", sans-serif;

	/* Spacing */
	--kbf-space-xxs: 8px;
	--kbf-space-xs: 16px;
	--kbf-space-s: 24px;
	--kbf-space-m: 32px;
	--kbf-space-l: 40px;
	--kbf-space-xl: 48px;
	--kbf-space-2xl: 64px;
	--kbf-space-5xl: 96px;

	/* Radius */
	--kbf-radius-minimal: 8px;
	--kbf-radius-s: 16px;
	--kbf-radius-m: 24px;
	--kbf-radius-xl: 40px;
	--kbf-radius-full: 999px;

	/* Layout */
	--kbf-max: 1440px;
	--kbf-gutter: clamp(20px, 5.5vw, 80px);
}

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

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

body {
	margin: 0;
	font-family: var(--kbf-font-body);
	font-size: 16px;
	line-height: 1.6;
	color: var(--kbf-text-primary);
	background: var(--kbf-surface-primary);
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

img {
	max-width: 100%;
	display: block;
}

a {
	color: inherit;
	text-decoration: none;
}

h1,
h2,
h3 {
	font-family: var(--kbf-font-title);
	font-weight: 700;
	line-height: 1.2;
	margin: 0;
}

/* Shared display heading: "A Tradition / Cinnamon Rolls" scale */
.kbf-display {
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: clamp(40px, 6vw, 64px);
	line-height: 1.15;
}

/* Figma "Heading 3": 48px title, one step below .kbf-display. Used for the
   About statement and the "Our History" heading. */
.kbf-heading {
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: clamp(30px, 4.2vw, 48px);
	line-height: 1.2;
	margin: 0;
}

.kbf-eyebrow {
	font-family: var(--kbf-font-body);
	font-weight: 500;
	letter-spacing: 0.1em;
	text-transform: uppercase;
}

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

.kbf-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--kbf-space-xxs);
	font-family: var(--kbf-font-body);
	font-weight: 700;
	font-size: 18px;
	line-height: 1;
	padding: 17px 24px 15px;
	border-radius: var(--kbf-radius-full);
	border: 1px solid transparent;
	cursor: pointer;
	transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease, opacity 0.18s ease;
}

.kbf-btn--solid {
	background: var(--kbf-barn-red);
	color: var(--kbf-text-secondary);
}

.kbf-btn--solid:hover {
	background: var(--kbf-barn-red-dark);
}

.kbf-btn--outline {
	background: transparent;
	color: var(--kbf-barn-red);
	border-color: var(--kbf-barn-red);
}

.kbf-btn--outline:hover {
	background: var(--kbf-barn-red);
	color: var(--kbf-text-secondary);
}

/* Filled cream — the primary action when the ground is barn red (Visit modal). */
.kbf-btn--cream {
	background: var(--kbf-surface-primary);
	color: var(--kbf-text-primary);
}

.kbf-btn--cream:hover {
	background: var(--kbf-surface-secondary);
}

/* On the dark hero the header CTA is a cream outline */
.kbf-btn--ghost {
	background: transparent;
	color: var(--kbf-text-secondary);
	border-color: var(--kbf-text-secondary);
}

.kbf-btn--ghost:hover {
	background: var(--kbf-text-secondary);
	color: var(--kbf-text-primary);
}

.kbf-btn[aria-disabled="true"] {
	opacity: 0.55;
	pointer-events: none;
}

/* Full-bleed within its column — the preorder frames set the Terms and Pay
   actions this way, and every action is this on a phone. */
.kbf-btn--block {
	width: 100%;
}

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

/*
 * The masthead sits on the cream page ground, with dark ink and the solid
 * barn-red order button. This is the default because it is what the design
 * does on every page except one — About and the Terms of Sale used to opt into
 * it individually, which left every route nobody had styled yet, and every 404,
 * rendering a chalkboard bar that appears nowhere in the design.
 *
 * The home page is the single exception: its masthead overlays the hero
 * photograph, so it is transparent with cream ink. Those overrides follow
 * immediately below, and kbf_wordmark_src() picks the matching ink cut of the
 * wordmark for the same one page.
 */
.kbf-header {
	position: static;
	z-index: 20;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--kbf-space-l);
	padding: clamp(20px, 3vw, 32px) var(--kbf-gutter);
	background: var(--kbf-surface-primary);
}

/* On the home page the header overlays the hero, transparent. */
.home .kbf-header {
	position: absolute;
	inset: 0 0 auto 0;
	background: transparent;
}

.kbf-header__logo {
	display: block;
	width: clamp(190px, 22vw, 296px);
	flex-shrink: 0;
}

.kbf-header__logo img {
	width: 100%;
	height: auto;
}

/* Figma groups the nav with the order CTA on the right, logo alone on the left
   — so push the panel holding both to the right edge. */
.kbf-header__panel {
	display: flex;
	align-items: center;
	gap: var(--kbf-space-l);
	margin-left: auto;
}

.kbf-nav {
	display: flex;
	align-items: center;
	gap: var(--kbf-space-l);
}

.kbf-nav ul {
	display: flex;
	align-items: center;
	gap: var(--kbf-space-l);
	margin: 0;
	padding: 0;
	list-style: none;
}

.kbf-nav a {
	font-weight: 600;
	font-size: 16px;
	line-height: 1.4;
	color: var(--kbf-text-primary);
	white-space: nowrap;
}

/* Cream ink for the one masthead that sits on a photograph. */
.home .kbf-nav a,
.home .kbf-nav-toggle {
	color: var(--kbf-text-secondary);
}

.kbf-nav a:hover {
	opacity: 0.75;
}

/*
 * The route the visitor is already on, in barn red — the "selected" state of
 * the navigation item in the design system, which every page frame carrying a
 * navigable route shows. Matched on aria-current so the highlight and the thing
 * a screen reader announces cannot drift apart; kbf_primary_nav_fallback() sets
 * it, and the filter below adds it to whatever menu an admin assigns.
 */
.kbf-nav a[aria-current="page"] {
	color: var(--kbf-barn-red);
}

/* On the one masthead that sits on a photograph, barn red disappears into the
   hero — that page keeps the cream ink and marks the current item by weight. */
.home .kbf-nav a[aria-current="page"] {
	color: var(--kbf-text-secondary);
	font-weight: 700;
}

/*
 * kbf_order_cta() renders the masthead control as `ghost`, which is a cream
 * outline — right over the hero, invisible on cream. On the cream ground the
 * design calls for the solid barn-red button, so the variant is restyled here
 * for the masthead rather than branching on the page inside kbf_order_cta().
 * The home page keeps the outline it was designed for.
 */
.kbf-header .kbf-btn--ghost {
	background: var(--kbf-barn-red);
	border-color: var(--kbf-barn-red);
	color: var(--kbf-text-secondary);
}

.kbf-header .kbf-btn--ghost:hover {
	background: var(--kbf-barn-red-dark);
	border-color: var(--kbf-barn-red-dark);
	color: var(--kbf-text-secondary);
}

.home .kbf-header .kbf-btn--ghost {
	background: transparent;
	border-color: var(--kbf-text-secondary);
	color: var(--kbf-text-secondary);
}

.home .kbf-header .kbf-btn--ghost:hover {
	background: var(--kbf-text-secondary);
	border-color: var(--kbf-text-secondary);
	color: var(--kbf-text-primary);
}

.kbf-header__actions {
	display: flex;
	align-items: center;
	gap: var(--kbf-space-l);
}

/* Signed-in visitors only. Set in the nav's type so it reads as a quiet
   secondary action beside the order CTA rather than competing with it. */
.kbf-header__logout {
	font-weight: 600;
	font-size: 16px;
	line-height: 1.4;
	color: var(--kbf-text-primary);
	white-space: nowrap;
}

.home .kbf-header__logout {
	color: var(--kbf-text-secondary);
}

.kbf-header__logout:hover {
	opacity: 0.75;
}

/* Menu toggle (checkbox hack) only shows on small screens */
.kbf-nav-checkbox {
	position: absolute;
	opacity: 0;
	pointer-events: none;
}

.kbf-nav-toggle {
	display: none;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	color: var(--kbf-text-primary);
	cursor: pointer;
}

.kbf-nav-toggle svg {
	width: 26px;
	height: 26px;
}

/*
 * Text for assistive technology only. WordPress ships this class for admin
 * screens, not for a front end, so a theme that uses it has to define it —
 * page-menu.php, menu/group.php and the Payment step's "opens in a new tab"
 * all rely on it. Clipped rather than `display: none`, which would take it out
 * of the accessibility tree along with the layout.
 */
.screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	border: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* ---------- Main / sections ---------- */

.kbf-main {
	overflow: clip;
}

.kbf-section {
	padding-block: clamp(56px, 8vw, 96px);
}

.kbf-container {
	width: 100%;
	max-width: var(--kbf-max);
	margin-inline: auto;
	padding-inline: var(--kbf-gutter);
}

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

.kbf-hero {
	position: relative;
	/* Figma hero photo is 1440 x 909: height = 909/1440 = 63.1% of the width, so
	   it stays proportional to the frame. Driving it from vw (not aspect-ratio)
	   avoids aspect-ratio inflating the WIDTH when the floor applies on phones. */
	min-height: max(500px, 63.1vw);
	display: flex;
	align-items: flex-start;
	justify-content: center;
	text-align: center;
	color: var(--kbf-text-secondary);
	background: var(--kbf-surface-tertiary);
	overflow: hidden;
}

/*
 * The visible hero photo is the node "KnausBerryFarm-HERO 2": a 1545 x 1156
 * rectangle at (-105, -247) in the 1440 x 909 frame, whose image fill is itself
 * scaled/positioned inside that box (a transform the MCP does not expose in the
 * node coordinates). Reproducing only the box left the barn too high, so the
 * crop is calibrated against the frame: zoomed to place the roofline ~57% down
 * with clear sky above for the wordmark, matching "Homepage - CLIENT APPROVED".
 */
/*
 * 1:1 with the Figma node stack. "KnausBerryFarm-HERO 2" renders its source at
 * 1957 x 1305 and positions it at (-105, -247) in the 1440 x 909 frame — so the
 * image box is those exact ratios of the hero. This reproduces the frame's crop
 * pixel-for-pixel (no object-position / transform guessing).
 */
.kbf-hero__bg {
	position: absolute;
	left: -7.292%;    /* -105  / 1440 */
	top: -27.173%;    /* -247  /  909 */
	width: 135.903%;  /*  1957 / 1440 */
	height: 143.564%; /*  1305 /  909 */
	max-width: none;  /* override the global img{max-width:100%} that clamps it */
	object-fit: cover;
}

/*
 * The Figma "shadow" node: a 1440 x 981 overlay at the top with a blue-grey
 * tint that fades out by ~25% down. This is the exact fill — earlier a warm
 * top+bottom scrim threw the hero's colour off.
 */
.kbf-hero__scrim {
	position: absolute;
	left: 0;
	top: 0;
	width: 100%;
	height: 107.92%; /* 981 / 909 */
	background: linear-gradient(181.38deg, rgba(88, 126, 153, 0.6) 10.39%, rgba(54, 17, 18, 0) 24.682%);
}

.kbf-hero__inner {
	position: relative;
	z-index: 2;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--kbf-space-l);
	width: 100%;
	/* Figma: content container starts at x=106 (width 1228, centred) and y=248
	   in the 1440-wide frame. 248/1440 = 17.22% keeps the eyebrow at that line. */
	max-width: 1228px;
	padding: clamp(110px, 17.22%, 248px) var(--kbf-gutter) 90px;
}

.kbf-hero__eyebrow {
	/* Figma: Josefin Sans 26px, tracking 2.6px. */
	font-size: clamp(15px, 1.8vw, 26px);
	letter-spacing: 0.1em;
	color: var(--kbf-surface-primary);
}

.kbf-hero__logo {
	/* Figma: wordmark 994px wide in a 1440 frame = 69%. */
	width: min(994px, 69vw);
	height: auto;
}

/* ---------- Tradition ---------- */

.kbf-tradition {
	position: relative;
	text-align: center;
	background: var(--kbf-surface-primary);
	/* flow-root establishes a block formatting context so the badge's negative
	   margin overflows UP into the hero (straddling the edge) without collapsing
	   through and dragging the whole cream section over the photo. */
	display: flow-root;
	padding-block: 0 clamp(56px, 8vw, 96px);
}

/* Figma: badge is 131x132, centred, top y=850 in a 997-tall hero — its centre
   sits on the hero photo's bottom edge. Recreated by pulling it up half its
   height so the top half overlaps the photo and the bottom half the cream. */
.kbf-badge {
	position: relative;
	z-index: 5;
	display: block;
	width: 131px;
	height: 132px;
	margin: -66px auto clamp(24px, 3.5vw, 40px);
	/* The exported PNG carries opaque corners around the seal; clip the box to a
	   circle so it reads as the round stamp it is in Figma (the seal fills the
	   131x132 box, so a 50% radius trims only the corners). */
	border-radius: 50%;
}

.kbf-tradition__title {
	color: var(--kbf-text-primary);
}

.kbf-tradition__title span {
	display: block;
}

.kbf-tradition__title .kbf-tradition__accent {
	color: var(--kbf-barn-red);
}

/* ---------- Product band (Cinnamon Rolls) ---------- */

.kbf-product {
	position: relative;
	/* Figma: product band (image container) is 1440 x 933 -> 933/1440 = 64.8vw. */
	min-height: clamp(560px, 64.8vw, 933px);
	display: flex;
	align-items: center;
	color: var(--kbf-text-secondary);
	background: var(--kbf-surface-tertiary);
	overflow: hidden;
}

.kbf-product__bg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
}

.kbf-product__scrim {
	position: absolute;
	inset: 0;
	background: linear-gradient(90deg, rgba(30, 28, 24, 0.85) 0%, rgba(30, 28, 24, 0.55) 45%, rgba(30, 28, 24, 0.05) 100%);
}

.kbf-product__inner {
	position: relative;
	z-index: 2;
	width: 100%;
	max-width: var(--kbf-max);
	margin-inline: auto;
	padding-inline: var(--kbf-gutter);
}

.kbf-product__content {
	max-width: 520px;
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-l);
	align-items: flex-start;
}

.kbf-product__title {
	color: var(--kbf-text-secondary);
}

.kbf-product__text {
	font-size: clamp(17px, 2vw, 20px);
	font-weight: 500;
	margin: 0;
	max-width: 489px;
}

/* ---------- Gallery (polaroid collage) ---------- */

.kbf-gallery {
	background: var(--kbf-surface-primary);
	/* Figma: the photos band is 1440 x 584 full-bleed with the polaroids
	   bleeding to the edges — no side gutter, only breathing room above/below. */
	padding-block: clamp(24px, 4vw, 48px);
	overflow: hidden;
}

.kbf-gallery img {
	display: block;
	width: 100%;
	height: auto;
}

/* ---------- Testimonials ---------- */

.kbf-testimonials {
	position: relative;
	background: var(--kbf-surface-primary);
	padding-block: clamp(40px, 6vw, 72px);
}

/* Desktop scroll arrows, grouped at the top-right above the cards. Hidden in
   markup and on mobile (touch only); revealed by carousel.js on wide screens.
   The [hidden] rule must beat the base display. */
.kbf-testimonials__nav {
	display: flex;
	justify-content: flex-end;
	gap: var(--kbf-space-xxs);
	width: 100%;
	max-width: var(--kbf-max);
	margin: 0 auto var(--kbf-space-s);
	padding-inline: var(--kbf-gutter);
}

.kbf-carousel-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	padding: 0;
	border: 1px solid var(--kbf-border-dark);
	border-radius: 50%;
	background: #fff;
	color: var(--kbf-barn-red);
	cursor: pointer;
	transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease, opacity 0.18s ease;
}

.kbf-carousel-btn[hidden] {
	display: none;
}

.kbf-carousel-btn:hover {
	background: var(--kbf-barn-red);
	border-color: var(--kbf-barn-red);
	color: #fff;
}

.kbf-carousel-btn:disabled {
	opacity: 0.35;
	cursor: default;
}

.kbf-carousel-btn svg {
	width: 18px;
	height: 18px;
}

@media (max-width: 860px) {
	/* Mobile is touch-only — no arrows. */
	.kbf-testimonials__nav {
		display: none;
	}
}

.kbf-testimonials__track {
	display: flex;
	gap: var(--kbf-space-l);
	padding-inline: var(--kbf-gutter);
	overflow-x: auto;
	/* proximity, not mandatory: mandatory snap cancels the arrows' smooth
	   programmatic scroll, while proximity still snaps touch swipes. */
	scroll-snap-type: x proximity;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
}

.kbf-testimonials__track::-webkit-scrollbar {
	display: none;
}

.kbf-review {
	scroll-snap-align: center;
	flex: 0 0 clamp(280px, 80vw, 464px);
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-l);
	background: #fff;
	border: 1px solid var(--kbf-hairline);
	border-radius: 2px;
	padding: var(--kbf-space-s);
	min-height: 360px;
}

.kbf-review__mark {
	width: 44px;
	height: 32px;
}

.kbf-review__text {
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: 17px;
	line-height: 1.6;
	color: var(--kbf-chalkboard);
	margin: 0;
	flex: 1 1 auto;
}

.kbf-review__person {
	display: flex;
	align-items: center;
	gap: 20px;
}

.kbf-review__person > span {
	display: flex;
	flex-direction: column;
}

.kbf-review__avatar {
	width: 56px;
	height: 56px;
	border-radius: 50%;
	object-fit: cover;
	flex-shrink: 0;
}

.kbf-review__name {
	display: block;
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: 16px;
	line-height: 1;
	margin: 0 0 var(--kbf-space-xxs);
	color: var(--kbf-chalkboard);
}

.kbf-review__role {
	display: block;
	font-size: 14px;
	font-weight: 500;
	line-height: 1.4;
	margin: 0;
	color: var(--kbf-chalkboard);
}

/* ---------- Visit (address / hours over strawberries) ---------- */

.kbf-visit {
	position: relative;
	display: flex;
	align-items: center;
	min-height: clamp(520px, 48vw, 685px);
	padding-block: clamp(48px, 7vw, 80px);
	overflow: hidden;
}

.kbf-visit__bg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
}

.kbf-visit__inner {
	position: relative;
	z-index: 2;
	width: 100%;
	max-width: var(--kbf-max);
	margin-inline: auto;
	padding-inline: var(--kbf-gutter);
}

.kbf-card {
	position: relative;
	background: #fff;
	width: min(541px, 100%);
	padding: 40px 30px 30px;
	display: flex;
	flex-wrap: wrap;
	gap: var(--kbf-space-l);
	box-shadow: 0 20px 50px rgba(30, 28, 24, 0.18);
}

/* Figma "Rectangle 13": a 0.75px barn-red frame inset 10px inside the card. */
.kbf-card::before {
	content: "";
	position: absolute;
	inset: 10px;
	border: 0.75px solid var(--kbf-barn-red);
	pointer-events: none;
}

.kbf-card__col {
	flex: 1 1 200px;
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-l);
}

.kbf-card__block h3 {
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: 24px;
	line-height: 1;
	color: var(--kbf-barn-red);
	margin: 0 0 12px;
}

.kbf-card__block hr {
	border: 0;
	/* Figma Line 1/2/3: 1px barn-red rules under each heading (not grey). */
	border-top: 1px solid var(--kbf-barn-red);
	margin: 0 0 12px;
}

.kbf-card__block p {
	margin: 0;
	font-weight: 500;
	line-height: 1.6;
	color: #000;
}

.kbf-card__block p + p {
	margin-top: var(--kbf-space-xs);
}

.kbf-card__cta {
	margin-top: auto;
	align-self: flex-start;
}

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

.kbf-footer {
	background: var(--kbf-surface-secondary);
	color: var(--kbf-chalkboard-80);
	padding: var(--kbf-space-s) var(--kbf-gutter) var(--kbf-space-l);
}

.kbf-footer__inner {
	max-width: var(--kbf-max);
	margin-inline: auto;
}

.kbf-footer__top {
	display: flex;
	flex-wrap: wrap;
	gap: var(--kbf-space-xl);
	justify-content: space-between;
	padding-block: var(--kbf-space-l);
}

.kbf-footer__logo {
	width: 296px;
	max-width: 70vw;
	margin-bottom: var(--kbf-space-s);
}

.kbf-footer__address {
	font-weight: 700;
	font-size: 16px;
	line-height: 1.4;
	color: var(--kbf-chalkboard-80);
	margin: 0;
}

.kbf-footer__nav ul {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: var(--kbf-space-s);
}

.kbf-footer__nav a {
	font-weight: 600;
	font-size: 16px;
	color: var(--kbf-chalkboard);
}

.kbf-footer__nav a:hover {
	color: var(--kbf-barn-red);
}

.kbf-footer__divider {
	border: 0;
	border-top: 1px solid var(--kbf-border-dark);
	margin: 0;
}

.kbf-footer__bottom {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--kbf-space-s);
	padding-top: var(--kbf-space-s);
}

.kbf-social {
	display: flex;
	align-items: center;
	gap: var(--kbf-space-s);
}

.kbf-social a {
	display: inline-flex;
	color: var(--kbf-chalkboard);
}

.kbf-social a:hover {
	color: var(--kbf-barn-red);
}

.kbf-social svg {
	width: 24px;
	height: 24px;
}

.kbf-footer__copy {
	font-size: 14px;
	font-weight: 400;
	margin: 0;
	color: var(--kbf-chalkboard-80);
}

/* ---------- Responsive: mobile section layout ---------- */

@media (max-width: 860px) {
	/* Nudge the Cinnamon Rolls copy + CTA just below centre — off the bright
	   rolls at the top for legibility, without dropping to the bottom of the
	   band. Kept as a visual shift so the layout box stays centred. */
	.kbf-product__inner {
		transform: translateY(clamp(28px, 11vw, 64px));
	}
}

/* ---------- Responsive: mobile image framing ---------- */

@media (max-width: 860px) {
	/* The hero crop is calibrated to the 1440x909 desktop frame; on a portrait
	   phone those exact offsets push the barn off-centre. Recreate the desktop
	   framing instead: centre the barn horizontally and zoom in a touch so it
	   sits prominent and grounded (less empty sky/gravel), like on desktop. */
	.kbf-hero__bg {
		left: 0;
		top: 0;
		width: 100%;
		height: 100%;
		object-position: 33% center;
		transform: scale(1.18);
		transform-origin: 33% 82%;
	}

	/* The collage is a wide strip: at full width it collapses to a thin band with
	   tiny photos. Give it real height and let it swipe sideways instead — with
	   the scrollbar hidden so it doesn't leave an ugly bar. */
	.kbf-gallery {
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
	}

	.kbf-gallery::-webkit-scrollbar {
		display: none;
	}

	.kbf-gallery img {
		width: auto;
		height: clamp(220px, 62vw, 300px);
		max-width: none;
	}
}

/* ---------- Responsive: mobile nav ---------- */

@media (max-width: 860px) {
	/* Row 1 is just the logo + hamburger; the nav and the order CTA drop into a
	   collapsible panel revealed by the toggle. */
	.kbf-header {
		flex-wrap: wrap;
		align-items: center;
		column-gap: var(--kbf-space-s);
		row-gap: 0;
	}

	.kbf-header__logo {
		order: 1;
	}

	.kbf-nav-toggle {
		display: inline-flex;
		order: 2;
		margin-left: auto;
	}

	/* The nav and the CTA drop below the logo row as one panel. */
	.kbf-header__panel {
		display: block;
		flex-basis: 100%;
		order: 5;
		margin-left: 0;
	}

	.kbf-nav ul {
		display: none;
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		width: 100%;
		margin: 0;
		padding: var(--kbf-space-s) 0 0;
	}

	.kbf-nav a {
		display: block;
		padding: 14px 0;
		font-size: 18px;
		border-top: 1px solid rgba(251, 248, 243, 0.14);
	}

	.kbf-header__actions {
		display: none;
		padding-top: var(--kbf-space-s);
	}

	/* A full-height tap target, matching the nav rows above it. */
	.kbf-header__logout {
		padding: 14px 0;
		font-size: 18px;
	}

	/* When the menu is open, drop a solid panel behind the whole header so the
	   items never sit on top of the hero photo (and it works over any page). */
	.kbf-header:has(.kbf-nav-checkbox:checked) {
		background: var(--kbf-barn-red);
		padding-bottom: var(--kbf-space-l);
	}

	/* The label toggles the sibling checkbox, which reveals the nav and CTA.
	   No JavaScript on the marketing pages. */
	.kbf-nav-checkbox:checked ~ .kbf-header__panel .kbf-nav ul,
	.kbf-nav-checkbox:checked ~ .kbf-header__panel .kbf-header__actions {
		display: flex;
	}

	/*
	 * The home page's masthead is already out of the flow (it overlays the
	 * hero), so opening the menu there expands it over the page. Every other
	 * masthead sits in the flow, and left alone it would push the whole page
	 * down as it opens. The header becomes the positioning context and the
	 * panel floats out of the flow beneath it, so the bar keeps its closed
	 * height and the menu overlays the content instead of displacing it.
	 */
	.kbf-header {
		position: relative;
	}

	.home .kbf-header {
		position: absolute;
	}

	.kbf-nav-checkbox:checked ~ .kbf-header__panel {
		position: absolute;
		top: 100%;
		left: 0;
		right: 0;
		background: var(--kbf-barn-red);
		padding: 0 var(--kbf-gutter) var(--kbf-space-l);
	}

	/* The bar itself already turns barn red on open; with the panel floating,
	   the extra bottom padding that made room for it in the flow is not
	   wanted. */
	.kbf-header:has(.kbf-nav-checkbox:checked) {
		padding-bottom: clamp(20px, 3vw, 32px);
	}

	/* Open, the bar is barn red behind the whole header, so the cream-ground
	   colours have to go back to their inverted values. Harmless on the home
	   page, which is already cream-inked. */
	.kbf-header:has(.kbf-nav-checkbox:checked) .kbf-nav a,
	.kbf-header:has(.kbf-nav-checkbox:checked) .kbf-header__logout,
	.kbf-header:has(.kbf-nav-checkbox:checked) .kbf-nav-toggle {
		color: var(--kbf-text-secondary);
	}

	.kbf-header:has(.kbf-nav-checkbox:checked) .kbf-btn--ghost {
		background: transparent;
		border-color: var(--kbf-text-secondary);
		color: var(--kbf-text-secondary);
	}

	/*
	 * Every page but home serves the dark ink cut of the wordmark (see
	 * kbf_wordmark_src()), which disappears once that bar turns barn red. The
	 * src is chosen server-side, so the open state swaps the rendered image to
	 * the cream cut rather than shipping both. The home page already has the
	 * cream cut and needs no swap.
	 */
	.kbf-header:has(.kbf-nav-checkbox:checked) .kbf-header__logo img {
		content: url("img/logo.svg");
	}

	.home .kbf-header:has(.kbf-nav-checkbox:checked) .kbf-header__logo img {
		content: none;
	}
}

/* ==========================================================================
   Menu page
   ==========================================================================
   Design tokens are the Figma variables for the two menu frames — 98:393
   (desktop, 1440) and 98:822 (mobile, 402). Everything below references the
   custom properties at the top of this file; nothing re-types a value.

   Mobile first, like the preorder wizard and unlike the older page blocks
   above: both frames exist, the phone one is the narrower constraint, and most
   of this site's traffic is a phone. So the base rules ARE the 402 frame, and
   the min-width blocks widen it back out to 1440. Nothing here is a max-width
   override undoing a desktop rule.

   The board is one column until it is wide enough for all three at their
   design width (3 x 402 + 2 x 37 gutters = 1280), which is the only width the
   desktop frame is drawn at. In between it would either squeeze the columns
   below 402 or leave the third one orphaned, so it stays stacked.
   ========================================================================== */

/* ---------- Title + download ---------- */

.kbf-menu-head {
	background: var(--kbf-surface-primary);
	padding-block: clamp(32px, 4.4vw, 64px) clamp(48px, 6.7vw, 96px);
}

.kbf-menu-head__inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: clamp(24px, 2.2vw, 32px);
	text-align: center;
}

.kbf-menu-head__title {
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: clamp(32px, 4.45vw, 64px);
	line-height: 1.2;
	margin: 0;
}

/*
 * A step down from .kbf-btn on a phone: the mobile frame draws this one at 14px
 * regular in a 36px pill, where every other action on the site stays at 18px.
 * The asymmetric padding is .kbf-btn's own cap-centring trick, rescaled.
 */
.kbf-menu-head__download {
	font-size: 14px;
	font-weight: 400;
	padding: 11px 24px 9px;
}

.kbf-menu-head__download img {
	width: 12px;
	height: 12px;
}

/* ---------- Filter + quick links ---------- */

.kbf-menu-filter {
	background: var(--kbf-surface-secondary);
	padding-block: var(--kbf-space-s);
}

.kbf-menu-filter__inner {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-s);
}

.kbf-menu-filter__bar {
	display: grid;
	grid-template-areas:
		"label toggle"
		"options options";
	grid-template-columns: 1fr auto;
	align-items: center;
	gap: var(--kbf-space-xs);
	padding-bottom: var(--kbf-space-s);
	border-bottom: 1px solid var(--kbf-border-tertiary);
}

.kbf-menu-filter__label {
	grid-area: label;
	margin: 0;
	font-family: var(--kbf-font-body);
	font-weight: 700;
	font-size: 14px;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--kbf-barn-red);
}

.kbf-menu-filter__options {
	grid-area: options;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--kbf-space-l);
	margin: 0;
	padding: 0;
	border: 0;
}

.kbf-menu-filter__option {
	display: inline-flex;
	align-items: center;
	gap: var(--kbf-space-xxs);
	font-size: 16px;
	line-height: 1.56;
	color: var(--kbf-chalkboard);
	cursor: pointer;
}

/*
 * The design's radio, drawn rather than shipped as two SVGs: a real input keeps
 * the keyboard and the grouping the browser already gives us, and the ring has
 * to take the focus outline anyway.
 *
 * It is nudged up because the ring moves to meet the letters, not the other way
 * round. align-items: center above centres the two *boxes*, and their boxes do
 * share a centre line to the pixel — but Josefin Sans is lopsided: at 16px its
 * ascent is 12 and its descent 4, so the line box reserves far more room under
 * the baseline than over the caps. Centring that box centres the empty
 * descender along with the text, leaving the visible glyphs about 2.2px above
 * the middle of the ring.
 *
 * The correction belongs here rather than on the text, because the text's
 * baseline is not free to move: "Filter by:" sits beside these options and the
 * two share a baseline exactly. Trimming the text box instead — text-box:
 * trim-both cap alphabetic, which is what the Figma text node does — squares
 * the ring against its own label but drops that shared baseline by 1.6px,
 * trading one misalignment for a more visible one.
 *
 * The offset is half the font's own asymmetry, (descent - (ascent - cap)) / 2,
 * which is 2.195px at the 16px this label is set in. Line-height cancels out of
 * that arithmetic, so leading can change freely above without disturbing it.
 *
 * In px, to match the ring's own fixed 18px — and because em would be wrong
 * here anyway: an input does not inherit font-size, so an em on this element
 * resolves against the browser's own ~13.3px form-control size rather than the
 * 16px the text is set in. It is tied to this typeface and this size; revisit
 * it if either changes.
 */
.kbf-menu-filter__option input {
	appearance: none;
	position: relative;
	top: -2.195px;
	margin: 0;
	width: 18px;
	height: 18px;
	border: 1px solid var(--kbf-barn-red);
	border-radius: var(--kbf-radius-full);
	background: transparent;
	cursor: pointer;
}

.kbf-menu-filter__option input:checked {
	border-width: 5px;
}

.kbf-menu-filter__option input:focus-visible {
	outline: 2px solid var(--kbf-barn-red);
	outline-offset: 2px;
}

.kbf-menu-filter__toggle {
	grid-area: toggle;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	padding: 0;
	border: 0;
	background: transparent;
	cursor: pointer;
}

/* Open is the chevron pointing up; closed flips it back down, as annotated. */
.kbf-menu-filter__toggle img {
	width: 24px;
	height: 24px;
	transform: rotate(180deg);
	transition: transform 0.18s ease;
}

.kbf-menu-filter__toggle[aria-expanded="false"] img {
	transform: none;
}

.kbf-menu-quicklinks {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 110px));
	justify-content: start;
	gap: var(--kbf-space-xs) 12px;
}

.kbf-menu-quicklinks.is-collapsed {
	display: none;
}

.kbf-menu-quicklink {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--kbf-space-xxs);
}

.kbf-menu-quicklink__image {
	width: 100%;
	aspect-ratio: 1;
	object-fit: cover;
	border-radius: var(--kbf-radius-minimal);
}

.kbf-menu-quicklink__label {
	font-family: var(--kbf-font-body);
	font-weight: 700;
	font-size: 10px;
	line-height: 1.2;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	text-align: center;
	color: var(--kbf-barn-red);
}

.kbf-menu-quicklink:hover .kbf-menu-quicklink__label {
	color: var(--kbf-barn-red-dark);
}

/* ---------- The board ---------- */

.kbf-menu-board {
	padding-block: clamp(40px, 4.4vw, 64px);
}

.kbf-menu-board__grid {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: var(--kbf-space-m);
}

.kbf-menu-board__heading {
	display: flex;
	align-items: center;
	gap: var(--kbf-space-xs);
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: clamp(32px, 3.34vw, 48px);
	line-height: 1.2;
	margin: 0;
}

.kbf-menu-board__heading img {
	width: clamp(32px, 3.34vw, 48px);
	height: clamp(32px, 3.34vw, 48px);
}

/*
 * The three columns carry different gaps in the frame — 40, 60 and 28 — which
 * is the designer levelling the bottoms of a masonry layout by hand. They are
 * reproduced rather than averaged, but only once the board is actually three
 * columns; stacked, one rhythm reads better than three.
 */
.kbf-menu-column {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-l);
}

/* ---------- One card ---------- */

.kbf-menu-group {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-m);
	/* The anchor target, so a quick link lands on the photograph and not
	   underneath the sticky masthead. */
	position: relative;
}

.kbf-menu-anchor {
	position: absolute;
	top: -120px;
	display: block;
	visibility: hidden;
}

/*
 * On a phone the card is the screen: the photograph runs edge to edge, out
 * through the page gutter, and the lines under it sit at the gutter itself —
 * which is the 23px the mobile frame insets them by. The card only becomes a
 * 402px object with its own rounded edge and its own inset once the board is
 * three columns.
 */
.kbf-menu-group__image {
	/* Wider than its container on purpose, so past the base img { max-width }. */
	max-width: none;
	width: calc(100% + 2 * var(--kbf-gutter));
	margin-inline: calc(-1 * var(--kbf-gutter));
	aspect-ratio: 402 / 228;
	object-fit: cover;
}

.kbf-menu-group__body {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-s);
}

/*
 * The rule around the Cinnamon Rolls title and its priced block — the one card
 * on the board whose product can be ordered in advance, set apart so the
 * "Available for preorder" note reads as applying to a thing rather than
 * floating beside a heading.
 *
 * Barn red, the colour that note is set in.
 *
 * An outline, not a border. A border with padding would be the obvious way to
 * draw this and it is the wrong one: padding pushes the title and the prices
 * inwards, so the text stops lining up with Pecan Rolls below it and with every
 * other card's title across the board. The point is to draw a rule around type
 * that stays exactly where it was. An outline takes no space in the layout and
 * outline-offset pushes the line outwards instead, which is the same drawing
 * without any of the movement.
 *
 * The offset is space-xxs so the line sits nearer what it encloses than what it
 * excludes: 8px off the title and the prices inside, and 16px clear of Pecan
 * Rolls below, which is a different product that happens to share this card. At
 * space-xs it was the other way round and the rule read as though the Pecan
 * Rolls line belonged inside it. It clears the photograph above either way.
 *
 * The gap below re-creates the body's own column gap, because the frame takes
 * the title and the block out of that flow and into its own — without it the
 * two would close up and the text would move after all.
 */
.kbf-menu-group__frame {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-s);
	outline: 1px solid var(--kbf-barn-red);
	outline-offset: var(--kbf-space-xxs);
	border-radius: var(--kbf-radius-minimal);
}

.kbf-menu-group__title {
	font-family: var(--kbf-font-body);
	font-weight: 700;
	font-size: 16px;
	line-height: 1.2;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--kbf-barn-red);
	margin: 0;
	padding-bottom: var(--kbf-space-xxs);
	border-bottom: 1px solid var(--kbf-border-tertiary);
}

.kbf-menu-item {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-xs);
}

/* The rule the design draws between two priced blocks in the same card. */
.kbf-menu-item + .kbf-menu-item {
	padding-top: var(--kbf-space-s);
	border-top: 1px solid var(--kbf-border-tertiary);
}

.kbf-menu-item__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--kbf-space-xs);
}

.kbf-menu-item__title {
	font-family: var(--kbf-font-body);
	font-weight: 700;
	font-size: 14px;
	line-height: 1.2;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--kbf-barn-red);
	margin: 0;
}

.kbf-menu-item__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-xxs);
}

.kbf-menu-line {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--kbf-space-xs);
	font-size: 16px;
	line-height: 1.4;
	color: var(--kbf-text-primary);
}

.kbf-menu-line.is-small {
	font-size: 14px;
}

.kbf-menu-line__price {
	text-align: right;
	white-space: nowrap;
}

/* A condition attached to something on the board rather than a price:
   "Special Order" beside Shoofly, and "Available for preorder" beside the
   Cinnamon Rolls card's title. One rule, so the two cannot drift apart —
   they are the same annotation in two places, and the second was asked for
   as "the format Special Order has". */
.kbf-menu-line__note,
.kbf-menu-group__note {
	margin-left: var(--kbf-space-xxs);
	font-weight: 700;
	font-size: 10px;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--kbf-barn-red);
	white-space: nowrap;
}

/* The group title is 16px and this sits inside it, so it needs its own
   line-height to keep the note's smaller box from stretching the heading's
   line and pushing the divider rule under it down. */
.kbf-menu-group__note {
	line-height: 1;
}

.kbf-menu-item__empty {
	margin: 0;
	font-size: 16px;
	line-height: 1.4;
	color: var(--kbf-chalkboard-65);
}

/*
 * Two price columns. A real table because the price only means anything read
 * against both its row and its column — "$7.10" is the price of a dozen herb
 * rolls, not of herb rolls.
 */
.kbf-menu-item__table {
	width: 100%;
	border-collapse: collapse;
	font-size: 16px;
	line-height: 1.4;
	color: var(--kbf-text-primary);
}

.kbf-menu-item__table th,
.kbf-menu-item__table td {
	padding: 0 0 var(--kbf-space-xxs);
	text-align: right;
	font-weight: 400;
	white-space: nowrap;
}

.kbf-menu-item__table tr:last-child th,
.kbf-menu-item__table tr:last-child td {
	padding-bottom: 0;
}

.kbf-menu-item__table th[scope="row"] {
	text-align: left;
	width: 100%;
	white-space: normal;
}

/*
 * The column headers wrap where the frame wraps them — "Half / Dozen" over two
 * lines — because the row names take the width and the price columns are only
 * as wide as a price.
 */
.kbf-menu-item__table thead th {
	font-weight: 600;
	padding-bottom: var(--kbf-space-xxs);
	white-space: normal;
}

.kbf-menu-item__table td,
.kbf-menu-item__table thead th + th {
	padding-left: var(--kbf-space-xs);
}

.kbf-menu-item__table .is-small th,
.kbf-menu-item__table .is-small td {
	font-size: 14px;
}

/* ---------- Preorder badge ---------- */

/*
 * The pill beside Cinnamon Rolls.
 *
 * The padding is asymmetric on purpose and the horizontal padding is uneven by
 * a fraction of an em, both to centre what the reader actually sees. Josefin
 * Sans reserves descender room this text never uses — it is all capitals — so
 * centring the line box leaves the caps sitting about 3px high in a 23px pill;
 * and letter-spacing is applied after the final letter too, which pushes the
 * optical centre half a space to the left. The numbers below cancel both, and
 * are what the design does by hand in the frame.
 */
.kbf-preorder-badge {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 7px calc(8px - 0.05em) 4px calc(8px + 0.05em);
	border-radius: var(--kbf-radius-full);
	background: var(--kbf-barn-red);
	color: var(--kbf-text-secondary);
	font-family: var(--kbf-font-body);
	font-weight: 700;
	font-size: 10px;
	line-height: 1.2;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	white-space: nowrap;
}

.kbf-preorder-badge:hover {
	background: var(--kbf-barn-red-dark);
}

/* ---------- Filtering ---------- */

/*
 * "Bakery" and "Shakestand" narrow the board and the quick links to one section
 * of the farm. Without JavaScript nothing sets data-menu-filter to anything but
 * "all", so the whole board renders and the radios are simply an inert form.
 */
.kbf-menu[data-menu-filter="bakery"] [data-menu-section="shakestand"],
.kbf-menu[data-menu-filter="shakestand"] [data-menu-section="bakery"] {
	display: none;
}

@media (min-width: 768px) {
	/* The desktop frame puts the filter and the quick links on one centred
	   line; the collapse is a phone affordance and has nothing to control. */
	.kbf-menu-filter {
		padding-block: var(--kbf-space-m);
	}

	.kbf-menu-filter__inner {
		align-items: center;
		gap: var(--kbf-space-m);
	}

	.kbf-menu-filter__bar {
		display: flex;
		justify-content: center;
		gap: var(--kbf-space-m);
		padding-bottom: 0;
		border-bottom: 0;
	}

	.kbf-menu-filter__label {
		font-size: 16px;
	}

	.kbf-menu-filter__toggle {
		display: none;
	}

	.kbf-menu-quicklinks {
		grid-template-columns: repeat(7, 110px);
		justify-content: center;
		gap: var(--kbf-space-xl);
	}

	/* The toggle is gone, so the strip cannot stay collapsed behind it. */
	.kbf-menu-quicklinks.is-collapsed {
		display: grid;
	}

	.kbf-menu-head__download {
		font-size: 18px;
		font-weight: 700;
		padding: 17px 24px 15px;
	}

	.kbf-menu-head__download img {
		width: 16px;
		height: 16px;
	}

	/* The card stops being the screen and becomes an object on it. */
	.kbf-menu-group__image {
		width: 100%;
		margin-inline: 0;
		border-radius: var(--kbf-radius-minimal);
	}

	.kbf-menu-group__body {
		padding-inline: 23px;
	}
}

@media (min-width: 1100px) {
	.kbf-menu-board__grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		column-gap: 37px;
		row-gap: var(--kbf-space-m);
	}

	/*
	 * Placed rather than flowed. Bakery covers the first two columns and
	 * Shakestand the third, and both headings share the first row — auto
	 * placement cannot express that from a source order that also has to stack
	 * correctly on a phone.
	 */
	.kbf-menu-board__heading--bakery {
		grid-area: 1 / 1 / 2 / 3;
	}

	.kbf-menu-board__heading--shakestand {
		grid-area: 1 / 3 / 2 / 4;
	}

	.kbf-menu-column {
		grid-row: 2;
	}

	.kbf-menu-column--1 {
		grid-column: 1;
		gap: var(--kbf-space-l);
	}

	.kbf-menu-column--2 {
		grid-column: 2;
		gap: 60px;
	}

	.kbf-menu-column--3 {
		grid-column: 3;
		gap: 28px;
	}

	/*
	 * One section at a time: the columns that are left close the gap and centre
	 * rather than leaving the board hanging off an empty track. They keep their
	 * design width instead of stretching to fill it — a card is drawn at 402 and
	 * a 600px-wide one is a photograph blown up half as big again.
	 */
	.kbf-menu[data-menu-filter="bakery"] .kbf-menu-board__grid {
		grid-template-columns: repeat(2, minmax(0, 402px));
		justify-content: center;
	}

	.kbf-menu[data-menu-filter="shakestand"] .kbf-menu-board__grid {
		grid-template-columns: minmax(0, 402px);
		justify-content: center;
	}

	.kbf-menu[data-menu-filter="shakestand"] .kbf-menu-board__heading--shakestand,
	.kbf-menu[data-menu-filter="shakestand"] .kbf-menu-column--3 {
		grid-column: 1;
	}
}

/* ==========================================================================
   Preorder wizard
   ==========================================================================
   Design tokens are the Figma variables for the three step frames —
   315:1996 (Terms), 315:2026 (Order Details) and 315:2105 (Payment).
   Everything below references the custom properties at the top of this file;
   nothing re-types a value.

   Mobile first, deliberately: those three frames exist at 1440 only, and most
   of this site's traffic is a phone. So the base rules below ARE the phone
   layout — one column, full-width actions, the step indicator reduced to
   numbers plus the name of the step being worked on — and the two min-width
   blocks at the end widen it back out to the frames. Nothing here is a
   max-width override undoing a desktop rule.

   Two places depart from the frames on purpose, both about legibility:
   the selected pickup-time chip and the active/completed step numbers use
   full-strength cream rather than the design's text-invert (cream at 60%),
   which on barn red is about 2.4:1; and the rule above "Total due today" is a
   real hairline rather than border-tertiary, which is the same colour as the
   card it sits on and therefore invisible.
   ========================================================================== */

.kbf-preorder-head {
	text-align: center;
	background: var(--kbf-surface-primary);
	padding-bottom: var(--kbf-space-s);
}

.kbf-preorder-head__inner {
	max-width: var(--kbf-max);
	margin-inline: auto;
}

.kbf-preorder-head__title {
	font-size: clamp(32px, 5vw, 48px);
	margin-bottom: var(--kbf-space-xs);
}

.kbf-preorder-closed {
	background: var(--kbf-surface-primary);
}

.kbf-preorder-closed__inner {
	max-width: 560px;
	margin-inline: auto;
	text-align: center;
	background: #fff;
	border: 1px solid var(--kbf-hairline);
	border-radius: var(--kbf-radius-m);
	padding: var(--kbf-space-xl) var(--kbf-space-l);
}

.kbf-preorder-closed__title {
	font-family: var(--kbf-font-title);
	font-size: 26px;
	color: var(--kbf-barn-red);
	margin: 0 0 var(--kbf-space-xs);
}

.kbf-preorder-closed__text {
	margin: 0;
	line-height: 1.6;
}

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

/*
 * Flat on the cream ground. The wizard used to sit in a white card with a
 * shadow; none of the three frames has one — the page ground runs edge to edge
 * behind every step, and the only raised surfaces are the Terms of Sale card,
 * the order summary and the reminders panel.
 */
.kbf-preorder-wizard-wrap {
	background: var(--kbf-surface-primary);
	padding: var(--kbf-space-m) var(--kbf-gutter) var(--kbf-space-2xl);
}

.kbf-preorder-wizard {
	/* The 1440 frame less its two 80px gutters. */
	max-width: 1280px;
	margin-inline: auto;
}

.kbf-preorder-bulk-callout {
	margin: 0 0 var(--kbf-space-s);
	padding: var(--kbf-space-xs) var(--kbf-space-s);
	border-radius: var(--kbf-radius-s);
	background: var(--kbf-barn-red);
	color: var(--kbf-text-secondary);
	text-align: center;
	font-weight: 600;
}

/* ---------- Step indicator ---------- */

.kbf-step-indicator {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--kbf-space-xs);
	list-style: none;
	/* 24px of its own, then the frame's 8px wrap padding plus its 28px gap. */
	margin: 0 0 36px;
	padding: var(--kbf-space-s) 0;
}

.kbf-step-indicator__item {
	display: flex;
	align-items: center;
	gap: 10px;
	font-size: 14px;
	font-weight: 600;
	color: var(--kbf-text-primary);
}

.kbf-step-indicator__num {
	position: relative;
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	border: 1.5px solid var(--kbf-border-tertiary);
	border-radius: var(--kbf-radius-s);
	font-size: 14px;
	font-weight: 700;
	line-height: 1;
}

/* Not reached yet: the ring carries the shape, the digit and its name recede. */
.kbf-step-indicator__item:not(.is-active):not(.is-complete) {
	opacity: 0.45;
}

.kbf-step-indicator__item.is-active .kbf-step-indicator__num {
	background: var(--kbf-barn-red);
	border-color: var(--kbf-barn-red);
	color: var(--kbf-text-secondary);
}

.kbf-step-indicator__item.is-active .kbf-step-indicator__label {
	font-weight: 700;
}

/*
 * Done: a chalkboard disc with a tick. The digit stays in the DOM — a screen
 * reader still reads "1 Terms" — and is only hidden from view, because the
 * tick is decoration, not the step's name.
 */
.kbf-step-indicator__item.is-complete .kbf-step-indicator__num {
	background: var(--kbf-chalkboard);
	border-color: var(--kbf-chalkboard);
	color: transparent;
}

.kbf-step-indicator__item.is-complete .kbf-step-indicator__num::after {
	content: "✓";
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--kbf-text-secondary);
}

.kbf-step-indicator__connector {
	flex: 1 1 16px;
	min-width: 12px;
	max-width: 64px;
	height: 2px;
	border-radius: 1px;
	background: var(--kbf-border-tertiary);
}

/* The rail behind a step already taken is brand-coloured. */
.kbf-step-indicator__item.is-complete + .kbf-step-indicator__connector {
	background: var(--kbf-barn-red);
}

/*
 * Three step names do not fit a phone, and truncating or scrolling a progress
 * indicator is worse than shortening it: only the step being worked on is
 * named, and the others stay as the numbered circles the design already draws.
 */
.kbf-step-indicator__item:not(.is-active) .kbf-step-indicator__label {
	display: none;
}

/* ---------- Panels ---------- */

.kbf-preorder-panel--centred {
	max-width: 680px;
	margin-inline: auto;
}

.kbf-preorder-panel__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--kbf-space-xs);
	margin-top: var(--kbf-space-s);
}

.kbf-preorder-panel__actions .kbf-btn {
	/* Full width on a phone whatever the frame does at 1440. */
	width: 100%;
}

.kbf-preorder-panel__actions .kbf-btn[disabled] {
	opacity: 0.5;
	cursor: not-allowed;
}

.kbf-preorder-back {
	display: inline-block;
	margin: 0 0 var(--kbf-space-s);
	padding: 4px 0;
	background: none;
	border: 0;
	font-family: var(--kbf-font-body);
	font-size: 14px;
	font-weight: 600;
	color: var(--kbf-text-primary);
	opacity: 0.7;
	cursor: pointer;
}

.kbf-preorder-back:hover,
.kbf-preorder-back:focus-visible {
	opacity: 1;
	color: var(--kbf-barn-red);
}

/* A step's own heading, where it has one: the Account and Payment frames both
   set it at 36px Playfair over the Back control. */
.kbf-preorder-panel__title--large {
	margin: 0 0 var(--kbf-space-s);
	font-family: var(--kbf-font-title);
	font-size: clamp(30px, 6vw, 40px);
	line-height: 1.1;
}

/* ---------- Terms step ---------- */

.kbf-preorder-terms__eyebrow {
	margin: 0 0 var(--kbf-space-s);
	font-family: var(--kbf-font-body);
	font-weight: 600;
	font-size: clamp(19px, 5vw, 25px);
	line-height: 1.2;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--kbf-barn-red);
}

/*
 * The three numbered lines. Playfair, not the body face — this is the one
 * place on the page where running copy is set in the title family.
 */
.kbf-preorder-things {
	margin: 0 0 var(--kbf-space-s);
	padding-left: 1.5em;
	font-family: var(--kbf-font-title);
	font-weight: 400;
	font-size: clamp(17px, 2.2vw, 19px);
	line-height: 1.35;
	color: var(--kbf-text-primary);
}

.kbf-preorder-things li + li {
	margin-top: 10px;
}

.kbf-preorder-things strong {
	font-weight: 800;
}

.kbf-preorder-terms-card {
	background: var(--kbf-surface-secondary);
	border-radius: var(--kbf-radius-s);
	padding: var(--kbf-space-s) var(--kbf-space-xs);
	margin-bottom: var(--kbf-space-s);
}

.kbf-preorder-terms-card__title {
	margin: 0 0 20px;
	font-weight: 700;
	font-size: 14.5px;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	text-align: center;
}

.kbf-preorder-terms-card__item + .kbf-preorder-terms-card__item {
	margin-top: 20px;
}

.kbf-preorder-terms-card__item h2 {
	font-family: var(--kbf-font-body);
	font-size: 15px;
	font-weight: 600;
	line-height: 1.3;
	margin: 0 0 6px;
}

.kbf-preorder-terms-card__item p {
	margin: 0;
	font-size: 14px;
	line-height: 1.45;
	color: var(--kbf-chalkboard-75);
}

/*
 * Emphasis inside a clause. The copy carries its own <strong> (one of the two
 * tags kbf_copy_html() allows) and the design sets that half in barn red at
 * full strength, which is why the clause's tint is a colour token rather than
 * an opacity on the paragraph.
 */
.kbf-preorder-terms-card__item p strong {
	font-weight: 700;
	color: var(--kbf-barn-red);
}

.kbf-preorder-terms-link {
	display: inline-block;
	margin-top: 5px;
	color: var(--kbf-barn-red);
	font-weight: 600;
	font-size: 13.5px;
	text-decoration: underline;
}

.kbf-preorder-checkbox {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	/* WCAG 2.5.8: the whole label is the target, so keep the row tappable. */
	min-height: 24px;
	margin-bottom: var(--kbf-space-s);
	font-size: 15px;
	font-weight: 600;
	line-height: 1.5;
	cursor: pointer;
}

.kbf-preorder-checkbox input {
	flex: 0 0 auto;
	width: 20px;
	height: 20px;
	margin: 2px 0 0;
	border-radius: 5px;
	accent-color: var(--kbf-barn-red);
}

/* ---------- Account step ---------- */

/*
 * Two equal halves inside one rounded, clipped box — the same composition the
 * Order in Advance modal uses, and for the same reason: cream for the door a
 * returning customer already has a key to, barn red for the one that makes a
 * new key. Neither panel carries a radius of its own; the container's
 * overflow:hidden is what rounds the outer corners, so the seam between them
 * stays straight.
 *
 * The fields are .kbf-auth-field, shared with the sign-in and sign-up pages.
 * Two forms that create the same kind of account should not look like two
 * different forms, and the only thing that changes here is the ground they sit
 * on — handled by the --brand modifier below rather than by a second copy of
 * every field rule.
 */
.kbf-preorder-account-columns {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	overflow: hidden;
	border-radius: var(--kbf-radius-s);
}

.kbf-preorder-account-panel {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--kbf-space-m);
	/* The frame's 64 / 56; eased down so the fields keep a usable measure once
	   the two halves have to share a narrow window. */
	padding: clamp(40px, 5vw, 64px) clamp(24px, 4.5vw, 56px);
	background: var(--kbf-surface-secondary);
	color: var(--kbf-text-primary);
	text-align: center;
}

.kbf-preorder-account-panel--brand {
	background: var(--kbf-barn-red);
	color: var(--kbf-text-secondary);
}

.kbf-preorder-account-panel__head {
	display: flex;
	flex-direction: column;
	gap: 12px;
	/* The divider under the heading is the head's own bottom border rather than
	   an element of its own, so the panel's 32px gap sits below it and this
	   padding repeats it above — the frame's 166 / 199 rhythm. */
	padding-bottom: var(--kbf-space-m);
	border-bottom: 1px solid var(--kbf-border-dark);
	width: 100%;
}

/* Cream on barn red needs its own rule: --kbf-border-dark disappears into it. */
.kbf-preorder-account-panel--brand .kbf-preorder-account-panel__head {
	border-bottom-color: rgba(251, 248, 243, 0.3);
}

.kbf-preorder-account-panel__title {
	margin: 0;
	font-family: var(--kbf-font-title);
	font-size: clamp(26px, 4.5vw, 34px);
	line-height: 1.1;
	/* Colour is the panel's, not the heading's — the same markup sits on cream
	   and on barn red. */
	color: inherit;
}

/* Figma "Label Medium": 13px bold, 1.04px tracking — 0.08em at this size. */
.kbf-preorder-account-panel__eyebrow {
	margin: 0;
	font-family: var(--kbf-font-body);
	font-size: 13px;
	font-weight: 700;
	line-height: 1.2;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: inherit;
	opacity: 0.75;
}

.kbf-preorder-account-panel__form {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--kbf-space-s);
	width: 100%;
}

.kbf-preorder-account-panel__submit {
	width: 100%;
}

/*
 * A rejected attempt, above the form it was made in. Cream card on the cream
 * panel would vanish, so the tone is carried by a barn-red rule and barn-red
 * text; on the brand panel both would vanish instead, so it inverts.
 */
.kbf-preorder-account-panel__error {
	width: 100%;
	margin: 0;
	padding: var(--kbf-space-xs);
	border-radius: var(--kbf-radius-minimal);
	background: var(--kbf-surface-primary);
	border-left: 3px solid var(--kbf-barn-red);
	color: var(--kbf-barn-red);
	font-size: 14px;
	line-height: 1.5;
	text-align: left;
}

/* The same refusal, when the panel it belongs to is no longer on the page —
   registration turned off between the form being served and submitted. */
.kbf-preorder-account-notice {
	margin: 0 0 var(--kbf-space-s);
	padding: var(--kbf-space-xs);
	border-radius: var(--kbf-radius-minimal);
	background: var(--kbf-surface-secondary);
	border-left: 3px solid var(--kbf-barn-red);
	color: var(--kbf-barn-red);
	font-size: 14px;
	line-height: 1.5;
}

.kbf-preorder-account-panel__error-line {
	margin: 0;
}

.kbf-preorder-account-panel__error-line + .kbf-preorder-account-panel__error-line {
	margin-top: var(--kbf-space-xxs);
}

/* The labels and hints inside the barn-red panel are the panel's cream, not the
   chalkboard the shared field rules set. */
.kbf-preorder-account-panel--brand .kbf-auth-field__label,
.kbf-preorder-account-panel--brand .kbf-auth-field__hint {
	color: var(--kbf-text-secondary);
}

/* "Forgot password?" sits between the fields and the button, centred. */
.kbf-preorder-account-panel .kbf-auth-card__forgot {
	color: var(--kbf-text-primary);
	font-weight: 600;
	opacity: 0.7;
}

.kbf-preorder-account-panel .kbf-auth-card__forgot:hover,
.kbf-preorder-account-panel .kbf-auth-card__forgot:focus-visible {
	opacity: 1;
	color: var(--kbf-barn-red);
}

/*
 * Stacked before the two columns get cramped. Below roughly 860px each half is
 * under 220px of usable measure once the panel padding is paid, which is
 * narrower than the email address the fields have to hold.
 */
@media (max-width: 860px) {
	.kbf-preorder-account-columns {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* ---------- Order Details step ---------- */

.kbf-preorder-hero {
	display: grid;
	gap: var(--kbf-space-s);
}

/* On a phone the product photo leads, the way it does on every other
   photography-forward section of this site. */
.kbf-preorder-hero__image {
	order: -1;
}

.kbf-preorder-hero__image img {
	width: 100%;
	max-width: 560px;
	aspect-ratio: 1;
	object-fit: cover;
	border-radius: var(--kbf-radius-s);
	background: var(--kbf-surface-secondary);
}

.kbf-preorder-hero__info {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.kbf-preorder-eyebrow {
	margin: 0;
	font-size: 14px;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--kbf-barn-red);
}

.kbf-preorder-hero__title {
	margin: 0;
	font-family: var(--kbf-font-title);
	font-size: clamp(34px, 7vw, 48px);
	line-height: 1.1;
}

.kbf-preorder-hero__desc {
	margin: 0;
	font-size: clamp(16px, 2vw, 18px);
	line-height: 1.4;
	color: var(--kbf-chalkboard-75);
}

.kbf-preorder-hero__price {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--kbf-space-xxs);
	margin: 0;
}

.kbf-preorder-hero__price-figure {
	font-size: clamp(20px, 4vw, 24px);
	font-weight: 700;
}

.kbf-preorder-hero__price-note {
	font-size: 14px;
	font-weight: 400;
	color: var(--kbf-chalkboard-60);
}

.kbf-preorder-selection {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-m);
	padding-top: var(--kbf-space-s);
}

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

.kbf-preorder-field {
	margin: 0;
}

.kbf-preorder-field label,
.kbf-preorder-field__label {
	display: block;
	margin-bottom: var(--kbf-space-xs);
	font-size: clamp(16px, 2vw, 18px);
	font-weight: 600;
}

/* The card fields on the Payment step: a smaller label over a shorter box.
   The Square field's label is a span rather than a <label>, because the input
   it names is inside Square's own iframe and there is no control in this
   document for a <label> to point at — so it is matched here explicitly. */
.kbf-preorder-field--compact label,
.kbf-preorder-field--compact .kbf-preorder-field__label {
	margin-bottom: var(--kbf-space-xxs);
	font-size: 13px;
	color: var(--kbf-chalkboard-75);
}

.kbf-preorder-control {
	width: 100%;
	height: 56px;
	padding-inline: 20px;
	font-family: var(--kbf-font-body);
	font-size: 16px;
	font-weight: 600;
	color: var(--kbf-text-primary);
	background: var(--kbf-surface-primary);
	border: 1px solid var(--kbf-border-tertiary);
	border-radius: var(--kbf-radius-s);
}

.kbf-preorder-field--compact .kbf-preorder-control {
	height: 52px;
	padding-inline: var(--kbf-space-xs);
	font-size: 15px;
	font-weight: 400;
}

.kbf-preorder-control:focus-visible {
	outline: 2px solid var(--kbf-barn-red);
	outline-offset: 1px;
}

.kbf-preorder-control::placeholder {
	color: var(--kbf-text-primary);
	opacity: 0.45;
}

.kbf-preorder-control:disabled {
	cursor: not-allowed;
	background: var(--kbf-surface-primary);
	color: var(--kbf-chalkboard-60);
	/* Safari otherwise greys a disabled field past the design's own tint. */
	-webkit-text-fill-color: var(--kbf-chalkboard-60);
}

/*
 * The same 340px box as the time select below it — the two are one pair in the
 * frame and must read as one.
 *
 * appearance is reset for the same reason it is on the select, not to restyle
 * anything: left native, iOS Safari sizes a date input from its own shadow
 * content and treats that as a minimum the declared width cannot go under, so
 * the field rendered wider than the select beside it and ran past the column.
 * Clearing the intrinsic minimum alongside it is what makes width and max-width
 * the only things deciding the box. The calendar indicator is the platform's
 * either way — unlike the select, nothing here replaces it — so this changes
 * nothing on an engine that was already honouring the width.
 */
.kbf-preorder-control--date {
	max-width: 340px;
	appearance: none;
	-webkit-appearance: none;
	min-width: 0;
}

.kbf-preorder-control--date::-webkit-calendar-picker-indicator {
	opacity: 0.6;
	cursor: pointer;
}

.kbf-preorder-date-error,
.kbf-preorder-quantity-error,
.kbf-preorder-slot-error {
	color: var(--kbf-barn-red);
	font-weight: 600;
	font-size: 14px;
	margin: var(--kbf-space-xxs) 0 0;
}

.kbf-preorder-bulk-note {
	margin: var(--kbf-space-xxs) 0 0;
	font-size: 13px;
	line-height: 1.45;
	color: var(--kbf-chalkboard-60);
}

/* Whose order this is, in place of the name and email the account supplies. */
.kbf-preorder-account {
	margin: 0;
	font-size: 14px;
	line-height: 1.5;
	color: var(--kbf-chalkboard-80);
}

/* ---------- Pickup times ---------- */

/*
 * The frame's "Time Select" (Figma 315:2049): one field the same shape as the
 * pickup date above it, with the design system's dropdown chevron on the right.
 * It replaces the grid of Selector Chips an earlier version of this step used —
 * that was the design system's own control, but it was not what this frame
 * draws, and a dozen configurable windows as chips is a wall of near-identical
 * buttons on a phone.
 *
 * Shape, type and radius all come from .kbf-preorder-control, which the date
 * field uses too, so the two fields cannot drift apart. Only what makes it a
 * dropdown is here.
 *
 * Note one deliberate departure: the frame fills this field white while the
 * date field beside it, in that same frame, is cream. Two identically shaped
 * fields in different whites reads as a mistake rather than as emphasis, so
 * both are the cream the rest of the wizard's fields use. Worth a designer's
 * word if that was intended.
 */
.kbf-preorder-control--select {
	max-width: 340px;
	/* The chevron is the exported icon (Figma 133:3165), not a drawn glyph, and
	   it replaces the platform's own arrow — which is why appearance is reset.
	   Right-aligned 20px in, as the frame places it, with the text stopped
	   clear of it rather than running underneath. */
	appearance: none;
	padding-right: 44px;
	background-image: url("img/chevron-down.svg");
	background-repeat: no-repeat;
	background-position: right 20px center;
	background-size: 16px 16px;
	cursor: pointer;
}

/*
 * Still a field, not a dead control: it is disabled until a pickup date has
 * been chosen, and its one option says so. The default disabled treatment
 * greys it to the point of looking broken, so it keeps the enabled field's
 * ink and only softens the placeholder.
 */
.kbf-preorder-control--select:disabled {
	cursor: default;
	opacity: 0.6;
}

/* ---------- Quantity stepper ---------- */

.kbf-preorder-stepper-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--kbf-space-s);
}

.kbf-preorder-stepper {
	display: flex;
	align-items: center;
	background: var(--kbf-surface-primary);
	border: 1px solid var(--kbf-border-tertiary);
	border-radius: var(--kbf-radius-s);
	overflow: hidden;
}

.kbf-preorder-stepper__btn {
	flex: 0 0 auto;
	width: 48px;
	height: 48px;
	background: none;
	border: 0;
	font-family: var(--kbf-font-body);
	font-size: 20px;
	font-weight: 600;
	line-height: 1;
	color: var(--kbf-text-primary);
	cursor: pointer;
}

.kbf-preorder-stepper__btn:hover {
	background: var(--kbf-surface-secondary);
}

/*
 * "6 dz" is one figure made of two elements: the number stays a real number
 * input, because the quantity is typed and read by the e2e suite, and the unit
 * is a static label beside it.
 */
.kbf-preorder-stepper__display {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 3px;
	width: 88px;
	height: 48px;
}

.kbf-preorder-stepper input[data-testid="preorder-quantity"] {
	/* Two digits: the maximum is dozens, not rolls. */
	width: 2.5ch;
	padding: 0;
	background: none;
	border: 0;
	font-family: var(--kbf-font-body);
	font-size: 18px;
	font-weight: 700;
	text-align: right;
	color: var(--kbf-text-primary);
	appearance: textfield;
	-moz-appearance: textfield;
}

.kbf-preorder-stepper input[data-testid="preorder-quantity"]::-webkit-outer-spin-button,
.kbf-preorder-stepper input[data-testid="preorder-quantity"]::-webkit-inner-spin-button {
	appearance: none;
	margin: 0;
}

.kbf-preorder-stepper__unit {
	font-size: 18px;
	font-weight: 700;
}

.kbf-preorder-stepper__total {
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.kbf-preorder-stepper__total span {
	font-size: 14px;
	color: var(--kbf-chalkboard-80);
}

.kbf-preorder-stepper__total strong {
	font-size: clamp(20px, 4vw, 24px);
	color: var(--kbf-text-primary);
}

/* ---------- Payment step ---------- */

.kbf-preorder-columns {
	display: grid;
	gap: var(--kbf-space-s);
	align-items: start;
}

.kbf-preorder-summary-card {
	background: var(--kbf-surface-secondary);
	border-radius: var(--kbf-radius-s);
	padding: var(--kbf-space-s) var(--kbf-space-xs);
}

.kbf-preorder-summary-card h2 {
	margin: 0 0 20px;
	font-family: var(--kbf-font-body);
	font-size: 20px;
	font-weight: 600;
}

.kbf-preorder-summary {
	margin: 0;
}

.kbf-preorder-summary__row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--kbf-space-xs);
	padding-block: 10px;
	font-size: 15px;
}

.kbf-preorder-summary__row dt {
	color: var(--kbf-chalkboard-65);
}

.kbf-preorder-summary__row dd {
	margin: 0;
	font-weight: 600;
	text-align: right;
}

/*
 * The design's rule here is border-tertiary, which is the colour of the card
 * it sits on — invisible. The total is the number the customer is about to be
 * charged, so it gets a rule that can actually be seen.
 */
.kbf-preorder-summary__row--total {
	margin-top: 10px;
	padding-top: 20px;
	border-top: 1px solid var(--kbf-hairline);
}

.kbf-preorder-summary__row--total dt {
	font-size: 16px;
	font-weight: 600;
	color: var(--kbf-text-primary);
}

.kbf-preorder-summary__row--total dd {
	font-size: clamp(20px, 4vw, 24px);
	font-weight: 700;
}

.kbf-preorder-summary__note {
	margin: 20px 0 0;
	font-size: 13px;
	line-height: 1.45;
	color: var(--kbf-chalkboard-60);
}

.kbf-preorder-payment-form {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-s);
}

.kbf-preorder-payment-form h2 {
	margin: 0;
	font-family: var(--kbf-font-body);
	font-size: 20px;
	font-weight: 600;
}

.kbf-preorder-panel__hint {
	margin: 0;
	font-size: 14px;
	line-height: 1.45;
	color: var(--kbf-chalkboard-65);
}

.kbf-preorder-card-fields {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-xs);
}

/* Square serves the card inputs inside its own iframe and sizes it once it
   loads. The min-height holds the space in the meantime, so the Pay button
   does not jump down the page as the field appears. */
.kbf-preorder-card-mount {
	min-height: 90px;
}

.kbf-preorder-card-error {
	color: var(--kbf-error, #b32d2e);
}

.kbf-preorder-row {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--kbf-space-xs);
}

.kbf-preorder-card-fields__note {
	margin: 0;
	font-size: 12px;
	line-height: 1.45;
	color: var(--kbf-chalkboard-60);
}

.kbf-preorder-readbefore {
	background: var(--kbf-surface-secondary);
	border-radius: 12px;
	padding: 20px var(--kbf-space-s);
}

.kbf-preorder-readbefore h2 {
	margin: 0 0 10px;
	font-size: 15px;
}

.kbf-preorder-readbefore ul {
	display: grid;
	gap: 10px;
	margin: 0;
	padding-left: 20px;
}

.kbf-preorder-readbefore li {
	font-size: 13.5px;
	line-height: 1.45;
	color: var(--kbf-chalkboard-75);
}

.kbf-preorder-checkboxes {
	display: grid;
	gap: 12px;
}

.kbf-preorder-checkboxes .kbf-preorder-checkbox {
	margin-bottom: 0;
	font-size: 13.5px;
}

.kbf-preorder-checkboxes .kbf-preorder-checkbox input {
	width: 18px;
	height: 18px;
	border-radius: 4px;
}

/* The two confirmations below the agreement are acknowledgements, not the
   agreement itself, and the design sets them back a step. */
.kbf-preorder-checkbox--muted {
	font-weight: 400;
}

.kbf-preorder-checkbox--muted span {
	color: var(--kbf-chalkboard-80);
}

.kbf-preorder-payment-form .kbf-preorder-panel__actions {
	margin-top: 0;
}

.kbf-preorder-pay-btn {
	width: 100%;
}

.kbf-preorder-error,
.kbf-preorder-payment-note {
	margin: 0;
	text-align: center;
	color: var(--kbf-barn-red);
	font-weight: 600;
	font-size: 14px;
}

.kbf-preorder-confirmation {
	margin: 0;
	text-align: center;
	color: var(--kbf-chalkboard);
	font-weight: 600;
	font-size: 15px;
}

/* ---------- Responsive: back out to the frames ---------- */

/*
 * Where the three step names fit again. Below this the connectors flex; from
 * here they are the frame's fixed 64px rails.
 */
@media (min-width: 720px) {
	.kbf-step-indicator__item {
		font-size: 15px;
	}

	.kbf-step-indicator__item:not(.is-active) .kbf-step-indicator__label {
		display: inline;
	}

	.kbf-step-indicator__connector {
		flex: 0 0 64px;
		max-width: none;
	}
}

@media (min-width: 600px) {
	.kbf-preorder-terms-card {
		padding: 28px var(--kbf-space-m);
	}

	.kbf-preorder-summary-card {
		padding: var(--kbf-space-m);
	}

	.kbf-preorder-row {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}

	/* Actions stop being full-bleed — except the two the frames set that way. */
	.kbf-preorder-panel__actions .kbf-btn {
		width: auto;
	}

	.kbf-preorder-panel__actions .kbf-btn--block,
	.kbf-preorder-panel__actions .kbf-preorder-pay-btn {
		width: 100%;
	}
}

@media (min-width: 1024px) {
	.kbf-preorder-wizard-wrap {
		padding-block: var(--kbf-space-2xl) var(--kbf-space-5xl);
	}

	/*
	 * The frame's 560px square on the right with the selection column beside
	 * it. Both track widths are ratios rather than the frame's fixed 560px:
	 * at 1440 the photo lands on 560 and is capped there by its own max-width,
	 * and between 1024 and 1440 the two shrink together instead of the photo
	 * holding its size and squeezing the date, stepper and subtotal into a
	 * column too narrow to hold them on one line.
	 */
	.kbf-preorder-hero {
		grid-template-columns: minmax(0, 1fr) minmax(0, 0.88fr);
		gap: var(--kbf-space-2xl);
	}

	.kbf-preorder-hero__image {
		order: 0;
	}

	.kbf-preorder-selection {
		padding-block: var(--kbf-space-2xl);
	}

	.kbf-preorder-columns {
		/* The frame's 440px summary, allowed to give ground before the card
		   fields do — Expiry / CVC / ZIP share one row from 600px up. */
		grid-template-columns: minmax(300px, 440px) minmax(0, 1fr);
		gap: var(--kbf-space-2xl);
	}
}

/* ==========================================================================
   About
   ==========================================================================
   Design tokens are the Figma variables for the "About" frame (33:659).
   Everything below references the custom properties at the top of this file;
   nothing re-types a value. The section keeps its own responsive rules with it
   rather than joining the home-page groups above — About is one self-contained
   page.

   The geometry the whole timeline hangs off, read from the Figma frame:
   the card is 1062 x 540, the cream info panel is 546 x 540 sitting at x=516,
   and the copy starts at x=565 / y=44 in a 448px measure. Every proportion
   below is one of those numbers over 1062 or 540 — including the per-entry
   photo crops, which history.php supplies as percentages of exactly that
   1062 x 540 box.
   ========================================================================== */

/* ---------- About: intro statement ---------- */

.kbf-about-intro {
	text-align: center;
	background: var(--kbf-surface-primary);
	/*
	 * Figma: the text block starts 34px under the masthead, and the photo
	 * divider butts straight up against it. The bottom padding reserves the
	 * cream the divider will occupy once it is pulled up out of the history
	 * section below (see .kbf-photostrip) — 363 of the divider's 531px.
	 */
	padding-block: clamp(24px, 3vw, 34px) 25.208vw; /* 363 / 1440 */
}

.kbf-about-intro__eyebrow {
	/* Figma "Label Medium": Josefin Sans bold 14px, tracking 1.4px. */
	font-weight: 700;
	font-size: 14px;
	line-height: 1.2;
	color: var(--kbf-barn-red);
	margin: 0 0 var(--kbf-space-xxs);
}

.kbf-about-intro__title {
	/* Figma: the statement is set in a 1059px measure inside the 1440 frame. */
	max-width: 1059px;
	margin-inline: auto;
	color: var(--kbf-text-primary);
}

/* ---------- About: photo divider ---------- */

/*
 * A single composited transparent PNG, like the homepage collage — the tilted
 * frames and overlaps are baked in by the design.
 *
 * Figma: the strip is 1440 x 531 with its top edge at y=347 and the chalkboard
 * band starting at y=710, so 363 of its 531px sit on the cream and the rest on
 * the chalkboard. It is the first child of the (flow-root) history section and
 * is pulled up by exactly that 363px — 363/1440 of the viewport width, since
 * the image is full-bleed — so it straddles the seam at every width. Same
 * technique as .kbf-badge on the homepage.
 */
.kbf-photostrip {
	position: relative;
	z-index: 2;
	margin-top: -25.208vw; /* -363 / 1440 */
	/* Figma: the strip ends at y=878 and the eyebrow starts at 979. */
	margin-bottom: clamp(40px, 7.01vw, 101px); /* 101 / 1440 */
}

.kbf-photostrip__img {
	width: 100%;
	height: auto;
}

/* ---------- About: our history ---------- */

.kbf-history {
	/* flow-root establishes a block formatting context so the photo divider's
	   negative margin overflows UP onto the cream instead of collapsing through
	   and dragging the whole chalkboard band over the intro. */
	display: flow-root;
	background: var(--kbf-surface-tertiary);
	color: var(--kbf-text-secondary);
	/* Figma: the cards end at y=1819 and the chalkboard band at 1948. */
	padding-bottom: clamp(48px, 8.96vw, 129px); /* 129 / 1440 */
}

.kbf-history__intro {
	text-align: center;
	/* Figma: the title block ends at y=1062, the timeline starts at 1104. */
	margin-bottom: clamp(24px, 2.92vw, 42px); /* 42 / 1440 */
	padding-inline: var(--kbf-gutter);
}

.kbf-history__eyebrow {
	font-weight: 700;
	font-size: 14px;
	line-height: 1.2;
	color: var(--kbf-roof-60);
	margin: 0 0 var(--kbf-space-xxs);
}

.kbf-history__title {
	color: var(--kbf-text-secondary);
}

/* ---------- About: timeline year rail ---------- */

.kbf-timeline__rail {
	width: 100%;
	max-width: var(--kbf-max);
	margin-inline: auto;
	margin-bottom: var(--kbf-space-l);
	padding-inline: var(--kbf-gutter);
}

.kbf-timeline__years {
	display: flex;
	/* Bottom-aligned, so the dots sit on one line whatever height the labels
	   above them resolve to. */
	align-items: flex-end;
	justify-content: center;
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * The whole rail — 1924 through 2025 — is always in view: it is the map of the
 * timeline, so it must never itself need scrolling. The years share the width
 * equally and shrink to fit (flex-basis 0 with min-width 0, so a long label
 * cannot force an overflow), capped so they do not sprawl on a wide screen.
 */
.kbf-timeline__year {
	position: relative;
	flex: 1 1 0;
	min-width: 0;
	max-width: 200px;
}

/* The rail: a hairline from the previous dot to this one. It is drawn on the
   list item, so the dots (nested deeper) always paint over it. */
.kbf-timeline__year + .kbf-timeline__year::before {
	content: "";
	position: absolute;
	left: -50%;
	bottom: 8px; /* the dot box is 16px tall and bottom-aligned */
	width: 100%;
	border-top: 1px solid var(--kbf-roof-60);
	opacity: 0.5;
}

.kbf-timeline__year-link {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 12px;
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: 18px;
	line-height: 1;
	color: var(--kbf-roof-60);
	transition: color 0.18s ease;
}

/* A year is four digits — never let it wrap once the items get narrow. */
.kbf-timeline__year-label {
	white-space: nowrap;
}

.kbf-timeline__year-link:hover {
	color: var(--kbf-surface-primary);
}

/* A fixed 16px box with the circle centred inside it, so growing the current
   dot from 8px to 16px keeps every dot on the same centre line. */
.kbf-timeline__dot {
	position: relative;
	display: block;
	width: 16px;
	height: 16px;
}

.kbf-timeline__dot::before {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: currentColor;
	transform: translate(-50%, -50%);
	transition: width 0.18s ease, height 0.18s ease;
}

/*
 * The current year is marked by ink and by its dot, not by its size. Setting it
 * larger than its neighbours made stepping along the rail read as a zoom: the
 * label the visitor was looking at grew while the one they had just left shrank,
 * and every year between them shifted sideways to make room — on a control whose
 * whole job is to be a stable map of the timeline.
 */
.kbf-timeline__year.is-current .kbf-timeline__year-link {
	color: var(--kbf-surface-primary);
}

.kbf-timeline__year.is-current .kbf-timeline__dot::before {
	width: 16px;
	height: 16px;
}

/* ---------- About: timeline arrows ---------- */

.kbf-timeline__nav {
	display: flex;
	justify-content: center;
	gap: var(--kbf-space-xl);
	/* Figma: the arrows end at y=1219, the cards start at 1255. */
	margin-bottom: clamp(24px, 2.5vw, 36px); /* 36 / 1440 */
}

/* On the chalkboard the arrows are bare 24px cream chevrons, not the 40px white
   discs the testimonials carousel uses on cream. */
.kbf-history .kbf-carousel-btn {
	width: 24px;
	height: 24px;
	background: transparent;
	border-color: transparent;
	color: var(--kbf-surface-primary);
}

.kbf-history .kbf-carousel-btn svg {
	width: 24px;
	height: 24px;
}

.kbf-history .kbf-carousel-btn:hover {
	background: transparent;
	border-color: transparent;
	color: var(--kbf-barn-red);
}

/* ---------- About: timeline cards ---------- */

.kbf-timeline__track {
	display: flex;
	/* Equal boxes, never content-sized: a card's height comes from its own
	   aspect-ratio below, so nothing an editor types into one entry can make
	   that card a different size from the other six. */
	align-items: flex-start;
	gap: var(--kbf-space-m);
	overflow-x: auto;
	/* proximity, not mandatory: mandatory snap cancels the arrows' smooth
	   programmatic scroll, while proximity still snaps touch swipes. */
	scroll-snap-type: x proximity;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	/* Enough side padding that the first and last card can reach the centre. */
	padding-inline: max(var(--kbf-gutter), calc((100% - min(1062px, 88vw)) / 2));
}

.kbf-timeline__track::-webkit-scrollbar {
	display: none;
}

/*
 * Figma: the card is 1062 x 540 — a full-bleed photo with a 546px info panel
 * over its right half — and it is that size for every one of the seven
 * entries. The width is fixed and the height comes from the design ratio, so
 * all seven are exactly the same box no matter how much copy an entry carries;
 * the panel's type is sized in cqw against the card, so the whole composition
 * is a pure scale of the Figma frame at any width.
 *
 * A content-driven floor was what broke this: the longest entry (1992) set the
 * row's height for everybody, which both made the cards taller than the design
 * and — because the frame is what the crop percentages resolve against —
 * stretched every photo vertically by the same 4%.
 */
.kbf-timeline__card {
	container-type: inline-size;
	position: relative;
	display: flex;
	justify-content: flex-end;
	flex: 0 0 min(1062px, 88vw);
	aspect-ratio: 1062 / 540;
	border-radius: var(--kbf-radius-s);
	overflow: hidden;
	scroll-snap-align: center;
	scroll-margin-inline: var(--kbf-gutter);
}

/*
 * The photo's own half of the card: Figma's 1062 x 540 card less the 546px info
 * panel over its right, so 516 x 540. It is given as a percentage of the card
 * rather than as 516px because the card itself is fluid — min(1062px, 88vw) —
 * and the composition is a pure scale of the Figma frame at any width.
 *
 * It used to be the whole card, with each photograph placed inside it by four
 * percentages per entry. The photographs now carry that framing themselves, so
 * the frame is the one box they are all cropped to and nothing has to be
 * measured per card. Stacked on mobile it becomes the card's photo band at the
 * same 516 x 540 proportion; see the media query at the end of this section.
 */
.kbf-timeline__frame {
	position: absolute;
	inset: 0 auto 0 0;
	width: 48.5876%; /* 516 / 1062 */
	overflow: hidden;
}

/*
 * A plain fill. Every photograph is already cropped to the frame's 516 x 540,
 * so cover neither crops nor distorts anything here — it is only insurance for
 * a photograph an editor uploads at some other proportion.
 */
.kbf-timeline__photo {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/*
 * Figma sets the panel's inset at 44px top and bottom. It is 32 here, which
 * lifts the year 12px and hands the copy the 24px it is short: the longest entry
 * (1992) overran the panel by 18px and was the one card on the rail that
 * scrolled. Taken off both edges rather than only the top, so the panel stays
 * balanced, and off the padding rather than the 48px under the year, because the
 * gap is what keeps the year reading as a stamp rather than a heading.
 *
 * All seven cards move together — the point of a fixed box is that nothing an
 * editor types into one entry makes it a different card from its neighbours, and
 * that holds for where the year sits too.
 */
.kbf-timeline__info {
	position: relative;
	width: 51.41cqw;           /* 546 / 1062 */
	padding: 3.01cqw 4.61cqw;  /*  32 / 1062, 49 / 1062 */
	display: flex;
	flex-direction: column;
	gap: clamp(16px, 4.52cqw, 48px);
	background: var(--kbf-surface-secondary);
	color: var(--kbf-text-primary);
	border-radius: 0 var(--kbf-radius-s) var(--kbf-radius-s) 0;
}

.kbf-timeline__stamp {
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: clamp(36px, 9.04cqw, 96px); /* 96 / 1062 */
	/*
	 * Figma trims the year's text box to the cap height (text-box-trim: trim-both
	 * with a cap edge), so at 128px it occupies 91px, not a full 128px line. A
	 * full line box makes every card 31px taller than the design and pushes the
	 * whole chalkboard band out with it. Playfair Display's cap height is 0.71em,
	 * which reproduces that box in every browser — `text-box` is not supported
	 * widely enough yet to rely on, and digits have no descenders to clip.
	 */
	line-height: 0.71;
	color: var(--kbf-barn-red);
	margin: 0;
}

/*
 * The card is a fixed box, so the copy is what has to give when an entry runs
 * long. No shipped entry does any more — the panel's inset above was cut to make
 * room for the longest of them — but this stays as the safety net for an editor
 * who writes past the box: it scrolls inside the panel rather than clipping the
 * text or making that one card a different size from its neighbours. min-height:
 * 0 is what lets a flex child shrink below its content and actually scroll.
 */
.kbf-timeline__copy {
	display: flex;
	flex-direction: column;
	gap: clamp(10px, 1.51cqw, 16px);
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-width: thin;
}

.kbf-timeline__headline {
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: clamp(18px, 2.26cqw, 24px);
	line-height: 1.56;
	margin: 0;
}

.kbf-timeline__copy p {
	font-size: clamp(14px, 1.51cqw, 16px);
	line-height: 1.56;
	margin: 0;
}

/* ---------- Instagram feed (About and U-Pick) ---------- */

/*
 * Shared by both templates that end on this band (template-parts/instagram.php).
 * About leads with the strip alone; U-Pick introduces it with a heading, and
 * sets its own bottom spacing in the U-Pick section further down.
 */
.kbf-instagram {
	display: flex;
	flex-direction: column;
	gap: clamp(32px, 4.5vw, 64px);
	background: var(--kbf-surface-secondary-30);
	/* Figma: 96px above the button, and the frame leaves noticeably more air
	   under the strip — the photos stop 130px short of its bottom edge. */
	padding-block: clamp(48px, 7vw, 96px) clamp(56px, 9vw, 130px);
}

.kbf-instagram__inner {
	width: 100%;
	max-width: var(--kbf-max);
	margin-inline: auto;
	padding-inline: var(--kbf-gutter);
}

/*
 * Heading on the left, button on the right, baselines aligned — and on a phone
 * the button wraps under the standfirst. With no heading (About) the row has a
 * single child, which leaves the button exactly where it has always been.
 */
.kbf-instagram__header {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: var(--kbf-space-s);
}

.kbf-instagram__heading {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-xxs);
}

.kbf-instagram__title {
	color: var(--kbf-text-primary);
}

.kbf-instagram__text {
	/* Figma: "Body Large" in a 626px measure. */
	max-width: 626px;
	margin: 0;
	font-size: 18px;
	line-height: 1.56;
	color: var(--kbf-chalkboard-75);
}

/*
 * Figma: six 267 x 400 photos with a 16px gap measure 1682px inside the 1440
 * frame, and the row is centred and clipped — so 121px is cut off the first
 * photo and 121px off the last, and the strip bleeds evenly into both edges.
 *
 * Centring a *clipped* row is what reproduces that symmetry. A scrolling row
 * cannot: a scroll container's overflow is only ever reachable past its end, so
 * centring one pins the row flush to the left edge and cuts the full 242px off
 * the right instead.
 */
.kbf-instagram__feed {
	display: flex;
	gap: var(--kbf-space-xs);
	justify-content: center;
	overflow: clip;
}

.kbf-instagram__photo {
	flex: 0 0 auto;
	/* 267 / 1440 of the frame, so the strip keeps bleeding off both edges on a
	   wider screen instead of leaving tinted margins, and holds the design's
	   exact 267 x 400 at 1440. */
	width: max(180px, 18.54vw);
	height: auto;
	aspect-ratio: 267 / 400;
	object-fit: cover;
}

/* The live feed wraps each photo in a link to its post (sbi/feed.php). The
   link takes the photo's place in the row, so the strip lays out the same
   whether it is live or the fallback photos. */
.kbf-instagram__post {
	display: block;
	flex: 0 0 auto;
}

.kbf-instagram__post:hover .kbf-instagram__photo {
	opacity: 0.85;
}

.kbf-instagram__post:focus-visible {
	outline: 2px solid var(--kbf-barn-red);
	outline-offset: 2px;
}

/* ---------- About: responsive ---------- */

@media (max-width: 860px) {
	/* The divider is a wide strip: at full width it collapses to a thin band
	   with unreadable photos. Give it real height and let it swipe sideways
	   instead, exactly as the homepage collage does — and re-derive the lift
	   from that height so it still straddles the seam. */
	.kbf-about-intro {
		padding-bottom: calc(0.6836 * clamp(220px, 62vw, 300px));
	}

	.kbf-photostrip {
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
		margin-top: calc(-0.6836 * clamp(220px, 62vw, 300px)); /* -363 / 531 */
	}

	.kbf-photostrip::-webkit-scrollbar {
		display: none;
	}

	.kbf-photostrip__img {
		width: auto;
		height: clamp(220px, 62vw, 300px);
		max-width: none;
	}

	/* Mobile is touch-only — no arrows (carousel.js hides them too). */
	.kbf-timeline__nav {
		display: none;
	}

	/* Seven years share roughly 335px on a phone. The labels step down a size
	   (and the rail tightens) so the full 1924 → 2025 span still fits on screen
	   in one view rather than scrolling. One size for all seven, as on desktop —
	   see the note on .is-current above. */
	.kbf-timeline__rail {
		padding-inline: var(--kbf-space-xs);
	}

	.kbf-timeline__year-link {
		font-size: 12px;
		gap: 10px;
	}

	/*
	 * The card's side-by-side composition needs the photo and the copy to share
	 * a 1062px line; on a phone it stacks instead. Every cqw-derived value is
	 * restated in absolute units here, because a card that narrow would scale
	 * the copy down to a few pixels.
	 *
	 * Stretched, so the seven cards stay exactly the same size as each other
	 * here too: the photo band is fixed and the cream panel is what grows, so a
	 * short entry shows its slack as empty panel at the bottom of the card
	 * rather than as a shorter card.
	 */
	.kbf-timeline__card {
		flex: 0 0 min(420px, 86vw);
		flex-direction: column;
		justify-content: flex-start;
		aspect-ratio: auto;
	}

	.kbf-timeline__track {
		align-items: stretch;
	}

	/*
	 * Stacked, the photo band is the same 516 x 540 slice the desktop card
	 * shows, so the same picture appears on a phone — it is the proportion every
	 * photograph is cropped to, and the band is the whole card's width here
	 * rather than a fraction of it.
	 *
	 * Fixed, and never shrunk by a long entry: the cards share a width, so one
	 * ratio makes every photo exactly the same size as well as the same crop.
	 */
	.kbf-timeline__frame {
		position: relative;
		inset: auto;
		flex: 0 0 auto;
		width: 100%;
		aspect-ratio: 516 / 540;
	}

	.kbf-timeline__info {
		/* Grows into the rest of the card; the copy stays top-aligned, so the
		   leftover shows as empty panel below it rather than re-flowing. */
		flex: 1 1 auto;
		justify-content: flex-start;
		width: 100%;
		padding: var(--kbf-space-s);
		gap: var(--kbf-space-s);
		border-radius: 0;
	}

	.kbf-timeline__stamp {
		font-size: 64px;
	}

	/* Stacked there is room for the longest entry, so nothing scrolls. */
	.kbf-timeline__copy {
		gap: var(--kbf-space-xs);
		overflow-y: visible;
	}

	.kbf-timeline__headline {
		font-size: 20px;
		/* The headlines run to two or three lines, which would start each card's
		   paragraph on a different line. Reserving the tallest (three lines at
		   the 1.56 leading) lands every paragraph on the same one. A longer
		   headline still grows — this is a floor, not a fixed height. */
		min-height: calc(3 * 1.56em);
	}

	.kbf-timeline__copy p {
		font-size: 16px;
	}

	/*
	 * The design's centred-and-clipped strip only works where most of the row is
	 * on screen. On a phone barely two photos fit, so clipping would put four of
	 * the six permanently out of reach — it swipes instead, from the first.
	 */
	.kbf-instagram__feed {
		justify-content: flex-start;
		overflow-x: auto;
		overflow-y: hidden;
		scroll-snap-type: x proximity;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
	}

	.kbf-instagram__feed::-webkit-scrollbar {
		display: none;
	}

	.kbf-instagram__photo,
	.kbf-instagram__post {
		scroll-snap-align: center;
	}

	.kbf-instagram__photo {
		width: clamp(180px, 55vw, 267px);
	}
}

/* ==========================================================================
   U-Pick (page-upick.php)
   ==========================================================================
   Design tokens are the Figma variables for the "U-Pick" frame (524:2046), a
   1440 x 2424 desktop board. There is no mobile board for this page, so every
   rule below is authored mobile-first and the desktop numbers arrive at their
   own breakpoint or at the top of a clamp() — the ratios are the frame's, the
   floors are what keeps a 96px title and a two-column row readable on a phone.

   The vertical geometry the page hangs off, read from the frame: the masthead
   ends at y=186, the hero photograph runs 186 → 697, the season band 697 →
   1313, the Instagram band 1313 → 2046, and the footer starts there.
   ========================================================================== */

/* ---------- U-Pick: the seal on the masthead rule ---------- */

/*
 * Figma draws this as a 1440 x 132 layer at y=109: a hairline inset 20px from
 * both edges with the seal centred on it. The rule sits 11px above the top of
 * the hero photograph, so the seal's lower half laps onto the fruit — the same
 * straddling trick as .kbf-badge on the home page, but anchored to the block's
 * bottom edge rather than pulled up by a negative margin, because here it is
 * the gap between two sections rather than a section's first child.
 *
 * Sized from two custom properties so the seal stays centred on the rule at
 * every width: the offset that centres it is half its own height, measured from
 * the same lap that lifts the rule off the hero.
 *
 * The block has to be at least tall enough to hold the half of the seal that
 * sits above the rule, because .kbf-main clips and this is its first child —
 * anything higher than the block's own top edge is cut off rather than laid
 * over the masthead. The frame's cream band satisfies that at 1440 (74 vs the
 * 76.5 needed, within the hairline) but not on a phone, where the band is
 * clamped to 48 while the seal only falls to 96: the top 11px of the seal, the
 * "KNAUS BERRY FARM" of the lettering, were being sliced flat.
 */
.kbf-upick-stamp {
	--kbf-seal: clamp(96px, 9.1vw, 131px);
	/* How far the rule — and so the seal's centre — sits above the hero. */
	--kbf-seal-lap: 11px;
	position: relative;
	z-index: 5;
	/* The cream between the masthead and the hero: the frame's 186px, less the
	   masthead's own height, is 74 at 1440 — floored at whatever keeps the
	   seal's upper half inside the block. */
	height: max(
		clamp(48px, 5.14vw, 74px),
		calc(var(--kbf-seal) / 2 + var(--kbf-seal-lap))
	);
}

.kbf-upick-stamp__rule {
	position: absolute;
	/* Figma insets the rule 20px from each edge, at every width. */
	left: 20px;
	right: 20px;
	bottom: var(--kbf-seal-lap);
	border-top: 1px solid var(--kbf-border-dark);
}

.kbf-upick-stamp__seal {
	position: absolute;
	left: 50%;
	bottom: calc(var(--kbf-seal-lap) - var(--kbf-seal) / 2);
	width: var(--kbf-seal);
	height: var(--kbf-seal);
	transform: translateX(-50%);
}

/* ---------- U-Pick: hero ---------- */

.kbf-upick-hero {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	/* Figma: 1440 x 511 -> 511/1440. The floor is the mobile decision: that
	   ratio alone would leave a 138px band on a phone, too shallow for the
	   title to sit in. */
	min-height: max(420px, 35.486vw);
	padding: var(--kbf-space-2xl) var(--kbf-gutter);
	text-align: center;
	color: var(--kbf-text-secondary);
	background: var(--kbf-surface-tertiary);
	overflow: hidden;
}

/*
 * The frame places a 1440 x 1077 image box at y=-218 inside the 511-tall hero,
 * and the photograph (1170 x 1455) fills that box. Composing the two leaves the
 * band between 32% and 61% of the photograph's height on screen, whose centre
 * is 46.4% — which object-position reaches at 45% once the overflow cover
 * leaves is taken into account. Reproducing it this way rather than as an
 * absolutely-positioned box means the same framing survives the taller crop a
 * phone gets.
 */
.kbf-upick-hero__bg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	max-width: none;
	object-fit: cover;
	object-position: center 45%;
}

.kbf-upick-hero__inner {
	position: relative;
	z-index: 2;
	width: 100%;
	/* Figma: the text measure inside the 1228px content container. */
	max-width: 1126px;
}

.kbf-upick-hero__eyebrow {
	/* Figma "Label Large": Josefin Sans bold 16px, tracking 1.6px. */
	margin: 0;
	font-weight: 700;
	font-size: 16px;
	line-height: 1.2;
}

.kbf-upick-hero__title {
	/* Figma "Heading 1": Playfair Display bold 96px. The eyebrow sits directly
	   on it — the frame gives the pair no gap at all. */
	margin: 0;
	font-size: clamp(40px, 6.67vw, 96px);
	line-height: 1.2;
	color: inherit;
}

/* ---------- U-Pick: the season statement ---------- */

.kbf-upick-intro {
	display: grid;
	gap: clamp(32px, 5.56vw, 80px);
	align-items: center;
	width: 100%;
	max-width: var(--kbf-max);
	margin-inline: auto;
	/*
	 * Figma fixes this band at 616px tall with 64/96 padding and the row centred
	 * inside it, which leaves 122px above the photograph and 154px below. Stated
	 * as padding rather than a height so the copy can grow past the photograph
	 * when an editor writes more of it.
	 */
	padding: clamp(48px, 8.47vw, 122px) var(--kbf-gutter) clamp(56px, 10.7vw, 154px);
}

.kbf-upick-intro__copy {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-s);
}

.kbf-upick-intro__head {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.kbf-upick-intro__eyebrow {
	/* Figma "Label Medium": Josefin Sans bold 14px, tracking 1.4px. */
	margin: 0;
	font-weight: 700;
	font-size: 14px;
	line-height: 1.2;
	color: var(--kbf-barn-red);
}

.kbf-upick-intro__title {
	color: var(--kbf-text-primary);
}

.kbf-upick-intro__when {
	display: flex;
	flex-direction: column;
	gap: 17px;
}

.kbf-upick-intro__text {
	/* Figma "Body Large" at 75%. */
	margin: 0;
	font-size: 18px;
	line-height: 1.56;
	color: var(--kbf-chalkboard-75);
}

/*
 * Barn red at 75%, which no colour token carries — and this paragraph is a
 * single colour throughout, so the opacity has nothing else to drag down with
 * it (unlike the body copy above, where a token exists precisely because it
 * would have).
 */
.kbf-upick-intro__note {
	margin: 0;
	font-weight: 700;
	font-size: 18px;
	line-height: 1.4;
	color: var(--kbf-barn-red);
	opacity: 0.75;
}

.kbf-upick-intro__photo {
	width: 100%;
	height: auto;
	/* Figma: 545 x 340, radius/radius-minimal. */
	aspect-ratio: 545 / 340;
	object-fit: cover;
	border-radius: var(--kbf-radius-minimal);
}

/* ---------- U-Pick: Instagram ---------- */

/*
 * The shared band, with this frame's own spacing: U-Pick leaves 72px under the
 * strip where About leaves 130.
 */
.kbf-instagram--upick {
	padding-bottom: clamp(40px, 5vw, 72px);
}

/* ---------- U-Pick: from the phone up ---------- */

@media (min-width: 1024px) {
	/*
	 * Figma: 655px of copy and 545px of photograph across the 1280px measure,
	 * with the 80px gutter either side. Stated as fractions so the pair keeps the
	 * frame's proportion at every width above the breakpoint, and reaches the
	 * design's exact numbers at 1440.
	 *
	 * 1024 rather than the 860 the rest of the marketing pages break at — the
	 * same width the preorder columns use. Below it the 655:545 split leaves the
	 * statement wrapping to three lines beside a photograph too small to read
	 * the field in, and both are better off with the full measure.
	 */
	.kbf-upick-intro {
		grid-template-columns: 655fr 545fr;
	}
}

/* ==========================================================================
   Visit modal (global)
   ==========================================================================
   Opened from the "Visit" item in the primary navigation, over whatever page
   the visitor is on. Design tokens are the Figma variables for the "Visit
   Modal" frame; every value below references a custom property already defined
   at the top of this file, including the divider rule (--kbf-border-dark is
   exactly the design's #46443E at 20%).
   ========================================================================== */

/* Hold the page still behind the dialog so a scroll gesture over the backdrop
   does not scroll the article underneath it. */
.kbf-modal-open {
	overflow: hidden;
}

.kbf-modal {
	position: fixed;
	inset: 0;
	z-index: 100;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: clamp(16px, 3vw, 40px);
	/*
	 * inset:0 on a fixed element measures the LARGE viewport — the height the
	 * page would have with the browser's address bar retracted — so on a phone
	 * where the bar is showing, the bottom of the dialog sits underneath it and
	 * has to be scrolled into view. dvh is the dynamic viewport: it tracks the
	 * bar, so the dialog is sized against what is actually on screen.
	 *
	 * Height wins over the over-constrained inset, and a browser without dvh
	 * drops this declaration and falls back to the inset above.
	 */
	height: 100dvh;
}

/* display:flex above would otherwise beat the hidden attribute. */
.kbf-modal[hidden] {
	display: none;
}

.kbf-modal__backdrop {
	position: absolute;
	inset: 0;
	background: rgba(0, 0, 0, 0.3);
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
}

.kbf-modal__dialog {
	position: relative;
	display: flex;
	flex-direction: column;
	width: min(1275px, 100%);
	max-height: 100%;
	background: var(--kbf-surface-primary);
	border-radius: var(--kbf-radius-s);
	box-shadow: 0 4px 24px rgba(0, 0, 0, 0.16);
	/* Clips the CTA bar to the dialog's rounded bottom corners. */
	overflow: hidden;
}

/* On touch widths this scrolls, and keeping the CTA bar outside it is what pins
   the two buttons to the bottom of the dialog. On desktop it never scrolls —
   see the min-width: 1025px block below. */
.kbf-modal__body {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	display: flex;
	flex-direction: column;
	/*
	 * No gap between the title row and the Info + Map row: in the design the
	 * content area is 577px = 58 (title) + 519 (row) with nothing between them.
	 * What separates the info column from the title is the row's own bottom
	 * alignment, not a gap. The 32px before the CTA bar is this box's bottom
	 * padding. A gap here pushes the map, the info column and the bar all down
	 * by 32px at once.
	 */
	gap: 0;
	padding: var(--kbf-space-m);
}

/*
 * Desktop: the modal is a fixed composition — 1275 x 757, map included — and the
 * design has no scrolling state for it. So it is never allowed to grow a
 * scrollbar. When the window is shorter than the composition, visit-modal.js
 * scales the whole dialog down as one unit, which keeps every proportion
 * exactly as drawn. Cropping the map or scrolling the body would both change
 * the design; scaling does not.
 */
@media (min-width: 1025px) {
	/*
	 * The design's own width, stated rather than fitted. Letting it shrink to
	 * the window would re-flow the composition — the map would narrow while the
	 * 356px info column and the 18px type did not — and it would stop being the
	 * thing that was drawn. It is held at 1275 and scaled instead.
	 *
	 * Scoped to the Visit modal's root rather than written against
	 * .kbf-modal__dialog on its own: this is the fixed-composition treatment,
	 * and the Order modal is the counter-example — it is type and buttons, so it
	 * reflows to the window and must be allowed to shrink.
	 */
	[data-kbf-visit-modal-root] .kbf-modal__dialog {
		width: 1275px;
		/* It is a flex item, so without this it would quietly shrink back to the
		   window instead of keeping the design's width and being scaled. */
		flex-shrink: 0;
		max-height: none;
		transform-origin: center center;
	}

	.kbf-modal__body {
		overflow: visible;
	}
}

.kbf-visit-modal__head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--kbf-space-s);
}

.kbf-visit-modal__title {
	color: var(--kbf-text-primary);
}

.kbf-modal__close {
	flex-shrink: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: var(--kbf-barn-red);
	color: var(--kbf-text-secondary);
	cursor: pointer;
	transition: background-color 0.18s ease;
}

.kbf-modal__close:hover {
	background: var(--kbf-barn-red-dark);
}

/* Inverted, for a close control that sits on the barn-red ground rather than on
   the cream one (Order modal). Same glyph, swapped ink. */
.kbf-modal__close--cream {
	background: var(--kbf-surface-primary);
	color: var(--kbf-barn-red);
}

.kbf-modal__close--cream:hover {
	background: var(--kbf-surface-secondary);
}

.kbf-modal__close svg {
	display: block;
	width: 16px;
	height: 16px;
}

/* Info column is fixed-ish at the design's 356px; the map takes the rest.
   Bottom-aligned, as in the design, so the map's lower edge lines up with the
   last line of the contact block. */
.kbf-visit-modal__grid {
	display: grid;
	grid-template-columns: minmax(260px, 356px) minmax(0, 1fr);
	align-items: end;
	gap: var(--kbf-space-xl);
}

.kbf-visit-modal__info {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-xl);
}

/* Figma "Label Large" over the divider rule: the heading and its rule are one
   unit, so the rule is the heading's own border rather than a separate <hr>. */
.kbf-visit-modal__label {
	margin: 0 0 12px;
	padding-bottom: var(--kbf-space-xxs);
	font-weight: 700;
	font-size: 16px;
	line-height: 1.2;
	color: var(--kbf-barn-red);
	border-bottom: 1px solid var(--kbf-border-dark);
}

/* Figma "Body Large". */
.kbf-visit-modal__block p {
	margin: 0;
	font-weight: 400;
	font-size: 18px;
	line-height: 1.56;
	color: var(--kbf-text-primary);
}

.kbf-visit-modal__block p + p {
	margin-top: var(--kbf-space-xs);
}

.kbf-visit-modal__block a:hover {
	color: var(--kbf-barn-red);
}

/* The map is sized by ratio, not by height, so it stays whole at every width.
   The cream ground is what shows while Google's iframe is still loading. */
.kbf-visit-modal__map {
	position: relative;
	width: 100%;
	aspect-ratio: 807 / 518;
	background: var(--kbf-surface-secondary);
	overflow: hidden;
}

.kbf-visit-modal__map iframe {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

.kbf-visit-modal__cta {
	flex-shrink: 0;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: var(--kbf-space-s);
	padding: var(--kbf-space-m);
	background: var(--kbf-barn-red);
}

/* ---------- Visit modal: responsive ---------- */

/*
 * Its own breakpoint rather than the site-wide 860px: the two columns need
 * roughly 1200px to hold the design's proportions, so they stack while there is
 * still plenty of width — squeezing the map to a sliver first would be worse
 * than stacking early.
 */
@media (max-width: 1024px) {
	/* Stacked, so the title now needs the gap that bottom alignment provided in
	   the two-column composition. */
	.kbf-modal__body {
		gap: var(--kbf-space-m);
	}

	.kbf-visit-modal__grid {
		grid-template-columns: minmax(0, 1fr);
		align-items: stretch;
		gap: var(--kbf-space-m);
	}

	.kbf-visit-modal__info {
		gap: var(--kbf-space-m);
	}

	.kbf-visit-modal__map {
		aspect-ratio: 16 / 10;
	}
}

/*
 * Phones. Still a modal: it keeps its margin, its corners and a visible strip
 * of the blurred page on all four sides, so it reads as something layered over
 * the page rather than as a new screen. Tighter padding and a shorter map are
 * what buy the room for that.
 */
@media (max-width: 860px) {
	.kbf-modal {
		padding: var(--kbf-space-xs);
	}

	.kbf-modal__body {
		padding: var(--kbf-space-s);
		gap: var(--kbf-space-s);
	}

	.kbf-visit-modal__map {
		aspect-ratio: 3 / 2;
	}

	.kbf-visit-modal__cta {
		padding: var(--kbf-space-s);
		gap: var(--kbf-space-xs);
	}

	/* Two full-width buttons rather than two cramped ones side by side. */
	.kbf-visit-modal__cta .kbf-btn {
		flex: 1 1 240px;
	}
}

/*
 * Phones proper. The block above stacks the composition; this one re-scales it,
 * because stacking alone left every measure at its desktop size — a 30px title,
 * 18px body copy and 32px between blocks. Held together on a 390px screen that
 * pushed the map 252px below the fold, so the one thing a visitor opens this
 * modal for on a phone — where the farm is — was invisible and gave no hint it
 * was there.
 *
 * Nothing is removed. Every line of copy, both buttons and the map are still
 * here; they are just sized for the screen, which brings the top of the map
 * above the fold so the panel reads as scrollable instead of as finished.
 */
@media (max-width: 560px) {
	/* Tighter padding and gaps throughout: every pixel the fixed content gives
	   back is a pixel the map gets, and the map is what was being squeezed out. */
	.kbf-modal__body {
		gap: var(--kbf-space-xs);
		padding: var(--kbf-space-xs);
	}

	.kbf-visit-modal__title {
		font-size: 22px;
	}

	.kbf-visit-modal__grid,
	.kbf-visit-modal__info {
		gap: var(--kbf-space-xs);
	}

	.kbf-visit-modal__label {
		margin-bottom: var(--kbf-space-xxs);
	}

	/* 18px at 1.56 is a desktop measure. The hours block is five lines, so the
	   leading is where most of its height actually goes. */
	.kbf-visit-modal__block p {
		font-size: 16px;
		line-height: 1.4;
	}

	.kbf-visit-modal__block p + p {
		margin-top: var(--kbf-space-xxs);
	}

	/*
	 * The map stops having a size of its own and takes whatever is left.
	 *
	 * Everything else in this modal is type, and type has the height it has. The
	 * map is the one element that is legible at any height, so it is the one that
	 * should absorb the difference — sized by aspect ratio it forced a total the
	 * screen could not meet, and the overflow came out as a scroll that hid the
	 * bottom of the panel. Given the remaining space instead, the composition
	 * fits the viewport by construction, at any phone height.
	 *
	 * min-height is the floor below which a map is no longer worth showing. Only
	 * a screen too short even for that falls back to scrolling the body, which is
	 * why overflow-y stays auto rather than hidden: clipping the contact details
	 * would be worse than scrolling to them.
	 */
	.kbf-visit-modal__grid {
		display: flex;
		flex-direction: column;
		flex: 1 1 auto;
		min-height: 0;
	}

	.kbf-visit-modal__info {
		flex: 0 0 auto;
	}

	/*
	 * Everything else in this modal is type, and type has the height it has, so
	 * the map is the only part that can carry the modal's overall size. Rather
	 * than a fixed height it scales with the viewport: taller phones get a bigger
	 * map and a bigger panel, short ones get the floor and the panel stays inside
	 * the screen. 96px is the floor below which a map stops being worth showing,
	 * and the ceiling keeps it from swelling past the rest of the composition.
	 *
	 * This is the one number to turn if the panel should be larger or smaller
	 * overall — the middle term is the whole control.
	 */
	.kbf-visit-modal__map {
		flex: 1 1 auto;
		aspect-ratio: auto;
		height: clamp(96px, 20dvh, 200px);
		/*
		 * Stated separately because the clamp's lower bound is not a floor here:
		 * a flex item's default min-height resolves to its content, and an empty
		 * iframe wrapper has none, so flex-shrink is free to squeeze this to a
		 * sliver on a short screen. This is what actually holds the floor.
		 */
		min-height: 96px;
	}

	/*
	 * Side by side on a phone, against the stacked pair the wider breakpoint
	 * asks for. Stacked, this bar was 168px — a quarter of the modal — for two
	 * buttons, and that is what pushed the panel to the edges of the screen with
	 * no strip of page left around it. In one row it is 76px, and the modal ends
	 * up nearer the Order modal's size: a panel over the page rather than a
	 * takeover of it.
	 *
	 * flex-basis 0 rather than the wider breakpoint's 240px is what puts them on
	 * one row at all; min-width:0 lets them share the width evenly instead of
	 * each insisting on its label. 15px and tighter side padding are what keep
	 * both labels on one line at 360px, the narrowest phone worth designing for.
	 */
	.kbf-visit-modal__cta {
		padding: var(--kbf-space-xs);
		gap: var(--kbf-space-xxs);
		flex-wrap: nowrap;
		/*
		 * The CTA's label is not fixed — it follows the channel state and whether
		 * the visitor is signed in — so one of the pair can always turn out to be
		 * the longer. Stretching means that if it does wrap, both buttons match
		 * instead of one standing a line taller than the other.
		 */
		align-items: stretch;
	}

	.kbf-visit-modal__cta .kbf-btn {
		flex: 1 1 0;
		min-width: 0;
		padding-inline: var(--kbf-space-xxs);
		font-size: 15px;
	}
}

/* ==========================================================================
   Order in Advance modal (global)
   ==========================================================================
   Opened from the "Order in Advance" control in the primary navigation, over
   whatever page the visitor is on. It shares the shell above — backdrop,
   dialog, close control — and adds the two-panel composition.

   Unlike the Visit modal this one genuinely reflows rather than being scaled:
   it is type and two buttons with no fixed-ratio element in it, so every width
   can be given the layout that actually suits it. Design tokens are the Figma
   variables for the "Order in Advance Modal" frame; every value below
   references a custom property already defined at the top of this file,
   including the divider rule (--kbf-border-dark is exactly the design's
   #46443E at 20%, on both panels as drawn).
   ========================================================================== */

/* The design's 1275 x 470: two equal halves, the dialog's own overflow:hidden
   clipping them to its rounded corners so neither panel carries a radius. */
.kbf-order-modal__dialog {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	/* Corners still clip — any non-visible overflow honours border-radius — while
	   a window too short for the composition scrolls instead of cropping it. */
	overflow: hidden auto;
}

/*
 * 96px top and bottom and 107px either side at the design's width, easing down
 * to something a phone can carry. The panel is the padding box for its half, so
 * the two halves stay exactly equal whatever the copy does.
 */
.kbf-order-modal__panel {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-m);
	padding: clamp(40px, 6.7vw, 96px) clamp(24px, 7.5vw, 107px);
	text-align: center;
	color: var(--kbf-text-primary);
}

.kbf-order-modal__panel--brand {
	background: var(--kbf-barn-red);
	color: var(--kbf-text-secondary);
}

.kbf-order-modal__head {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-xs);
}

/* Colour is the panel's, not the heading's — the same markup sits on cream and
   on barn red. */
.kbf-order-modal__title {
	color: inherit;
}

/* Figma "Label Medium": 14px bold, 0.1em tracking (the design's 1.4px at 14px). */
.kbf-order-modal__range {
	margin: 0;
	font-weight: 700;
	font-size: 14px;
	line-height: 1.2;
	color: inherit;
}

/*
 * The divider is the body's own top border rather than a separate element: the
 * panel's 32px gap sits above it and this padding repeats it below, which is
 * the design's 91 / 123 / 155 rhythm exactly.
 */
.kbf-order-modal__body {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--kbf-space-s);
	padding-top: var(--kbf-space-m);
	border-top: 1px solid var(--kbf-border-dark);
}

/* Figma "Body Large", held to the design's 304px measure so the two lines break
   where they were drawn. */
.kbf-order-modal__copy {
	max-width: 304px;
	margin: 0;
	font-weight: 400;
	font-size: 18px;
	line-height: 1.56;
	color: inherit;
}

/* The address is the label, so it has to survive a narrow phone rather than
   push the dialog sideways. */
.kbf-order-modal__email {
	max-width: 100%;
	word-break: break-word;
}

.kbf-order-modal__close {
	position: absolute;
	top: var(--kbf-space-s);
	right: var(--kbf-space-s);
}

/* ---------- Order modal: responsive ---------- */

/*
 * The email address is the longest unbreakable thing in the modal, and it runs
 * out of room well before the layout does — while the panels are still side by
 * side. Shrinking the type and giving the button back its side padding is what
 * keeps it on one line; letting it wrap breaks it mid-address, which is worse
 * than a point of type.
 */
@media (max-width: 1100px) {
	.kbf-order-modal__email {
		padding-inline: var(--kbf-space-xs);
		font-size: 16px;
	}
}

/*
 * Stacked before the two columns get cramped. Below roughly 900px each half is
 * under 200px of usable measure once the panel padding is paid, which breaks a
 * 48px title and the email address well before it breaks the layout.
 */
@media (max-width: 900px) {
	.kbf-order-modal__dialog {
		grid-template-columns: minmax(0, 1fr);
	}

	/*
	 * Stacked, the top-right corner belongs to the cream panel, so the inverted
	 * close control would be cream on cream. Back to the default treatment.
	 */
	.kbf-modal__close--cream {
		background: var(--kbf-barn-red);
		color: var(--kbf-text-secondary);
	}

	.kbf-modal__close--cream:hover {
		background: var(--kbf-barn-red-dark);
	}

	/*
	 * Stacked, the first heading shares its top corner with the close control,
	 * and "Cinnamon rolls pre-order" is long enough to run underneath it once
	 * the panel padding narrows. Inset on both sides, so the heading stays
	 * centred on the panel rather than on what is left of it.
	 */
	.kbf-order-modal__panel:first-of-type .kbf-order-modal__title {
		padding-inline: var(--kbf-space-s);
	}
}

@media (max-width: 560px) {
	/*
	 * Stacked on a phone the two panels are read one after the other, so any
	 * difference in their padding reads as a mistake rather than as emphasis —
	 * they are kept symmetrical. The close control shares the first panel's top
	 * corner but never its centred text: it moves tight into the corner, and the
	 * heading's inset above keeps the text clear of it.
	 *
	 * The design's 96px of breathing room is a desktop proportion. Held here it
	 * costs about 130px of height, which is the difference between the whole
	 * modal fitting a phone and the visitor having to scroll it to find out
	 * there was a second option at all.
	 */
	.kbf-order-modal__panel {
		gap: var(--kbf-space-xs);
		padding: var(--kbf-space-s);
	}

	.kbf-order-modal__close {
		top: var(--kbf-space-xxs);
		right: var(--kbf-space-xxs);
	}

	.kbf-order-modal__head {
		gap: var(--kbf-space-xxs);
	}

	.kbf-order-modal__body {
		gap: var(--kbf-space-xs);
		padding-top: var(--kbf-space-xs);
	}

	/* 18px is a desktop measure. At 16px the same two lines cost 12px less per
	   panel, which is real height on a phone and no loss of legibility. */
	.kbf-order-modal__copy {
		font-size: 16px;
	}

	/* One step smaller again, which holds the address on one line down to about a
	   340px viewport. Below that word-break above catches it rather than letting
	   it push the dialog sideways. */
	.kbf-order-modal__email {
		font-size: 15px;
	}
}

/* ---------- Terms of Sale — Advance Orders (page-terms-of-sale.php) ---------- */

/*
 * Laid out to the frame: both the title block and the clause stack run the
 * full width between the 80px gutters, which is what .kbf-container already
 * provides. Nothing here caps the measure — an earlier version held the text
 * to 72ch for readability, which on a wide monitor left it hugging the left
 * edge with half the page empty. The design puts the copy across the full
 * column, so that is what this does.
 */

/*
 * A wider floor on the side gutter, for this template only.
 *
 * The shared --kbf-gutter is clamp(20px, 5.5vw, 80px). That 5.5% is the frame's
 * 80px-at-1440 expressed proportionally, and it is right for the pages it was
 * written for — they break their width up with photographs, cards and buttons,
 * so nothing sits against the edge for long. This page is eleven dense
 * paragraphs and nothing else, so every line begins and ends within ~21px of
 * the screen edge on a phone, which reads as text spilling off the display even
 * though it measures as fitting. Raising the floor to 24px (and the slope to
 * 6vw so it reaches the floor smoothly) buys back the margin.
 *
 * Set as the custom property on the page rather than as padding on the content,
 * so the masthead, the clauses and the footer all keep reading the same value
 * and stay aligned to one another. The clamp still lands on exactly 80px at
 * 1440px and above, so the desktop rendering is untouched and still matches the
 * frame to the pixel.
 */
.page-template-page-terms-of-sale {
	--kbf-gutter: clamp(24px, 6vw, 80px);
}

.kbf-terms__content {
	/* 64/96 from the frame, scaled down proportionally on small screens rather
	   than held — a phone spends that top padding on nothing. */
	padding-block: clamp(40px, 5.5vw, 64px) clamp(64px, 8vw, 96px);
	/* The frame sets leading-[normal] on this whole block, which in Josefin Sans
	   is a notably tight ~1.05. The body default is 1.6, so without this the
	   clauses render looser than the design and every paragraph is a line taller
	   than it should be. Inherited by the headings and paragraphs below. */
	line-height: normal;
	/* Nothing in the copy is long enough to overflow today — the widest
	   unbreakable token is the wholesale address at 242px, against 272px of
	   room on a 320px phone. This is so it stays that way: an address or a URL
	   added to a clause later cannot push the column past the screen edge. */
	overflow-wrap: break-word;
}

.kbf-terms__inner {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-l);
	/*
	 * Measured from the viewport edge, like the masthead — not from
	 * .kbf-container's 1440px cap.
	 *
	 * .kbf-header is full-bleed: no max-width, just --kbf-gutter of padding on
	 * each side. .kbf-container caps at --kbf-max and centres. Below 1440px the
	 * cap is never reached and the two agree, which is why this held for the
	 * frame; above it the masthead keeps opening while the text stops, so at
	 * 1800px the clauses sat 172px inside the wordmark on either side and the
	 * page read as two documents stacked rather than one.
	 *
	 * Dropping the cap leaves the gutter as the only thing setting either edge,
	 * so the title starts under the wordmark and the clauses end under the order
	 * CTA at every width. Safe to run unbounded here specifically: this page is
	 * one column of prose with no grid or media to stretch, and the block above
	 * already records that the design runs the copy the full width between the
	 * gutters rather than capping the measure.
	 */
	max-width: none;
}

.kbf-terms__head {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

/* The shared eyebrow at this frame's measurements: 14px, 0.08em, semibold,
   barn red. The base .kbf-eyebrow carries the family, transform and weight
   baseline; only what this frame sets differently is restated. */
.kbf-terms__eyebrow {
	margin: 0;
	font-size: 14px;
	font-weight: 600;
	letter-spacing: 0.08em;
	color: var(--kbf-barn-red);
}

/*
 * 44px in the frame. Not .kbf-heading (48px) or .kbf-display (64px): those are
 * marketing scales, and this heading sits directly above body copy rather than
 * over a photograph. Floored at 30px so it stays a clear step above the 18px
 * clause headings on a phone.
 */
.kbf-terms__title {
	margin: 0;
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: clamp(30px, 4.6vw, 44px);
	line-height: normal;
	color: var(--kbf-text-primary);
}

.kbf-terms__standfirst {
	margin: 0;
	font-size: 16px;
	/* The frame's 70%, as a colour rather than opacity: opacity would fade the
	   links a clause can contain along with the text around them. */
	color: color-mix(in srgb, var(--kbf-text-primary) 70%, transparent);
}

.kbf-terms__sections {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-m);
}

.kbf-terms__section {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-xxs);
	/* The wizard links to individual clauses; without this an anchored jump
	   lands the heading underneath the masthead. */
	scroll-margin-top: var(--kbf-space-l);
}

.kbf-terms__section-title {
	margin: 0;
	/* Body family, not the title serif — the frame sets these as Josefin
	   SemiBold 18px, which keeps eleven headings from reading as eleven
	   separate pages. */
	font-family: var(--kbf-font-body);
	font-weight: 600;
	font-size: 18px;
	/* Restated rather than inherited: the base h1–h3 rule sets 1.2, which wins
	   over the leading-normal on the content block above and leaves each clause
	   heading sitting ~3px lower in its line box than the frame has it. */
	line-height: normal;
	color: var(--kbf-text-primary);
}

.kbf-terms__section-body {
	margin: 0;
	font-size: 15px;
	color: color-mix(in srgb, var(--kbf-text-primary) 75%, transparent);
}

/* Contact details inside a clause. Underlined rather than colour-only: these
   are the addresses someone uses to cancel an order, and they have to be
   findable by anyone skim-reading or colour-blind. */
.kbf-terms__link {
	color: var(--kbf-barn-red);
	text-decoration: underline;
	text-underline-offset: 2px;
	/* An email address is one long unbreakable token; on a narrow phone it
	   would otherwise push the whole column sideways. */
	word-break: break-word;
}

.kbf-terms__link:hover,
.kbf-terms__link:focus-visible {
	color: var(--kbf-barn-red-dark);
}

/* The way back to the wizard, which links here from its Terms and Payment
   steps. Same treatment as the wizard's own "← Back" controls
   (.kbf-preorder-back), so going there and coming back read as one flow. */
.kbf-terms__back {
	align-self: flex-start;
	margin-bottom: var(--kbf-space-xs);
	color: var(--kbf-chalkboard-80);
	font-family: var(--kbf-font-body);
	font-size: 14px;
	line-height: 1.4;
	text-decoration: none;
}

.kbf-terms__back:hover,
.kbf-terms__back:focus-visible {
	color: var(--kbf-barn-red);
}

@media (max-width: 560px) {
	/*
	 * At phone width the 40px gap between the title block and the clauses, and
	 * the 32px between clauses, are most of what the reader scrolls past. One
	 * step down on each keeps the whole document about a screen shorter without
	 * the clauses running together — the 8px inside each clause is untouched, so
	 * a heading still belongs visibly to the paragraph under it.
	 */
	.kbf-terms__inner {
		gap: var(--kbf-space-m);
	}

	.kbf-terms__sections {
		gap: var(--kbf-space-s);
	}

	/* 15px is a desktop measure for a document read at arm's length. At 16px the
	   clauses are legible on a phone held closer, and the 18px headings still
	   read as headings.

	   The frame's tight leading is a desktop proportion too: at 1280px a clause
	   is two lines, where on a phone the same clause is eight or nine stacked at
	   ~1.05, which reads as a block rather than as lines. There is no mobile
	   frame to be 1:1 with, so the leading opens up here and only here — the
	   desktop rendering still matches the design exactly. */
	.kbf-terms__section-body,
	.kbf-terms__standfirst {
		font-size: 16px;
		line-height: 1.5;
	}
}

/* ---------- Auth: sign-in (page-login.php) ---------- */

/*
 * The frame is a single card centred on the cream page ground, between the
 * standard masthead and footer. 80px of air above it and 100px below, which is
 * what gives the card its weight on a 1440px screen; both collapse on a phone,
 * where the card is most of the viewport and that much padding is just scroll.
 */
.kbf-auth {
	display: flex;
	justify-content: center;
	/* The card sizes to its contents. Stated rather than left to the default
	   stretch, because the rule below lets this box grow past its contents and
	   a stretched card would grow with it. */
	align-items: flex-start;
	padding: 80px var(--kbf-gutter) 100px;
	background: var(--kbf-surface-primary);
}

/*
 * Keep the footer at the bottom of the window on the auth pages.
 *
 * These are the shortest pages on the site — the reset-request card is a
 * single field — and on a tall phone the masthead, card and footer together
 * come up short of the viewport. The footer then stops where its content
 * stops and the cream page ground shows beneath it as a pale strip, which
 * reads as a rendering fault rather than as the end of the page. Measured at
 * 430x1000: 46px of it.
 *
 * Scoped to pages that actually carry .kbf-auth, so no other template's
 * layout is touched — the marketing pages are all far taller than any
 * viewport and have never had the problem. Where :has() is unsupported the
 * page renders exactly as it does today, which is the right way for this to
 * fail.
 *
 * svh rather than vh: on mobile Safari and Chrome vh is the viewport with the
 * browser chrome *hidden*, so sizing to it would leave the footer pushed
 * below the fold until the address bar collapses — trading a gap for a
 * scrollbar on a page that should have neither.
 */
body:has(.kbf-auth) {
	display: flex;
	flex-direction: column;
	min-height: 100svh;
}

body:has(.kbf-auth) .kbf-auth {
	flex: 1;
}

.kbf-auth-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	/* Every child of the card sits 24px from the next in the frame — the
	   heading, the standfirst, the form and the sign-up row alike. One gap
	   states that once; the two places that differ (the field pair, and the
	   card's own 44px top and bottom) say so explicitly below. */
	gap: var(--kbf-space-s);
	width: 100%;
	max-width: 480px;
	padding: 44px var(--kbf-space-l);
	border-radius: 20px;
	background: var(--kbf-surface-secondary);
	text-align: center;
}

.kbf-auth-card__eyebrow {
	margin: 0;
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.08em;
	color: var(--kbf-barn-red);
}

.kbf-auth-card__title {
	margin: 0;
	font-family: var(--kbf-font-title);
	font-weight: 700;
	font-size: 32px;
	line-height: 1.2;
	color: var(--kbf-text-primary);
}

.kbf-auth-card__standfirst {
	margin: 0;
	font-size: 14px;
	line-height: 1.4;
	color: var(--kbf-text-primary);
	opacity: 0.65;
}

/*
 * The sign-up standfirst is one line in the frame even though it measures
 * 454px — 54px wider than the card's 400px content box, bleeding 27px into
 * each 40px of padding. That is the design as drawn, and it is reproduced
 * here rather than quietly wrapped, because the single line is what gives the
 * card its proportions above the five fields.
 *
 * Only while there is room for it. Below the breakpoint the card is narrower
 * than the sentence and the frame offers no answer, so the text wraps — the
 * card clips its overflow, and a clipped sentence is worse than a wrapped one.
 */
@media (min-width: 561px) {
	.kbf-auth-card__standfirst--wide {
		white-space: nowrap;
	}
}

/*
 * Neither of these appears in the frame, because the frame cannot show a form
 * that has been submitted. They take the card's own width so a two-line message
 * does not re-centre the block it sits above.
 */
.kbf-auth-card__error,
.kbf-auth-card__notice {
	width: 100%;
	margin: 0;
	padding: 12px var(--kbf-space-xs);
	border-radius: var(--kbf-radius-s);
	font-size: 14px;
	font-weight: 600;
	line-height: 1.4;
	text-align: left;
}

.kbf-auth-card__error {
	color: var(--kbf-barn-red);
	/* Tinted rather than solid barn red: this is a mistyped password, not a
	   failed payment, and a loud panel on a calm card overstates it. */
	background: rgba(189, 84, 95, 0.1);
}

/* The sign-up form validates five fields at once and can report several
   problems together, so the panel holds a list rather than a sentence. */
.kbf-auth-card__error-line {
	margin: 0;
}

.kbf-auth-card__error-line + .kbf-auth-card__error-line {
	margin-top: 6px;
}

.kbf-auth-card__notice {
	color: var(--kbf-text-primary);
	background: var(--kbf-surface-primary);
}

.kbf-auth-card__form {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--kbf-space-s);
	width: 100%;
}

.kbf-auth-card__fields {
	display: flex;
	flex-direction: column;
	gap: 18px;
	width: 100%;
}

.kbf-auth-field {
	display: flex;
	flex-direction: column;
	gap: var(--kbf-space-xxs);
	width: 100%;
	text-align: left;
}

.kbf-auth-field__label {
	font-family: var(--kbf-font-body);
	font-size: 13px;
	font-weight: 600;
	line-height: 1;
	color: var(--kbf-text-primary);
	opacity: 0.75;
}

/* Why a field is being asked for, under the field. Sits in the block's own
   8px rhythm, so it needs no margin of its own — only the sign-up form's
   Mobile Number uses one. */
.kbf-auth-field__hint {
	margin: 0;
	width: 100%;
	font-size: 12.5px;
	line-height: 1.4;
	color: var(--kbf-text-primary);
	opacity: 0.55;
}

.kbf-auth-field__input {
	width: 100%;
	height: 52px;
	padding: 0 var(--kbf-space-xs);
	font-family: var(--kbf-font-body);
	font-size: 15px;
	color: var(--kbf-text-primary);
	/* Cream field on the card's warmer ground. The frame's border token is the
	   card colour itself, so the edge reads as a seam rather than a rule —
	   which is why the focus state below has to supply a visible one. */
	background: var(--kbf-surface-primary);
	border: 1px solid var(--kbf-surface-secondary);
	border-radius: var(--kbf-radius-s);
}

/*
 * Mobile Number as two controls on one row: the country dropdown, then the
 * number. The dropdown takes what its text needs up to a cap and the number
 * takes the rest — a long country name is clipped in the closed control
 * rather than squeezing the field people actually type into.
 */
.kbf-phone-field {
	display: flex;
	gap: var(--kbf-space-xxs);
	width: 100%;
}

/* Same chevron and treatment as the wizard's time select, so every dropdown
   on the site reads as one control. */
.kbf-phone-field__country {
	flex: 0 1 auto;
	width: auto;
	max-width: 60%;
	min-width: 0;
	padding-right: 36px;
	appearance: none;
	background-image: url("img/chevron-down.svg");
	background-repeat: no-repeat;
	background-position: right 14px center;
	background-size: 14px 14px;
	text-overflow: ellipsis;
	cursor: pointer;
}

.kbf-phone-field__number {
	flex: 1 1 0;
	min-width: 0;
}

/* A phone's panel is under 300px of measure, which clips both the country and
   the number side by side. Stacked, each gets the full width. */
@media (max-width: 480px) {
	.kbf-phone-field {
		flex-direction: column;
	}

	.kbf-phone-field__country {
		flex: none;
		width: 100%;
		max-width: none;
	}

	.kbf-phone-field__number {
		flex: none;
	}
}

.kbf-auth-field__input::placeholder {
	color: var(--kbf-text-primary);
	opacity: 0.45;
}

.kbf-auth-field__input:focus-visible {
	outline: 2px solid var(--kbf-barn-red);
	outline-offset: 1px;
}

.kbf-auth-card__link {
	font-family: var(--kbf-font-body);
	font-weight: 600;
	color: var(--kbf-barn-red);
}

.kbf-auth-card__link:hover,
.kbf-auth-card__link:focus-visible {
	color: var(--kbf-barn-red-dark);
	text-decoration: underline;
}

.kbf-auth-card__forgot {
	font-size: 13.5px;
	line-height: 1;
}

.kbf-auth-card__submit {
	width: 100%;
}

.kbf-auth-card__switch {
	margin: 0;
	font-size: 14px;
	line-height: 1.4;
}

.kbf-auth-card__switch-text {
	/* 6px in the frame; a normal word space between the sentence and the link
	   is close enough and survives the row wrapping on a narrow phone. */
	margin-right: 6px;
	color: var(--kbf-text-primary);
	opacity: 0.7;
}

/* ---------- Auth: responsive ---------- */

@media (max-width: 560px) {
	.kbf-auth {
		padding: var(--kbf-space-l) var(--kbf-gutter) var(--kbf-space-2xl);
	}

	/*
	 * The card loses its side inset and most of its vertical padding: at 360px
	 * the 40px gutters plus 40px card padding leave the inputs under 200px
	 * wide, which is narrower than the address they have to hold.
	 */
	.kbf-auth-card {
		padding: var(--kbf-space-m) var(--kbf-space-s);
	}

	.kbf-auth-card__title {
		font-size: 28px;
	}
}
