/*
 * WordPress / Elementor overrides.
 *
 * Loaded LAST, after the verbatim design stylesheets. Anything here exists
 * because WordPress or Elementor wraps the design's markup differently to the
 * prototype — not because the design needed changing.
 *
 * Keep design fixes out of this file, and keep this file out of the design's
 * stylesheets: the split is what lets those stay byte-identical and diffable.
 */

/*
 * Let NorthBite sections own their own width.
 *
 * Elementor boxes its columns to the configured content width (1140px by
 * default). The design's containers are wider — `.section` is 1320px and the
 * `.full` sections are edge-to-edge — so a boxed column crops every one of
 * them.
 *
 * Widening the widget wrapper to the viewport hands width control back to the
 * design's own CSS: `.section` re-centres itself at max-width 1320px, and
 * `.lp-hero.full` fills the viewport. Doing it here rather than asking the
 * client to configure each Elementor section keeps the design correct however
 * they build the page, and independent of Elementor's content-width setting.
 *
 * Must load after the design stylesheets: `.lp-hero.full { margin: 0 }` has
 * equal specificity and would otherwise win on source order.
 *
 * The `.elementor-element` prefix is required, not decorative: Elementor's
 * `.elementor-widget-wrap > .elementor-element { width: 100% }` is 0,2,0 and
 * beats a lone attribute selector, so `width` silently stayed at the column
 * width while max-width and margin applied — leaving the section offset.
 */
.elementor-element[class*="elementor-widget-northbite-"] {
	width: 100vw;
	max-width: 100vw;
	margin-left: calc(50% - 50vw);
	margin-right: 0;
}

/*
 * That breakout is 100vw, which ignores the scrollbar and would add a
 * horizontal scrollbar of its width. Clip on the main wrapper rather than the
 * body: `overflow` on an ancestor breaks position:sticky, and the sticky nav
 * sits outside #main.
 */
.site-main {
	overflow-x: clip;
}

/*
 * Make the `hidden` attribute actually hide things.
 *
 * `hidden` is display:none in the UA stylesheet, so any author rule that sets
 * display beats it — and the design sets display on plenty of components
 * (.lp-show-chip is inline-flex, .lp-nav-drawer is flex). The theme toggles
 * visibility with `hidden` rather than a class so state is exposed to
 * assistive tech, which means this rule is load-bearing: without it every
 * "hidden" step of the showcase renders at once, stacked.
 *
 * No !important needed — this file loads last, so it wins the tie on source
 * order.
 */
[hidden] {
	display: none;
}

/*
 * Centred section heading.
 *
 * The prototype centres the How It Works heading with inline styles on its
 * .section-head. Named here so the template stays markup-only. Values copied
 * from the prototype.
 */
.section-head.lp-show-head {
	justify-content: center;
	text-align: center;
	flex-direction: column;
	align-items: center;
}

/*
 * Button rows the prototype styled inline.
 *
 * A few of its sections wrap their buttons in an unclassed <div> with inline
 * flex styles. Templates can't carry inline styles cleanly, so the same values
 * are named here. Values copied from the prototype, not invented.
 */
.lp-about-actions {
	display: flex;
	gap: 10px;
	margin-top: 22px;
	flex-wrap: wrap;
}

/*
 * FAQ question is a <button>, not the prototype's clickable <div>.
 *
 * The accordion has to be operable by keyboard, which means a real button —
 * but a button brings its own width, font, background and border, so the row
 * collapsed to the width of its text. This resets it back to what the
 * design's .lp-faq-q rule assumes it is styling.
 */
.lp-faq-q {
	width: 100%;
	background: none;
	border: 0;
	margin: 0;
	color: inherit;
	text-align: left;
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;

	/*
	 * font-family and line-height only — NOT the `font` shorthand. This file
	 * loads after the design's, so `font: inherit` also reset the font-weight
	 * 600 and font-size 16.5px that the design sets here, rendering the
	 * questions lighter and smaller than designed.
	 */
	font-family: inherit;
	line-height: inherit;
}

