/**
 * GEBA Group — section components.
 *
 * The layout half of the patterns in `patterns/`. Everything here is keyed to a
 * `geba-*` class the pattern sets, and every value is a token: the reference
 * writes its sections as inline styles against the same custom properties, so
 * porting a band is a matter of moving those declarations into a class.
 *
 * Order follows the Domov screen top to bottom, since that is the screen most
 * of these were lifted from and the one they are read against.
 */

/* ------------------------------------------------------------------- icon */

.geba-icon {
  display: inline-flex;
  flex: none;
  line-height: 0;
  color: inherit;
}

.geba-icon svg {
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

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

/*
 * The kicker above a section heading. One paragraph — the reference sets a
 * rotating snowflake beside it, which the client asked to drop, and with the
 * icon gone the flex row it lived in had nothing left to arrange. The demotion
 * is entirely typographic, so the heading it introduces keeps its own level.
 */
.geba-eyebrow {
  margin: 0;
  font-size: var(--type-eyebrow-size);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-eyebrow);
  line-height: 1;
  text-transform: uppercase;
  color: var(--text-brand);
}

/*
 * The eyebrow is set solid, which is right while it is one line — the space
 * around it belongs to the heading group, not to the text. It wraps on a phone
 * (the home hero's runs to two lines), and 13px caps set 13px apart have no gap
 * between them at all. Scoped to the phone so a single-line eyebrow's box, and
 * therefore every section heading's position, is untouched everywhere else.
 */
@media (max-width: 900px) {
  .geba-eyebrow {
    line-height: var(--leading-normal);
  }
}

.geba-eyebrow--inverse {
  color: var(--blue-300);
}

/*
 * The three bands whose eyebrow is not inside a `.geba-section-heading`.
 *
 * Their column gap spaces the lead and the buttons below the title as well, so
 * it cannot simply be reduced. The eyebrow pulls itself back to the same
 * --space-3 step the section headings use, and nothing else in the column
 * moves.
 */
.geba-hero__content > .geba-eyebrow,
.geba-welcome__body > .geba-eyebrow {
  margin-block-end: calc(var(--space-3) - var(--space-4));
}

.geba-service-intro__body > .geba-eyebrow {
  margin-block-end: calc(var(--space-3) - var(--space-6));
}

/* --------------------------------------------------- layout containers */

/*
 * There is nothing here on purpose, and this note is why.
 *
 * `styles.spacing.blockGap` used to be `--space-6`, and core turned that one
 * value into four different things at once:
 *
 *   :root :where(.is-layout-flow)        > * { margin-block-start: 24px }
 *   :root :where(.is-layout-constrained) > * { margin-block-start: 24px }
 *   .is-layout-flex, .is-layout-grid         { gap: 24px }
 *   plus the editor's block-gap control default
 *
 * The first two are the ones this theme never wanted. Almost every container
 * here is a flex or grid row with a `gap` of its own, and core's margin landed
 * on top of that gap — every "the spacing is too big" report traced back to it.
 * Worse, `constrained` is what a section band and `post-content` are, so each
 * full-bleed band also started 24px below the one above it with page
 * background showing through the seam.
 *
 * It was answered twice with a blanket counter-rule — first a hand-kept list of
 * selectors, then `[class*="geba-"] ... > *`. Both were wide `> *` selectors
 * reaching into markup they knew nothing about, which is the thing to avoid.
 *
 * `blockGap` is now `0`, so core emits no margin at all and every space in this
 * theme is declared by the class that owns it. The three shapes that were
 * living off core's `gap` say so themselves now:
 *
 *   .wp-block-buttons          — components.css
 *   .geba-nav, .geba-header    — parts.css (they already did)
 *   .geba-page__content, .geba-post__body, .geba-job — the prose rhythm, below
 *
 * So: to change how far a section sits from its neighbour, edit that section's
 * own rule. Nothing global will fight you for it.
 */

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

/*
 * The step from an eyebrow to the title it introduces is one number,
 * --space-3, everywhere on the site.
 *
 * A section heading owns that step as its own column gap, so it has to be a
 * flex column rather than a flow container: `blockGap` is 0 (ADR-030), and a
 * flow container therefore puts nothing between the kicker and the heading.
 * The `--split` variant re-states `row` below, because this declaration beats
 * core's `:root :where(.is-layout-flex)` on source order.
 */
.geba-section-heading {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-block-end: var(--space-10);
}

.geba-section-heading :is(h2, h3) {
  margin: 0;
  font-size: var(--type-h2-size);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  text-wrap: pretty;
}

.geba-section-heading--center {
  max-width: 640px;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  text-align: center;
}

/*
 * Defaults to the light-band colour and inverts inside a dark one. It was
 * written the other way round, which made it white on white the first time a
 * light section used it.
 */
.geba-section-heading__lead {
  margin: 0;
  font-size: var(--type-body-size);
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-muted);
  text-wrap: pretty;
}

.geba-section--dark .geba-section-heading__lead {
  color: var(--text-inverse-muted);
}

/* The split variant puts the heading and its "see all" button on one rule. */
.geba-section-heading--split {
  flex-direction: row;
  gap: var(--space-4);
  align-items: flex-end;
  margin-block-end: var(--space-8);
}

.geba-section-heading__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* ------------------------------------------------------------------- hero */

/*
 * A core/cover bound to the page's featured image. The reference cross-fades
 * four slides; the second and later slides arrive with `geba/carousel` in
 * Phase 5, and the markup here is already the shape that block will wrap.
 */
.geba-hero {
  min-height: 820px;
  /*
	 * Vertical only, and this also replaces core/cover's own 14px default
	 * padding. The gutter belongs to `.geba-hero__inner`, not to the band: the
	 * reference writes every container as `max-width: var(--container-max);
	 * padding: 0 var(--gutter)`, so the measure is 1232px and the copy lines up
	 * with the header's logo. Padding the band instead put the hero 24px
	 * further left than the chrome above it.
	 */
  padding-block: var(--space-24);
  padding-inline: 0;
  background: var(--blue-950);
  isolation: isolate;
  overflow: hidden;
}

/*
 * The container. The cover's own layout is flow, so nothing else centres this
 * — deliberately, because a constrained layout would also centre the copy
 * inside it at a specificity no class can override.
 */
.geba-hero__inner {
  position: relative;
  z-index: 2;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
  width: 100%;
}

/*
 * The photograph at nearly full strength and in colour, over the deep ocean
 * blue, with the ocean side wash on top so the copy still sits on the darkest
 * part of the frame. The reference dimmed it to 46% over near-black and greyed
 * it; the client asked for the site to be bright (ADR-084).
 */
.geba-hero .wp-block-cover__image-background,
.geba-hero__slide img {
  opacity: 0.9;
  filter: saturate(1.05);
  transition: opacity var(--dur-slow) var(--ease-standard);
}

/*
 * The cross-fade the reference runs behind the headline: four photographs
 * stacked in the same frame, one visible at a time.
 *
 * Slide one is the page's featured image — the cover's own background — so it
 * stays editable where every other page's photograph is. The rest are image
 * blocks in the pattern, which is what lets `wp geba seed-pages` swap the
 * placeholder for the attachment whose alt text matches (ADR-013).
 *
 * `z-index: -2` puts the layer under core's dim span, exactly as the reference
 * does. It only works because `.wp-block-cover__inner-container` is `position:
 * static` here — a positioned container would trap the negative index inside
 * its own stacking context and the photographs would cover the wash.
 */
.geba-hero__slides {
  position: absolute;
  inset: 0;
  z-index: -2;
  pointer-events: none;
}

.geba-hero__slide {
  position: absolute;
  inset: 0;
  margin: 0;
}

.geba-hero__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
}

/* Set by assets/js/motion.js; slide one is the cover's own background image. */
.geba-hero__slide.is-active img {
  opacity: 0.9;
}

.geba-hero.has-slides .wp-block-cover__image-background {
  opacity: 0;
}

.geba-hero.has-slides .wp-block-cover__image-background.is-active {
  opacity: 0.9;
}

/*
 * Left static, so the floaters inside it position against the cover — which is
 * the band — rather than against the container.
 *
 * And `z-index: auto`. Core gives the container `z-index: 1`, and the cover is
 * a flex box, so on a flex item that z-index makes a stacking context even
 * while the container is static. Every slide after the first was then painted
 * inside it, as one layer at the wash's own z-index and after it — over the
 * wash. At 46% opacity the wash still showed through; a slide at full strength
 * left the copy on a bright photograph. Without the context the slides go
 * under the cover's image and the wash, as the featured image does.
 *
 * Core writes that z-index through `body:not(.editor-styles-wrapper)
 * .wp-block-cover:not(.wp-block-cover:has(…)) .wp-block-cover__inner-container`,
 * which is (0,6,1); hence the repeated classes, at (0,7,0).
 */
.geba-hero .wp-block-cover__inner-container {
  position: static;
}

.geba-hero.geba-hero.geba-hero.geba-hero.geba-hero
  .wp-block-cover__inner-container.wp-block-cover__inner-container {
  z-index: auto;
}

.geba-hero__content {
  max-width: 620px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
}

.geba-hero__title {
  margin: 0;
  font-size: clamp(2.25rem, 4.4vw, 48px);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-inverse);
  text-wrap: pretty;
}

.geba-hero__lead {
  margin: 0 0 var(--space-3);
  max-width: 520px;
  font-size: var(--text-lg);
  font-weight: var(--weight-light);
  line-height: var(--leading-normal);
  color: var(--text-inverse-muted);
  text-wrap: pretty;
}

.geba-hero__actions {
  gap: var(--space-3);
  margin-block-end: var(--space-6);
}

/*
 * Two drifting glyphs, purely decorative and out of the accessibility tree.
 * Over the wash, where the container's stacking context used to put them.
 */
.geba-hero__floaters {
  position: absolute;
  z-index: 1;
  inset: 0;
  pointer-events: none;
  color: var(--blue-300);
}

.geba-hero__floater {
  position: absolute;
  display: grid;
  place-items: center;
  border-radius: var(--radius-md);
}

.geba-hero__floater--one {
  top: 40px;
  left: 40px;
  width: 86px;
  height: 86px;
}

/* Bottom right of the band, mirroring the wind glyph at the top left. */
.geba-hero__floater--two {
  right: var(--space-10);
  bottom: var(--space-16);
  width: 104px;
  height: 104px;
}

@media (max-width: 900px) {
  .geba-hero {
    /*
     * `!important`, and it is the only lever there is. `core/cover` writes its
     * `minHeight` attribute as an *inline* style, so no stylesheet rule can
     * reach it — this 620px had never once applied, and every phone got the
     * desktop's 820px with the copy floating in the middle of it.
     */
    min-height: 620px !important;
    /*
     * 96px of band padding above and below 509px of copy is what put the
     * headline a third of the way down the screen. Half of it on a phone.
     */
    padding-block: var(--space-12);
  }

  .geba-hero__floaters {
    display: none;
  }
}

/* ------------------------------------------------------------------ trust */

.geba-trust {
  gap: var(--space-10);
  margin-block-start: var(--space-4);
  padding-block-start: var(--space-5);
  border-block-start: var(--hairline) solid var(--border-inverse);
  align-self: stretch;
}

/*
 * The figures are `geba/stat-counter` blocks, so the hero only says where it
 * differs from that component: smaller than the section-level statistic, in
 * plain white rather than blue, and tight enough to sit under the buttons.
 */
.geba-trust .geba-stat {
  gap: 0;
}

.geba-trust .geba-stat__value {
  font-size: var(--text-2xl);
  color: var(--text-inverse);
}

.geba-trust .geba-stat__label {
  font-size: var(--text-2xs);
  letter-spacing: 0.05em;
  white-space: nowrap;
}

/* ---------------------------------------------------------------- welcome */

/*
 * Rides up over the hero on a shallow diagonal. The clip path is the
 * reference's own polygon; the negative margin is what lets it overlap, and the
 * padding puts the content back where it belongs.
 */
.geba-welcome {
  position: relative;
  z-index: 1;
  margin-block-start: -110px;
  padding-block: calc(var(--section-y) + 110px) var(--section-y);
  background: var(--ink-0);
  clip-path: polygon(0 70px, 26% 44px, 74% 110px, 100% 62px, 100% 100%, 0 100%);
}

