/* ==========================================================
   Typefaces — generated by wp/scripts/extract-fonts.mjs
   ========================================================== */

@font-face {
  font-family: 'Archivo Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  src: url(../fonts/archivo-latin-ext-wdth-normal.woff2) format('woff2-variations');
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

@font-face {
  font-family: 'Archivo Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  src: url(../fonts/archivo-latin-wdth-normal.woff2) format('woff2-variations');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

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

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



/* ==========================================================
   src/styles/global.css
   ========================================================== */
/* ===================================================================
   ARTUMO — global tokens & base
   Brand: #F58432 orange · #D9C7A5 sand · white · black
   Type: Archivo (expanded) display · Inter body
   =================================================================== */

:root {
  /* Brand color, exact values from the identity */
  --orange: #f58432;
  --orange-deep: oklch(0.66 0.17 51);      /* pressed / hover on orange */
  --sand: #d9c7a5;
  --sand-ink: oklch(0.32 0.03 75);          /* readable ink on sand */
  --white: #ffffff;
  --off: oklch(0.985 0.004 65);             /* barely-warm paper for large fields of text */
  --black: oklch(0.17 0.008 55);            /* warm near-black */
  --black-true: oklch(0.12 0.006 55);       /* deepest stage for photography */
  --ink: oklch(0.21 0.01 55);               /* body ink on light */
  --ink-soft: oklch(0.42 0.012 55);         /* secondary ink on light, AA on white */
  --paper-ink-on-black: oklch(0.93 0.006 65);
  --soft-ink-on-black: oklch(0.72 0.01 65);
  --ink-on-orange: oklch(0.185 0.02 55);    /* near-black w/ orange warmth */

  /* Type */
  --font-display: 'Archivo Variable', 'Archivo', 'Helvetica Neue', sans-serif;
  --font-body: 'Inter Variable', 'Inter', system-ui, sans-serif;

  /* Fluid scale (1.333 ratio anchors) */
  --step--1: clamp(0.83rem, 0.8rem + 0.15vw, 0.94rem);
  --step-0: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  --step-1: clamp(1.33rem, 1.24rem + 0.45vw, 1.6rem);
  --step-2: clamp(1.77rem, 1.58rem + 0.95vw, 2.37rem);
  --step-3: clamp(2.37rem, 2.02rem + 1.75vw, 3.55rem);
  --step-4: clamp(3.16rem, 2.5rem + 3.3vw, 5.33rem);
  --step-5: clamp(3.8rem, 2.7rem + 5.5vw, 6rem);

  /* Space */
  --space-2xs: clamp(0.5rem, 0.45rem + 0.25vw, 0.7rem);
  --space-xs: clamp(0.75rem, 0.68rem + 0.35vw, 1rem);
  --space-s: clamp(1rem, 0.9rem + 0.5vw, 1.4rem);
  --space-m: clamp(1.5rem, 1.3rem + 1vw, 2.25rem);
  --space-l: clamp(2.5rem, 2rem + 2.5vw, 4.5rem);
  --space-xl: clamp(4rem, 3rem + 5vw, 8rem);
  --space-2xl: clamp(6rem, 4.5rem + 7.5vw, 12rem);

  --container: 90rem;
  --gutter: clamp(1.25rem, 0.75rem + 2.5vw, 3.5rem);

  /* Motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);      /* quint-ish */
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-quick: 240ms;
  --dur-move: 520ms;
  --dur-settle: 900ms;

  /* z scale */
  --z-nav: 100;
  --z-overlay: 200;

  /* Header ink for pages whose top section is light (overridden per style) */
  --header-ink: var(--ink);
}

/* =========================================================
   Style 2 — Dark stage. The site lives in the dark; the
   photography is the light source, orange is accent light.
   ========================================================= */
html[data-style='2'] {
  --header-ink: var(--paper-ink-on-black);
  --header-ink-contrast: var(--black-true);
}

html[data-style='2'] body {
  background: var(--black-true);
  color: var(--paper-ink-on-black);
}

html[data-style='2'] ::selection {
  background: var(--orange);
  color: var(--black-true);
}

/* ------------------------------------------------------------------ */

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

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

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.6;
  color: var(--ink);
  background: var(--white);
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

/* Tapping twice is how a visitor walks the wall — two rooms on, two
   images on. Safari on iPhone reads a second tap in the same place as a
   double tap and zooms into it instead, which throws the whole room off
   its scale and leaves the visitor fighting their way back out. Nothing
   on this site wants a double tap, so no control here answers one.
   `manipulation` gives up only that gesture: panning and pinch-to-zoom
   are untouched, so the visitor can still zoom a photograph as they
   would in a gallery — the viewport is never locked. */
a,
button,
summary,
[role='button'] {
  touch-action: manipulation;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-stretch: 125%;
  font-weight: 560;
  line-height: 1.04;
  letter-spacing: 0.005em;
  margin: 0;
  text-wrap: balance;
  text-transform: uppercase;
}

p {
  margin: 0;
  text-wrap: pretty;
}

a {
  color: inherit;
}

:focus-visible {
  outline: 3px solid var(--orange);
  outline-offset: 3px;
  border-radius: 2px;
}

.on-dark :focus-visible,
[data-theme='dark'] :focus-visible {
  outline-color: var(--orange);
}

::selection {
  background: var(--orange);
  color: var(--ink-on-orange);
}

/* Layout primitives ------------------------------------------------ */

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

.prose {
  max-width: 68ch;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  top: 0.75rem;
  left: 0.75rem;
  z-index: calc(var(--z-nav) + 1);
  background: var(--black);
  color: var(--white);
  padding: 0.6rem 1rem;
  font-family: var(--font-display);
  font-stretch: 125%;
  text-transform: uppercase;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-decoration: none;
  transform: translateY(-300%);
}

.skip-link:focus-visible {
  transform: none;
}

/* The drifting-baseline headline motif is gone (2026-07-27, client). It
   was removed in two steps and both are worth remembering: first the
   settle that played the words into their offsets, then the offsets
   themselves — because words on staggered baselines make a wrapped
   headline read as unevenly spaced lines, which is exactly what the
   client saw once the movement stopped distracting from it. The drift
   survives where it came from and where it costs nothing: the wordmark
   itself, drawn in the logo SVG. Headlines are now plain elements, so
   line rhythm is the line-height and nothing else. */

/* Scroll reveals (enhance-only: content visible by default) -------- */

@media (prefers-reduced-motion: no-preference) {
  .reveal {
    opacity: 0.001;
    transform: translateY(1.25rem);
    transition:
      opacity 0.8s var(--ease-out),
      transform 0.9s var(--ease-out-expo);
  }

  .reveal.is-in {
    opacity: 1;
    transform: none;
  }

  /* Never leave content hidden if JS fails */
  html.no-js .reveal {
    opacity: 1;
    transform: none;
  }
}

/* Chrome pills ------------------------------------------------------
   The language switch and the menu are the same object wearing two
   words, so they are one definition — three copies of this ring had
   already drifted apart across the homepage, the project room and the
   404 before it was written down once.

   Always white. The chrome used to be difference-blended, which held up
   on black and on white but inverted to mid-grey against a mid-grey
   photograph — exactly where a wordmark most needs to be read. The pill
   frosts its own backdrop instead, the way the wall's arrows do: the
   disc blurs and desaturates what is behind it, so the ring separates
   from any image without the venue lowering its lights. */
.chrome-pill {
  pointer-events: auto;
  font-family: var(--font-display);
  font-stretch: 120%;
  font-weight: 580;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.85rem;
  /* stated, not inherited: a <button> defaults to `normal` while an <a>
     takes the body's 1.6, which made the two pills different heights */
  line-height: 1.3;
  text-decoration: none;
  background: oklch(0.08 0.006 55 / 0.6);
  -webkit-backdrop-filter: blur(7px) saturate(0.55);
  backdrop-filter: blur(7px) saturate(0.55);
  border: 1.5px solid oklch(1 0 0 / 0.7);
  border-radius: 999px;
  color: #fff;
  padding: 0.45rem 1rem;
  cursor: pointer;
  transition:
    background-color var(--dur-quick) var(--ease-out),
    border-color var(--dur-quick) var(--ease-out),
    color var(--dur-quick) var(--ease-out);
}

.chrome-pill:hover,
.chrome-pill:focus-visible {
  background: #fff;
  border-color: #fff;
  color: #000;
}

/* The two of them travel together at the top right */
.chrome-controls {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* A control that cannot open anything is worse than no control */
html.no-js [data-menu-open] {
  display: none;
}

/* A shared, quiet chevron keeps navigation controls readable without the
   visual weight of a text arrow. */
.icon-chevron {
  display: block;
  width: 1.35rem;
  height: 1.35rem;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Wall arrows -------------------------------------------------------
   One definition for both walls — the homepage exhibition and the
   project media wall are driven the same way, so they must not drift
   apart. Quiet until touched, then they answer with light.

   The disc frosts and desaturates whatever is behind it. A flat dark
   wash disappeared against bright photography (a lit mural, fog, fire);
   blurring the backdrop separates the ring from any image without
   dimming the photograph itself. */
.arrow-ring {
  width: 2.9rem;
  height: 2.9rem;
  display: grid;
  place-items: center;
  padding: 0;
  font-family: var(--font-display);
  font-size: 1.15rem;
  line-height: 1;
  color: #fff;
  background: oklch(0.08 0.006 55 / 0.66);
  border: 1px solid oklch(1 0 0 / 0.55);
  border-radius: 999px;
  cursor: pointer;
  -webkit-backdrop-filter: blur(7px) saturate(0.55);
  backdrop-filter: blur(7px) saturate(0.55);
  box-shadow: 0 2px 18px oklch(0.05 0 0 / 0.55);
  transition:
    color var(--dur-quick) var(--ease-out),
    border-color var(--dur-quick) var(--ease-out),
    background-color var(--dur-quick) var(--ease-out),
    box-shadow var(--dur-quick) var(--ease-out),
    opacity var(--dur-quick) var(--ease-out);
}

.arrow-ring:hover:not(:disabled),
.arrow-ring:focus-visible {
  color: var(--orange);
  border-color: var(--orange);
  background: oklch(0.08 0.006 55 / 0.8);
  box-shadow:
    0 0 18px oklch(0.73 0.16 55 / 0.5),
    0 2px 18px oklch(0.05 0 0 / 0.55);
}

.arrow-ring:disabled {
  opacity: 0.3;
  cursor: default;
}


/* ==========================================================
   src/components/Logo.astro
   ========================================================== */

  .logo {
    display: block;
    height: auto;
  }


/* ==========================================================
   src/components/Loader.astro
   ========================================================== */

  /* Never present unless script armed it. This is the whole no-JS story:
     the panel is display:none in the stylesheet, and only the inline head
     script can turn it on. */
  .loader {
    display: none;
  }

  html.is-arriving .loader {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 300; /* over the chrome (--z-nav 100) and the menu (200) */
    color: var(--paper-ink-on-black);
    pointer-events: none;
  }

  /* The dark field. It wipes upward off the homepage rather than fading:
     a fade would show the work through a veil, and this site never dims
     photography. */
  .ld-panel {
    position: absolute;
    inset: 0;
    background: var(--black-true);
    clip-path: inset(0 0 0 0);
    animation: ld-wipe 1.05s var(--ease-out) 7.15s both;
  }

  /* the stage's own light, the same orange the wall uses */
  .ld-panel::before {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(120% 80% at 50% 45%, oklch(0.66 0.17 51 / 0.1), transparent 62%);
  }

  .ld-line,
  .ld-count {
    position: absolute;
    bottom: clamp(1.5rem, 1rem + 2vw, 3.5rem);
    margin: 0;
    /* Match the homepage's display copy while keeping the loader's compact
       HUD sizing and spacing. */
    font-family: var(--font-display);
    font-stretch: 114%;
    font-size: var(--step--1);
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    animation:
      ld-hud-in 0.75s var(--ease-out) 0.25s both,
      ld-hud-out 0.7s var(--ease-out) 6.8s forwards;
  }

  .ld-line {
    inset-inline-start: var(--gutter);
    color: #fff;
    max-width: 60%;
  }

  .ld-count {
    inset-inline-end: var(--gutter);
    color: var(--orange);
    font-variant-numeric: tabular-nums;
  }

  .ld-count-label {
    margin-inline-end: 0.55em;
  }

  /* A fifth off both on a phone. At full size the agency line wrapped to
     two, which turned a quiet footnote into a paragraph and pushed it up
     against the counter; the two are one row and they shrink together so
     they stay one row. */
  @media (max-width: 48rem) {
    .ld-line,
    .ld-count {
      font-size: calc(var(--step--1) * 0.8);
      letter-spacing: 0.12em;
    }

    /* the 60% cap was cut for the larger type and is what kept the line
       wrapping after it shrank; at this size it fits beside the counter
       on one row, which is the whole reason for shrinking it */
    .ld-line {
      max-width: 72%;
    }
  }

  /* The wordmark, at the width the design gives it — 61.5% of the frame —
     but never wider than the page's own measure and never so wide on a
     phone that the letters have nowhere to come from. */
  .ld-mark {
    --mark-w: min(78vw, 61.5rem);
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: 50%;
    width: var(--mark-w);
    aspect-ratio: 1310.6 / 415.4;
    translate: -50% -50%;
    animation: ld-mark-out 0.55s var(--ease-out) 7.2s both;
  }

  /* Two nested boxes because one element cannot run two transforms: the
     outer carries the letter in from its edge, the inner drops it off the
     shared baseline into the offset the real mark has. */
  .ld-glyph {
    position: absolute;
    inset: 0;
    animation: ld-enter 1.35s var(--ease-out-expo) calc(0.35s + var(--slot) * 0.5s) both;
  }

  .ld-flush {
    display: block;
    height: 100%;
    /* 1 viewBox unit is the mark's width over its viewBox width */
    animation: ld-flush 1s var(--ease-out) 5.55s both;
  }

  .ld-glyph svg {
    display: block;
    width: 100%;
    height: 100%;
  }

  @keyframes ld-enter {
    from {
      transform: translate(var(--sx), var(--sy));
      opacity: 0;
      filter: blur(10px);
    }
    to {
      transform: translate(0, 0);
      opacity: 1;
      filter: blur(0);
    }
  }

  @keyframes ld-flush {
    from {
      transform: translateY(calc(var(--flush) * var(--mark-w) / 1310.6));
    }
    to {
      transform: translateY(0);
    }
  }

  @keyframes ld-wipe {
    from {
      clip-path: inset(0 0 0 0);
    }
    to {
      clip-path: inset(0 0 100% 0);
    }
  }

  @keyframes ld-hud-in {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }

  @keyframes ld-hud-out {
    to {
      opacity: 0;
    }
  }

  @keyframes ld-mark-out {
    to {
      opacity: 0;
    }
  }

  /* The arrival is skipped outright under reduced motion — the script does
     not arm it — but a visitor who changes the setting mid-visit should
     not be left with a panel in the air. */
  @media (prefers-reduced-motion: reduce) {
    html.is-arriving .loader {
      display: none;
    }
  }


/* ==========================================================
   src/components/Menu.astro
   ========================================================== */

  /* The plan itself: the venue with the lights up. Black like every other
     surface here — the visit does not change buildings when the visitor
     asks where they are. */
  .menu {
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    margin: 0;
    padding: 0;
    border: 0;
    background: var(--black-true);
    color: var(--paper-ink-on-black);
  }

  .menu::backdrop {
    background: oklch(0.06 0.004 55 / 0.92);
  }

  .menu-sheet {
    height: 100%;
    display: grid;
    align-content: center;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: var(--gutter);
  }

  .menu-close {
    position: absolute;
    inset-block-start: clamp(1rem, 0.8rem + 1vw, 1.8rem);
    inset-inline-end: var(--gutter);
    width: 2.9rem;
    height: 2.9rem;
    display: grid;
    place-items: center;
    padding: 0;
    font-size: 1.1rem;
    line-height: 1;
    color: var(--paper-ink-on-black);
    background: none;
    border: 1px solid oklch(1 0 0 / 0.3);
    border-radius: 999px;
    cursor: pointer;
    transition:
      color var(--dur-quick) var(--ease-out),
      border-color var(--dur-quick) var(--ease-out);
  }

  .menu-close:hover,
  .menu-close:focus-visible {
    color: var(--orange);
    border-color: var(--orange);
  }

  .menu-list,
  .menu-chapters {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .menu-list > li + li {
    margin-block-start: clamp(1.2rem, 0.9rem + 1.4vw, 2.2rem);
  }

  /* The three destinations, at the scale the rooms themselves speak at */
  .menu-list > li > a {
    display: inline-block;
    font-family: var(--font-display);
    font-stretch: 125%;
    font-weight: 560;
    text-transform: uppercase;
    letter-spacing: 0.01em;
    line-height: 1.05;
    font-size: min(var(--step-4), 11vw);
    color: var(--paper-ink-on-black);
    text-decoration: none;
    transition: color var(--dur-quick) var(--ease-out);
  }

  .menu-list > li > a:hover,
  .menu-list > li > a:focus-visible {
    color: var(--orange);
  }

  /* Three names under the room they belong to. This was a catalogue index
     — a number, the name, and a rule run out to the edge — and the client
     asked for the numbers and the rules gone (2026-08-28). What they were
     doing was tying the rows into a block and standing in for an indent;
     what does that now is the block's own left edge, shared with the room
     name above it, and the space around it. A chapter is a place, not an
     item in a numbered list, and the count was the one thing here that
     had to be re-derived every time the client added or dropped one. */
  .menu-chapters {
    margin-block-start: clamp(0.8rem, 0.6rem + 1vw, 1.4rem);
    display: grid;
    justify-items: start;
    gap: clamp(0.1rem, 0.05rem + 0.2vw, 0.3rem);
  }

  .menu-chapters a {
    display: block;
    font-family: var(--font-display);
    font-stretch: 118%;
    font-weight: 520;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: clamp(0.8rem, 0.72rem + 0.4vw, var(--step-0));
    line-height: 1.2;
    color: var(--paper-ink-on-black);
    text-decoration: none;
    /* a name is a small target; the whole row is a large one */
    padding-block: clamp(0.45rem, 0.35rem + 0.4vw, 0.7rem);
    transition: color var(--dur-quick) var(--ease-out);
  }

  .menu-chapters a:hover,
  .menu-chapters a:focus-visible {
    color: var(--orange);
  }

  /* On a phone the index was set at the size it takes beside a desktop's
     huge room names, which left ~13px type on ~28px rows — too small to
     read comfortably and well under a finger. The rows grow into proper
     targets here, and a long name simply wraps, which is honest — it is
     never shrunk back to a size that was too small to tap. */
  @media (max-width: 48rem) {
    .menu-chapters {
      gap: 0.15rem;
    }

    .menu-chapters a {
      font-size: clamp(1rem, 0.86rem + 0.7vw, 1.2rem);
      letter-spacing: 0.04em;
      padding-block: 0.75rem;
    }
  }


/* ==========================================================
   src/components/Footer.astro
   ========================================================== */

  .site-footer {
    background: var(--black-true);
    color: var(--soft-ink-on-black);
    padding-block: var(--space-m);
    /* The homepage snaps vertically so the exhibition settles into view.
       The stage is sticky, so its snap area travels with the stuck box and
       the scroller can settle short of the document's end — leaving this
       line unreachable. A snap position of its own gives the scroller
       somewhere legitimate to land at the bottom. */
    scroll-snap-align: end;
  }

  .footer-legal {
    font-size: 0.82rem;
    letter-spacing: 0.04em;
  }


/* ==========================================================
   src/components/pages/HomePageB.astro
   ========================================================== */

  /* Floating chrome: readable over any room via difference blend */
  .chrome {
    position: fixed;
    inset-inline: 0;
    top: 0;
    z-index: var(--z-nav);
    /* the same measure every room's text sits on, so the wordmark and the
       big blocks below it share one left edge — the chrome ran full-bleed
       before, which put the logo outside the measure on any screen wider
       than the container */
    max-width: var(--container);
    margin-inline: auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: clamp(1rem, 0.8rem + 1vw, 1.8rem) var(--gutter);
    pointer-events: none;
    /* Always white, never the difference of what is behind it. Difference
       blend kept the chrome legible on black and on white, but against a
       mid-grey photograph it inverted to mid-grey and the wordmark went
       soft. The logo is separated by a shadow of its own instead, and the
       pills are frosted like the wall's arrows — both of which leave the
       photograph exactly as it was, which dimming would not. */
    color: #fff;
  }

  .chrome a {
    pointer-events: auto;
  }

  .chrome-logo {
    /* the wordmark stays white on any photograph: a soft dark halo,
       not a wash over the image behind it */
    filter: drop-shadow(0 1px 10px oklch(0.08 0.006 55 / 0.85));
    display: block;
    color: #fff;
  }

  .chrome-logo .chrome-logo-svg {
    width: clamp(7rem, 6rem + 4vw, 9.5rem);
  }

  /* Every room here is sticky, and a link cannot aim at a sticky element:
     once it has stuck, the browser reads its position as "the top of the
     screen" and a jump to it is a jump to where you already are. Going
     DOWN the page worked, because the target had not stuck yet; going
     back UP did nothing at all, which is exactly what a menu that "works
     only once" feels like. So the ids hang on these instead — empty,
     zero-height, and in the flow, where a room's real place on the page
     is a thing that can still be read. */
  .anchor {
    display: block;
    height: 0;
  }

  /* Rooms (hero, about, contact keep the vertical stacking) */
  .room {
    position: sticky;
    top: 0;
    min-height: 100svh;
    display: grid;
    align-items: end;
  }

  .room-hero {
    background: var(--orange);
    color: var(--ink-on-orange);
    align-items: center;
  }

  .room-hero > .container {
    position: relative;
    z-index: 1;
    width: 100%;
    min-width: 0;
  }

  .room-hero .hero-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* The type needs a ground, and the photograph must not be dimmed to give
     it one — so the scrim is a gradient anchored where the words are,
     exactly as the wall's captions do it, and the rest of the image is
     untouched. Wide screens put the statement on the left, so the scrim
     runs that way; a phone stacks it low, so there it runs up from the
     foot. */
  .room-hero::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
      to top,
      oklch(0.08 0.006 55 / 0.92) 0%,
      oklch(0.08 0.006 55 / 0.72) 34%,
      transparent 82%
    );
  }

  @media (min-width: 48rem) {
    .room-hero::after {
      background: linear-gradient(
        to right,
        oklch(0.08 0.006 55 / 0.92) 0%,
        oklch(0.08 0.006 55 / 0.7) 38%,
        transparent 80%
      );
    }
  }

  .room-hero .b-hero-title {
    font-size: min(var(--step-5), 13.5vw);
    max-width: 14ch;
  }

  .enter {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    margin-block-start: var(--space-l);
    font-family: var(--font-display);
    font-stretch: 120%;
    font-weight: 580;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: var(--step-0);
    color: var(--ink-on-orange);
    text-decoration: none;
    padding-block: 0.25rem;
  }

  .enter-chevron {
    width: 24px;
    height: 24px;
    stroke-width: 1.75;
  }

  .enter:hover {
    color: var(--black-true);
  }

  /* Style 2 — dark stage: the statement room is a dark hall, orange is
     a strip of light */
  html[data-style='2'] .room-hero {
    background: var(--black-true);
    color: var(--paper-ink-on-black);
  }

  html[data-style='2'] .enter {
    color: var(--orange);
  }

  html[data-style='2'] .enter:hover {
    color: var(--paper-ink-on-black);
  }

  /* ==================================================================== */
  /* The stage: one wall of project rooms, chaptered by the floor plan    */
  /* ==================================================================== */
  /* the lockup: reaching the stage settles it into full view */
  html {
    scroll-snap-type: y proximity;
  }

  .stage {
    /* the floor plan's own height — everything that sits at the foot of
       the stage clears it by way of this one number. One shape at every
       width now: a name, then the bars. */
    --rail-h: 2.8rem;
    position: sticky;
    top: 0;
    height: 100svh;
    overflow: clip;
    background: var(--black-true);
    color: var(--paper-ink-on-black);
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }

  html.no-js .stage {
    position: relative;
    height: auto;
    overflow: visible;
  }

  /* Document height only: it paints nothing, so what the visitor sees for
     its whole length is the stage still pinned behind it. Rooms slide over
     one another in this stack, and this simply delays the statement room's
     arrival by a screen. */
  .stage-runway {
    height: 100svh;
  }

  /* Without JS the wall is a plain vertical stack of rooms that is already
     many screens long — an extra empty screen there is just a hole. */
  html.no-js .stage-runway {
    display: none;
  }

  /* The wall ------------------------------------------------------------- */
  .wall {
    /* positioned: the panels' offsetLeft is read against it */
    position: relative;
    display: flex;
    height: 100%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    cursor: grab;
  }

  .wall.is-dragging {
    scroll-snap-type: none;
    cursor: grabbing;
    user-select: none;
  }

  .wall::-webkit-scrollbar {
    display: none;
  }

  .wall:focus-visible {
    outline: 2px solid var(--orange);
    outline-offset: -4px;
  }

  html.no-js .wall {
    display: block;
    height: auto;
    overflow: visible;
  }


  /* Prev / next arrows: quiet rings that answer with light */
  .wing-nav {
    position: absolute;
    inset-inline-end: var(--gutter);
    inset-block-end: calc(var(--rail-h) + clamp(1.4rem, 1.1rem + 1.6vw, 2.6rem));
    display: flex;
    gap: 0.6rem;
    z-index: 5;
  }

  html.no-js .wing-nav {
    display: none;
  }

  /* the ring itself is .arrow-ring in global.css — shared with the
     project media wall so the two walls cannot drift apart */

  /* The floor plan ------------------------------------------------------- */
  /* Three segments across the foot of the stage, each as wide as the work
     its chapter holds, filling as the visitor travels. It answers the two
     things a still photograph cannot: that there are other chapters, and
     that the wall continues sideways. It never moves on its own — the fill
     tracks scroll position, so it only ever reports where the visitor put
     it. The top padding is a fade zone, not height: it carries the scrim
     that keeps the bars legible over a bright photograph. */
  .rail {
    position: absolute;
    inset: auto 0 0;
    z-index: 4;
    display: grid;
    gap: 0;
    padding: 2.75rem var(--gutter) 0.4rem;
    background: linear-gradient(
      to top,
      oklch(0.08 0.006 55 / 0.92) 42%,
      oklch(0.08 0.006 55 / 0)
    );
  }

  .rail-segs {
    display: flex;
    align-items: flex-end;
    gap: clamp(0.5rem, 0.35rem + 0.8vw, 1.1rem);
  }

  /* The plan names ONE chapter — the one being stood in — and it does so
     at every width. Each segment used to carry its own name on a wide
     screen, which worked while there were three chapters of similar size;
     with four, "Privatūs renginiai" has a 170px segment and "Instaliacijos
     ir prekės ženklo aktyvacijos" a 340px one, and both clipped. The rule
     the narrow screens already proved applies everywhere: a chapter name
     cut to "INSTALIACIJOS IR PREKĖS ŽEN…" tells a visitor less than no
     name at all. The four names in full live in the menu. */
  .rail-now {
    display: block;
    font-family: var(--font-display);
    font-stretch: 118%;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: var(--step--1);
    line-height: 1.2;
    color: var(--paper-ink-on-black);
  }

  html.no-js .rail {
    display: none;
  }

  .rail-seg {
    /* as wide as the chapter is long, so the plan is a map of the wall */
    flex: var(--w) 1 0;
    min-width: 0;
    display: grid;
    gap: 0.5rem;
    margin: 0;
    /* Without a name a segment is four pixels tall, which is a bar but not
       a target. The padding reaches up into the empty band the caption
       already leaves above the plan, so the finger gets something to hit
       without the bar itself growing. */
    padding: 1.1rem 0 0.6rem;
    align-content: end;
    background: none;
    border: 0;
    text-align: start;
    cursor: pointer;
    color: var(--soft-ink-on-black);
  }

  .rail-track {
    height: 4px;
    background: oklch(1 0 0 / 0.3);
    overflow: clip;
  }

  .rail-fill {
    display: block;
    height: 100%;
    background: var(--orange);
    transform-origin: left center;
    /* set from the wall's scroll position; 0 until the script speaks */
    transform: scaleX(var(--p, 0));
  }

  /* still rendered, never shown: `sync()` reads the standing chapter's
     name out of the lit segment, so there is one source of truth for it */
  .rail-name {
    display: none;
    font-family: var(--font-display);
    font-stretch: 118%;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: var(--step--1);
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .rail-seg.is-at {
    color: var(--paper-ink-on-black);
  }

  .rail-seg:hover .rail-track,
  .rail-seg:focus-visible .rail-track {
    background: oklch(1 0 0 / 0.5);
  }

  .rail-seg:focus-visible {
    outline: 3px solid var(--orange);
    outline-offset: 4px;
  }

  .wing-room {
    position: relative;
    flex: 0 0 100%;
    height: 100%;
    scroll-snap-align: start;
    overflow: clip;
  }

  html.no-js .wing-room {
    height: 100svh;
  }

  .wing-room .wing-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* The photograph still answers attention, but it answers the way IN
     rather than itself — it is no longer a link, so a scale on the image
     alone would be an affordance that leads nowhere. */
  @media (prefers-reduced-motion: no-preference) {
    .wing-room .wing-img {
      transition: transform 1.4s var(--ease-out-expo);
    }

    .wing-room:has(.wing-cta:hover) .wing-img,
    .wing-room:has(.wing-cta:focus-visible) .wing-img {
      transform: scale(1.03);
    }
  }

  /* A room only speaks when the wall rests on it, so a neighbour caught
     at the screen edge never shows half a title. Enhance-only: without
     JS nothing carries [data-live] and every caption stays put. */
  .wall[data-live] .wing-room:not(.is-at) .wing-caption {
    opacity: 0;
  }

  @media (prefers-reduced-motion: no-preference) {
    .wing-caption {
      transition: opacity var(--dur-move) var(--ease-out);
    }
  }

  .wing-caption {
    position: absolute;
    inset: 0;
    /* it covers the whole room, so it must not stand between the wall and
       a drag — only the link inside it takes the pointer */
    pointer-events: none;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 0.45rem;
    padding: clamp(1.25rem, 1rem + 2.5vw, 3.5rem) var(--gutter);
    /* leaves the control cluster clear on the right */
    padding-inline-end: clamp(6rem, 5rem + 6vw, 12rem);
    /* …and the floor plan clear underneath */
    padding-block-end: calc(var(--rail-h) + clamp(1.75rem, 1.25rem + 3vw, 4.5rem));
    background: linear-gradient(to top, oklch(0.1 0.01 55 / 0.85) 0%, transparent 42%);
  }

  .wing-title {
    font-family: var(--font-display);
    font-stretch: 125%;
    font-weight: 580;
    text-transform: uppercase;
    line-height: 1.05;
    font-size: min(var(--step-3), var(--tvw, 9vw));
    max-width: 22ch;
    text-wrap: balance;
  }

  /* Always shown, on every device. It used to be revealed by hovering the
     room, which was affordable while the whole photograph was the link;
     now that the CTA is the only way into a project, a visitor who never
     hovers would have no way in at all. */
  .wing-cta {
    align-self: start;
    pointer-events: auto;
    font-family: var(--font-display);
    font-stretch: 118%;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: var(--step--1);
    color: var(--orange);
    text-decoration: none;
    /* the words are a small target; the padding makes the row a real one */
    padding-block: 0.35rem;
    transition: color var(--dur-quick) var(--ease-out);
  }

  .wing-cta:hover,
  .wing-cta:focus-visible {
    color: var(--paper-ink-on-black);
  }

  /* Stage: tablet + mobile ------------------------------------------------ */
  @media (max-width: 64rem) {
    /* the clamp bottoms out at 1rem on a phone, which parks the rings on
       the very edge — lift them clear of it and of the plan */
    .wing-nav {
      inset-block-end: calc(var(--rail-h) + 2.4rem);
    }

    .wing-caption {
      padding-inline-end: var(--gutter);
      padding-block-end: calc(var(--rail-h) + clamp(6rem, 5rem + 4vw, 7rem));
    }
  }

  /* ==================================================================== */
  /* The closing credit: who has trusted ARTUMO                           */
  /* ==================================================================== */
  /* One quiet line along the foot of the last room — a credit, not a
     banner. Nothing ever slides over the last room, so the roster needs no
     emphasis to be read, and it keeps the ask on the screen with it rather
     than making the visitor scroll through a room of names to reach the
     one thing the page is asking for. This is also the modesty the client
     asked for: five names at reading size on a hairline, not five names at
     poster size. Two faint rules give it edges — the footer's legal line
     sits directly beneath on the same black, and without them the two read
     as one block. */
  .strip {
    align-self: end;
    width: 100%;
    min-width: 0;
    /* the rules run the full width of the room — they are what divides the
       roster from the footer's legal line, so they must reach both edges */
    padding-block: clamp(0.9rem, 0.7rem + 1vw, 1.5rem);
    border-block: 1px solid oklch(1 0 0 / 0.14);
  }

  /* …but the names inside them line up with the ask above and the legal
     line below, because on a wide screen those two are what the eye reads
     the page's structure from. Both are `.container`, so this takes the
     same cap AND the same gutter: the gutter alone left the roster flush
     to the window while everything around it stopped at the measure. */
  .strip-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem clamp(1rem, 0.8rem + 1.4vw, 2rem);
    min-width: 0;
  }

  .strip-kicker,
  .strip-list li {
    font-family: var(--font-display);
    font-stretch: 118%;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: var(--step--1);
    line-height: 1.2;
  }

  .strip-kicker {
    color: var(--orange);
  }

  .strip-list {
    list-style: none;
    margin: 0;
    padding: 0;
    min-width: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem clamp(1rem, 0.8rem + 1.4vw, 2rem);
    color: var(--soft-ink-on-black);
  }

  /* About + contact rooms */
  .room-about {
    background: var(--sand);
    color: var(--sand-ink);
    align-items: center;
  }

  .room-about > .container,
  .room-contact > .container {
    width: 100%;
    min-width: 0;
  }

  .b-about {
    font-family: var(--font-display);
    font-stretch: 114%;
    font-weight: 500;
    font-size: var(--step-2);
    line-height: 1.3;
    max-width: 34ch;
    text-wrap: balance;
  }

  /* Style 2: the statement speaks in the same voice as everything else.
     The room lost its brown ground on 26 July because it was the only
     surface on the page where the ground changed colour, and the warmth
     moved into sand type; on 27 July the type followed, for the same
     reason one step further in — a paragraph in its own colour still
     reads as a different site talking. Warmth on this page is carried by
     orange light and by the photography, not by tinted body text. */
  html[data-style='2'] .room-about {
    background: var(--black-true);
    color: var(--paper-ink-on-black);
  }

  .room-contact {
    background: var(--orange);
    color: var(--ink-on-orange);
    align-items: center;
  }

  /* Only while the roster is actually in the room: the ask centres in what
     is left, and the bar runs flush to the room's edge so its lower
     hairline is what divides the roster from the footer's legal line.
     Written as `:has()` so taking the roster out restores a plain room
     without a second switch to remember. */
  .room-contact:has(.strip) {
    grid-template-rows: 1fr auto;
    padding-block-end: 0;
  }

  .room-contact .b-contact-title {
    font-size: min(var(--step-4), 10vw);
    max-width: 16ch;
  }

  /* One voice under the headline, and now one shape as well. The lead,
     the address and the number were three fonts at three sizes in three
     colours; they were given one setting, and the client still read them
     as three different things — because they still were. The address
     underlined itself with `text-decoration` while the number drew a
     `border-block-end` two thirds of a line lower, so the two ways of
     reaching us wore two different rules, and each sat on a line of its
     own with a different gap above it. There is one rule now, drawn one
     way, and the two links sit side by side as the pair they are.
     Everything under the headline is the same font, the same size, the
     same weight and the same colour; the only mark that separates any of
     it is the rule that says a thing can be clicked. */
  .b-contact-lead,
  .b-contact-list a {
    font-family: var(--font-display);
    font-stretch: 114%;
    font-size: var(--step-2);
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: normal;
    text-transform: none;
    color: var(--ink-on-orange);
  }

  .b-contact-lead {
    margin-block-start: var(--space-s);
    max-width: 38ch;
  }

  .b-contact-list {
    list-style: none;
    /* Keep every way to reach ARTUMO on its own line. The phone number is
       intentionally below the email on desktop as well as mobile. */
    margin: var(--space-m) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2xs);
  }

  /* the one rule, drawn once: the text's own underline, so it follows the
     word rather than the box, at a distance that clears the descenders in
     "agency" */
  .b-contact-list a {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.28em;
    border: 0;
    padding: 0;
    transition: color var(--dur-quick) var(--ease-out);
  }

  .b-contact-list a:hover {
    color: var(--black-true);
  }

  /* Style 2: orange becomes light, not surface */
  html[data-style='2'] .room-contact {
    background: var(--black-true);
    color: var(--paper-ink-on-black);
  }

  html[data-style='2'] .room-contact .b-contact-title {
    color: var(--orange);
  }

  /* …and one colour with it: the lead used to sit a step darker than the
     two links beside it, which is the difference the client was reading
     as three separate things */
  html[data-style='2'] .b-contact-lead,
  html[data-style='2'] .b-contact-list a {
    color: var(--paper-ink-on-black);
  }

  html[data-style='2'] .b-contact-list a:hover {
    color: var(--orange);
  }


/* ==========================================================
   src/components/pages/ProjectPage.astro
   ========================================================== */

  .proom {
    --seam: oklch(0.24 0.012 55);
    background: var(--black-true);
    color: #fff;
  }

  /* Chrome: logo + language pill */
  .p-chrome {
    position: fixed;
    inset-inline: 0;
    top: 0;
    z-index: var(--z-nav);
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: clamp(1rem, 0.8rem + 1vw, 1.8rem) var(--gutter);
    pointer-events: none;
    /* Always white, never the difference of what is behind it. Difference
       blend kept the chrome legible on black and on white, but against a
       mid-grey photograph it inverted to mid-grey and the wordmark went
       soft. The logo is separated by a shadow of its own instead, and the
       pills are frosted like the wall's arrows — both of which leave the
       photograph exactly as it was, which dimming would not. */
    color: #fff;
  }

  .p-chrome-logo {
    /* the wordmark stays white on any photograph: a soft dark halo,
       not a wash over the image behind it */
    filter: drop-shadow(0 1px 10px oklch(0.08 0.006 55 / 0.85));
    pointer-events: auto;
    display: block;
    color: #fff;
  }

  .p-chrome-logo .p-chrome-logo-svg {
    width: clamp(7rem, 6rem + 4vw, 9.5rem);
  }

  @media (min-width: 64rem) {
    .p-chrome {
      padding-inline: clamp(1.5rem, 1rem + 1.5vw, 2.5rem);
    }
  }

  /* Label: head ---------------------------------------------------------- */
  .p-head {
    --head-pad: var(--gutter);
    container-type: inline-size;
    /* The top padding is what holds the back link clear of the fixed
       chrome above it. It was 4.8/5.2rem, which put the link's cap-height
       17px under the language pill on a desktop — inside the chrome's own
       padding box, so the two read as one crowded row rather than as the
       chrome and then the label. Taken to 6.75rem first, which cleared the
       crowding at 42px but was not a change anyone could see; the point
       was the space, not the absence of a collision, so it went further,
       then back down by 25px in two passes once it could be seen and
       judged — it stands at 87px on a phone and 111px on a desktop, which
       leaves the back link clearing the pills by 39px and 45px. The foot
       took 10px in the other direction, so the head sits a little lower
       between the chrome and the title beneath it — written as a `calc()`
       on the spacing token rather than a flat value, so it still moves
       with the scale. Measured, not guessed: QA holds the clearance rather
       than the padding, because the padding is only the means and the
       chrome's height is a clamp that can move. */
    /* It scales the way the chrome above it does. A flat 87px was right on
       a phone and 9px too tight on a tablet, because the chrome is not a
       fixed height — its padding is a vw clamp and its wordmark a wider
       one, so it grows between those widths while a flat padding does
       not. Solved for the two ends rather than nudged: 87px at 390px,
       107px at 834px, capped where the desktop rule takes over. */
    padding: clamp(5.4375rem, 4.34rem + 4.5vw, 6.9375rem) var(--head-pad)
      calc(var(--space-s) + 10px);
  }

  .p-back {
    display: inline-flex;
    flex-direction: column;
    justify-content: center;
    gap: 0.35rem;
    min-height: 44px;
    max-width: 100%;
    font-family: var(--font-display);
    font-stretch: 118%;
    font-weight: 560;
    font-size: var(--step--1);
    line-height: 1.4;
    color: var(--orange);
    text-decoration: none;
    margin-block-end: var(--space-s);
  }

  .p-back-action {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
  }

  .p-back-action .icon-chevron {
    width: 1.1rem;
    height: 1.1rem;
  }

  .p-back-category {
    color: #fff;
    font-size: calc(var(--step--1) - 1px);
    font-weight: inherit;
    font-stretch: inherit;
  }

  .p-back:hover .p-back-action,
  .p-back:focus-visible .p-back-action {
    text-decoration: underline;
    text-underline-offset: 0.2em;
    text-decoration-thickness: 1px;
  }

  .p-back:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 5px;
  }

  .p-kind {
    font-family: var(--font-display);
    font-stretch: 125%;
    font-weight: 560;
    font-size: var(--step--1);
    line-height: 1.04;
    letter-spacing: 0.005em;
    color: #fff;
    text-transform: none;
  }

  .p-title {
    /* Longest word (e.g. TRANSFORMACIJOS) must stay inside the label.
       Sized against the actual container width; the vw variant below is
       the fallback for browsers without container-query units. */
    font-size: min(var(--step-2), var(--title-vw, 10vw));
    font-size: min(
      var(--step-2),
      calc((100cqw - 2 * var(--head-pad)) / (var(--title-len, 12) * 0.92))
    );
    line-height: 1.08;
    margin-block: 0.35rem 0.55rem;
    text-wrap: balance;
  }

  /* Media wall ------------------------------------------------------------ */
  .p-wall {
    position: relative;
    height: 62svh;
    min-width: 0;
    max-width: 100%;
    overflow: clip;
    background: var(--black-true);
  }

  .p-wall-track {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    gap: clamp(0.25rem, 0.5vw, 0.6rem);
    height: 100%;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
  }

  .p-wall-track::-webkit-scrollbar {
    display: none;
  }

  .p-item {
    flex: 0 0 auto;
    height: 100%;
    aspect-ratio: var(--ar, 3 / 2);
    max-width: 94vw;
    scroll-snap-align: start;
    overflow: clip;
  }

  .p-item .p-item-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .p-item-video video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    background: var(--black);
  }

  /* Wall controls */
  .p-wall-ui {
    position: absolute;
    inset-inline: var(--gutter);
    inset-block-end: 1.6rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.9rem;
    pointer-events: none;
  }

  .p-wall-ui > * {
    pointer-events: auto;
  }

  /* Stepped back while a video plays. `inert` is set alongside this from
     the script — opacity alone would leave invisible buttons in the tab
     order, which is worse than leaving them visible. */
  .p-wall-ui.is-away {
    opacity: 0;
    pointer-events: none;
  }

  @media (prefers-reduced-motion: no-preference) {
    .p-wall-ui {
      transition: opacity var(--dur-move) var(--ease-out);
    }
  }

  html.no-js .p-wall-ui {
    display: none;
  }

  .p-pos {
    margin: 0;
    font-family: var(--font-display);
    font-stretch: 118%;
    font-size: var(--step--1);
    letter-spacing: 0.08em;
    color: #fff;
    text-shadow: 0 0 12px oklch(0.1 0.01 55 / 0.9);
  }

  /* Prev / next: quiet rings that answer with light, matching the
     exhibition wall's arrows on the homepage */
  .p-nav {
    display: flex;
    align-items: center;
    gap: 0.6rem;
  }

  /* the ring itself is .arrow-ring in global.css — shared with the
     homepage exhibition so the two walls cannot drift apart */

  /* Label: full text -------------------------------------------------------- */
  .p-info {
    padding: var(--space-m) var(--gutter) var(--space-l);
    max-width: 46rem;
  }

  .p-desc {
    font-family: var(--font-body);
    font-stretch: 100%;
    font-weight: 500;
    font-size: var(--step-0);
    line-height: 1.45;
    text-wrap: balance;
    margin-block-end: var(--space-m);
  }

  .p-section-heading {
    font-size: var(--step-0);
    color: #fff;
    margin-block-end: var(--space-2xs);
  }

  .p-block {
    margin-block-end: var(--space-m);
  }

  .p-block h2 {
    font-size: var(--step-0);
    color: #fff;
    margin-block-end: var(--space-2xs);
  }

  .p-block p {
    line-height: 1.6;
    max-width: 65ch;
  }

  .p-credits {
    margin: 0 0 var(--space-m);
    display: grid;
    gap: var(--space-xs);
    padding-block-start: var(--space-s);
    border-block-start: 1px solid var(--seam);
  }

  .p-credits dt {
    font-family: var(--font-display);
    font-stretch: 125%;
    font-weight: 560;
    line-height: 1.04;
    letter-spacing: 0.005em;
    font-size: var(--step--1);
    color: #fff;
    text-transform: uppercase;
    margin-block-end: 0.3rem;
  }

  .p-credits dd {
    margin: 0;
    line-height: 1.65;
    max-width: 55ch;
  }

  /* Desktop: fixed label + full-height wall, no page scroll ---------------- */
  @media (min-width: 64rem) {
    .proom {
      display: grid;
      grid-template-columns: clamp(21rem, 28vw, 26rem) minmax(0, 1fr);
      grid-template-rows: auto 1fr;
      height: 100svh;
      overflow: clip;
    }

    .p-head {
      --head-pad: clamp(1.5rem, 1rem + 1.5vw, 2.5rem);
      grid-column: 1;
      grid-row: 1;
      padding: 6.9375rem var(--head-pad) calc(var(--space-2xs) + 10px);
      border-inline-end: 1px solid var(--seam);
    }

    .p-info {
      grid-column: 1;
      grid-row: 2;
      overflow-y: auto;
      max-width: none;
      padding: var(--space-s) clamp(1.5rem, 1rem + 1.5vw, 2.5rem) var(--space-l);
      border-inline-end: 1px solid var(--seam);
    }

    .p-wall {
      grid-column: 2;
      grid-row: 1 / span 2;
      height: 100%;
    }

    .p-wall-track {
      scroll-snap-type: none;
    }

    .p-item {
      max-width: none;
    }

    .p-title {
      /* longest word must stay inside the label panel */
      font-size: min(var(--step-1), var(--title-vw-d, 2.2vw));
      font-size: min(
        var(--step-1),
        calc((100cqw - 2 * var(--head-pad)) / (var(--title-len, 12) * 0.92))
      );
    }

    .p-desc {
      font-size: var(--step-0);
      line-height: 1.5;
    }

    .p-wall-ui {
      inset-inline: auto var(--gutter);
      inset-block-end: 1.4rem;
      flex-direction: column;
      align-items: flex-end;
      gap: 0.6rem;
    }
  }

  /* Short viewports / 200% zoom: let the page scroll rather than crush */
  @media (min-width: 64rem) and (max-height: 34rem) {
    .proom {
      display: block;
      height: auto;
      overflow: visible;
    }

    .p-wall {
      height: 80svh;
    }

    .p-head,
    .p-info {
      border-inline-end: 0;
    }
  }


