/*
Theme Name: Nikhef Lite
Theme URI: https://www.nikhef.nl
Author: Nikhef
Description: A lightweight WordPress theme in the style of www.nikhef.nl, for Nikhef satellite sites, group pages and personal sites. On sites that display job openings it shows them as cards. No frameworks, no build tooling, one tiny script.
Requires at least: 6.4
Tested up to: 7.1.3
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: nikhef-lite
Tags: one-column, custom-menu, featured-images
*/

/* --------------------------------------------------------------------------
 * Fonts (same variable webfonts as the main nikhef.nl site)
 * ------------------------------------------------------------------------ */

/* Roboto - latin-ext */
@font-face {
	font-family: "Roboto";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("assets/fonts/Roboto-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Roboto - latin */
@font-face {
	font-family: "Roboto";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("assets/fonts/Roboto-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Roboto Mono - latin-ext */
@font-face {
	font-family: "Roboto Mono";
	font-style: normal;
	font-weight: 100 700;
	font-display: swap;
	src: url("assets/fonts/RobotoMono-Variable-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Roboto Mono - latin */
@font-face {
	font-family: "Roboto Mono";
	font-style: normal;
	font-weight: 100 700;
	font-display: swap;
	src: url("assets/fonts/RobotoMono-Variable-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------------------
 * Design tokens (subset of the main site's token set)
 * ------------------------------------------------------------------------ */

:root {
	/* Color palette. */
	--colors-brand-1: #00b3c4; /* cyan   - focus outlines */
	--colors-brand-2: #6e1077; /* purple - hover accents  */
	--colors-brand-3: #e6223d; /* red    - logo, tags      */
	--colors-brand-4: #e3d88b; /* sand   - footer titles   */
	--colors-brand-5: #00223d; /* navy                     */
	--colors-brand-7: #272733; /* dark   - footer, buttons */
	--colors-neutral-10: #f2f2f2;
	--colors-neutral-20: #d9d9d9;
	--colors-neutral-30: #828282;
	--colors-neutral-40: #525252;
	--colors-neutral-black: #1e1e1e;
	--colors-neutral-white: #ffffff;

	/* Spacing scale. */
	--spacing-1: 0.25rem;
	--spacing-2: 0.5rem;
	--spacing-3: 0.75rem;
	--spacing-4: 1rem;
	--spacing-5: 1.5rem;
	--spacing-6: 2rem;
	--spacing-7: 3rem;
	--spacing-8: 4rem;
	--spacing-9: 5rem;
	--spacing-10: 6rem;

	/* Layout. */
	--header-height: 5rem;
	--max-container-width: 90rem;
	/* The official small container: the news pages cap the leader content
	 * at this width (minus its side padding), measured on nikhef.nl. */
	--max-small-container-width: 70rem;
	--max-content-width: 52.5rem;
	--border-radius-1: 0.5rem;
	--border-radius-2: 0.67rem;
	--container-padding: 3rem;

	/* Typography. */
	--font-family-default: "Roboto", sans-serif;
	--font-family-headings: "Roboto Mono", monospace;
	--typscale-body-lg: 1.20rem;
	--typscale-heading-3: 1.375rem;
	--body-text-color: var(--colors-neutral-black);
	--text-muted: var(--colors-neutral-40);
	--text-underline-offset: 0.3125rem;

	/* Motion (used sparingly: buttons and link underlines). */
	--cubic-bezier: cubic-bezier(0.6, 0.6, 0, 1);
	--animation-speed-slow: 600ms;
	--animation-speed-fast: 400ms;

	/* Arrow icon (matches the main site), usable as a CSS mask. */
	--arrow-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M12.0788 7.5H0V6.5H12.0788L6.28653 0.7077L7 0L14 7L7 14L6.28653 13.2923L12.0788 7.5Z'/%3E%3C/svg%3E");
	/* Diagonal arrow for links to other sites (the main site's external icon). */
	--external-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M15.9449 8.76226L7.40389 17.3033L6.69678 16.5962L15.2378 8.05516L7.04625 8.05517L7.05033 7.05025H16.9498V16.9497L15.9449 16.9538L15.9449 8.76226Z'/%3E%3C/svg%3E");
	/* Chevron for menu items with a fold-out (the main site's arrow-down). */
	--chevron-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M6 7.20081C5.88119 7.20081 5.76753 7.181 5.65901 7.1414C5.5497 7.1018 5.45545 7.03249 5.37624 6.93348L0.178218 1.76516C0.0594061 1.64635 0 1.51764 0 1.37903C0 1.24041 0.0693072 1.1117 0.207921 0.992887C0.326733 0.874075 0.455446 0.814668 0.594059 0.814668C0.732673 0.814668 0.861387 0.864173 0.980198 0.963183L6 6.01269L11.0198 0.963183C11.1386 0.844371 11.2673 0.790114 11.4059 0.800411C11.5446 0.809916 11.6832 0.874075 11.8218 0.992887C11.9406 1.1117 12 1.24041 12 1.37903C12 1.51764 11.9406 1.64635 11.8218 1.76516L6.62376 6.93348C6.54455 7.03249 6.45069 7.1018 6.34218 7.1414C6.23287 7.181 6.11881 7.20081 6 7.20081Z'/%3E%3C/svg%3E");
}

/* Responsive design tokens, like the official theme rescales its typscale
 * tokens at its breakpoints (medium 55rem, small 30rem): body copy drops a
 * step on tablets, card/list headings drop a step on phones. Components
 * read the tokens, so this is the only place the scale changes. */
@media (max-width: 55rem) {
	:root {
		--container-padding: 1rem;
		--typscale-body-lg: 1.125rem;
	}
}

@media (max-width: 30rem) {
	:root {
		--typscale-heading-3: 1.25rem;
	}
}

/* --------------------------------------------------------------------------
 * Base
 * ------------------------------------------------------------------------ */

*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	-webkit-text-size-adjust: 100%;
}

body {
	margin: 0;
	color: var(--body-text-color);
	background-color: var(--colors-neutral-white);
	font-family: var(--font-family-default);
	font-size: 1rem;
	line-height: 1.8;
	font-weight: 400;
	text-underline-offset: var(--text-underline-offset);
	-moz-osx-font-smoothing: grayscale;
	-webkit-font-smoothing: antialiased;
}

img,
svg,
video {
	max-width: 100%;
	height: auto;
}

h1,
h2,
h3,
h4,
h5,
h6 {
	font-family: var(--font-family-headings);
	font-weight: 400;
	line-height: 1.2;
	color: var(--body-text-color);
	margin: 0 0 var(--spacing-5);
}

h1 {
	font-size: clamp(1.75rem, 4vw, 2.25rem);
	line-height: 1;
}

h2 {
	font-size: clamp(1.5rem, 3.5vw, 2rem);
}

h3 {
	font-size: var(--typscale-heading-3);
}

h4 {
	font-size: 1.125rem;
	font-weight: 600;
}

p {
	margin: 0 0 var(--spacing-4);
}

/* Body copy reads a step larger than the UI chrome: the Large font
 * preset is the normal size for paragraphs in the content, like the
 * official body-lg (20px). Tag chips and the entry meta line pin their
 * own smaller size, so they stay exactly as they are. */
.site-main p:not(.is-style-tag):not(.entry-meta) {
	font-size: var(--typscale-body-lg);
	line-height: 2.0rem;
}

a {
	color: var(--body-text-color);
	text-decoration: underline;
	text-decoration-thickness: 1px;
}

a:hover {
	color: var(--colors-brand-2);
}

:focus-visible {
	outline: 2px solid var(--colors-brand-1);
	outline-offset: 2px;
}

.container {
	margin: 0 auto;
	padding: 0 var(--container-padding);
	max-width: var(--max-container-width);
	width: 100%;
}

.screen-reader-text,
.skip-link {
	border: 0;
	clip: rect(1px, 1px, 1px, 1px);
	clip-path: inset(50%);
	height: 1px;
	width: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	word-wrap: normal !important;
}

.skip-link:focus {
	clip: auto;
	clip-path: none;
	left: 0;
	top: 0;
	width: auto;
	height: auto;
	margin: 0;
	padding: var(--spacing-2) var(--spacing-4);
	background: var(--colors-neutral-white);
	color: var(--colors-neutral-black);
	z-index: 999;
	text-decoration: none;
}

/* Lists: square bullets like the main site. */
.entry-content ul,
.page-intro ul {
	list-style: none;
	padding: 0;
	margin: 0 0 var(--spacing-4);
}

.entry-content ul li,
.page-intro ul li {
	position: relative;
	padding-left: 1.5rem;
	margin-bottom: 0.5rem;
}

.entry-content ul li::before,
.page-intro ul li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.7em;
	width: 0.5rem;
	height: 0.5rem;
	background-color: currentColor;
}

.entry-content ol {
	padding-left: 1.5rem;
	margin: 0 0 var(--spacing-4);
}

/* --------------------------------------------------------------------------
 * Header
 *
 * Base state: the light header (white bar, black links, red logo) that
 * every page shows. Pages with a hero under the header (the "Hero (no
 * title)" template, body class has-hero) additionally get the transparent
 * state from nikhef.nl: a dark gradient that fades out toward the bottom,
 * white links and a white logo. The tiny script in footer.php adds the
 * nl-hero-header class and swaps in scrolled the moment the page moves,
 * snapping the header back to the light state. Without JavaScript (or on
 * pages without a hero) the header simply stays light, the safe fallback.
 * ------------------------------------------------------------------------ */

/* The header state colors are registered so they can transition between
 * the transparent and the light state; unregistered custom properties
 * snap instead of animating. Initial values mirror the light state. */
@property --icon-color {
	syntax: "<color>";
	inherits: true;
	initial-value: #e6223d;
}

@property --header-link-color {
	syntax: "<color>";
	inherits: true;
	initial-value: #1e1e1e;
}

@property --chip-bg {
	syntax: "<color>";
	inherits: true;
	initial-value: #e6223d;
}

@property --chip-color {
	syntax: "<color>";
	inherits: true;
	initial-value: #ffffff;
}

.site-header {
	position: sticky;
	top: 0;
	z-index: 100;
	background: var(--colors-neutral-white);
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
	--icon-color: var(--colors-brand-3);
	--header-link-color: var(--colors-neutral-black);
	/* The site chip follows the header state. */
	--chip-bg: var(--colors-brand-3);
	--chip-color: var(--colors-neutral-white);
	/* The header crossfades between its states: the colors travel with
	 * the registered properties above, the gradient via the overlay,
	 * the white base via background-color (white and transparent are
	 * both colors, so they interpolate). */
	transition:
		background-color var(--animation-speed-slow) var(--cubic-bezier),
		box-shadow var(--animation-speed-slow) var(--cubic-bezier),
		--icon-color var(--animation-speed-slow) var(--cubic-bezier),
		--header-link-color var(--animation-speed-slow) var(--cubic-bezier),
		--chip-bg var(--animation-speed-slow) var(--cubic-bezier),
		--chip-color var(--animation-speed-slow) var(--cubic-bezier);
}

/* The dark hero gradient lives on an overlay layer so it can crossfade
 * with the white base state (a background-image itself does not
 * interpolate). z-index -1 paints it over the white background but
 * under the header content. */
.has-hero .site-header.nl-hero-header::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background: linear-gradient(0deg, rgba(0, 0, 0, 0) 6.42%, rgba(20, 20, 28, 0.78) 87.61%);
	opacity: 0;
	transition: opacity var(--animation-speed-slow) var(--cubic-bezier);
}

.has-hero .site-header.nl-hero-header:not(.scrolled) {
	/* Transparent, not white-under-gradient: the hero image must show
	 * through wherever the gradient itself is see-through. */
	background: transparent;
	box-shadow: none;
	--icon-color: var(--colors-neutral-white);
	--header-link-color: var(--colors-neutral-white);
	/* White chip with black text over the hero; snaps to red on scroll. */
	--chip-bg: var(--colors-neutral-white);
	--chip-color: var(--colors-neutral-black);
}

.has-hero .site-header.nl-hero-header:not(.scrolled)::before {
	opacity: 1;
}

.header-bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--spacing-3) var(--spacing-6);
	block-size: calc(1.4 * var(--header-height));
	max-width: calc(var(--max-container-width) * 1.2);
}

.header__logo {
	flex-shrink: 0;
	display: inline-flex;
}

.header__logo svg {
	display: block;
  	height: 3.5rem;
  	width: auto;
}

/* Custom logo (optional, via the Customizer on satellite/personal sites). */
.custom-logo-link {
	display: inline-flex;
	flex-shrink: 0;
}

.custom-logo {
	height: 2.75rem;
	width: auto;
}

.header__chip {
	display: inline-block;
	background: var(--chip-bg, var(--colors-brand-3));
	color: var(--chip-color, var(--colors-neutral-white));
	font-family: var(--font-family-headings);
	font-size: 0.875rem;
	line-height: 1.4;
	padding: var(--spacing-1) var(--spacing-5) var(--spacing-1) var(--spacing-3);
	clip-path: polygon(0 0, calc(100% - 0.5rem) 0, 100% 100%, 0% 100%);
	text-decoration: none;
}

.header__chip:hover {
	/* The chip keeps its state colors; no purple on hover. */
	background: var(--chip-bg, var(--colors-brand-3));
	color: var(--chip-color, var(--colors-neutral-white));
}

.header__nav {
	margin-left: auto;
}

.header__nav .menu {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-2) var(--spacing-5);
	list-style: none;
	margin: 0;
	padding: 0;
}

.header__nav .menu a {
	display: flex;
	align-items: center;
	color: var(--header-link-color);
	padding: var(--spacing-1) 0;
	text-decoration: none;
	/* Animated underline: grows in from the right on hover, like nikhef.nl.
	 * Nothing at rest, not even for the current page. */
	background: linear-gradient(0deg, currentColor, currentColor) no-repeat right bottom / 0 1px;
	transition: background-size var(--animation-speed-slow) var(--cubic-bezier);
}

.header__nav .menu a:hover {
	background-size: 100% 1px;
	background-position-x: left;
	color: var(--header-link-color);
}

/*
 * Menu items with a fold-out: the item contains a hidden checkbox and a
 * label (with the chevron) rendered by the Nikhef_Lite_Walker. The submenu
 * opens on click - and on keyboard via the checkbox - with no JavaScript.
 */
.header__nav .menu-item-has-children {
	position: relative;
	display: flex;
	align-items: center;
	flex-wrap: wrap;
}

.header__nav .menu-item-has-children > a {
	margin: 0;
}

/*
 * The label overlays the entire parent item: clicking anywhere on it toggles
 * the submenu, like the menu buttons on nikhef.nl. The page itself stays
 * reachable via the submenu entries and via keyboard (the link is still
 * there underneath the label).
 */
.menu-toggle {
	position: absolute;
	opacity: 0;
	margin: 0;
	inline-size: 1px;
	block-size: 1px;
}

.menu-toggle-label {
	position: absolute;
	inset: 0;
	display: block;
	cursor: pointer;
}

/* The chevron sits NEXT to the text: the item reserves space for it. */
.header__nav .menu-item-has-toggle {
	padding-inline-end: calc(0.625rem + var(--spacing-3));
}

.menu-toggle-label .chevron {
	position: absolute;
	inset-inline-end: 0.25rem;
	top: 50%;
	translate: 0 -40%;
	inline-size: 0.8rem;
	block-size: 0.5rem;
	background-color: var(--header-link-color);
	mask: var(--chevron-icon) center / contain no-repeat;
	transition: rotate var(--animation-speed-slow) var(--cubic-bezier);
}

.menu-toggle:focus-visible ~ .menu-toggle-label {
	outline: 2px solid var(--colors-brand-1);
	outline-offset: 2px;
}

.menu-toggle:checked ~ .menu-toggle-label .chevron {
	rotate: 180deg;
}

/* The label swallows the hover, so show the underline on the link via :has(). */
.menu-item-has-children:has(.menu-toggle-label:hover) > a {
	background-size: 100% 1px;
	background-position-x: left;
}

/* The fold-out submenu, shown when its checkbox is checked. It sits on top
 * of the parent item's toggle label so its links stay clickable. */
.header__nav .sub-menu {
	z-index: 11;
	position: absolute;
	top: 100%;
	left: 0;
	z-index: 10;
	min-width: 14rem;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--spacing-2);
	margin: 0;
	padding: var(--spacing-4);
	list-style: none;
	background: var(--colors-neutral-white);
	box-shadow: 0 8px 16px -4px rgba(0, 0, 0, 0.15);
	opacity: 0;
	visibility: hidden;
	transition:
		opacity var(--animation-speed-slow) var(--cubic-bezier),
		visibility 0s linear var(--animation-speed-slow);
}

.menu-toggle:checked ~ .sub-menu {
	opacity: 1;
	visibility: visible;
	transition:
		opacity var(--animation-speed-slow) var(--cubic-bezier),
		visibility 0s;
}

/* The fold-out panel is white in every header state, so its links stay
 * black even on hover: the bar links' hover rule repaints them with
 * var(--header-link-color), which is white on the transparent front-page
 * header. Same specificity, later in the file, so this wins. */
.header__nav .sub-menu a,
.header__nav .sub-menu a:hover {
	color: var(--colors-neutral-black);
}

/* Menu position: right (default) or centered, via the Customizer. */
.menu-pos-center .header__nav {
	margin-left: auto;
	margin-right: auto;
}

.menu-pos-center .header-bar {
	justify-content: space-between;
}

/* The optional "Header (right)" widget area: pinned to the right edge
 * of the bar, next to the menu. The bar height is fixed (the hero
 * pull-up is tuned to it), so widgets lay out in a row, never stacked.
 * Widget titles render as small muted labels so a widget can carry one
 * without shouting. Links inherit the header's color state (black on
 * the white bar, white over the transparent hero) instead of the
 * content-link treatment. */
.header__widgets {
	display: flex;
	align-items: center;
	column-gap: var(--spacing-4);
	flex-shrink: 0;
}

.header-widget {
	display: flex;
	align-items: center;
	font-size: 0.875rem;
}

.header-widget a {
	color: inherit;
}

.header-widget__title {
	margin: 0 0 0.25em;
	font-family: var(--font-family-headings);
	font-size: 0.75rem;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--colors-neutral-30);
}

.header-widget__title + * {
	margin-block-start: 0;
}

/* The hamburger: hidden on wide screens, shown below 55rem. */
.header-menu-toggle-label {
	display: none;
}

@media (max-width: 55rem) {
	/* The bar keeps its exact desktop height: the hero pull-up and the
	 * negative margin on .site-main--hero are tuned to it, so the menu
	 * folds into the hamburger instead of stacking the bar taller. */
	.header-menu-toggle-label {
		display: flex;
		align-items: center;
		order: 3;
		margin-inline-start: auto;
		padding: var(--spacing-3);
		cursor: pointer;
	}

	.header-menu-toggle-lines {
		position: relative;
		display: block;
		inline-size: 1.5rem;
		block-size: 1rem;
	}

	.header-menu-toggle-lines::before,
	.header-menu-toggle-lines::after {
		content: "";
		position: absolute;
		inset-inline: 0;
		inset-block-start: 50%;
		block-size: 2px;
		background-color: var(--header-link-color);
		transition: transform var(--animation-speed-slow) var(--cubic-bezier);
	}

	.header-menu-toggle-lines::before {
		transform: translateY(-4px);
	}

	.header-menu-toggle-lines::after {
		transform: translateY(4px);
	}

	.header-menu-toggle:checked ~ .header-menu-toggle-label .header-menu-toggle-lines::before {
		transform: translateY(0) rotate(45deg);
	}

	.header-menu-toggle:checked ~ .header-menu-toggle-label .header-menu-toggle-lines::after {
		transform: translateY(0) rotate(-45deg);
	}

	.header-menu-toggle:focus-visible ~ .header-menu-toggle-label {
		outline: 2px solid var(--colors-brand-1);
		outline-offset: 2px;
	}

	/* The menu itself is gone until the hamburger is checked; it then
	 * fades in and drops out from under the whole header as a panel
	 * (the same opacity/visibility pattern as the submenu fold-outs).
	 * visibility waits out the fade when hiding, so the panel only
	 * leaves the tab order once it is actually invisible.
	 *
	 * The widget area hides with it: the folded bar cannot host it
	 * (fixed height), and the fold-out panel owns the full width. */
	.header__widgets {
		display: none;
	}

	.header__nav {
		position: absolute;
		inset-inline: 0;
		top: 100%;
		padding: var(--spacing-2) var(--container-padding) var(--spacing-4);
		background: var(--colors-neutral-white);
		box-shadow: 0 8px 16px -4px rgba(0, 0, 0, 0.15);
		opacity: 0;
		visibility: hidden;
		transition:
			opacity var(--animation-speed-slow) var(--cubic-bezier),
			visibility 0s linear var(--animation-speed-slow);
	}

	.header-menu-toggle:checked ~ .header__nav {
		opacity: 1;
		visibility: visible;
		transition:
			opacity var(--animation-speed-slow) var(--cubic-bezier),
			visibility 0s;
	}

	.header__nav .menu {
		flex-direction: column;
		gap: 0;
		width: 100%;
	}

	.header__nav .menu a {
		padding: var(--spacing-2) 0;
		border-bottom: 1px solid rgba(128, 128, 128, 0.25);
	}

	/* Submenus drop in below their item once the toggle is checked. */
	.header__nav .sub-menu {
		position: static;
		flex-basis: 100%;
		background: transparent;
		box-shadow: none;
		padding: 0 0 var(--spacing-2) var(--spacing-5);
		min-width: 0;
	}

	/* The transparent hero state does not fit the folded bar (the white
	 * hamburger and logo need the light state): snap to white below the
	 * breakpoint, like the scrolled state. Same selectors, later in the
	 * cascade, so they win. */
	.has-hero .site-header.nl-hero-header:not(.scrolled) {
		background: var(--colors-neutral-white);
		box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
		--icon-color: var(--colors-brand-3);
		--header-link-color: var(--colors-neutral-black);
		--chip-bg: var(--colors-brand-3);
		--chip-color: var(--colors-neutral-white);
	}

	.has-hero .site-header.nl-hero-header:not(.scrolled)::before {
		opacity: 0;
	}
}

/* --------------------------------------------------------------------------
 * Main content
 * ------------------------------------------------------------------------ */

.site {
	padding-block: var(--spacing-7) var(--spacing-10);
}

.site-main {
	min-height: 40vh;
}

.page-header {
	margin-bottom: var(--spacing-8);
	max-width: var(--max-content-width);
}

.page-intro {
	/* As wide as the job cards grid below, not the narrower text column. */
	margin-bottom: var(--spacing-8);
}

/* --------------------------------------------------------------------------
 * Hero pages: the "Hero (no title)" page template together with the Hero
 * and Hero slideshow patterns (see patterns/). The hero fills the width of
 * the page and nuzzles up against the header, which floats over it in its
 * transparent state (see the Header section above), like the pages on
 * nikhef.nl that open with a big image strip.
 * ------------------------------------------------------------------------ */

/* The 50vw escape can overshoot by the scrollbar width; that must not
 * become a horizontal scrollbar. */
html:has(body.has-hero) {
	overflow-x: hidden;
}

.has-hero .site {
	padding-block: 0;
}

/* Full-bleed content that escapes the centered .site container, pulled up
 * under the floating header. */
.site-main--hero {
	margin-inline: calc(50% - 50vw);
	margin-block-start: calc(-1.4 * var(--header-height));
	max-width: none;
}

/*
 * Compound selector on purpose: .site-main--hero is ONE class (not two),
 * so a plain ".site-main--hero > article" ties with the later reading-width
 * rule ".site-main > article" and loses on cascade order. Requiring both
 * classes on the same element outranks it.
 */
.site-main.site-main--hero > article {
	max-width: none;
}

/*
 * The shared article renderer caps content at reading width; on hero pages
 * the hero blocks must escape it. Everything else still reads centered,
 * like the articles on nikhef.nl.
 */
.site-main--hero .entry-content {
	max-width: none;
}

.site-main--hero .entry-content > :not(.nl-hero):not(.nl-slideshow):not(.alignwide):not(.alignfull) {
	max-width: var(--max-content-width);
	margin-inline: auto;
}

/* Hero blocks always span the full escaped main, whatever layout or width
 * rules apply around them. */
.site-main--hero .nl-hero,
.site-main--hero .nl-slideshow,
.site-main--hero .nl-slideshow > * {
	max-width: none;
	margin-inline: 0;
}

.site-main--hero .alignwide {
	max-width: var(--max-container-width);
	margin-inline: auto;
}

/* Hero cover block: a core Cover block with the nl-hero class. At least
 * most of the viewport tall, with room at the top for the floating header.
 * The dark backdrop is what shows while no image is set yet. */
.nl-hero {
	min-block-size: calc(100dvh - var(--header-height));
	padding-block: calc(var(--header-height) + var(--spacing-8)) var(--spacing-8);
	justify-content: flex-start;
	background: var(--colors-neutral-black);
	color: var(--colors-neutral-white);
}

.nl-hero .wp-block-cover__inner-container {
	inline-size: 100%;
	max-width: var(--max-container-width);
	margin-inline: auto;
	padding-inline: var(--container-padding);
	align-items: flex-start;
}

.nl-hero h1,
.nl-hero h2,
.nl-hero h3 {
	color: var(--colors-neutral-white);
}

/* The semi-opaque content box over the image, the .blur box of nikhef.nl. */
.nl-blur-box {
	max-inline-size: 35rem;
	padding: var(--spacing-5);
	background: rgba(39, 39, 51, 0.6);
	backdrop-filter: blur(10px);
	border-radius: var(--border-radius-1);
}

/* Light button variant in the hero text boxes (also the slideshow
 * caption, which is a sibling of the covers, not inside one): white on
 * the image, purple on hover. */
.nl-hero .wp-block-button__link,
.nl-slideshow > .nl-blur-box .wp-block-button__link {
	background: var(--colors-neutral-white);
	color: var(--colors-neutral-black);
}

.nl-hero .wp-block-button__link:hover,
.nl-slideshow > .nl-blur-box .wp-block-button__link:hover {
	background: var(--colors-brand-2);
	color: var(--colors-neutral-white);
}

/*
 * Hero slideshow: stacked cover blocks cross-fading, pure CSS. The first
 * slide stays in flow and gives the strip its height, the rest overlay it.
 * With three slides each is on screen for 7.5 seconds. The strip has a
 * dark base so no transition ever flashes the page background.
 */
.nl-slideshow {
	position: relative;
	overflow: clip;
	background: var(--colors-neutral-black);
}

.nl-slideshow .nl-hero {
	position: absolute;
	inset: 0;
	min-block-size: 0;
	block-size: 100%;
	animation: nl-hero-fade-mid 22.5s infinite;
	/* The later slides wait out their animation-delay: without backwards
	 * fill the keyframes do not apply during the delay, so slides 2 and 3
	 * would sit at their base opacity (1, the default) and the last slide
	 * would cover the strip until its window starts. Backwards fill pins
	 * each waiting slide at its 0% keyframe (opacity 0) instead, so the
	 * rotation is correct from the very first cycle. */
	animation-fill-mode: backwards;
}

/* The first slide stays in flow and gives the strip its height: a hero
 * height of its own, not a percentage of the (auto-height) group. Empty
 * covers have no content, so any height depending on content collapses. */
.nl-slideshow .nl-hero:first-child {
	position: relative;
	min-block-size: calc(100dvh - var(--header-height));
	block-size: auto;
	animation-name: nl-hero-fade-first;
	animation-delay: 0s;
}

.nl-slideshow .nl-hero:nth-child(2) {
	animation-delay: 7.5s;
}

.nl-slideshow .nl-hero:nth-child(3) {
	animation-name: nl-hero-fade-last;
	animation-delay: 15s;
}

/*
 * A blur box as a direct child of the slideshow is the caption shared by
 * all slides: one text box overlays the strip instead of a copy per slide.
 * It is a sibling of the covers, so it carries its own white text instead
 * of inheriting it from a cover. The inline start follows the site
 * container like on nikhef.nl: half the strip minus half the container
 * width, with the container padding as the absolute minimum, so the box
 * hugs the centered container on wide screens and the viewport padding on
 * narrow ones.
 */
.nl-slideshow > .nl-blur-box {
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: max(var(--container-padding), calc(50% - var(--max-container-width) / 2 + var(--container-padding)));
	translate: 0 -50%;
	margin: 0;
	max-inline-size: 35rem;
	z-index: 5;
	color: var(--colors-neutral-white);
}

.nl-slideshow > .nl-blur-box h1,
.nl-slideshow > .nl-blur-box h2,
.nl-slideshow > .nl-blur-box h3,
.nl-slideshow > .nl-blur-box p {
	color: var(--colors-neutral-white);
}

/*
 * Fade-over, not out-and-back-in: each slide holds full opacity until the
 * next one has fully faded in on top of it, and only then drops. The three
 * keyframe sets differ by position: slides whose successor is stacked above
 * them (the first and second) ramp in at their window start over the slide
 * beneath, then drop the moment they are covered. The last slide cannot be
 * covered (its successor sits at the bottom of the stack), so the first
 * slide pre-ramps beneath it at the end of its cycle, and the last slide
 * fades out at the window end, dissolving into the already-opaque first
 * slide. That makes the wrap-around a cross-fade too, with no dip and no
 * cut anywhere.
 */
@keyframes nl-hero-fade-first {
	0% {
		opacity: 1;
	}

	36% {
		opacity: 1;
	}

	36.1%,
	97.3% {
		opacity: 0;
	}

	100% {
		opacity: 1;
	}
}

@keyframes nl-hero-fade-mid {
	0% {
		opacity: 0;
	}

	2.7% {
		opacity: 1;
	}

	36% {
		opacity: 1;
	}

	36.1%,
	100% {
		opacity: 0;
	}
}

@keyframes nl-hero-fade-last {
	0% {
		opacity: 0;
	}

	2.7% {
		opacity: 1;
	}

	33.3% {
		opacity: 1;
	}

	36%,
	100% {
		opacity: 0;
	}
}

/* Crossing the 55rem boundary must not replay the menu fade or the
 * header states: while the window is being resized, transitions freeze.
 * The footer script toggles this class. */
.is-resizing .site-header,
.is-resizing .site-header::before,
.is-resizing .header__nav,
.is-resizing .header__nav .sub-menu {
	transition: none;
}

@media (prefers-reduced-motion: reduce) {
	/* The header crossfade and the menu fades snap instead. */
	.site-header,
	.site-header::before,
	.header__nav,
	.header__nav .sub-menu {
		transition: none;
	}

	.nl-slideshow .nl-hero {
		animation: none;
	}

	/* Show only the first slide; nothing moves for those who prefer not to. */
	.nl-slideshow .nl-hero:nth-child(n + 2) {
		display: none;
	}
}

/* Narrow viewports, like nikhef.nl: below 55rem the text box may span the
 * (padded) strip; below 30rem its padding shrinks a step. */
@media (max-width: 55rem) {
	.nl-blur-box {
		max-inline-size: 100%;
	}

	.nl-slideshow > .nl-blur-box {
		max-inline-size: calc(100% - 2 * var(--container-padding));
	}
}

@media (max-width: 30rem) {
	.nl-blur-box {
		padding: var(--spacing-4);
	}
}

/* --------------------------------------------------------------------------
 * Tags and leaders.
 *
 * The Tag paragraph style: a red chip with a slanted side and white mono
 * text, like the tags on nikhef.nl (insert a paragraph, pick "Tag" under
 * Styles). The Leader block pattern: the dark chamfered box that opens the
 * news pages on nikhef.nl, holding a title, an intro and an image.
 * ------------------------------------------------------------------------ */

p.is-style-tag {
	display: inline-block;
	max-width: fit-content;
	margin: 0 0 var(--spacing-4);
	padding: var(--spacing-1) var(--spacing-5) var(--spacing-1) var(--spacing-3);
	background: var(--colors-brand-3);
	color: var(--colors-neutral-white);
	font-family: var(--font-family-headings);
	font-size: 0.875rem;
	line-height: 1.4;
	text-decoration: none;
	clip-path: polygon(0 0, calc(100% - 0.5rem) 0, 100% 100%, 0% 100%);
}

p.is-style-tag a {
	color: inherit;
}

/*
 * Several tags on one line: put the tag paragraphs in a Group block and
 * set its layout to Row (Flex). The group's gap spaces the chips.
 */
.is-layout-flex > p.is-style-tag {
	margin: 0;
}

/* The leader: the dark chamfered opener of the news pages on nikhef.nl
 * (the official hero-small block). The box itself keeps 1rem of air at
 * the page edges, like the official margin; when it opens the content
 * it spans nearly the full page width (see below). The content inside
 * sits in a centered container capped at the official small container
 * width, so nothing reaches the edges of the dark box. */
.nl-leader {
	margin: 0 var(--spacing-4) var(--spacing-7);
	padding-block: var(--spacing-9);
	background: var(--colors-brand-7);
	color: var(--colors-neutral-white);
	clip-path: polygon(0 0, calc(100% - 5rem) 0, 100% 2rem, 100% 100%, 5rem 100%, 0 calc(100% - 2rem));
}

.nl-leader__inner {
	inline-size: 100%;
	max-inline-size: var(--max-small-container-width);
	margin-inline: auto;
	padding-inline: var(--container-padding);
	display: flex;
	flex-direction: column;
	gap: var(--spacing-5);
}

.nl-leader h1,
.nl-leader h2,
.nl-leader h3,
.nl-leader p {
	color: var(--colors-neutral-white);
}

/* The breadcrumb line above the columns: small and white, like the
 * official breadcrumbs (they keep the body font; only the date is mono). */
.nl-leader .wp-block-breadcrumbs {
	color: var(--colors-neutral-white);
	font-size: 0.875rem;
}

.nl-leader .wp-block-breadcrumbs ol {
	list-style: none;
	margin: 0;
	padding: 0;
}

/* The post date between the tags and the title. */
.nl-leader__date {
	font-family: var(--font-family-headings);
	font-size: 0.875rem;
	margin: 0 0 -0.6rem 0;
}

/* The tag row: a flex (Row) group with a little air between the chips. */
.nl-leader .is-layout-flex {
	gap: var(--spacing-3);
}

/* The row's gap spaces the chips; the stack above and below is spaced by
 * the inner container's flex gap. */
.nl-leader p.is-style-tag {
	margin: 0;
}

.nl-leader .wp-block-columns {
	gap: var(--spacing-8);
}

/* The image column: cropped to the official 4:3 ratio with the official
 * rounded corners (hero-small on nikhef.nl). The ratio here is the
 * default; picking another one in the editor writes an inline style on
 * the figure, which wins over this rule. No height forcing: an explicit
 * height would defeat the ratio, which is exactly what the old rule did. */
.nl-leader .wp-block-image {
	aspect-ratio: 4 / 3;
	margin: 0;
	overflow: hidden;
}

.nl-leader img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	border-radius: var(--border-radius-1);
}