/*
 * Showcase progress bar duration.
 *
 * The design declares `animation: lpFill linear forwards` with no duration —
 * the prototype supplies it inline per render, so on its own the bar completes
 * in 0s. The theme passes the step interval as --nb-track-ms instead, which
 * survives the animation-name reset JS uses to replay the bar.
 */
.lp-show-track i {
	animation-duration: var(--nb-track-ms, 5200ms);
}

/*
 * Same width/appearance reset for the showcase steps. Their typography needs no
 * fixing here: the design already declares `font: inherit` on .lp-show-step.
 */
.lp-show-step {
	width: 100%;
	background: none;
	text-align: left;
	-webkit-appearance: none;
	appearance: none;
}

/*
 * No override is needed for the App CTA photograph's height.
 *
 * An earlier version forced `height: 100%` here, to beat Elementor's
 * `.elementor img { height: auto }` (0,1,1), because the picture was cropped to the
 * panel with object-fit: cover. It now scales with the panel instead, so height: auto
 * is exactly what it wants and Elementor's own rule agrees with the theme's.
 */

/* App CTA — the prototype styles these inline. Values copied verbatim.
   The .app-cta-loc / -search / -shot / -rest rules that used to live here styled the
   inside of a phone drawn in CSS. That mock-up was replaced by a photograph on
   2026-07-29, so the rules had nothing left to match and are gone with it. */
.app-cta-pill {
	display: inline-block;
	padding: 4px 12px;
	background: white;
	border-radius: 999px;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: .04em;
	text-transform: uppercase;
	color: var(--warm-1);
	margin-bottom: 14px;
}


/*
 * Hero background — flat colour on the phone side only, per client direction.
 *
 * The design lights the panel with three radial glows over its diagonal
 * gradient: amber at 84% 18%, green at 12% 82%, orange at 72% 92%. The client
 * asked for the right-hand side — the phone and floating cards — to sit on a
 * flat colour instead, with the left copy side left as designed.
 *
 * So only the glow layer is redeclared: the green bottom-left glow stays, the
 * two warm right-side glows are dropped, and a horizontal wash fades to the
 * flat colour across the middle. The fade means no hard seam down the panel.
 *
 * Done on .lp-hero-mesh rather than .lp-hero-inner deliberately — the mesh is
 * an overlay that already sits behind the content, so the design's gradient
 * rule is neither duplicated nor touched, and sections/landing.css stays
 * byte-identical to the design. Delete this block to get the original panel.
 *
 * The fade must COMPLETE before the video's left edge, which sits at roughly 60%
 * of the viewport. An earlier version finished at 70%, so the background was
 * still mid-transition (#111B14) where the video's flat #161D18 began — and the
 * video read as a lighter rectangle. Finishing at 50% leaves the whole phone
 * column on the flat colour at every width where the hero is side-by-side.
 */
/*
 * Matched to the hero video's own background so the two blend with no visible
 * video box.
 *
 * Sampled from the ENCODED files, not the source: the source clip's background is
 * #141C18, but scaling and re-encoding shifted it to #161D18. WebM, MP4 and the
 * poster all agree on #161D18, and it is constant across all ten seconds, so a
 * flat colour matches exactly.
 *
 * Re-check this if the clip is ever re-encoded.
 */
:root {
	--nb-hero-bg: #161D18;
}

/*
 * Hero phone video.
 *
 * Replaces the CSS phone mock-up when a video is set. The clip already contains
 * its own device frame, so the sizing here is chosen so the phone *inside* the
 * video renders at the same width the mock-up had: the device body spans about
 * 900 of the video's 1080px, so 288px of phone needs 288 * 1080/900 = ~345px of
 * video. Height follows from the 9:16 aspect, and aspect-ratio reserves the box
 * before the file loads so there is no layout shift.
 *
 * The -3deg tilt is copied from the mock-up so the phone still sits with the
 * angled floating cards.
 */
