/* Page shell for /Page/{businessId}.
 *
 * Owns: the document primitives (shell width, bands, section heads, buttons, media frames),
 * the masthead, the cover, "We serve", "The room", the footer chrome and the share dialog.
 * Section-level styling for the menu, hours and takeaway sections lives in
 * restaurant-content.css and builds on the primitives declared here.
 *
 * Built from approved-design/index.html. Where this file and that file disagree, that file
 * wins; deliberate departures are recorded in approved-design/DIVERGENCES.md.
 *
 * Colour resolves only through var(--color-...) in site-restaurant-tokens.css. The single
 * sanctioned exception is a third-party brand mark, commented at its point of use.
 */

:root {
	/* A photograph does not re-tint when the UI theme flips. Each theme aliases the token
	   that is already dark (or already light) in that theme, so cover text stays legible in
	   both. Still token-only. */
	--media-ink: var(--color-inverse);
	--on-media: var(--color-inverse-foreground);
	--media-tint: var(--color-primary);

	--measure: 66ch;
	--shell: 1240px;
	--gut: clamp(1.25rem, 4vw, 3.5rem);
	--rhythm: clamp(3.5rem, 8vw, 7.5rem);
	--edge: 3px;

	/* Sticky geometry. Every offset on the page derives from these, including the menu rail
	   in restaurant-content.css. The masthead and that rail are the only sticky layers. */
	--masthead-h: 4.25rem;
	--rail-h: 4rem;
	--sticky-h: calc(var(--masthead-h) + var(--rail-h));

	/* The open/closed lamp and pill. The one colour family on this page that is NOT the
	   merchant's: whether the venue is open now is a fact about the world, and a fact stated in
	   the tenant's accent said "open" in magenta on one merchant and teal on the next, with
	   nothing to stop a generated palette putting a red-family dot beside the word Open.
	   Green means open and amber means closed on every one of the ten themes.

	   These are the only literal colours declared outside site-restaurant-tokens.css. That file
	   is generated from one merchant brand colour per theme and cannot express a value that must
	   NOT vary with the brand. Recorded in approved-design/DIVERGENCES.md, and owned by the
	   palette generator the moment it grows a brand-independent status role.
	   Measured: text 7.1:1, lamp 4.5:1 against the pill; lamp 9.2:1 on the cover's ink. */
	--color-status-open-surface: #E3F5E8;
	--color-status-open-text: #0F5C33;
	--color-status-open-lamp: #157F42;
	--color-status-closed-surface: #F7EAE1;
	--color-status-closed-text: #7E3B14;
	--color-status-closed-lamp: #B25C22;

	/* The pill on the cover keeps the venue's photograph behind it, so its plate is the ink in
	   both colour modes and its lamps are the light pair in both. */
	--color-status-open-lamp-onmedia: #43C97C;
	--color-status-closed-lamp-onmedia: #DE8F4E;
}

:root[data-color-theme="dark"] {
	--media-ink: var(--color-background);
	--on-media: var(--color-text);
	--media-tint: var(--color-secondary);

	/* Same two meanings, redrawn for a dark plate. See the note in :root above. */
	--color-status-open-surface: #12291D;
	--color-status-open-text: #7BE3A6;
	--color-status-open-lamp: #43C97C;
	--color-status-closed-surface: #2B1C11;
	--color-status-closed-text: #F3B489;
	--color-status-closed-lamp: #DE8F4E;
}

/* ---------------------------------------------------------------- document */

*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
	scroll-padding-top: calc(var(--masthead-h) + 1rem);
}

body {
	margin: 0;
	font-family: Inter, "Noto Sans SC", "Noto Sans TC", system-ui, sans-serif;
	font-optical-sizing: auto;
	background: var(--color-background);
	color: var(--color-text);
	font-size: 1rem;
	line-height: 1.6;
	overflow-x: hidden;
}

img,
svg {
	max-width: 100%;
}

h1,
h2,
h3,
h4 {
	margin: 0;
	font-weight: 300;
	letter-spacing: -0.03em;
	line-height: 1.06;
}

p {
	margin: 0;
}

ul,
ol {
	margin: 0;
	padding: 0;
	list-style: none;
}

a {
	color: var(--color-link);
}

button,
input,
select,
textarea {
	font: inherit;
}

:where(a, button, summary, input, [tabindex]):focus-visible {
	outline: 3px solid var(--color-focus);
	outline-offset: 3px;
	border-radius: var(--edge);
}

.skip {
	position: absolute;
	left: 0.75rem;
	top: -6rem;
	z-index: 90;
	background: var(--color-primary);
	color: var(--color-primary-foreground);
	padding: 0.9rem 1.4rem;
	border-radius: var(--edge);
	font-weight: 600;
	text-decoration: none;
	transition: top 160ms ease;
}