/* A leader spans the full page width and sits flush under the header,
 * like the news pages on nikhef.nl: the percentage margin escapes the
 * centered reading column to the viewport edges; 49.7vw instead of
 * 50vw leaves a little extra air per side (measured against the
 * official page), and the block's own 1rem side margin adds the rest.
 * The 2rem pull-up lifts it over the site's 3rem top padding, so it
 * rests 1rem under the header. */
.entry-content > .nl-leader {
	margin-inline: calc(50% - 49.7vw + var(--spacing-4));
	margin-block-start: calc(-1 * var(--spacing-6));
}

/* "Full width" means viewport-wide, like the full sections on nikhef.nl:
 * core's layout CSS ships no alignfull escape on this setup, so the
 * theme provides the same percentage-margin escape the leader uses.
 * 49.7vw instead of 50vw keeps scrollbar rounding from becoming a
 * horizontal scrollbar. */
.entry-content > .alignfull {
	margin-inline: calc(50% - 49.7vw);
}

/* The escaped leader (and leader-small) reach for the viewport edges;
 * keep scrollbar rounding from adding a horizontal scrollbar. */
html:has(.entry-content > .nl-leader),
html:has(.entry-content > .nl-leader-small),
html:has(.entry-content > .alignfull) {
	overflow-x: hidden;
}



