/* Content sections of /Page/{businessId}: hours & location, menu, reviews, takeaway & delivery.
 * Ported from approved-design/index.html; that file is the reference this must match.
 * Colour comes only from var(--color-*) in site-restaurant-tokens.css. There is no literal
 * colour in this file: the two sanctioned brand marks, Google Maps and Waze, are inline SVG
 * in _Contact.cshtml and carry their hex values there, commented at the point of use.
 *
 * Depends on restaurant-shell.css for the page chrome these sections sit inside: the :root
 * geometry tokens (--gut, --shell, --rhythm, --edge, --measure, --masthead-h, --rail-h,
 * --sticky-h), the editorial primitives (.shell, .band, .sechead, .overline, .h2, .h3,
 * .prose, .btn, .sr), the open/closed pill (.state) and the dialog chrome (.modal).
 */

:root {
	/* The leading gutter of a dish row. One number, because every row gets the same box
	   whether or not the merchant supplied a photograph. */
	--shot-w: 96px;

	/* The mark a dish shot shows when the merchant's photograph failed to load. Carried as a
	   mask rather than an image so it is painted in a theme token like every other mark on the
	   page; the colour inside the data URI is the mask's alpha and never reaches the screen. */
	--glyph-broken-photo: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='14' rx='2'/%3E%3Cpath d='m4.5 16.5 4-4 2.5 2.5'/%3E%3Ccircle cx='15.4' cy='9.6' r='1.3'/%3E%3Cpath d='m3.5 3.5 17 17'/%3E%3C/svg%3E");
}

@media (min-width: 640px) {
	:root {
		--shot-w: 124px;
	}
}

/* ------------------------------------------------------------------ shared */

/* 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. */
.hourswrap > *,
.finding > *,
.ordergrid > *,
.partner > *,
.dish__row > *,
.rail__in > * {
	min-width: 0;
}

/* ---------------------------------------------------- hours & location */

.hourswrap {
	display: grid;
	gap: 1rem;
}

@media (min-width: 720px) {
	.hourswrap {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 1.5rem;
	}
}

/* A venue whose kitchen keeps its hours gets one table, not two identical ones. The single card
   holds the width the pair had, so the day and its times stay at the ends of a readable row
   instead of at the ends of the shell. */
.hourswrap:has(> :only-child) {
	grid-template-columns: minmax(0, 38rem);
}

.hourscard {
	border: 1px solid var(--color-border-subtle);
	border-radius: var(--edge);
	overflow: hidden;
	background: var(--color-background);
}

.hourscard__top {
	padding: 1rem 1.15rem;
	background: var(--color-surface);
	border-bottom: 1px solid var(--color-border-subtle);
}

.hourscard__title {
	font-size: 0.95rem;
	font-weight: 700;
	letter-spacing: -0.01em;
}

.hourscard__sub {
	margin-top: 2px;
	font-size: 0.82rem;
	color: var(--color-muted-foreground);
}

.hourscard__now {
	margin-top: 0.7rem;
}

.hoursrow {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 0.7rem 1.15rem;
	border-top: 1px solid var(--color-border-subtle);
	font-size: 0.95rem;
}

.hoursrow:first-of-type {
	border-top: 0;
}

.hoursrow[data-today="true"] {
	background: var(--color-accent-subtle);
}

.hoursrow[data-today="true"] .hoursrow__d,
.hoursrow[data-today="true"] .hoursrow__t {
	color: var(--color-text);
	font-weight: 700;
}

.hoursrow__d {
	color: var(--color-muted-foreground);
	font-weight: 500;
}

.hoursrow__t {
	display: grid;
	justify-items: end;
	font-variant-numeric: tabular-nums;
	font-weight: 600;
	white-space: nowrap;
}

.hoursrow__t.is-closed-day {
	font-weight: 500;
	font-style: italic;
	color: var(--color-muted-foreground);
}

.todaytag {
	margin-inline-start: 0.55rem;
	padding: 0.15rem 0.4rem;
	border-radius: 999px;
	font-size: 0.62rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-primary-foreground);
	background: var(--color-primary);
	vertical-align: 0.1em;
}

.finding {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: clamp(2rem, 4vw, 3.5rem);
	margin-top: clamp(2rem, 5vw, 3.5rem);
	padding-top: clamp(2rem, 5vw, 3.5rem);
	border-top: 1px solid var(--color-border-subtle);
}