.geba-welcome__columns {
  gap: var(--space-12);
}

.geba-welcome__media {
  position: relative;
  padding: 0 46px 54px 0;
}

.geba-welcome__photo img {
  display: block;
  width: 100%;
  height: 460px;
  object-fit: cover;
  border-radius: var(--radius-media);
  box-shadow: var(--shadow-lg);
}

.geba-welcome__inset {
  position: absolute;
  right: 0;
  bottom: 0;
  margin: 0;
}

.geba-welcome__inset img {
  display: block;
  width: 238px;
  height: 262px;
  object-fit: cover;
  border: 6px solid var(--ink-0);
  border-radius: var(--radius-media);
  box-shadow: var(--shadow-xl);
}

.geba-welcome__badge {
  position: absolute;
  left: 26px;
  bottom: 30px;
  width: 197px;
  padding: 15px 0 13px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: var(--surface-inverse);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.geba-welcome__badge-value {
  margin: 0;
  font-size: var(--text-4xl);
  font-weight: var(--weight-black);
  line-height: 0.86;
  color: var(--text-inverse);
}

.geba-welcome__badge-label {
  margin: 0;
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-inverse-muted);
}

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

.geba-welcome__body h2 {
  margin: 0;
  font-size: var(--type-h2-size);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  text-wrap: pretty;
}

.geba-welcome__intro,
.geba-welcome__outro {
  margin: 0;
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-muted);
  text-wrap: pretty;
}

.geba-welcome__intro {
  font-size: var(--type-body-size);
}

.geba-welcome__outro {
  max-width: 560px;
  font-size: var(--text-base);
}

@media (max-width: 900px) {
  .geba-welcome {
    margin-block-start: 0;
    padding-block: var(--section-y-xs);
    clip-path: none;
  }

  .geba-welcome__media {
    padding: 0;
  }

  /*
   * 460px is a portrait crop of a landscape photograph, which on a 342px
   * column is most of the screen before a word of the section is read. The
   * file's own ratio would give 226px; 260 keeps a little of the crop.
   */
  .geba-welcome__photo img {
    height: 260px;
  }

  .geba-welcome__inset,
  .geba-welcome__badge {
    display: none;
  }
}

/* -------------------------------------------------------------- checklist */

.geba-checklist {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
  gap: var(--space-3) var(--space-5);
}

.geba-checklist__item {
  gap: var(--space-3);
  align-items: center;
}

.geba-checklist__mark {
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-circle);
  background: var(--blue-50);
  color: var(--blue-600);
}

.geba-checklist__text {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--text-secondary);
}

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

.geba-card-grid {
  gap: var(--grid-gap);
}

/*
 * The grid item is the loop's `<li>`; the card is inside it. Without this the
 * card is only as tall as its own copy, so a row of cards with different
 * amounts of text ends up ragged and their footer links do not line up.
 */
.geba-card-grid > li {
  display: flex;
}

.geba-card-grid > li > * {
  flex: 1;
  min-width: 0;
}

/* ------------------------------------------------------------- service band */

.geba-service-band {
  position: relative;
  overflow: hidden;
}

/*
 * The watermark: the G mark alone, centred behind the cards. Not the full
 * wordmark — a lockup with the "group d.o.o." line reads as smudge rather than
 * as a mark. On the sand band it is the entry screen's white G, which needs
 * far more than the 5% that showed it on black.
 */
.geba-service-band::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 620px;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  background: url("../img/g-mark.svg") center / contain no-repeat;
  opacity: 0.7;
  pointer-events: none;
}

/*
 * The card reads `--surface-glass`, which a band makes white (see "dark bands"
 * below), and a white card on the sand is drawn by its shadow rather than an
 * edge. The hover deepens the shadow; the fill stays.
 */
.geba-service-card {
  /*
   * `relative` for the shine: the sweep in the entry-doors section below is
   * shared with this card, and it is absolutely positioned against whatever
   * box it lands in. `overflow: hidden` here is what clips it at the corners.
   */
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--surface-glass);
  border: var(--hairline) solid transparent;
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sand-card);
  transition: var(--transition-card);
}

.geba-service-card:hover {
  transform: translateY(var(--lift-card));
  box-shadow: var(--shadow-sand-card-lifted);
}

.geba-service-card__media {
  margin: 0;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--ink-100);
}

.geba-service-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out-expo);
}

.geba-service-card:hover .geba-service-card__media img {
  transform: scale(1.1);
}

.geba-service-card__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--pad-card);
}

/* Lifted so it straddles the photograph's bottom edge. */
.geba-service-card__body > .geba-service-card__icon {
  width: 56px;
  height: 56px;
  margin-block: -58px var(--space-2);
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  border-radius: var(--radius-circle);
  background: var(--geba-blue);
  color: var(--ink-0);
  box-shadow: var(--shadow-brand-sm);
  transition: var(--transition-control);
}

.geba-service-card__title {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-snug);
  line-height: var(--leading-snug);
}

/*
 * One colour for every title, linked or not. A service with no page of its own
 * (ADR-047) has a title without a link, and it took the band's heading colour
 * while its neighbours took the link's. Two classes to stand over the dark
 * band's heading rule, which is (0,1,1).
 */
.geba-service-card .geba-service-card__title {
  color: var(--link-color-inverse);
}

.geba-service-card__title a {
  color: var(--text-inverse);
  text-decoration: none;
}

.geba-service-card__text {
  flex: 1;
  margin: 0;
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-inverse-muted);
}

.geba-service-card__more {
  margin-block-start: var(--space-3);
}

/* --------------------------------------------------------------- handoffs */

/* White between two bands, so the bands keep a rhythm. */
.geba-handoffs {
  padding-block: var(--space-14);
  background: var(--surface-page);
  border-block-start: var(--hairline) solid var(--border-subtle);
}

/*
 * Two cards, two columns, full width. Core's grid layout uses `auto-fill`,
 * which keeps laying out empty tracks — with a 380px minimum in a 1232px
 * container that is three tracks for two cards, and the pair ends up occupying
 * two thirds of the band. `auto-fit` collapses the empty track instead.
 */
.geba-handoffs__grid {
  gap: var(--grid-gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr));
}

.geba-handoff {
  /* `relative` for the stretched link below, which is what makes the whole
     capsule the hit target. */
  position: relative;
  gap: var(--space-5);
  align-items: center;
  padding: var(--space-7) var(--space-8);
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: var(--transition-card);
}

/*
 * The same lift the reference and news cards take, because it is now the same
 * kind of object: one card, one destination, the whole box clickable.
 *
 * `focus-within` as well, so tabbing to the link shows the card responding
 * rather than only the link's own focus ring.
 */
/*
 * The card is a direct child of a staggered block, so the arrival's own
 * `transition` lands on it at (0,3,0) and replaced `--transition-card`
 * outright — `transition` is one property, and the winning declaration takes
 * the whole list with it. The lift, the shadow and the keyline had nothing to
 * animate on and snapped.
 *
 * Restating both at (0,4,0) is the fix. It has to be both: drop the arrival's
 * half and the card stops fading in with its neighbours.
 */
.geba-js [data-geba-motion][data-geba-motion-stagger] > .geba-handoff {
  transition: var(--geba-motion-transition), var(--transition-card);
}

.geba-handoff:hover,
.geba-handoff:focus-within {
  transform: translateY(var(--lift-card));
  border-color: var(--blue-200);
  box-shadow: var(--shadow-lg);
}

/*
 * The whole capsule is the hit target, and it is still exactly one link.
 *
 * The anchor's own `::after` is stretched over the card rather than the card
 * being wrapped in a second anchor: a link inside a link is invalid markup, and
 * two anchors would give a screen reader two entries for one destination. This
 * way the accessible name stays the one the anchor already had, and the pointer
 * gets the whole box.
 */
.geba-handoff__link a::after {
  content: "";
  position: absolute;
  inset: 0;
}

/*
 * The card wraps rather than crushing.
 *
 * Its three parts sit in one row while there is room for them. The link is
 * `white-space: nowrap` and the body took whatever was left over, so on a phone
 * the body was squeezed to nothing — the title broke across four lines and the
 * link still ran past the card's edge, where `overflow: hidden` cut it in half.
 *
 * A minimum width on the body is what forces the break instead: once the row
 * cannot hold 52px of mark plus 200px of copy plus the link, the link takes a
 * row of its own. No breakpoint, because the card's width comes from the grid
 * and not from the viewport — the same card is narrow in a three-column band.
 *
 * Two classes, because the block's own `flexWrap: nowrap` attribute is printed
 * into the head as a one-class rule that would otherwise win on source order.
 */
.geba-handoffs .geba-handoff {
  flex-wrap: wrap;
}

.geba-handoff__mark {
  width: 52px;
  height: 52px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  background: var(--blue-50);
  color: var(--blue-600);
}

.geba-handoff__body {
  /*
   * 180px, because that is what keeps the mark beside the copy on a 360px
   * phone — the narrowest width the site is built for. Below it the mark takes
   * a row too, which is the right answer at that size rather than a failure.
   */
  flex: 1 1 180px;
}

.geba-handoff__title {
  margin: 0 0 6px;
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
  color: var(--text-primary);
  text-wrap: pretty;
}

.geba-handoff__text {
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  color: var(--text-muted);
}

.geba-handoff__link {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  white-space: nowrap;
}

.geba-handoff__link a {
  color: var(--blue-600);
  text-decoration: none;
}

/*
 * On a phone the card stacks and centres: mark, question, the line of copy,
 * then the link as the card's last row.
 *
 * These two are signposts rather than information — the only cards on the site
 * whose job is to send the visitor somewhere else — so they are centred on
 * purpose, and the headline gets the full width of the card, which is what it
 * was short of. The desktop row is unchanged.
 *
 * Two classes, and the block sits after the rules above rather than in the
 * phone block further up the file. Both are about the same thing: a media query
 * adds no specificity, so this only beats `.geba-handoff` by coming later, and
 * only beats core's printed `.wp-container-…{flex-wrap:nowrap;align-items:center}`
 * by carrying a second class. See ADR-058.
 */
@media (max-width: 900px) {
  .geba-handoffs .geba-handoff {
    flex-direction: column;
    align-items: center;
    text-align: center;
    /* 32px a side is a tenth of a 342px card each way, and all of it comes off
       the headline's measure. */
    padding: var(--space-7) var(--space-6);
  }

  /*
   * The floor that forces the wrap in a row is a *height* in a column, which
   * would leave 180px of empty card under two lines of copy.
   */
  .geba-handoff__body {
    flex: none;
  }
}

/* ---------------------------------------------------------------- process */

.geba-steps {
  position: relative;
  gap: var(--space-5);
  counter-reset: geba-step;
}

/*
 * The dashed rule that joins the tiles. Drawn once across the row rather than
 * between the items, so it does not care how many steps there are.
 */
.geba-steps::before {
  content: "";
  position: absolute;
  top: 52px;
  left: 10%;
  right: 10%;
  border-block-start: var(--rule-strong) dashed var(--border-inverse);
}

.geba-step {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  counter-increment: geba-step;
}

.geba-step__tile {
  position: relative;
  width: 104px;
  height: 104px;
  margin-block-end: var(--space-5);
  display: grid;
  place-items: center;
  border: var(--hairline) solid transparent;
  border-radius: 24px;
  background: var(--ink-0);
  color: var(--blue-600);
  /* In --blue-950, so the white tile stands off the blue band. */
  box-shadow: 0 1px 2px rgb(15 38 53 / 12%), 0 22px 40px -22px rgb(15 38 53 / 60%);
}

/*
 * The step number. A counter rather than an attribute: core/group has no
 * attribute to carry one, so adding `data-step` by hand made the editor read
 * every tile as invalid — and the counter renumbers itself when a step is
 * moved or removed, which an attribute would not.
 */
.geba-step__tile::before {
  content: counter(geba-step);
  position: absolute;
  top: -7px;
  left: -7px;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-circle);
  background: var(--geba-orange);
  color: var(--geba-black);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
}

.geba-step__title {
  margin: 0 0 var(--space-2);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  color: var(--text-inverse);
}

