/* ============================================================
   HYPERLAB — components.css
   Header, navigation, menu overlay, footer, links, tiles, forms
   ============================================================ */

/* ---------- Header ---------- */

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  height: var(--header-h);
  transition:
    background-color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    backdrop-filter var(--dur-fast) var(--ease);
  border-bottom: 1px solid transparent;
}

.site-header.is-scrolled {
  background: rgba(31, 32, 35, 0.82);
  -webkit-backdrop-filter: blur(16px) saturate(1.05);
  backdrop-filter: blur(16px) saturate(1.05);
  border-bottom-color: var(--line-soft);
}

.header-inner {
  height: 100%;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
}

/* Wordmark — typeset live in Jost; the badge itself lives in the footer.
   The leading vertical bar echoes the light bars in the logo. */
/* Brand gold material — every component that carries the logo's own metal.
   Display-scale type only; small tracked labels use flat --gold-editorial. */
.wordmark,
.manifesto-signature-name,
.manifesto-lede,
.manifesto-strike span,
.work-title,
.tile-title,
.service-name {
  background: var(--gold-sheen), var(--gold-metallic);
  background-size: 100% 100%, 100% 100%;
  background-repeat: no-repeat, no-repeat;
  background-blend-mode: screen, normal;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter: var(--gold-depth-filter);
}

/* Hero statement blocks carry the logo's metal, same treatment as the
   wordmark above. They are multi-line, so the material is tiled at the
   line height instead of stretched once across the whole block: every
   line gets the full champagne-to-bronze run rather than line 1 taking
   the highlight and the last line the dark bevel. 1.15em == line-height.
   .manifesto-strike has one span per line; .manifesto-lede is a single
   <p> whose three lines come from max-width, so tiling is what gives it
   per-line runs without touching the markup. */
.manifesto-lede,
.manifesto-strike span {
  background-size: 100% 100%, 100% 1.15em;
  background-repeat: no-repeat, repeat-y;
}

/* Solid fallback: the rule above paints the gold by clipping a background
   to the glyphs, which needs transparent text. Where background-clip: text
   is unavailable the fill never paints and transparent text would render
   as nothing at all — so declare the solid gold unconditionally and only
   go transparent where the clip is actually supported. */
.manifesto-lede,
.manifesto-strike span {
  color: var(--gold-editorial);
  -webkit-text-fill-color: var(--gold-editorial);
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .manifesto-lede,
  .manifesto-strike span {
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

.wordmark {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mark);
  font-weight: 300;
  font-size: 15px;
  letter-spacing: 0.44em;
  white-space: nowrap;
}
.wordmark::before {
  content: "";
  width: 1px;
  height: 16px;
  background: var(--gold-soft);
  margin-right: 18px;
}

/* Primary nav */
.site-nav { display: flex; gap: clamp(24px, 3vw, 44px); }

.site-nav a {
  position: relative;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-2);
  padding-block: 8px;
  transition: color var(--dur-fast) var(--ease);
}
.site-nav a::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease);
}
.site-nav a:hover { color: var(--text); }
.site-nav a:hover::after { transform: scaleX(1); }
.site-nav a[aria-current="page"] { color: var(--text); }
.site-nav a[aria-current="page"]::after { transform: scaleX(1); }

/* Menu toggle (mobile) */
.menu-toggle {
  display: none;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text);
  padding: 8px 0;
}

