/*
Theme Name: Foodyx
Author: Foodyx
Description: Standalone block theme for the Foodyx marketing site. B2B restaurant technology — modern European software, not a food brand. All design values are defined once in theme.json.
Version: 0.26.2
Requires at least: 6.7
Tested up to: 7.1
Requires PHP: 8.2
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: foodyx
Tags: block-theme, full-site-editing, accessibility-ready, one-column
*/

/*
 * Global CSS only. Every value here is a token from theme.json — never a literal.
 * Mobile-first: the 360px case is the unconditional default, widened with
 * min-width only. No max-width queries.
 */

/* Gutter widens at tablet. Root padding is owned by theme.json; this only
   swaps which token feeds it. */
@media (min-width: 768px) {
	body {
		--wp--style--root--padding-left: var(--wp--custom--gutter--md);
		--wp--style--root--padding-right: var(--wp--custom--gutter--md);
	}
}

/*
 * Header and navigation.
 *
 * .fx-brand is the LOGO SLOT, currently rendering a text wordmark. The only
 * Foodyx lockups that exist are white-on-transparent, which measure about
 * 1.02:1 on this near-white header and cannot ship. A light-background logo in
 * SVG is an outstanding asset.
 *
 * The disclosure is a native <details>/<summary>: real disclosure semantics,
 * keyboard-operable, announced as expandable, and no JavaScript — which
 * docs/architecture.md requires and WordPress core's overlay menu does not give.
 *
 * From tablet up the panel must be permanently open. A closed <details> hides
 * its content, so that reveal is done with ::details-content. The @supports
 * guard is a safety catch, not decoration: if a browser cannot force the reveal,
 * the summary stays visible and the nav keeps working as a disclosure at every
 * width. Hiding the summary unconditionally would delete the navigation
 * outright on such a browser.
 */
.fx-bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--50);
}

/* The wordmark is the brand in the header; no standalone mark exists yet, so
   the mark slot is deliberately absent rather than faked from the wordmark. */
.fx-brand {
	display: inline-flex;
	align-items: center;
	text-decoration: none;
}

.fx-brand img {
	display: block;
	width: auto;
	height: 1.5rem;
}

.fx-brand--footer img {
	height: 1.25rem;
}

.fx-brand:focus-visible {
	outline: var(--wp--custom--focus--width) solid var(--wp--preset--color--accent);
	outline-offset: var(--wp--custom--focus--offset);
}

.fx-header {
	position: relative;
}

.fx-navwrap {
	flex: 0 0 auto;
}

/* The summary is the control. Its default marker is removed in every engine. */
.fx-nav-btn {
	display: inline-flex;
	align-items: center;
	gap: var(--wp--preset--spacing--30);
	min-height: 44px;
	padding: 0 var(--wp--preset--spacing--40);
	border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--connector);
	border-radius: var(--wp--custom--radius--md);
	font-size: var(--wp--preset--font-size--sm);
	list-style: none;
	cursor: pointer;
}

.fx-nav-btn::-webkit-details-marker {
	display: none;
}

.fx-nav-btn::marker {
	content: "";
}

.fx-nav-btn:focus-visible {
	outline: var(--wp--custom--focus--width) solid var(--wp--preset--color--accent);
	outline-offset: var(--wp--custom--focus--offset);
}

.fx-nav-btn__bars,
.fx-nav-btn__bars::before,
.fx-nav-btn__bars::after {
	display: block;
	width: 1rem;
	height: var(--wp--custom--border--hairline);
	background-color: currentColor;
}

.fx-nav-btn__bars {
	position: relative;
}

.fx-nav-btn__bars::before,
.fx-nav-btn__bars::after {
	content: "";
	position: absolute;
	left: 0;
}

.fx-nav-btn__bars::before { top: -5px; }
.fx-nav-btn__bars::after { top: 5px; }

/*
 * Closed by default on phones, so the first viewport belongs to content. When
 * open the panel drops the full width of the header rather than inheriting the
 * control's narrow column, and it sits above the page on its own ground.
 */
