/*
 * pm-mega — the Products mega menu panel (WO-18).
 *
 * Source frame: docs/design/frames/18-mega-menu.json (node 55:862) — the
 * panel under the header band: inset 96 from both edges (not full bleed —
 * contradictions[0]), 8px below the bar, padding 48 / 80, gap 24, four
 * equal cards gap 8, each 266 × 478 at 1440 (measured.panel,
 * measured.panel.cards.card_box).
 *
 * Compiled to assets/css/components/mega-menu.css by src/build-tokens.mjs,
 * which substitutes the breakpoint markers (a comment holding `@bp <name>`)
 * with the pixel value from tokens/tokens.json. Never type a breakpoint
 * width here.
 *
 * Every colour is a --pm-color-* token; spacing, motion and font values
 * come from tokens/tokens.json. No hex value and no literal breakpoint
 * width may appear in this file. The two values the token file does not
 * hold (the blurb's 16px body size, the eyebrow rule's 40 × 1) are typed
 * with a comment citing the extract — the same technique as the header's
 * 14px and the gallery card's 18px.
 *
 * The panel anchors to the header: .pm-header { position: relative } below
 * (WO-17's later position: fixed keeps the anchor). The card reuses the
 * .pm-gallery-card classes for image and caption so WO-12's CSS styles it —
 * mega-menu.php enqueues poortman-gallery-card alongside this file.
 *
 * Below the laptop breakpoint the panel is never rendered open (WO-18 AC 3):
 * every panel rule is scoped to laptop and above, the markup's hidden
 * attribute keeps it display: none, and the behaviour bails on matchMedia —
 * the mobile navigation (WO-19) owns products below this width.
 */

/*
 * The hidden attribute is the closed state. The UA stylesheet already
 * hides it; this explicit guard keeps the panel closed even under a
 * stylesheet that sets display on the panel's classes.
 */
.pm-mega[hidden] {
	display: none;
}

/*
 * The panel's positioning ancestor is the header band (WO-18 decision).
 * The panel renders inside the Products item (navigation.php, test round 1:
 * DOM-adjacent to the trigger for the keyboard's tab order), but no element
 * between the panel and the header is positioned, so the header is the
 * nearest positioned ancestor and top: calc(100% + 8px) is 8px below the
 * bar at every header variant and width. The overlay variant's own
 * position: absolute (header.css, WO-38) wins on the contact page by
 * specificity; the anchor is the same element either way.
 */
.pm-header {
	position: relative;
}

/*
 * The trigger (WO-18 AC 1, re-shaped by WO-83): the Products item IS this
 * button now — the walker writes chevron then label inside it, and no link
 * beside it (the user's ruling of 2026-09-21: with no products overview
 * page, the item navigates nowhere). The button is the item's flex row, so
 * WO-82's `li:has(> .pm-header__mega-toggle) { gap }` moved onto it and the
 * old `width: 16px` / `order: -1` (a chevron-only button ordered before the
 * link) went with them.
 *
 * Box model (WO-82 AC 1, unchanged): the frame's 32px row = the 16px line +
 * 2×8 block padding, centred in the 80px bar. The 4px chevron-to-label gap
 * is the language switcher's own (16-header.json#measured.right_cluster
 * .language) — no mega measurement exists for it. `font: inherit` — a
 * <button> does not inherit font from .pm-header as the links do (the
 * mobile toggle's precedent, mobile-menu.css); it comes BEFORE the
 * line-height so the shorthand's own line-height loses to the row's 16px.
 * The colour transition is declared here on the button alone — the label
 * inherits colour and the chevron is currentColor, so both follow the
 * button's animated value as one unit; the label declares no transition of
 * its own (header.css's shared TYPE rule).
 */
.pm-header__mega-toggle {
	display: inline-flex;
	align-items: center;
	gap: var(--pm-space-1);
	padding: var(--pm-space-2) 0;
	font: inherit;
	line-height: 16px;
	border: 0;
	background: none;
	color: inherit;
	cursor: pointer;
	transition: color var(--pm-motion-duration-fast) var(--pm-motion-ease-standard);
}

.pm-header__mega-toggle svg {
	display: block;
	width: 16px;
	height: 16px;
	/* The chevron rotation is the open state's (header.css, WO-18 AC 3);
	   it animates over the fast duration like every header transition. */
	transition: transform var(--pm-motion-duration-fast) var(--pm-motion-ease-standard);
}