@media (max-width: 899px) {
  .site-nav { display: none; }
  .menu-toggle { display: block; }

  /* ⚠️ AUF PROJEKTSEITEN BLEIBT "WORK" IM KOPF SICHTBAR — neben MENU.
     Auf dem Desktop steht WORK dauerhaft in der Kopfzeile; das ist der
     Ausstieg aus einem Projekt zurueck ins Archiv. Unter 900px verschwand
     die ganze .site-nav und wurde durch einen 42x34-Knopf mit der
     Aufschrift "Menu" ersetzt. Erreichbar war der Ausstieg damit weiterhin
     (der Kopf ist position:fixed und blendet nie aus), aber NICHTS auf dem
     Schirm sagte noch "Work" — auf einer Fallseite von 7437px Hoehe lag der
     einzige Inhaltslink bei 87 %, und der fuehrt seitwaerts zum naechsten
     Projekt, nicht zurueck.

     ⚠️ GESCOPT AUF data-project, NICHT AUF data-page. data-page="work"
     traegt auch work.html und die zehn Kategorieseiten — dort zeigte der
     Link auf die Seite, auf der man schon steht. Und website-hero-films.html
     ist eine Projektseite, traegt aber data-page="services", denselben Wert
     wie services.html: ueber data-page waere sie nur einzuschliessen, indem
     man Services mit einschliesst. Deshalb ein eigenes Attribut.

     ⚠️ [data-nav="work"], NICHT href. Der Verweis lautet "work.html" im
     Wurzelverzeichnis und "../work.html" auf den Kategorieseiten; ein
     href-Selektor waere je nach Tiefe mal richtig, mal falsch. data-nav
     steht auf 34 von 35 Seiten und ist tiefenunabhaengig. */
  body[data-project] .site-nav { display: flex; }
  body[data-project] .site-nav a:not([data-nav="work"]) { display: none; }
}

/* ---------- Menu overlay (mobile) ---------- */

.menu-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(31, 32, 35, 0.97);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  display: flex;
  flex-direction: column;
  padding: var(--gutter);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--dur-fast) var(--ease),
    visibility 0s linear var(--dur-fast);
}
.menu-overlay.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur-fast) var(--ease);
}

.menu-overlay-head {
  height: var(--header-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-inline: calc(-1 * var(--gutter));
  padding-inline: var(--gutter);
  flex: 0 0 auto;
}

.menu-overlay-links {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(18px, 4vh, 30px);
}

.menu-overlay-links a {
  display: flex;
  align-items: baseline;
  gap: 20px;
  font-family: var(--font-display);
  font-weight: 525;
  font-size: clamp(2rem, 8vw, 3.2rem);
  line-height: 1.1;
  color: var(--text-bright);
}
.menu-overlay-links a .num { font-size: 12px; }

.menu-overlay-foot {
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 24px;
  border-top: 1px solid var(--line-soft);
}

body.menu-locked { overflow: hidden; }

/* ---------- Links & buttons ---------- */

/* Quiet link — caps with a gold underline that slides in */
.link-quiet {
  position: relative;
  display: inline-block;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text);
  padding-bottom: 7px;
}
.link-quiet::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--line);
}
.link-quiet::before {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  z-index: 1;
  width: 100%;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease);
}
.link-quiet:hover::before { transform: scaleX(1); }

/* Ghost pill — hairline border, lifts on hover. Never filled, never gold. */
.btn-ghost {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 15px 30px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text);
  transition:
    background-color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.btn-ghost:hover {
  background: var(--surface);
  border-color: var(--gold-soft);
}
.btn-ghost .arrow { font-family: var(--font-mark); font-weight: 300; }

/* Serif link — for oversized email addresses etc. */
.link-serif {
  font-family: var(--font-display);
  font-weight: 525;
  color: var(--text-bright);
  border-bottom: 1px solid var(--line);
  transition: border-color var(--dur-fast) var(--ease);
}
.link-serif:hover { border-color: var(--gold); }

/* ---------- Work tile ---------- */

.tile { display: block; }

.tile .frame {
  border-radius: 0;               /* film frames stay sharp */
}
.tile .frame > img,
.tile .frame > video {
  transition: transform 900ms var(--ease), opacity 900ms var(--ease);
}
.tile:hover .frame > img,
.tile:hover .frame > video { transform: scale(1.035); }
.tile:hover .frame { background: var(--surface-hover); }

/* ---------- Navigation affordance ----------
   A text label marking a tile that leads to another page. Every .tile is an
   <a> to a case study or a project page, so the marker belongs to the
   component rather than to individual tiles. Inline players — the case
   galleries and the Shorts grid — are figures and cards, not tiles, and are
   untouched.

   It sits on its own line under the title, left aligned, at roughly two
   thirds of .tile-title. The meta line's 10px was tried first and buried it;
   at this size it reads as an invitation without competing with the title.
   Tracking stays at the meta line's 0.18em so it still belongs to that
   system.

   Colour is inherited on purpose and never declared: the white-first block
   in base.css repaints .tile-tag with !important through a universal
   selector, which does not reach pseudo-elements. Copying --text-2 here
   would leave the label a shade darker than the very line it belongs to;
   inheriting follows that rule wherever it moves.

   Always visible, never tied to :hover: trackpad and touch users never see a
   hover state, which is exactly the audience the marker exists for. It is a
   pseudo-element, so it cannot take pointer events and the whole tile stays
   the hit target.

   An earlier version drew a plus in the corner of the frame. It was dropped
   with this label: two signals for one meaning is one too many. */