.geba-step__text {
  margin: 0;
  max-width: 230px;
  font-size: var(--text-sm);
  font-weight: var(--weight-light);
  line-height: var(--leading-normal);
  color: var(--text-inverse-muted);
  text-wrap: pretty;
}

@media (max-width: 900px) {
  .geba-steps::before {
    display: none;
  }
}

/*
 * On a phone the steps stack one to a row, so the tile is no longer competing
 * with four neighbours for the measure and the desktop size reads small
 * against a full-width column. Everything grows together — tile, icon, number
 * and copy — because a bigger tile with the same 32px icon is just a bigger
 * empty box.
 *
 * The `width`/`height` on the icon are attributes the block writes, and a
 * presentational attribute loses to any rule at all; nothing here needs
 * `!important`.
 */
@media (max-width: 600px) {
  .geba-steps {
    gap: var(--space-7);
  }

  .geba-step__tile {
    width: 128px;
    height: 128px;
    border-radius: 28px;
  }

  .geba-step__tile svg {
    width: 40px;
    height: 40px;
  }

  .geba-step__tile::before {
    top: -8px;
    left: -8px;
    width: 34px;
    height: 34px;
    font-size: var(--text-xs);
  }

  .geba-step__title {
    font-size: var(--text-xl);
  }

  .geba-step__text {
    max-width: 300px;
    font-size: var(--text-base);
  }
}

/* --------------------------------------------------------------- projects */

/*
 * Five projects in view, up to ten in the rail.
 *
 * The row breaks out of the 1232px measure the rest of the page keeps: at a
 * desktop width five cards inside that container are too narrow to read, and
 * the reference runs this band edge to edge. 1920px is the cap — past that the
 * row would stop being a row and start being a horizon.
 *
 * The rail is a real horizontal scroll container over a flex track. That is
 * what makes it draggable: a touch swipe and a trackpad flick are the browser's
 * own, with its momentum and its snapping, and `assets/js/motion.js` adds only
 * the mouse drag and the unattended advance on top. Without the script the rail
 * is still the whole section and still swipes.
 */
/*
 * The window. `alignfull` in the pattern, so core's breakout has already taken
 * it to the viewport edge; the padding brings the cards back to a 1920px
 * measure and holds the gutter below that width.
 *
 * `100vw` rather than `100%`, because a percentage here resolves against the
 * band's padded content box — which is the width the row is trying to escape.
 * `scroll-padding-inline` repeats the same expression so a snapped card lands
 * on the gutter rather than on the viewport edge.
 *
 * The scrollbar is hidden because the row is edge to edge and a bar under it
 * would read as a page-wide one. The cards are reachable by keyboard — a
 * focused link scrolls its own card into view — so nothing is lost with it.
 */
.geba-projects__rail {
  --geba-rail-inset: max(var(--gutter), calc((100vw - 1920px) / 2));

  /*
   * Room for what leaves the card's own box. A scroll container clips on both
   * axes whatever `overflow-y` says — `visible` beside an `auto` computes to
   * `auto`, which is a vertical scrollbar rather than a way out — so the room
   * has to be inside the box: the padding makes it and the negative margin
   * below gives it straight back, leaving the band exactly where it was.
   *
   * The two numbers are `--shadow-lg` (`0 22px 52px`) measured against the
   * hover lift. Above: 52 − 22 = 30px of shadow, plus 6px of lift. Below:
   * 22 + 52 = 74px, less the same 6px. Asymmetric on purpose — the rail's box
   * overlaps whatever sits above it by exactly this much, and the band above is
   * the section heading with the "Vse reference" button in it.
   */
  --geba-rail-bleed-start: 40px;
  --geba-rail-bleed-end: 80px;

  padding-inline: var(--geba-rail-inset);
  padding-block: var(--geba-rail-bleed-start) var(--geba-rail-bleed-end);
  scroll-padding-inline: var(--geba-rail-inset);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

.geba-projects__rail::-webkit-scrollbar {
  display: none;
}

/*
 * The padding above and below is room for the shadow, not space in the band, so
 * it is given straight back. A compound selector because core writes
 * `:root :where(.is-layout-constrained) > :last-child { margin-block-end: 0 }`
 * at (0,2,0) and the rail is that last child — a single class loses to it and
 * the band grows by 80px at the bottom, which is exactly the kind of thing that
 * ships. ADR-038.
 */
.geba-section.geba-projects .geba-projects__rail {
  margin-block: calc(var(--geba-rail-bleed-start) * -1)
    calc(var(--geba-rail-bleed-end) * -1);
}

/*
 * While a mouse drag is in flight the snap has to be off, or every pointermove
 * is fought by the scroller trying to settle on a card.
 */
.geba-projects__rail.is-dragging {
  scroll-snap-type: none;
  user-select: none;
}

.geba-projects__rail.is-dragging .geba-projects__grid {
  cursor: grabbing;
}

/*
 * The grab cursor lives on the track, not on the rail: the rail's box now
 * reaches past the row on both sides to hold the cards' shadows, and a hand
 * hovering apparently blank page reads as a bug.
 */
.geba-projects__grid {
  display: flex;
  gap: var(--grid-gap-sm);
  margin: 0;
  padding: 0;
  list-style: none;
  cursor: grab;
}

.geba-projects__grid > li {
  /* Five across, sharing the four gaps between them. */
  flex: 0 0 calc((100% - 4 * var(--grid-gap-sm)) / 5);
  display: flex;
  scroll-snap-align: start;
}

.geba-projects__grid > li > * {
  flex: 1;
  min-width: 0;
}

@media (max-width: 1100px) {
  .geba-projects__grid > li {
    flex-basis: calc((100% - 2 * var(--grid-gap-sm)) / 3);
  }
}

@media (max-width: 780px) {
  .geba-projects__grid > li {
    flex-basis: calc((100% - var(--grid-gap-sm)) / 2);
  }
}

@media (max-width: 520px) {
  .geba-projects__grid > li {
    flex-basis: 100%;
  }
}

.geba-project-card {
  /* `relative` for the shine, `overflow: hidden` to clip it at the corners —
     the sweep is shared with the entry doors and the service cards, and it is
     positioned against whatever box it lands in. */
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  transition: var(--transition-card);
}

.geba-project-card:hover {
  transform: translateY(var(--lift-card));
  box-shadow: var(--shadow-lg);
}

.geba-project-card__media {
  margin: 0;
}

.geba-project-card__media img {
  display: block;
  width: 100%;
  height: 220px;
  object-fit: cover;
}

.geba-project-card__body {
  flex: 1;
  padding: var(--space-6) var(--space-5) var(--space-7);
}

.geba-project-card__kicker {
  display: block;
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  line-height: 1.3;
  text-transform: uppercase;
  color: var(--text-muted);
}

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

.geba-project-card__title {
  margin: 8px 0 0;
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  line-height: 1.3;
  text-wrap: pretty;
}

.geba-project-card__title a {
  color: var(--text-primary);
  text-decoration: none;
}

/* ----------------------------------------------------------- testimonials */

/* White on the band, like the service cards, and drawn by the same shadow. */
.geba-quote {
  padding: var(--space-7);
  background: var(--surface-glass);
  border: var(--hairline) solid transparent;
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sand-card);
  overflow: hidden;
}

/* The oversized opening mark, set as content rather than shipped as an icon. */
.geba-quote::before {
  content: "\201C";
  display: block;
  font-size: var(--text-4xl);
  line-height: 0.6;
  color: var(--geba-orange);
}

.geba-quote__text {
  margin: var(--space-3) 0 var(--space-5);
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-inverse-muted);
  text-wrap: pretty;
}

.geba-quote__author {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--text-inverse);
}

.geba-quote__city {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--text-inverse-muted);
}

/*
 * Three across or one above another, never two and one. Below the width where
 * three fit, core's 300px minimum laid out two columns and left the third
 * quote alone on a row of its own, which read as a gap on every tablet. One
 * column instead, at the narrow measure and centred under the centred heading,
 * so a short quote does not stretch into one long line across the band. Two
 * classes, because core prints the grid's own columns at (0,1,0) after this
 * stylesheet.
 */
@media (max-width: 1000px) {
  .geba-testimonials .geba-card-grid {
    grid-template-columns: minmax(0, 1fr);
    max-width: var(--container-narrow);
    margin-inline: auto;
  }
}

/* --------------------------------------------------------------- partners */

.geba-partners {
  border-block: var(--hairline) solid var(--border-subtle);
}

/*
 * Edge to edge, not inside the measure. A marquee that stops 344px short of
 * each side reads as a cropped strip rather than as a band running past the
 * frame, and the logos have nowhere to come from or go to.
 */
.geba-marquee {
  position: relative;
  overflow: hidden;
}

/*
 * The space between logos is a margin on each item, not `gap` on the track.
 *
 * The loop works by translating the track exactly half its own width, and the
 * track holds two copies of the list. With `gap` the total is
 * `2n items + (2n - 1) gaps`, so half of it is one copy *minus half a gap* —
 * the second copy lands half a gap short of where the first started and the
 * wrap shows as a stutter. A trailing margin makes each item measure
 * `item + space`, so half the track is exactly one copy and the wrap is
 * invisible. The last item's trailing margin falls outside the frame.
 */
.geba-marquee__track {
  display: flex;
  width: max-content;
  /*
   * Core puts `max-width: 100%` on `.wp-block-post-template`, which caps
   * `max-content` at the parent's width — the track measured 1280px while its
   * logos measured 4076px, so `translateX(-50%)` moved half a viewport instead
   * of half the list and the row snapped back with the band empty behind it.
   */
  max-width: none;
}

.geba-marquee__track > * {
  margin-inline-end: var(--space-7);
}

.geba-partner {
  width: 200px;
  height: 150px;
  flex: none;
  display: grid;
  place-items: center;
  padding-inline: var(--space-6);
  border-radius: var(--radius-md);
  background: var(--ink-0);
  transition: var(--transition-control);
}

.geba-partner__logo {
  margin: 0;
}

.geba-partner__logo img {
  width: 76px;
  height: 76px;
  object-fit: contain;
  opacity: 0.72;
  transition: var(--transition-control);
}

.geba-partner:hover .geba-partner__logo img {
  opacity: 1;
}

/*
 * The wordmark fallback, for a partner whose logo has not been supplied. When
 * there is a logo the name is still needed for screen readers, so it is hidden
 * visually rather than removed — see `:has()` below.
 */
.geba-partner__name {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-snug);
  color: var(--text-muted);
  text-align: center;
}

