/* ==========================================================================
   Koh Tomyums — "Gilt"
   Design tokens, base, header, footer, front page, editorial.
   Colours are authored in oklch with a hex fallback on the line above, so
   browsers without oklch support keep the earlier declaration.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
	/*
	 * Ground
	 */
	--kt-ground: #0d0c0a;
	--kt-ground: oklch(0.09 0.005 60);
	--kt-ground-2: #100e0c;
	--kt-ground-2: oklch(0.10 0.006 60);
	--kt-ground-3: #14120f;
	--kt-ground-3: oklch(0.11 0.006 60);

	/* Panel fills — translucent so the veiled photograph reads through */
	--kt-panel: rgba(23, 20, 16, 0.55);
	--kt-panel: oklch(0.14 0.008 62 / 0.55);
	--kt-panel-top: rgba(26, 23, 18, 0.55);
	--kt-panel-top: oklch(0.15 0.010 62 / 0.55);
	--kt-panel-bottom: rgba(19, 17, 14, 0.66);
	--kt-panel-bottom: oklch(0.12 0.006 60 / 0.66);
	--kt-panel-blur: blur(3px);

	/* Field fill — every text input, select and textarea, theme and block
	   checkout alike. Was written out three times as a literal. */
	--kt-field: rgba(19, 17, 14, 0.6);
	--kt-field: oklch(0.12 0.006 60 / 0.6);

	/* Gold */
	--kt-gold: #d7a75c;
	--kt-gold: oklch(0.80 0.11 85);
	/*
	 * Alias, not a colour. Measured against gold-hi this was 1.05:1 — a
	 * difference no eye can see that nevertheless split 47 declarations from
	 * gold-hi's 35. One bright gold, one name that survives for compatibility.
	 */
	--kt-gold-bright: var(--kt-gold-hi);
	--kt-gold-hi: #f1c072;
	--kt-gold-hi: oklch(0.88 0.11 85);
	/* Alias: 1.07:1 from gold — indistinguishable, so it IS gold now. */
	--kt-gold-soft: var(--kt-gold);
	--kt-gold-dim: #b88c46;
	--kt-gold-dim: oklch(0.70 0.10 85);
	--kt-gold-line: #9e7739;
	--kt-gold-line: oklch(0.62 0.09 85);
	--kt-gold-line-2: #8a682f;
	--kt-gold-line-2: oklch(0.55 0.08 85);
	/* The cart-link / drawer frame weight — was a literal in three places. */
	--kt-gold-line-mid: #6b5225;
	--kt-gold-line-mid: oklch(0.45 0.06 85);
	--kt-gold-line-soft: #7a5b2b;
	--kt-gold-line-soft: oklch(0.50 0.07 85);
	--kt-gold-line-faint: #56421f;
	--kt-gold-line-faint: oklch(0.36 0.04 85);

	/* Rules & frames — --kt-gold-rule is the name used in the developer handoff */
	--kt-gold-rule: rgba(110, 84, 42, 0.65);
	--kt-gold-rule: oklch(0.42 0.06 85 / 0.65);
	--kt-frame: var(--kt-gold-rule);
	--kt-frame-soft: rgba(80, 63, 35, 0.7);
	--kt-frame-soft: oklch(0.34 0.04 85 / 0.7);
	--kt-hairline: rgba(66, 55, 36, 0.6);
	--kt-hairline: oklch(0.30 0.03 85 / 0.6);
	--kt-hairline-faint: rgba(55, 47, 34, 0.6);
	--kt-hairline-faint: oklch(0.26 0.02 85 / 0.6);
	--kt-divider: #3d3733;
	--kt-divider: oklch(0.26 0.015 85);

	/* Text */
	--kt-text: #dcd7cf;
	--kt-text: oklch(0.86 0.015 88);
	--kt-text-bright: #f2ece2;
	--kt-text-bright: oklch(0.93 0.02 88);
	--kt-text-hero: #f7f3ec;
	--kt-text-hero: oklch(0.96 0.01 88);
	--kt-text-body: #cfc9c1;
	--kt-text-body: oklch(0.80 0.015 88);
	/* --kt-text-dim is the handoff's name for secondary label / description text */
	--kt-text-dim: #b2aba0;
	--kt-text-dim: oklch(0.70 0.015 88);
	/* Alias: 1.03:1 from text-dim. The ladder is honest at five steps. */
	--kt-text-quiet: var(--kt-text-dim);
	--kt-text-muted: #a8a29a;
	--kt-text-muted: oklch(0.68 0.015 88);
	--kt-text-faint: #948e86;
	--kt-text-faint: oklch(0.64 0.015 88);
	--kt-text-meta: #97846a;
	--kt-text-meta: oklch(0.64 0.03 85);
	/* Unavailable / switched-off label: sold-out slots, disabled chips.
	   Was duplicated as --kt-res-off and --kty-slot-off in two part files. */
	--kt-text-off: #56514a;
	--kt-text-off: oklch(0.44 0.015 88);

	/* Chili pips — --kt-chili is the handoff's name for the lit pip */
	--kt-chili: #d9532f;
	--kt-chili: oklch(0.62 0.19 32);
	--kt-chili-on: var(--kt-chili);
	/* Unlit pip: an ember, not a ghost. The grayscale-emoji treatment this
	   replaces measured ~1.3:1 — "2 of 3" read as "two chillies, full stop". */
	--kt-chili-off: #77463c;
	--kt-chili-off: oklch(0.45 0.07 32);
	/*
	 * SEMANTIC: the rest border of anything focusable. gold-line-faint (2.04:1)
	 * failed the 3:1 non-text minimum on every input, chip and picker on the
	 * money paths; gold-line-soft passes at 3.13:1. Decorative hairlines keep
	 * using gold-line-faint directly — this token is what separates the roles.
	 */
	--kt-border-control: var(--kt-gold-line-soft);

	/* Ink on gold */
	--kt-on-gold: #1c1916;
	--kt-on-gold: oklch(0.15 0.01 60);

	/* Status */
	/*
	 * The herb green — the palette's one supporting tone, drawn from the
	 * cuisine rather than from a framework default. Three jobs only: success
	 * states, the vegetarian marker, and nothing else. hue 130 sits close
	 * enough to the warm room not to jar, far enough to be unmistakably green.
	 */
	--kt-herb: #8eb267;
	--kt-herb: oklch(0.72 0.11 130);
	--kt-herb-line: #475d3b;
	--kt-herb-line: oklch(0.45 0.06 135);
	--kt-ok: var(--kt-herb);
	/*
	 * A true orange, not another gold. The old #e0a83c measured 1.03:1 against
	 * the brand gold — a warning dressed as an ornament. 25 hue-degrees and a
	 * visible chroma step now separate "alert" from "champagne"; 7.6:1 on the
	 * ground keeps its text duties AA.
	 */
	--kt-warn: #e78b30;
	--kt-warn: oklch(0.72 0.15 60);
	/*
	 * Crimson-leaning, deliberately 12 hue-degrees off the chilli's orange-red
	 * (#d9532f, hue 32): a plate can carry "spicy" and "something went wrong"
	 * at once, and the two must never read as one red.
	 */
	--kt-err: #d9525a;
	--kt-err: oklch(0.62 0.17 20);

	/* Type */
	--kt-font-body: "Manrope", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
	--kt-font-display: "Cinzel", "Times New Roman", Georgia, serif;
	--kt-font-script: "Great Vibes", "Snell Roundhand", cursive;

	/* Layout */
	--kt-max: 1440px;
	--kt-gutter: clamp(20px, 5vw, 64px);
	--kt-stack: clamp(24px, 3.4vw, 34px);
	/*
	 * Header height. This token IS the height — `.kt-header` takes it as a
	 * min-height and carries no vertical padding — so the five things that
	 * measure against it (the drop-down's max-height, the drawer and search
	 * panel's top inset, and two scroll offsets) are exact at every width.
	 *
	 * It used to be the other way round: the height emerged from
	 * `padding: clamp(10px, 1.2vw, 16px)` on a 44px content floor, and this
	 * token restated the answer by hand. That only agreed at the widest
	 * viewports — at 950px the real header was ~67px while the token still said
	 * 76, so the drawer opened 9px below the bar it is supposed to hang from.
	 *
	 * The floor is `.kt-brand`'s 44px tap target (WCAG 2.5.5), so these values
	 * cannot go below 44 without shrinking the target itself.
	 */
	--kt-header-h: 56px;

	/*
	 * Radius scale — 4 / 8 / 12, plus one true pill.
	 *
	 * One step of 4px between tiers, so the difference reads as a decision
	 * rather than as drift. Nothing in the theme, the parts or the block
	 * checkout may hard-code a corner: every `border-radius` resolves to one of
	 * these four. The only literal `0`s left are the inner edges of joined
	 * groups (a search field welded to its button, the qty input inside its
	 * frame), where the group itself carries the radius.
	 *
	 * Kept small on purpose. Gilt is hairline gold frames and Cinzel caps; a
	 * soft corner reads as a different brand.
	 */
	--kt-radius-sm: 4px;      /* buttons, inputs, chips, notices, small controls */
	--kt-radius: 8px;         /* cards and panels */
	--kt-radius-lg: 12px;     /* large feature panels */
	--kt-radius-pill: 999px;  /* genuine pills and circular marks only */

	/*
	 * Motion.
	 *
	 * The two originals are duration+easing shorthands used as
	 * `transition: color var(--kt-ease)`. The additions below are the vocabulary
	 * for anything larger than a colour change.
	 *
	 * The curve is easeOutQuint: it leaves fast and settles slowly, which reads
	 * as weight rather than speed. Nothing here overshoots or bounces — a bounce
	 * is playful, and this room is not. Durations are long enough to be felt and
	 * short enough never to be waited for.
	 *
	 * `--kt-ease-out` is already referenced (with this exact fallback) by the
	 * order-status animations in parts/confirmation.css.
	 */
	--kt-ease: 180ms ease;
	--kt-ease-slow: 200ms ease;
	--kt-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
	--kt-ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
	--kt-motion-hover: 320ms cubic-bezier(0.22, 1, 0.36, 1);
	--kt-motion-reveal: 760ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* --------------------------------------------------------------------------
   2. Reset & base
   -------------------------------------------------------------------------- */

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

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

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

	/*
	 * The universal flattener below zeroes transition-DURATION but not DELAY
	 * (same catch the order-status track documents). The drawer and search
	 * panel stage `visibility 0s 200ms` exits, which under reduced motion
	 * would leave them invisible-but-present for 200ms — so their transitions
	 * are cancelled outright, delay included.
	 */
	.kt-drawer,
	.kt-drawer__body,
	.kt-search-panel {
		transition: none;
	}

	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

body {
	margin: 0;
	overflow-x: hidden;
	background-color: var(--kt-ground);
	color: var(--kt-text);
	font-family: var(--kt-font-body);
	font-size: 15px;
	font-weight: 300;
	line-height: 1.7;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

/* The veiled backdrop: a dark food photograph under a ~95% black wash.
   The image is set inline from the Customizer; this is the wash over it. */
.kt-site {
	position: relative;
	min-height: 100vh;
	background-color: var(--kt-ground);
	background-repeat: no-repeat;
	background-position: center top;
	background-size: cover;
	background-attachment: scroll;
}

.kt-site::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background: linear-gradient(
		to bottom,
		rgba(13, 12, 10, 0.90) 0%,
		rgba(13, 12, 10, 0.955) 20%,
		rgba(13, 12, 10, 0.965) 55%,
		rgba(13, 12, 10, 0.945) 100%
	);
	background: linear-gradient(
		to bottom,
		oklch(0.09 0.005 60 / 0.90) 0%,
		oklch(0.09 0.005 60 / 0.955) 20%,
		oklch(0.09 0.005 60 / 0.965) 55%,
		oklch(0.09 0.005 60 / 0.945) 100%
	);
}

.kt-site > * {
	position: relative;
	z-index: 1;
}

/* No backdrop image chosen — keep it off flat black with a faint warm wash. */
.kt-site.is-plain::before {
	background: radial-gradient(120% 80% at 50% 0%, rgba(60, 46, 26, 0.22), transparent 60%);
	background: radial-gradient(120% 80% at 50% 0%, oklch(0.28 0.04 70 / 0.22), transparent 60%);
}

img {
	max-width: 100%;
	height: auto;
	border: 0;
}

svg {
	display: block;
}

a {
	color: inherit;
	text-decoration: none;
	transition: color var(--kt-ease);
}

a:hover {
	color: var(--kt-gold-bright);
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
	outline: 2px solid var(--kt-gold-soft);
	outline-offset: 3px;
}

button {
	font: inherit;
	color: inherit;
}

h1, h2, h3, h4, h5, h6 {
	margin: 0;
	font-weight: 400;
	line-height: 1.2;
}

p {
	margin: 0 0 1.1em;
	text-wrap: pretty;
}

p:last-child {
	margin-bottom: 0;
}

ul, ol {
	margin: 0 0 1.1em;
	padding-left: 1.3em;
}

hr {
	border: 0;
	border-top: 1px solid var(--kt-hairline);
	margin: 2.4em 0;
}

blockquote {
	margin: 1.6em 0;
	padding-left: 1.4em;
	border-left: 1px solid var(--kt-gold-line);
	color: var(--kt-text-bright);
	font-size: 1.05em;
}

code, pre, kbd, samp {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 0.92em;
}

pre {
	overflow-x: auto;
	padding: 18px 20px;
	border: 1px solid var(--kt-frame-soft);
	border-radius: var(--kt-radius);
	background: var(--kt-panel);
}

table {
	width: 100%;
	border-collapse: collapse;
}

::selection {
	background: var(--kt-gold);
	color: var(--kt-on-gold);
}

/* Skip link */
.kt-skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 999;
	padding: 14px 24px;
	background: var(--kt-gold);
	color: var(--kt-on-gold);
	font-size: 12px;
	font-weight: 800;
	letter-spacing: 0.2em;
	text-transform: uppercase;
}

.kt-skip-link:focus {
	left: 12px;
	top: 12px;
}

.screen-reader-text {
	position: absolute !important;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.screen-reader-text:focus {
	position: static !important;
	width: auto;
	height: auto;
	clip: auto;
	clip-path: none;
	white-space: normal;
}

/* --------------------------------------------------------------------------
   3. Layout primitives
   -------------------------------------------------------------------------- */

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

.kt-section {
	max-width: var(--kt-max);
	margin: var(--kt-stack) auto 0;
	padding-inline: var(--kt-gutter);
}

.kt-section--flush {
	margin-top: 0;
}

.kt-section--anchor {
	scroll-margin-top: 100px;
}

/* Bordered translucent panel — the workhorse frame */
.kt-panel {
	border: 1px solid var(--kt-frame);
	border-radius: var(--kt-radius-lg);
	padding: clamp(30px, 4vw, 44px) clamp(20px, 3.6vw, 48px);
}

.kt-panel--fill {
	background: linear-gradient(to bottom, var(--kt-panel-top), var(--kt-panel-bottom));
	-webkit-backdrop-filter: var(--kt-panel-blur);
	backdrop-filter: var(--kt-panel-blur);
	padding: clamp(34px, 5vw, 54px) clamp(18px, 3.4vw, 48px) clamp(32px, 4.6vw, 48px);
}

.kt-panel--media {
	position: relative;
	overflow: hidden;
	padding: 0;
}

/* --------------------------------------------------------------------------
   4. Type helpers
   -------------------------------------------------------------------------- */

.kt-display {
	font-family: var(--kt-font-display);
	letter-spacing: 0.16em;
	color: var(--kt-gold-soft);
}

.kt-script {
	font-family: var(--kt-font-script);
	color: var(--kt-gold-soft);
	font-weight: 400;
}

.kt-eyebrow {
	font-size: 12px;
	font-weight: 500;
	letter-spacing: 0.3em;
	text-transform: uppercase;
	color: var(--kt-text-dim);
}

.kt-label {
	font-size: 10px;
	font-weight: 700;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--kt-gold);
}