/* --------------------------------------------------------------------------
 * Job openings list (cards like the main site's news cards)
 * ------------------------------------------------------------------------ */

.job-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	/* min() keeps the 26rem column from forcing the grid wider than a
	 * phone viewport: below 26rem the column simply fills the width. */
	grid-template-columns: repeat(auto-fill, minmax(min(26rem, 100%), 1fr));
	gap: var(--spacing-6);
}

.job-card {
	display: flex;
	flex-direction: column;
	gap: var(--spacing-4);
	background: var(--colors-neutral-white);
	border: 1px solid var(--colors-neutral-20);
	border-radius: var(--border-radius-1);
	box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.22);
	padding: var(--spacing-5);
	min-height: 30rem;
}

.job-card:hover {
	box-shadow: 0 12px 24px -8px rgba(0, 0, 0, 0.15), 0 4px 8px -2px rgba(0, 0, 0, 0.1);
}

.job-card__title {
	font-family: var(--font-family-headings);
	font-size: 1.5rem;
	line-height: 1.3;
	min-height: 6rem;
	margin: 0;
	order: 2;
	color: var(--colors-neutral-black);
}

.job-card__summary {
	margin: 0;
	order: 3;
}

/* "Read more" element: the primary button from nikhef.nl, see the Buttons
 * section below for its looks. */