.geba-partner:has(.geba-partner__logo img) .geba-partner__name {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------- cta band */

/*
 * The brand blue where the page asks something of the visitor: the process
 * band and the call to action (ADR-084). Both read the inverse tokens, so the
 * blue is set the way a band sets its sand — deep enough for white copy,
 * blue-600 being 4.9:1. The process band is also a dark band, and two classes
 * outrank that rule's one.
 */
.geba-section.geba-process,
.geba-cta-band {
  --surface-inverse: var(--blue-600);
  --text-inverse: var(--ink-0);
  --text-inverse-muted: rgb(255 255 255 / 92%);
  --border-inverse: rgb(255 255 255 / 42%);
  --link-color-inverse: var(--ink-0);
  --blue-300: var(--blue-100);

  background: var(--wash-brand-deep);
}

/* The call to action as the brand's own button: filled white on the blue. */
.geba-cta-band .wp-block-button.is-style-geba-pill-outline .wp-block-button__link {
  border-color: var(--ink-0);
  background: var(--ink-0);
  color: var(--blue-700);
}

.geba-cta-band .wp-block-button.is-style-geba-pill-outline .wp-block-button__link:hover {
  border-color: var(--blue-50);
  background: var(--blue-50);
  color: var(--blue-700);
  box-shadow: var(--shadow-md);
}

.geba-cta-band__inner {
  gap: var(--space-6);
  padding-block: var(--space-12);
}

.geba-cta-band__title {
  margin: 0;
  max-width: 520px;
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  line-height: var(--leading-normal);
  color: var(--text-inverse);
  text-wrap: pretty;
}

/* ------------------------------------------------------------------- blog */

.geba-blog-card {
  /* `relative` for the shine, `overflow: hidden` to clip it at the corners —
     the sweep is shared with the entry doors and the service cards, and it is
     positioned against whatever box it lands in. */
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  transition: var(--transition-card);
}

.geba-blog-card:hover {
  transform: translateY(var(--lift-card));
  box-shadow: var(--shadow-lg);
}

.geba-blog-card__media {
  margin: 0;
}

.geba-blog-card__media img {
  display: block;
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.geba-blog-card__body {
  padding: var(--space-6);
  text-align: center;
}

.geba-blog-card__title {
  margin: 0 0 var(--space-3);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  text-wrap: pretty;
}

.geba-blog-card__title a {
  color: var(--text-primary);
  text-decoration: none;
}

.geba-blog-card__meta {
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/* The rule the reference sets between the date and the category. */
.geba-blog-card__meta .geba-blog-card__category::before {
  content: "|";
  margin-inline-end: var(--space-2);
  color: var(--border-strong);
}

.geba-blog-card__category,
.geba-blog-card__category a {
  color: var(--text-primary);
  font-weight: var(--weight-bold);
  text-decoration: none;
}

/* -------------------------------------------------------------- page hero */

/*
 * The shallow hero every inner screen opens with: centred title over a dark
 * band, breadcrumb underneath. Half the height of the division home's hero and
 * with no call to action — it introduces a page rather than selling one.
 */
.geba-hero-page {
  min-height: 420px;
  /* Gutter on the container, not on the band — see `.geba-hero`. */
  padding-block: var(--space-24);
  padding-inline: 0;
  background: var(--blue-950);
  isolation: isolate;
  overflow: hidden;
}

/* In colour, like the division home's hero, a little dimmer under its title. */
.geba-hero-page .wp-block-cover__image-background {
  opacity: 0.78;
  filter: saturate(1.05);
}

.geba-hero-page .wp-block-cover__inner-container {
  position: static;
}

.geba-hero-page__inner {
  position: relative;
  z-index: 2;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  text-align: center;
}

.geba-hero-page__inner > :where(*) {
  margin-block: 0;
}

.geba-hero-page__title {
  margin: 0;
  font-size: var(--text-5xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
  color: var(--text-inverse);
  text-wrap: pretty;
}

/* ------------------------------------------------------------- field list */

/*
 * The list `geba/repeater-list` renders. Marker and text on a baseline, not
 * centred: the text wraps to two lines often enough that centring the marker
 * would leave it floating beside the gap.
 */
.geba-field-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.geba-field-list__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.geba-field-list__mark {
  flex: none;
  margin-block-start: 3px;
  line-height: 0;
  color: var(--blue-600);
}

.geba-field-list__mark svg {
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.geba-field-list__title,
.geba-field-list__text {
  margin: 0;
}

.geba-field-list__text {
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  line-height: var(--leading-normal);
  color: var(--text-secondary);
}

/* On a dark band the marker and the text both invert. */
.geba-section--dark .geba-field-list__mark {
  color: var(--blue-300);
}

.geba-section--dark .geba-field-list__text {
  color: var(--text-inverse-muted);
}

/* ----------------------------------------------------------- service rows */

.geba-service-list__rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
  list-style: none;
  margin: 0;
  padding: 0;
}

.geba-service-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr));
  gap: var(--space-12);
  align-items: center;
  padding-block-end: var(--space-12);
  border-block-end: var(--hairline) solid var(--border-subtle);
}

.geba-service-row > :where(*) {
  margin-block: 0;
}

/*
 * The zebra. Done on the loop item rather than per row in the markup, so the
 * alternation survives a service being added, removed or reordered.
 */
.geba-service-list__rows > li:nth-child(even) .geba-service-row__media {
  order: 2;
}

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

.geba-service-row__media img {
  display: block;
  width: 100%;
  height: 360px;
  object-fit: cover;
  /* Same figures as `.geba-service-card__media img`: the two are the same
     photograph in two layouts and should answer a pointer the same way. */
  transition: transform var(--dur-slow) var(--ease-out-expo);
}

/*
 * The zoom is on the link, not on the figure.
 *
 * `post-featured-image` is `isLink`, so the photograph is an anchor — except on
 * a service with `no_detail_page`, where the anchor is dropped and the image
 * stands alone (ADR-047). Hovering a photograph that goes nowhere and having it
 * move would advertise a page that answers 404. Scoped to the anchor, the one
 * service without a page simply does not respond, which is correct.
 */
.geba-service-row__media a:hover img,
.geba-service-row__media a:focus-visible img {
  transform: scale(1.1);
}

.geba-service-row__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.geba-service-row__body > :where(*) {
  margin-block: 0;
}

.geba-service-row__icon {
  width: 72px;
  height: 72px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-md);
  background: var(--blue-50);
  color: var(--blue-600);
}

.geba-service-row__title {
  margin: 0;
  font-size: var(--text-3xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
  text-wrap: pretty;
}

.geba-service-row__title a {
  color: var(--text-primary);
  text-decoration: none;
}

.geba-service-row__text {
  margin: 0;
  font-size: var(--type-body-size);
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-muted);
  text-wrap: pretty;
}

.geba-service-row__more {
  align-self: flex-start;
  margin-block-start: var(--space-2);
}

@media (max-width: 900px) {
  .geba-service-row {
    gap: var(--space-8);
    padding-block-end: var(--space-8);
  }

  .geba-service-row__media img {
    height: 260px;
  }
}

/* ------------------------------------------------------ service — intro */

.geba-service-intro__columns {
  gap: var(--space-16);
}

.geba-service-intro__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.geba-service-intro__body > :where(*) {
  margin-block: 0;
}

.geba-service-intro__body h2 {
  margin: 0;
  font-size: var(--type-h2-size);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  text-wrap: pretty;
}

.geba-service-intro__lede p {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-secondary);
  text-wrap: pretty;
}

.geba-service-intro__actions {
  gap: var(--space-3);
  margin-block-start: var(--space-2);
}

/*
 * The media stack: the service's own photograph with two detail shots laid over
 * its lower corners. The column reserves the room the details hang into, so
 * they overlap the picture without overlapping the copy beside it.
 */
.geba-service-intro__media {
  position: relative;
  padding: 0 0 84px 64px;
}

.geba-service-intro__photo {
  margin: 0;
}

.geba-service-intro__photo img {
  display: block;
  width: 100%;
  height: 460px;
  object-fit: cover;
  border-radius: var(--radius-media);
  box-shadow: var(--shadow-lg);
}

.geba-service-intro__detail {
  position: absolute;
  margin: 0;
}

.geba-service-intro__detail img {
  display: block;
  object-fit: cover;
  border: 6px solid var(--ink-0);
  border-radius: var(--radius-media);
}

/*
 * A service with no detail photograph should show nothing rather than a broken
 * image: the binding leaves the `src` empty, and an empty `src` resolves to the
 * page itself.
 */
.geba-service-intro__detail img[src=""] {
  display: none;
}

.geba-service-intro__detail--one {
  left: 0;
  bottom: 0;
}

.geba-service-intro__detail--one img {
  width: 212px;
  height: 212px;
  box-shadow: var(--shadow-xl);
}

.geba-service-intro__detail--two {
  right: 26px;
  bottom: 22px;
}

.geba-service-intro__detail--two img {
  width: 186px;
  height: 150px;
  box-shadow: var(--shadow-lg);
}

/* -------------------------------------------------------- service — why */

.geba-service-why__columns {
  gap: var(--space-16);
}

.geba-service-why__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.geba-service-why__body > :where(*) {
  margin-block: 0;
}

.geba-service-why__title {
  margin: 0;
  font-size: var(--type-h2-size);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
  text-wrap: pretty;
}

.geba-service-why__text {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-inverse-muted);
  text-wrap: pretty;
}

/*
 * The reason list as cards. `geba/repeater-list` renders the same markup
 * everywhere; which of its shapes a section wants is a class on the block.
 */
.is-style-geba-reason-cards {
  gap: var(--space-4);
}

.is-style-geba-reason-cards .geba-field-list__item {
  align-items: flex-start;
  gap: var(--space-5);
  padding: var(--space-6);
  background: var(--surface-glass);
  border: var(--hairline) solid var(--border-inverse);
  border-radius: var(--radius-card);
}

.is-style-geba-reason-cards .geba-field-list__mark {
  width: 48px;
  height: 48px;
  margin: 0;
  display: grid;
  place-items: center;
  border-radius: var(--radius-md);
  background: var(--surface-glass);
  color: var(--blue-300);
}

.is-style-geba-reason-cards .geba-field-list__title {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-snug);
  color: var(--text-inverse);
}

.is-style-geba-reason-cards .geba-field-list__text {
  margin: 0;
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  color: var(--text-inverse-muted);
}

/* --------------------------------------------------- service — includes */

.geba-service-includes {
  background: var(--surface-muted);
  border-block-start: var(--hairline) solid var(--border-subtle);
}

.is-style-geba-include-cards {
  /*
   * What bounds a card's height. The reference gives the variant's photograph
   * a fixed 280px and lets the copy sit beside it; ours had `height: 100%` on
   * a stretched cell instead, so the picture grew to its own aspect ratio and
   * took the card with it — 442px where the design draws 344px.
   *
   * A row whose copy is longer than the picture still grows: clipping a
   * variant's detail list would lose content rather than tidy it.
   */
  --geba-include-media-h: 280px;

  gap: var(--grid-gap);
}

/*
 * Two columns: the variant's photograph beside its copy, swapping sides row by
 * row. The counter shares the copy's cell, so the grid stays two columns and
 * the zebra is one `order` on the even rows.
 */
/*
 * Three children — photograph, counter, copy — in two columns. The counter and
 * the copy share a column and stack, so the rows are placed explicitly rather
 * than left to auto-flow, which would have put the counter in the picture's
 * cell.
 */
.is-style-geba-include-cards .geba-field-list__item {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: auto auto;
  align-content: center;
  column-gap: var(--space-10);
  padding: var(--space-8);
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
}

.is-style-geba-include-cards .geba-field-list__media {
  grid-column: 1;
  grid-row: 1 / span 2;
  align-self: center;
}

.is-style-geba-include-cards .geba-field-list__mark--number {
  grid-column: 2;
  grid-row: 1;
  align-self: end;
  margin-block-end: var(--space-3);
}

.is-style-geba-include-cards .geba-field-list__body {
  grid-column: 2;
  grid-row: 2;
}

/* Even rows put the picture on the right. */
.is-style-geba-include-cards
  .geba-field-list__item:nth-child(even)
  .geba-field-list__media {
  grid-column: 2;
}

.is-style-geba-include-cards
  .geba-field-list__item:nth-child(even)
  .geba-field-list__mark--number,
.is-style-geba-include-cards
  .geba-field-list__item:nth-child(even)
  .geba-field-list__body {
  grid-column: 1;
}

/* A row with no photograph gives its copy the whole card. */
.is-style-geba-include-cards
  .geba-field-list__item:not(:has(.geba-field-list__media)) {
  grid-template-columns: 1fr;
}

.is-style-geba-include-cards
  .geba-field-list__item:not(:has(.geba-field-list__media))
  :where(.geba-field-list__mark--number, .geba-field-list__body) {
  grid-column: 1;
}

.is-style-geba-include-cards .geba-field-list__media {
  margin: 0;
  border-radius: var(--radius-media);
  overflow: hidden;
}

/*
 * A row's picture is a drawing on a transparent ground, cropped to the subject
 * and its disc (ADR-076) — it is shown whole, never cropped to the frame.
 */
.is-style-geba-include-cards .geba-field-list__media img {
  display: block;
  width: 100%;
  height: var(--geba-include-media-h);
  object-fit: contain;
}

@media (max-width: 900px) {
  .is-style-geba-include-cards .geba-field-list__item,
  .is-style-geba-include-cards
    .geba-field-list__item:not(:has(.geba-field-list__media)) {
    grid-template-columns: 1fr;
    row-gap: var(--space-5);
  }

  .is-style-geba-include-cards .geba-field-list__media,
  .is-style-geba-include-cards
    .geba-field-list__item:nth-child(even)
    .geba-field-list__media {
    grid-column: 1;
    grid-row: auto;
  }

  .is-style-geba-include-cards .geba-field-list__mark--number,
  .is-style-geba-include-cards .geba-field-list__body,
  .is-style-geba-include-cards
    .geba-field-list__item:nth-child(even)
    .geba-field-list__mark--number,
  .is-style-geba-include-cards
    .geba-field-list__item:nth-child(even)
    .geba-field-list__body {
    grid-column: 1;
    grid-row: auto;
  }
}

