/**
 * GEBA Group — components.
 *
 * The block style variations registered in functions.php, plus the shared
 * card and control surfaces the patterns compose from. Each rule targets a
 * core block's `is-style-*` class rather than a bespoke wrapper, so a section
 * built in the editor styles itself.
 */

/* ---------------------------------------------------------------- section */

.wp-block-group.is-style-geba-section {
	padding-block: var(--section-y);
}

.wp-block-group.is-style-geba-section-dark {
	padding-block: var(--section-y);
	background: var(--surface-inverse);
	color: var(--text-inverse);
}

.wp-block-group.is-style-geba-section-dark :is(h1, h2, h3, h4, h5, h6) {
	color: var(--text-inverse);
}

.wp-block-group.is-style-geba-section-dark a:not(.wp-block-button__link) {
	color: var(--link-color-inverse);
}

@media (max-width: 1000px) {
	.wp-block-group.is-style-geba-section,
	.wp-block-group.is-style-geba-section-dark {
		padding-block: var(--section-y-sm);
	}
}

@media (max-width: 900px) {
	.wp-block-group.is-style-geba-section,
	.wp-block-group.is-style-geba-section-dark {
		padding-block: var(--section-y-xs);
	}
}

/* ------------------------------------------------------------------- card */

.wp-block-group.is-style-geba-card {
	padding: var(--pad-card);
	background: var(--surface-card);
	border: var(--hairline) solid var(--border-subtle);
	border-radius: var(--radius-card);
	box-shadow: var(--shadow-sm);
	transition: var(--transition-card);
}

.wp-block-group.is-style-geba-card:hover {
	transform: translateY(var(--lift-card));
	box-shadow: var(--shadow-lg);
	border-color: var(--border-default);
}

@media (max-width: 900px) {
	.wp-block-group.is-style-geba-card {
		padding: var(--pad-card-sm);
	}
}

/* ---------------------------------------------------------------- eyebrow */

/*
 * A section's kicker. Set on a heading so the document outline stays honest;
 * the visual demotion is entirely typographic.
 */
.wp-block-heading.is-style-geba-eyebrow {
	font-size: var(--type-eyebrow-size);
	font-weight: var(--weight-medium);
	line-height: var(--leading-normal);
	letter-spacing: var(--tracking-eyebrow);
	text-transform: uppercase;
	color: var(--text-brand);
	margin-block-end: var(--space-3);
}

/* ------------------------------------------------------------------- lead */

.wp-block-paragraph.is-style-geba-lead {
	font-size: var(--text-xl);
	font-weight: var(--weight-light);
	line-height: var(--leading-snug);
	color: var(--text-secondary);
	max-width: var(--container-text);
}

/* ------------------------------------------------------------------ media */

.wp-block-image.is-style-geba-media img {
	border-radius: var(--radius-media);
}

/* -------------------------------------------------------------- accordion */

/*
 * core/details, styled as the reference's Accordion. No custom block: the
 * open/closed state is the browser's, which is one less thing to get wrong
 * for keyboard and screen-reader users.
 */
.wp-block-details.is-style-geba-accordion {
	border-block-end: var(--hairline) solid var(--border-subtle);
	padding-block: var(--space-5);
}

.wp-block-details.is-style-geba-accordion > summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	cursor: pointer;
	list-style: none;
	font-size: var(--text-lg);
	font-weight: var(--weight-medium);
	color: var(--text-primary);
	transition: var(--transition-control);
}

.wp-block-details.is-style-geba-accordion > summary::-webkit-details-marker {
	display: none;
}

.wp-block-details.is-style-geba-accordion > summary:hover {
	color: var(--text-brand);
}

/* The chevron: a rotating caret drawn in CSS rather than an icon request. */
.wp-block-details.is-style-geba-accordion > summary::after {
	content: "";
	flex: none;
	width: 0.6em;
	height: 0.6em;
	border-right: var(--rule-strong) solid currentColor;
	border-bottom: var(--rule-strong) solid currentColor;
	transform: rotate(45deg) translate(-0.15em, -0.15em);
	transition: transform var(--dur-base) var(--ease-standard);
}

.wp-block-details.is-style-geba-accordion[open] > summary::after {
	transform: rotate(225deg) translate(-0.15em, -0.15em);
}