.kt-label--quiet {
	font-weight: 600;
	color: var(--kt-text-faint);
}

/* Section heading with hairline rules and diamond pips */
.kt-ornament {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: clamp(12px, 2vw, 22px);
	padding: clamp(20px, 3vw, 26px) 0 10px;
}

.kt-ornament__rule {
	height: 1px;
	width: clamp(28px, 6vw, 70px);
	background: var(--kt-gold-line-soft);
	flex: none;
}

.kt-ornament__pip {
	width: 7px;
	height: 7px;
	background: var(--kt-gold-dim);
	transform: rotate(45deg);
	flex: none;
}

.kt-ornament__title {
	font-family: var(--kt-font-display);
	font-size: clamp(21px, 3.2vw, 34px);
	letter-spacing: 0.18em;
	color: var(--kt-gold-soft);
	text-align: center;
	line-height: 1.15;
}

/* Plain centred panel heading */
.kt-heading {
	text-align: center;
	font-family: var(--kt-font-display);
	font-size: clamp(19px, 2.6vw, 26px);
	letter-spacing: 0.16em;
	color: var(--kt-gold-soft);
	margin-bottom: clamp(26px, 3.6vw, 36px);
}

.kt-heading--lg {
	font-size: clamp(21px, 3vw, 30px);
	margin-bottom: 8px;
}

.kt-subheading {
	text-align: center;
	font-size: 11.5px;
	letter-spacing: 0.3em;
	text-transform: uppercase;
	color: var(--kt-text-dim);
	margin-bottom: clamp(28px, 4vw, 40px);
}

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */

.kt-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 12px;
	padding: 15px 30px;
	border: 1px solid transparent;
	border-radius: var(--kt-radius-sm);
	background: transparent;
	font-family: var(--kt-font-body);
	font-size: 12.5px;
	font-weight: 600;
	letter-spacing: 0.24em;
	text-transform: uppercase;
	line-height: 1;
	cursor: pointer;
	text-align: center;
	/* 44px is the minimum comfortable touch target; the padding above gets a
	   text button there, but a short label in a tight row can fall short. */
	min-height: 44px;
	transition: background var(--kt-ease), color var(--kt-ease), border-color var(--kt-ease);
}

.kt-btn--gold {
	border-color: var(--kt-gold-line);
	color: var(--kt-gold-hi);
}

.kt-btn--gold:hover,
.kt-btn--gold:focus-visible {
	background: var(--kt-gold);
	color: var(--kt-on-gold);
}

.kt-btn--ghost {
	border-color: var(--kt-border-control);
	color: var(--kt-text-body);
}

.kt-btn--ghost:hover,
.kt-btn--ghost:focus-visible {
	border-color: var(--kt-gold-line);
	color: var(--kt-gold-hi);
}

.kt-btn--solid {
	background: var(--kt-gold);
	color: var(--kt-on-gold);
	font-weight: 800;
	letter-spacing: 0.22em;
	font-size: 12px;
	padding: 13px 30px;
}

.kt-btn--solid:hover,
.kt-btn--solid:focus-visible {
	background: var(--kt-gold-hi);
	color: var(--kt-on-gold);
}

.kt-btn--sm {
	padding: 10px 24px;
	font-size: 11px;
	letter-spacing: 0.22em;
	border-color: var(--kt-gold-line-2);
	/* Rest at --kt-gold, not bright: brightness is the hover/current signal,
	   and a control that RESTS at the hover lightness has nowhere to go. */
	color: var(--kt-gold);
}

.kt-btn--sm:hover,
.kt-btn--sm:focus-visible {
	background: var(--kt-gold);
	color: var(--kt-on-gold);
}

.kt-btn--block {
	display: flex;
	width: 100%;
}

.kt-btn[disabled],
.kt-btn.is-disabled {
	opacity: 0.45;
	pointer-events: none;
}

.kt-btn__arrow {
	font-family: Georgia, "Times New Roman", serif;
}

/* --------------------------------------------------------------------------
   6. Chili-pip heat scale
   -------------------------------------------------------------------------- */

.kt-heat {
	display: inline-flex;
	gap: 3px;
	align-items: center;
	line-height: 1;
}

/* Each pip is an inline SVG chilli filled with currentColor — which is what
   lets the theme own this red instead of borrowing whichever emoji the device
   ships. The old grayscale-emoji unlit state measured ~1.3:1 on the ground and
   simply vanished, so a "Heat: Medium" dish read as two chillies full stop,
   understating every dish's ceiling. Unlit is now an ember: recessive but
   findable, and the count is legible as N *of 3*. */
.kt-heat__pip {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	font-size: 13px;
	line-height: 1;
	flex: none;
	color: var(--kt-chili-off);
}

.kt-heat__pip svg {
	width: 1em;
	height: 1em;
	display: block;
	fill: currentColor;
}

.kt-heat__pip.is-lit {
	color: var(--kt-chili-on);
}

.kt-heat--lg .kt-heat__pip {
	font-size: 18px;
}

/* --------------------------------------------------------------------------
   7. Site header / navigation
   -------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   Distraction-free checkout — no header changes on purpose
   --------------------------------------------------------------------------
   header.php and footer.php stop rendering the nav, the icons, the drawer, the
   search panel and the whole footer on the checkout (see
   kty_is_focused_checkout()), and that is the whole of it. The header keeps the
   same layout and the same sticky behaviour as every other page, so the logo
   stays exactly where a customer last saw it.

   Centring the lone logo and dropping the sticky positioning were both tried and
   reverted at the owner's request: moving the one landmark that survives is a
   change of its own, and a checkout that reshuffles the masthead reads as a
   different site at the moment trust matters most.

   `.kt-checkout-focus` stays on <body> as a styling hook for anything that does
   need it later.
   -------------------------------------------------------------------------- */

.kt-header {
	position: sticky;
	top: 0;
	z-index: 50;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 20px;
	/* Height comes from the token, not from padding — see the note on
	   --kt-header-h. `align-items: center` above centres the 44px brand mark,
	   the nav and the icon row inside it.
	
	   The 1px padding-top is not a nudge, it is the border's counterweight.
	   `box-sizing: border-box` means the bottom hairline eats 1px of the 64px
	   box, so the CONTENT box is 63px and `align-items: center` lands everything
	   on 31.5 while the bar's visible middle is 32 — half a pixel high, on every
	   item at once. One pixel of padding at the top restores the symmetry the
	   border broke, and the bar's total height does not change. */
	min-height: var(--kt-header-h);
	padding: 1px var(--kt-gutter) 0;
	background: rgba(11, 10, 9, 0.78);
	background: oklch(0.08 0.004 60 / 0.78);
	-webkit-backdrop-filter: blur(14px);
	backdrop-filter: blur(14px);
	border-bottom: 1px solid rgba(66, 55, 36, 0.4);
	border-bottom: 1px solid oklch(0.30 0.03 85 / 0.4);
}

/* The logo link is the "go home" target and was 34px tall on a phone. The
   header has room for a 44px hit area without growing. */
.kt-brand {
	display: flex;
	align-items: center;
	min-height: 44px;
	flex: none;
}

.kt-brand img,
.kt-brand svg {
	height: clamp(30px, 3vw, 38px);
	width: auto;
	display: block;
}

.kt-brand__text {
	font-family: var(--kt-font-display);
	font-size: clamp(17px, 2.4vw, 24px);
	letter-spacing: 0.14em;
	color: var(--kt-gold);
	white-space: nowrap;
}

.kt-nav {
	display: flex;
	flex-wrap: wrap;
	gap: clamp(18px, 2.6vw, 42px);
}

.kt-nav ul {
	display: flex;
	flex-wrap: wrap;
	gap: clamp(18px, 2.6vw, 42px);
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * Centre the link inside its own item.
 *
 * The links are `display: inline` at 12.5px, but the <li> inherits the body's
 * 15px/1.7 line-height, so each item carried a 25.5px strut and the smaller
 * link sat on ITS baseline rather than in the middle — 1px below the logo and
 * the icon row, which is exactly the sort of gap the eye reads as "not quite
 * lined up" without being able to name it.
 *
 * Making the item a flex container drops the strut: the link is blockified into
 * a flex item and centred on the cross axis. Scoped with `>` so the drop-down's
 * own list items keep their normal stacking.
 */
.kt-nav ul > li {
	display: flex;
	align-items: center;
}

.kt-nav a {
	font-size: 12.5px;
	font-weight: 500;
	letter-spacing: 0.22em;
	text-transform: uppercase;
	color: #d9d3c9;
	color: oklch(0.85 0.03 88);
	white-space: nowrap;
}

.kt-nav .current-menu-item > a,
.kt-nav .current_page_item > a,
.kt-nav .current-menu-ancestor > a,
.kt-nav .current-menu-parent > a,
.kt-nav a[aria-current="page"] {
	color: var(--kt-gold);
}

.kt-nav a:hover {
	color: var(--kt-gold-bright);
}

/* Desktop dropdown for product-category submenus */
.kt-nav li {
	position: relative;
}

.kt-nav .sub-menu {
	position: absolute;
	top: calc(100% + 14px);
	left: 50%;
	transform: translateX(-50%) translateY(-6px);
	display: block;
	min-width: 230px;
	flex-direction: column;
	gap: 0;
	padding: 10px 0;
	background: rgba(11, 10, 9, 0.96);
	background: oklch(0.08 0.004 60 / 0.96);
	-webkit-backdrop-filter: blur(14px);
	backdrop-filter: blur(14px);
	border: 1px solid var(--kt-frame-soft);
	border-radius: var(--kt-radius);
	opacity: 0;
	visibility: hidden;
	transition: opacity var(--kt-ease), transform var(--kt-ease), visibility var(--kt-ease);

	/* 17 categories make this ~660px tall. The header is position:sticky, so a
	   dropdown taller than the viewport cannot be scrolled to — the panel stays
	   pinned while the page moves underneath, and the last items are simply
	   unreachable on a 1366x768 laptop. Cap it and let the panel scroll itself. */
	max-height: calc(100vh - var(--kt-header-h) - 24px);
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-width: thin;
	scrollbar-color: var(--kt-gold-line-faint) transparent;
}

.kt-nav .sub-menu::-webkit-scrollbar {
	width: 6px;
}

.kt-nav .sub-menu::-webkit-scrollbar-thumb {
	background: var(--kt-gold-line-faint);
	border-radius: var(--kt-radius-pill, 999px);
}

.kt-nav .sub-menu::-webkit-scrollbar-track {
	background: transparent;
}

.kt-nav li:hover > .sub-menu,
.kt-nav li:focus-within > .sub-menu {
	opacity: 1;
	visibility: visible;
	transform: translateX(-50%) translateY(0);
}

.kt-nav .sub-menu li {
	width: 100%;
}

.kt-nav .sub-menu a {
	display: block;
	padding: 9px 22px;
	font-size: 11.5px;
	letter-spacing: 0.16em;
	white-space: nowrap;
}

.kt-header__actions {
	display: flex;
	align-items: center;
	gap: 12px;
	flex: none;
}

.kt-cart-link {
	display: flex;
	align-items: center;
	gap: 10px;
	border: 1px solid var(--kt-gold-line-2);
	color: var(--kt-gold-bright);
	padding: 11px 22px;
	border-radius: var(--kt-radius-sm);
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	white-space: nowrap;
	transition: background var(--kt-ease), color var(--kt-ease);
}

.kt-cart-link:hover,
.kt-cart-link:focus-visible {
	background: var(--kt-gold);
	color: var(--kt-on-gold);
}

.kt-burger {
	display: none;
	align-items: center;
	justify-content: center;
	width: 46px;
	height: 46px;
	padding: 0;
	background: transparent;
	border: 1px solid var(--kt-border-control);
	color: var(--kt-gold-bright);
	cursor: pointer;
	border-radius: var(--kt-radius-sm);
}

.kt-burger__bars {
	display: flex;
	flex-direction: column;
	gap: 5px;
	width: 20px;
}

.kt-burger__bars span {
	height: 1.5px;
	background: #e2bb72;
	background: oklch(0.85 0.09 85);
}

/* Mobile drawer */
.kt-drawer {
	position: fixed;
	inset: 0;
	z-index: 400;
	/*
	 * flex + opacity/visibility, not display:none toggling. The drawer is the
	 * primary navigation on a phone and was the ONE surface that teleported —
	 * display can't transition, so entrance and exit were both a single frame.
	 * The [hidden] attribute still wins while the drawer is closed (see below),
	 * so nothing is focusable or clickable until JS opens it; JS removes hidden
	 * first, forces a reflow, then adds is-open so the transition has a
	 * from-state — the same sequence the search panel uses.
	 */
	display: flex;
	opacity: 0;
	visibility: hidden;
	transition: opacity 200ms var(--kt-ease-soft), visibility 0s 200ms;
	flex-direction: column;
	background: rgba(9, 8, 7, 0.97);
	background: oklch(0.07 0.004 60 / 0.97);
	-webkit-backdrop-filter: blur(12px);
	backdrop-filter: blur(12px);
	font-family: var(--kt-font-body);
	overflow-y: auto;
	overscroll-behavior: contain;
}

.kt-drawer[hidden] {
	display: none;
}

.kt-drawer.is-open {
	opacity: 1;
	visibility: visible;
	/* Enter 320 / exit 200 — arrivals on the quint, departures quicker and
	   symmetric-soft, matching the langbar's manner. */
	transition: opacity 320ms var(--kt-ease-out, cubic-bezier(0.22, 1, 0.36, 1)), visibility 0s;
}

/* The link column rises a step behind the veil, so opening reads as the menu
   arriving rather than a curtain merely appearing. */
.kt-drawer__body {
	transform: translateY(8px);
	transition: transform 200ms var(--kt-ease-soft);
}

.kt-drawer.is-open .kt-drawer__body {
	transform: none;
	transition: transform 320ms var(--kt-ease-out, cubic-bezier(0.22, 1, 0.36, 1));
}

.kt-drawer__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: clamp(14px, 2vw, 26px) var(--kt-gutter);
	border-bottom: 1px solid rgba(66, 55, 36, 0.4);
	border-bottom: 1px solid oklch(0.30 0.03 85 / 0.4);
	flex: none;
}

.kt-drawer__head img {
	height: 38px;
	width: auto;
	display: block;
}

.kt-drawer__close {
	width: 46px;
	height: 46px;
	background: transparent;
	border: 1px solid var(--kt-gold-line-mid);
	border-radius: var(--kt-radius-sm);
	color: var(--kt-gold-bright);
	font-size: 20px;
	line-height: 1;
	cursor: pointer;
	flex: none;
}

.kt-drawer__body {
	flex: 1;
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 4px;
	padding: 24px clamp(24px, 7vw, 64px);
}

.kt-drawer__body ul {
	list-style: none;
	margin: 0;
	padding: 0;
}

.kt-drawer__body > ul > li > a,
.kt-drawer__link {
	display: block;
	font-family: var(--kt-font-display);
	font-size: clamp(26px, 8vw, 40px);
	letter-spacing: 0.1em;
	color: #f0eae0;
	color: oklch(0.94 0.02 88);
	padding: 14px 0;
	border-bottom: 1px solid rgba(52, 45, 34, 0.7);
	border-bottom: 1px solid oklch(0.24 0.02 85 / 0.7);
}