/*
 * The counter the design puts above each variant's name.
 *
 * A single digit, 1 upwards — the reference numbers its variants the same way,
 * and `01` read as a code rather than as a step. The disc is larger than the
 * reference's 34px because at a single digit and --text-sm it was the smallest
 * thing in a card 340px tall and stopped reading as a step number at all.
 */
.geba-field-list__mark--number {
  width: 44px;
  height: 44px;
  margin: 0;
  display: grid;
  place-items: center;
  border-radius: var(--radius-circle);
  background: var(--blue-50);
  color: var(--blue-600);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  line-height: 1;
}

.is-style-geba-include-cards .geba-field-list__title {
  margin: 0;
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-snug);
  color: var(--text-primary);
  text-wrap: pretty;
}

.is-style-geba-include-cards .geba-field-list__text {
  margin: 0;
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-muted);
  text-wrap: pretty;
}

/* ------------------------------------------------------------ field list */

.geba-field-list__body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.geba-field-list__nested {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/*
 * The nested level's marker is drawn in CSS rather than repeated from the
 * sprite — it is a bullet, and one `<use>` per line for a bullet is markup
 * nobody benefits from.
 */
.geba-field-list__nested-item {
  position: relative;
  padding-inline-start: var(--space-6);
  font-size: var(--text-sm);
  font-weight: var(--weight-light);
  color: var(--text-secondary);
}

.geba-field-list__nested-item::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.55em;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-circle);
  background: var(--blue-300);
}

/* ------------------------------------------------------------ field text */

.geba-field-text > :first-child {
  margin-block-start: 0;
}

.geba-field-text > :last-child {
  margin-block-end: 0;
}

/* ------------------------------------------------- outline on dark bands */

.wp-block-button.geba-button--dark .wp-block-button__link {
  border-color: var(--border-strong);
  color: var(--text-primary);
}

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

@media (max-width: 900px) {
  .geba-service-intro__photo img {
    height: 300px;
  }
}

/* ------------------------------------------------------- project archive */

.geba-section--muted {
  background: var(--surface-muted);
}

.geba-project-archive__query {
  margin-block-start: var(--space-12);
}

/* The archive card carries an excerpt and a place the home page's does not. */
.geba-project-card__text {
  margin: var(--space-3) 0 0;
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-muted);
  text-wrap: pretty;
}

.geba-project-card--detailed .geba-project-card__body {
  display: flex;
  flex-direction: column;
}

/*
 * The place line is pushed to the foot of the card so cards of different copy
 * lengths still line their metadata up across a row.
 */
.geba-project-card__meta {
  gap: var(--space-2);
  margin-block-start: auto;
  padding-block-start: var(--space-5);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.geba-project-card__meta > * {
  margin: 0;
}

/*
 * The year is a separate field from the place, so it is a separate bound
 * paragraph, and the separator belongs to it rather than to the pair. Not
 * every project has one — "Ptuj, od 2023" has no sortable year — so an empty
 * value takes the separator with it.
 */
.geba-project-card__year:not(:empty)::before {
  content: "· ";
}

.geba-project-card__year:empty {
  display: none;
}

/* --------------------------------------------------------- project case */

.geba-project-case__columns {
  gap: var(--space-16);
}

.geba-project-case__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}

.geba-project-case__part {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.geba-project-case__part > * {
  margin: 0;
}

.geba-project-case__part :is(h2) {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

.geba-project-case__text p {
  margin: 0 0 var(--space-4);
  font-size: var(--type-body-size);
  font-weight: var(--weight-light);
  line-height: var(--type-body-leading);
  color: var(--text-secondary);
  text-wrap: pretty;
}

.geba-project-case__text p:last-child {
  margin-block-end: 0;
}

/*
 * A part whose only content is a field that rendered nothing would leave its
 * heading stranded — the reference details one project and the rest carry no
 * case study yet, so this is the common state, not the edge case.
 */
.geba-project-case__part:not(:has(.geba-field-text, .geba-field-list)) {
  display: none;
}

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

/*
 * The aside is a stack of cards and the stack is what sticks, not the first
 * card in it. Sticky on the column rather than on a wrapper inside it because
 * the column is `align-self: flex-start` — content height — and a sticky child
 * of a content-height parent has no room to travel and never moves.
 */
.geba-project-case__aside {
  position: sticky;
  top: calc(var(--header-h-scrolled) + var(--space-6));
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/*
 * One box, three uses: the facts to read, the enquiry to send, the page to
 * print. Written once here so the three stay the same object.
 */
.geba-aside-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  padding: var(--space-8);
  background: var(--surface-muted);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-card);
}

.geba-aside-card > * {
  margin: 0;
}

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

.geba-facts__row {
  gap: var(--space-4);
  padding-block-end: var(--space-3);
  border-block-end: var(--hairline) solid var(--border-subtle);
}

.geba-facts__row:last-child {
  border-block-end: none;
  padding-block-end: 0;
}

/*
 * Every fact is optional. A row whose value rendered empty is hidden rather
 * than shown as a label with nothing after it — `core/post-terms` removes
 * itself when the post has no term, so that row disappears on its own.
 */
.geba-facts__row:has(.geba-facts__value:empty) {
  display: none;
}

/*
 * The label keeps its width and the value wraps. A reference names up to five
 * domains in its "Kategorija" row, and as two shrinkable flex items the long
 * value squeezed its short label until it broke mid-word — "Kateg / orija".
 */
.geba-facts__key {
  flex: none;
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.geba-facts__value {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  text-align: right;
}

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

/* ------------------------------------------------------ aside call to action */

.geba-project-cta__prompt {
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  color: var(--text-muted);
}

/*
 * The pill fills its card, the way the print button below it does. A lone
 * control sized to its own words leaves a ragged edge down the aside, and the
 * two cards then disagree about where their right-hand side is.
 *
 * All three levels have to be told: `wp-block-buttons` is a flex row that sizes
 * its items to content, `wp-block-button` is the item, and the link inside it is
 * `inline-flex` from the pill style, so it would sit at its text width inside a
 * full-width parent.
 */
.geba-project-cta .wp-block-buttons,
.geba-project-cta .wp-block-button,
.geba-project-cta .wp-block-button__link {
  width: 100%;
}

/*
 * The two action cards are tighter than the facts card: a card holding one
 * control does not need the breathing room of a five-row table.
 */
.geba-project-cta,
.geba-project-print {
  gap: var(--space-4);
  padding: var(--space-6);
}

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

/*
 * `geba/print-button` ships `hidden` and unhides itself from `view.js`, so
 * without JavaScript the card would be an empty box. It is not there at all
 * instead — see the block's render.php.
 */
.geba-project-print:not(:has(.geba-print-button:not([hidden]))) {
  display: none;
}

/* On paper the button has already done its job. */
@media print {
  .geba-project-print {
    display: none;
  }
}

/* ------------------------------------------------------- project gallery */

/*
 * `geba/field-gallery` renders nothing without photographs, which would leave
 * the band as a heading over an empty stripe.
 */
.geba-project-gallery:not(:has(.geba-field-gallery)) {
  display: none;
}

@media (max-width: 900px) {
  .geba-project-case__aside {
    position: static;
  }

  .geba-project-case__columns {
    gap: var(--space-10);
  }
}

/* --------------------------------------------------------- stat cards */

/*
 * `geba/repeater-list` as the project's Rezultati row: the repeater's `value`
 * is the title and its `label` the text, so the same block that renders a
 * bulleted list renders a row of figures with no extra markup.
 */
.geba-field-list.is-style-geba-stat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
  gap: var(--grid-gap-sm);
}

.is-style-geba-stat-cards .geba-field-list__item {
  padding: var(--space-6);
  background: var(--surface-muted);
  border-radius: var(--radius-card);
}

.is-style-geba-stat-cards .geba-field-list__title {
  font-size: var(--text-3xl);
  font-weight: var(--weight-black);
  line-height: var(--leading-flat);
  letter-spacing: var(--tracking-tight);
  color: var(--text-primary);
}

.is-style-geba-stat-cards .geba-field-list__text {
  margin-block-start: 4px;
  font-size: var(--text-sm);
  font-weight: var(--weight-light);
  color: var(--text-muted);
}

/* ------------------------------------------------- page hero strapline */

/*
 * The project hero's line under the title — the reference prints the project's
 * category there.
 */
.geba-hero-page__lead {
  /* The hero column's `gap` is the step; see `.geba-hero-page__pills`. */
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--weight-light);
  color: var(--text-inverse-muted);
}

.geba-hero-page__lead:empty {
  display: none;
}

/* ------------------------------------------------------------ about intro */

.geba-about-intro__columns {
  gap: var(--space-16);
}

.geba-about-intro__body > * {
  margin-block: 0;
}

.geba-about-intro__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

/*
 * `>`, not a descendant: the kicker in this column is a paragraph too, and as a
 * descendant rule this beat `.geba-eyebrow` on specificity and rendered "O nas"
 * at 18px in body grey — the one eyebrow on the site that did not look like an
 * eyebrow. The two prose paragraphs are direct children; the kicker is inside
 * `.geba-section-heading`.
 */
.geba-about-intro__body > p {
  font-size: var(--type-body-size);
  font-weight: var(--weight-light);
  line-height: var(--type-body-leading);
  color: var(--text-secondary);
  text-wrap: pretty;
}

/*
 * The photograph's own height, up to the 420px the reference drew. The client's
 * photograph here is the team, at 2.2:1: a fixed 420px cropped the two people
 * on the left at 1280 and half the team on a phone. A landscape photograph now
 * shows whole; a taller one is still cut to 420 and covered as before.
 */
.geba-about-intro__media img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 420px;
  object-fit: cover;
}

/* ---------------------------------------------------------------- values */

/*
 * `auto-fit`, which is what the reference writes, and not core's `auto-fill`.
 *
 * A `grid` layout with `minimumColumnWidth` emits
 * `repeat(auto-fill, minmax(min(280px, 100%), 1fr))`. `auto-fill` keeps the
 * empty tracks it can fit, so three values at the 1232px measure were laid out
 * as three cards in a four-track grid: 287px each and a hole on the right,
 * rather than three columns filling the row. `auto-fit` collapses the empty
 * track and the three share the width.
 */
.geba-values__grid {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  gap: var(--grid-gap);
}

.geba-value-card {
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-8);
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
}

.geba-value-card > * {
  margin: 0;
}

/*
 * A value card's icon. The reference draws it as `IconCircle` at 60px in the
 * soft tone — a disc, not the rounded square the service rows use.
 */
.geba-value-card__icon {
  width: 60px;
  height: 60px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-circle);
  background: var(--blue-50);
  color: var(--blue-600);
}

/* No heading above them, so the band's own padding is the only space needed. */
.geba-career-why .geba-values__grid {
  margin-block: 0;
}

.geba-value-card__title {
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-snug);
}

.geba-value-card__text {
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-muted);
  text-wrap: pretty;
}

/* ------------------------------------------------------------------- team */

.geba-team__grid {
  gap: var(--grid-gap);
}

.geba-team-card {
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.geba-team-card > * {
  margin: 0;
}

/*
 * Nobody has a portrait yet, and the reference draws that state rather than
 * collapsing the card. It cannot be drawn on the figure: core renders no
 * `core/image` at all when its bound URL is empty, so there is no figure to
 * style. The empty frame is the card's own `::before`, present only when the
 * figure is not.
 */
.geba-team-card:not(:has(.geba-team-card__portrait))::before {
  content: "Portret · fotografija v pripravi";
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 5;
  padding-inline: var(--space-4);
  background: var(--surface-muted);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-media);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-align: center;
  text-transform: uppercase;
  color: var(--text-muted);
}

.geba-team-card__portrait {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--surface-muted);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-media);
}

.geba-team-card__portrait img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.geba-team-card__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.geba-team-card__body > * {
  margin: 0;
}

.geba-team-card__name {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-snug);
}