.skip:focus {
	top: 0.75rem;
}

.sr {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
	border: 0;
}

.shell {
	width: 100%;
	max-width: var(--shell);
	margin-inline: auto;
	padding-inline: var(--gut);
}

/* Grid and flex children default to min-width:auto, which lets a wide min-content child push
   its track past the viewport. Opt every layout child out of that. */
.masthead__in > *,
.serve > *,
.room > *,
.strip > *,
.actionband__in > *,
.facil__list > * {
	min-width: 0;
}

/* -------------------------------------------------------------- primitives */

/* Two micro-label treatments and no more: .overline names the section a heading belongs to,
   .grouplabel names a group of items that has no heading of its own. Both sit at 0.75rem —
   tracked capitals at 11.2px were the smallest type on the page and under a 12px floor, and the
   page had grown three different small-heading sizes (11.2 / 11.5 / 15.2px) for the same job. */
.overline,
.grouplabel {
	font-size: 0.75rem;
	letter-spacing: 0.2em;
	text-transform: uppercase;
}

.overline {
	font-weight: 600;
	color: var(--color-muted-foreground);
}

.grouplabel {
	font-weight: 700;
	color: var(--color-muted-foreground);
}

.display {
	font-size: clamp(2.7rem, 9vw, 5.75rem);
	font-weight: 200;
	letter-spacing: -0.045em;
}

.h2 {
	font-size: clamp(1.85rem, 4.4vw, 3.1rem);
	font-weight: 250;
}

.h3 {
	font-size: clamp(1.25rem, 2.4vw, 1.6rem);
	font-weight: 400;
	letter-spacing: -0.02em;
}

.prose {
	font-size: 1.0625rem;
	font-weight: 350;
	line-height: 1.75;
	max-width: var(--measure);
}

.prose--sm {
	font-size: 0.95rem;
	line-height: 1.7;
}

/* A link inside running text still has to be pressable on a phone. */
.prose a,
.serve__foot a,
.covercard__tel a {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
}

/* ------------------------------------------------------------------ buttons */

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.6rem;
	min-height: 56px;
	padding: 1.05rem 1.9rem;
	font: inherit;
	font-weight: 600;
	font-size: 1rem;
	letter-spacing: -0.005em;
	border-radius: var(--edge);
	border: 1px solid transparent;
	background: transparent;
	color: var(--color-text);
	text-decoration: none;
	cursor: pointer;
	transition: background-color 180ms ease, border-color 180ms ease, transform 180ms ease;
}

.btn:active {
	transform: translateY(1px);
}

.btn svg {
	flex: 0 0 auto;
}

.btn--primary {
	background: var(--color-primary);
	color: var(--color-primary-foreground);
}

.btn--primary:hover {
	background: var(--color-primary-text);
}

/* The rim is not decoration: --color-secondary is a light tint in eight of the ten light themes
   and reaches 1.17:1 against the canvas, so a borderless fill would have no boundary. And hover
   must not move the fill: --color-secondary-foreground on --color-secondary clears 4.5:1 in all
   ten themes, but T02 and T10 clear it by 0.02, so any shift of the fill drops a theme below the
   floor. See docs/business-rules/landing-restaurant-page.md — "The two external actions". */
.btn--secondary {
	background: var(--color-secondary);
	color: var(--color-secondary-foreground);
	border-color: var(--color-secondary-text);
}

.btn--secondary:hover {
	border-color: var(--color-inverse);
}

.btn__leave {
	margin-inline-start: auto;
}

.btn--quiet {
	background: var(--color-background);
	color: var(--color-text);
	border-color: var(--color-border);
}

.btn--quiet:hover {
	background: var(--color-muted);
}

.btn--sm {
	min-height: 44px;
	padding: 0.7rem 1.15rem;
	font-size: 0.9rem;
}

/* ----------------------------------------------------------------- masthead */

.masthead {
	position: sticky;
	top: 0;
	z-index: 60;
	background: var(--color-background);
	border-bottom: 1px solid var(--color-border-subtle);
}

/* The bar's own hairline is part of --masthead-h, not extra on top of it. Every offset on the
   page — the menu rail's sticky top, --sticky-h, scroll-padding — is derived from that token, and
   a bar one pixel taller than the number those offsets use leaves a seam the page scrolls
   through. */
.masthead__in {
	display: flex;
	align-items: center;
	gap: clamp(0.5rem, 2vw, 1.25rem);
	height: calc(var(--masthead-h) - 1px);
}

/* Menumiz brand mark — the page is Menumiz's product surface. A single-colour mark, drawn in
   currentColor so it reads in both themes. */