/*
 * The category submenu is hidden in the drawer.
 *
 * All 17 categories rendered as wrapped chips pushed the four primary links —
 * the reason the drawer exists — most of a screen down. "Order Online" leads to
 * the shop, which carries the same category chip row at full width, so nothing
 * is lost but the scrolling.
 */
.kt-drawer__body .sub-menu {
	display: none;
	flex-wrap: wrap;
	gap: 6px;
	padding: 10px 0 4px;
	border-bottom: 1px solid rgba(52, 45, 34, 0.7);
	border-bottom: 1px solid oklch(0.24 0.02 85 / 0.7);
}

/*
 * The category list inside the drawer. These were bare 17px-tall text links as
 * narrow as 33px ("Rice"), packed three and four to a row — the smallest tap
 * targets on the site by a wide margin. Drawn as chips instead: same type, same
 * colour, but a 44px-tall hit area with its own hairline so the target is
 * visible rather than merely present.
 */
.kt-drawer__body .sub-menu a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 44px;
	min-width: 76px;
	padding: 0 16px;
	border: 1px solid var(--kt-border-control);
	border-radius: var(--kt-radius-sm);
	font-family: var(--kt-font-body);
	font-size: 12px;
	letter-spacing: 0.16em;
	line-height: 1;
	text-transform: uppercase;
	color: var(--kt-text-muted);
	transition: border-color var(--kt-ease), color var(--kt-ease);
}

.kt-drawer__body .sub-menu a:hover,
.kt-drawer__body .sub-menu a:focus-visible {
	border-color: var(--kt-gold-line);
	color: var(--kt-gold-hi);
}

.kt-drawer__foot {
	flex: none;
	padding: 0 clamp(24px, 7vw, 64px) clamp(30px, 6vw, 48px);
	display: flex;
	flex-direction: column;
	gap: 16px;
}

.kt-drawer__cart {
	display: block;
	text-align: center;
	background: var(--kt-gold);
	color: var(--kt-on-gold);
	padding: 16px 28px;
	font-size: 12px;
	font-weight: 800;
	letter-spacing: 0.24em;
	text-transform: uppercase;
	border-radius: var(--kt-radius-sm);
}

.kt-drawer__meta {
	font-size: 12.5px;
	line-height: 1.8;
	color: var(--kt-text-muted);
	text-align: center;
}

body.kt-drawer-open {
	overflow: hidden;
}

/* "Drawer opens below 900px" — developer handoff, SiteNav row. */
@media (max-width: 899px) {
	.kt-nav {
		display: none;
	}

	.kt-burger {
		display: flex;
	}

	.kt-cart-link {
		padding: 11px 16px;
		letter-spacing: 0.14em;
	}
}

/* --------------------------------------------------------------------------
   8. Hero
   -------------------------------------------------------------------------- */

.kt-hero {
	position: relative;
	overflow: hidden;
}

.kt-hero__img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 70% 50%;
}

/*
 * THE RIGHT-HAND STOPS ARE TUNED TO THE PHOTOGRAPH'S LUMINANCE, NOT TO TASTE.
 *
 * This ramp was built around a picture with a big white plate on its right,
 * which measured about 200 there; 0.42 of veil left roughly 116 on screen and
 * the food read clearly. The Thai table that replaced it is evenly warm and
 * mid-dark — measured 87 in the same region at its brightest crop — so the
 * same 0.42 produced 44, and the hero went black. The picture was loading and
 * decoding perfectly; it was simply being covered.
 *
 * Sampled the file through a canvas (it is same-origin now, which is what
 * makes that possible) and re-cut the right half: 0.14 at 80% leaves about 75.
 * Not the 116 the old plate gave, but plainly a photograph rather than a dark
 * rectangle.
 *
 * THE LEFT HALF IS UNTOUCHED, deliberately. 0.97 out to 30% and 0.72 at 56% is
 * what the headline and lede are measured against; the text never reaches the
 * stops that moved. Anyone swapping the hero photograph again should re-measure
 * rather than re-guess — the crop offset is nearly useless as a lever here
 * (every vertical position lands within 75-87) and the veil is the real one.
 */
.kt-hero__veil-h {
	position: absolute;
	inset: 0;
	background: linear-gradient(
		to right,
		rgba(15, 13, 11, 0.97) 30%,
		rgba(15, 13, 11, 0.72) 56%,
		rgba(15, 13, 11, 0.14) 80%,
		rgba(15, 13, 11, 0.34)
	);
	background: linear-gradient(
		to right,
		oklch(0.10 0.006 60 / 0.97) 30%,
		oklch(0.10 0.006 60 / 0.72) 56%,
		oklch(0.10 0.006 60 / 0.14) 80%,
		oklch(0.10 0.006 60 / 0.34)
	);
}

.kt-hero__veil-v {
	position: absolute;
	inset: 0;
	background: linear-gradient(
		to bottom,
		rgba(15, 13, 11, 0.55),
		transparent 28%,
		transparent 60%,
		rgba(13, 12, 10, 1) 97%
	);
	background: linear-gradient(
		to bottom,
		oklch(0.10 0.006 60 / 0.55),
		transparent 28%,
		transparent 60%,
		var(--kt-ground) 97%
	);
}

.kt-hero__inner {
	position: relative;
	max-width: var(--kt-max);
	margin: 0 auto;
	padding: clamp(64px, 9vw, 110px) var(--kt-gutter) clamp(80px, 11vw, 140px);
}

.kt-hero__content {
	max-width: 640px;
}

/*
 * The hero heading is one <h1> holding two block spans. The wrapper carries no
 * type of its own — it only resets what an <h1> would otherwise impose — and
 * each span keeps the exact declarations its former element had, so the two
 * lines render identically to when they were a <p> and an <h1>.
 */
.kt-hero__title {
	margin: 0;
	font-size: inherit;
	letter-spacing: normal;
	line-height: inherit;
}

.kt-hero__title-over {
	display: block;
	font-family: var(--kt-font-display);
	font-size: clamp(30px, 5.2vw, 54px);
	letter-spacing: 0.12em;
	color: var(--kt-text-hero);
	line-height: 1.1;
}

.kt-hero__title-main {
	display: block;
	font-family: var(--kt-font-display);
	font-size: clamp(48px, 8.6vw, 92px);
	letter-spacing: 0.04em;
	color: var(--kt-gold);
	line-height: 1;
	margin-top: 2px;
}

.kt-hero__script {
	font-family: var(--kt-font-script);
	font-size: clamp(27px, 3.6vw, 38px);
	color: var(--kt-gold-soft);
	margin-top: 22px;
	line-height: 1.3;
}

.kt-hero__lede {
	font-size: clamp(14px, 1.5vw, 15.5px);
	line-height: 1.75;
	color: var(--kt-text-body);
	font-weight: 300;
	max-width: 440px;
	margin-top: 20px;
	text-wrap: pretty;
}

.kt-hero__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 14px;
	margin-top: 38px;
}

/*
 * The hero veils are tuned for the DESKTOP composition, where the text sits in
 * the left third and the photograph is only asked to stay out of its way —
 * hence the horizontal ramp from 0.97 down to 0.42.
 *
 * Below 900px that assumption breaks: the column goes full width, so the lede
 * and both buttons sit on top of whatever the right of the photograph happens
 * to be. With the current picture that is a white plate and a tomato, and
 * 0.42 of veil over them is nowhere near enough to read body text against.
 *
 * Two changes, both needed:
 *
 *   object-position moves to the LEFT of the frame, which on this photograph
 *   is the dark counter rather than the lit plate — a free contrast win that
 *   costs nothing and still shows the food.
 *
 *   the ramp flattens and deepens. 0.88 is not arbitrary: white hero text sits
 *   near 0.90 relative luminance, so 4.5:1 needs the backdrop under 0.16, and
 *   a plate highlight around 0.85 only gets there once the veil passes ~0.82.
 *   The body grey needs a little more again, so 0.88 is the floor across the
 *   whole width, not just at the edge.
 *
 * The photograph survives as warm texture rather than as a subject. On a
 * 375px screen that is the right trade: the words are the product.
 */
@media (max-width: 900px) {
	.kt-hero__img {
		object-position: 28% 50%;
	}

	.kt-hero__veil-h {
		background: linear-gradient(
			to right,
			rgba(15, 13, 11, 0.95),
			rgba(15, 13, 11, 0.88)
		);
		background: linear-gradient(
			to right,
			oklch(0.10 0.006 60 / 0.95),
			oklch(0.10 0.006 60 / 0.88)
		);
	}
}

/* --------------------------------------------------------------------------
   9. Front page — specialties
   -------------------------------------------------------------------------- */

.kt-specialties__tag {
	text-align: center;
	font-size: 12px;
	font-weight: 500;
	letter-spacing: 0.3em;
	text-transform: uppercase;
	color: var(--kt-text-dim);
}

.kt-specialties__grid-wrap {
	overflow: hidden;
	padding: clamp(38px, 5vw, 54px) 0 clamp(48px, 6vw, 66px);
}

.kt-specialties__grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
	column-gap: 0;
	row-gap: 44px;
	margin-left: -1px;
}

.kt-specialty {
	text-align: center;
	padding: 0 clamp(12px, 2.2vw, 30px);
	display: flex;
	flex-direction: column;
	align-items: center;
	border-left: 1px solid var(--kt-divider);
}

/*
 * `.kt-specialty__icon` used to sit here — a 72px gold circle above each
 * heading, costing 94px of column height (72 plus a 22px margin). The marks
 * are typographic now, like the dish list and the FAQ, so the rule is gone
 * rather than left orphaned. The title's own margin already spaces the block.
 */

.kt-specialty__title {
	font-family: var(--kt-font-display);
	font-size: 15px;
	letter-spacing: 0.16em;
	color: var(--kt-text-bright);
	margin-bottom: 10px;
}

.kt-specialty__text {
	font-size: 13.5px;
	line-height: 1.6;
	color: var(--kt-text-muted);
	font-weight: 300;
	max-width: 240px;
}

/* --------------------------------------------------------------------------
   10. Typographic dish cards (front page highlights + product loop)
   -------------------------------------------------------------------------- */

.kt-dishes {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	gap: clamp(18px, 2.4vw, 28px);
}

.kt-dish {
	border: 1px solid var(--kt-frame-soft);
	border-radius: var(--kt-radius);
	background: var(--kt-panel);
	-webkit-backdrop-filter: var(--kt-panel-blur);
	backdrop-filter: var(--kt-panel-blur);
	overflow: hidden;
	display: flex;
	flex-direction: column;
	transition: border-color var(--kt-ease-slow), transform var(--kt-ease-slow);
}

.kt-dish:hover,
.kt-dish:focus-within {
	border-color: var(--kt-gold-line);
	transform: translateY(-3px);
}

.kt-dish__body {
	padding: 36px 24px 32px;
	text-align: center;
	display: flex;
	flex-direction: column;
	align-items: center;
	flex: 1;
}

.kt-dish__badge {
	font-size: 10px;
	font-weight: 700;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--kt-gold);
	margin-bottom: 18px;
	min-height: 1em;
}

.kt-dish__badge--quiet {
	font-weight: 600;
	color: var(--kt-text-faint);
}

.kt-dish__title {
	font-family: var(--kt-font-display);
	font-size: 19px;
	letter-spacing: 0.12em;
	color: var(--kt-text-bright);
	line-height: 1.25;
	text-transform: uppercase;
}

/*
 * The dish name is a link, and on a phone it was a 20–26px-tall tap target.
 * The hit area is grown with an absolutely positioned overlay rather than with
 * padding: on the archive card the name shares a `align-items: baseline` flex
 * row with the price, and vertical padding on an inline-block would shift its
 * baseline off the price's. This changes no geometry at all.
 */
/*
 * The WHOLE card is the link.
 *
 * This started life as a 44px tap-target overlay bounded to the name itself. It
 * now stretches to the card, because a dish card has one destination and people
 * click the card, not the four words at the top of it — the search results have
 * always behaved this way (`.kt-dish--result` IS an <a> wrapping the row), so
 * this makes the menu agree with them rather than inventing anything.
 *
 * `position: static` on the anchor is what does it: the overlay then resolves
 * against `.kt-dish`, which is the positioned ancestor. It also supersedes the
 * old min-height, since a card is comfortably taller than 44px.
 *
 * Anything interactive inside the card has to ride ABOVE the overlay — see the
 * z-index rule below, which is the half of this that breaks silently if the
 * card ever gains a second control.
 */
.kt-dish {
	position: relative;
}

.kt-dish__title a {
	position: static;
}

.kt-dish__title a::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
}

.kt-dish__cta,
.kt-dish form.cart,
.kt-dish .kt-btn,
.kt-dish button,
.kt-dish select,
.kt-dish input {
	position: relative;
	z-index: 2;
}

.kt-dish__rule {
	width: 34px;
	height: 1px;
	background: var(--kt-gold-line-soft);
	margin: 16px 0;
	flex: none;
}

.kt-dish__price {
	font-family: var(--kt-font-display);
	font-size: 22px;
	color: var(--kt-gold);
	line-height: 1.2;
}

.kt-dish__price del {
	color: var(--kt-text-faint);
	font-size: 0.72em;
	margin-right: 8px;
}

.kt-dish__price ins {
	text-decoration: none;
}

.kt-dish__heat {
	margin-top: 16px;
}

.kt-dish__text {
	font-size: 13.5px;
	line-height: 1.65;
	color: var(--kt-text-muted);
	font-weight: 300;
	margin-top: 16px;
	max-width: 300px;
}

.kt-dish__spacer {
	flex: 1;
}

.kt-dish__cta {
	margin-top: 24px;
}

/* --------------------------------------------------------------------------
   11. Front page — promo banner
   -------------------------------------------------------------------------- */

.kt-promo {
	border: 1px solid var(--kt-frame);
	border-radius: var(--kt-radius-lg);
	overflow: hidden;
	position: relative;
	min-height: 240px;
}

.kt-promo__img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.kt-promo__veil {
	position: absolute;
	inset: 0;
	background: linear-gradient(
		to right,
		rgba(17, 15, 12, 0.96) 34%,
		rgba(17, 15, 12, 0.6) 66%,
		rgba(17, 15, 12, 0.25)
	);
	background: linear-gradient(
		to right,
		oklch(0.11 0.006 60 / 0.96) 34%,
		oklch(0.11 0.006 60 / 0.6) 66%,
		oklch(0.11 0.006 60 / 0.25)
	);
}

.kt-promo__inner {
	position: relative;
	padding: clamp(30px, 4.4vw, 48px) clamp(22px, 4vw, 56px);
}

.kt-promo__kicker {
	font-family: var(--kt-font-display);
	font-size: clamp(14px, 1.8vw, 20px);
	letter-spacing: 0.2em;
	color: var(--kt-text-bright);
}

.kt-promo__title {
	font-family: var(--kt-font-display);
	font-size: clamp(27px, 4.4vw, 46px);
	letter-spacing: 0.06em;
	color: var(--kt-gold);
	margin-top: 8px;
	line-height: 1.1;
}

.kt-promo__meta {
	font-size: clamp(11px, 1.3vw, 13px);
	letter-spacing: 0.22em;
	text-transform: uppercase;
	color: #bdb5a9;
	color: oklch(0.75 0.02 88);
	margin-top: 12px;
}