.geba-team-card__role {
  font-size: var(--text-sm);
  font-weight: var(--weight-light);
  color: var(--text-muted);
}

/*
 * The telephone and e-mail lines. `geba/post` answers an empty field with
 * nothing, so a member who has no number keeps an empty paragraph, and it takes
 * no room. The first line that has something stands off the role.
 */
.geba-team-card__contact {
  font-size: var(--text-sm);
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.geba-team-card__contact:empty {
  display: none;
}

.geba-team-card__contact:not(:empty) {
  margin-block-start: var(--space-2);
}

.geba-team-card__contact:not(:empty) ~ .geba-team-card__contact {
  margin-block-start: 0;
}

.geba-team-card__contact a {
  color: inherit;
  text-decoration: none;
  transition: color var(--dur-base) var(--ease-standard);
}

.geba-team-card__contact a:hover {
  color: var(--link-color-hover);
}

/* -------------------------------------------------------- post archive */

.geba-post-archive__query {
  margin-block-start: var(--space-12);
}

.geba-blog-card--detailed .geba-blog-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  text-align: start;
}

.geba-blog-card__kicker {
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  line-height: 1.3;
  text-transform: uppercase;
  color: var(--text-muted);
}

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

.geba-blog-card--detailed .geba-blog-card__title {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  line-height: 1.3;
  text-align: start;
  text-wrap: pretty;
}

.geba-blog-card__text {
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  line-height: var(--leading-relaxed);
  color: var(--text-muted);
  text-wrap: pretty;
}

.geba-blog-card--detailed .geba-blog-card__date {
  margin-block-start: auto;
  padding-block-start: var(--space-4);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/*
 * The reference draws the newest post as a wide feature above the grid. It is
 * the grid's own first card, widened here — a second query for it would either
 * repeat the post below or need an offset, and an offset cannot survive
 * pagination.
 *
 * Only where there is more than one column to span: at one column the rule
 * would do nothing but stretch the photograph.
 */
@media (min-width: 900px) {
  .geba-post-archive__grid > li:first-child {
    grid-column: 1 / -1;
  }

  .geba-post-archive__grid > li:first-child .geba-blog-card {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: stretch;
  }

  .geba-post-archive__grid > li:first-child .geba-blog-card__media {
    height: 100%;
  }

  .geba-post-archive__grid > li:first-child .geba-blog-card__media img {
    height: 100%;
    min-height: 320px;
  }

  .geba-post-archive__grid > li:first-child .geba-blog-card__body {
    justify-content: center;
    padding: var(--space-12);
    gap: var(--space-3);
  }

  .geba-post-archive__grid > li:first-child .geba-blog-card__title {
    font-size: var(--text-3xl);
    letter-spacing: var(--tracking-tight);
  }

  .geba-post-archive__grid > li:first-child .geba-blog-card__text {
    font-size: var(--type-body-size);
  }
}

/* ----------------------------------------------------------- post single */

/* The post hero is the one hero the reference sets left, in a narrow measure. */
/*
 * The post hero is the project hero with a narrower measure. It used to be
 * left-aligned as well, which read as a third kind of hero on a site that has
 * two — and left the pills centred over a left-aligned title, because the pill
 * row is a flex box that shrinks to its content while the title fills the
 * column.
 */
.geba-hero-page--post .geba-hero-page__inner {
  max-width: 900px;
}

/*
 * No margin of its own: the step between every element in a page hero is the
 * column's `gap`, and a margin here added a second one on top of it.
 */
.geba-hero-page__pills {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
  margin: 0;
}

.geba-hero-page__pills a {
  display: inline-flex;
  align-items: center;
  padding: 7px 12px 4px;
  background: var(--surface-glass);
  border: var(--hairline) solid var(--border-inverse);
  border-radius: var(--radius-pill);
  color: var(--text-inverse);
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  line-height: 1;
  text-decoration: none;
  text-transform: uppercase;
}

.geba-post__columns {
  gap: var(--space-16);
}

/*
 * The article's rhythm, declared on the prose rather than on the column.
 *
 * `blockGap` is 0 (ADR-030), so `wp-block-post-content` — core's own flow
 * container — separates nothing. This rule used to sit on `.geba-post__body`,
 * whose only child *is* the post content, so it matched once and the whole
 * article ran together with no space between a paragraph and the next thing.
 * Only the headings looked right, because they carry a margin of their own.
 *
 * There is no `max-width` here any more either: the column is two thirds of the
 * measure, which is the measure the article is meant to be read at.
 */
.geba-post__body .wp-block-post-content > * + * {
  margin-block-start: var(--space-6);
}

.geba-post__body .wp-block-post-content > :is(h2) {
  margin-block-start: var(--space-12);
}

.geba-post__body :is(h2) {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

/*
 * `:where()` keeps this at one class of specificity, so a paragraph carrying a
 * block style — the lead, above all — still wins and keeps its own size. The
 * plain descendant selector would have beaten it on order alone.
 */
.geba-post__body :where(p, li) {
  font-size: var(--type-body-size);
  font-weight: var(--weight-light);
  line-height: var(--type-body-leading);
  color: var(--text-secondary);
  text-wrap: pretty;
}

/*
 * `margin-block-start` is left to the prose rhythm above; zeroing all four
 * sides here beat that rule on source order and welded the quote to the
 * paragraph before it.
 */
.geba-post__body .wp-block-quote {
  margin-block-end: 0;
  margin-inline: 0;
  padding: var(--space-8);
  background: var(--surface-muted);
  border-radius: var(--radius-card);
  border: none;
}

.geba-post__body .wp-block-quote p {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
}

.geba-post__aside {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  position: sticky;
  top: calc(var(--header-h-scrolled) + var(--space-6));
  align-self: start;
}

.geba-post-cta {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-8);
  background: var(--surface-brand-soft);
  border-radius: var(--radius-card);
}

.geba-post-cta > * {
  margin: 0;
}

.geba-post-cta__title {
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-snug);
  text-wrap: pretty;
}

.geba-post-more {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.geba-post-more > * {
  margin: 0;
}

.geba-post-more__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.geba-post-more__item {
  margin: 0;
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  line-height: var(--leading-snug);
}

.geba-post-more__item a {
  color: var(--text-primary);
  text-decoration: none;
}

.geba-post-more__item a:hover {
  color: var(--link-color-hover);
}

@media (max-width: 900px) {
  .geba-post__aside {
    position: static;
  }
}

/* ------------------------------------------------------------- dark bands */

/*
 * The dark bands are sand (ADR-084): the client asked for a bright site and
 * chose sand with the brand blue over black. Everything inside a band reads the
 * inverse tokens — the cards, the headings, the lead, the outline buttons — so
 * the band redefines those and the components follow without knowing. The
 * class names stay, because the pages store them; "dark" is historical.
 */
.geba-section--dark,
.wp-block-group.is-style-geba-section-dark {
  --surface-inverse: var(--surface-band);
  --surface-inverse-soft: var(--surface-band);
  --surface-glass: var(--ink-0);
  --text-inverse: var(--text-primary);
  --text-inverse-muted: var(--ink-600);
  --border-inverse: var(--border-band);
  --link-color-inverse: var(--text-brand);
  --blue-300: var(--blue-600);
}

/*
 * The dark section. It was only ever defined by the block style variation, so
 * a pattern that wrote the class itself — the careers steps — got no band.
 */
.geba-section--dark {
  background: var(--surface-inverse);
  color: var(--text-inverse);
}

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

.geba-cta-band__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 660px;
}

.geba-cta-band__text > * {
  margin: 0;
}

.geba-cta-band__text p {
  font-size: var(--type-body-size);
  font-weight: var(--weight-light);
  line-height: var(--type-body-leading);
  color: var(--text-inverse-muted);
  text-wrap: pretty;
}

/* --------------------------------------------------------------- careers */

.geba-jobs__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  list-style: none;
  margin: 0;
  padding: 0;
}

/*
 * A position is a card, not a row in a list, so it undoes two things the shared
 * `is-style-geba-accordion` block style adds for the accordions that *are* rows:
 * the rule under each one, and the outer `padding-block` — which sat on top of
 * this card's own summary padding and put 52px above every job title.
 */
.geba-job {
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  /* The card names its own colour, so it reads the same on whichever band it is
     put: on a dark one the inherited `--text-inverse` would be white on white.
     The keyline is the application form's card, and it is what draws a white
     card on the white band the positions sit on now. */
  color: var(--text-primary);
}

.geba-job.is-style-geba-accordion {
  padding-block: 0;
  border-block-end: none;
}

/* The marker comes from the theme's `is-style-geba-accordion` block style. */
.geba-job > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  padding: var(--space-7) var(--space-8);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-snug);
  color: var(--text-primary);
  cursor: pointer;
}

.geba-job > :not(summary) {
  margin-inline: var(--space-8);
  /* Not `padding-block-start: var(--space-4)`, which the block style sets on
     every panel child and which stacked on the margins below. */
  padding-block-start: 0;
}

/* The body of an open position is stacked blocks, not a laid-out component. */
.geba-job > * + * {
  margin-block-start: var(--space-5);
}

/* The header's own bottom padding is the space above the first panel row. */
.geba-job > summary + * {
  margin-block-start: 0;
}

.geba-job > :last-child {
  margin-block-end: var(--space-8);
}

.geba-job__meta {
  gap: var(--space-4);
}

.geba-job__tag {
  gap: var(--space-2);
  margin: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-light);
  color: var(--text-muted);
}

.geba-job__tag .geba-icon {
  color: var(--blue-600);
}

.geba-job__tag-label {
  margin: 0;
}

/* A position with no location or no contract type shows neither the icon. */
.geba-job__tag:has(> .geba-job__tag-label:empty) {
  display: none;
}

.geba-job__text p {
  margin: 0;
  font-size: var(--type-body-size);
  font-weight: var(--weight-light);
  line-height: var(--type-body-leading);
  color: var(--text-secondary);
  text-wrap: pretty;
}

/*
 * The requirements list, restored to its light-band colours.
 *
 * `.geba-section--dark .geba-field-list__*` inverts the marker and the copy for
 * a list printed straight onto the band. This one is printed onto a white card
 * inside that band, where `--blue-300` and `--text-inverse-muted` are all but
 * invisible. Same specificity, later in the file, so these win — and they are
 * scoped to the card rather than turning the dark rules off for everyone.
 */
.geba-job .geba-field-list__mark {
  color: var(--blue-600);
}

.geba-job .geba-field-list__title {
  color: var(--text-primary);
}

.geba-job .geba-field-list__text {
  color: var(--text-secondary);
}

/*
 * A position's lists — profile, tasks, what it offers — each under its own
 * heading. A list with no rows renders nothing, and its heading would be left
 * standing alone, so the part goes with it.
 */
.geba-job__part {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.geba-job__part:not(:has(.geba-field-list)) {
  display: none;
}

.geba-job__part > * {
  margin: 0;
}

/* (0,2,0), over the dark band's `:is(h1, …)` at (0,1,1): the card is white. */
.geba-job .geba-job__subhead {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-snug);
  color: var(--text-primary);
}

/* The single page binds location and contract type into bare paragraphs. */
.geba-job__tag:empty {
  display: none;
}

/*
 * The student position names neither, and an empty row would still take its
 * place in the stack — a gap above the description that no other card has.
 */
.geba-job__meta:not(
  :has(.geba-job__tag-label:not(:empty), p.geba-job__tag:not(:empty))
) {
  display: none;
}

/* The hiring steps: numbered by a counter so the editor can reorder them. */
.geba-career-process__grid {
  counter-reset: geba-career-step;
  gap: var(--grid-gap);
  margin-block-start: var(--space-12);
}

.geba-career-step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  counter-increment: geba-career-step;
  text-align: center;
}

.geba-career-step > * {
  margin: 0;
}

.geba-career-step::before {
  content: counter(geba-career-step);
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  margin-block-end: var(--space-3);
  background: var(--surface-glass);
  border: var(--hairline) solid var(--border-inverse);
  border-radius: var(--radius-circle);
  font-size: var(--text-xl);
  font-weight: var(--weight-black);
  color: var(--text-inverse);
}

.geba-career-step__title {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  color: var(--text-inverse);
}