/* WO-09's focus ring convention (2px accent, 2px offset) — the same ring
   header.css gives every other focusable header element. */
.pm-header__mega-toggle:focus-visible {
	outline: 2px solid var(--pm-color-accent);
	outline-offset: 2px;
}

/*
 * WO-83 — the card link's own no-underline. The old descendant rule
 * `.pm-header__menu-list a` (header.css) carried the only text-decoration:
 * none the panel's card links had; re-scoping that rule to the bar's direct
 * children (83-products-index.json#defect) returned the cards to the user
 * agent's underline. Nothing else in gallery-card.css or here removes it —
 * the card's title and blurb would render underlined.
 */
.pm-mega__card {
	text-decoration: none;
}

/*
 * The panel and its content exist only at laptop and above (WO-18 AC 3).
 * Below it the markup's hidden attribute keeps display: none — the guard
 * rule above — and nothing here can ever show it.
 */
@media (min-width: 1024px) {
	.pm-mega {
		position: absolute;
		/* 8px below the bar, plus the header's own 1px border-bottom
		   (header.css): the border sits between the padding box — the
		   containing block — and the bar's visible bottom edge, so it is
		   absorbed into the offset to keep the visible gap at the frame's
		   8px (test round 1). The border is transparent, not absent, on the
		   negative variant, so the geometry is the same either way. */
		top: calc(100% + var(--pm-space-2) + 1px);
		left: var(--pm-space-24);
		right: var(--pm-space-24);
		z-index: 10; /* above the page content, inside the header's context */
		box-sizing: border-box;
		/* 48 block / 80 sides (18-mega-menu.json#measured.panel.padding). */
		padding: var(--pm-space-12) var(--pm-space-20);
		/* Blush Linen on both header variants (WO-18 AC 5) — the panel is
		   the light surface even when the bar is the negative ink. */
		background-color: var(--pm-color-surface);
		color: var(--pm-color-ink);
		/* Motion (WO-18 AC 4): opacity + 4px translate over the fast
		   duration; reduced motion neutralises it below. */
		opacity: 0;
		transform: translateY(calc(-1 * var(--pm-space-1)));
		transition:
			opacity var(--pm-motion-duration-fast) var(--pm-motion-ease-standard),
			transform var(--pm-motion-duration-fast) var(--pm-motion-ease-standard);
	}

	.pm-mega.pm-mega--open {
		opacity: 1;
		transform: none;
	}

	/*
	 * The eyebrow: the section-header pattern — accent rule + Caption Meta
	 * text, 16px apart (28-intro.json#measured.eyebrow), left-aligned
	 * (18-mega-menu.json#measured.panel.eyebrow). Styled here, not reused
	 * from section-header.css, because that component stylesheet loads only
	 * through its widget (WO-79) and the header renders on every page.
	 */
	.pm-mega__eyebrow {
		display: inline-flex;
		align-items: center;
		gap: var(--pm-space-4);
		/* The panel's gap: 24 between the eyebrow row and the cards
		   (18-mega-menu.json#measured.panel.gap). */
		margin-bottom: var(--pm-space-6);
	}

	.pm-mega__eyebrow-rule {
		flex: none;
		width: 40px;
		height: 1px;
		background-color: var(--pm-color-accent);
	}

	.pm-mega__eyebrow-text {
		font-family: var(--pm-font-family-base);
		font-size: var(--pm-font-size-caption);
		font-weight: var(--pm-font-weight-400);
		line-height: var(--pm-leading-normal);
		letter-spacing: var(--pm-tracking-caption);
		text-transform: uppercase;
		color: var(--pm-color-accent);
	}

	/*
	 * The cards: a row, gap 8, four equal columns — 266 wide each at 1440
	 * (panel 1248 minus the 80 side padding twice = 1088; four cards with
	 * three 8px gaps). Fewer cards stretch to the same columns, no gaps
	 * left over (WO-18 AC 2).
	 */
	.pm-mega__cards {
		display: flex;
		gap: var(--pm-space-2);
		margin: 0;
		padding: 0;
		list-style: none;
	}

	.pm-mega__cards > li {
		flex: 1 1 0;
		min-width: 0;
	}

	/*
	 * Ticket 49/50 — the card's own scale transition, distinct from every
	 * other gallery card's slow duration (gallery-card.css) because the
	 * two extracts propose different timings for the two card kinds
	 * (49-card-hover.json#proposal.duration vs 50-image-hover.json
	 * #proposal.duration). The scale itself lives only in the gated hover
	 * rule below; this just describes how the property moves.
	 */
	.pm-mega__card .pm-gallery-card__media {
		transition: transform var(--pm-motion-duration-base) var(--pm-motion-ease-standard);
	}

	/*
	 * The card's photo scrim (WO-18): Texte at 32% over the whole photo,
	 * under the caption gradient. Paint order is DOM order: image, scrim,
	 * caption.
	 *
	 * Ticket 50: the scrim's colour never transitions (ticket 54 AC 2), so
	 * the crossfade to hover happens entirely through opacity, on a fixed
	 * scrim-card-hover background (Texte @60, 49-card-hover.json
	 * #hover.scrim). --pm-scrim-card-rest is the one typed derived value
	 * this ticket needs: 32/60 ≈ 0.533, so the rest composite still reads
	 * @32 — the same value WO-18 shipped (0.6 × 0.533 ≈ 0.32) — and only
	 * the hover state (opacity: 1, below) actually deepens it.
	 */
	.pm-mega__scrim {
		position: absolute;
		inset: 0;
		background-color: var(--pm-color-scrim-card-hover);
		--pm-scrim-card-rest: 0.533;
		opacity: var(--pm-scrim-card-rest);
		transition: opacity var(--pm-motion-duration-base) var(--pm-motion-ease-standard);
	}

	/*
	 * The blurb: Body 16 / 1.6 on-ink-muted — Ivory @60, the footer's
	 * muted on-ink text (18-mega-menu.json#measured.panel.cards.card).
	 * Body is Roboto Light 300 (28-intro.json#type_styles_evidenced). No
	 * --pm-font-size token holds 16px, so it is typed here citing the
	 * extract, like the header's 14px and the card title's 18px.
	 */
	.pm-mega__blurb {
		margin: 0;
		font-family: var(--pm-font-family-base);
		font-size: 16px;
		font-weight: var(--pm-font-weight-300);
		line-height: var(--pm-leading-body);
		color: var(--pm-color-on-ink-muted);
		/* A long Dutch blurb wraps rather than stretching the card
		   (MISTAKES.md §E4). */
		overflow-wrap: break-word;
	}

	/*
	 * Ticket 49/50 — the arrow row's box: 40 × 40, 1px border, radius pill,
	 * colour on-accent for both the border and the currentColor glyph
	 * (there is no on-ink role — the established white-on-photo role,
	 * gallery-card.css's card-title colour). Sizes the token file does not
	 * hold are typed here citing the extract, the same technique as the
	 * eyebrow rule above and the blurb's 16px. Unconditional box styling:
	 * the row is always in the DOM (49-card-hover.json#contradictions[0]),
	 * one <a> away from every mega card, but it starts invisible — only
	 * the gated hover rule below ever raises its opacity.
	 */
	.pm-mega__more {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: 40px;
		height: 40px;
		flex: none;
		opacity: 0;
		border: 1px solid var(--pm-color-on-accent);
		border-radius: var(--pm-radius-pill);
		color: var(--pm-color-on-accent);
	}

	.pm-mega__more svg {
		display: block;
		width: 16px;
		height: 16px;
	}

	/*
	 * The no-photo protection (belt on top of the hover gate below): a
	 * family card with no featured image keeps its flat surface-alt block
	 * untouched, so the row is removed from layout entirely rather than
	 * merely staying invisible — otherwise its 40px + the caption's own
	 * flex gap would still grow the caption's box even though it never
	 * lifts. mega-menu.php:107 writes .pm-mega__scrim unconditionally,
	 * which is why this checks the media, not the scrim.
	 */
	.pm-mega__card:not(:has(.pm-gallery-card__media)) .pm-mega__more {
		display: none;
	}

	/*
	 * Ticket 49/50 — the card's hover state. Photo scale to card-hover
	 * (1.15), scrim deepens (opacity to 1 on the fixed scrim-card-hover
	 * background above), caption lifts by the row's own height plus the
	 * caption's flex gap, arrow row fades in. Every rule here is keyed on
	 * a card that HAS media (:has(.pm-gallery-card__media)): a no-photo
	 * family card has no image to scale and, per the rule above, no row to
	 * reveal, so it gets no scrim deepening and no caption lift either —
	 * its flat surface-alt block stays exactly as WO-18/83 shipped it.
	 * `@media (hover: hover)` keeps all of it off any device that cannot
	 * hover (ticket 50 AC 3), so a tap can never leave a state stuck "on";
	 * under `(hover: none)` the caption is simply never translated, so it
	 * sits exactly where it does today — the 48px it would otherwise gain
	 * from the in-flow row is gone with the row (excluded above).
	 * `:focus-within` gives keyboard users the same state.
	 */
	@media (hover: hover) {
		.pm-mega .pm-mega__card:has(.pm-gallery-card__media) .pm-gallery-card__caption {
			/* 40 (the row) + 8 (the caption's own flex gap above it) — both
			   tokens, never the literal 48
			   (49-card-hover.json#hover.caption). */
			transform: translateY(calc(var(--pm-space-10) + var(--pm-space-2)));
			transition: transform var(--pm-motion-duration-base) var(--pm-motion-ease-standard);
		}

		.pm-mega .pm-mega__card:has(.pm-gallery-card__media) .pm-mega__more {
			transition: opacity var(--pm-motion-duration-base) var(--pm-motion-ease-standard);
		}

		.pm-mega .pm-mega__card:has(.pm-gallery-card__media):hover .pm-gallery-card__media,
		.pm-mega .pm-mega__card:has(.pm-gallery-card__media):focus-within .pm-gallery-card__media {
			transform: scale(var(--pm-motion-scale-card-hover));
		}

		.pm-mega .pm-mega__card:has(.pm-gallery-card__media):hover .pm-mega__scrim,
		.pm-mega .pm-mega__card:has(.pm-gallery-card__media):focus-within .pm-mega__scrim {
			opacity: 1;
		}

		.pm-mega .pm-mega__card:has(.pm-gallery-card__media):hover .pm-gallery-card__caption,
		.pm-mega .pm-mega__card:has(.pm-gallery-card__media):focus-within .pm-gallery-card__caption {
			transform: translateY(0);
		}

		.pm-mega .pm-mega__card:has(.pm-gallery-card__media):hover .pm-mega__more,
		.pm-mega .pm-mega__card:has(.pm-gallery-card__media):focus-within .pm-mega__more {
			opacity: 1;
		}
	}

	/*
	 * Touch (ticket 50 AC 3): the row is removed from layout, not merely
	 * left invisible — opacity: 0 alone would still reserve its 40px plus
	 * the caption's flex gap, growing the caption's box on a device that
	 * can never trigger the hover rule above to compensate with the lift.
	 * display: none drops both, so the caption sits exactly where it does
	 * today (aria-hidden decoration either way — nothing lost).
	 */
	@media (hover: none) {
		.pm-mega__more {
			display: none;
		}
	}
}