.kt-promo__cta {
	margin-top: 26px;
}

/* --------------------------------------------------------------------------
   12. Front page — stats
   -------------------------------------------------------------------------- */

.kt-stats {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
	gap: 24px 20px;
}

/*
 * A plain block now, and no rule needed for the alignment.
 *
 * This was a flex row holding a 58px circle beside the figure, and it carried
 * `justify-content: flex-start` for a reason worth keeping on record: centring
 * put every row's circle at a different x, because the labels differ in width
 * ("Dishes on the menu" against "Last kitchen order"). That measured a 9.2px
 * spread across the four marks stacked on a phone, and read as a wobble down
 * the column.
 *
 * With the marks gone (2.42.0) the figures share a left edge on their own —
 * every ancestor here computes `text-align: start`, checked rather than
 * assumed — so the flex context, the gap and the `.kt-stat__mark` rules have
 * all been removed instead of left behind doing nothing.
 */

.kt-stat__value {
	font-family: var(--kt-font-display);
	font-size: 22px;
	color: #e9c88a;
	color: oklch(0.90 0.06 85);
	line-height: 1;
}

.kt-stat__label {
	font-size: 12.5px;
	color: var(--kt-text-dim);
	font-weight: 300;
	margin-top: 6px;
}

/* --------------------------------------------------------------------------
   13. Front page — testimonial + reserve
   -------------------------------------------------------------------------- */

.kt-duo {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
	gap: clamp(18px, 2.4vw, 28px);
}

/* The card shares a grid row with .kt-feature, which carries min-height: 300px,
   so whichever of the two is taller sets the height and the other stretches to
   it. The old rule met that with `justify-content: center`, which parks the
   whole column as one clump in the middle and leaves the slack as two voids.
   Instead: the mark stays at the top, the dots sit at the bottom, and all the
   slack goes to the viewport in between, which centres the quote inside it. The
   card then reads as composed whether it is at its natural height or stretched.

   `margin: 0` is not redundant. This was a <figure>, and WordPress's global
   styles set `:where(figure) { margin: 0 0 1em }` at zero specificity — 15px of
   it, which left the card's bottom edge sitting 15px above the reservation
   card's. The element is a <div> now (the per-quote <figure>/<figcaption>
   pairing has to nest inside it), but the reset keeps that from coming back. */
.kt-quote {
	border: 1px solid var(--kt-frame);
	border-radius: var(--kt-radius-lg);
	padding: clamp(32px, 4vw, 44px) clamp(24px, 3.6vw, 48px);
	display: flex;
	flex-direction: column;
	margin: 0;
}

/* Great Vibes puts the ink of “ in the top quarter of its em box: measured at
   52px, the visible mark is 9px tall and stops 38px short of the bottom of its
   own line box. That dead space, plus the 18px margin under it, was the hole
   between the mark and the quote. Pull the box back up by roughly the measured
   overhang — in em, so it tracks the font-size — and the remainder reads as the
   deliberate gap it always should have been. */
.kt-quote__mark {
	font-family: var(--kt-font-script);
	font-size: 52px;
	color: var(--kt-gold-dim);
	line-height: 1;
	margin-bottom: -0.31em;
}

/* Takes the card's spare height and centres the quote in it. Grid rather than
   block so the rotator can stack every quote in the one cell (below): the cell
   is then as tall as the longest quote and nothing shifts as they change.

   Both centring axes are load-bearing. align-content centres the cell in a card
   that has been stretched by its neighbour; align-items keeps each quote at its
   own height inside that cell instead of stretching to the tallest one, which
   would leave the short quotes hanging from the top with the slack below. */
.kt-quote__viewport {
	flex: 1 1 auto;
	display: grid;
	align-content: center;
	align-items: center;
	gap: 26px;
	min-width: 0;
}

.kt-quote__item {
	margin: 0;
}

.kt-quote.is-rotating .kt-quote__item {
	grid-area: 1 / 1;
	opacity: 0;
	visibility: hidden;
	/* The reveal tier's duration and the family's slow-fade curve — 420ms
	   generic ease was the one motion in the system belonging to no token. */
	transition: opacity var(--kt-motion-reveal), visibility var(--kt-motion-reveal);
}

.kt-quote.is-rotating .kt-quote__item.is-active {
	opacity: 1;
	visibility: visible;
}

.kt-quote__text {
	font-size: clamp(15px, 1.7vw, 17px);
	line-height: 1.75;
	color: #e0dbd3;
	color: oklch(0.88 0.015 88);
	font-weight: 300;
	max-width: 520px;
	text-wrap: pretty;
	margin: 0;
	padding: 0;
	border: 0;
}

.kt-quote__cite {
	font-size: 12px;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--kt-gold-dim);
	margin-top: 20px;
	font-style: normal;
}

/* Built by assets/js/theme.js, and only when there is more than one quote.
   The negative inline start pulls the first dot's ink back onto the text's left
   edge — the button is a 20px touch target around a 6px dot. */
.kt-quote__dots {
	display: flex;
	margin-top: 28px;
	margin-inline-start: -7px;
}

.kt-quote__dot {
	width: 20px;
	height: 24px;
	display: grid;
	place-items: center;
	background: none;
	border: 0;
	padding: 0;
	border-radius: var(--kt-radius-pill);
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
}

.kt-quote__dot::after {
	content: "";
	width: 6px;
	height: 6px;
	border-radius: var(--kt-radius-pill);
	background: #6f5c33;
	background: oklch(0.45 0.05 85);
	transition: background var(--kt-ease);
}

.kt-quote__dot:hover::after {
	background: var(--kt-gold-dim);
}

.kt-quote__dot.is-active::after {
	background: var(--kt-gold);
}

.kt-feature {
	border: 1px solid var(--kt-frame);
	border-radius: var(--kt-radius-lg);
	overflow: hidden;
	position: relative;
	min-height: 300px;
}

.kt-feature__img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/*
 * The two cards that carry the D'Deck photograph crop it low.
 *
 * That file is 765x703 — very nearly square — inside boxes of roughly 1.83:1
 * and 2.2:1, so cover keeps only about half its height, and a centred crop
 * spends most of that on empty night sky: the palms, the lit railing, the water
 * and the terrace tables all sit in the lower half of the frame. 88% keeps the
 * whole of that band with just enough sky to breathe.
 *
 * Scoped to the two cards rather than to `.kt-feature__img` at large, because
 * the same class also renders the book-a-table HERO fallback and the
 * order-received venue picture, which show the wider bay and want their default
 * centre. If the terrace photograph is ever replaced with a landscape original,
 * this rule should go with it.
 */
.kt-front .kt-feature__img,
.kt-res-terrace .kt-feature__img {
	object-position: 50% 88%;
}

.kt-feature__veil {
	position: absolute;
	inset: 0;
	background: linear-gradient(to top, rgba(17, 15, 12, 0.95) 20%, rgba(17, 15, 12, 0.3) 70%);
	background: linear-gradient(to top, oklch(0.11 0.006 60 / 0.95) 20%, oklch(0.11 0.006 60 / 0.3) 70%);
}

.kt-feature__inner {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	padding: clamp(26px, 3.2vw, 36px) clamp(24px, 3.4vw, 40px);
}

.kt-feature__title {
	font-family: var(--kt-font-display);
	font-size: clamp(18px, 2.2vw, 22px);
	letter-spacing: 0.14em;
	color: var(--kt-text-bright);
}

.kt-feature__text {
	font-size: 13.5px;
	line-height: 1.6;
	color: #c6bfb5;
	color: oklch(0.78 0.015 88);
	font-weight: 300;
	margin-top: 10px;
}

.kt-feature__cta {
	align-self: flex-start;
	margin-top: 18px;
	padding: 11px 24px;
	font-size: 11px;
	letter-spacing: 0.22em;
}

/* --------------------------------------------------------------------------
   14. FAQ
   -------------------------------------------------------------------------- */

.kt-faq {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
	gap: clamp(24px, 3vw, 40px);
}

.kt-faq__q {
	font-family: var(--kt-font-display);
	font-size: 15px;
	letter-spacing: 0.1em;
	color: var(--kt-text-bright);
	text-transform: uppercase;
}

.kt-faq__a {
	font-size: 14px;
	line-height: 1.75;
	color: var(--kt-text-dim);
	font-weight: 300;
	margin-top: 10px;
	text-wrap: pretty;
}

/* --------------------------------------------------------------------------
   15. Footer
   -------------------------------------------------------------------------- */

.kt-footer {
	max-width: var(--kt-max);
	margin: clamp(48px, 6vw, 70px) auto 0;
	padding-inline: var(--kt-gutter);
	scroll-margin-top: 100px;
}

.kt-footer__grid {
	border-top: 1px solid var(--kt-hairline);
	padding: clamp(38px, 5vw, 56px) 0 40px;
	display: grid;
	/* Explicit columns, not auto-fit. With auto-fit the column count depended on
	   how many columns had content, so the footer changed shape between pages
	   and configurations — and the first column (with the logo) stretched. */
	/* Three columns since the newsletter column was removed. Explicit, not
	   auto-fit, so the footer keeps the same shape on every page. */
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: clamp(30px, 3.6vw, 48px);
}

@media (max-width: 1024px) {
	.kt-footer__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 600px) {
	.kt-footer__grid {
		grid-template-columns: minmax(0, 1fr);
	}
}

.kt-footer__logo {
	/* Belt and braces: the source is an SVG with no intrinsic pixel size, so if
	   `height` is ever lost to another rule it would scale to the full column
	   width. max-height pins it whatever else happens. */
	height: 46px;
	max-height: 46px;
	width: auto;
	max-width: 100%;
	object-fit: contain;
	object-position: left center;
	display: block;
	margin-bottom: 20px;
}

.kt-footer__about {
	font-size: 14px;
	line-height: 1.7;
	color: var(--kt-text-muted);
	font-weight: 300;
	max-width: 300px;
}

.kt-footer__title {
	font-family: var(--kt-font-display);
	font-size: 13px;
	letter-spacing: 0.2em;
	color: var(--kt-gold);
	margin-bottom: 18px;
}

.kt-footer__links {
	display: flex;
	flex-direction: column;
	gap: 2px;
	font-size: 14px;
	color: var(--kt-text-body);
	font-weight: 300;
	list-style: none;
	margin: 0;
	padding: 0;
}

/*
 * Footer links were 19px tall — a 19px tap target on a phone. The row gap
 * above is traded for padding on the link itself so the touch area grows
 * without the column getting taller, and the text keeps its own baseline
 * rhythm. Desktop appearance is unchanged apart from 3px of extra leading.
 */
.kt-footer__links a,
/*
 * The tap target is the ROW's job, not the link's. When the anchor carried
 * `min-height: 34px` (44px on mobile) against a 22.4px line box, the two rows
 * holding links stood 11.6px taller than the address lines — 21.6px on mobile —
 * and the block's rhythm visibly drifted even though the container gap was a
 * uniform 7px. Rows are now all one height, and the link simply fills its row.
 */
.kt-footer__contact a {
	display: flex;
	align-items: center;
	align-self: stretch;
	width: 100%;
	min-height: 0;
	padding-block: 0;
}

@media (max-width: 900px) {
	.kt-footer__links a {
		min-height: 44px;
	}
}

.kt-footer__contact {
	/* Flex rows with an explicit gap, not <br />. The old markup mixed <br />
	   with whitespace from the PHP conditionals and wrapped two of the lines in
	   <a>, so each row resolved a slightly different line box and the rhythm
	   visibly drifted down the block. */
	display: flex;
	flex-direction: column;
	/*
	 * Matched to .kt-footer__links, which is the rhythm the design wants: 2px
	 * between rows that are their own line box. With line-height 1.7 at 14px
	 * that is a 23.8px row and a 25.8px step, identical to the quick links
	 * beside it. (This block previously used 34px rows and no gap, which read
	 * as noticeably looser than the column next to it.)
	 */
	gap: 2px;
	font-size: 14px;
	line-height: 1.7;
	color: var(--kt-text-body);
	font-weight: 300;
	/* <address> is italic by default; the design sets it roman. */
	font-style: normal;
}

/*
 * Natural line box on a pointer device, exactly like a quick-links row. The
 * enlarged tap target is only needed where there is no mouse, and that is the
 * one place both columns already agree — `.kt-footer__links a` takes 44px at
 * the same breakpoint, so the two lists stay in step at every width.
 */
.kt-footer__contact-line {
	display: flex;
	align-items: center;
	min-height: 0;
}

@media (max-width: 900px) {
	.kt-footer__contact-line {
		min-height: 44px;
	}
}

.kt-footer__note {
	font-size: 14px;
	line-height: 1.7;
	color: var(--kt-text-muted);
	font-weight: 300;
	margin-bottom: 16px;
}

.kt-signup {
	display: flex;
}

.kt-signup input[type="email"] {
	flex: 1;
	min-width: 0;
	border: 1px solid var(--kt-border-control);
	border-right: none;
	background: transparent;
	padding: 12px 16px;
	font-family: var(--kt-font-body);
	font-size: 13px;
	color: var(--kt-text);
	border-radius: var(--kt-radius-sm) 0 0 var(--kt-radius-sm);
}

.kt-signup input[type="email"]::placeholder {
	color: var(--kt-text-faint);
	opacity: 1;
}

.kt-signup button {
	background: var(--kt-gold);
	color: var(--kt-on-gold);
	border: 0;
	/* Outer corners of the welded field+button group; the seam stays square. */
	border-radius: 0 var(--kt-radius-sm) var(--kt-radius-sm) 0;
	padding: 12px 20px;
	font-size: 11px;
	font-weight: 800;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	cursor: pointer;
	display: grid;
	place-items: center;
	transition: background var(--kt-ease);
}

.kt-signup button:hover {
	background: var(--kt-gold-hi);
}

.kt-footer__bar {
	border-top: 1px solid var(--kt-hairline-faint);
	padding: 22px 0 34px;
	display: flex;
	flex-wrap: wrap;
	gap: 14px;
	justify-content: space-between;
	font-size: 12.5px;
	color: var(--kt-text-meta);
}

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

/* Design credit. Sits on the copyright line, dropping to its own line when the
   bar runs out of room rather than squeezing the copyright. */
.kt-footer__credit {
	display: inline-block;
	white-space: nowrap;
}

/*
 * Every middot on the footer's bottom line, spaced from one place.
 *
 * footer.php replaces the plain " · " inside the owner's copyright string with
 * this span as well, so the separator the string ships with and the one before
 * the design credit are the same element and cannot drift apart. The spaces are
 * replaced along with the dot, so this margin is the whole gap rather than an
 * addition to a space that is still there — which is how the two came to differ
 * by 12px in the first place.
 *
 * `em` so the gap tracks the line's font size instead of needing a second
 * number at another breakpoint.
 */
.kt-footer__sep {
	margin: 0 0.85em;
}

/* The global `a { color: inherit }` makes links look like body text, which is
   right for the footer nav but leaves this one unreadable as a link. */
.kt-footer__credit a {
	color: var(--kt-gold-dim);
	transition: color var(--kt-ease);
}