.brandmark {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.55rem;
	min-height: 44px;
	min-width: 44px;
	flex: 0 0 auto;
	color: var(--color-text);
	text-decoration: none;
}

.brandmark svg {
	width: 30px;
	height: 30px;
	flex: 0 0 auto;
}

.brandmark__word {
	font-size: 0.95rem;
	font-weight: 700;
	letter-spacing: -0.02em;
}

@media (max-width: 460px) {
	.brandmark__word {
		display: none;
	}
}

.masthead__sep {
	width: 1px;
	align-self: stretch;
	margin-block: 1rem;
	background: var(--color-border-subtle);
	flex: 0 0 auto;
}

.wordmark {
	display: inline-flex;
	flex-direction: column;
	justify-content: center;
	min-height: 44px;
	min-width: 0;
	font-size: 0.9rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-text);
	text-decoration: none;
}

/* text-overflow works on a block box, not on the flex container above it. On the container the
   venue's name hard-clipped mid-word, and "HARD ROCK CAI" is a different venue. */
.wordmark__name,
.wordmark__city {
	display: block;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.wordmark__city {
	font-size: 0.6rem;
	font-weight: 500;
	letter-spacing: 0.22em;
	color: var(--color-muted-foreground);
}

.masthead nav {
	display: none;
	margin-inline-start: auto;
	gap: 1.35rem;
}

.masthead nav a {
	color: var(--color-muted-foreground);
	text-decoration: none;
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 44px;
	min-width: 44px;
	padding: 0.6rem 0;
	border-bottom: 2px solid transparent;
}

.masthead nav a:hover,
.masthead nav a.active {
	color: var(--color-text);
	border-bottom-color: var(--color-primary);
}

/* A rigid cluster of 44px controls, so it is the one item in the bar that does not yield. It is
   given min-width:0 above with every other flex child, which let it shrink below its own buttons
   and push the theme toggle 10px past the viewport at 320. The venue's name yields instead — it
   is the item that can be shortened and still be read. */
.masthead__tail {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	margin-inline-start: auto;
	flex: 0 0 auto;
}

@media (min-width: 1000px) {
	.masthead nav {
		display: flex;
	}

	.masthead__tail {
		margin-inline-start: 0;
	}
}

/* ------------------------------------------------- section navigation panel */

/* The same list the bar prints above 1000px, as a disclosure hanging off the sticky bar below it.
   Not a drawer: no overlay, no scroll lock, no full-screen sheet — it costs nothing while shut,
   and it is the only way to reach Hours from the middle of a menu 13 screens long. */
.sectionsbtn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.45rem;
	min-height: 44px;
	min-width: 44px;
	flex: 0 0 auto;
	padding: 0.6rem 0.9rem;
	cursor: pointer;
	font: inherit;
	font-size: 0.85rem;
	font-weight: 700;
	border-radius: 999px;
	color: var(--color-primary-text);
	background: var(--color-primary-subtle);
	border: 1px solid color-mix(in srgb, var(--color-primary-text) 35%, transparent);
}

.sectionsbtn:hover {
	background: var(--color-primary-soft);
}

/* Below this the bar cannot hold the word without truncating the venue's own name, which is the
   one thing in the bar that says whose page this is. A hamburger carrying an accessible name is
   the affordance every phone already knows. */
@media (max-width: 519px) {
	.sectionsbtn {
		padding-inline: 0.7rem;
	}

	.sectionsbtn__word {
		display: none;
	}
}

@media (min-width: 1000px) {
	.sectionsbtn {
		display: none;
	}
}

.sectionspanel {
	background: var(--color-background);
	border-bottom: 1px solid var(--color-border-subtle);
	padding-block: 1.25rem 1.5rem;
	max-height: 70svh;
	overflow-y: auto;
}

.sectionspanel[hidden] {
	display: none;
}

/* The panel's second group — the links that leave the site.
   See docs/business-rules/landing-restaurant-page.md — "Section navigation". */
.panelacts {
	display: grid;
	gap: 0.6rem;
	margin-top: 1.35rem;
	padding-top: 1.35rem;
	border-top: 1px solid var(--color-border-subtle);
}

/* The grid owns the space under this label; restaurant-content.css gives every other label in
   the panel a margin of its own. */
.sectionspanel .panelacts .grouplabel {
	margin-bottom: 0;
}

.panelacts .btn {
	justify-content: flex-start;
	text-align: start;
}

.iconbtn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	flex: 0 0 auto;
	background: transparent;
	border: 1px solid var(--color-border-subtle);
	color: var(--color-text);
	cursor: pointer;
}

.iconbtn:hover {
	background: var(--color-muted);
}

/* The mark names the theme the button switches to, which is what its label says. One icon in both
   themes told the reader nothing about which way the switch goes. */
.iconbtn__sun {
	display: none;
}