.wp-block-details.is-style-geba-accordion > *:not(summary) {
	padding-block-start: var(--space-4);
	color: var(--text-secondary);
	max-width: var(--container-text);
}

/* -------------------------------------------------- core layout gaps */

/*
 * The two core layouts a pattern places without a class of its own. Every
 * other flex or grid row here is a `geba-*` component that states its own gap;
 * these two were living off `styles.spacing.blockGap`, which is `0` now — see
 * the note under "layout containers" in sections.css.
 *
 * `--space-6` because that is what `blockGap` was: the point of the change is
 * to make the value visible, not to move it.
 */
.wp-block-buttons {
	gap: var(--space-6);
}

.wp-block-columns {
	gap: var(--space-6);
}

/* --------------------------------------------------------------- buttons */

/*
 * theme.json sets the fill button's colour, radius and padding. What it has
 * no slot for is the press state and the transition.
 */
.wp-block-button__link {
	transition: var(--transition-control);
}

.wp-block-button__link:active {
	transform: scale(var(--press-scale));
	translate: 0 0;
}

.wp-block-button.is-style-outline .wp-block-button__link {
	border-color: var(--border-strong);
	color: var(--text-primary);
}

.wp-block-button.is-style-outline .wp-block-button__link:hover {
	border-color: var(--geba-blue);
	color: var(--text-brand);
	background: var(--surface-brand-soft);
}

/*
 * The trailing arrow. Drawn as a rotated border pair, the same way the
 * accordion's caret is: the sprite's `arrow-right` would have to live inside
 * the button's link, and a pattern cannot put a block there.
 *
 * A modifier class rather than a style variation, because the reference puts
 * the arrow on pills and on square controls alike — the shape and the arrow are
 * independent choices, and a block can only carry one `is-style-*`.
 */
.wp-block-button.geba-button--arrow .wp-block-button__link,
.wp-block-read-more.is-style-geba-arrow-link {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
}

.wp-block-button.geba-button--arrow .wp-block-button__link::after,
.wp-block-read-more.is-style-geba-arrow-link::after {
	content: "";
	flex: none;
	width: 0.42em;
	height: 0.42em;
	border-top: var(--rule-strong) solid currentColor;
	border-right: var(--rule-strong) solid currentColor;
	transform: rotate(45deg);
	transition: transform var(--dur-base) var(--ease-out-expo);
}

.wp-block-button.geba-button--arrow .wp-block-button__link:hover::after,
.wp-block-read-more.is-style-geba-arrow-link:hover::after {
	transform: rotate(45deg) translate(0.12em, -0.12em);
}

/*
 * Reversed fill: white on the dark hero, against the brand-filled sibling.
 * Compounded with the shape classes so it outranks whichever of them sets a
 * background — those rules are the same specificity and come later in the file.
 */
.wp-block-button.geba-button--light .wp-block-button__link,
.wp-block-button.is-style-geba-pill.geba-button--light .wp-block-button__link,
.wp-block-button.is-style-geba-pill-outline.geba-button--light .wp-block-button__link {
	background: var(--ink-0);
	border-color: var(--ink-0);
	color: var(--geba-black);
}

.wp-block-button.geba-button--light .wp-block-button__link:hover,
.wp-block-button.is-style-geba-pill.geba-button--light .wp-block-button__link:hover,
.wp-block-button.is-style-geba-pill-outline.geba-button--light .wp-block-button__link:hover {
	background: var(--ink-100);
	border-color: var(--ink-100);
	color: var(--geba-black);
}

/* --------------------------------------------------------- pill buttons */

/*
 * Both shapes share one box. They did not: the filled pill and the outline one
 * had different padding and font sizes, so a pair of them side by side came out
 * different heights and widths. The filled one carries a border of its own
 * colour rather than none, so the border box matches too.
 */
.wp-block-button.is-style-geba-pill .wp-block-button__link,
.wp-block-button.is-style-geba-pill-outline .wp-block-button__link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 48px;
	padding: 15px 26px 13px;
	border: var(--hairline) solid transparent;
	border-radius: var(--radius-pill);
	font-size: var(--text-sm);
	font-weight: var(--weight-medium);
	line-height: 1;
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	/*
	 * The lift is its own property so the press scale can compose with it —
	 * `transform` is a single slot, and `:active` would otherwise cancel it.
	 */
	transition:
		background-color var(--dur-base) var(--ease-standard),
		border-color var(--dur-base) var(--ease-standard),
		color var(--dur-base) var(--ease-standard),
		box-shadow var(--dur-slow) var(--ease-out-expo),
		translate var(--dur-slow) var(--ease-out-expo);
}