@media (min-width: 900px) {
	.finding {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	}
}

/* Whichever half survives takes the whole width, so a merchant without coordinates
   never leaves an empty column beside the address. */
.finding:has(> :only-child) {
	grid-template-columns: minmax(0, 1fr);
}

/* The map beside the address. The frame carries the aspect ratio and .media paints its hatch,
   so the box is at its final size before the embed loads and nothing below it moves when it
   arrives. Gated on the location capability in _Contact.cshtml — an empty 16:10 frame is the
   defect that gate exists to prevent. */
.mapframe {
	margin: 0;
}

.mapmedia {
	aspect-ratio: 16 / 10;
}

/* The embed is a third party's and can be slow, blocked or refused. The caption is the page's
   own, so the frame names itself even while it is still hatch — the iframe's title reaches
   assistive technology and nobody else. */
.mapframe__cap {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	margin-top: 0.65rem;
	font-size: 0.85rem;
	font-weight: 500;
	color: var(--color-muted-foreground);
}

.mapframe__cap svg {
	width: 16px;
	height: 16px;
	flex: 0 0 auto;
	color: var(--color-primary-text);
}

.addr {
	margin-top: 1.25rem;
	font-style: normal;
	font-size: 1.15rem;
	font-weight: 300;
	line-height: 1.55;
}

/* The channels the merchant record actually holds. Every empty state on the page ends
   here rather than in a sentence about what the venue does not do. */
.contacts {
	display: grid;
	gap: 0.15rem;
	margin-top: 1.25rem;
}

.contacts li {
	display: flex;
	align-items: center;
	gap: 0.85rem;
}

.contacts svg {
	width: 20px;
	height: 20px;
	flex: 0 0 auto;
	color: var(--color-primary-text);
}

.contacts a {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	font-size: 1rem;
	font-weight: 500;
	color: var(--color-link);
	overflow-wrap: anywhere;
}

.maprow {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	margin-top: 1.5rem;
}

.maplink {
	display: inline-flex;
	align-items: center;
	gap: 0.7rem;
	min-height: 56px;
	padding: 0.85rem 1.45rem;
	background: var(--color-background);
	color: var(--color-text);
	border: 1px solid var(--color-border);
	border-radius: var(--edge);
	text-decoration: none;
	font: inherit;
	font-weight: 600;
	cursor: pointer;
}

.maplink:hover {
	background: var(--color-muted);
}

.maplink svg {
	flex: 0 0 auto;
}

.maplink small {
	display: block;
	font-size: 0.72rem;
	font-weight: 500;
	color: var(--color-muted-foreground);
	letter-spacing: 0.03em;
}

/* --- MENU -------------------------------------------------------------
   There is exactly ONE sticky layer inside the menu: .rail. It is opaque, its height is
   fixed at --rail-h, and its containing block is .menuscope, which ends with the last
   category. It therefore cannot reach the section below it, and content passing beneath
   it is never visible through it.
   --------------------------------------------------------------------- */

.menuhead {
	display: grid;
	gap: 1rem;
	margin-bottom: 1.5rem;
}

.searchwrap {
	position: relative;
	max-width: 44rem;
}

.searchwrap > svg {
	position: absolute;
	left: 1rem;
	top: 50%;
	transform: translateY(-50%);
	width: 20px;
	height: 20px;
	color: var(--color-muted-foreground);
	pointer-events: none;
}

#MenuSearch {
	width: 100%;
	min-height: 60px;
	padding: 0.9rem 6.5rem 0.9rem 3rem;
	font: inherit;
	font-size: 1.05rem;
	color: var(--color-text);
	background: var(--color-background);
	border: 2px solid var(--color-border);
	border-radius: var(--edge);
}

#MenuSearch::placeholder {
	color: var(--color-muted-foreground);
}

#MenuSearch:focus {
	border-color: var(--color-focus);
}

.searchwrap__clear {
	position: absolute;
	right: 0.5rem;
	top: 50%;
	transform: translateY(-50%);
	min-height: 44px;
	padding: 0.6rem 1rem;
	border-radius: var(--edge);
	font: inherit;
	font-size: 0.85rem;
	font-weight: 700;
	cursor: pointer;
	background: var(--color-surface);
	color: var(--color-text);
	border: 1px solid var(--color-border-subtle);
}