.tile-meta::after {
  content: "View project \2192";
  font-size: clamp(0.8rem, 1vw, 0.92rem);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.tile-meta::after {
  flex: 0 0 100%;
  margin-top: 12px;
}

/* .next-tease deliberately does NOT get the label. It already announces
   itself with "Next film" above a display-size title, and it sits over a
   poster rather than on the page background, where a tracked 14px line
   reads faint. One announcement is enough there. */

.tile-meta {
  display: flex;
  flex-wrap: wrap;              /* lets the affordance label take its own row */
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-top: 16px;
}

.tile-title {
  position: relative;
  padding-bottom: 5px;
  font-family: var(--font-display);
  font-weight: 525;
  font-size: clamp(1.15rem, 1.5vw, 1.4rem);
  line-height: 1.25;
}
.tile-title::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease);
}
.tile:hover .tile-title::after { transform: scaleX(1); }

.tile-tag {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-2);
  white-space: nowrap;
}

/* ---------- Form elements ---------- */

.field { display: grid; gap: 10px; }

.field input,
.field textarea {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-ui);
  color: var(--text);
  font: inherit;
  padding: 14px 16px;
  transition: border-color var(--dur-fast) var(--ease);
}
.field input::placeholder,
.field textarea::placeholder { color: var(--text-3); }
.field input:focus-visible,
.field textarea:focus-visible {
  outline: none;
  border-color: var(--gold-soft);
}

/* ⚠️ DER SICHTBARE TEIL VON aria-invalid. Bis 23.08.2026 sagte die
   Fehlermeldung "check the highlighted fields", und hervorgehoben war nichts —
   der Satz beschrieb etwas, das es nicht gab. Diese Regel macht ihn wahr.

   ⚠️ NICHT NUR FARBE. Der Rahmen wird ausserdem doppelt so dick (2px statt
   1px), damit die Markierung auch ohne Farbunterscheidung erkennbar ist. Die
   Fehlermeldung nennt die Felder zusaetzlich beim Namen — Farbe ist hier also
   die dritte Ebene, nicht die einzige.

   ⚠️ Gesetzt und entfernt wird die Klasse ausschliesslich vom Handler in
   contact.html, immer gemeinsam mit aria-invalid. Nie einzeln benutzen: sichtbar
   ohne die Ansage waere genau die Halbheit, die hier behoben wurde. */
.field input.is-invalid,
.field textarea.is-invalid {
  border-color: var(--gold);
  border-width: 2px;
}

/* ---------- Footer ---------- */

.site-footer {
  flex-shrink: 0;
  background: var(--bg);
  border-top: 1px solid var(--line);
}

.footer-top {
  display: grid;
  grid-template-columns: minmax(140px, 0.9fr) 1fr 1fr 1.8fr;
  gap: clamp(32px, 4vw, 64px);
  padding-block: clamp(64px, 8vw, 104px);
}

.footer-col { display: grid; gap: 18px; align-content: start; }
.footer-col .label { margin-bottom: 6px; }

.footer-col a {
  font-size: 13px;
  color: var(--text-2);
  transition: color var(--dur-fast) var(--ease);
  justify-self: start;
}
.footer-col a:hover { color: var(--gold-bright); }
.footer-col p { font-size: 13px; color: var(--text-2); }

/* Badge — the circular logo, used exactly as provided.
   Falls back to a quiet ring until the PNG is dropped in assets/logo/. */
.badge {
  width: 104px;
  height: 104px;
  border-radius: 50%;
  overflow: hidden;
  position: relative;
}
.badge img { width: 100%; height: 100%; object-fit: cover; }
.badge.is-fallback {
  border: 1px solid var(--line);
  display: grid;
  place-items: center;
}
.badge.is-fallback::after {
  content: "HYPERLAB";
  font-family: var(--font-mark);
  font-weight: 300;
  font-size: 9px;
  letter-spacing: 0.34em;
  color: var(--text-3);
  padding-left: 0.34em;
}