.fx-nav__list {
	position: absolute;
	top: 100%;
	left: 0;
	right: 0;
	z-index: 10;
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--20);
	margin: 0;
	padding: var(--wp--preset--spacing--50) 0 var(--wp--preset--spacing--60);
	border-bottom: var(--wp--custom--border--hairline) solid var(--wp--preset--color--rule);
	background-color: var(--wp--preset--color--field);
	list-style: none;
}

.fx-nav__list a {
	display: flex;
	align-items: center;
	min-height: 44px;
	color: var(--wp--preset--color--ink);
	font-size: var(--wp--preset--font-size--sm);
	text-decoration: none;
}

.fx-nav__list a:hover,
.fx-nav__list a:focus-visible {
	color: var(--wp--preset--color--accent-strong);
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

/* The demo request is the one action in the header, not a peer link. */
.fx-nav-cta a {
	justify-content: center;
	padding: 0 var(--wp--preset--spacing--50);
	border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--accent);
	border-radius: var(--wp--custom--radius--md);
	color: var(--wp--preset--color--accent);
	font-weight: 700;
}

.fx-nav-cta a:hover,
.fx-nav-cta a:focus-visible {
	background-color: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--field);
	text-decoration: none;
}

@media (min-width: 48rem) {
	.fx-nav__list {
		position: static;
		flex-direction: row;
		align-items: center;
		gap: var(--wp--preset--spacing--60);
		padding: 0;
		border-bottom: 0;
		background-color: transparent;
	}

	.fx-nav-cta a {
		min-height: 40px;
	}

	/* Force the panel open and retire the control — only where that is possible. */
	@supports selector(::details-content) {
		.fx-nav > .fx-nav-btn {
			display: none;
		}

		.fx-nav::details-content {
			content-visibility: visible;
			block-size: auto;
			overflow: visible;
		}
	}
}

/*
 * Site footer.
 *
 * FROZEN — approved 18 September 2026. See patterns/footer.php for what the
 * freeze covers and for the promotions it deliberately does not cover.
 *
 * The one dark surface on the site, and the page's ending. It is a full-bleed
 * band: .alignfull plus useRootPaddingAwareAlignments lets WordPress cancel the
 * root gutter itself, so no negative margin is hand-written here.
 *
 * Mobile-first: one column is the unconditional default, two at 48rem, the full
 * four at 64rem. Borders before shadows — there is no shadow and no gradient on
 * this surface, only hairlines.
 *
 * Contrast on #15181C: body text (ground) 16.1:1, muted 8.0:1, the light petrol
 * 7.1:1. The page's normal accent is #00635C and measures about 1.5:1 here,
 * which is why --wp--custom--on-dark--accent exists and is used for focus and
 * hover inside this band. A focus ring nobody can see is not a focus ring.
 */
.fx-footer {
	margin-block-start: var(--wp--preset--spacing--110);
	padding-block: var(--wp--preset--spacing--90) var(--wp--preset--spacing--60);
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--ground);
}

.fx-footer__grid {
	display: grid;
	gap: var(--wp--preset--spacing--80);
}

/* ---- Brand column ---- */

.fx-footer__wordmark {
	display: inline-flex;
	text-decoration: none;
}

.fx-footer__wordmark img {
	display: block;
	width: auto;
	height: 1.5rem;
}

.fx-footer__line {
	margin: var(--wp--preset--spacing--50) 0 0;
	max-width: 30ch;
	color: var(--wp--custom--on-dark--muted);
	font-size: var(--wp--preset--font-size--sm);
	line-height: 1.5;
}

/*
 * Two of the three marks are links; LinkedIn has no confirmed URL and stays a
 * static span rather than becoming a focusable dead end on href="#". The two
 * cases share every box property, so the tap target is 44px whichever element
 * renders, and supplying the LinkedIn URL swaps the element without touching
 * this CSS.
 *
 * The glyphs are solid silhouettes filled with currentColor — monochrome by
 * construction. No brand colour is introduced here, and none should be: three
 * brand colours in one row would be the loudest thing on the page.
 */
.fx-footer__social {
	display: flex;
	gap: var(--wp--preset--spacing--40);
	margin: var(--wp--preset--spacing--60) 0 0;
	padding: 0;
	list-style: none;
}