.wp-block-button.is-style-geba-pill .wp-block-button__link {
	background: var(--geba-blue);
	border-color: var(--geba-blue);
	color: var(--text-on-brand);
}

.wp-block-button.is-style-geba-pill-outline .wp-block-button__link {
	background: transparent;
	border-color: var(--border-inverse);
	color: var(--text-inverse);
}

/*
 * Hover: rise a little and deepen the shadow, the same gesture the cards make.
 * `translate` rather than `transform` so `:active`'s press scale still lands.
 */
.wp-block-button.is-style-geba-pill .wp-block-button__link:hover,
.wp-block-button.is-style-geba-pill-outline .wp-block-button__link:hover {
	translate: 0 var(--lift-card);
}

.wp-block-button.is-style-geba-pill .wp-block-button__link:hover {
	background: var(--blue-600);
	border-color: var(--blue-600);
	box-shadow: var(--shadow-brand);
}

/*
 * The hover's edge and the read-more link's hover below are the band's own text
 * colour rather than white: the same thing on a dark band, and still visible
 * on a band that redefines the inverse tokens to go light.
 */
.wp-block-button.is-style-geba-pill-outline .wp-block-button__link:hover {
	background: var(--surface-glass);
	border-color: var(--text-inverse);
	box-shadow: var(--shadow-md);
}

.wp-block-button.geba-button--dark .wp-block-button__link:hover {
	box-shadow: var(--shadow-md);
}

.wp-block-button.geba-button--light .wp-block-button__link:hover {
	box-shadow: var(--shadow-lg);
}

/* --------------------------------------------------------- print button */

/*
 * `geba/print-button` is a real `<button>`, so it inherits none of the pill's
 * box from `.wp-block-button__link` and none of the browser's button styling is
 * wanted either. The shape is the outline pill's, restated for a light ground:
 * `is-style-geba-pill-outline` is written in inverse colours for the dark hero
 * and would be invisible in the aside.
 *
 * Full width because it is the only thing in its card, and a lone control
 * floating left of a box looks unfinished.
 */
.geba-print-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	width: 100%;
	min-height: 48px;
	margin: 0;
	padding: 15px 26px 13px;
	background: transparent;
	border: var(--hairline) solid var(--border-strong);
	border-radius: var(--radius-pill);
	color: var(--text-primary);
	font-family: inherit;
	font-size: var(--text-sm);
	font-weight: var(--weight-medium);
	line-height: 1;
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	cursor: pointer;
	transition:
		background-color var(--dur-base) var(--ease-standard),
		border-color var(--dur-base) var(--ease-standard),
		color var(--dur-base) var(--ease-standard);
}

.geba-print-button:hover {
	background: var(--surface-brand-soft);
	border-color: var(--geba-blue);
	color: var(--text-brand);
}

.geba-print-button:active {
	transform: scale(var(--press-scale));
}