/* Reduced motion neutralises the panel's transition, the button's colour
   transition and the chevron's rotation transition (WO-18 AC 4; WO-83;
   MEMORY.md §3.5). The button's neutralisation lives HERE, not in
   header.css's block — this file prints after header.css, so at equal
   specificity this block wins over the base rule above.
   Ticket 50 AC 4: the same block also drops the card's scale, the caption's
   lift and the arrow row entirely — "no movement", not "no animated
   movement" (ticket 54 AC 1) — placed after the `@media (hover: hover)`
   block above so it wins the (0,5,0)/(0,3,0) ties on the identical
   selectors by source order; only the scrim's colour cue is left to snap
   instead of fade. */
@media (prefers-reduced-motion: reduce) {
	.pm-mega,
	.pm-header__mega-toggle,
	.pm-header__mega-toggle svg,
	.pm-mega__scrim,
	.pm-mega__card .pm-gallery-card__media,
	.pm-mega .pm-mega__card:has(.pm-gallery-card__media) .pm-gallery-card__caption {
		transition: none;
	}

	.pm-mega__more {
		display: none;
	}

	.pm-mega .pm-mega__card:has(.pm-gallery-card__media) .pm-gallery-card__caption,
	.pm-mega .pm-mega__card:has(.pm-gallery-card__media):hover .pm-gallery-card__caption,
	.pm-mega .pm-mega__card:has(.pm-gallery-card__media):focus-within .pm-gallery-card__caption {
		transform: none;
	}

	.pm-mega .pm-mega__card:has(.pm-gallery-card__media):hover .pm-gallery-card__media,
	.pm-mega .pm-mega__card:has(.pm-gallery-card__media):focus-within .pm-gallery-card__media {
		transform: none;
	}
}