.fx-footer__social-item {
	display: grid;
	place-items: center;
	width: 44px;
	height: 44px;
	border: var(--wp--custom--border--hairline) solid var(--wp--custom--on-dark--rule);
	border-radius: var(--wp--custom--radius--md);
	color: var(--wp--preset--color--ground);
}

.fx-footer__social-item svg {
	display: block;
	width: 1.25rem;
	height: 1.25rem;
}

/*
 * The inactive mark is muted, the same signal the pending list items use. Live
 * and not-yet-live must not look identical, or the row quietly promises three
 * destinations and delivers two.
 */
.fx-footer__social-item--pending {
	color: var(--wp--custom--on-dark--muted);
	border-color: var(--wp--custom--on-dark--rule);
}

a.fx-footer__social-item:hover,
a.fx-footer__social-item:focus-visible {
	border-color: var(--wp--custom--on-dark--accent);
	color: var(--wp--custom--on-dark--accent);
}

/* ---- Link columns ---- */

/* Title case as authored. No text-transform: the heading is read, not stamped. */
.fx-footer__title {
	margin: 0 0 var(--wp--preset--spacing--50);
	color: var(--wp--preset--color--ground);
	font-size: var(--wp--preset--font-size--base);
	font-weight: 700;
	letter-spacing: -0.01em;
	line-height: 1.3;
}

.fx-footer__list {
	display: grid;
	gap: var(--wp--preset--spacing--30);
	margin: 0;
	padding: 0;
	list-style: none;
}

.fx-footer__list li {
	max-width: none;
}

/*
 * A link and a not-yet-a-link sit in the same list, so they must not look the
 * same. The link takes the footer's full text colour; a pending item stays
 * muted and carries no underline affordance at all.
 */
.fx-footer a {
	color: var(--wp--preset--color--ground);
	font-size: var(--wp--preset--font-size--sm);
	line-height: 1.4;
	text-decoration: none;
}

.fx-footer a:hover,
.fx-footer a:focus-visible {
	color: var(--wp--custom--on-dark--accent);
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.fx-footer :where(a, button, summary, [tabindex]):focus-visible {
	outline: var(--wp--custom--focus--width) solid var(--wp--custom--on-dark--accent);
	outline-offset: var(--wp--custom--focus--offset);
}

.fx-footer__pending,
.fx-footer__lang {
	color: var(--wp--custom--on-dark--muted);
	font-size: var(--wp--preset--font-size--sm);
	line-height: 1.4;
}

/* ---- Bottom bar ---- */

.fx-footer__bar {
	display: grid;
	gap: var(--wp--preset--spacing--50);
	margin-block-start: var(--wp--preset--spacing--90);
	padding-block-start: var(--wp--preset--spacing--60);
	border-block-start: var(--wp--custom--border--hairline) solid var(--wp--custom--on-dark--rule);
}

.fx-footer__copy {
	margin: 0;
	max-width: none;
	color: var(--wp--custom--on-dark--muted);
	font-size: var(--wp--preset--font-size--xs);
	line-height: 1.4;
}

.fx-footer__legal {
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--30) var(--wp--preset--spacing--60);
	margin: 0;
	padding: 0;
	list-style: none;
}

.fx-footer__legal li {
	max-width: none;
}

/*
 * The bottom bar runs one step smaller than the columns above it. A promoted
 * legal item is an <a>, which would otherwise take the footer's own link size
 * and sit a step larger than the pending items beside it — so the link is
 * named here too and the row keeps one type size whatever mix of links and
 * plain text it happens to hold.
 */
.fx-footer__legal a,
.fx-footer__legal .fx-footer__pending,
.fx-footer__lang {
	font-size: var(--wp--preset--font-size--xs);
}

/* Visually hidden, still read aloud. */
.fx-sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

@media (min-width: 48rem) {
	.fx-footer {
		padding-block: var(--wp--preset--spacing--100) var(--wp--preset--spacing--70);
	}

	.fx-footer__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--wp--preset--spacing--90) var(--wp--preset--spacing--80);
	}

	.fx-footer__bar {
		grid-template-columns: 1fr auto;
		align-items: center;
	}

	.fx-footer__legal {
		justify-content: flex-end;
	}
}