.job-card__link {
	margin-top: auto;
	order: 4;
	align-self: flex-start;
}

/* --------------------------------------------------------------------------
 * Classification chips - markup matches the Ubeeo ATS plugin output.
 * Styled like the tags on the main site: red, monospace, clipped corner.
 * ------------------------------------------------------------------------ */

.classifications {
	display: flex;
	flex-wrap: wrap;
	column-gap: var(--spacing-3);
	row-gap: var(--spacing-2);
	align-items: center;
	order: 1;
	margin: 0;
}

/* The classification labels are not shown: the values sit next to each
 * other as plain tags, e.g. <LHCb> <PhD position> <Amsterdam>. */
.classifications .classification {
	display: contents;
}

.classifications .class_type {
	display: none;
}

.classifications .class_values {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-2);
}

.classifications .class_values > span {
	display: inline-flex;
	align-items: center;
	clip-path: polygon(0 0, calc(100% - 0.5rem) 0, 100% 100%, 0% 100%);
	padding: var(--spacing-1) var(--spacing-5) var(--spacing-1) var(--spacing-3);
	background: var(--colors-brand-3);
	color: var(--colors-neutral-white);
	font-family: var(--font-family-headings);
	font-size: 0.875rem;
	line-height: 1.4;
}

.job-single .entry-header .classifications {
	order: 0;
	margin-bottom: var(--spacing-4);
}

/* --------------------------------------------------------------------------
 * Single job page
 * ------------------------------------------------------------------------ */

/*
 * Centered article column for every single template - pages, posts and job
 * openings all read like the articles on nikhef.nl. The overview cards live
 * inside lists, so they keep spanning the full grid.
 */
.site-main > article {
	max-width: var(--max-content-width);
	margin-inline: auto;
}

.job-single .entry-header {
	margin-bottom: var(--spacing-6);
}

.entry-content {
	max-width: var(--max-content-width);
}

/* Wide-width blocks (the block toolbar's Wide alignment) sit at the
 * official small container: 70rem, measured on nikhef.nl as the
 * 1120px container between the 840px text and the 1440px full-width
 * stretch (the three-card "more news" grid on the news pages uses it).
 * The reading-width cap above would clamp them back to 52.5rem, so
 * the percentage-margin escape lets them break out to their measured
 * width, the same trick as the leader but calibrated to 70rem instead
 * of the viewport. Excluded from hero pages, which have their own
 * alignwide at the full container width. The scoped !important is
 * deliberate: WordPress generates margin-inline: auto !important for
 * alignwide children through a lower-specificity selector; between
 * two importants, specificity decides and this one outranks it. */
.site-main:not(.site-main--hero) .entry-content > .alignwide {
	margin-inline: calc(50% - calc(var(--max-small-container-width)*1.2) / 2) !important;
	max-width: calc(var(--max-small-container-width)*1.2);
}

/* On viewports narrower than the wide size the negative margins would
 * push past the edges; give the block the container padding instead,
 * which reads as slightly wider than the text without a scrollbar. */
@media (max-width: 70rem) {
	.site-main:not(.site-main--hero) .entry-content > .alignwide {
		margin-inline: calc(var(--container-padding) * -1) !important;
		max-width: calc(100% + var(--container-padding) * 2);
	}
}

/* The wide-block escape reaches for the viewport edges; keep scrollbar
 * rounding from adding a horizontal scrollbar on pages that have one. */
html:has(.entry-content > .alignwide) {
	overflow-x: hidden;
}

/*
 * Content links, like the articles on nikhef.nl: fully underlined at rest,
 * the underline retracts to the right on hover. Links to other sites carry
 * a small diagonal arrow (added server-side, class external-link).
 */
.entry-content a:not(.hs_apply_button):not(.button):not(.wp-block-button__link),
.page-intro a:not(.hs_apply_button):not(.button):not(.wp-block-button__link),
.no-results a:not(.hs_apply_button):not(.button):not(.wp-block-button__link) {
	color: inherit;
	text-decoration: none;
	background: linear-gradient(0deg, currentColor, currentColor) no-repeat left bottom / 100% 1px;
	transition: background-size var(--animation-speed-slow) var(--cubic-bezier);
}

.entry-content a:not(.hs_apply_button):not(.button):not(.wp-block-button__link):hover,
.page-intro a:not(.hs_apply_button):not(.button):not(.wp-block-button__link):hover,
.no-results a:not(.hs_apply_button):not(.button):not(.wp-block-button__link):hover {
	color: inherit;
	background-size: 0 1px;
	background-position-x: right;
}

.entry-content a.external-link:not(.hs_apply_button):not(.wp-block-button__link)::after,
.page-intro a.external-link:not(.hs_apply_button):not(.wp-block-button__link)::after,
.no-results a.external-link:not(.hs_apply_button):not(.wp-block-button__link)::after {
	content: "";
	display: inline-block;
	/* 1.5rem (24px), the intrinsic size of the external icon on nikhef.nl,
	 * aligned to the middle of the line so it does not poke above it. */
	inline-size: 1.5rem;
	block-size: 1.5rem;
	margin-left: var(--spacing-1);
	vertical-align: middle;
	background-color: currentColor;
	mask: var(--external-icon) center / contain no-repeat;
	transition: transform var(--animation-speed-slow) var(--cubic-bezier);
}

/* The official site does not animate the arrow on plain content links
 * (checked on nikhef.nl); kept here commented out as an option.
.entry-content a.external-link:not(.hs_apply_button):not(.wp-block-button__link):hover::after,
.page-intro a.external-link:not(.hs_apply_button):not(.wp-block-button__link):hover::after,
.no-results a.external-link:not(.hs_apply_button):not(.wp-block-button__link):hover::after {
	transform: translateX(5px);
}
*/

.entry-content h2,
.entry-content h3,
.entry-content h4 {
	margin-top: var(--spacing-6);
}

.entry-content blockquote {
	margin: var(--spacing-5) 0;
	padding-left: var(--spacing-4);
	border-left: 0.5rem solid var(--colors-brand-3);
	background: var(--colors-neutral-10);
}

.entry-content table {
	border-collapse: collapse;
	width: 100%;
	margin: 0 0 var(--spacing-4);
	border: 1px solid var(--colors-neutral-20);
}

.entry-content th,
.entry-content td {
	padding: var(--spacing-2) var(--spacing-3);
	text-align: left;
	border: 1px solid var(--colors-neutral-20);
}

.entry-content th {
	background-color: var(--colors-brand-7);
	color: var(--colors-neutral-white);
	font-weight: 700;
}

.entry-content tr:nth-child(even) {
	background-color: var(--colors-neutral-10);
}

.entry-thumbnail {
	margin-bottom: var(--spacing-5);
}

/* --------------------------------------------------------------------------
 * Buttons - the primary button from nikhef.nl: dark with white underlined
 * uppercase text, a clipped bottom-right corner, and an arrow that slides
 * right on hover; the background turns purple (brand-2).
 * ------------------------------------------------------------------------ */