.footer-cta { align-content: start; display: grid; gap: 22px; }
.footer-cta .display-m { font-size: clamp(1.4rem, 2.2vw, 2.1rem); }

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 16px 32px;
  align-items: center;
  justify-content: space-between;
  padding-block: 26px;
  border-top: 1px solid var(--line-soft);
}

.footer-bottom, .footer-bottom a {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-3);
}
.footer-bottom a { transition: color var(--dur-fast) var(--ease); }
.footer-bottom a:hover { color: var(--gold-bright); }
.footer-bottom-links { display: flex; gap: 28px; }

@media (max-width: 899px) {
  .footer-top { grid-template-columns: 1fr 1fr; }
  .footer-cta { grid-column: 1 / -1; }
}
@media (max-width: 559px) {
  .footer-top { grid-template-columns: 1fr; }
}

/* ---------- Page stub (pre-build placeholder pages) ---------- */

.stub {
  min-height: 72vh;
  display: grid;
  align-content: center;
  gap: 26px;
  padding-top: var(--header-h);
}
.stub .divider { width: 72px; }
.stub-note { color: var(--text-3); font-size: 13px; letter-spacing: 0.04em; }

/* ---------- Menu close ---------- */

.menu-close {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-2);
  transition: color var(--dur-fast) var(--ease);
}
.menu-close:hover { color: var(--text); }

/* ---------- Hero ---------- */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
}

.hero-media { position: absolute; inset: 0; }

/* Placeholder note sits out of the text zone, top right */
.hero-media .frame-note {
  place-items: start end;
  padding-top: calc(var(--header-h) + 32px);
  padding-right: var(--gutter);
}

/* ⚠️ KNOWN ISSUE — vignette effectively runs at 5% opacity (28.07.2026).
   Every .hero-media also carries .grain, and .grain::after (base.css)
   sets `opacity: 0.05` on THIS SAME pseudo-element. Specificity is equal
   (one class + one pseudo), so source order decides: components.css wins
   the background-image and paints the vignette here — but the 0.05 from
   base.css is never overridden and still applies to the whole layer.
   Net effect: the gradient below reads as ~5% of its written strength.
   Measured and confirmed via getComputedStyle(el, '::after').opacity.

   NOT FIXED DELIBERATELY. Restoring it to full strength darkens every
   hero's top and bottom edges substantially, which changes hero
   legibility — the Noah Priest hero currently passes WCAG at its worst
   point (min 6.12:1, target 4.5:1) with the vignette neutralised, so any
   fix has to be re-measured per hero, not just switched on.
   Affects: index.html, noah-priest.html, monztapressure.html. */
.hero-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(26, 20, 15, 0.2) 0%,
    rgba(26, 20, 15, 0.55) 62%,
    rgba(26, 20, 15, 0.92) 100%
  );
}

/* Noah Priest hero only: a flat dark plateau covering the full text
   column, then a wide smooth fade so the pilot and aircraft stay fully
   visible. Sits above the photo but below the existing vertical vignette
   (::after). */
.np-hero-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    rgba(10, 10, 10, 0.42) 0%,
    rgba(10, 10, 10, 0.42) 58%,
    rgba(10, 10, 10, 0) 80%
  );
}

/* Homepage manifesto hero: fade fully to solid --bg at the foot so the
   section boundary below becomes invisible, regardless of image content. */
.hero--manifesto .hero-media::after {
  opacity: 1;
  background: linear-gradient(
    180deg,
    rgba(26, 20, 15, 0.2) 0%,
    rgba(26, 20, 15, 0.55) 55%,
    rgba(26, 20, 15, 0.97) 88%,
    var(--bg) 100%
  );
}

.hero-inner {
  position: relative;
  z-index: 1;
  display: grid;
  gap: clamp(22px, 3vh, 34px);
  padding-top: calc(var(--header-h) + 24px);
  padding-bottom: clamp(28px, 4vh, 44px);
}

.hero-eyebrow { display: flex; align-items: center; gap: 18px; }
.hero-eyebrow .divider { width: 56px; }

.hero-headline { max-width: 14ch; }
.hero-sub { max-width: 56ch; color: var(--text-2); }