.geba-print-button__icon {
	display: block;
	flex: none;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* ------------------------------------------------------------ telephones */

/*
 * Two telephone numbers in one contact tile, each its own link — the German
 * site gives a German number beside the Slovenian one. The tile cannot be the
 * link any more, so the numbers carry the hover. The dot between them is
 * drawn, with empty alternative text, so a screen reader reads two numbers
 * rather than "middle dot"; inline-block keeps it out of the underline.
 */
.geba-phones {
	display: flex;
	flex-wrap: wrap;
	column-gap: var(--space-2);
}

.geba-phones a {
	color: inherit;
	text-decoration: none;
}

.geba-phones a:hover,
.geba-phones a:focus-visible {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.geba-phones a + a::before {
	content: "·" / "";
	display: inline-block;
	margin-inline-end: var(--space-2);
}

/* ------------------------------------------------------------ read more */

.wp-block-read-more.is-style-geba-arrow-link {
	font-size: var(--text-xs);
	font-weight: var(--weight-medium);
	letter-spacing: var(--tracking-eyebrow);
	text-transform: uppercase;
	text-decoration: none;
	color: var(--blue-300);
	transition: color var(--dur-base) var(--ease-standard);
}

.wp-block-read-more.is-style-geba-arrow-link:hover {
	color: var(--text-inverse);
}

/* On a dark section an outline button needs the inverse hairline. */
.is-style-geba-section-dark .wp-block-button.is-style-outline .wp-block-button__link {
	border-color: var(--border-inverse);
	color: var(--text-inverse);
}

.is-style-geba-section-dark
	.wp-block-button.is-style-outline
	.wp-block-button__link:hover {
	border-color: var(--geba-blue);
	color: var(--text-inverse);
	background: var(--surface-glass);
}

/*
 * A read-more drawn as an outline control rather than a link. core/read-more
 * renders a bare <a>, so the button shape has to be described here; it is the
 * only block that can carry a per-post permalink inside a query loop.
 */
.wp-block-read-more.is-style-geba-outline-link {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	padding: 14px var(--pad-control-x) 10px;
	border: var(--hairline) solid var(--border-strong);
	border-radius: var(--radius-control);
	font-size: var(--text-sm);
	font-weight: var(--weight-medium);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	text-decoration: none;
	color: var(--text-primary);
	transition: var(--transition-control);
}

.wp-block-read-more.is-style-geba-outline-link::after {
	content: "";
	flex: none;
	width: 0.42em;
	height: 0.42em;
	border-top: var(--rule-strong) solid currentColor;
	border-right: var(--rule-strong) solid currentColor;
	transform: rotate(45deg);
	transition: transform var(--dur-base) var(--ease-out-expo);
}

.wp-block-read-more.is-style-geba-outline-link:hover {
	border-color: var(--geba-blue);
	color: var(--text-brand);
	background: var(--surface-brand-soft);
}

.wp-block-read-more.is-style-geba-outline-link:hover::after {
	transform: rotate(45deg) translate(0.12em, -0.12em);
}

/* ------------------------------------------------------------ filter pills */

/*
 * `geba/filter-pills`. Each pill is a term archive link, so the active one is
 * whichever archive is being viewed — there is no JavaScript state to style
 * against, only `aria-current`, which the block sets alongside `.is-active`.
 */
/*
 * Every pill carries this bar's own fragment, so an archive opens at the bar
 * the visitor just used rather than at the top of the page. The margin holds
 * that landing clear of the sticky header: the root's `scroll-padding-top` is
 * the same offset, but it is not applied to the fragment scroll a browser
 * performs on load, and this is.
 */
.geba-filter-pills {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  justify-content: center;
  gap: var(--space-3);
  scroll-margin-block-start: calc(var(--header-h-scrolled) + var(--space-8));
}

/*
 * The archive's bar: the pills, and under them the action that prints the whole
 * binder. A column so the pills keep the full measure to centre across — laid
 * out as a row they would shift left by the button's width, which is a change
 * to how the archive reads for the sake of a secondary control.
 */
.geba-project-archive__bar {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.geba-project-archive__bar .geba-print-button--quiet {
  align-self: flex-end;
  width: auto;
  min-height: 36px;
  padding: 9px 18px 7px;
  border-color: var(--border-subtle);
  font-size: var(--text-2xs);
  color: var(--text-muted);
}

.geba-project-archive__bar .geba-print-button--quiet:hover {
  border-color: var(--geba-blue);
  color: var(--text-brand);
}

.geba-filter-pills__pill {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 9px 18px 7px;
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: 1;
  text-decoration: none;
  transition:
    background var(--dur-base) var(--ease-standard),
    border-color var(--dur-base) var(--ease-standard),
    color var(--dur-base) var(--ease-standard);
}

.geba-filter-pills__pill:hover,
.geba-filter-pills__pill:focus-visible {
  border-color: var(--border-brand);
  color: var(--text-primary);
}

.geba-filter-pills__pill.is-active {
  background: var(--surface-brand);
  border-color: var(--surface-brand);
  color: var(--text-on-brand);
}

/* -------------------------------------------------------------- pagination */

/*
 * `core/query-pagination`. Core renders the current page as a bare `<span>`
 * and the rest as links, so the two are styled together and the current one
 * is filled in — the same pair of states as a filter pill above.
 */
.geba-pagination {
  gap: var(--space-2);
  margin-block-start: var(--space-12);
}

.geba-pagination .page-numbers,
.geba-pagination .wp-block-query-pagination-previous,
.geba-pagination .wp-block-query-pagination-next {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  min-height: 40px;
  padding: 8px 14px 6px;
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: 1;
  text-decoration: none;
  transition:
    background var(--dur-base) var(--ease-standard),
    border-color var(--dur-base) var(--ease-standard),
    color var(--dur-base) var(--ease-standard);
}

.geba-pagination a:hover,
.geba-pagination a:focus-visible {
  border-color: var(--border-brand);
  color: var(--text-primary);
}

.geba-pagination .page-numbers.current {
  background: var(--surface-brand);
  border-color: var(--surface-brand);
  color: var(--text-on-brand);
}

.geba-pagination .page-numbers.dots {
  background: none;
  border-color: transparent;
}

/* ---------------------------------------------------------- field gallery */

/*
 * `geba/field-gallery`. The column count is intrinsic: the block writes the
 * minimum column width it was given into `--geba-gallery-min`, and the same
 * `auto-fit` the reference uses everywhere does the rest.
 */
.geba-field-gallery {
  display: grid;
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(100%, var(--geba-gallery-min, 280px)), 1fr)
  );
  gap: var(--grid-gap);
}

.geba-field-gallery__item {
  margin: 0;
  overflow: hidden;
  border-radius: var(--radius-media);
  box-shadow: var(--shadow-sm);
}

/*
 * A ratio rather than the reference's fixed 240px height.
 *
 * The reference's tiles sit in a row of known width; ours are grid cells whose
 * width follows the viewport, so a fixed height gave each tile an arbitrary
 * shape — and core sizes its lightbox container from the tile's shape, which
 * left a 3:2 photograph opening into a 6:5 box at two thirds of the size it
 * could have been. 3:2 is what the photography is.
 */
.geba-field-gallery__item img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  transition: scale var(--dur-slow) var(--ease-out-expo);
}