:root[data-color-theme="dark"] .iconbtn__moon {
	display: none;
}

:root[data-color-theme="dark"] .iconbtn__sun {
	display: block;
}

/* ------------------------------------------------------------ media frames */

/* A frame that holds a merchant photograph. The tinted hatch is what shows while the image is
   still arriving and if it never does, so an empty frame is never a bare rectangle. A frame
   must always carry a rule that gives it height: an element sized only by aspect-ratio with
   no such rule renders as a 2px line rather than as nothing. */
.media {
	position: relative;
	overflow: hidden;
	border-radius: var(--edge);
	background:
		repeating-linear-gradient(135deg, transparent 0 14px, color-mix(in srgb, var(--color-text) 4%, transparent) 14px 15px),
		linear-gradient(160deg, color-mix(in srgb, var(--media-tint) 18%, var(--color-surface)) 0%, var(--color-surface-elevated) 100%);
	border: 1px solid var(--color-border-subtle);
	margin: 0;
}

.media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* A frame can also hold an embed rather than a photograph — the map in Hours & location. The
   frame keeps its own aspect ratio and its hatch, so the embed fills a box that is already at
   its final size. */
.media iframe {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

.media--dark {
	background:
		repeating-linear-gradient(115deg, transparent 0 26px, color-mix(in srgb, var(--on-media) 6%, transparent) 26px 27px),
		radial-gradient(58% 74% at 74% 26%, color-mix(in srgb, var(--color-secondary) 30%, transparent) 0%, transparent 70%),
		radial-gradient(90% 82% at 20% 6%, color-mix(in srgb, var(--media-tint) 62%, transparent) 0%, transparent 66%),
		linear-gradient(180deg, color-mix(in srgb, var(--media-ink) 70%, transparent) 0%, var(--media-ink) 100%),
		var(--media-ink);
	border-color: transparent;
}

/* ---------------------------------------------------------------- the cover */

.cover {
	position: relative;
	margin: 0;
	isolation: isolate;
}

.cover__media {
	position: absolute;
	inset: 0;
	z-index: 0;
	border-radius: 0;
}

.cover__scrim {
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	background: linear-gradient(180deg,
		color-mix(in srgb, var(--media-ink) 55%, transparent) 0%,
		color-mix(in srgb, var(--media-ink) 18%, transparent) 32%,
		color-mix(in srgb, var(--media-ink) 64%, transparent) 70%,
		color-mix(in srgb, var(--media-ink) 90%, transparent) 100%);
}

/* The cover text is bottom-aligned against a fixed min-height. The name, the cuisine line and
   the facts row are all a diner needs above the action buttons; BusinessDesc is not here, it
   renders once, in The room. */
.cover__inner {
	position: relative;
	z-index: 2;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	min-height: min(80svh, 730px);
	padding-block: clamp(4rem, 11vw, 7rem) 0;
}

.cover[data-cover="true"] .cover__inner {
	color: var(--on-media);
}

/* The second of the three stacked devices, for the text block rather than for the button row.
   The frame gradient above cannot protect these words: they are bottom-aligned above an action
   row that is one line wide at 1440 and three lines tall at 390, so the same sentence lands
   anywhere between 31% and 80% of the cover depending on how many actions the merchant has.
   Measured on /Page/1440, whose PageHeaderImg is a white cat: at 390 the frame gradient left the
   cuisine line at 2.63:1 and the venue name at 4.26:1. This plate is anchored to the words
   instead, so the wash beneath them is the same whatever the frame does — it fades in over a
   fixed 3.5rem, above which the photograph is untouched. See approved-design/DIVERGENCES.md. */
.cover[data-cover="true"] .cover__plate {
	margin-top: -3.5rem;
	padding-top: 3.5rem;
	background: linear-gradient(180deg,
		transparent 0,
		color-mix(in srgb, var(--media-ink) 68%, transparent) 3.5rem,
		color-mix(in srgb, var(--media-ink) 80%, transparent) 100%);
}

.cover__body {
	padding-bottom: clamp(2rem, 5vw, 3.25rem);
}

.cover__body > * {
	max-width: 40rem;
}

/* The merchant's mark. The chip is the point: BusinessLogo is artwork of unknown aspect, unknown
   colour and unknown transparency, and it sits over a photograph nobody controls. A fixed opaque
   box with object-fit: contain gives a wide lockup and a square badge the same footprint, keeps a
   surface under a transparent PNG, and takes the mark out of the cover's contrast problem
   entirely. The box is sized in CSS and the img carries width and height, so a slow or missing
   logo cannot shift the layout.

   The plate is --on-media, not --color-background. Merchant artwork is drawn for a light
   background, and --on-media is the token that stays light in both colour modes — the same
   reason the cover text uses it. On --color-background the chip would invert with the theme and
   a dark mark on a transparent ground would disappear into it. */
.covermark {
	display: grid;
	place-items: center;
	width: clamp(60px, 9vw, 84px);
	aspect-ratio: 1;
	margin-bottom: 1.15rem;
	padding: 0.5rem;
	border-radius: calc(var(--edge) * 4);
	background: var(--on-media);
	border: 1px solid color-mix(in srgb, var(--media-ink) 14%, transparent);
	overflow: hidden;
}

.covermark img {
	width: 100%;
	height: 100%;
	object-fit: contain;
}

/* Full strength, not a 78% mix. The overline sits in the weakest part of the scrim, and a
   partially transparent white measured 4.24:1 against a mid-tone photograph — the merchant's
   photograph is the one background nobody controls. Hierarchy is carried by size and tracking,
   which do not depend on what is underneath. */
.cover[data-cover="true"] .overline {
	color: var(--on-media);
}

.cover h1 {
	margin-block: 0.9rem 1.1rem;
}

.cover[data-cover="true"] h1 {
	color: var(--on-media);
}

.coverstat {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.65rem 1.15rem;
	margin-top: 1.6rem;
	font-size: 0.9rem;
	font-weight: 500;
	color: var(--color-muted-foreground);
}

.cover[data-cover="true"] .coverstat {
	color: color-mix(in srgb, var(--on-media) 86%, transparent);
}

.coverstat b {
	font-weight: 700;
}

/* The separator belongs to the item BEFORE it. These are flex items, so the mark travels with
   whichever item it is drawn on: on the item that follows, a wrapped line opens with a floating
   dot that reads as a bullet, which is what the cuisine line did at 1440. Drawn on the item that
   precedes, it can only ever land at the end of a line, where a run-in list is supposed to
   continue. */
.coverstat > *:not(:last-child)::after {
	content: "·";
	margin-inline-start: 0.7rem;
	opacity: 0.55;
}

@media (max-width: 640px) {
	.coverstat {
		gap: 0.4rem 0.8rem;
	}

	.coverstat > *:not(:last-child)::after {
		content: none;
	}
}

/* Price is a band, not a range. It is printed exactly once on the page. */
.band-price {
	font-weight: 700;
	letter-spacing: 0.02em;
}

/* A merchant with no cover image must not get a dark rectangle where the photograph would
   have been. The cover becomes a typographic panel in theme colours, sized to its content
   rather than to the viewport, and the action row sits on that panel instead of on imagery. */
.cover[data-cover="false"] {
	background:
		radial-gradient(120% 90% at 12% 0%, var(--color-primary-wash) 0%, transparent 62%),
		var(--color-surface);
	border-bottom: 1px solid var(--color-border-subtle);
}

.cover[data-cover="false"] .cover__inner {
	min-height: 0;
	padding-block: clamp(3rem, 9vw, 5.5rem) 0;
}

.cover[data-cover="false"] .overline {
	color: var(--color-primary-text);
}

.cover[data-cover="false"] .actionband {
	background: var(--color-surface-elevated);
	backdrop-filter: none;
	border-top: 1px solid var(--color-border-subtle);
}

/* The contact card the cover becomes when nothing downstream carries the address. */
.covercard {
	margin-top: 2rem;
	display: grid;
	gap: 0.5rem;
	justify-items: start;
}

.covercard__addr {
	font-style: normal;
	font-size: clamp(1.1rem, 2.4vw, 1.4rem);
	font-weight: 300;
	line-height: 1.5;
}

.covercard__tel a {
	font-size: 1.05rem;
	font-weight: 600;
}

.covercard__note {
	margin-top: 0.5rem;
	font-size: 0.95rem;
	max-width: 48ch;
	color: var(--color-muted-foreground);
}

.cover[data-cover="true"] .covercard__addr,
.cover[data-cover="true"] .covercard__note,
.cover[data-cover="true"] .covercard__tel a {
	color: var(--on-media);
}

.state {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.4rem 0.85rem;
	border-radius: 999px;
	font-size: 0.78rem;
	font-weight: 700;
	letter-spacing: 0.05em;
	text-transform: uppercase;
}

/* Never currentColor: the lamp is the one mark on the page whose colour is the message, so it
   takes the status token directly rather than whatever ink surrounds it. */
.state__led {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	flex: 0 0 auto;
}

.state[data-open="true"] {
	background: var(--color-status-open-surface);
	color: var(--color-status-open-text);
}

.state[data-open="true"] .state__led {
	background: var(--color-status-open-lamp);
}

.state[data-open="false"] {
	background: var(--color-status-closed-surface);
	color: var(--color-status-closed-text);
}

.state[data-open="false"] .state__led {
	background: var(--color-status-closed-lamp);
}

/* On a photograph the pill gets an opaque plate and takes the same ink/paper pair as the venue
   name, so its legibility never depends on the image behind it. A wash of the pill's own text
   colour — 26% accent under 100% accent — measured 1.60:1 over a mid-tone photograph. The open
   and closed states stay distinguishable by the word and by the lamp, not by the plate. */
.cover[data-cover="true"] .state {
	background: var(--media-ink);
	color: var(--on-media);
}

.cover[data-cover="true"] .state[data-open="true"] .state__led {
	background: var(--color-status-open-lamp-onmedia);
}

.cover[data-cover="true"] .state[data-open="false"] .state__led {
	background: var(--color-status-closed-lamp-onmedia);
}

/* A row of generously padded buttons sitting ON the cover image. Legibility is carried by
   three stacked devices, not one: a bottom-weighted gradient scrim, a blurred tinted plate
   under the row, and buttons with their own contrast. */
.actionband {
	border-top: 1px solid color-mix(in srgb, var(--on-media) 24%, transparent);
	background: color-mix(in srgb, var(--media-ink) 64%, transparent);
	backdrop-filter: blur(20px) saturate(1.15);
}

/* Flex, not a fixed grid: the row has to look deliberate whether the merchant supports five
   actions or two. Items share the line and cap their own width, so nothing stretches to
   absurd length when there are few. */
.actionband__in {
	display: flex;
	flex-wrap: wrap;
	gap: 0.85rem;
	padding-block: clamp(1.15rem, 3vw, 1.75rem);
}

.actionband .btn {
	flex: 1 1 9rem;
	max-width: 22rem;
}

@media (max-width: 479px) {
	.actionband .btn {
		padding-inline: 1.05rem;
	}
}

/* See docs/business-rules/landing-restaurant-page.md — "The cover's action row". */
@media (max-width: 1099px) {
	.actionband .btn--primary,
	.actionband .btn--secondary {
		flex-basis: 100%;
		max-width: none;
	}
}

/* Two actions cannot fill a 1240px row. Stretched to a share of it they left 522px of nothing
   beside them on /Page/1445 and read as a band whose other buttons had failed to arrive. At their
   own width, led by a solid button, the same two read as a venue with two things to offer — the
   left-aligned action row this page already uses at the end of the menu and under the delivery
   panel. The lead keeps the full width on a phone, where it is the whole line anyway. */
.actionband__in:not(:has(> :nth-child(3))) .btn:not(.btn--primary):not(.btn--secondary) {
	flex: 0 1 auto;
}

@media (min-width: 1100px) {
	.actionband__in:not(:has(> :nth-child(3))) .btn {
		flex: 0 1 auto;
	}
}

.btn--onmedia {
	color: var(--on-media);
	background: color-mix(in srgb, var(--on-media) 12%, transparent);
	border-color: color-mix(in srgb, var(--on-media) 48%, transparent);
}

.btn--onmedia:hover {
	background: color-mix(in srgb, var(--on-media) 24%, transparent);
	border-color: var(--on-media);
}

.btn--onmedia:focus-visible {
	outline-color: var(--color-accent);
}

/* ----------------------------------------------------------- section bands */

.band {
	padding-block: var(--rhythm);
}

.band--tint {
	background: var(--color-surface);
}

.band--wash {
	background: var(--color-primary-wash);
}

/* Every band carries a hairline at its top edge as well as a background change, so a boundary
   is legible even when two neighbouring bands share a background. */
.band + .band {
	border-top: 1px solid var(--color-border-subtle);
}

/* One treatment for every section head: label, title, note, stacked. Nothing sits inline
   beside the heading, so two type sizes never share a line. */
.sechead {
	display: grid;
	justify-items: start;
	gap: 0.6rem;
	margin-bottom: clamp(1.75rem, 4vw, 3rem);
}

.sechead .overline {
	color: var(--color-primary-text);
}

.sechead__note {
	font-size: 0.98rem;
	color: var(--color-muted-foreground);
	max-width: var(--measure);
}

/* ---------------------------------------------------------------- we serve */

.serve {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
	gap: 0.85rem;
}

.serve__item {
	display: flex;
	align-items: center;
	gap: 0.9rem;
	padding: 1.05rem 1.15rem;
	min-height: 76px;
	background: var(--color-background);
	border: 1px solid var(--color-border-subtle);
	border-radius: var(--edge);
}

.serve__item svg {
	width: 22px;
	height: 22px;
	flex: 0 0 auto;
	color: var(--color-primary-text);
}

.serve__txt {
	display: grid;
	gap: 2px;
	min-width: 0;
}

.serve__name {
	font-size: 0.95rem;
	font-weight: 600;
}

.serve__val {
	font-size: 0.85rem;
	font-weight: 600;
	color: var(--color-accent-text);
}

/* What the venue does NOT offer has to be findable without reading every tile. Four cues,
   none of them colour on its own: the items are pulled out of the list into their own
   labelled group (position), the group label spans the grid (structure), each tile carries a
   heavy inline-start bar (shape), and the value is the word NO in a chip beside a slashed
   icon (text). */
.serve__group {
	display: flex;
	align-items: center;
	gap: 0.85rem;
	margin-top: 1.25rem;
	color: var(--color-secondary-text);
}

.serve > .serve__group {
	grid-column: 1 / -1;
}

.serve__group--plain {
	color: var(--color-muted-foreground);
}

.serve__group::after {
	content: "";
	flex: 1 1 auto;
	height: 1px;
	background: var(--color-border-subtle);
}

.serve__item--no {
	background: var(--color-muted);
	border-inline-start: 6px solid var(--color-secondary);
	padding-inline-start: 0.95rem;
}

.serve__item--no svg {
	color: var(--color-secondary-text);
}

.serve__item--no .serve__name {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

.serve__no {
	display: inline-block;
	padding: 0.05rem 0.4rem;
	font-size: 0.62rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-secondary-foreground);
	background: var(--color-secondary);
	border-radius: var(--edge);
}

.serve__item--no .serve__val {
	color: var(--color-text);
	font-weight: 500;
}

.serve__foot {
	margin-top: 1.5rem;
	font-size: 0.95rem;
	color: var(--color-muted-foreground);
	max-width: var(--measure);
}

.serve__foot a {
	color: var(--color-link);
}

/* One row of PageAssets.Facilities is one chip. A tile grid cannot take thirty of them
   without becoming the page, and the record really does hold thirty for the busiest merchant,
   so the list wraps at its own rhythm and never truncates: a diner looking for the one that
   matters to them is scanning, not reading. */
.facil {
	margin-top: 2.25rem;
}

.facil__list {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-top: 1.1rem;
}

.facil__item {
	display: inline-flex;
	align-items: center;
	gap: 0.55rem;
	min-height: 44px;
	padding: 0.5rem 0.95rem;
	background: var(--color-background);
	border: 1px solid var(--color-border-subtle);
	border-radius: var(--edge);
	font-size: 0.9rem;
	font-weight: 500;
}

.facil__item svg,
.facil__item img {
	width: 17px;
	height: 17px;
	flex: 0 0 auto;
	color: var(--color-primary-text);
}

/* ---------------------------------------------------------------- the room */

/* The room's photographs are not a uniform grid: one frame is promoted -- the merchant's page
   image when they have set one, the gallery's first frame otherwise -- and pairs with
   BusinessDesc beside it, and the rest sit under the pair as an even strip. The prose is what
   gives the photographs a purpose.
   Every combination the record can produce has to hold:
     description + photos    the pair, then the strip
     photos, no description  the promoted frame takes the full width and the section is the
                             uneven gallery on its own
     description, no photos  the prose stands alone, with no empty column beside it
     neither                 the section is not rendered at all
   A page image with an empty gallery is the pair with no strip under it.
   Below 900px the pair stacks, image first. BbGallery has no caption column, so the frames
   stay wordless. */
.room {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: clamp(1.75rem, 4vw, 3rem);
	align-items: center;
}

@media (min-width: 900px) {
	.room {
		grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
	}
}

/* Whichever half survives takes the whole width rather than leaving a hole where its partner
   would have been. */
.room:has(> :only-child) {
	grid-template-columns: minmax(0, 1fr);
}

.room__prose > p + p {
	margin-top: 0.9rem;
}

/* Portrait beside the prose, because a 4:5 frame holds its own against a column of text where
   a landscape one is dwarfed by it. */
.room__media {
	aspect-ratio: 4 / 5;
}

@media (max-width: 899px) {
	.room__media {
		aspect-ratio: 3 / 2;
	}
}

/* Alone, the promoted frame becomes the wide lead the strip sits under. The mobile override
   repeats the :has() selector so it still outranks it. */
.room:has(> :only-child) .room__media {
	aspect-ratio: 21 / 9;
}

@media (max-width: 899px) {
	.room:has(> :only-child) .room__media {
		aspect-ratio: 3 / 2;
	}
}

/* auto-fit rather than a fixed three, because a real BbGallery holds anything from one image
   to thirty and a lone frame parked in a three-column track reads as two frames that failed
   to load. */
.strip {
	display: grid;
	gap: 0.9rem;
	grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
	margin-top: clamp(1.75rem, 4vw, 3rem);
}

.strip .media {
	aspect-ratio: 1 / 1;
}

@media (max-width: 620px) {
	.strip {
		grid-template-columns: 1fr;
	}

	.strip .media {
		aspect-ratio: 16 / 9;
	}
}

/* ------------------------------------------------------------------ footer */

.foot {
	padding-block: var(--rhythm) 3rem;
	background: var(--color-surface);
	border-top: 1px solid var(--color-border-subtle);
}

.foot__grid {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 2.5rem;
}

@media (min-width: 620px) {
	.foot__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 1000px) {
	.foot__grid {
		grid-template-columns: 1.5fr 1fr 1fr 1fr;
	}
}

.foot h4 {
	margin-bottom: 1rem;
}

/* Two kinds of column share this grid: lists of links and a list of facts. Styled alike they
   read alike, and a fact that looks like a navigation row is a false affordance — a reader
   should never have to hover to find out whether something is a destination. The two rules
   below are keyed on whether the row contains an anchor, so a column added later cannot
   reacquire the defect by forgetting a class. */
.foot li {
	display: flex;
	align-items: center;
	min-height: 44px;
}

.foot a {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	min-width: 44px;
	color: var(--color-text);
	text-decoration: none;
}

/* A resting underline, not a hover-only one: the affordance has to be visible before the
   pointer arrives, and on a touch screen there is no hover at all. Restricted to the lists and
   the address — the venue wordmark and the Menumiz lockup are marks, not text links. */
.foot li a,
.foot__addr a {
	text-decoration: underline;
	text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
	text-underline-offset: 0.28em;
}

.foot a:hover {
	color: var(--color-primary-text);
	text-decoration: underline;
	text-decoration-color: currentColor;
}

/* Statements, so they take neither the navigation rhythm nor an underline: no 44px target row,
   a quieter foreground and a smaller size. The distinction survives greyscale because it is
   carried by weight, size and spacing as well as by colour. */
.foot li:not(:has(a)) {
	align-items: flex-start;
	min-height: 0;
	padding-block: 0.34rem;
	font-size: 0.92rem;
	line-height: 1.5;
	color: var(--color-muted-foreground);
}

.foot__addr {
	font-size: 1rem;
}

.foot__state {
	margin-top: 1rem;
}

/* A negative in a list of positives has to survive greyscale, so it takes a mark as well as
   the word. It is the only line in Good to know that is not plain text. */
.factno {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
}

.factno svg {
	width: 16px;
	height: 16px;
	flex: 0 0 auto;
	color: var(--color-secondary-text);
}

.poweredby {
	display: flex;
	align-items: center;
	gap: 0.65rem;
	margin-top: 2rem;
}

.poweredby svg {
	width: 34px;
	height: 34px;
	flex: 0 0 auto;
	color: var(--color-text);
}

.poweredby__txt {
	font-size: 0.85rem;
	color: var(--color-muted-foreground);
}

.poweredby__txt b {
	display: block;
	font-size: 1rem;
	font-weight: 700;
	letter-spacing: -0.02em;
	color: var(--color-text);
}

.colophon {
	margin-top: 3rem;
	padding-top: 1.5rem;
	border-top: 1px solid var(--color-border-subtle);
	font-size: 0.82rem;
	color: var(--color-muted-foreground);
}

/* ------------------------------------------------------------ share dialog */

/* modal.js sets display:flex on .modal, inserts .modal-backdrop inside it and puts
   .modal-show on <body>. The dialog's inner controls are styled in restaurant-content.css. */
.modal {
	display: none;
	position: fixed;
	inset: 0;
	z-index: 100;
	align-items: center;
	justify-content: center;
	padding: 1rem;
}

.modal-backdrop {
	position: fixed;
	inset: 0;
	background: color-mix(in srgb, var(--color-overlay) 76%, transparent);
}

.modal-dialog {
	position: relative;
	z-index: 1;
	width: 100%;
	max-width: 30rem;
	margin-inline: auto;
}

.modal-content {
	background: var(--color-background);
	border: 1px solid var(--color-border-subtle);
	border-radius: var(--edge);
	overflow: hidden;
}

.modal-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 1rem 1.25rem;
	border-bottom: 1px solid var(--color-border-subtle);
}

.modal-title {
	font-size: 1.15rem;
	font-weight: 400;
}

.modal-header .btn-close {
	width: 44px;
	min-height: 44px;
	padding: 0;
	font-size: 1.1rem;
	color: var(--color-text);
}

.modal-body {
	padding: 1.25rem;
	max-height: 70svh;
	overflow-y: auto;
}

body.modal-show {
	overflow: hidden;
}

/* ---------------------------------------------------------- motion contract */

@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}

	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		animation-delay: 0ms !important;
		transition-duration: 0.01ms !important;
		transition-delay: 0ms !important;
		scroll-behavior: auto !important;
	}
}