.button,
.job-card__link,
.hs_applybutton a,
button,
input[type="submit"],
.wp-block-button__link {
	display: inline-flex;
	align-items: center;
	gap: var(--spacing-4);
	padding: var(--spacing-4) var(--spacing-3);
	padding-inline-end: calc(var(--spacing-3) + var(--spacing-4));
	border: 0;
	border-radius: 0;
	clip-path: polygon(0 0, calc(100% - var(--spacing-4)) 0, 100% 100%, 0 100%);
	background: var(--colors-brand-7);
	color: var(--colors-neutral-white);
	font-family: var(--font-family-headings);
	font-size: 0.875rem;
	font-weight: 400;
	text-transform: uppercase;
	text-decoration: underline;
	text-underline-offset: var(--text-underline-offset);
	cursor: pointer;
	transition:
		background var(--animation-speed-slow) var(--cubic-bezier),
		text-underline-offset var(--animation-speed-slow) var(--cubic-bezier);
}

.button:hover,
.job-card__link:hover,
.hs_applybutton a:hover,
button:hover,
input[type="submit"]:hover,
.wp-block-button__link:hover {
	background: var(--colors-brand-2);
	color: var(--colors-neutral-white);
	/* Like nikhef.nl: the underline drops down a little on hover. */
	text-underline-offset: calc(var(--text-underline-offset) + 0.15rem);
}

/*
 * The outline variant (the "Outline" style of core Button blocks), like the
 * secondary button on nikhef.nl: a plain rectangle, no clipped corner, with
 * a white background and a thin black border. On hover it inverts to the
 * dark button with white text.
 */
.wp-block-button.is-style-outline .wp-block-button__link {
	background: var(--colors-neutral-white);
	color: var(--colors-neutral-black);
	border: 1px solid var(--colors-neutral-black);
	border-radius: 0;
	clip-path: none;
}

.wp-block-button.is-style-outline .wp-block-button__link:hover {
	background: var(--colors-brand-7);
	border-color: var(--colors-brand-7);
	color: var(--colors-neutral-white);
}

/*
 * The arrow variant. Base buttons have no arrow; these get one: the "With
 * arrow" style of core Button blocks (is-style-arrow), the .button--arrow
 * class, the job card and apply links, and core Button blocks whose block
 * settings carry the nl-arrow class. The arrow slides right on hover, like
 * nikhef.nl.
 */
.button--arrow::after,
.job-card__link::after,
.hs_applybutton a::after,
.wp-block-button.nl-arrow .wp-block-button__link::after,
.wp-block-button__link.nl-arrow::after,
.wp-block-button.is-style-arrow .wp-block-button__link::after {
	content: "";
	display: inline-block;
	inline-size: 1rem;
	block-size: 1rem;
	flex-shrink: 0;
	background-color: currentColor;
	mask: var(--arrow-icon) center / contain no-repeat;
	transition: transform var(--animation-speed-slow) var(--cubic-bezier);
}

.button--arrow:hover::after,
.job-card__link:hover::after,
.hs_applybutton a:hover::after,
.wp-block-button.nl-arrow .wp-block-button__link:hover::after,
.wp-block-button__link.nl-arrow:hover::after,
.wp-block-button.is-style-arrow .wp-block-button__link:hover::after {
	transform: translateX(5px);
}

/*
 * Buttons pointing at another site turn their arrow 45 degrees into the
 * external direction, like the external icon on nikhef.nl. The theme adds
 * the external-link class server-side (see nikhef_lite_mark_external_links).
 * The rotation lives inside the same transform list as the hover slide, so
 * the arrow slides sideways first and then rotates in place; a separate
 * rotate property would swing the translated arrow upward.
 */
.button--arrow.external-link::after,
.job-card__link.external-link::after,
.hs_applybutton a.external-link::after,
.wp-block-button.nl-arrow .wp-block-button__link.external-link::after,
.wp-block-button__link.nl-arrow.external-link::after,
.wp-block-button.is-style-arrow .wp-block-button__link.external-link::after {
	transform: rotate(-45deg);
}

.button--arrow.external-link:hover::after,
.job-card__link.external-link:hover::after,
.hs_applybutton a.external-link:hover::after,
.wp-block-button.nl-arrow .wp-block-button__link.external-link:hover::after,
.wp-block-button__link.nl-arrow.external-link:hover::after,
.wp-block-button.is-style-arrow .wp-block-button__link.external-link:hover::after {
	transform: translateX(5px) rotate(-45deg);
}

.hs_applybutton {
	margin: 0 0 var(--spacing-5);
}

/* The plugin's own [jobs] shortcode output, when used in page content. */
.jobs-list-wrapper .jobs-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(26rem, 1fr));
	gap: var(--spacing-6);
}

.jobs-list-wrapper .job-item {
	display: flex;
	flex-direction: column;
	gap: var(--spacing-3);
	background: var(--colors-neutral-white);
	border: 1px solid var(--colors-neutral-20);
	border-radius: var(--border-radius-1);
	box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.22);
	padding: var(--spacing-5);
	color: inherit;
	text-decoration: none;
}

.jobs-list-wrapper .job-item:hover {
	box-shadow: 0 12px 24px -8px rgba(0, 0, 0, 0.15), 0 4px 8px -2px rgba(0, 0, 0, 0.1);
}

.jobs-list-wrapper .job-title {
	font-family: var(--font-family-headings);
	font-weight: 400;
	font-size: 1.25rem;
	line-height: 1.3;
	text-decoration: underline transparent;
}

.jobs-list-wrapper .job-item:hover .job-title {
	text-decoration-color: currentColor;
}

.jobs-list-wrapper .summary {
	color: var(--text-muted);
}

/* --------------------------------------------------------------------------
 * Forms
 * ------------------------------------------------------------------------ */

label {
	font-size: 0.875rem;
}

input[type="text"],
input[type="email"],
input[type="search"],
input[type="tel"],
input[type="url"],
textarea,
select {
	padding: var(--spacing-3);
	font-family: inherit;
	font-size: 1rem;
	background: var(--colors-neutral-white);
	border: 1px solid var(--colors-neutral-black);
	border-radius: 0;
	width: 100%;
}

.search-form {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-2);
	margin: var(--spacing-4) 0;
}

.search-form input[type="search"] {
	flex: 1;
	max-width: 24rem;
	width: auto;
}

/* The plugin's application/referral form popups inherit these basics. */

/* --------------------------------------------------------------------------
 * Generic post list (blog index, archives, search)
 * ------------------------------------------------------------------------ */

.post-list {
	list-style: none;
	margin: 0;
	padding: 0;
	max-width: var(--max-content-width);
}

.post-list__item {
	margin-bottom: var(--spacing-5);
	padding-bottom: var(--spacing-5);
	border-bottom: 1px solid var(--colors-neutral-20);
}

.post-list__item .post-card__title {
	font-family: var(--font-family-headings);
	font-size: 1.25rem;
	margin: 0 0 var(--spacing-2);
}

.post-list__item .post-card__title a {
	display: inline;
	color: var(--colors-neutral-black);
	text-decoration: none;
	/* Red arrow after the title, like the news cards on nikhef.nl. */
}

.post-list__item .post-card__title a::after {
	content: "";
	display: inline-block;
	inline-size: 0.75rem;
	block-size: 0.75rem;
	margin-left: var(--spacing-2);
	vertical-align: baseline;
	background-color: var(--colors-brand-3);
	mask: var(--arrow-icon) center / contain no-repeat;
}

.post-list__item .post-card__title a:hover {
	color: var(--colors-brand-2);
}

.post-list__item .post-card__title a:hover::after {
	background-color: var(--colors-brand-2);
}

.entry-meta {
	color: var(--colors-neutral-30);
	font-family: var(--font-family-headings);
	font-size: 0.875rem;
	margin: 0 0 var(--spacing-2);
}

.archive-description {
	max-width: var(--max-content-width);
}

.pagination {
	margin: var(--spacing-6) 0;
}

.pagination .nav-links {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-2);
}

.pagination .page-numbers {
	display: inline-block;
	padding: var(--spacing-2) var(--spacing-3);
	border: 1px solid var(--colors-neutral-20);
	color: var(--colors-neutral-black);
	text-decoration: none;
}

.pagination .page-numbers.current {
	background: var(--colors-brand-7);
	border-color: var(--colors-brand-7);
	color: var(--colors-neutral-white);
}

.pagination .page-numbers:hover {
	color: var(--colors-neutral-black);
	text-decoration: underline;
}

/* --------------------------------------------------------------------------
 * Editor building blocks (see the patterns/ directory): cards, the
 * accordion (WP core Details block) and core buttons, styled like nikhef.nl.
 * ------------------------------------------------------------------------ */

/* Card boxes. The whole box is clickable through the title link (see
 * the stretched link below). The tight padding frames the image; the
 * content below it sits at the roomier original padding, via inline
 * margins and a taller bottom. */
.nl-card {
	position: relative;
	overflow: hidden;
	background: var(--colors-neutral-white);
	border: 1px solid var(--colors-neutral-20);
	border-radius: var(--border-radius-1);
	box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.22);
	padding: var(--spacing-2);
	padding-block-end: var(--spacing-5);
}

.nl-card > :not(.wp-block-image) {
	/* The scoped !important is deliberate and earns its keep: the
	 * constrained layout emits margin-inline: auto !important on the
	 * children (through a zero-specificity :where wrapper), which
	 * flattens any plain margin. Specificity decides between
	 * importants, and this selector outranks it. Padding cannot be
	 * used instead: the chip is an inline-block and would pad inside
	 * its red box. */
	margin-inline: calc(var(--spacing-5) - var(--spacing-2)) !important;
}

/* The aspect ratio is a property of the image block (set in the block
 * settings, shipped by the pattern), never a global rule: other images
 * dropped into a card keep their own shape. The corners are ours, and
 * so is the fill: without the inline-size the image renders at the
 * width attribute WordPress prints from the attachment metadata, so a
 * small portrait sits lost in a wide card while a large one fills it. */
.nl-card img {
	inline-size: 100%;
	border-radius: var(--border-radius-1);
}

/* The card title link: a transparent underline that fades in on hover
 * (like the research cards) and the stretched overlay that makes the
 * whole card clickable. The .site-main .wp-block-group scope is
 * deliberate: the content-link rules and the external-link arrow
 * ::after outrank a plainer selector, and the arrow would otherwise
 * shrink the overlay to a 1.5rem icon parked far off-screen when the
 * card links to another site. Every arrow property is reset here. */
.site-main .wp-block-group.nl-card .wp-block-heading a ,
.site-main .wp-block-nikhef-lite-card.nl-card .wp-block-heading a {
	color: inherit;
	background: none;
	text-decoration: underline;
	text-decoration-color: transparent;
	transition: text-decoration-color var(--animation-speed-slow) var(--cubic-bezier);
}

@media (hover: hover) {
	.site-main .wp-block-group.nl-card:hover .wp-block-heading a ,
.site-main .wp-block-nikhef-lite-card.nl-card:hover .wp-block-heading a {
		text-decoration-color: currentColor;
	}
}

.site-main .wp-block-group.nl-card:focus-within .wp-block-heading a ,
.site-main .wp-block-nikhef-lite-card.nl-card:focus-within .wp-block-heading a {
	text-decoration-color: currentColor;
}