.searchwrap__clear[hidden] {
	display: none;
}

.menutools {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.6rem;
}

.menutools__count {
	flex: 1 1 12rem;
	font-size: 0.9rem;
	color: var(--color-muted-foreground);
}

.menuscope {
	position: relative;
}

.rail {
	position: sticky;
	top: var(--masthead-h);
	z-index: 50;
	height: var(--rail-h);
	background: var(--color-background);
	border-block: 1px solid var(--color-border-subtle);
	margin-inline: calc(var(--gut) * -1);
}

.band--tint .rail {
	background: var(--color-surface);
}

.rail__in {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	height: 100%;
	max-width: var(--shell);
	margin-inline: auto;
	padding-inline: var(--gut);
}

/* Only the alpha channel of this gradient is used, so the token here is a mask stop, not
   a colour. It tells the reader the row scrolls past both edges instead of ending in a
   hard cut. */
.rail__scroll {
	display: flex;
	gap: 0.5rem;
	flex: 1 1 auto;
	min-width: 0;
	overflow-x: auto;
	overflow-y: hidden;
	scroll-snap-type: x proximity;
	scrollbar-width: none;
	-webkit-overflow-scrolling: touch;
	padding-block: 0.4rem;
	mask-image: linear-gradient(to right, transparent 0, var(--color-text) 18px, var(--color-text) calc(100% - 18px), transparent 100%);
}

.rail__scroll::-webkit-scrollbar {
	display: none;
}

.chip {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	flex: 0 0 auto;
	min-height: 44px;
	padding: 0.6rem 1rem;
	scroll-snap-align: start;
	white-space: nowrap;
	text-decoration: none;
	font-size: 0.9rem;
	font-weight: 600;
	color: var(--color-text);
	background: var(--color-background);
	border: 1px solid var(--color-border-subtle);
	border-radius: 999px;
}

.chip:hover {
	background: var(--color-muted);
}

.chip[hidden] {
	display: none;
}

.chip__n {
	font-size: 0.75rem;
	font-weight: 700;
	color: var(--color-muted-foreground);
	font-variant-numeric: tabular-nums;
}

.chip[aria-current="true"] {
	background: var(--color-primary);
	color: var(--color-primary-foreground);
	border-color: var(--color-primary);
}

.chip[aria-current="true"] .chip__n {
	color: color-mix(in srgb, var(--color-primary-foreground) 78%, transparent);
}

.rail__btn {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.4rem;
	min-height: 44px;
	min-width: 44px;
	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);
}

.rail__btn svg {
	width: 16px;
	height: 16px;
}

.rail__btn:hover {
	background: var(--color-primary-soft);
}

/* The index panel is in normal flow directly beneath the rail — never an overlay.
   Opening it scrolls it to rest under the stuck rail. */
.indexpanel {
	scroll-margin-top: calc(var(--sticky-h) + 0.75rem);
	padding: 1.5rem 0 1.75rem;
	border-bottom: 1px solid var(--color-border-subtle);
}

.indexpanel[hidden] {
	display: none;
}

.indexpanel h3 {
	margin-bottom: 0.85rem;
}

/* The category index and the section index in the masthead are one component. It is the page's
   answer to "a list too long for the bar it is reached from", and it works the same in both. */
.sectionspanel .grouplabel,
.indexpanel .grouplabel {
	margin-bottom: 0.85rem;
}

.indexgrid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
	gap: 0.5rem;
}

.indexgrid a {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	min-height: 48px;
	padding: 0.65rem 0.95rem;
	border-radius: var(--edge);
	text-decoration: none;
	font-size: 0.92rem;
	font-weight: 600;
	color: var(--color-text);
	background: var(--color-background);
	border: 1px solid var(--color-border-subtle);
}

.indexgrid a:hover {
	border-color: var(--color-border);
}

.indexgrid span {
	flex: 0 0 auto;
	font-size: 0.78rem;
	font-weight: 700;
	color: var(--color-muted-foreground);
}

/* --- categories & dishes ----------------------------------------------- */

.cat {
	scroll-margin-top: calc(var(--sticky-h) + 1rem);
	padding-block: 1.75rem;
	border-top: 1px solid var(--color-border-subtle);
}