.lp-phone-video {
	width: 345px;

	/*
	 * ...but never wider than the space it has (client, 2026-08-17: hero cut off
	 * on small screens). The width above is fixed, and a grid item cannot shrink
	 * below its min-content, so under about 350px of viewport this width dragged
	 * the whole hero grid — headline and lead included — wider than the screen,
	 * where .lp-hero-inner's overflow: hidden sliced it. The matching
	 * `min-width: 0` on the grid's columns is in sections/landing.css.
	 *
	 * The 16px is room for this element's own translateX(10px) rotate(-3deg), so
	 * the tilt cannot push it back off-screen. Kept deliberately small: at 1024px
	 * an allowance of 22px already began shaving a pixel off a video that fitted
	 * perfectly well, and this must not alter any width that already works.
	 */
	max-width: calc(100% - 16px);
	aspect-ratio: 720 / 1280;

	/*
	 * translateX compensates for the device not being centred in its own frame.
	 * Measured across every second of the clip: the device body spans x 41..639
	 * of 720, so its centre sits 20px left of the frame's centre — 9.6px at the
	 * rendered width. Centring the video therefore left the phone about 10px
	 * short of where the mock-up's phone was, opening a gap between it and the
	 * floating cards, which are positioned against the column rather than the
	 * phone. Shifting right restores the design's overlap.
	 */
	transform: translateX(10px) rotate(-3deg);
	position: relative;
	/*
	 * No z-index, matching the mock-up it replaces. Raising it put the video
	 * over the floating cards, which are meant to overlap the phone.
	 */
}