.geba-career-step__text {
  max-width: 230px;
  font-size: var(--text-sm);
  font-weight: var(--weight-light);
  color: var(--text-inverse-muted);
  text-wrap: pretty;
}

/* --------------------------------------------------------------- contact */

.geba-section--tight {
  padding-block: var(--section-y-sm);
}

.geba-contact-facts__grid {
  gap: var(--grid-gap-sm);
}

.geba-contact-fact {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--space-7);
  background: var(--surface-muted);
  border-radius: var(--radius-card);
}

.geba-contact-fact > * {
  margin: 0;
}

.geba-contact-fact__title {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-snug);
}

.geba-contact-fact__text {
  font-size: var(--text-sm);
  font-weight: var(--weight-light);
  color: var(--text-muted);
}

.geba-contact__columns {
  gap: var(--space-12);
}

.geba-contact-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--pad-card-lg);
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
}

.geba-contact-card > * {
  margin: 0;
}

.geba-contact-card__title {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
}

.geba-contact-card__lead {
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  color: var(--text-muted);
  text-wrap: pretty;
}

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

.geba-contact-details {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-8);
  background: var(--surface-inverse);
  border-radius: var(--radius-card);
  color: var(--text-inverse);
}

.geba-contact-details > * {
  margin: 0;
}

.geba-contact-details__rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.geba-contact-row > * {
  margin: 0;
}

.geba-contact-row__key {
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-inverse-muted);
}

.geba-contact-row__value {
  margin-block-start: 4px;
  font-size: var(--text-base);
  color: var(--text-inverse);
}

.geba-contact-details__rule {
  margin: 0;
  border: none;
  border-block-start: 1px solid rgba(255, 255, 255, 0.14);
}

.geba-contact-details__legal {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.geba-contact-details__legal p {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-light);
  color: var(--text-inverse-muted);
}

/*
 * The map is a panel, not an embed. An embedded Google map is a third-party
 * script with consent implications, and whether to carry it is the client's
 * call — the reference draws the same placeholder.
 */
.geba-contact-map {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  height: 320px;
  background: var(--surface-muted);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-media);
}

.geba-contact-map > * {
  margin: 0;
}

.geba-contact-map__address {
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
}

.geba-contact-map__note {
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/* ----------------------------------------------------------- entry screen */

/*
 * The division chooser. It is the one screen with no header and no footer, so
 * it lays itself out over the full viewport: the bar at the top, the choice in
 * the middle.
 *
 * Sand, not the near-black the reference drew: the client found the screen too
 * dark and asked for a lighter, warmer ground (October 2026). Two layers — a
 * sand gradient, lightest behind the headline and deepening towards the edges,
 * and under the panels a faint bloom of the brand blue, so the blue on the
 * screen belongs to it rather than sitting on it. The panels' drafts used a
 * flatter sand, kept below as a comment.
 *
 * The panels are the direction the client chose out of four (ADR-080), with
 * the eyebrow and the main link in the logo's blue and three G's behind the
 * panels. ADR-082.
 */
.geba-entry {
  /*
   * The room either side of the panels. The wide measure's 280px on a desktop;
   * on a laptop enough for a good part of a G to show beside each side panel
   * (see the G's below); at 1024 no more than the bar can spare without
   * wrapping. The bar and the headline share the measure, so the logo stands
   * over the left panel's edge.
   */
  --geba-entry-margin: clamp(var(--gutter-lg), 18vw - 120px, 280px);
  --geba-entry-measure: min(
    var(--container-wide) - 2 * var(--gutter-lg),
    100% - 2 * var(--geba-entry-margin)
  );

  position: relative;
  overflow: hidden;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /*background: linear-gradient(180deg, var(--sand-50) 0%, var(--sand-100) 38%);*/
  background:
    radial-gradient(
      58% 46% at 50% 64%,
      color-mix(in srgb, var(--blue-200) 38%, transparent),
      transparent 72%
    ),
    radial-gradient(
      130% 95% at 50% 0%,
      var(--sand-50) 0%,
      var(--sand-100) 50%,
      var(--sand-200) 100%
    );
  background-color: var(--sand-100);
  color: var(--text-primary);
  isolation: isolate;
}

.geba-entry > .wp-block-post-content {
  flex: 1;
  display: flex;
  flex-direction: column;
}

/*
 * The screen sets its own measure — see the template. The contact row under
 * the panels shares it, or on a phone its e-mail tile and its last social icon
 * touched the screen's edges.
 */
.geba-entry__bar,
.geba-entry__grid,
.geba-entry-contact--under {
  width: 100%;
  max-width: var(--geba-entry-measure);
  margin-inline: auto;
}

/*
 * The headline is not held in by the G's margin. The German one is a good deal
 * longer, and on the panels' measure it took two lines from 1280 to 1536 and
 * pushed the panels under the fold.
 */
.geba-entry__intro {
  width: 100%;
  max-width: min(
    var(--container-wide) - 2 * var(--gutter-lg),
    100% - 2 * var(--gutter)
  );
  margin-inline: auto;
}

/* Sized to the viewport, so the three panels stand above the fold at 1280×800. */
.geba-entry__bar {
  gap: var(--space-6);
  padding-block: clamp(18px, 3.4vh, var(--space-8));
}

.geba-entry__logo {
  margin: 0;
}

.geba-entry__logo img {
  display: block;
  width: 150px;
  height: auto;
}

.geba-entry__contact {
  gap: var(--space-6);
  align-items: center;
}

/*
 * The bar's right-hand side: two contact tiles, the social profiles and the
 * language switcher, on one line until the viewport takes it apart.
 */
.geba-entry__aside {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6);
}

/*
 * The contact row, printed twice by `geba_entry_contact_row()` and shown once.
 *
 * In the bar it is part of the aside's row. Under the doors it is a centred
 * band of its own, and that is the only version below 700px: four social icons,
 * two contact tiles and a language menu wrapped the bar into three lines and
 * made a 213px header out of a screen whose whole job is to offer three doors.
 */
.geba-entry-contact {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6);
}

.geba-entry-contact--under {
  display: none;
}

@media (max-width: 700px) {
  .geba-entry-contact--bar {
    display: none;
  }

  .geba-entry-contact--under {
    display: flex;
    justify-content: center;
    gap: var(--space-6) var(--space-8);
    margin-block-start: var(--space-10);
  }
}

.geba-entry__contact-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  font-size: var(--text-xs);
  line-height: 1.3;
  color: var(--text-muted);
  text-decoration: none;
}

/*
 * The icon sits in its own tile: white, with the same keyline as the social
 * icons and the language menu beside it, and the icon in the brand blue. The
 * white chips this screen first carried had no keyline and were too quiet on
 * the sand to read as controls, and the pale blue tiles that replaced them did
 * not match the outlined icons in the same row (October 2026).
 */
.geba-entry__tile {
  flex: none;
  display: grid;
  place-items: center;
  width: 50px;
  height: 50px;
  border: var(--hairline) solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-page);
  color: var(--blue-600);
  transition: var(--transition-control);
}

/*
 * The social icons and the language menu keep the header's outlined style
 * (parts.css), the icons on white rather than on the sand. Every chip in the
 * row answers the cursor the same way.
 */
.geba-entry .geba-socials a {
  background: var(--surface-page);
}

.geba-entry__contact-item:hover .geba-entry__tile,
.geba-entry .geba-socials a:hover {
  border-color: var(--geba-blue);
  background: var(--surface-brand-soft);
}

.geba-entry__contact-text {
  display: flex;
  flex-direction: column;
}

.geba-entry__contact-text strong {
  font-weight: var(--weight-bold);
  color: var(--text-primary);
}

.geba-entry__body {
  flex: 1;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(var(--space-1), 2vh, var(--space-7))
    clamp(var(--space-10), 7vh, var(--space-20));
}

.geba-entry__intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  margin-block-end: clamp(var(--space-7), 5vh, var(--space-14));
  text-align: center;
}

/*
 * The single watermark the screen carried before the three G's below. The
 * Slovenian page still stores a copy of it from an earlier composition, and
 * that page carries hand edits, so the copy is hidden here rather than cut out
 * of the content.
 */
.geba-entry__mark {
  display: none;
}

.geba-entry__intro > * {
  margin: 0;
}

/*
 * The company's name over the three panels, set as a name rather than as a
 * section eyebrow: the client asked for it larger and heavier than the small,
 * widely tracked label every other section opens with (October 2026). In the
 * same blue as every other eyebrow, though: for a while it took the logo's
 * lighter blue, which is 2.4:1 on the sand, and the client asked for it to
 * match the eyebrows instead. The stored paragraph also carries
 * `geba-eyebrow--inverse` from the screen's dark days, hence the colour here.
 */
.geba-entry__intro .geba-eyebrow {
  font-size: var(--text-base);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-caps);
  color: var(--text-brand);
}

.geba-entry__title {
  font-size: clamp(32px, 1.2rem + 2.4vw, 54px);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-primary);
  text-wrap: balance;
}

/*
 * Three tall photographs side by side, the middle one wider and in front: it is
 * the division this site is about.
 *
 * Each is framed in the sand, a spread shadow in the ground's own colour, and
 * they overlap by the frame's width, so the overlap reads as one picture laid
 * over the next. The panel under the cursor widens and the other two narrow to
 * their titles. The row's height follows the viewport, so the three stand above
 * the fold from 1280×800 up.
 */
.geba-entry__grid {
  --geba-panel-h: clamp(380px, 58vh, 640px);

  position: relative;
  display: flex;
  align-items: stretch;
  height: var(--geba-panel-h);
  isolation: isolate;
}

/*
 * Three white G's behind the panels, where the client placed them (October
 * 2026): one half under the left panel, one half under the right, and one
 * behind the middle panel. All three are one size, so the middle one is all
 * but hidden by its panel, which is what the client asked of it.
 *
 * Drawn here rather than printed as images, because they are placed by the
 * panels and the panels are this grid's. `z-index: -1` inside the grid's own
 * stacking context (`isolation` above) puts them on the ground and under the
 * doors, and each door's sand frame then cuts the G where it goes under. The
 * outer pair is one box a G's width wider than the grid, with a G at either
 * end, so each is centred on the outer edge of its panel. How much of it shows
 * is the screen's margin: half at 1920, about a third on a laptop.
 */
.geba-entry__grid::before,
.geba-entry__grid::after {
  --geba-g: calc(var(--geba-panel-h) * 0.85);

  content: "";
  position: absolute;
  z-index: -1;
  inset-block-start: 50%;
  translate: 0 -50%;
  height: var(--geba-g);
  background: url("../img/g-mark.svg") center / contain no-repeat;
  opacity: 0.8;
  pointer-events: none;
}

.geba-entry__grid::before {
  inset-inline: calc(var(--geba-g) / -2);
  background:
    url("../img/g-mark.svg") left center / contain no-repeat,
    url("../img/g-mark.svg") right center / contain no-repeat;
}

.geba-entry__grid::after {
  inset-inline: 0;
}

.geba-door {
  --geba-door-frame: var(--space-2);
  --geba-door-transition:
    flex-grow var(--dur-reveal) var(--ease-out-expo),
    box-shadow var(--dur-reveal) var(--ease-out-expo);

  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-width: 0;
  margin-inline: calc(-1 * var(--geba-door-frame));
  overflow: hidden;
  border-radius: 30px;
  background: var(--blue-950);
  box-shadow:
    0 0 0 var(--geba-door-frame) var(--sand-100),
    0 40px 70px -40px rgb(94 72 40 / 50%);
  isolation: isolate;
  transition: var(--geba-door-transition);
}

.geba-door:first-child {
  margin-inline-start: 0;
}

.geba-door:last-child {
  margin-inline-end: 0;
}

.geba-door--side {
  z-index: 1;
}

.geba-door--main {
  z-index: 2;
  flex-grow: 1.5;
}

/*
 * The grid arrives with a stagger, and an arrival's `transition` lands on each
 * door at (0,3,0) and takes the whole list with it, so the panels had nothing
 * to widen on. Restated here with both halves (ADR-063) — and with the delays
 * split: the arrival's properties keep the stagger, the door's own start at
 * once, or the third door would answer the cursor 280ms late.
 */