.cat:first-of-type {
	border-top: 0;
}

.cat[hidden] {
	display: none;
}

.cat__summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	cursor: pointer;
	list-style: none;
	min-height: 56px;
	padding-block: 0.25rem;
}

.cat__summary::-webkit-details-marker {
	display: none;
}

/* The heading and its standfirst are one block so the dish count and caret stay on the
   heading's line however many lines the standfirst runs to. */
.cat__title {
	flex: 1 1 auto;
	min-width: 0;
}

.cat__meta {
	display: flex;
	align-items: center;
	gap: 0.85rem;
	flex: 0 0 auto;
}

.cat__n {
	font-size: 0.78rem;
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--color-muted-foreground);
	white-space: nowrap;
}

.cat__caret {
	width: 1.6rem;
	height: 1.6rem;
	color: var(--color-muted-foreground);
	transition: transform 200ms ease;
}

details[open] > .cat__summary .cat__caret {
	transform: rotate(180deg);
}

/* Two lines, then it clamps: SubMenuDesc holds multi-paragraph text for some merchants
   and a category heading is not the place to read it. */
.cat__note {
	margin-top: 0.4rem;
	font-size: 0.92rem;
	font-weight: 350;
	color: var(--color-muted-foreground);
	max-width: 62ch;
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	overflow: hidden;
}

.dishes {
	margin-top: 1.25rem;
	list-style: none;
	padding: 0;
}

/* Desktop: the reading measure is held by splitting the list into two columns rather than
   by narrowing the section, so the price never drifts away from its dish name and the
   category rules run the full width of the shell. */
@media (min-width: 960px) {
	.dishes {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: clamp(2rem, 3vw, 3.5rem);
		align-items: start;
	}
}

/* Every row carries the rule, including the first: the rule above row one separates the
   list from its own category heading, and a :first-child exemption goes stale the moment
   search hides that row. */
.dish {
	padding-block: 1.15rem;
	border-top: 1px dotted var(--color-border-subtle);
}

.dish[hidden] {
	display: none;
}

.dish__row {
	display: flex;
	align-items: flex-start;
	gap: 1rem;
}

.dish__main {
	flex: 1 1 auto;
	min-width: 0;
}

/* The name holds a floor, and the row wraps rather than squeezing it below that floor. At 320
   the photograph took 96px and the price 71px, leaving the dish name 83px — the narrowest column
   in the row, and the one thing on it a diner is reading. The floor is a measure, not a
   breakpoint: the price drops to its own line exactly when it would otherwise cost the name a
   readable line, and stays on the name's line at every width where both fit.
   overflow-wrap because DishName is merchant free text and one unbroken word can be longer than
   the column it is given. */
.dish__name {
	flex: 1 1 auto;
	min-width: 8.5rem;
	overflow-wrap: anywhere;
	font-size: 1.05rem;
	font-weight: 550;
	letter-spacing: -0.01em;
	line-height: 1.35;
	color: var(--color-text);
}

.dish__desc {
	margin-top: 0.2rem;
	font-size: 0.93rem;
	font-weight: 350;
	color: var(--color-muted-foreground);
	max-width: 58ch;
}

/* Nothing competes with the price for the right edge, so it sits on the dish name's line,
   where a printed menu has always put it. */
.dish__head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0.15rem 0.9rem;
	margin: 0;
}