.geba-field-gallery__item:hover img {
  scale: 1.04;
}

/*
 * The enlarged photograph, whole.
 *
 * A tile is cropped to a fixed height, and core sizes its lightbox container
 * from the tile's *visible* box — so a 3:2 photograph opened from a 6:5 tile
 * came back cropped to 6:5, cut on both sides. Core writes `object-fit: cover`
 * onto the enlarged image as an inline style, which is why this needs
 * `!important`: there is no selector that beats an inline declaration.
 *
 * `contain` inside a container that is still the tile's shape means the photo
 * is a little smaller than the container. Nothing shows: the lightbox figure
 * has no background of its own. See ADR-034.
 */
.wp-lightbox-overlay figure img {
  object-fit: contain !important;
}

/* ------------------------------------------------------------ stat counter */

/*
 * `geba/stat-counter`. Straight from the reference's `StatCounter`: a black
 * numeral in brand blue over a small upper-case label, tabular figures so the
 * count-up does not make the line jump about while it runs.
 */
.geba-stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: flex-start;
  text-align: left;
}

.geba-stat--center {
  align-items: center;
  text-align: center;
}

.geba-stat__value {
  font-size: var(--text-5xl);
  font-weight: var(--weight-black);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
  color: var(--geba-blue);
  white-space: nowrap;
}

.geba-stat--dark .geba-stat__value {
  color: var(--blue-300);
}

.geba-stat__label {
  font-size: var(--text-sm);
  font-weight: var(--weight-light);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}

.geba-stat--dark .geba-stat__label {
  color: var(--text-inverse-muted);
}

/* ----------------------------------------------------------------- carousel */

/*
 * `geba/carousel`. Swiper's own stylesheet does the sliding; this file gives
 * the block the reference's controls and, importantly, states what the markup
 * looks like *before* Swiper has run.
 *
 * That pre-init state is not a loading placeholder — it is the whole component
 * without JavaScript: a native scroll-snap rail, which is exactly what the
 * reference's project row is. Swiper's classes only take over once it has
 * added `.swiper-initialized`.
 */
.geba-carousel {
  position: relative;

  /* Swiper reads these for its pagination; setting them beats overriding it. */
  --swiper-pagination-color: var(--surface-brand);
  --swiper-pagination-bullet-inactive-color: var(--border-strong);
  --swiper-pagination-bullet-inactive-opacity: 1;
  --swiper-pagination-bullet-horizontal-gap: 4px;
  --swiper-pagination-bullet-width: 8px;
  --swiper-pagination-bullet-height: 8px;
}