/* Noah Priest hero only: three short editorial paragraphs replace the
   single hero-sub line here — generous inter-paragraph spacing and a
   wider column, without touching .hero-sub itself (shared with other
   artist-feature heroes such as Monztapressure). Colour already resolves
   to --text-bright site-wide via the white-first rule below. */
.hero-sub-group {
  display: grid;
  gap: clamp(22px, 3vh, 34px);
  max-width: 62ch;
}
.hero-sub-group .hero-sub {
  max-width: none;
}

.hero-actions {
  display: flex;
  align-items: center;
  gap: 30px;
  flex-wrap: wrap;
  padding-top: 6px;
}

.hero-foot {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-block: 30px 38px;
  border-top: 1px solid var(--line-soft);
}

/* ---------- Hero manifesto ---------- */

.hero--manifesto { justify-content: space-between; }

.manifesto {
  gap: clamp(28px, 5vh, 56px);
  padding-top: calc(var(--header-h) + 8px);
}

.manifesto-lede {
  font-family: var(--font-display);
  font-weight: 525;
  font-size: clamp(1.6rem, 3.2vw, 2.6rem);
  line-height: 1.15;
  letter-spacing: 0.008em;
  text-transform: uppercase;
  text-wrap: balance;
  max-width: 18ch;
}

.manifesto-strike {
  display: flex;
  flex-direction: column;
  font-family: var(--font-display);
  font-weight: 525;
  font-size: clamp(1.6rem, 3.2vw, 2.6rem);
  line-height: 1.15;
  letter-spacing: 0.008em;
  text-transform: uppercase;
  margin-top: clamp(16px, 3vh, 32px);
}

.manifesto-proof {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(1.05rem, 1.8vw, 1.5rem);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
}

.manifesto-signature {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-bottom: 4px;
}

.manifesto-signature-name {
  font-family: var(--font-mark);
  font-weight: 400;
  font-size: clamp(2.7rem, 7.6vw, 6.8rem);
  letter-spacing: 0.15em;
}

.manifesto-signature-tag {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(0.8rem, 1vw, 1.05rem);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* ---------- Simple grids ---------- */

.grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2.5vw, 36px);
}
@media (max-width: 899px) { .grid-3 { grid-template-columns: 1fr; } }

/* ---------- Services strip (compact index) ---------- */

.services-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: clamp(24px, 3vw, 56px);
  border-top: 1px solid var(--line);
}

.services-strip li {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 22px 4px;
  border-bottom: 1px solid var(--line-soft);
}

.services-strip .strip-link {
  display: flex;
  align-items: baseline;
  gap: 16px;
  width: 100%;
}

.services-strip .name {
  position: relative;
  padding-bottom: 4px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text);
}
.services-strip .name::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease);
}
.services-strip .strip-link:hover .name::after { transform: scaleX(1); }

@media (max-width: 899px) {
  .services-strip { grid-template-columns: 1fr; }
}

/* ---------- Editorial work grid ---------- */

.work-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: clamp(20px, 2.5vw, 40px);
  row-gap: clamp(56px, 7vw, 104px);
}

.work-grid .tile { grid-column: span 12; }

@media (min-width: 900px) {
  .work-grid .tile--w7 { grid-column: span 7; }
  .work-grid .tile--w5 { grid-column: span 5; }
  .work-grid .tile--w12 { grid-column: span 12; }
  .work-grid .tile--off { margin-top: clamp(56px, 8vw, 128px); }
}

.tile-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: 14px;
}
/* Client/category label rides with its numeral in gold. Scoped to .tile-head so
   the other .label--muted uses (frame notes, footer headers, "Scroll") stay muted. */
.tile-head .label { color: var(--gold-editorial); }

/* Image fallback pattern: <img data-fb> sits above a .frame-note;
   if the file is missing, main.js removes the img and the note shows. */
.frame > img ~ .frame-note { z-index: 0; }
.frame > img { z-index: 1; }

/* ---------- Filter row ---------- */

.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 28px;
  align-items: baseline;
  margin-bottom: clamp(48px, 6vw, 80px);
}