.lp-phone-video-el {
	width: 100%;
	height: 100%;
	display: block;
	object-fit: cover;
	/* The clip's own background; keeps the box invisible before the first frame. */
	background: var(--nb-hero-bg);

	/*
	 * Feather the edges.
	 *
	 * The clip's background is not perfectly uniform — encoding leaves it
	 * varying by about one level, reading #161E19 near the phone and #161D18 at
	 * the corners. A flat page colour therefore cannot match it everywhere, and
	 * the residual one-level step showed as a faint straight line down the
	 * video's edge: a visible box around the phone.
	 *
	 * Fading the outermost few percent to transparent removes the boundary
	 * entirely, so any residual difference blends instead of forming an edge.
	 * Only empty background sits in that margin, so nothing of the phone is
	 * lost. Two gradients intersected, to feather all four sides.
	 *
	 * Kept narrow (3% / 2.5%) on purpose: the mismatch is only about one level,
	 * so a short fade is enough to break the edge while keeping the device's
	 * own shadow crisp. Re-encoding cannot avoid this — measured at CRF 20 and
	 * at 4:4:4, the decoded background is still not a single value, because the
	 * variation comes from YUV-to-RGB rounding and encoder dithering rather
	 * than from quality.
	 */
	-webkit-mask-image:
		linear-gradient(to right, transparent, #000 3%, #000 97%, transparent),
		linear-gradient(to bottom, transparent, #000 2.5%, #000 97.5%, transparent);
	-webkit-mask-composite: source-in;
	mask-image:
		linear-gradient(to right, transparent, #000 3%, #000 97%, transparent),
		linear-gradient(to bottom, transparent, #000 2.5%, #000 97.5%, transparent);
	mask-composite: intersect;
}

/* Mirrors the mock-up's own smaller size at the design's breakpoint (248/288). */
@media (max-width: 760px) {
	.lp-phone-video {
		width: 297px;
	}
}

.lp-hero-mesh {
	background:
		linear-gradient(90deg, transparent 34%, var(--nb-hero-bg) 50%),
		radial-gradient(circle at 12% 82%, rgba(0, 167, 93, .42), transparent 42%);
}

/*
 * The same patch, turned through 90 degrees, for the stacked layout (client,
 * 2026-08-17: the video's background shows as a box on small screens).
 *
 * The flat colour above exists so the video's own background — a flat #161D18
 * baked into the clip — has something matching behind it. It runs left-to-right
 * because in the side-by-side layout the phone is always in the right-hand half.
 * Stacked, the phone is centred and low instead, so its left side came to sit on
 * the green gradient: measured at 390px, the video's background differed from
 * the page beside it by 30 levels, and by 48 at 900px. That difference is the
 * box.
 *
 * So below 941px the flat colour runs top-to-bottom, covering the lower part of
 * the hero where the phone now is, and the green glow moves up from 82% to 26%
 * to sit behind the copy instead of behind the phone. The hero still reads green
 * where the text is, which is what that glow is for.
 *
 * Measured after: 1 level at 390px, against 30 before. The 22 levels that remain
 * at 900px are the device's own drop shadow inside the clip, not a mismatch —
 * confirmed by eye; there is no edge to see.
 *
 * Widening .lp-phone-video-el's mask instead only halved the difference (26 -> 7
 * levels) because a partly-transparent mismatched colour still shows, and it
 * begins eating the device's shadow. Painting a feathered pad on
 * .lp-hero-phone does not work either: that box is shorter than the video, so it
 * does not cover it.
 */
@media (max-width: 1300px) {
	.lp-hero-mesh {
		background:
			linear-gradient(180deg, transparent 30%, var(--nb-hero-bg) 44%),
			radial-gradient(circle at 12% 26%, rgba(0, 167, 93, .42), transparent 42%);
	}
}

/*
 * News empty state.
 *
 * Not in the design — the prototype had hard-coded posts, so it never needed
 * one. Built from the same tokens as the design's cards so a brand new site
 * still looks deliberate rather than half-finished.
 */
.lp-news-empty {
	background: var(--card);
	border: 1px solid var(--line);
	border-radius: var(--r-lg);
	padding: 40px 28px;
	text-align: center;
	box-shadow: var(--shadow-sm);
}

.lp-news-empty-ico {
	display: inline-grid;
	place-items: center;
	width: 46px;
	height: 46px;
	border-radius: 999px;
	background: var(--nb-soft);
	color: var(--nb-3);
	margin-bottom: 14px;
}

.lp-news-empty p {
	color: var(--mute);
	font-size: 15px;
	margin: 0 auto;
	max-width: 46ch;
}

/* Editor-only prompt: quieter than the public message, and clearly a note. */
.lp-news-empty-admin {
	border-top: 1px dashed var(--line);
	color: var(--mute-2) !important;
	font-size: 13px !important;
	margin-top: 20px !important;
	padding-top: 16px;
}

.lp-news-empty-admin a {
	color: var(--nb-3);
	text-decoration: underline;
}

/*
 * Final CTA logo height.
 *
 * `.lp-final-logo` (0,1,0) loses to Elementor's `.elementor img { height: auto }`
 * (0,1,1), so the logo rendered at its natural size — several times too large.
 * Re-stated as `img.lp-final-logo` (0,1,1) to win on source order, including the
 * design's two smaller breakpoints.
 *
 * The header and footer logos are unaffected: they sit outside Elementor's
 * wrapper, so that rule never reaches them.
 */
img.lp-final-logo {
	height: 68px;
	width: auto;
}

/*
 * 44px below 760px, mirroring the design's effective cascade. It also has a
 * 48px rule at 700px in landing.css, but site.css loads later and its 760px
 * rule wins there too — so 48px never actually applies, and copying it would
 * only reintroduce dead CSS.
 */
@media (max-width: 760px) {
	img.lp-final-logo {
		height: 44px;
	}
}

/*
 * Photos inside the design's .food-ph containers.
 *
 * The prototype set these on the element via inline styles in its FoodPh
 * component; the theme renders markup from PHP, so they live here instead.
 * Without them the image keeps its natural aspect ratio and the container
 * shows the top of the frame rather than the centre.
 *
 * Deliberately `div.food-ph img` (0,1,2) rather than `.food-ph img` (0,1,1):
 * Elementor ships `.elementor img { height: auto }` at the same specificity
 * and its stylesheet loads after the theme's, so an equal-specificity rule
 * loses on source order. The extra element selector wins it without
 * resorting to !important.
 */
div.food-ph img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/*
 * Contact page additions.
 *
 * The prototype's own .form-card, .field, .input, .side-card and .contact-way
 * styles are already in sections/site.css and used unchanged. Only these three
 * are new, because the prototype had no server to talk to.
 */

/* Portals card: the prototype styled this stack inline. Values copied. */
.nb-portal-links {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

/*
 * Honeypot. Hidden without display:none, because some bots skip fields that are
 * display:none — and taken out of the layout so it cannot be seen or tabbed to.
 */
.nb-hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* Validation message. No prototype equivalent — built from the design's tokens. */
.form-error {
	background: var(--warn-bg);
	border: 1px solid rgba(201, 122, 10, .25);
	border-radius: var(--r);
	color: var(--warn);
	font-size: 14.5px;
	line-height: 1.5;
	margin: 0 0 18px;
	padding: 12px 14px;
}

/*
 * ---------------------------------------------------------------------------
 * How It Works — real app screenshots
 * ---------------------------------------------------------------------------
 *
 * The prototype draws each step's phone in CSS (.lp-mphone, a fixed 260x400).
 * Where the client has supplied a screenshot of the real app we show that
 * instead, and the supplied exports already include the device frame.
 *
 * It fills the mock-up's full 260x400 box, cropped from the top — so a real
 * screenshot reads at the same size and in the same frame as the design's drawn
 * phone, cut off below the last visible row rather than shrunk to fit as a whole
 * device (client, 2026-07-30).
 *
 * That crop costs about a fifth off the bottom of a phone-shaped export: at
 * 640x1225 the box shows the top 985px. The bottom of each screen goes with it —
 * the "Add item" button on the order screen, the delivery PIN on the tracking
 * one. That is the design's own treatment, which cuts mid-content too, and the
 * information is repeated in the step copy beside it. If a screen's payoff has
 * to stay visible, re-export it cropped shorter rather than changing this: an
 * export at 260:400 fills the box with nothing lost.
 *
 * Selector is .lp-show-phone img.lp-show-shot, not .lp-show-shot: Elementor's
 * `.elementor img { height: auto }` has the same specificity as a lone class
 * and would win or lose on load order alone.
 */
.lp-show-phone img.lp-show-shot {
	display: block;
	width: 260px;
	height: 400px;
	max-width: 100%;
	object-fit: cover;
	object-position: top;

	/* The frame shadow the CSS mock-up carried, so the phone keeps its lift. */
	filter: drop-shadow(0 34px 70px rgba(20, 25, 22, .28));
}

/*
 * ---------------------------------------------------------------------------
 * Legal pages
 * ---------------------------------------------------------------------------
 *
 * The prose card reuses .form-card and .nb-prose wholesale; only the measure
 * needs setting. 860px minus the card's 32px padding leaves about 796px of text,
 * matching the article template's 780px.
 */
.nb-legal {
	max-width: 860px;
}

/* The eyebrow is a link back to the Legal index here, so give it link affordance. */
a.page-eyebrow {
	text-decoration: none;
	transition: background var(--nb-dur, .18s) ease;
}

a.page-eyebrow:hover {
	background: rgba(255, 255, 255, .2);
}

/*
 * ---------------------------------------------------------------------------
 * Cookie consent banner
 * ---------------------------------------------------------------------------
 *
 * Not in the design prototype, so it is built from the design's own tokens and
 * button classes rather than new visual language.
 *
 * position: fixed is load-bearing, not styling: the page measures 0 Cumulative
 * Layout Shift and a banner in normal flow would push the footer down after the
 * inline reveal script runs.
 */
.nb-cookie {
	position: fixed;
	z-index: 60;
	right: 16px;
	bottom: 16px;
	left: 16px;
	background: white;
	border: 1px solid var(--line);
	border-radius: var(--r-lg);
	box-shadow: 0 18px 50px rgba(20, 25, 22, .18);
	padding: 18px 20px;

	/*
	 * A short viewport — a small phone in landscape — would otherwise push the
	 * buttons off the top of the screen, leaving a banner that can be read but not
	 * answered. Scrolling inside it keeps every option reachable.
	 */
	max-height: calc(100vh - 32px);
	overflow-y: auto;
}

/* Sits above the hero's floating cards but below the sticky nav. */
.nb-cookie-inner {
	display: flex;
	align-items: center;
	gap: 20px;
	max-width: 1320px;
	margin: 0 auto;
	flex-wrap: wrap;
}

.nb-cookie-text {
	flex: 1 1 420px;
	min-width: 0;
}

.nb-cookie-text strong {
	display: block;
	font-size: 15px;
	margin-bottom: 3px;
}

.nb-cookie-text p {
	margin: 0;
	font-size: 13.5px;
	line-height: 1.5;
	color: var(--mute);
}

/* The policy link sits in muted body text, so it needs its own affordance. */
.nb-cookie-text a {
	color: var(--nb-3);
	font-weight: 600;
	text-decoration: underline;
	text-underline-offset: 2px;
}

.nb-cookie-text a:hover {
	text-decoration-thickness: 2px;
}

/*
 * The category list. A fieldset for the grouping a screen reader announces, with
 * its own border and margins reset — the browser default box would read as a
 * second card inside the banner.
 */
.nb-cookie-cats {
	flex: 1 1 100%;
	display: flex;
	flex-wrap: wrap;
	gap: 10px 22px;
	margin: 0;
	padding: 14px 0 0;
	border: 0;
	border-top: 1px solid var(--line);
}

.nb-cookie-cat {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	flex: 1 1 210px;
	font-size: 13px;
	cursor: pointer;
}

/* Essential is not a choice, and should not invite a click that does nothing. */
.nb-cookie-cat.is-fixed {
	cursor: default;
}

.nb-cookie-cat input {
	/* Nudged to sit on the label's first line rather than its box. */
	margin: 2px 0 0;
	width: 16px;
	height: 16px;
	accent-color: var(--nb);
	flex-shrink: 0;
}

.nb-cookie-cat-text {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.nb-cookie-cat-text strong {
	font-size: 13px;
	font-weight: 700;
}

.nb-cookie-cat-text span {
	color: var(--mute);
	font-size: 12px;
	line-height: 1.4;
}

.nb-cookie-actions {
	display: flex;
	gap: 10px;
	flex-shrink: 0;
	margin-left: auto;
}

/*
 * Every button keeps its full size on small screens. Shrinking "Reject" to a
 * text link is the dark pattern this banner is meant to avoid.
 */
@media (max-width: 620px) {
	.nb-cookie-actions {
		width: 100%;
		margin-left: 0;
		flex-wrap: wrap;
	}

	/*
	 * Stacked at equal width rather than wrapped. Three buttons on two rows left
	 * "Accept all" spanning the full width beneath a half-width "Reject optional",
	 * which is the size asymmetry this banner exists to avoid — the parity has to
	 * survive the breakpoint, not just hold on desktop.
	 */
	.nb-cookie-actions .cbtn {
		flex: 1 1 100%;
		justify-content: center;
	}

	/* One category per line, so the descriptions stay readable. */
	.nb-cookie-cat {
		flex: 1 1 100%;
	}
}

/*
 * The fixed banner would otherwise cover the last rows of the footer — including
 * the Privacy Policy and Terms links.
 *
 * main.js measures the banner and sets --nb-cookie-space, because a fixed number
 * cannot be right: the category list reflows, so the banner is 240px at desktop
 * width, 306px at 900px, and over 600px on a phone. These fallbacks cover the
 * tallest case in their range for the moment before that runs.
 */
body:has(.nb-cookie:not([hidden])) .site-foot {
	padding-bottom: var( --nb-cookie-space, 340px );
}

@media (max-width: 620px) {
	body:has(.nb-cookie:not([hidden])) .site-foot {
		padding-bottom: var( --nb-cookie-space, 640px );
	}
}

/*
 * ---------------------------------------------------------------------------
 * Placeholder links
 * ---------------------------------------------------------------------------
 *
 * The client asked for buttons that exist before their destinations do, so they
 * can be pointed at real URLs later "without requiring redevelopment". These
 * render as <span aria-disabled="true"> rather than links, so the layout is final
 * while nothing pretends to be clickable.
 *
 * Muted rather than hidden, and never mistakable for an active control: no hover,
 * no pointer, and out of the tab order by virtue of not being a link.
 */
.cbtn.is-placeholder,
.lp-badge.is-placeholder,
.lp-nav-signin.is-placeholder {
	opacity: .55;
	cursor: default;
	pointer-events: none;
}

/*
 * The same treatment for a footer menu item whose outbound URL is not set yet.
 * The flat walker renders those as an <a> with no href, so the design's own
 * .sf-col a / .sf-bar-links a typography already applies and only the muting is
 * needed here.
 */
.sf-col a.is-placeholder,
.sf-bar-links a.is-placeholder {
	opacity: .55;
	cursor: default;
	pointer-events: none;
}

/* The design's primary button is solid green; muted green on white fails contrast,
   so a placeholder drops to the neutral treatment instead of a faded fill. */
.cbtn.primary.is-placeholder,
.cbtn.white.is-placeholder {
	background: var(--line-2);
	border: 1px solid var(--line);
	color: var(--mute);
	box-shadow: none;
}

/*
 * Store badges are grouped per app, since the customer and driver apps are
 * separate listings and unlabelled badges side by side would be ambiguous.
 */
.nb-badge-group + .nb-badge-group {
	margin-top: 14px;
}

.nb-badge-label {
	display: block;
	margin-bottom: 8px;
	font-size: 11.5px;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, .5);
}

/*
 * ---------------------------------------------------------------------------
 * Anchor targets clear the sticky nav
 * ---------------------------------------------------------------------------
 *
 * `.lp-nav` is position:sticky at top:0, so following #about scrolled the section
 * to y=0 and the nav then covered its heading — the first line was cut in half.
 * The design prototype has the same bug; nothing in it sets scroll-padding.
 *
 * scroll-padding-top on the scroll container, rather than scroll-margin-top on each
 * target, so it applies to every anchor without anybody having to remember to add a
 * class: the seven section anchors, #top, and anything added later.
 *
 * This is the no-JavaScript fallback. main.js handles anchors itself, measuring the
 * nav live and settling the reveal animation first, because a fixed number cannot be
 * right at every width — the nav measured 65px at 375, 73px around 500–700 and 79px
 * from 768 up — and because `.reveal` paints a section 30px below its layout position
 * until it animates in, which made the same link land differently depending on the
 * order the visitor navigated in.
 *
 * 84px: clears the tallest nav by 5px. Deliberately not more — the gap between the
 * nav and the target shows whatever is above it, so an over-large value reveals a
 * strip of the previous section above the heading.
 */
:root {
	--nb-nav-clearance: 84px;
}

html {
	scroll-padding-top: var(--nb-nav-clearance);
}

/* ---------------------------------------------------------------------------
 * Branded empty state for post cards.
 *
 * The design's .food-ph falls back to one peach gradient. That is fine for a
 * single card and wrong for a row: the News section shows three cards, and with
 * no featured images all three rendered the identical block, which reads as
 * "the images failed to load" rather than as a deliberate empty state.
 *
 * So the gradient is tinted per category and the brand mark sits in the middle
 * at low opacity. Tints are built from the existing tokens rather than new hex
 * values, so they stay in step if the palette moves. A post WITH a featured
 * image is untouched — the photo covers the gradient completely.
 *
 * Stopgap: real photography replaces this. See PRODUCTION-DEPLOYMENT.md §5.
 * ------------------------------------------------------------------------- */
.lp-post .food-ph.nb-ph-company  { --bg-grad: linear-gradient(135deg, var(--nb-soft), #C9E6D5); }
.lp-post .food-ph.nb-ph-partners { --bg-grad: linear-gradient(135deg, var(--nb-warm), #FFE2C1); }
.lp-post .food-ph.nb-ph-riders   { --bg-grad: linear-gradient(135deg, #FFE2C1, #FFC79E); }

.lp-post .nb-ph-mark {
	display: grid;
	place-items: center;
	color: var(--nb-3);
	opacity: .22;
}

.lp-post .nb-ph-mark svg { width: 56px; height: 56px; }

/* An image still wins: hide the mark if both somehow render. */
.lp-post .food-ph img ~ .nb-ph-mark { display: none; }