.geba-carousel .swiper:not(.swiper-initialized) .swiper-wrapper {
  gap: var(--geba-carousel-gap, 24px);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.geba-carousel .swiper:not(.swiper-initialized) .swiper-slide {
  flex: 0 0
    calc(
      (
          100% - (var(--geba-carousel-per-view, 3) - 1) *
            var(--geba-carousel-gap, 24px)
        ) / var(--geba-carousel-per-view, 3)
    );
  scroll-snap-align: start;
}

/* The editor shows the slides as a plain rail; nothing hides any of them. */
.geba-carousel--editing .swiper-wrapper {
  display: flex;
  gap: var(--geba-carousel-gap, 24px);
  overflow-x: auto;
}

.geba-carousel__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-block-start: var(--space-8);
}

.geba-carousel__pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  width: auto;
}

.geba-carousel__nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-circle);
  color: var(--text-secondary);
  cursor: pointer;
  transition:
    background var(--dur-base) var(--ease-standard),
    border-color var(--dur-base) var(--ease-standard),
    color var(--dur-base) var(--ease-standard),
    scale var(--dur-fast) var(--ease-out-expo);
}

/* One arrow in the sprite; the previous button is the same shape mirrored. */
.geba-carousel__nav--prev svg {
  scale: -1 1;
}

.geba-carousel__nav:hover,
.geba-carousel__nav:focus-visible {
  background: var(--surface-brand);
  border-color: var(--surface-brand);
  color: var(--text-on-brand);
}

.geba-carousel__nav:active {
  scale: var(--press-scale);
}

.geba-carousel__nav.swiper-button-disabled {
  opacity: 0.4;
  pointer-events: none;
}

/*
 * The gallery variant is photographs, so its slides are sized by the block
 * rather than by whatever each image's own proportions happen to be — five
 * stock photographs at four aspect ratios would otherwise make a ragged row.
 */
.geba-carousel--gallery .swiper-slide figure {
  margin: 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius-media);
  box-shadow: var(--shadow-sm);
}

.geba-carousel--gallery .swiper-slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  cursor: zoom-in;
}

/* ----------------------------------------------------------------- lightbox */

/*
 * The gallery variant's zoom. A native `<dialog>`, so the modal behaviour —
 * focus containment, Escape, inertness of the page behind — is the browser's
 * rather than ours, for the same reason the mobile navigation is core's.
 */
.geba-lightbox {
  position: fixed;
  max-width: min(92vw, 1200px);
  max-height: 92vh;
  padding: 0;
  background: none;
  border: 0;
  overflow: visible;
}

.geba-lightbox::backdrop {
  background: color-mix(in srgb, var(--geba-black) 84%, transparent);
}

.geba-lightbox__image {
  display: block;
  width: 100%;
  height: auto;
  max-height: 92vh;
  object-fit: contain;
  border-radius: var(--radius-media);
}

.geba-lightbox__close {
  position: absolute;
  inset-block-start: calc(var(--space-4) * -1);
  inset-inline-end: calc(var(--space-4) * -1);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: var(--ink-0);
  border: 0;
  border-radius: var(--radius-circle);
  color: var(--text-primary);
  cursor: pointer;
}

/* ------------------------------------------------------------------ forms */

/*
 * Contact Form 7's own stylesheet is never enqueued (see the plugin's
 * inc/forms.php), so everything a field looks like is here, in the design's
 * tokens. The form templates in the plugin's cf7/ directory write the rows and
 * the labels; CF7 contributes the controls and its own wrappers, which is why
 * the selectors below reach for `.wpcf7-*` in a few places.
 */
.geba-form .wpcf7-form > * + * {
  margin-block-start: var(--space-5);
}

.geba-form__row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5);
}

@media (max-width: 640px) {
  .geba-form__row {
    grid-template-columns: 1fr;
  }
}

.geba-form__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.geba-form__field label {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text-secondary);
}

/* The asterisk is decoration; `aria-required` on the control carries the fact. */
.geba-form__required {
  border: 0;
  color: var(--text-brand);
  text-decoration: none;
}

.geba-form .wpcf7-form-control-wrap {
  display: block;
}

.geba-form input[type="text"],
.geba-form input[type="tel"],
.geba-form input[type="email"],
.geba-form input[type="file"],
.geba-form select,
.geba-form textarea {
  width: 100%;
  min-height: 48px;
  padding: 12px 16px;
  background: var(--surface-muted);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-field);
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  line-height: 1.4;
  transition: var(--transition-control);
}