.filter-link {
  position: relative;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-editorial);
  padding-block: 8px;
  transition: color var(--dur-fast) var(--ease);
}
.filter-link::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease);
}
.filter-link:hover { color: var(--gold-editorial-bright); }
.filter-link.is-active { color: var(--gold-editorial-bright); }
.filter-link.is-active::after { transform: scaleX(1); }

.archive-count { color: var(--text-3); }

/* ---------- Case study ---------- */

.case-hero {
  padding-top: var(--header-h);
  position: relative;
}
.case-hero .frame { box-shadow: none; }

.play-btn {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-items: center;
  cursor: pointer;
}
.play-btn .play-ring {
  width: 92px;
  height: 92px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.35);
  display: grid;
  place-items: center;
  background: rgba(31, 32, 35, 0.35);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.play-btn:hover .play-ring { border-color: var(--gold); transform: scale(1.05); }
.play-btn .play-ring::after {
  content: "";
  display: block;
  margin-left: 4px;
  border-style: solid;
  border-width: 9px 0 9px 15px;
  border-color: transparent transparent transparent var(--text);
}

.case-title-block {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 32px 64px;
  align-items: end;
  padding-block: clamp(48px, 6vw, 80px) clamp(36px, 5vw, 64px);
}
@media (max-width: 899px) { .case-title-block { grid-template-columns: 1fr; align-items: start; } }

.case-meta { display: grid; gap: 12px; text-align: right; }
@media (max-width: 899px) { .case-meta { text-align: left; } }

/* Case metadata reads one step brighter than muted labels elsewhere */
.case-meta p { color: var(--text-bright); }
.case-meta .label--muted { color: var(--text-2); }

.credits { border-top: 1px solid var(--line); }
.credit-row {
  display: grid;
  grid-template-columns: minmax(160px, 240px) 1fr;
  gap: 24px;
  padding-block: 18px;
  border-bottom: 1px solid var(--line-soft);
}
.credit-row dt { color: var(--text-2); }
.credit-row dd { margin: 0; color: var(--text-bright); font-size: 14px; }

.stills-stack { display: grid; gap: clamp(24px, 3.5vw, 56px); }

.next-tease { display: block; position: relative; }
.next-tease .frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(31, 32, 35, 0.55);
  transition: background var(--dur-fast) var(--ease);
}
.next-tease:hover .frame::after { background: rgba(31, 32, 35, 0.35); }
.next-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 18px;
  text-align: center;
  padding: 24px;
}

/* ---------- Service rows (Services page) ---------- */

.service-index { border-bottom: 1px solid var(--line); }

.service-row {
  position: relative;
  display: grid;
  grid-template-columns: 72px 1fr auto;
  align-items: center;
  column-gap: clamp(16px, 3vw, 48px);
  padding-block: clamp(30px, 4vw, 48px);
  border-top: 1px solid var(--line);
  transition: background-color var(--dur-fast) var(--ease);
}
.service-row:hover { background: var(--bg-2); }

.service-cell { display: grid; gap: 10px; }

.service-name {
  font-family: var(--font-display);
  font-weight: 525;
  font-size: clamp(1.5rem, 3vw, 2.6rem);
  line-height: 1.1;
}
.service-line { color: var(--text-2); max-width: 52ch; font-size: 17px; }

/* Work head — shared by the archive pages AND the homepage Selected Work
   section so both read as one continuous portfolio section: gold serif
   title, bright white editorial statement (the archive-side white wins
   against the uniform-gold block via the scoped exception in base.css). */
.work-title {
  font-family: "Playfair Display", Didot, Georgia, serif;
  font-weight: 500;
  font-size: clamp(38px, 4.18vw, 62px);
  line-height: 1.1;
  /* Solid fallback for the metal above — painted whenever
     background-clip: text is unavailable (see Gold decision #3). */
  color: var(--gold-editorial);
  /* Tiled at THIS element's line height (1.1, not the 1.15 the manifesto
     headlines use) so the champagne-to-bronze run maps onto the line. */
  background-size: 100% 100%, 100% 1.1em;
  background-repeat: no-repeat, repeat-y;
}
.work-statement {
  font-family: "Playfair Display", Didot, Georgia, serif;
  font-weight: 500;
  font-size: clamp(30px, 4.8vw, 66px);
  line-height: 1.12;
  letter-spacing: 0.005em;
  margin-top: clamp(18px, 2.4vw, 32px);
  max-width: 18ch;
  color: var(--text-bright);
}
.work-statement > span { display: block; }   /* direct lines only — inline accents inside a line stay inline */