.kt-footer__credit a:hover,
.kt-footer__credit a:focus-visible {
	color: var(--kt-gold-hi);
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* --------------------------------------------------------------------------
   16. Editorial — pages, posts, archives, search, 404
   -------------------------------------------------------------------------- */

.kt-page-head {
	max-width: var(--kt-max);
	margin-inline: auto;
	padding: clamp(48px, 7vw, 88px) var(--kt-gutter) clamp(24px, 3vw, 34px);
	text-align: center;
}

.kt-page-head__title {
	font-family: var(--kt-font-display);
	font-size: clamp(28px, 5vw, 52px);
	letter-spacing: 0.1em;
	color: var(--kt-gold);
	line-height: 1.1;
	text-transform: uppercase;
}

.kt-page-head__sub {
	font-family: var(--kt-font-script);
	font-size: clamp(22px, 3vw, 30px);
	color: var(--kt-gold-soft);
	margin-top: 14px;
}

.kt-page-head__meta {
	font-size: 12px;
	letter-spacing: 0.28em;
	text-transform: uppercase;
	color: var(--kt-text-dim);
	margin-top: 16px;
}

.kt-prose {
	max-width: 760px;
	margin-inline: auto;
	font-size: 15.5px;
	line-height: 1.85;
	color: var(--kt-text-body);
}

.kt-prose h1,
.kt-prose h2,
.kt-prose h3,
.kt-prose h4 {
	font-family: var(--kt-font-display);
	color: var(--kt-gold-soft);
	letter-spacing: 0.1em;
	margin: 1.8em 0 0.6em;
	line-height: 1.25;
}

.kt-prose h2 { font-size: clamp(20px, 2.6vw, 27px); }
.kt-prose h3 { font-size: clamp(17px, 2.1vw, 21px); }
.kt-prose h4 { font-size: 16px; }

/* Exclude buttons: a bare `.kt-prose a` rule out-specifies `.kt-btn--solid`
   and would paint gold text on the gold fill. */
.kt-prose a:not([class*="kt-btn"]):not(.button):not(.kt-chip) {
	color: var(--kt-gold-bright);
	border-bottom: 1px solid var(--kt-gold-line-faint);
}

.kt-prose a:not([class*="kt-btn"]):not(.button):not(.kt-chip):hover {
	border-bottom-color: var(--kt-gold);
}

/* WooCommerce shortcode pages: full width, no reading measure. The commerce
   templates carry their own layout and their own heading. */
.kt-shop-page {
	max-width: none;
	margin-inline: auto;
}

.kt-prose img,
.kt-prose figure {
	border-radius: var(--kt-radius);
}

.kt-prose figcaption {
	font-size: 12.5px;
	color: var(--kt-text-faint);
	text-align: center;
	margin-top: 10px;
}

.kt-prose ul li::marker {
	color: var(--kt-gold-dim);
}

.kt-prose .alignwide {
	max-width: 1080px;
	margin-inline: auto;
}

.kt-prose .alignfull {
	max-width: none;
}

.alignleft { float: left; margin: 0 1.6em 1.2em 0; }
.alignright { float: right; margin: 0 0 1.2em 1.6em; }
.aligncenter { display: block; margin-inline: auto; }

.kt-post-list {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
	gap: clamp(18px, 2.4vw, 28px);
}

.kt-post-card {
	border: 1px solid var(--kt-frame-soft);
	border-radius: var(--kt-radius);
	background: var(--kt-panel);
	-webkit-backdrop-filter: var(--kt-panel-blur);
	backdrop-filter: var(--kt-panel-blur);
	padding: clamp(24px, 3vw, 34px);
	display: flex;
	flex-direction: column;
	transition: border-color var(--kt-ease-slow), transform var(--kt-ease-slow);
}

.kt-post-card:hover {
	border-color: var(--kt-gold-line);
	transform: translateY(-3px);
}

.kt-post-card__date {
	font-size: 11px;
	letter-spacing: 0.24em;
	text-transform: uppercase;
	color: var(--kt-text-faint);
	margin-bottom: 14px;
}

.kt-post-card__title {
	font-family: var(--kt-font-display);
	font-size: 19px;
	letter-spacing: 0.08em;
	color: var(--kt-text-bright);
	line-height: 1.3;
}

.kt-post-card__excerpt {
	font-size: 13.5px;
	line-height: 1.7;
	color: var(--kt-text-muted);
	margin-top: 14px;
	flex: 1;
}

.kt-post-card__more {
	margin-top: 20px;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.22em;
	text-transform: uppercase;
	color: var(--kt-gold-bright);
	align-self: flex-start;
}

/* Pagination */
.kt-pagination {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 8px;
	margin-top: clamp(34px, 4vw, 48px);
}

.kt-pagination .page-numbers {
	display: grid;
	place-items: center;
	min-width: 42px;
	height: 42px;
	padding: 0 12px;
	border: 1px solid var(--kt-frame-soft);
	border-radius: var(--kt-radius-sm);
	font-size: 12px;
	letter-spacing: 0.14em;
	color: var(--kt-text-dim);
	transition: border-color var(--kt-ease), color var(--kt-ease);
}

.kt-pagination .page-numbers:hover {
	border-color: var(--kt-gold-line);
	color: var(--kt-gold-bright);
}

.kt-pagination .page-numbers.current {
	background: var(--kt-gold);
	border-color: var(--kt-gold);
	color: var(--kt-on-gold);
	font-weight: 700;
}

.kt-pagination .page-numbers.dots {
	border-color: transparent;
}

/* --------------------------------------------------------------------------
   17. Forms
   -------------------------------------------------------------------------- */

.kt-field {
	display: block;
	margin-bottom: 18px;
}

.kt-field__label,
label {
	display: block;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--kt-text-dim);
	margin-bottom: 8px;
}

input[type="text"],
input[type="email"],
input[type="tel"],
input[type="url"],
input[type="password"],
input[type="search"],
input[type="number"],
input[type="date"],
input[type="time"],
select,
textarea {
	width: 100%;
	padding: 13px 16px;
	background: var(--kt-field);
	border: 1px solid var(--kt-border-control);
	border-radius: var(--kt-radius-sm);
	font-family: var(--kt-font-body);
	font-size: 14px;
	font-weight: 400;
	color: var(--kt-text-bright);
	transition: border-color var(--kt-ease);
	-webkit-appearance: none;
	appearance: none;
}

input::placeholder,
textarea::placeholder {
	color: var(--kt-text-faint);
	opacity: 1;
}

/* Deliberately no `outline: none` here. This rule has the same specificity as
   the `:focus-visible` ring above and comes later, so suppressing the outline
   would silently remove the keyboard focus indicator from every field. */
input:focus,
select:focus,
textarea:focus {
	border-color: var(--kt-gold-line);
}

textarea {
	min-height: 130px;
	resize: vertical;
	line-height: 1.65;
}

select {
	background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
	background-position: calc(100% - 20px) center, calc(100% - 14px) center;
	background-size: 6px 6px, 6px 6px;
	background-repeat: no-repeat;
	padding-right: 42px;
	color: var(--kt-text-bright);
}

select option {
	background: var(--kt-ground-2);
	color: var(--kt-text-bright);
}

/*
 * Checkboxes and radios.
 *
 * `accent-color` alone only tints the *checked* state — an unchecked box keeps
 * the operating system's light-grey square, which on this ground was the
 * brightest object on the My Account page. So the control is drawn from
 * scratch: field fill, gold hairline, gold mark.
 *
 * Deliberately excluded: anything WooCommerce Blocks owns. The block checkout
 * paints its own tick as a separate absolutely-positioned <svg> next to the
 * input, so an `appearance: none` box with a pseudo-element mark would draw the
 * tick twice. Those controls are restyled in checkout-blocks.css instead.
 */
input[type="checkbox"]:not([class*="wc-block"]),
input[type="radio"]:not([class*="wc-block"]) {
	-webkit-appearance: none;
	appearance: none;
	display: inline-grid;
	place-content: center;
	flex: none;
	width: 18px;
	height: 18px;
	margin-right: 10px;
	padding: 0;
	border: 1px solid var(--kt-border-control);
	border-radius: var(--kt-radius-sm);
	background: var(--kt-field);
	vertical-align: -4px;
	cursor: pointer;
	transition: border-color var(--kt-ease), background var(--kt-ease);
}

input[type="radio"]:not([class*="wc-block"]) {
	border-radius: var(--kt-radius-pill);
}

input[type="checkbox"]:not([class*="wc-block"])::before,
input[type="radio"]:not([class*="wc-block"])::before {
	content: "";
	width: 11px;
	height: 11px;
	background: var(--kt-gold);
	transform: scale(0);
	transition: transform var(--kt-ease);
	/* A drawn tick — no glyph, so no dependency on a font shipping one. */
	clip-path: polygon(14% 46%, 0 62%, 42% 100%, 100% 20%, 84% 6%, 40% 70%);
}

input[type="radio"]:not([class*="wc-block"])::before {
	width: 8px;
	height: 8px;
	border-radius: var(--kt-radius-pill);
	clip-path: none;
}

input[type="checkbox"]:not([class*="wc-block"]):checked::before,
input[type="radio"]:not([class*="wc-block"]):checked::before {
	transform: scale(1);
}

input[type="checkbox"]:not([class*="wc-block"]):hover,
input[type="radio"]:not([class*="wc-block"]):hover,
input[type="checkbox"]:not([class*="wc-block"]):checked,
input[type="radio"]:not([class*="wc-block"]):checked {
	border-color: var(--kt-gold-line);
}

/* Search form */
.kt-searchform {
	display: flex;
	max-width: 560px;
	margin-inline: auto;
}

.kt-searchform input[type="search"] {
	border-right: none;
	border-radius: var(--kt-radius-sm) 0 0 var(--kt-radius-sm);
}

.kt-searchform button {
	flex: none;
	border: 0;
	border-radius: 0 var(--kt-radius-sm) var(--kt-radius-sm) 0;
	background: var(--kt-gold);
	color: var(--kt-on-gold);
	padding: 0 26px;
	font-size: 11px;
	font-weight: 800;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	cursor: pointer;
	transition: background var(--kt-ease);
}

.kt-searchform button:hover {
	background: var(--kt-gold-hi);
}

/* --------------------------------------------------------------------------
   18. Notices & empty states
   -------------------------------------------------------------------------- */

.kt-notice {
	border: 1px solid var(--kt-frame);
	border-left-width: 3px;
	border-left-color: var(--kt-gold);
	border-radius: var(--kt-radius-sm);
	background: var(--kt-panel);
	padding: 16px 20px;
	font-size: 14px;
	color: var(--kt-text-body);
	margin-bottom: 20px;
}

.kt-notice--error { border-left-color: var(--kt-err); }
.kt-notice--success { border-left-color: var(--kt-ok); }

.kt-empty {
	text-align: center;
	padding: clamp(48px, 7vw, 90px) 0;
}

.kt-empty__mark {
	width: 78px;
	height: 78px;
	border: 1px solid var(--kt-gold-line);
	border-radius: var(--kt-radius-pill);
	display: grid;
	place-items: center;
	margin: 0 auto 26px;
}

.kt-empty__title {
	font-family: var(--kt-font-display);
	font-size: clamp(20px, 3vw, 28px);
	letter-spacing: 0.14em;
	color: var(--kt-gold-soft);
	text-transform: uppercase;
}

.kt-empty__text {
	font-size: 14.5px;
	line-height: 1.75;
	color: var(--kt-text-muted);
	max-width: 460px;
	margin: 14px auto 0;
	font-weight: 300;
}

.kt-empty__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 14px;
	justify-content: center;
	margin-top: 30px;
}

/* --------------------------------------------------------------------------
   19. Comments
   -------------------------------------------------------------------------- */

.kt-comments {
	max-width: 760px;
	margin: clamp(40px, 5vw, 64px) auto 0;
}

.kt-comments__title {
	font-family: var(--kt-font-display);
	font-size: clamp(17px, 2.2vw, 22px);
	letter-spacing: 0.16em;
	color: var(--kt-gold-soft);
	text-transform: uppercase;
	margin-bottom: 26px;
}

.kt-comments ol.comment-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.kt-comments .comment-body {
	border-top: 1px solid var(--kt-hairline);
	padding: 22px 0;
}

.kt-comments .children {
	list-style: none;
	margin: 0;
	padding-left: clamp(16px, 4vw, 40px);
}

.kt-comments .comment-author {
	font-size: 12px;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--kt-gold-dim);
}

.kt-comments .comment-metadata {
	font-size: 11.5px;
	color: var(--kt-text-faint);
	margin-bottom: 10px;
}

/* --------------------------------------------------------------------------
   20. Utilities
   -------------------------------------------------------------------------- */

.kt-center { text-align: center; }
.kt-mt-0 { margin-top: 0; }
.kt-flex-spacer { flex: 1; }

.kt-stack-sm > * + * { margin-top: 10px; }
.kt-stack-md > * + * { margin-top: 20px; }

/* Small-screen tuning */
@media (max-width: 600px) {
	.kt-specialty {
		border-left: none;
		border-top: 1px solid var(--kt-divider);
		padding-top: 34px;
	}

	.kt-specialties__grid {
		margin-left: 0;
		row-gap: 0;
	}

	.kt-hero__actions .kt-btn {
		flex: 1 1 100%;
	}

	.kt-footer__bar {
		justify-content: center;
		text-align: center;
	}
}

/* Print */
@media print {
	.kt-header,
	.kt-drawer,
	.kt-footer,
	.kt-hero__img,
	.kt-promo,
	.kt-skip-link {
		display: none !important;
	}

	body,
	.kt-site {
		background: #fff !important;
		color: #000 !important;
	}

	.kt-site::before { display: none; }
}

/* ==========================================================================
   Paragraph margin reset for component text
   --------------------------------------------------------------------------
   These components use <p> for their text, so the global
   `p { margin: 0 0 1.1em }` above applies to them. Most set only `margin-top`,
   which left an unreset 1.1em underneath — visible as the lopsided gap in the
   stats row, where the value sat high, the label sat low, and the circular mark
   no longer lined up with either.

   Reset the box here rather than in each component, so the next <p>-based
   component does not inherit the same bug.
   ========================================================================== */

.kt-stat__value,
.kt-stat__label,
.kt-specialty__text,
.kt-dish__text,
.kt-dish__price,
.kt-hero__script,
.kt-hero__lede,
.kt-promo__kicker,
.kt-promo__meta,
.kt-quote__text,
.kt-quote__cite,
.kt-feature__text,
.kt-faq__a,
.kt-footer__about,
.kt-specialties__tag,
.kt-empty__text {
	margin-block-end: 0;
}

/* Deliberately NOT in the list above: .kt-subheading, .kt-dish__badge and
   .kt-footer__note each set their own bottom margin on purpose. Adding them
   collapsed the gap under the section sub-headline and under the card badge. */

/* Restore the intended leading gaps that were previously doing double duty
   alongside the inherited bottom margin. */
.kt-stat__value { margin-block-start: 0; }
.kt-stat__label { margin-block-start: 6px; }

/* --------------------------------------------------------------------------
   Frameless panel
   --------------------------------------------------------------------------
   A panel that groups already-bordered children (the dish cards in Menu
   Highlights) must not draw a frame of its own — a bordered box wrapping three
   bordered boxes reads as clutter. The heading and the cards carry the
   grouping; the panel only supplies rhythm.

   Panels whose children are NOT bordered — the stats row, the FAQ grid — keep
   their frame and are deliberately left alone.
   -------------------------------------------------------------------------- */

.kt-panel--frameless {
	border: 0;
	border-radius: 0;
	background: none;
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
	padding-inline: 0;
	padding-block: clamp(8px, 1.5vw, 18px);
}

/* --------------------------------------------------------------------------
   Header actions — icons on mobile, label on desktop
   --------------------------------------------------------------------------
   The mobile bar previously showed a bordered "CART · 4" pill next to a
   bordered burger: two competing boxes and a word where an icon reads faster.
   On small screens the cart collapses to a basket glyph with its count, a
   search glyph appears, and every border comes off — the bar's own bottom
   hairline is enough separation.
   -------------------------------------------------------------------------- */