@media (min-width: 64rem) {
	/*
	 * Four tracks, unevenly weighted. The brand carries a wordmark, a line of
	 * copy and the social row; Producten carries nine items against three and
	 * four. Equal tracks would leave the right half of the band ragged, with a
	 * column of products running 150px below its neighbours.
	 */
	.fx-footer__grid {
		grid-template-columns: 1.5fr 1.8fr 1fr 1fr;
		gap: var(--wp--preset--spacing--80);
	}

	/*
	 * Products run two-up, in row order, so the list is five rows instead of
	 * nine and the four columns end at roughly the same depth. Grid rather than
	 * CSS columns: column flow would drop "Alle producten" — the one real link
	 * in the list — to the foot of the second sub-column.
	 */
	.fx-footer__list--split {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: var(--wp--preset--spacing--50);
	}
}

/*
 * WhatsApp.
 *
 * A contact channel, not a chat product. No widget, no card, no popup, no
 * pulse: every entry point is a plain link that opens WhatsApp.
 *
 * The treatment is Foodyx petrol, not WhatsApp green. Green is a second brand
 * shouting on a page that already has one, and the glyph carries all the
 * recognition the button needs.
 */
.fx-wa {
	display: inline-flex;
	align-items: center;
	gap: var(--wp--preset--spacing--40);
	text-decoration: none;
}

.fx-wa__glyph {
	display: inline-flex;
	flex: 0 0 auto;
}

.fx-wa__glyph svg {
	display: block;
	width: 1.25rem;
	height: 1.25rem;
}

/* ---- Floating control stack -------------------------------------------- */

/*
 * Back to top above WhatsApp, one fixed column in the bottom-right corner.
 * They are one control stack: one position, one gap, one tuck. Both are 48px
 * so the column reads as a unit rather than two unrelated dots.
 *
 * env(safe-area-inset-*) keeps the stack clear of the home indicator and the
 * rounded corners on a phone; the fallback keeps the same gap everywhere else.
 */
.fx-float {
	position: fixed;
	z-index: 40;
	right: calc(var(--wp--custom--gutter--sm) + env(safe-area-inset-right, 0px));
	bottom: calc(var(--wp--custom--gutter--sm) + env(safe-area-inset-bottom, 0px));
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--wp--preset--spacing--40);
	transition:
		transform var(--wp--custom--motion--base) var(--wp--custom--motion--ease),
		translate var(--wp--custom--motion--base) var(--wp--custom--motion--ease),
		opacity var(--wp--custom--motion--base) var(--wp--custom--motion--ease);
}

/*
 * Stepped aside while a reserved region is on screen — the footer's legal bar
 * and the newsletter form. Not hidden: the controls stay in the tab order and
 * the stack returns the moment focus enters it.
 */
/*
 * Tuck. The controller writes an inline transform for a lift, so the tuck uses
 * its own property rather than transform — otherwise the two would overwrite
 * each other and whichever ran last would win.
 */
.fx-float.is-tucked {
	opacity: 0;
	translate: 0 150%;
}

.fx-float:focus-within {
	opacity: 1;
	translate: none;
}

.fx-float__btn {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* Both controls carry a border — WhatsApp's is transparent — so the box must
	   include it, or the filled one renders 2px larger and the shared right edge
	   of the stack stops being shared. */
	box-sizing: border-box;
	width: 48px;
	height: 48px;
	padding: 0;
	border-radius: 50%;
	cursor: pointer;
	transition:
		opacity var(--wp--custom--motion--base) var(--wp--custom--motion--ease),
		transform var(--wp--custom--motion--base) var(--wp--custom--motion--ease),
		background-color var(--wp--custom--motion--fast) var(--wp--custom--motion--ease),
		border-color var(--wp--custom--motion--fast) var(--wp--custom--motion--ease),
		color var(--wp--custom--motion--fast) var(--wp--custom--motion--ease);
}

.fx-float__btn svg {
	display: block;
	width: 1.5rem;
	height: 1.5rem;
}

/* WhatsApp: the filled one, and the only filled one. */
.fx-wa--float {
	border: var(--wp--custom--border--hairline) solid transparent;
	background-color: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--field);
}