/* ==========================================================
   src/pages/404.astro
   ========================================================== */

  .nf-chrome {
    position: fixed;
    inset-inline: 0;
    top: 0;
    z-index: var(--z-nav);
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: clamp(1rem, 0.8rem + 1vw, 1.8rem) var(--gutter);
    /* Always white, never the difference of what is behind it. Difference
       blend kept the chrome legible on black and on white, but against a
       mid-grey photograph it inverted to mid-grey and the wordmark went
       soft. The logo is separated by a shadow of its own instead, and the
       pills are frosted like the wall's arrows — both of which leave the
       photograph exactly as it was, which dimming would not. */
    color: #fff;
  }

  .nf-chrome-logo {
    /* the wordmark stays white on any photograph: a soft dark halo,
       not a wash over the image behind it */
    filter: drop-shadow(0 1px 10px oklch(0.08 0.006 55 / 0.85));
    display: block;
    color: #fff;
  }

  .nf-chrome-logo .nf-chrome-logo-svg {
    width: clamp(7rem, 6rem + 4vw, 9.5rem);
  }

  .nf-chrome-lang {
    font-family: var(--font-display);
    font-stretch: 120%;
    font-weight: 580;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.85rem;
    text-decoration: none;
    border: 1.5px solid #fff;
    border-radius: 999px;
    color: #fff;
    padding: 0.45rem 1rem;
  }

  .nf {
    background: var(--orange);
    color: var(--ink-on-orange);
    min-height: 100svh;
    display: grid;
    align-items: center;
    padding-block: var(--space-2xl);
  }

  .nf > .container {
    width: 100%;
    min-width: 0;
  }

  /* Style 2 — dark stage */
  html[data-style='2'] .nf {
    background: var(--black-true);
    color: var(--paper-ink-on-black);
  }

  html[data-style='2'] .nf-code {
    color: var(--orange);
  }

  html[data-style='2'] .nf-actions a {
    color: var(--paper-ink-on-black);
  }

  html[data-style='2'] .nf-actions a:hover {
    color: var(--orange);
  }

  .nf-code {
    font-family: var(--font-display);
    font-stretch: 125%;
    font-weight: 600;
    font-size: var(--step-5);
    line-height: 1;
  }

  .nf .nf-title {
    font-size: var(--step-3);
    margin-block-start: var(--space-s);
  }

  .nf-lead {
    margin-block-start: var(--space-s);
    font-size: var(--step-1);
    max-width: 38ch;
  }

  .nf-actions {
    margin-block-start: var(--space-l);
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-m);
  }

  .nf-actions a {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-family: var(--font-display);
    font-stretch: 120%;
    font-weight: 560;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: var(--step-0);
    color: var(--ink-on-orange);
    text-decoration: none;
    border-block-end: 2px solid currentColor;
    padding-block-end: 0.2rem;
  }

  .nf-actions a:hover {
    color: var(--black-true);
  }