.kt-icon-link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 42px;
	height: 42px;
	color: var(--kt-gold);
	flex: none;
	transition: color var(--kt-ease);
}

.kt-icon-link:hover,
.kt-icon-link:focus-visible {
	color: var(--kt-gold-hi);
}

/*
 * The header icons carry the burger's stroke weight.
 *
 * kty_icon() draws into a 32-unit viewBox at stroke-width 1.3, so at the
 * header's 20px the stroke lands at 1.3 × 20 ÷ 32 = 0.81px — barely half the
 * 1.5px of the burger bars sitting immediately beside them, which is why the
 * search and cart read as lighter than the menu. 2.4 user units × 20 ÷ 32
 * = exactly 1.5px.
 *
 * Set here rather than on kty_icon() because that helper has 20 callers at
 * sizes from 16px to 64px, and a single global value cannot be right for all of
 * them. `vector-effect: non-scaling-stroke` would express the intent directly
 * and size-independently, but it only applies as an ATTRIBUTE — set through CSS
 * it computes to `none` in Chromium, so it is not an option from a stylesheet.
 * If the header icon size ever changes, this number has to change with it.
 */
.kt-icon-link svg,
.kt-cart-link svg {
	stroke-width: 2.4;
}

.kt-cart-link__icon {
	display: none;               /* desktop shows the word instead */
	align-items: center;
}

@media (max-width: 899px) {
	.kt-icon-link {
		display: inline-flex;
	}

	.kt-cart-link__icon {
		display: inline-flex;
	}

	/* The word "Cart ·" is redundant beside a basket glyph. */
	.kt-cart-link__label {
		display: none;
	}

	/* No boxes on the mobile bar. */
	.kt-cart-link,
	.kt-burger {
		border: 0;
		background: transparent;
		padding: 0;
		width: 42px;
		height: 42px;
		justify-content: center;
		gap: 0;
	}

	.kt-cart-link:hover,
	.kt-cart-link:focus-visible {
		background: transparent;
		color: var(--kt-gold-hi);
	}

	/* Count rides the basket as a small gold marker rather than sitting inline. */
	.kt-cart-link {
		position: relative;
	}

	/* Scoped to the header link. There is a second .kt-cart-count inside the
	   drawer's "View cart" button; absolutely positioning that one tore the
	   number out of the button and parked it in the page's top-right corner. */
	.kt-cart-link .kt-cart-count {
		position: absolute;
		top: 2px;
		inset-inline-end: 0;
		min-width: 16px;
		height: 16px;
		display: grid;
		place-items: center;
		padding-inline: 4px;
		border-radius: var(--kt-radius-pill, 999px);
		background: var(--kt-gold);
		color: var(--kt-on-gold);
		font-size: 10px;
		font-weight: 800;
		line-height: 1;
	}

	.kt-cart-count:empty,
	.kt-header__actions {
		gap: 2px;
	}
}

/* The drawer's cart button keeps its count inline, as part of the label. */
.kt-drawer__cart .kt-cart-count {
	position: static;
	display: inline;
	min-width: 0;
	height: auto;
	padding: 0;
	border-radius: 0;
	background: none;
	color: inherit;
	font-size: inherit;
	font-weight: inherit;
}

/* No boxes in the drawer header either — matches the borderless top bar. */
.kt-drawer__close {
	border: 0;
	width: 42px;
	height: 42px;
	font-size: 26px;
}

/* --------------------------------------------------------------------------
   Desktop header actions — the same borderless icon set as mobile
   -------------------------------------------------------------------------- */

@media (min-width: 900px) {
	/* Account has an icon now, so the text link beside it would repeat itself.
	   Tagged server-side in kty_tag_account_menu_item(); the drawer keeps the
	   text link because there is no account icon at mobile widths. */
	.kt-nav .kt-nav__item--account {
		display: none;
	}

	/* Match the mobile treatment: glyphs, no boxes. */
	.kt-cart-link {
		position: relative;
		border: 0;
		background: transparent;
		padding: 0;
		width: 42px;
		height: 42px;
		justify-content: center;
		gap: 0;
	}

	.kt-cart-link:hover,
	.kt-cart-link:focus-visible {
		background: transparent;
		color: var(--kt-gold-hi);
	}

	.kt-cart-link__icon {
		display: inline-flex;
	}

	.kt-cart-link__label {
		display: none;
	}

	.kt-cart-link .kt-cart-count {
		position: absolute;
		top: 2px;
		inset-inline-end: 0;
		min-width: 16px;
		height: 16px;
		display: grid;
		place-items: center;
		padding-inline: 4px;
		border-radius: var(--kt-radius-pill, 999px);
		background: var(--kt-gold);
		color: var(--kt-on-gold);
		font-size: 10px;
		font-weight: 800;
		line-height: 1;
	}

	.kt-header__actions {
		gap: 4px;
	}
}

/* The desktop bar is a little taller than the phone one. Changing this number
   changes the header itself — nothing else needs touching. */
@media (min-width: 901px) {
	:root {
		--kt-header-h: 64px;
	}
}

/* ---------------------------------------------------------------------------
   Instant search panel
   --------------------------------------------------------------------------
   Drops out of the header rather than covering the page: the customer keeps
   sight of where they are, and on a phone the field lands directly under the
   thumb that tapped the icon.

   `hidden` is the closed state, so with JavaScript off the panel never
   participates in layout at all and the icon stays a plain link to /?s=.
   -------------------------------------------------------------------------- */

.kt-search-panel {
	position: fixed;
	inset: var(--kt-header-h) 0 auto;
	z-index: 60;
	padding: clamp(14px, 2.4vw, 22px) var(--kt-gutter) clamp(18px, 3vw, 26px);
	background: rgba(11, 10, 9, 0.94);
	background: oklch(0.08 0.004 60 / 0.94);
	-webkit-backdrop-filter: blur(18px);
	backdrop-filter: blur(18px);
	border-bottom: 1px solid var(--kt-gold-line);
	opacity: 0;
	transform: translateY(-8px);
	/* Exit: 200ms soft. The panel used to be hidden synchronously on close, so
	   its 180ms entrance had a 0ms exit — it vanished in one frame on Escape. */
	transition: opacity 200ms var(--kt-ease-soft), transform 200ms var(--kt-ease-soft);
}

.kt-search-panel.is-open {
	opacity: 1;
	transform: none;
	/* Entrance a touch livelier than the exit, on the arrival curve. */
	transition: opacity 260ms var(--kt-ease-out, cubic-bezier(0.22, 1, 0.36, 1)), transform 260ms var(--kt-ease-out, cubic-bezier(0.22, 1, 0.36, 1));
}

/*
 * Grid, not "form with padding for an absolutely-positioned close button".
 * The focus ring is drawn on `.kt-searchform`, so any padding reserved on the
 * form for the close button lands INSIDE the ring — the ring then wrapped the
 * × as well and left dead space after the submit. Giving the close its own
 * column keeps the form box exactly as wide as the control it frames.
 */
.kt-search-panel__inner {
	max-width: 720px;
	margin: 0 auto;
	display: grid;
	grid-template-columns: 1fr auto;
	align-items: center;
	gap: 12px;
}

.kt-search-panel__close {
	width: 40px;
	height: 40px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background: none;
	border: 0;
	font-size: 26px;
	line-height: 1;
	color: var(--kt-gold);
	cursor: pointer;
	transition: color var(--kt-ease);
}

.kt-search-panel__close:hover,
.kt-search-panel__close:focus-visible {
	color: var(--kt-gold-hi);
}

/* Results are capped so the panel never runs past the fold on a phone. */
.kt-search-panel__results {
	grid-column: 1 / -1;
	display: flex;
	flex-direction: column;
	max-height: min(52vh, 420px);
	overflow-y: auto;
	overscroll-behavior: contain;
}

.kt-search-hit {
	display: grid;
	grid-template-columns: 1fr auto;
	align-items: baseline;
	gap: 2px 14px;
	padding: 11px 12px;
	border-radius: var(--kt-radius-sm);
	text-decoration: none;
	transition: background-color var(--kt-ease);
}

.kt-search-hit:hover,
.kt-search-hit:focus-visible,
.kt-search-hit.is-active {
	background: rgba(193, 159, 95, 0.1);
	background: oklch(0.72 0.06 85 / 0.1);
}

.kt-search-hit__name {
	font-size: 15px;
	color: var(--kt-text);
}

.kt-search-hit__price {
	grid-column: 2;
	grid-row: 1;
	font-size: 14px;
	color: var(--kt-gold);
	white-space: nowrap;
}

.kt-search-hit__cat {
	grid-column: 1;
	font-size: 11.5px;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--kt-text-muted);
}

.kt-search-panel__empty {
	margin: 0;
	padding: 12px;
	font-size: 14px;
	color: var(--kt-text-muted);
}

/* The page behind the panel should not scroll away under it. */
body.kt-search-open {
	overflow: hidden;
}

/* The panel's field should own the width — `.kt-searchform` is centred and
   capped for the search page, where it is the whole content of a hero. Here it
   is a header bar, and the submit is a secondary affordance: the results appear
   as you type, and this button only exists to reach the full results page. */
.kt-search-panel .kt-searchform {
	max-width: none;
	margin-inline: 0;
}

.kt-search-panel .kt-searchform input[type="search"] {
	flex: 1 1 auto;
	min-width: 0;
}

.kt-search-panel .kt-searchform button {
	padding: 0 16px;
	font-size: 10px;
	letter-spacing: 0.16em;
}

/* ---------------------------------------------------------------------------
   Search field — one frame, and no WebKit furniture
   --------------------------------------------------------------------------
   Two separate bugs showed up as "the search bar looks strange in Safari".

   1. The doubled frame. `.kt-searchform` is a joined group: the input carries
      `border-right: none` and only its left corners are rounded, and the submit
      supplies the right half. The global focus ring
      (`input:focus-visible { outline: 2px; outline-offset: 3px }`) is drawn
      around the INPUT alone, so on focus a fully-rounded gold rectangle
      appeared 3px outside a half-rounded box and ran on underneath the gold
      button — reading as a second, misaligned border.

      The ring belongs on the group. `:focus-within` also covers the submit
      button, which is what you want for a control that behaves as one unit.
      Removing the inner outline is safe precisely because the wrapper ring
      replaces it; focus is never left unindicated.

   2. The grey ⊗. `appearance: none` does not remove
      `::-webkit-search-cancel-button` — it is a shadow-DOM pseudo-element and
      needs its own reset. It rendered as a light grey circle on the near-black
      field in both Safari and Chrome. The panel already has an explicit close
      control, so the native one is dropped rather than restyled.
   -------------------------------------------------------------------------- */

.kt-searchform:focus-within {
	outline: 2px solid var(--kt-gold-soft);
	outline-offset: 3px;
	border-radius: var(--kt-radius-sm);
}

/* Matches the global ring's specificity on `input:focus-visible` and wins on
   source order; scoped so every other field keeps its own outline. */
.kt-searchform input[type="search"]:focus,
.kt-searchform input[type="search"]:focus-visible,
.kt-searchform button:focus,
.kt-searchform button:focus-visible {
	outline: none;
}

.kt-searchform input[type="search"]::-webkit-search-cancel-button,
.kt-searchform input[type="search"]::-webkit-search-decoration,
.kt-searchform input[type="search"]::-webkit-search-results-button,
.kt-searchform input[type="search"]::-webkit-search-results-decoration {
	-webkit-appearance: none;
	appearance: none;
	display: none;
}

/* ---------------------------------------------------------------------------
   Cart count badge — a real circle, with the digit actually centred
   --------------------------------------------------------------------------
   Measured on the live header at a count of 8: the badge was 16.6 x 16, so not
   round, and the digit sat left of centre.

   Both come from the same place. The badge inherits the header's letter-spacing,
   which adds tracking AFTER the last character — the inline box measured 8.6px
   for a glyph that advances about 6px. `place-items: center` then centres that
   box, trailing space included, which pushes the visible digit left. The same
   2.6px also pushed the box past its 16px min-width, so it stopped being a
   circle.

   Zeroing the tracking fixes the offset and lets min-width govern the shape.
   Padding is small enough that one digit never exceeds min-width (circle) while
   two still expand into a pill. `tabular-nums` keeps 1 the same width as 8, so
   the badge does not resize as the cart changes.

   Declared outside the media queries that define the badge, and after them, so
   it applies at every breakpoint.
   -------------------------------------------------------------------------- */

.kt-cart-link .kt-cart-count {
	box-sizing: border-box;
	min-width: 18px;
	height: 18px;
	padding-inline: 3px;
	letter-spacing: 0;
	font-variant-numeric: tabular-nums;
	line-height: 1;

	/*
	 * Sat at `top: 2px`, which put the badge's lower edge at 20px — across the
	 * basket's rim at ~18.5px, so the icon's top-right corner was covered and
	 * the basket read as cut off. At 0 the badge clears the rim and the whole
	 * basket is visible; it still overlaps the corner of the 42px hit area, so
	 * nothing moves in the header layout.
	 */
	top: 0;
}

/* ---------------------------------------------------------------------------
   Dropdown hover bridge
   --------------------------------------------------------------------------
   `.kt-nav .sub-menu` opens at `top: calc(100% + 14px)`, so 14px of empty space
   separates the nav item from the panel. That strip belongs to neither box: a
   cursor travelling from "Order online" down to the categories leaves the item
   before it reaches the panel, `li:hover` goes false, and the menu closes under
   the pointer. Measured on the live header — item bottom y=50.1, panel top
   y=58.1.

   Hovering the panel itself was never the problem: `.sub-menu` is a DOM child of
   the item, so `li:hover` already holds while the pointer is inside it. Only the
   gap needed covering.

   The bridge cannot be `.sub-menu::before`. The panel is a scroll container —
   `overflow-y: auto`, because 17 categories overflow a laptop viewport — and a
   scroll container clips generated content outside its padding box, so a bridge
   there would be invisible and un-hittable.

   So it hangs off the item, and only while that item is hovered or focused:
   a permanent transparent strip below the header would sit over the hero and
   swallow clicks. It is the panel's width, not the item's, because the panel is
   230px against a ~120px item and overhangs 55px either side — a cursor moving
   diagonally toward a category on the far edge would otherwise still fall out.
   -------------------------------------------------------------------------- */

.kt-nav li.menu-item-has-children:hover::after,
.kt-nav li.menu-item-has-children:focus-within::after {
	content: "";
	position: absolute;
	top: 100%;
	left: 50%;
	width: 230px;
	height: 16px;
	transform: translateX(-50%);
}

/* ==========================================================================
   Motion
   --------------------------------------------------------------------------
   Restraint is the whole point. Everything here is opacity and transform only,
   so it composites and never reflows; nothing moves more than 20px; nothing
   overshoots. The room is dark and the type is Cinzel — the motion should feel
   like weight settling, not like a UI showing off.

   PROGRESSIVE ENHANCEMENT, DELIBERATELY
   The reveal states are scoped to `html.kt-motion`, a class theme.js adds at
   run time. Without JavaScript — or if the script fails — none of these
   selectors match and every element is simply visible. Content must never
   depend on a script to be seen; that is the one way a reveal animation can
   actually break a page.

   The global `prefers-reduced-motion` reset near the top of this file already
   flattens every animation and transition site-wide, so nothing below needs its
   own opt-out.
   ========================================================================== */