.geba-js [data-geba-motion][data-geba-motion-stagger] > .geba-door {
  --geba-door-arrival: calc(
    var(--geba-motion-delay) + var(--geba-motion-index) *
      var(--geba-motion-stagger)
  );

  transition: var(--geba-motion-transition), var(--geba-door-transition);
  transition-delay:
    var(--geba-door-arrival), var(--geba-door-arrival),
    var(--geba-door-arrival), var(--geba-door-arrival), 0s, 0s;
}

.geba-door:is(:hover, :focus-within) {
  z-index: 4;
  box-shadow:
    0 0 0 var(--geba-door-frame) var(--sand-100),
    0 2px 6px rgb(94 72 40 / 12%),
    0 60px 110px -44px rgb(94 72 40 / 55%);
}

/*
 * The panel under the cursor widens and the other two narrow to their titles:
 * only side by side, and only where a pointer can hover, because on a phone a
 * tap would leave one panel wide and the others folded. A door holding the
 * keyboard's focus is the one being chosen, the same as one under the cursor.
 * The transitions read `--dur-*`, so under reduced motion the panels change
 * width without moving.
 */
@media (min-width: 901px) and (hover: hover) and (pointer: fine) {
  .geba-entry__grid:has(> .geba-door:is(:hover, :focus-within))
    > .geba-door:not(:hover, :focus-within) {
    flex-grow: 0.75;
  }

  .geba-entry__grid > .geba-door:is(:hover, :focus-within) {
    flex-grow: 2.2;
  }

  .geba-entry__grid:has(> .geba-door:is(:hover, :focus-within))
    > .geba-door:not(:hover, :focus-within)
    .geba-door__text {
    max-height: 0;
    opacity: 0;
  }
}

/*
 * The shine: one narrow highlight swept across a card on hover. Four kinds of
 * card carry it — the entry doors, the service cards, the reference cards and
 * the news cards. It is drawn on the card itself rather than on the photograph
 * so it crosses the whole thing, and each card's `overflow: hidden` is what
 * clips it at the corners.
 *
 * `focus-within` as well as `hover`, so it answers a keyboard the same way.
 *
 * The sweep is a transition rather than an animation, so leaving the card
 * mid-way glides it back out instead of snapping.
 *
 * On a white card — the reference and news cards — the white highlight reads
 * across the photograph and fades out over the copy. That is the nature of
 * white on white; a tint that showed there would be a colour, not a flash.
 */
.geba-door::before,
.geba-service-card::before,
.geba-project-card::before,
.geba-blog-card::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: 55%;
  z-index: 2;
  pointer-events: none;
  translate: -180% 0;
  rotate: 0deg;
  transform: skewX(-18deg);
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgb(255 255 255 / 6%) 38%,
    rgb(255 255 255 / 26%) 50%,
    rgb(255 255 255 / 6%) 62%,
    transparent 100%
  );
  transition: translate var(--dur-shine) var(--ease-out-expo);
}

.geba-door:hover::before,
.geba-door:focus-within::before,
.geba-service-card:hover::before,
.geba-service-card:focus-within::before,
.geba-project-card:hover::before,
.geba-project-card:focus-within::before,
.geba-blog-card:hover::before,
.geba-blog-card:focus-within::before {
  translate: 320% 0;
}

/*
 * The photograph fills the panel, under the copy: `z-index: -1` inside the
 * door's own stacking context. The copy is not raised over it instead, because
 * the copy holds the link and may not be positioned — see the link below.
 */
.geba-door__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  margin: 0;
}

.geba-door .geba-door__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform calc(2 * var(--dur-reveal)) var(--ease-out-expo);
}

.geba-door:is(:hover, :focus-within) .geba-door__media img {
  transform: scale(1.05);
}

/* Deep ocean rising from the foot of the photograph, for the copy to sit on. */
.geba-door__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    transparent 30%,
    color-mix(in srgb, var(--blue-950) 52%, transparent) 58%,
    color-mix(in srgb, var(--blue-950) 92%, transparent) 100%
  );
}

/*
 * The copy at the foot of the panel. Two classes for the margin that puts it
 * there: the door is a flow group, and core's `:root :where(.is-layout-flow)
 * > *` sets every child's top margin at (0,1,0), after this file.
 */
.geba-door > .geba-door__body {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-block-start: auto;
  padding: var(--space-8);
}

.geba-door--main > .geba-door__body {
  gap: var(--space-3);
}

.geba-door__body > * {
  margin: 0;
}

.geba-door__title {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  color: var(--ink-0);
}

.geba-door--main .geba-door__title {
  font-size: var(--text-3xl);
}

/* Capped and clipped, so it can fold away while the cursor is on another panel. */
.geba-door__text {
  max-width: 38ch;
  max-height: 8em;
  overflow: hidden;
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  color: rgb(255 255 255 / 86%);
  text-wrap: pretty;
  transition:
    max-height var(--dur-reveal) var(--ease-out-expo),
    opacity var(--dur-slow) var(--ease-standard);
}

.geba-door--main .geba-door__text {
  font-size: var(--text-lg);
}

/* A door with no description loses the gap. */
.geba-door__text:empty {
  display: none;
}

/*
 * The link is a pill at the foot of the copy. The main door's is filled in the
 * logo's blue, as every filled button on the site is (it was terracotta in the
 * drafts the client chose from); the side doors' are glass that turns white
 * under the cursor.
 */
.geba-door__more {
  padding-block-start: var(--space-3);
}

.geba-door .geba-door__more a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 12px 22px;
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  letter-spacing: 0.01em;
  text-decoration: none;
  transition: var(--transition-control);
}

.geba-door--main .geba-door__more a {
  color: var(--text-on-brand);
  background: var(--geba-blue);
}

.geba-door--main:is(:hover, :focus-within) .geba-door__more a {
  background: var(--blue-600);
}

.geba-door--side .geba-door__more a {
  color: var(--ink-0);
  background: rgb(255 255 255 / 14%);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 60%);
}

.geba-door--side:is(:hover, :focus-within) .geba-door__more a {
  color: var(--ink-900);
  background: var(--ink-0);
}

/*
 * The whole card is the link. The reference draws each door as one anchor;
 * here the door stays a group of blocks the editor can rearrange, and the
 * "Vstopite" link is stretched over it instead. A door whose sister company
 * has no URL yet has no anchor at all, so it stretches nothing.
 *
 * Which is why neither the copy nor the link is ever positioned or filtered:
 * either would become this pseudo-element's containing block, and only the
 * pill would be left to click.
 */
.geba-door__more a::after {
  content: "";
  position: absolute;
  inset: 0;
}

/*
 * One panel above another, the division this site is about first. The G's are
 * for the wide screen only: beside a full-width panel there is no edge to put
 * one behind.
 */
@media (max-width: 900px) {
  .geba-entry {
    --geba-entry-margin: var(--gutter);
  }

  .geba-entry__grid {
    flex-direction: column;
    height: auto;
    gap: var(--space-4);
  }

  .geba-entry__grid::before,
  .geba-entry__grid::after {
    display: none;
  }

  .geba-door {
    flex: none;
    min-height: 260px;
    margin: 0;
  }

  .geba-door--main {
    order: -1;
    min-height: 320px;
  }

  .geba-door > .geba-door__body {
    padding: var(--space-7);
  }

  /* A shorter panel puts more of its copy over the photograph: the shade starts higher. */
  .geba-door__media::after {
    background: linear-gradient(
      180deg,
      color-mix(in srgb, var(--blue-950) 8%, transparent) 0%,
      color-mix(in srgb, var(--blue-950) 62%, transparent) 42%,
      color-mix(in srgb, var(--blue-950) 94%, transparent) 100%
    );
  }
}

/* ------------------------------------------------------- careers: prijava */

/*
 * The application form's card. Same box as the contact card, centred and
 * narrower: this band is a single column at the foot of the careers screen,
 * and a form line longer than about 70 characters is hard to scan.
 */
.geba-career-form__card {
  max-width: 760px;
  margin-inline: auto;
  padding: var(--pad-card-lg);
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  /* The band round it is dark: the card takes the light band's colours back,
     or CF7's messages and the consent's link would be white on white. */
  color: var(--text-primary);
}

.geba-section--dark .geba-career-form__card a:not(.wp-block-button__link) {
  color: var(--link-color);
}

@media (max-width: 640px) {
  .geba-career-form__card {
    padding: var(--pad-card-sm);
  }
}

/* ------------------------------------------------------ a single position */

/*
 * The careers accordion's panel, given a page of its own. Same card box as the
 * application form so the two read as one screen, and the same field blocks
 * inside it so a position looks identical whichever way a visitor reaches it.
 */
.geba-job-single__card {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  max-width: 860px;
  margin-inline: auto;
  padding: var(--pad-card-lg);
  background: var(--ink-0);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
}

.geba-job-single__card > * {
  margin: 0;
}

.geba-job-single__subhead {
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
}

@media (max-width: 640px) {
  .geba-job-single__card {
    padding: var(--pad-card-sm);
  }
}

/* -------------------------------------------------------------------- 404 */

/*
 * The band is a flex column because `blockGap` is `0` (ADR-030): as a flow
 * container nothing here separated from anything, and the screen arrived as
 * five elements stacked edge to edge. Every step is declared, and the two that
 * are not the base step say so.
 */
.geba-404 {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  text-align: center;
}

.geba-404__code {
  margin: 0;
}

.geba-404 .wp-block-heading {
  margin: 0;
  max-width: 18ch;
}

/*
 * The lead needs its own measure and cannot borrow one. `.is-style-geba-lead`
 * in components.css is (0,2,0) and caps at --container-text (1122px), which
 * beats core's constrained-layout rule at (0,1,0) — so the 760px the pattern
 * asks for never reached this paragraph and the sentence ran on one line, wider
 * than the heading above it. Matching that specificity is what takes it back;
 * sections.css loads after components.css, so source order decides.
 *
 * 34ch sits under the heading's 18ch and over the field's 520px: three widths
 * stepping inward, which is the shape the screen wants.
 */
.geba-404 .geba-404__lead {
  margin: 0;
  max-width: 34ch;
}

/*
 * The field is the next thing to do, not the next thing to read, so it sits a
 * step further down than the prose above it.
 */
.geba-404__search {
  width: 100%;
  max-width: 520px;
  margin: var(--space-4) 0 0;
}

/*
 * core/search's "button-inside" variant: the wrapper carries the border and the
 * field is bare inside it, which makes one pill rather than a rectangle with a
 * detached button beside it. The padding is the ring's room, so the button does
 * not touch the edge.
 */
.geba-404__search .wp-block-search__inside-wrapper {
  padding: var(--space-1);
  background: var(--surface-muted);
  border: var(--hairline) solid var(--border-subtle);
  border-radius: var(--radius-pill);
  transition: var(--transition-control);
}

.geba-404__search .wp-block-search__inside-wrapper:hover {
  border-color: var(--border-default);
}

.geba-404__search:focus-within .wp-block-search__inside-wrapper {
  border-color: var(--border-brand);
}

.geba-404__search .wp-block-search__input {
  min-height: 44px;
  padding: 0 var(--space-3);
  background: transparent;
  border: 0;
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  line-height: 1.4;
}

/* The wrapper draws the focus edge, so the field inside must not draw a second. */
.geba-404__search .wp-block-search__input:focus-visible {
  outline: none;
}

.geba-404__search .wp-block-search__input::placeholder {
  color: var(--text-faint);
}

.geba-404__search .wp-block-search__button {
  flex: none;
  min-height: 44px;
  margin: 0;
  padding: 0 var(--space-5);
  background: var(--surface-brand);
  border: 0;
  border-radius: var(--radius-pill);
  color: var(--text-inverse);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  cursor: pointer;
  transition: var(--transition-control);
}

.geba-404__search .wp-block-search__button:hover {
  background: var(--surface-brand-deep);
}

.geba-404__search .wp-block-search__button:active {
  transform: scale(var(--press-scale));
}

/*
 * The way back is the fallback, not the first suggestion, so it sits below the
 * field with a full step of air rather than the base gap.
 */
.geba-404__actions {
  justify-content: center;
  margin-block-start: var(--space-2);
}

@media (max-width: 600px) {
  .geba-404__search .wp-block-search__button {
    padding: 0 var(--space-4);
  }
}