/* The stretched overlay moves to ::before so ::after can be the arrow.
 * Same mechanism, same properties, different pseudo-element. */
.site-main .wp-block-group.nl-card .wp-block-heading a::before ,
.site-main .wp-block-nikhef-lite-card.nl-card .wp-block-heading a::before {
	content: "";
	position: absolute;
	inset: -100rem;
	display: block;
	inline-size: auto;
	block-size: auto;
	background: transparent;
	mask: none;
}

/* The card title arrow, like the button arrow: right-pointing for
 * internal links, rotated 45 degrees counter-clockwise for external
 * links. The scoped selector wins the tie with the content-link
 * external arrow ::after (same specificity, later in the file), so
 * the --arrow-icon and the card's sizing override the content-link's
 * --external-icon and 1.5rem sizing. */
.site-main .wp-block-group.nl-card .wp-block-heading a::after ,
.site-main .wp-block-nikhef-lite-card.nl-card .wp-block-heading a::after {
	content: "";
	display: inline-block;
	inline-size: 1rem;
	block-size: 1rem;
	margin-inline-start: var(--spacing-2);
	vertical-align: baseline;
	background-color: currentColor;
	mask: var(--arrow-icon) center / contain no-repeat;
	transition: transform var(--animation-speed-slow) var(--cubic-bezier);
}

.site-main .wp-block-group.nl-card .wp-block-heading a.external-link::after ,
.site-main .wp-block-nikhef-lite-card.nl-card .wp-block-heading a.external-link::after {
	transform: rotate(-45deg);
}

/* The arrow slides right on hover, like the button arrow; external
 * links keep their rotation while sliding. */
@media (hover: hover) {
	.site-main .wp-block-group.nl-card:hover .wp-block-heading a::after ,
.site-main .wp-block-nikhef-lite-card.nl-card:hover .wp-block-heading a::after {
		transform: translateX(5px);
	}

	.site-main .wp-block-group.nl-card:hover .wp-block-heading a.external-link::after ,
.site-main .wp-block-nikhef-lite-card.nl-card:hover .wp-block-heading a.external-link::after {
		transform: translateX(5px) rotate(-45deg);
	}
}

/* Buttons sit above the stretched overlay, so cards that still carry
 * one (older pattern instances) keep their own working click target. */
.nl-card .wp-block-buttons {
	position: relative;
	z-index: 2;
}

/* Research card: the image cards of the nikhef.nl "Research themes" grid.
 * A rounded picture (a Cover block) with the shared .nl-blur-box centered
 * on it, chips above the title, and the whole card clickable through the
 * title link. Geometry and motion measured on
 * www.nikhef.nl/en/research/research-themes-nikhef/: 4:3 ratio, 8px corners,
 * 16px/48px padding, zoom to 125% over 0.6s. */
.nl-research-card {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	aspect-ratio: 4 / 3;
	padding: var(--spacing-4) var(--spacing-7);
	background: var(--colors-brand-7);
	/* Measured 8px on both official pages, but the rounder 0.67rem cut
	 * reads better on our card sizes (Mattijs, reviewed against the
	 * official front page). */
	border-radius: var(--border-radius-2);
	color: var(--colors-neutral-white);
}

.nl-research-card .nl-research-card__image {
	position: absolute;
	inset: 0;
	min-block-size: 0;
	background: var(--colors-brand-7);
}

.nl-research-card .wp-block-cover__image-background {
	transition: transform var(--animation-speed-slow) cubic-bezier(0.1, 0.1, 0, 1);
}

.nl-research-card:hover .wp-block-cover__image-background,
.nl-research-card:focus-within .wp-block-cover__image-background {
	transform: scale(1.25);
}

/* Inside the card the blur box spans the content area (between the side
 * paddings) and centers its content, like on the site. The box is
 * positioned and lifted above the cover: z-index 3 beats the z-index 1
 * WordPress puts on .wp-block-cover__background. The tint and the blur
 * live on a ::before, NOT on the box itself: a backdrop-filter on the
 * box makes it a composited layer, and Chromium then clips the stretched
 * link overlay to the box bounds, killing the whole-card click. */
.nl-research-card .nl-blur-box {
	position: relative;
	z-index: 3;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--spacing-4);
	inline-size: 100%;
	max-inline-size: none;
	min-block-size: 7.8125rem; /* 125px, measured on nikhef.nl */
	text-align: center;
	background: transparent;
	backdrop-filter: none;
}

.nl-research-card .nl-blur-box::before {
	content: "";
	position: absolute;
	/* Below the box content (z-index: -1): without it the tint and the
	 * blur are part of the backdrop that can swallow the chip and title
	 * text, depending on the compositing mode. Pinned here it stays a
	 * backdrop layer for the image only. */
	z-index: -1;
	inset: 0;
	border-radius: inherit;
	background: rgba(39, 39, 51, 0.6);
	backdrop-filter: blur(10px);
}

.nl-research-card .nl-research-card__tags {
	gap: var(--spacing-3);
}

.nl-research-card p.is-style-tag {
	margin: 0;
}

.nl-research-card .wp-block-heading {
	margin: 0;
	font-family: var(--font-family-default);
	font-size: 1.75rem;
	line-height: 1.2;
	/* The global heading color is the body color; the card title is white
	 * like the site's. */
	color: var(--colors-neutral-white);
}

.nl-research-card .wp-block-heading a {
	color: inherit;
	text-decoration: underline;
	text-decoration-color: transparent;
	text-underline-offset: var(--text-underline-offset);
	/* The underline fades in on hover, like the research cards on
	 * nikhef.nl; the color transition carries the motion. */
	transition: text-decoration-color var(--animation-speed-slow) var(--cubic-bezier);
}

/* The hover guard keeps touch devices from sticking in hover state. */
@media (hover: hover) {
	.nl-research-card:hover .wp-block-heading a {
		text-decoration-color: currentColor;
	}
}

.nl-research-card:focus-within .wp-block-heading a {
	text-decoration-color: currentColor;
}

/* Stretched link: the title link makes the whole card clickable. The blur
 * box owns its positioned descendants (backdrop-filter creates a
 * containing block), so the overlay escapes it with a negative inset and
 * the card's overflow: hidden clips it back to exactly the card.
 * The .site-main scope is deliberate: the content-link rules
 * (.entry-content a:not(...)) and the external-link arrow ::after outrank
 * a plain component selector, and the arrow would otherwise shrink the
 * overlay to a 1.5rem icon parked far off-screen when the card links to
 * another site. Every property the arrow sets is reset here. */
.site-main .nl-research-card .nl-blur-box .wp-block-heading a {
	color: inherit;
	background: none;
	/* The title carries a transparent underline that fades in on hover,
	 * like the research cards on nikhef.nl. Everything lives in this
	 * cascade-winning rule: the content-link styling sets
	 * text-decoration: none at (0,4,1) and its background-size
	 * transition, so the component rule below never gets a say inside
	 * entry-content. */
	text-decoration: underline;
	text-decoration-color: transparent;
	transition: text-decoration-color var(--animation-speed-slow) var(--cubic-bezier);
}

@media (hover: hover) {
	.site-main .nl-research-card:hover .nl-blur-box .wp-block-heading a {
		text-decoration-color: currentColor;
	}
}

.site-main .nl-research-card:focus-within .nl-blur-box .wp-block-heading a {
	text-decoration-color: currentColor;
}

.site-main .nl-research-card .nl-blur-box .wp-block-heading a::after {
	content: "";
	position: absolute;
	inset: -100rem;
	display: block;
	inline-size: auto;
	block-size: auto;
	background: transparent;
	mask: none;
}

/* Accordion: WP core Details blocks, like the accordions on nikhef.nl. */
.nl-accordions {
	border-block-start: 1px solid var(--colors-neutral-30);
}

.wp-block-details {
	margin-top: -1px;
	border-block-end: 1px solid var(--colors-neutral-30);
	border-block-start: 1px solid var(--colors-neutral-30);
}

.wp-block-details > summary {
	position: relative;
	display: flex;
	padding: var(--spacing-5) 0 var(--spacing-5) calc(var(--spacing-5) + var(--spacing-4));
	cursor: pointer;
	font-weight: 700;
	font-size: var(--typscale-body-lg);
	color: var(--colors-neutral-black);
	list-style: none;
	transition: color var(--animation-speed-slow) var(--cubic-bezier);
}

.wp-block-details > summary::marker,
.wp-block-details > summary::-webkit-details-marker {
	content: "";
	display: none;
}

.wp-block-details > summary:hover {
	color: var(--colors-brand-3);
}

/* The plus marker: a horizontal line, plus a vertical line that collapses
 * into a minus when the section is open. */
.wp-block-details > summary::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	inset-block-start: 50%;
	transform: translateY(-50%);
	width: 1rem;
	height: 1px;
	background-color: currentColor;
}

.wp-block-details > summary::after {
	content: "";
	position: absolute;
	inset-inline-start: calc(0.5rem - 0.5px);
	inset-block-start: 50%;
	transform: translateY(-50%);
	width: 1px;
	height: 1rem;
	background-color: currentColor;
	transition: transform var(--animation-speed-slow) var(--cubic-bezier);
}

.wp-block-details[open] > summary::after {
	transform: translateY(-50%) scaleY(0);
}

/* The slow open/close itself lives in the footer script (see the note
 * there): the pure-CSS interpolate-size route only works in Chromium,
 * and the fold-out has to animate in Firefox too. Without JavaScript
 * the native instant toggle applies, which is fine. */

/* Core Button blocks are part of the button family above: the primary
 * look by default, and the arrow variant when the block carries the
 * nl-arrow class (Advanced, Additional CSS classes in the editor). */

/* --------------------------------------------------------------------------
 * No results
 * ------------------------------------------------------------------------ */

.no-results {
	max-width: var(--max-content-width);
	padding: var(--spacing-6) 0;
}

/* --------------------------------------------------------------------------
 * Footer - mirrors nikhef.nl: dark bar with the logo and tagline on the
 * left, a three-column footer menu (top-level items are the column
 * titles), the partner logos, and a light bottom bar with the copyright.
 * ------------------------------------------------------------------------ */

.site-footer {
	background: var(--colors-brand-7);
	color: var(--colors-neutral-white);
	font-size: 0.875rem;
	/* The footer logo follows the footer text color, like on nikhef.nl;
	 * without this the registered --icon-color initial (the header red)
	 * would leak in through the SVG fallback. */
	--icon-color: var(--colors-neutral-white);
}

.footer__main {
	padding-block: var(--spacing-8);
}

.footer__grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: var(--spacing-8);
}

.footer__logo {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--spacing-4);
}

.footer__logo svg {
	height: auto;
	display: block;
	max-width: 100%;
}

.footer__tagline {
	margin: 0;
	/* 19ch + balanced wrapping matches the official footer: the EN
	 * tagline wraps to the same three lines ("National Institute" fits
	 * one line; 17ch broke it into four). */
	max-inline-size: 19ch;
	text-wrap: balance;
	font-family: var(--font-family-headings);
	font-size: 0.875rem;
	line-height: 1.35;
}

.footer__col {
	min-width: 0;
}

.footer__title {
	color: var(--colors-brand-4);
	font-family: var(--font-family-default);
	font-size: 1rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	margin: 0 0 var(--spacing-3);
}