/*
 * TWO BUGS LIVED HERE, and together they silenced the site's centrepiece
 * motion — every reveal SNAPPED instead of rising, verified live
 * (transitionDuration computed 0s on a pending element).
 *
 * 1. --kt-motion-reveal already contains "760ms cubic-bezier(...)". Appending
 *    var(--kt-ease-out) produced TWO easing functions in one transition item,
 *    which is invalid at computed-value time — and an invalid var() substitution
 *    doesn't drop the declaration, it collapses the property to its initial
 *    value. The token carries its own curve; nothing may add another.
 * 2. The transition was declared only on the :not(.is-in) state. A transition
 *    runs from the AFTER-change style; the moment is-in landed, the rule — and
 *    the transition with it — no longer matched. It must live on the selector
 *    that matches in both states.
 */
.kt-motion [data-kt-reveal] {
	transition:
		opacity var(--kt-motion-reveal),
		transform var(--kt-motion-reveal);
	transition-delay: var(--kt-reveal-delay, 0ms);
}

.kt-motion [data-kt-reveal]:not(.is-in) {
	opacity: 0;
	transform: translate3d(0, 20px, 0);
	will-change: opacity, transform;
}

.kt-motion [data-kt-reveal].is-in {
	opacity: 1;
	transform: none;
	/* Dropped once it has landed: leaving it on holds a compositor layer for
	   every revealed element, which on the 146-dish menu is a real cost. */
	will-change: auto;
}

/* --------------------------------------------------------------------------
   Hero — the one entrance that plays on load
   --------------------------------------------------------------------------
   Staggered so the eye is led down the column: name, then the script line, then
   the sentence, then the buttons. 90ms apart is enough to read as sequence
   without anyone waiting for the last one.
   -------------------------------------------------------------------------- */

.kt-motion .kt-hero__title-over,
.kt-motion .kt-hero__title-main,
.kt-motion .kt-hero__script,
.kt-motion .kt-hero__lede,
.kt-motion .kt-hero__actions {
	opacity: 0;
	transform: translate3d(0, 14px, 0);
	animation: kt-rise 900ms var(--kt-ease-out) forwards;
}

.kt-motion .kt-hero__title-over  { animation-delay: 60ms; }
.kt-motion .kt-hero__title-main  { animation-delay: 150ms; }
.kt-motion .kt-hero__script      { animation-delay: 300ms; }
.kt-motion .kt-hero__lede        { animation-delay: 390ms; }
.kt-motion .kt-hero__actions     { animation-delay: 480ms; }

@keyframes kt-rise {
	to {
		opacity: 1;
		transform: none;
	}
}

/* --------------------------------------------------------------------------
   Hairlines
   --------------------------------------------------------------------------
   The gold rules under section headings draw themselves in from the centre.
   `transform: scaleX()` on an existing element — no extra markup, no layout.
   -------------------------------------------------------------------------- */

.kt-motion [data-kt-reveal] .kt-ornament__rule,
.kt-motion [data-kt-reveal] .kt-res-panel__rule,
.kt-motion [data-kt-reveal] .kt-menu__cat-rule {
	transform: scaleX(0);
	transform-origin: center;
	transition: transform 900ms var(--kt-ease-out) 180ms;
}

.kt-motion [data-kt-reveal].is-in .kt-ornament__rule,
.kt-motion [data-kt-reveal].is-in .kt-res-panel__rule,
.kt-motion [data-kt-reveal].is-in .kt-menu__cat-rule {
	transform: scaleX(1);
}

/* --------------------------------------------------------------------------
   Hover — a lift, not a jump
   --------------------------------------------------------------------------
   2px and a warmer hairline. The card already has a border and a translucent
   fill, so the whole effect is the edge catching a little more light. Guarded
   on `hover: hover` so a touch device never sticks in the hovered state after a
   tap.
   -------------------------------------------------------------------------- */

@media (hover: hover) {
	.kt-dish {
		transition:
			transform var(--kt-motion-hover),
			border-color var(--kt-motion-hover),
			background-color var(--kt-motion-hover);
	}

	.kt-dish:hover,
	.kt-dish:focus-within {
		transform: translate3d(0, -2px, 0);
		border-color: var(--kt-gold-line);
	}

	/* The gold CTAs get a slow sheen crossing them. `overflow: hidden` and a
	   skewed pseudo-element, so there is nothing to clean up and nothing that
	   can escape the button's own box. */
	/*
	 * The doubled class is load-bearing — this is what CONTAINS the sheen.
	 *
	 * WooCommerce ships
	 *   .woocommerce:where(body:not(…)) button.button { overflow: visible }
	 * which scores (0,2,1) and outranks a plain `.kt-btn--solid` at (0,1,0). So
	 * on every WooCommerce button — Add to cart included — the clip silently did
	 * not apply, and the highlight swept OUTSIDE the button instead of across
	 * it: visible off the left edge, which is exactly where it starts. Repeating
	 * the modifier takes this to (0,3,0), decided on class count rather than on
	 * which stylesheet happens to load last.
	 *
	 * `overflow: hidden` rather than `clip-path: inset(0 round …)`, which would
	 * also have won: clip-path clips the focus ring too, and a keyboard user
	 * losing their focus outline is a worse bug than the one being fixed here.
	 */
	.kt-btn.kt-btn--solid.kt-btn--solid,
	.kt-btn.kt-btn--gold.kt-btn--gold {
		position: relative;
		overflow: hidden;
	}

	.kt-btn--solid::after,
	.kt-btn--gold::after {
		content: "";
		position: absolute;
		top: 0;
		bottom: 0;
		left: -60%;
		width: 40%;
		background: linear-gradient(
			100deg,
			transparent,
			rgba(255, 255, 255, 0.28),
			transparent
		);
		transform: skewX(-18deg);
		opacity: 0;
		pointer-events: none;
	}

	.kt-btn--solid:hover::after,
	.kt-btn--gold:hover::after {
		animation: kt-sheen 900ms var(--kt-ease-soft) 1;
	}

	/* Solid and gold flip at the same tempo as the ghost beside them — the
	   hero pairs the two voices in one row, and 180ms against 320ms read as a
	   glitch rather than a choice. */
	.kt-btn--solid,
	.kt-btn--gold {
		transition:
			background-color var(--kt-motion-hover),
			color var(--kt-motion-hover),
			border-color var(--kt-motion-hover);
	}

	/* Ghost buttons and nav items warm rather than move. */
	.kt-btn--ghost {
		transition:
			border-color var(--kt-motion-hover),
			color var(--kt-motion-hover),
			background-color var(--kt-motion-hover);
	}

	.kt-icon-link,
	.kt-cart-link {
		transition: transform var(--kt-motion-hover), color var(--kt-motion-hover);
	}

	.kt-icon-link:hover {
		transform: translate3d(0, -1px, 0);
	}
}

/* --------------------------------------------------------------------------
   Language switcher — one control, not three
   --------------------------------------------------------------------------
   Two presentations of the same markup:

   NO JS (default)   a flat row of short codes, exactly what this used to be,
                     so language switching survives without scripting.
   WITH JS (.kt-js)  a disclosure — one 38px control matching search, account
                     and cart, opening a menu that names the languages in full.

   The inline script in header.php swaps no-js for kt-js before first paint, so
   the header never renders one layout and then jumps to the other.
   -------------------------------------------------------------------------- */

.kt-lang {
	position: relative;
	flex: none;
}

/* --- the no-JS row -------------------------------------------------------- */

.kt-lang__toggle {
	display: none;
}

.kt-lang__menu {
	display: flex;
	align-items: center;
	gap: 2px;
	list-style: none;
	margin: 0;
	padding: 0;
}

.kt-lang__opt {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 30px;
	height: 30px;
	padding: 0 5px;
	border-radius: var(--kt-radius-sm);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.08em;
	line-height: 1;
	color: var(--kt-text-faint);
	transition: color var(--kt-ease), background var(--kt-ease);
}

/* Without JS the full name is the accessible name only — the short code is
   what fits a header. With JS the roles swap (below). */
.kt-lang__full {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.kt-lang__opt:hover,
.kt-lang__opt:focus-visible {
	color: var(--kt-text-body);
}

.kt-lang__opt.is-active {
	background: var(--kt-hairline-faint);
	color: var(--kt-text);
}

/* --- the disclosure ------------------------------------------------------- */

.kt-js .kt-lang__toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 4px;
	min-width: 38px;
	height: 38px;
	padding: 0 8px;
	border: 0;
	background: none;
	border-radius: var(--kt-radius-sm);
	font-family: inherit;
	/* The navbar's exact type (theme.css:835): 12.5px / 500 / 0.22em / caps.
	   The switcher sits on the same bar and reads as a peer of HOME and MENU,
	   not as a smaller utility bolted beside them. Letter-spacing is trimmed
	   to 0.14em only because two or three characters with 0.22em of trailing
	   space look off-centre in a square control. */
	font-size: 12.5px;
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	line-height: 1;
	color: oklch(0.85 0.03 88);
	cursor: pointer;
	transition: color var(--kt-ease), background var(--kt-ease);
}

.kt-js .kt-lang__toggle:hover,
.kt-js .kt-lang__toggle:focus-visible,
.kt-js .kt-lang.is-open .kt-lang__toggle {
	color: var(--kt-text);
	background: var(--kt-hairline-faint);
}

/* A 4px chevron drawn from a rotated square — no icon file for one glyph. */
.kt-lang__caret {
	display: none;
	width: 4px;
	height: 4px;
	border-right: 1px solid currentColor;
	border-bottom: 1px solid currentColor;
	transform: translateY(-1px) rotate(45deg);
	transition: transform var(--kt-ease);
}

.kt-js .kt-lang__caret {
	display: block;
}

.kt-js .kt-lang.is-open .kt-lang__caret {
	transform: translateY(1px) rotate(225deg);
}

.kt-js .kt-lang__menu {
	position: absolute;
	top: calc(100% + 8px);
	right: 0;
	z-index: 70;
	display: block;
	min-width: 170px;
	padding: 6px;
	background: var(--kt-ground-2);
	border: 1px solid var(--kt-border-control);
	border-radius: var(--kt-radius-sm);
	box-shadow: 0 14px 34px rgba(0, 0, 0, 0.5);
	opacity: 0;
	visibility: hidden;
	transform: translateY(-6px);
	/* The overlay manner used everywhere on this site: 200ms out on the soft
	   curve, 260ms in on the arrival curve. */
	transition: opacity 200ms var(--kt-ease-soft), transform 200ms var(--kt-ease-soft), visibility 0s 200ms;
}

.kt-js .kt-lang.is-open .kt-lang__menu {
	opacity: 1;
	visibility: visible;
	transform: none;
	transition: opacity 260ms var(--kt-ease-out, cubic-bezier(0.22, 1, 0.36, 1)), transform 260ms var(--kt-ease-out, cubic-bezier(0.22, 1, 0.36, 1)), visibility 0s;
}

/*
 * HOVER TO OPEN is driven from JS, not from a CSS :hover rule on the menu.
 *
 * The control is a real menu button carrying aria-expanded, and that attribute
 * has to describe what is actually on screen. A CSS-only hover would show the
 * menu while aria-expanded stayed "false" — the header's other dropdowns can
 * use pure CSS precisely because they are plain links with no state to lie
 * about. So `is-open` remains the single source of truth and JS sets it on
 * pointer enter/leave; see initLangSwitcher() in theme.js.
 *
 * What CSS still owns is the toggle's own hover skin, which is styling and
 * carries no state.
 */
@media (hover: hover) and (pointer: fine) {
	.kt-js .kt-lang__toggle:hover {
		color: var(--kt-text);
		background: var(--kt-hairline-faint);
	}
}

/*
 * The hover bridge — same problem and same fix as the nav's own
 * (see "Dropdown hover bridge" below). The menu opens 8px under the toggle,
 * and that strip belongs to neither box: a cursor travelling down to the
 * languages leaves .kt-lang, pointerleave fires, and the menu closes under the
 * pointer. The menu is a DOM child of .kt-lang, so the panel itself was never
 * the problem; only the gap needed covering.
 *
 * Tied to `.is-open` rather than `:hover` — it is needed exactly when the menu
 * is showing, and a permanent transparent strip under the header would sit
 * over the hero and swallow clicks. Right-aligned at the menu's full width,
 * because the 170px panel overhangs the 38px toggle to the LEFT: a diagonal
 * move toward 简体中文 would otherwise fall straight out of the hit area.
 */
@media (hover: hover) and (pointer: fine) {
	.kt-js .kt-lang.is-open::after {
		content: "";
		position: absolute;
		top: 100%;
		right: 0;
		width: 170px;
		height: 10px;
	}
}

.kt-js .kt-lang__opt {
	justify-content: flex-start;
	width: 100%;
	min-width: 0;
	height: auto;
	padding: 10px 11px;
	font-size: 13px;
	font-weight: 400;
	letter-spacing: 0.04em;
	white-space: nowrap;
	color: var(--kt-text-body);
}

/* Inside the menu the full name is the label and the short code is redundant. */
.kt-js .kt-lang__short {
	display: none;
}

.kt-js .kt-lang__full {
	position: static;
	width: auto;
	height: auto;
	margin: 0;
	overflow: visible;
	clip-path: none;
}

.kt-js .kt-lang__opt.is-active {
	background: none;
	color: var(--kt-gold);
	font-weight: 600;
}

/* A gold tick on the current language: the menu says what you are reading
   without relying on weight alone. */
.kt-js .kt-lang__opt.is-active::after {
	content: "";
	width: 4px;
	height: 8px;
	margin-left: auto;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: rotate(45deg) translate(-1px, -1px);
}

.kt-js .kt-lang__opt:hover,
.kt-js .kt-lang__opt:focus-visible {
	background: var(--kt-hairline-faint);
	color: var(--kt-text);
}

@media (prefers-reduced-motion: reduce) {
	.kt-js .kt-lang__menu,
	.kt-lang__caret {
		transition: none;
	}
}

@media (max-width: 600px) {
	.kt-js .kt-lang__toggle {
		min-width: 36px;
		height: 36px;
		padding: 0 6px;
		font-size: 12px;
	}

	/*
	 * VIEWPORT-ANCHORED, not toggle-anchored.
	 *
	 * On a phone the switcher is the FIRST control in the actions row, not the
	 * last — logo, then EN, then search, account, cart, burger — so its right
	 * edge sits mid-header, and a 170px menu hung off it runs off the left of
	 * the screen. Measured: 9px of clearance at 375px, and negative below
	 * ~366px, which is where the reported clipping came from. Worse, the
	 * toggle slides further left as the viewport narrows, so the overflow
	 * grows exactly when there is least room.
	 *
	 * Fixed to the viewport's right gutter with a clamped width, it cannot
	 * overflow at any size and reads as a small sheet dropping from the header
	 * — the ordinary mobile pattern. `top` follows the real header height, so
	 * it stays put whether the bar is 56px or 64px.
	 */
	.kt-js .kt-lang__menu {
		position: fixed;
		top: calc(var(--kt-header-h) + 8px);
		right: var(--kt-gutter, 16px);
		left: auto;
		width: min(240px, calc(100vw - (var(--kt-gutter, 16px) * 2)));
		min-width: 0;
		padding: 8px;
	}

	/* Comfortable thumbs: 46px rows, up from 33px, and text at reading size
	   rather than label size. */
	.kt-js .kt-lang__opt {
		min-height: 46px;
		padding: 12px 14px;
		font-size: 14px;
	}

	/* The no-JS row keeps its compact form. */
	.no-js .kt-lang__opt {
		min-width: 26px;
		height: 26px;
		padding: 0 3px;
		font-size: 10.5px;
	}
}