.dish__price {
	flex: 0 0 auto;
	font-size: 1.05rem;
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

/* Dietary tags sit under the description rather than beside the name, because a name plus
   a price plus up to six chips on one line reflows badly at 390 and the price must keep
   the right edge to itself. Six is the real maximum a merchant has set on one dish, so
   the row wraps rather than truncating. */
.dish__tags {
	display: flex;
	flex-wrap: wrap;
	gap: 0.3rem;
	margin-top: 0.5rem;
}

.tag {
	display: inline-flex;
	align-items: center;
	padding: 0.15rem 0.45rem;
	font-size: 0.66rem;
	font-weight: 700;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: var(--color-primary-text);
	background: var(--color-primary-subtle);
	border-radius: var(--edge);
}

/* The ingredient list is quieter than the description because it is a list of things, not
   a sentence about the dish, and it is the field that makes the menu search find
   "saffron". */
.dish__made {
	margin-top: 0.35rem;
	font-size: 0.83rem;
	font-weight: 350;
	color: var(--color-muted-foreground);
	max-width: 58ch;
}

.dish__made em {
	font-style: normal;
	font-weight: 600;
	color: var(--color-text);
}

/* --- the dish shot -----------------------------------------------------
   The shot is the row's leading gutter, and its box is the same size in every row whether
   or not the merchant supplied a photograph. Only what fills it changes, so a mixed list
   keeps one left edge, one rhythm, and no suggestion that something failed to render.
   ----------------------------------------------------------------------- */

.dish__shot {
	position: relative;
	flex: 0 0 auto;
	width: var(--shot-w);
	aspect-ratio: 4 / 3;
}

.dish__photo {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	border-radius: var(--edge);
}

/* --- what a dish with a photograph shows before, and instead of, the photograph -------
   A shot the merchant has filled carries data-shot, and media-states.js moves it from
   loading to ready or failed. All three occupy this same box, so nothing on the row moves
   when one replaces another. The plate is ::before, behind .dish__photo, which is why the
   photograph itself no longer paints a background: it would cover the plate while the file
   was still arriving. A dish with no photograph carries no data-shot and keeps its course
   glyph, which is a different statement and must not be confused with a broken file.
   ------------------------------------------------------------------------------------- */

.dish__shot[data-shot]::before {
	content: '';
	position: absolute;
	inset: 0;
	border-radius: var(--edge);
	background: var(--color-surface);
}

.dish__shot[data-shot="loading"]::before {
	background-image: linear-gradient(100deg,
			transparent 0 35%,
			color-mix(in srgb, var(--color-text) 9%, transparent) 50%,
			transparent 65% 100%);
	background-repeat: no-repeat;
	background-size: 300% 100%;
	animation: shot-shimmer 1.4s linear infinite;
}

@keyframes shot-shimmer {
	from {
		background-position: 120% 0;
	}

	to {
		background-position: -120% 0;
	}
}

/* Still a visible placeholder, so the slot never reads as empty; just not a moving one. */
@media (prefers-reduced-motion: reduce) {
	.dish__shot[data-shot="loading"]::before {
		background-image: none;
		background-color: color-mix(in srgb, var(--color-text) 7%, var(--color-surface));
		animation: none;
	}
}

/* The merchant has a photograph and it did not arrive. Not the course glyph: the glyph says
   the merchant never supplied one, which would be a false statement about a dish whose file
   is merely broken. A failed image element still paints its own box, so it is taken out of
   the flow along with the stock badge that describes it. */
.dish__shot[data-shot="failed"] .dish__photo,
.dish__shot[data-shot="failed"] .dish__stock {
	display: none;
}

.dish__shot[data-shot="failed"]::before {
	background: var(--color-muted);
}

.dish__shot[data-shot="failed"]::after {
	content: '';
	position: absolute;
	inset: 0;
	background: var(--color-muted-foreground);
	-webkit-mask: var(--glyph-broken-photo) center / 46% 46% no-repeat;
	mask: var(--glyph-broken-photo) center / 46% 46% no-repeat;
}

/* A dish with no photograph gets its course glyph on a filled tile. Not a frame — an
   outlined empty box is what a failed image looks like — and not a letterform, which says
   nothing about the dish and repeats down the column. The tile is flat, the glyph names
   the course, and the box is the same size as the photograph slot so a mixed list keeps
   one left edge. */
.dish__plate {
	position: absolute;
	inset: 0;
	margin: 0;
	display: grid;
	place-items: center;
	color: var(--color-primary-text);
	background: var(--color-primary-subtle);
	border-radius: var(--edge);
}

.dish__plate svg {
	width: 52%;
	height: 52%;
}

/* DishIsStockImg: the merchant's stock image, not a photograph of this dish. The
   distinction is the merchant's own field and has to survive on the page. */
.dish__stock {
	position: absolute;
	left: 0.25rem;
	top: 0.25rem;
	z-index: 1;
	margin: 0;
	max-width: calc(100% - 0.5rem);
	padding: 0.1rem 0.32rem;
	font-size: 0.58rem;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	white-space: nowrap;
	color: var(--color-accent-text);
	background: var(--color-accent-subtle);
	border: 1px solid var(--color-accent-soft);
	border-radius: var(--edge);
}

/* No dish anywhere has a photograph. Rather than a column of identical tiles, the menu
   stops being an illustrated list and becomes a carte: no gutter, the name a size larger,
   solid rules, and more air between dishes. */
#MenuScope[data-photos="none"] .dish {
	padding-block: 1.4rem;
	border-top-style: solid;
}