.site-footer a {
	color: var(--colors-neutral-white);
}

/* The menu columns: flat link lists under their own header. */
.footer__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--spacing-3);
}

.footer__list a {
	color: var(--colors-neutral-white);
	text-decoration: none;
	line-height: 1.8;
	/* Underline grows in from the right on hover, like nikhef.nl. */
	background: linear-gradient(0deg, currentColor, currentColor) no-repeat right bottom / 0 1px;
	transition: background-size var(--animation-speed-slow) var(--cubic-bezier);
}

.footer__list a:hover {
	background-size: 100% 1px;
	background-position-x: left;
}

/* The social column: icon next to the label, like the official footer. */
.footer__social a {
	display: inline-flex;
	align-items: center;
	gap: var(--spacing-2);
}

.footer__social-icon {
	inline-size: 1rem;
	block-size: 1rem;
	flex-shrink: 0;
	fill: currentColor;
}

/* The external arrow after the label, like the official footer (their JS
 * injects a 24px icon into every cross-domain link; the social links are
 * always cross-domain, so ours is a plain CSS mask at the same size). */
.footer__social a::after {
	content: "";
	display: block;
	inline-size: 1.5rem;
	block-size: 1.5rem;
	flex-shrink: 0;
	background-color: currentColor;
	mask: var(--external-icon) center / contain no-repeat;
}

/* Academic partner logos (grid column 2 and onwards, spread edge to edge). */
.footer__partners {
	grid-column: 2 / -1;
	display: flex;
	flex-direction: column;
	gap: var(--spacing-6);
}

.footer__partners-title {
	font-size: 1.125rem;
	font-weight: 700;
	text-transform: uppercase;
	font-family: var(--font-family-default);
	color: var(--colors-brand-4);
	margin: 0;
}

.footer__partners-grid {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--spacing-8);
	justify-content: space-between;
}

.footer__partner {
	display: flex;
	align-items: center;
	justify-content: center;
}

.footer__partner img {
	display: block;
	height: 3rem;
	width: auto;
	filter: brightness(0) invert(1);
}

/* Light bottom bar with the copyright, like nikhef.nl. */
.footer__bottom {
	background: #e8e8e8;
	color: var(--colors-neutral-black);
	padding-block: var(--spacing-4);
}

.footer__bottom-inner {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-2) var(--spacing-6);
	align-items: center;
	font-size: 0.875rem;
}

.footer__bottom p {
	margin: 0;
}

.footer__bottom a {
	color: inherit;
}

@media (max-width: 75rem) {
	.footer__grid {
		grid-template-columns: 1fr;
		gap: var(--spacing-6);
	}

	.footer__logo {
		align-items: center;
	}

	.footer__logo svg {
		margin-inline: auto;
	}

	.footer__tagline {
		text-align: center;
	}

	.footer__col,
	.footer__partners {
		grid-column: 1;
	}

	.footer__partners-title {
		text-align: center;
	}

	.footer__partners-grid {
		justify-content: center;
		gap: var(--spacing-6);
	}

	.footer__partner img {
		height: 2.5rem;
	}
}

/* --------------------------------------------------------------------------
 * Leader small, link cards and tertiary navigation: three blocks measured
 * from nikhef.nl (their hero-small, link-cards and the tertiary
 * navigation on the work-and-study pages).
 * ------------------------------------------------------------------------ */

/* Leader small: the gray chamfered opener of overview pages like
 * https://www.nikhef.nl/actueel/colloquia/. */
.nl-leader-small {
	background: var(--colors-neutral-10);
	clip-path: polygon(0 0, calc(100% - 5rem) 0, 100% 2rem, 100% 100%, 5rem 100%, 0 calc(100% - 2rem));
}

/* Like the leader, the box escapes the content column itself (the layout
 * system has no working alignfull escape on this theme): percentage
 * margins reach for the viewport edges, leaving the official 1rem inset
 * per side. It works anywhere in the content, not just as the first
 * child, because hero-small opens overview pages below a page header. */
.entry-content > .nl-leader-small {
	margin-inline: calc(50% - 49.7vw + var(--spacing-4));
    margin-top: calc(-1 * var(--spacing-5));
    margin-bottom: var(--spacing-5);
}

.nl-leader-small__inner {
	max-inline-size: var(--max-small-container-width);
	margin-inline: auto;
	padding-inline: var(--container-padding);
	padding-block: var(--spacing-9);
}

.nl-leader-small__content {
	display: grid;
	grid-template-columns: 1fr 1fr;
	align-items: end;
	gap: var(--spacing-8);
}

.nl-leader-small__main {
	display: flex;
	flex-direction: column;
	gap: var(--spacing-4);
}

/* The chip keeps its slanted side here, like everywhere else; only its
 * margin pins into the flex column. */
.nl-leader-small p.is-style-tag {
	margin: 0;
}

.nl-leader-small__main .wp-block-heading {
	margin: 0;
	color: var(--colors-brand-3);
}

.nl-leader-small__main p:not(.is-style-tag) {
	margin: 0;
	font-size: var(--typscale-body-lg);
	line-height: 1.6;
}

.nl-leader-small .wp-block-buttons {
	margin: 0;
}

.nl-leader-small .wp-block-image {
	aspect-ratio: 4 / 3;
	margin: 0;
	overflow: hidden;
}

.nl-leader-small img {
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	border-radius: var(--border-radius-1);
}

@media (max-width: 55rem) {
	.nl-leader-small__content {
		grid-template-columns: 1fr;
	}

	.nl-leader-small__inner {
		padding-block: var(--spacing-8);
	}
}

/* Link cards: the red 4x1 row from the nikhef.nl front page. The row is
 * a wrapping flex; each card takes a quarter minus its share of the gap
 * (half on tablets, full width on phones). The outer cards carry the
 * mirror chamfers at the outer corners, like the official block. */
.nl-link-cards {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-5);
	justify-content: center;
	margin: 2rem;
}

.nl-link-card {
	display: flex;
	flex-direction: column;
	gap: var(--spacing-4);
	flex: 0 0 calc(25% - var(--spacing-5) * 3 / 4);
	background: var(--colors-brand-3);
	color: var(--colors-neutral-white);
	padding: calc(var(--spacing-5) + var(--spacing-6)) var(--spacing-6);
	overflow: hidden;
}

.nl-link-card:first-child {
	clip-path: polygon(0 0, 100% 0, 100% 100%, 5rem 100%, 0 calc(100% - 2rem));
}

.nl-link-card:last-child {
	clip-path: polygon(0 0, calc(100% - 5rem) 0, 100% 2rem, 100% 100%, 0 100%);
}

.nl-link-card .wp-block-heading {
	margin: 0;
	color: var(--colors-neutral-white);
	font-size: 1.25rem;
	font-weight: 700;
	font-family: var(--font-family-default);
}

.nl-link-card .wp-block-list {
	margin: 0;
	padding: 0;
	list-style: none;
}

.nl-link-card .wp-block-list li {
	margin: 0;
}

/* The content-link gradient must not run under the arrow (the arrow is
 * part of the link box). Measured on nikhef.nl/en/: the card links are
 * underlined, offset 5px at rest, dropping to 7.4px on hover over
 * 400ms, like the button underline. Four classes to out-specify the
 * content-link rules, then the later cascade wins. */
.entry-content .nl-link-cards .nl-link-card .wp-block-list a,
.entry-content .nl-link-cards .nl-link-card .wp-block-list a:hover {
	background: none;
	text-decoration: underline;
	text-underline-offset: var(--text-underline-offset);
	transition: text-underline-offset var(--animation-speed-fast) var(--cubic-bezier);
}

.nl-link-card .wp-block-list a {
	display: inline-flex;
	align-items: center;
	gap: var(--spacing-2);
	color: var(--colors-neutral-white);
	font-size: 0.875rem;
	font-weight: 500;
	letter-spacing: 0.05em;
	text-decoration: underline;
	text-underline-offset: var(--text-underline-offset);
	text-transform: uppercase;
	transition:
		gap var(--animation-speed-slow) var(--cubic-bezier),
		text-underline-offset var(--animation-speed-fast) var(--cubic-bezier);
}

.nl-link-card .wp-block-list a::after {
	content: "";
	display: block;
	inline-size: 1rem;
	block-size: 1rem;
	flex-shrink: 0;
	background-color: currentColor;
	mask: var(--arrow-icon) center / contain no-repeat;
}

@media (hover: hover) {
	.nl-link-card .wp-block-list a:hover {
		gap: var(--spacing-3);
	}

	/* Same four classes as the suppression rule above: the offset drop
	 * must out-specify it (the cascade tie goes to the later rule). */
	.entry-content .nl-link-cards .nl-link-card .wp-block-list a:hover {
		text-underline-offset: calc(var(--text-underline-offset) + 0.15rem);
	}
}

@media (max-width: 55rem) {
	.nl-link-card {
		flex: 0 0 calc(50% - var(--spacing-5) / 2);
	}

	.nl-link-card:first-child {
		clip-path: polygon(0 2rem, 5rem 0, 100% 0, 100% 100%, 0 100%);
	}

	.nl-link-card:last-child {
		clip-path: polygon(0 0, 100% 0, 100% calc(100% - 2rem), calc(100% - 5rem) 100%, 0 100%);
	}
}

@media (max-width: 30rem) {
	.nl-link-card {
		flex: 0 0 100%;
	}

	.nl-link-card:first-child {
		clip-path: polygon(0 0, calc(100% - 5rem) 0, 100% 2rem, 100% 100%, 0 100%);
	}

	.nl-link-card:last-child {
		clip-path: polygon(0 0, 100% 0, 100% 100%, 5rem 100%, 0 calc(100% - 2rem));
	}
}

/* Tertiary navigation: the teal card with a mono title, from
 * https://www.nikhef.nl/en/work-and-study/working-at-nikhef/. */
.nl-tertiary-nav .wp-block-heading {
	margin: 0 0 var(--spacing-3);
	/* The official dark teal (their brand-6). */
	color: rgb(4, 130, 142);
	font-family: var(--font-family-headings);
	font-size: 1.375rem;
	font-weight: 400;
	line-height: 1.2;
}

.nl-tertiary-nav__card {
	background: var(--colors-brand-1);
	color: var(--colors-neutral-black);
	padding: calc(var(--spacing-5) + var(--spacing-6)) var(--spacing-6);
	clip-path: polygon(0 0, 100% 0, 100% 100%, 5rem 100%, 0 calc(100% - 2rem));
}

.nl-tertiary-nav__card .wp-block-list {
	margin: 0;
	padding: 0;
	list-style: none;
}

.nl-tertiary-nav__card .wp-block-list li {
	margin: 0;
	padding: 0;
}

.nl-tertiary-nav__card a {
	display: flex;
	justify-content: space-between;
	align-items: flex-start;
	column-gap: var(--spacing-5);
	padding-block: var(--spacing-3);
	color: var(--colors-neutral-black);
	font-size: 0.875rem;
	font-weight: 500;
	letter-spacing: 0.05em;
	line-height: 1.45;
	text-decoration: none;
}