.fx-wa--float:hover,
.fx-wa--float:focus-visible {
	background-color: var(--wp--preset--color--accent-strong);
	color: var(--wp--preset--color--field);
}

/*
 * Back to top: secondary by construction. A bordered white disc against a
 * filled petrol one — borders before shadows, and no second filled control
 * competing with the channel that actually starts a conversation.
 *
 * Hidden until the page has been scrolled. visibility rather than the hidden
 * attribute, because `hidden` cannot fade and visibility:hidden still removes
 * it from the tab order.
 */
.fx-float__top {
	border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--rule);
	background-color: var(--wp--preset--color--field);
	color: var(--wp--preset--color--ink);
	visibility: hidden;
	opacity: 0;
	transform: translateY(var(--wp--preset--spacing--30));
}

.fx-float__top.is-shown {
	visibility: visible;
	opacity: 1;
	transform: none;
}

.fx-float__top:hover,
.fx-float__top:focus-visible {
	border-color: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--accent);
}

/*
 * One tooltip mechanism for the stack, fed by data-fx-tip so the visible label
 * and the accessible name come from the same string in PHP. A label, not a
 * widget: no role, no JS, and it never takes a pointer event.
 */
.fx-float__btn[data-fx-tip]::after {
	content: attr(data-fx-tip);
	position: absolute;
	top: 50%;
	right: calc(100% + var(--wp--preset--spacing--40));
	padding: var(--wp--preset--spacing--20) var(--wp--preset--spacing--40);
	border-radius: var(--wp--custom--radius--md);
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--ground);
	font-size: var(--wp--preset--font-size--xs);
	line-height: 1.4;
	white-space: nowrap;
	opacity: 0;
	transform: translateY(-50%);
	pointer-events: none;
	transition: opacity var(--wp--custom--motion--fast) var(--wp--custom--motion--ease);
}

/* Pointers only. On a phone there is no hover and the label would never leave. */
@media (hover: hover) and (min-width: 48rem) {
	.fx-float__btn[data-fx-tip]:hover::after,
	.fx-float__btn[data-fx-tip]:focus-visible::after {
		opacity: 1;
	}
}

/* ---- Inline WhatsApp actions ------------------------------------------- */

/*
 * The tertiary action in the closing band and the button on the contact page.
 * Text and glyph, no fill: one filled button per view, and it belongs to the
 * demo. This must read as the third option, because it is.
 */
.fx-wa--inline {
	min-height: 44px;
	color: var(--wp--preset--color--accent);
	font-size: var(--wp--preset--font-size--sm);
	font-weight: 700;
}

.fx-wa--inline:hover,
.fx-wa--inline:focus-visible {
	color: var(--wp--preset--color--accent-strong);
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

/* The contact page states the channel, so there it takes a quiet outline. */
.fx-wa--outline {
	justify-content: center;
	padding: 0 var(--wp--preset--spacing--60);
	border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--accent);
	border-radius: var(--wp--custom--radius--md);
}

.fx-wa--outline:hover,
.fx-wa--outline:focus-visible {
	border-color: var(--wp--preset--color--accent-strong);
	text-decoration: none;
}

/*
 * Browser surfaces. These ship with defaults belonging to no design system,
 * so they are themed from the palette: selection, caret, scrollbar, focus.
 */

::selection {
	background-color: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--field);
}

:where(input, textarea, [contenteditable]) {
	caret-color: var(--wp--preset--color--accent);
}

html {
	scrollbar-color: var(--wp--preset--color--connector) var(--wp--preset--color--ground);
}

/* Visible focus on every interactive element, on any background it can land on.
   Never removed without an equivalent replacement. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: var(--wp--custom--focus--width) solid var(--wp--preset--color--accent);
	outline-offset: var(--wp--custom--focus--offset);
}

/* Figures align in columns: numerals are tabular wherever they are read as data. */
:where(table) {
	font-variant-numeric: tabular-nums;
}

.is-style-tabular {
	font-variant-numeric: tabular-nums slashed-zero;
}

/* Prose measure. contentSize carries most of this; the cap is the backstop. */
:where(p, li) {
	max-width: var(--wp--custom--measure);
}

/* Motion is functional only. Transforms and parallax drop; opacity survives. */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}