#MenuScope[data-photos="none"] .dish__name {
	font-size: 1.15rem;
	font-weight: 500;
}

#MenuScope[data-photos="none"] .dish__price {
	font-size: 1.1rem;
}

#MenuScope[data-photos="none"] .dish__desc {
	max-width: 64ch;
}

.empty {
	padding: 2.5rem 0;
	color: var(--color-muted-foreground);
	max-width: var(--measure);
}

.empty[hidden] {
	display: none;
}

/* The menu ends with an explicit terminator, so the reader is never in doubt about where
   the carte stopped. */
.menuend {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	margin-top: 0.5rem;
	padding-top: 1.75rem;
	border-top: 3px solid var(--color-primary);
}

.menuend p {
	font-size: 0.9rem;
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--color-muted-foreground);
}

.menuend__acts {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
}

/* --- what guests say ---------------------------------------------------
   The stars are decoration. The number beside them is the value a screen reader gets,
   because half stars exist in the data and no arrangement of five glyphs states that
   unambiguously. */

.reviews {
	display: grid;
	gap: 1rem;
	grid-template-columns: repeat(auto-fit, minmax(17.5rem, 1fr));
}

.review {
	display: grid;
	gap: 0.7rem;
	align-content: start;
	padding: 1.35rem;
	background: var(--color-background);
	border: 1px solid var(--color-border-subtle);
	border-radius: var(--edge);
}

.review__top {
	display: flex;
	align-items: center;
	gap: 0.7rem;
	flex-wrap: wrap;
}

.review__stars {
	display: inline-flex;
	gap: 1px;
}

.review__stars svg {
	width: 15px;
	height: 15px;
	color: var(--color-primary);
}

.review__stars svg[data-off='true'] {
	color: var(--color-border);
}

.review__score {
	font-size: 0.85rem;
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

.review__when {
	margin-inline-start: auto;
	font-size: 0.8rem;
	color: var(--color-muted-foreground);
}

.review__body {
	font-size: 0.98rem;
	font-weight: 350;
	line-height: 1.65;
	overflow-wrap: anywhere;
}

.review__who {
	font-size: 0.87rem;
	font-weight: 600;
}

/* A merchant reply is a second voice inside one card, so it is indented and marked rather
   than set as another paragraph. */
.review__reply {
	padding: 0.8rem 0.95rem;
	background: var(--color-muted);
	border-inline-start: 3px solid var(--color-primary);
	border-radius: var(--edge);
	font-size: 0.9rem;
	font-weight: 350;
	line-height: 1.6;
	overflow-wrap: anywhere;
}

.review__replywho {
	display: block;
	margin-bottom: 0.3rem;
	font-size: 0.66rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-primary-text);
}

.reviews__foot {
	margin-top: 1.5rem;
	font-size: 0.92rem;
	color: var(--color-muted-foreground);
	max-width: var(--measure);
}

/* --- takeaway & delivery ------------------------------------------------
   Ordering lives on its own page with its own logic, exactly as booking does. This column
   says how collection works and links out; it does not reimplement a basket. */

.ordergrid {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: clamp(2rem, 4vw, 3.5rem);
}

@media (min-width: 900px) {
	.ordergrid {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	}
}

.ordergrid:has(> :only-child) {
	grid-template-columns: minmax(0, 1fr);
}

.collect {
	display: grid;
	gap: 1rem;
	justify-items: start;
	margin-top: 1.5rem;
}

.partners {
	display: grid;
	gap: 0.85rem;
	margin-top: 1.5rem;
	list-style: none;
	padding: 0;
}

/* [mark] [name] [Order] on one line at every width. The row wrapped because it was
   flex-wrap: wrap over a mark at its intrinsic width and a body with a 12rem flex basis:
   wrapping is decided on the basis, not on the shrunk size, so 132 + 192 + the action
   overflowed 390px and the action dropped to a line of its own, reading as a second row. */