.geba-form textarea {
  min-height: 160px;
  resize: vertical;
}

.geba-form input[type="file"] {
  padding: 10px 16px;
  font-size: var(--text-sm);
}

.geba-form input::placeholder,
.geba-form textarea::placeholder {
  color: var(--text-faint);
}

.geba-form input:hover,
.geba-form select:hover,
.geba-form textarea:hover {
  border-color: var(--border-default);
}

/* The global :focus-visible rule already draws the ring; this adds the edge. */
.geba-form input:focus-visible,
.geba-form select:focus-visible,
.geba-form textarea:focus-visible {
  border-color: var(--border-brand);
}

.geba-form .wpcf7-not-valid {
  border-color: var(--danger-500);
}

.geba-form .wpcf7-not-valid-tip {
  display: block;
  margin-block-start: var(--space-1);
  color: var(--danger-500);
  font-size: var(--text-sm);
}

/* --------------------------------------------------------- consent & send */

.geba-form__consent .wpcf7-list-item {
  display: block;
  margin: 0;
}

.geba-form__consent label {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-light);
  cursor: pointer;
}

.geba-form__consent input[type="checkbox"] {
  flex: none;
  width: 20px;
  height: 20px;
  margin-block-start: 2px;
  accent-color: var(--geba-blue);
}

.geba-form__actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

/*
 * The send button is the filled pill of the rest of the site. It repeats the
 * pill's box rather than borrowing the class, because CF7 renders an <input>
 * and the pill styles are written against the button block's anchor.
 */
.geba-form__submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 15px 26px 13px;
  background: var(--geba-blue);
  border: var(--hairline) solid var(--geba-blue);
  border-radius: var(--radius-pill);
  color: var(--text-on-brand);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: 1;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  cursor: pointer;
  transition: var(--transition-control);
}

.geba-form__submit:hover {
  background: var(--blue-600);
  border-color: var(--blue-600);
  box-shadow: var(--shadow-brand);
}

.geba-form__submit:disabled {
  cursor: progress;
  opacity: 0.6;
}

.geba-form .wpcf7-spinner {
  margin: 0;
}

/* ------------------------------------------------------------- responses */

/*
 * CF7 keeps two elements that its own stylesheet hides and ours has to: the
 * live region that announces the result to a screen reader, and the response
 * box, which is in the markup from the first paint and empty until a
 * submission answers. `form.init` is the class CF7 puts on an untouched form.
 */
.geba-form .screen-reader-response {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  border: 0;
  word-wrap: normal !important;
}

/* CF7's wrapper around the form's hidden inputs; its own stylesheet hides it. */
.geba-form .hidden-fields-container {
  display: none;
}

.geba-form form.init .wpcf7-response-output,
.geba-form form.resetting .wpcf7-response-output,
.geba-form form.submitting .wpcf7-response-output,
.geba-form .wpcf7-response-output:empty {
  display: none;
}

.geba-form .wpcf7-response-output {
  margin: var(--space-5) 0 0;
  padding: var(--space-4) var(--space-5);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
}

.geba-form form.sent .wpcf7-response-output {
  background: var(--success-50);
  border-color: var(--success-500);
  color: var(--text-primary);
}

.geba-form form.invalid .wpcf7-response-output,
.geba-form form.failed .wpcf7-response-output,
.geba-form form.spam .wpcf7-response-output {
  background: var(--danger-50);
  border-color: var(--danger-500);
  color: var(--text-primary);
}

.geba-form__missing {
  padding: var(--space-4);
  background: var(--warning-50);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
}

/* ------------------------------------------------------------ breadcrumbs */

/*
 * Yoast writes the trail as nested spans with a separator between them; the
 * type comes from the eyebrow classes the pattern puts on the block, so all
 * this adds is how a link in the trail behaves.
 */
.geba-breadcrumbs a {
  color: inherit;
  text-decoration: none;
  opacity: 0.8;
  transition: opacity var(--dur-base) var(--ease-standard);
}

.geba-breadcrumbs a:hover {
  opacity: 1;
  text-decoration: underline;
  text-underline-offset: 0.25em;
}

.geba-breadcrumbs .breadcrumb_last {
  font-weight: var(--weight-medium);
}