.nl-tertiary-nav__card a::after {
	content: "";
	display: block;
	inline-size: 1rem;
	block-size: 1rem;
	flex-shrink: 0;
	margin-block-start: 0.2em;
	background-color: currentColor;
	mask: var(--arrow-icon) center / contain no-repeat;
	transition: transform var(--animation-speed-slow) var(--cubic-bezier);
}

/* The content-link underline reaches to the arrow here (the link box
 * spans the full row); the label span carries the hover line instead. */
.entry-content .nl-tertiary-nav .nl-tertiary-nav__card .wp-block-list a {
	background: none;
	text-decoration: none;
}

/* The hover line under the text only, in the theme's idiom: it grows in
 * from the left and collapses out to the right, exactly like the footer
 * links. */
.nl-tertiary-nav__label {
	background: linear-gradient(0deg, currentColor, currentColor) no-repeat right bottom / 0 1px;
	transition: background-size var(--animation-speed-slow) var(--cubic-bezier);
}

.nl-tertiary-nav__card a:hover .nl-tertiary-nav__label {
	background-size: 100% 1px;
	background-position-x: left;
}

/* Items typed in the editor carry no label span: rich text cannot come
 * from a block template, so the span's hover line cannot exist there
 * (the block's list items are plain linked text). Those links, and
 * only those (:has() keeps pattern instances on the span rules), draw
 * the line with text-decoration instead: it hugs the text exactly
 * like the span's line, the 0.4em offset lands it on the text bottom
 * where the span's line sits, and the transparent-to-currentColor fade
 * runs over the same duration. The official site underlines its labels
 * the same text-decoration way (there without any animation).
 *
 * The .entry-content chain is not optional: the content-link reset
 * above sets text-decoration: none at (0,4,1), so the fallback needs
 * the full chain plus :has() to out-rank it. */
.entry-content .nl-tertiary-nav .nl-tertiary-nav__card .wp-block-list a:not(:has(.nl-tertiary-nav__label)) {
	text-decoration: underline;
	text-decoration-thickness: 1px;
	text-underline-offset: 0.4em;
	text-decoration-color: transparent;
	transition: text-decoration-color var(--animation-speed-slow) var(--cubic-bezier);
}

.entry-content .nl-tertiary-nav .nl-tertiary-nav__card .wp-block-list a:not(:has(.nl-tertiary-nav__label)):hover {
	text-decoration-color: currentColor;
}

.nl-tertiary-nav__card a:hover::after {
	transform: translateX(var(--spacing-1));
}

/* The content lists carry square ::before markers (see the Base list
 * styles). The pattern lists are not content lists: suppress the
 * squares in the tertiary navigation, the link cards and the related
 * news, where every whole card is an li of the post template and the
 * square would land outside the card's top-left corner. */
.nl-tertiary-nav__card ul li::before,
.nl-link-card ul li::before,
.nl-related__query .wp-block-post-template li::before {
	content: none;
}

/* Related news: the gray band with recent-news cards from the article
 * pages on nikhef.nl. The cards come from a core Query loop (see
 * patterns/related-news.php): the three newest posts, the current post
 * excluded via the block's excludeCurrent setting. */
.nl-related {
	padding-block: var(--spacing-8);
	background: var(--colors-neutral-10);
}

.nl-related > .wp-block-heading {
	color: var(--colors-brand-3);
	font-family: var(--font-family-headings);
	font-size: 2rem;
	font-weight: 400;
	line-height: 1.2;
	margin: 0 0 var(--spacing-8);
}

/* The official grid: three columns with 1.5rem gutters, two from 75rem
 * down, a single column below 55rem. Core renders the loop as flex (see
 * the displayLayout attr on the query); this grid wins on specificity. */
.nl-related__query .wp-block-post-template {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--spacing-6);
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Core's flex post-template emits
 * .wp-block-post-template.is-flex-container.is-flex-container.columns-3>li
 * { width: calc(33.33% - .833em) } inline (after style.css). In a flex
 * row that sizes the columns correctly, but our grid above turns the
 * percentage into a fraction of the grid CELL and shrinks every card
 * to a sliver. The doubled class chain matches core's specificity and
 * the .nl-related__query scope outranks it, order-independent. */
.nl-related__query .wp-block-post-template.is-flex-container.is-flex-container.columns-3 > li {
	width: auto;
}

@media (max-width: 75rem) {
	.nl-related__query .wp-block-post-template {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 55rem) {
	.nl-related__query .wp-block-post-template {
		grid-template-columns: 1fr;
	}
}

.nl-related__card {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--spacing-6);
	background: var(--colors-neutral-white);
	border: 1px solid var(--colors-neutral-20);
	border-radius: var(--border-radius-1);
	box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.22);
	padding: var(--spacing-2);
	padding-block-end: var(--spacing-5);
	transition: box-shadow var(--animation-speed-fast) var(--cubic-bezier);
}

/* Everything but the picture sits in the 1rem content inset, like the
 * card pattern. */
.nl-related__card > :not(.wp-block-post-featured-image) {
	margin-inline: calc(var(--spacing-5) - var(--spacing-2)) !important;
}

/* The image zooms on hover, clipped by its own frame like on nikhef.nl. */
.nl-related__card .wp-block-post-featured-image {
	margin: 0;
	overflow: hidden;
	border-radius: var(--border-radius-1);
}

.nl-related__card img {
	border-radius: var(--border-radius-1);
	transition: transform var(--animation-speed-slow) cubic-bezier(0.1, 0.1, 0, 1);
}

/* The categories render as chips, in the family look. The 2rem card gap
 * is pulled back to 1rem here so the chips hug the title like official. */
.nl-related__card .wp-block-post-terms {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-2);
	margin: 0;
	margin-block-end: calc(var(--spacing-4) - var(--spacing-6));
	font-size: 0.875rem;
}

.entry-content .nl-related .nl-related__card .wp-block-post-terms a {
	display: inline-block;
	margin: 0;
	padding: 0.25rem 1.5rem 0.25rem 0.75rem;
	background: var(--colors-brand-3);
	color: var(--colors-neutral-white);
	font-family: var(--font-family-headings);
	font-size: 0.875rem;
	line-height: 1.4;
	text-decoration: none;
	clip-path: polygon(0 0, calc(100% - 0.5rem) 0, 100% 100%, 0% 100%);
}

/* Official card titles read 28px Roboto (not the monospace heading face). */
.nl-related__card .wp-block-post-title {
	margin: 0;
	font-family: var(--font-family-default);
	font-size: 1.75rem;
	font-weight: 400;
	line-height: 1.2;
}

/* Four classes: the content-link rule is 0,4,1 and would otherwise paint
 * its permanent gradient underline over the card title. */
.entry-content .nl-related .nl-related__card .wp-block-post-title a {
	color: inherit;
	background: none;
	text-decoration: underline;
	text-decoration-color: transparent;
	text-underline-offset: var(--text-underline-offset);
	transition: text-decoration-color var(--animation-speed-slow) var(--cubic-bezier);
}

/* The stretched overlay makes the whole card clickable, the same trick
 * as the card pattern. */
.nl-related__card .wp-block-post-title a::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
}

/* The arrow after the title, official size: 1.25rem with a 1rem gap. */
.nl-related__card .wp-block-post-title a::after {
	content: "";
	display: inline-block;
	inline-size: 1.25rem;
	block-size: 1.25rem;
	margin-inline-start: var(--spacing-4);
	vertical-align: middle;
	background-color: currentColor;
	mask: var(--arrow-icon) center / contain no-repeat;
	transition: translate var(--animation-speed-slow) var(--cubic-bezier);
}

@media (hover: hover) {
	.nl-related__card:hover .wp-block-post-title a {
		text-decoration-color: currentColor;
	}

	.nl-related__card:hover .wp-block-post-title a::after {
		translate: 0.25rem 0;
	}

	.nl-related__card:hover .wp-block-post-featured-image img {
		transform: scale(1.25);
	}

	.nl-related__card:hover {
		box-shadow: 0 12px 24px -8px rgba(0, 0, 0, 0.15), 0 4px 8px -2px rgba(0, 0, 0, 0.1);
	}
}

.nl-related__card .wp-block-post-date {
	margin: 0;
	margin-block-start: auto;
	font-family: var(--font-family-headings);
	font-size: 0.875rem;
	color: var(--colors-neutral-30);
}

/* The block variant of the related news band: the pattern group gets its
 * centered content column from core's constrained layout; the block root
 * is not a group, so the same geometry comes from these two rules (the
 * wide query child escapes to the official 84rem, like alignwide does
 * inside a constrained group). */
.wp-block-nikhef-lite-related-news.nl-related > * {
	max-width: var(--max-content-width);
	margin-inline: auto;
}

.wp-block-nikhef-lite-related-news.nl-related > .alignwide {
	max-width: calc(var(--max-small-container-width) * 1.2);
}

/* --------------------------------------------------------------------------
 * Section (the block variant) and Research cards grid: two blocks that
 * approximate the official full-width content bands
 * (www.nikhef.nl/en/work-and-study/working-at-nikhef/) and the official
 * research-themes grid (www.nikhef.nl/en/research/research-themes-nikhef/).
 * ------------------------------------------------------------------------ */

/* The Section band: the background spans the viewport, the content stays
 * in its column. Measured on nikhef.nl: 64px block padding, 840px
 * content column, stacked bands with no gap between them. The colors
 * come from the color panel (has-*-background-color classes). */
.nl-section {
	padding-block: var(--spacing-8);
}

.nl-section > * {
	max-width: calc(var(--max-content-width) - 2rem);
	margin-inline: auto;
}

.nl-section > .alignwide {
	max-width: calc(var(--max-small-container-width) * 1.2);
}

.nl-section > .alignfull {
	max-width: none;
	margin-inline: 0;
}

/* Lists keep their column inside a band. The shared .entry-content ul
   reset zeroes the margin, which would otherwise pin a list to the
   band's left edge instead of the centered content column. */
.nl-section > ul,
.nl-section > ol {
	margin-inline: auto;
}

/* The bands breathe a little more between paragraphs than plain body
 * copy: the official sections keep 24px between paragraphs (measured),
 * against the site's default 16px. */
.nl-section p:not(.is-style-tag):not(.entry-meta) {
	margin-block: 0 var(--spacing-5);
}

.nl-section p:not(.is-style-tag):not(.entry-meta):last-child {
	margin-block-end: 0;
}

/* The Research cards grid: the official research-themes layout. Measured
 * on both the front page and the research themes page: two columns of
 * 696px cards at a 1440 viewport, 16px gutters, 16px edge inset, and a
 * 0.5rem card radius everywhere (the official cards are simply large,
 * which makes the same corner read rounder than on a small card). */
.nl-research-card-grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: var(--spacing-4);
	padding-inline: var(--spacing-10);
}

@media (max-width: 55rem) {
	.nl-research-card-grid {
		grid-template-columns: 1fr;
	}
}

/* The compact style of the grid: four columns of small cards (people
   pages built with Card blocks) instead of two large research theme
   cards. Used with the alignwide width inside a Section band. */
.nl-research-card-grid.is-style-compact {
	grid-template-columns: repeat(4, 1fr);
	padding-inline: var(--spacing-4);
}

@media (max-width: 55rem) {
	.nl-research-card-grid.is-style-compact {
		grid-template-columns: repeat(2, 1fr);
	}
}