.partner {
	display: flex;
	align-items: center;
	gap: 1.25rem;
	flex-wrap: nowrap;
	padding: 1.25rem 1.35rem;
	min-height: 104px;
	background: var(--color-background);
	border: 1px solid var(--color-border-subtle);
	border-radius: var(--edge);
	text-decoration: none;
	color: var(--color-text);
}

a.partner:hover {
	border-color: var(--color-border);
	background: var(--color-muted);
}

/* The partner mark is the artwork the merchant record carries, with the service name repeated
   as text beside it so the row still identifies itself if the mark fails to paint.

   cover, not contain. Every asset in MasterBizPageDeliveryServices is a 250x250 transparent
   canvas holding a wide wordmark across the middle: measured, the wordmark is 235x40 for Uber
   Eats, 233x28 for DoorDash, 228x42 for foodpanda, 223x42 for GrabFood, and the same shape for
   the other four. contain fits the canvas, so 84% of what it fit was padding and the wordmark
   painted 56x10 inside a 148x60 plate. cover fits the wordmark instead and crops the padding:
   the plate's 2.47:1 ratio takes the middle 40% of the source height, which is wider than the
   tallest artwork in the table, so no mark is clipped. See approved-design/DIVERGENCES.md. */
.partner__mark {
	flex: 0 0 auto;
	width: 148px;
	height: 60px;
	object-fit: cover;
	object-position: center;
}

@media (max-width: 560px) {
	.partner {
		gap: 0.9rem;
		padding: 1rem;
	}

	.partner__mark {
		width: 116px;
		height: 47px;
	}
}

@media (max-width: 400px) {
	.partner {
		gap: 0.7rem;
	}

	.partner__mark {
		width: 96px;
		height: 39px;
	}
}

.partner__name {
	flex: 1 1 auto;
	font-size: 1.2rem;
	font-weight: 700;
	letter-spacing: -0.015em;
}

.partner__go {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	font-size: 0.82rem;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--color-primary-text);
}

/* Below 360px the word and the service name cannot both hold a line, and the name is the one
   that identifies the row. The arrow keeps the action visible and the screen-reader text on
   .partner__go still names the destination in full. */
@media (max-width: 360px) {
	.partner__go-word {
		display: none;
	}
}

/* Not an error panel. A dashed outline and a struck-through icon told the reader something
   was broken or withheld; this is an invitation to talk to the venue, so it is a filled
   panel in the theme's own wash. */
.nopartners {
	margin-top: 1.5rem;
	padding: clamp(1.5rem, 4vw, 2.25rem);
	background: var(--color-primary-wash);
	border: 1px solid var(--color-border-subtle);
	border-radius: var(--edge);
}

.nopartners__icon {
	width: 40px;
	height: 40px;
	color: var(--color-primary-text);
}

.nopartners h4 {
	margin-top: 0.85rem;
	font-size: 1.15rem;
	font-weight: 500;
}

.nopartners p {
	margin-top: 0.6rem;
	font-size: 0.98rem;
	color: var(--color-muted-foreground);
	max-width: 46ch;
}

.nopartners__acts {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	margin-top: 1.5rem;
}

/* ------------------------------------------------------------- share modal
   The dialog chrome (.modal, .modal-content, .modal-header) belongs to
   restaurant-shell.css; only the share panel's own layout lives here. */

.share-url-label {
	display: block;
	margin-bottom: 0.5rem;
	font-size: 0.85rem;
	font-weight: 600;
	color: var(--color-text);
}

.share-url {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

.share-url input {
	flex: 1 1 14rem;
	min-width: 0;
	min-height: 44px;
	padding: 0.6rem 0.9rem;
	font: inherit;
	font-size: 0.95rem;
	color: var(--color-text);
	background: var(--color-background);
	border: 1px solid var(--color-border);
	border-radius: var(--edge);
}

.share-url input:focus {
	border-color: var(--color-focus);
}

.share-url-status {
	min-height: 1.25rem;
	margin-top: 0.5rem;
	font-size: 0.85rem;
	color: var(--color-muted-foreground);
}

.share-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	margin-top: 1.5rem;
}

.share-actions h3 {
	flex: 1 1 100%;
}

.share-actions .btn {
	min-width: 44px;
	padding-inline: 1.1rem;
}