/* Artwork page head — ONE shared treatment for dedicated-page heads that
   carry an uploaded artwork behind the head typography (Services, Studio,
   future pages). Each page provides only its artwork and tuning via
   custom properties on the section element:
     --head-artwork      url(...)           (required)
     --head-artwork-pos  background-position (default center)
     --head-dim-top      overlay strength at the top    (default .35)
     --head-dim-mid      overlay strength at 62%        (default .55)
   The gradient guards readability and dissolves the section into the
   page background at the bottom. Artwork images are always used exactly
   as provided — cover/position/overlay only. */
/* Loop video layer for an artwork head — sits behind the head content in the
   same frame as the still background, which stays visible as poster/fallback.
   In use on work.html and services.html only; work.html emits it from the
   generator (HEAD_VIDEO), services.html carries it in its own markup.
   The loops are 1280x720 / 24 fps / silent / faststart.
   Studio was tried and reverted — the loop read as too restless behind the
   type, so that hero stays on its still. */
.page-head--artwork { position: relative; }
.page-head-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
  pointer-events: none;
}
/* ⚠️ KNOWN ISSUE — this gradient does NOT dim the video (28.07.2026).
   It was documented here for years as "repeated above the video"; that was
   never true. This pseudo-element and .page-head-video are both at z-index 0,
   so they tie and paint in tree order, which puts the video on top. The
   gradient therefore only dims the still background underneath it, never
   the loop.

   Checked on .page-head--artwork with the video running and deliberately
   left as is. Headline, label and lead all readable. Do not fix unasked.

   The fix was built and shown (::before to z-index 1, video staying at 0)
   and consciously rejected: it restores the dimming but costs the label and
   the lead, which currently sit undimmed and gain from the collision.
   NOT z-index -1 on the video — without a stacking context on the parent
   that drops it behind the parent's own background. */
.page-head--artwork::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(26, 20, 15, var(--head-dim-top, 0.35)),
    rgba(26, 20, 15, var(--head-dim-mid, 0.55)) 62%,
    #1A140F 99%);
}
.page-head--artwork > *:not(.page-head-video) { position: relative; z-index: 1; }
.page-head--artwork {
  background:
    linear-gradient(to bottom,
      rgba(26, 20, 15, var(--head-dim-top, 0.35)),
      rgba(26, 20, 15, var(--head-dim-mid, 0.55)) 62%,
      #1A140F 99%),
    var(--head-artwork) var(--head-artwork-pos, center) / cover no-repeat;
}

.service-arrow {
  font-family: var(--font-mark);
  font-weight: 300;
  font-size: 22px;
  color: var(--text-3);
  transition: color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
  padding-right: clamp(4px, 2vw, 24px);
}
.service-row:hover .service-arrow { color: var(--gold-bright); transform: translateX(8px); }

.service-peek {
  position: absolute;
  right: clamp(72px, 12vw, 180px);
  top: 50%;
  width: clamp(180px, 18vw, 260px);
  aspect-ratio: 16 / 9;
  transform: translateY(-50%) scale(0.96);
  opacity: 0;
  overflow: hidden;
  background: var(--surface);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.45);
  pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.service-peek img { width: 100%; height: 100%; object-fit: cover; }
.service-row:hover .service-peek { opacity: 1; transform: translateY(-50%) scale(1); }
@media (max-width: 1099px) { .service-peek { display: none; } }

/* ---------- For whom / clients ---------- */

.for-whom {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 20px clamp(24px, 4vw, 56px);
}
.for-whom .who {
  font-family: var(--font-mark);
  font-weight: 300;
  font-size: clamp(1.25rem, 2.8vw, 2.1rem);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--text);
}
.for-whom .dot { color: var(--gold); font-size: 1.2rem; }

.clients-line {
  display: flex;
  flex-wrap: wrap;
  gap: 16px clamp(28px, 4vw, 48px);
  justify-content: center;
}
.clients-line span {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-2);
}

/* ---------- Process / acts ---------- */