/* --------------------------------------------------------------------------
   Language suggestion bar
   --------------------------------------------------------------------------
   Bottom-anchored, quiet, and dismissible. Not a modal and not a cookie-wall:
   it must never block a dish, a price or the cart, so it sits above the fold's
   bottom edge and the page underneath stays fully usable while it is open.

   `hidden` does the hiding until theme.js decides — so with JavaScript off it
   simply never appears, which is the correct failure mode for a suggestion.
   -------------------------------------------------------------------------- */

.kt-langbar {
	position: fixed;
	z-index: 60;
	left: 50%;
	bottom: 18px;
	transform: translate(-50%, 12px);
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 8px 14px;
	max-width: min(560px, calc(100vw - 32px));
	padding: 12px 18px;
	background: var(--kt-ground-2);
	border: 1px solid var(--kt-gold-line-faint);
	border-radius: var(--kt-radius-sm);
	box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
	opacity: 0;
	transition: opacity 260ms var(--kt-ease-soft, ease), transform 260ms var(--kt-ease-soft, ease);
}

.kt-langbar[hidden] {
	display: none;
}

.kt-langbar.is-in {
	opacity: 1;
	transform: translate(-50%, 0);
}

.kt-langbar__text {
	margin: 0;
	font-size: 13px;
	line-height: 1.5;
	color: var(--kt-text-body);
}

/* The accepting action is the only gold thing here: it is the one the reader
   this bar exists for is looking for. */
.kt-langbar__accept {
	padding: 7px 14px;
	border: 1px solid var(--kt-gold);
	border-radius: var(--kt-radius-sm);
	background: var(--kt-gold);
	color: var(--kt-on-gold);
	font-size: 12.5px;
	font-weight: 700;
	line-height: 1;
	white-space: nowrap;
}

.kt-langbar__accept:hover,
.kt-langbar__accept:focus-visible {
	background: var(--kt-gold-hi);
	border-color: var(--kt-gold-hi);
	color: var(--kt-on-gold);
}

.kt-langbar__dismiss {
	padding: 7px 10px;
	border: 0;
	background: none;
	color: var(--kt-text-faint);
	font-family: inherit;
	font-size: 12.5px;
	line-height: 1;
	cursor: pointer;
	white-space: nowrap;
}

.kt-langbar__dismiss:hover,
.kt-langbar__dismiss:focus-visible {
	color: var(--kt-text-body);
}

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

@media (max-width: 600px) {
	.kt-langbar {
		left: 16px;
		right: 16px;
		bottom: 14px;
		transform: translateY(12px);
		max-width: none;
	}

	.kt-langbar.is-in {
		transform: translateY(0);
	}
}

/* --------------------------------------------------------------------------
   Shared micro-motion keyframes
   --------------------------------------------------------------------------
   kt-sheen lives OUT here, not in the @media (hover: hover) block where its
   trigger sits: the .added confirmation replays it after an add-to-cart, and
   most of those taps happen on phones — inside the media block the keyframes
   simply did not exist on the devices that order the most.
   -------------------------------------------------------------------------- */

@keyframes kt-sheen {
	0%   { left: -60%; opacity: 0; }
	18%  { opacity: 1; }
	100% { left: 120%; opacity: 0; }
}

/* One spinner for every in-flight button state. currentColor, so it inherits
   whatever the button's text does — dark on gold, gold on ghost. */
@keyframes kt-spin {
	to { transform: rotate(1turn); }
}

/* The cart badge acknowledging a change. transform-only: it composites, and
   the badge is inside the fixed header where layout would be felt. */
@keyframes kt-count-pop {
	0%   { transform: scale(1); }
	35%  { transform: scale(1.35); }
	100% { transform: scale(1); }
}

.kt-cart-link .kt-cart-count.kt-count-pop {
	animation: kt-count-pop 320ms var(--kt-ease-out, cubic-bezier(0.22, 1, 0.36, 1)) 1;
}

/* A qty input acknowledging its new value: one soft gold wash, felt not seen. */
@keyframes kt-qty-tick {
	0%   { background-color: color-mix(in oklab, var(--kt-gold) 22%, transparent); }
	100% { background-color: transparent; }
}

.kt-qty input.kt-qty-tick {
	animation: kt-qty-tick 480ms var(--kt-ease-soft) 1;
}

/* Small entrance for elements that pop into existence (the View-cart link
   after an add, cart totals after a refresh). */
@keyframes kt-fade-up {
	from {
		opacity: 0;
		transform: translate3d(0, 6px, 0);
	}
}

/* --------------------------------------------------------------------------
   Page-to-page continuity — MPA view transitions
   --------------------------------------------------------------------------
   menu → dish → cart used to hard-cut between two paints of the same
   near-black ground. A cross-document view transition fades the old page down
   200ms and the new one up 320ms — the exit/enter split every overlay here
   already uses. Pure progressive enhancement: browsers without support keep
   the hard cut, and the reduced-motion guard keeps it off entirely for anyone
   who asked for stillness.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
	@view-transition {
		navigation: auto;
	}

	::view-transition-old(root) {
		animation: kt-vt-out 200ms var(--kt-ease-soft) both;
	}

	::view-transition-new(root) {
		animation: kt-vt-in 320ms var(--kt-ease-soft) both;
	}
}

@keyframes kt-vt-out {
	to { opacity: 0; }
}

@keyframes kt-vt-in {
	from { opacity: 0; }
}

/* --------------------------------------------------------------------------
   Chinese hero — the script slot gets its whisper back
   --------------------------------------------------------------------------
   Great Vibes has no CJK, so on /zh-hant/ and /zh-hans/ the script line falls
   back to Noto Serif — the same face as the gold title above it, and the hero
   reads as three stacked headings where English reads caps / whisper / caps.
   No calligraphic CJK font ships with Traditional coverage worth its weight,
   so the whisper is rebuilt from what the slot controls: a step down in size,
   weight and colour. gold-dim keeps AA at these sizes (6.4:1).
   -------------------------------------------------------------------------- */

:lang(zh) .kt-hero__script {
	font-size: clamp(22px, 3vw, 30px);
	font-weight: 400;
	color: var(--kt-gold-dim);
	letter-spacing: 0.14em;
}

/* --------------------------------------------------------------------------
   The Gilding — what is left of it
   --------------------------------------------------------------------------
   This was four pieces: the room breathes, the name settles, the headline
   catches the light, the script writes itself. Three of them were removed in
   2.46.0 for stuttering; the note below the drift records exactly which and
   why. What remains is:

     0ms      the photograph starts a 36s drift it never finishes
     60ms+    the hero text rises and fades in, staggered — see kt-rise in
              the hero entrance block above, which is the whole animation now

   Every rule hangs off .kt-motion, which initReveal() never adds under
   prefers-reduced-motion — so this is inert for anyone who asked for
   stillness, and with JS off the hero is simply static and visible. The media
   guard on the drift, the one INFINITE piece, is belt and braces on top.

   The drift re-declares nothing of kt-rise because it animates a different
   element (the photograph, not the text). The pieces that DID share elements
   with kt-rise had to restate it in their own `animation` shorthand, since
   `animation` is a single property — that coupling is gone with them.
   -------------------------------------------------------------------------- */

/* 1 — the room breathes. Composited scale, clipped by the hero's own
   overflow:hidden; alternate so it never jumps home. */
@media (prefers-reduced-motion: no-preference) {
	.kt-motion .kt-hero__img {
		transform-origin: 50% 42%;
		animation: kt-hero-drift 36s ease-in-out infinite alternate;
	}
}

@keyframes kt-hero-drift {
	from { transform: scale(1); }
	to   { transform: scale(1.055); }
}

/* --------------------------------------------------------------------------
   PIECES 2, 3 AND 4 WERE REMOVED IN 2.46.0 — and why matters, because they
   looked harmless and were not.
   --------------------------------------------------------------------------
   The hero used to layer three more effects on top of the entrance above:

     2  kt-track-settle  animated LETTER-SPACING on the overline, 0.30em to
                         0.12em. letter-spacing is a layout property, so the
                         browser re-flowed that line on every frame of the
                         animation rather than compositing it.

     3  kt-gild          animated BACKGROUND-POSITION across a 300%-wide
                         gradient clipped to the glyphs with
                         background-clip: text — and did it `infinite` on a
                         9s loop. background-position is a paint property, and
                         clipping paint to glyph outlines is among the more
                         expensive things a browser can be asked to repeat.
                         It never stopped: for as long as the tab was open,
                         the headline was being repainted.

     4  kt-inkwipe       animated MASK-POSITION on the script line. Also paint,
                         and by 2.35.0 also pointless — hero_script is empty,
                         so it was animating an element that never renders.

   None of the three is compositor-only, which is what "stuttery" was: three
   layout-or-paint animations on the largest type on the page, one of them
   looping forever. The entrance that remains (kt-rise, above) moves only
   opacity and transform, which the compositor handles without touching layout
   or repainting a glyph.

   Piece 1, the photograph's slow drift, is deliberately KEPT. It animates
   `transform: scale()` only — the one member of the set the compositor can
   run for free — and it is the background rather than the text. To make the
   hero completely still, delete the kt-hero-drift rule above it.

   The gold headline now takes its colour from `.kt-hero__title-main`
   (`color: var(--kt-gold)`) as it did before the sheen, and the overline
   keeps its resting `letter-spacing: 0.12em`. Nothing else had to change.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Embers — the hero background's life
   --------------------------------------------------------------------------
   Sparse golden embers rising through the dark over a faint charcoal glow:
   the grill at dusk, which is not a metaphor this menu has to reach for. The
   layer sits between the veils and the content, so the words always read over
   it, and the section's own overflow:hidden clips every flight path.

   Taste rules that keep it the right side of kitsch: at most fourteen, none
   larger than 5px, none brighter than 50% opacity, every flight 17s or
   slower, and the field is already mid-life at first paint (negative delays)
   — embers drift; they never "start".

   The whole layer is display:none unless .kt-motion is set, which theme.js
   adds when JavaScript runs AND prefers-reduced-motion is off — so for a
   reduced-motion visitor this entire section does not exist, rather than
   existing frozen.

   SHARED, NOT HERO-SPECIFIC (2.38.0)

   These rules were named .kt-hero__embers and belonged to the front page.
   The field now runs in every hero from one template part
   (template-parts/embers.php), so the class is plain .kt-embers and carries
   no assumption about its container beyond position:relative and
   overflow:hidden. Any section that has those can include the part.
   -------------------------------------------------------------------------- */

.kt-embers {
	display: none;
}

@media (prefers-reduced-motion: no-preference) {
	.kt-motion .kt-embers {
		display: block;
		position: absolute;
		inset: 0;
		pointer-events: none;
	}
}

/* The glow: one warm breath at the base of the text column, where a charcoal
   bed would sit under everything above it. Ten percent gold at its brightest,
   so the veiled photograph and the text contrast are both undisturbed. */
.kt-embers::before {
	content: "";
	position: absolute;
	left: -10%;
	bottom: -18%;
	width: 72%;
	height: 62%;
	background: radial-gradient(
		ellipse at 35% 85%,
		rgba(215, 167, 92, 0.12),
		transparent 64%
	);
	animation: kt-glow 11s var(--kt-ease-soft) infinite alternate;
}

@keyframes kt-glow {
	from {
		opacity: 0.55;
		transform: scale(1);
	}
	to {
		opacity: 1;
		transform: scale(1.07);
	}
}

.kt-embers span {
	position: absolute;
	left: var(--e-x, 50%);
	bottom: 4%;
	width: 4px;
	height: 4px;
	border-radius: 50%;
	/* A hot near-white core inside the gold, and a soft halo around it — the
	   difference between dust and embers. The halo is a box-shadow, so the
	   flight's transform scale enlarges it with the ember, and the element's
	   animated opacity fades core and halo as one. */
	background: radial-gradient(
		circle,
		rgba(255, 236, 200, 0.95) 0%,
		var(--kt-gold) 40%,
		transparent 70%
	);
	box-shadow: 0 0 8px 2px rgba(215, 167, 92, 0.18);
	filter: blur(0.5px);
	opacity: 0;
	animation: kt-ember var(--e-d, 22s) linear var(--e-t, 0s) infinite;
}

/* A second, fainter breath low on the right, out of phase with the first, so
   the whole base of the room feels warm rather than one lit corner. */
.kt-embers::after {
	content: "";
	position: absolute;
	right: -12%;
	bottom: -20%;
	width: 55%;
	height: 52%;
	background: radial-gradient(
		ellipse at 60% 90%,
		rgba(215, 167, 92, 0.06),
		transparent 60%
	);
	animation: kt-glow 13s var(--kt-ease-soft) -5s infinite alternate;
}

/*
 * One shared flight, fourteen different readings of it: the custom properties
 * set inline per ember feed the same keyframes, so scatter costs no extra CSS.
 * Linear timing on purpose — hot air rises at its own steady pace, and the
 * sway keypoints supply all the curve the path needs.
 */
/*
 * How far an ember climbs, and the halfway mark of its arc.
 *
 * These are TOKENS rather than literals because the flight is measured in vh,
 * and vh knows nothing about the box the field is in. That was harmless while
 * the only container was a hero roughly two-thirds of the viewport tall. It
 * stops being harmless in a short container that clips: the embers reach the
 * top edge at around half their flight, while opacity is still near its peak,
 * and get sliced off in a hard line instead of fading out.
 *
 * So a container shorter than the default sets its own rise — in px, not
 * percent, because a percentage in translate resolves against the ember
 * itself (4px), not against its parent. Set it a little UNDER the container's
 * height and the embers finish fading before they ever meet the edge.
 *
 * The defaults are the values the hero has always used, so nothing about the
 * two heroes changes.
 */
.kt-embers {
	--kt-ember-rise: 66vh;
	--kt-ember-mid: 34vh;
}

@keyframes kt-ember {
	0% {
		transform: translate3d(0, 0, 0) scale(var(--e-s, 1));
		opacity: 0;
	}
	8% {
		opacity: var(--e-o, 0.4);
	}
	45% {
		transform: translate3d(var(--e-sway, 16px), calc(var(--kt-ember-mid, 34vh) * -1), 0) scale(calc(var(--e-s, 1) * 0.92));
	}
	78% {
		opacity: calc(var(--e-o, 0.4) * 0.5);
	}
	100% {
		transform: translate3d(calc(var(--e-sway, 16px) * -0.6), calc(var(--kt-ember-rise, 66vh) * -1), 0) scale(calc(var(--e-s, 1) * 0.78));
		opacity: 0;
	}
}

/* Phones get half the field: the hero is shorter, the device is slower, and
   seven embers read the same at 375px as fourteen do at 1440. */
@media (max-width: 600px) {
	.kt-embers span:nth-child(2n) {
		display: none;
	}
}

/* --------------------------------------------------------------------------
   The flow layer — liquid gold, WebGL
   --------------------------------------------------------------------------
   hero-flow.js inserts this canvas between the veils and the content and adds
   kt-flow-on ONLY after WebGL init succeeds. That class is the entire
   handoff: it hides the CSS ember spans (which remain shipped as the
   fallback), so every failure path — no WebGL, save-data, weak hardware,
   reduced motion, JS off — lands on embers or on stillness, never on blank.
   -------------------------------------------------------------------------- */

.kt-hero__flow {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
}

.kt-flow-on .kt-embers span {
	display: none;
}