.process-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(32px, 4vw, 64px);
}
@media (max-width: 899px) { .process-grid { grid-template-columns: 1fr; } }

.process-item {
  border-top: 1px solid var(--gold-soft);
  padding-top: 26px;
  display: grid;
  gap: 16px;
  align-content: start;
}
.process-item .display-m { font-size: clamp(1.35rem, 2vw, 1.9rem); }

/* ---------- Capabilities index (Studio) ---------- */

.cap-row {
  display: grid;
  grid-template-columns: 64px minmax(180px, 1fr) 2fr;
  gap: 16px clamp(20px, 3vw, 48px);
  padding-block: 20px;
  border-top: 1px solid var(--line-soft);
  align-items: baseline;
}
.cap-row:last-child { border-bottom: 1px solid var(--line-soft); }
.cap-name {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-editorial);
}
.cap-line { color: var(--text-2); font-size: 14px; }
@media (max-width: 699px) {
  .cap-row { grid-template-columns: 48px 1fr; }
  .cap-line { grid-column: 2; }
}

/* ---------- Contact ---------- */

.contact-grid {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: clamp(48px, 7vw, 120px);
  align-items: start;
}
@media (max-width: 899px) { .contact-grid { grid-template-columns: 1fr; } }

.contact-form { display: grid; gap: 26px; }
.form-duo { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; }
@media (max-width: 599px) { .form-duo { grid-template-columns: 1fr; } }
.form-note { color: var(--text-3); font-size: 12px; letter-spacing: 0.04em; }

.contact-details { display: grid; gap: 36px; align-content: start; }
.contact-details .group { display: grid; gap: 12px; }
.contact-details a { color: var(--text-2); font-size: 14px; transition: color var(--dur-fast) var(--ease); justify-self: start; }
.contact-details a:hover { color: var(--gold-bright); }
.contact-details p { font-size: 14px; color: var(--text-2); }

/* ---------- Page hero (interior pages) ---------- */

.page-head {
  padding-top: calc(var(--header-h) + clamp(64px, 10vw, 128px));
  padding-bottom: clamp(48px, 7vw, 96px);
  display: grid;
  gap: 24px;
}
.page-head .serif-lead { color: var(--text-2); max-width: 52ch; }

/* Hero background still (dimmed until the real reel lands) */
.hero-media > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.6;
}

/* Noah Priest hero photo only: the source is a 21:9 plate, so the square
   hero container crops it hard. Pull the focal point right so the pilot
   sits about two thirds across — clear of the text column, off the edge. */
.hero-media > img[src="assets/img/noahpriest-hero.jpg"] {
  object-position: 84% 50%;
}
.frame:has(> img, > video) .frame-note { visibility: hidden; }

/* ---------- Editorial gallery (magazine image sequences) ----------
   Rows are built from typed entries in data.js:
   letterbox/film = full-bleed 2.39 · wide = contained 16:9 ·
   portrait = 4:5 · detail = 1:1 · portraits/details pair up two-up */

.gallery {
  display: grid;
  gap: clamp(44px, 6vw, 104px);
}

.g-pad {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.gallery figure { margin: 0; }
.gallery .frame { box-shadow: none; }

.g-duo {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(20px, 2.5vw, 44px);
  align-items: start;
}
@media (max-width: 699px) { .g-duo { grid-template-columns: 1fr; gap: 44px; } }

/* Solo vertical: editorial column, alternating left/right */
.g-solo { max-width: min(600px, 64%); }
.g-solo.g-alt { margin-left: auto; }
@media (max-width: 699px) { .g-solo, .g-solo.g-alt { max-width: 88%; margin-left: 0; } }

.g-caption {
  margin-top: 16px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* Editorial chapter spread: hero column + text in the open column */
.g-spread {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  gap: clamp(36px, 6vw, 104px);
  align-items: center;
}

.g-text {
  display: grid;
  gap: 20px;
  max-width: 38ch;
}
.g-text .serif-lead { font-size: clamp(1.3rem, 2vw, 1.8rem); }

@media (max-width: 899px) {
  .g-spread { grid-template-columns: 1fr; gap: 36px; }
  .g-text { max-width: 52ch; }
}

/* ---------- Full-bleed editorial band ---------- */

.band .frame { box-shadow: none; }
.band .g-caption { margin-top: 18px; }
