/* =========================================================================
   BRIDGECOM — L'ENSEIGNE
   Algerian shopfront signage: baked enamel on steel, limewash plaster,
   vinyl-cut letterforms, drilled screws.
   ========================================================================= */

/* --- 1. Reset ---------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
button, input, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; }
ul, ol { list-style: none; padding: 0; }

/* --- 2. The five cans -------------------------------------------------- */

:root {
  /* Locked palette. No invented in-between tones. */
  --limewash:  #F2EDE4;   /* the plastered wall */
  --night:     #141210;   /* baked enamel */
  --orange:    #E2570F;   /* signal orange — COMMIT ONLY */
  --blue:      #15497E;   /* enamel blue */
  --cream:     #F7F2E6;   /* the painted letter */

  /* Material tones. Each names a real thing. */
  --plaster:   #E6DFD2;   /* the wall's second coat, in shade */
  --chalk:     #B5AEA2;   /* chalk dust settled on dark enamel */
  --steel:     #2A2520;   /* the plate's folded edge */
  /* Brushed anodised stock. Two bright bands, two dark horizons; every
     stop clears 5.3:1 on the night plate. The hero wordmark and the
     Bridgecom house name are cut from it, so they read as one sign. */
  --metal: linear-gradient(177deg,
    #9C968A 0%,
    #E6DFD2 9%,
    #FFFFFF 15%,
    #CFC9BD 22%,
    #8E887C 34%,
    #B5AEA2 41%,
    #F7F2E6 50%,
    #FFFFFF 55%,
    #D7D2C8 63%,
    #8E887C 76%,
    #CFC9BD 85%,
    #F2EDE4 100%);

  --ground:        var(--limewash);
  --ground-shade:  var(--plaster);
  --ink:           var(--night);
  --ink-quiet:     var(--blue);
  --panel:         var(--night);
  --panel-ink:     var(--cream);
  --panel-quiet:   var(--chalk);
  --accent:        var(--orange);
  --accent-ink:    var(--night);   /* night on orange = 4.99:1 */
  --rule:          var(--night);
  --edge:          #141210;        /* the boundary of anything operable */

  /* Three rule weights, used as a meaning scale. */
  --w-hair: 1px;
  --w-rule: 2px;
  --w-band: 8px;

  --pad: clamp(1.25rem, 4vw, 4rem);
  --max: 86rem;

  /* Emil's curves: the browser defaults are too weak to read as intentional. */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

  color-scheme: light;
}

/* --- 3. Base ----------------------------------------------------------- */

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

body {
  background: var(--ground);
  color: var(--ink);
  font-family: "Hanken Grotesk", system-ui, sans-serif;
  font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* The painted letter. Archivo pushed wide and heavy: a brush, not a UI font. */
.sign {
  font-family: "Archivo", system-ui, sans-serif;
  font-variation-settings: "wdth" 118, "wght" 800;
  text-transform: uppercase;
  line-height: 0.88;
  letter-spacing: -0.025em;
  text-wrap: balance;
}

h1, h2, h3 { text-wrap: balance; }
p { max-width: 68ch; }

/* --- 4. Browser surfaces ----------------------------------------------- */

::selection { background: var(--accent); color: var(--accent-ink); }
body { caret-color: var(--accent); accent-color: var(--accent); }

:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: var(--w-rule) solid var(--accent);
  outline-offset: 3px;
  border-radius: 1px;
}

* { scrollbar-width: thin; scrollbar-color: var(--ink) var(--ground-shade); }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--ground-shade); }
::-webkit-scrollbar-thumb { background: var(--ink); border: 3px solid var(--ground-shade); }

a:not(.plaque):not(.navlink):not(.foot-nav a) {
  text-decoration-thickness: var(--w-hair);
  text-underline-offset: 0.22em;
  text-decoration-color: var(--ink-quiet);
}
a:not(.plaque):not(.navlink):hover { text-decoration-color: var(--accent); }

.num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* --- 5. The screw ------------------------------------------------------ */

.screws { position: absolute; inset: 0; pointer-events: none; }
.screws i {
  position: absolute;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--panel-quiet);
}
.screws i::after {
  content: "";
  position: absolute; left: 1.5px; right: 1.5px; top: 4px;
  height: var(--w-hair); background: var(--steel);
}
.screws i:nth-child(1) { top: 16px; left: 16px; }
.screws i:nth-child(2) { top: 16px; right: 16px; transform: rotate(38deg); }
.screws i:nth-child(3) { bottom: 16px; left: 16px; transform: rotate(72deg); }
.screws i:nth-child(4) { bottom: 16px; right: 16px; transform: rotate(14deg); }

/* --- 6. Shell ---------------------------------------------------------- */

.wrap { max-width: var(--max); margin-inline: auto; padding-inline: var(--pad); }
.wrap--wide { max-width: 100rem; }

section { position: relative; }
.band { padding-block: clamp(4.5rem, 10vw, 9rem); }
.band--dark { background: var(--panel); color: var(--panel-ink); }
.band--shade { background: var(--ground-shade); }

.band > .wrap > h2.sign,
.band > .wrap > .head > h2.sign {
  font-size: clamp(2.1rem, 5.6vw, 4.8rem);
  margin-bottom: 0.6em;
}
.lede {
  font-size: clamp(1.05rem, 0.95rem + 0.5vw, 1.35rem);
  color: var(--ink-quiet);
  margin-bottom: 3rem;
}
.band--dark .lede { color: var(--panel-quiet); }

/* --- 7. Navigation ----------------------------------------------------- */

.awning {
  position: sticky; top: 0; z-index: 30;
  background: var(--ground);
  border-bottom: var(--w-hair) solid var(--rule);
}
.awning .wrap { display: flex; align-items: center; gap: 1.5rem; min-height: 4.25rem; }

/* The supplied lockup, used whole. It carries its own spacing between the
   medallion and the wordmark, so .mark adds none. Two files, not one tinted
   image: the wordmark is black on limewash and cream on night, while the
   medallion stays as drawn on both. */
.mark { display: flex; align-items: center; text-decoration: none; margin-right: auto; }
/* max-width:100% is set on every image; here the parent is deliberately
   narrower than the lockup so it can be clipped, so this one opts out. */
.mark__img { height: 30px; width: auto; max-width: none; flex: none; display: block; }
.foot__grid .mark__img { height: 38px; }

@media (max-width: 34rem) {
  /* The wordmark's outlined half goes thin below this; the medallion alone
     still reads, and the header has a phone and a menu to fit. */
  .awning .mark { width: 30px; overflow: hidden; }
  .awning .mark__img { height: 30px; }
}

.nav { display: flex; align-items: center; gap: 1.6rem; }
.navlink {
  font-size: 0.82rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; text-decoration: none;
  padding-block: 0.4rem;
  border-bottom: var(--w-rule) solid transparent;
  transition: border-color 200ms var(--ease-out);
  white-space: nowrap;
}
.navlink:hover { border-bottom-color: var(--ink); }
.navtoggle { display: none; }
.awning__tel { white-space: nowrap; }
.awning__tel svg { width: 16px; height: 16px; stroke: var(--accent); flex: none; }

@media (max-width: 68rem) {
  .nav {
    position: absolute; inset: 4.25rem 0 auto 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--ground-shade);
    border-bottom: var(--w-rule) solid var(--edge);
    padding: 0.5rem var(--pad) 1.25rem;
  }
  .nav[hidden] { display: none; }
  .navlink { padding-block: 0.95rem; border-bottom: var(--w-hair) solid var(--rule); }
  .navtoggle {
    display: grid; place-items: center;
    width: 44px; height: 44px;
    border: var(--w-rule) solid var(--edge);
  }
  .navtoggle svg { width: 20px; height: 20px; }
  .awning__tel { margin-right: 0.25rem; }
}

/* On the narrowest phones the number becomes its pictogram; the tap target
   and the accessible name survive. */
@media (max-width: 34rem) {
  .awning .wrap { gap: 0.75rem; }
  .awning__tel span { display: none; }
  .awning__tel { padding-inline: 0.85rem; }
}

/* --- 8. The plaque ----------------------------------------------------- */

.plaque {
  position: relative;
  display: inline-flex; align-items: center; gap: 0.6rem;
  padding: 0.95rem 1.5rem;
  font-size: 0.85rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  text-decoration: none;
  min-height: 48px;
  border: var(--w-rule) solid var(--edge);
  background: var(--ground);
  color: var(--ink);
  transition: transform 160ms var(--ease-out),
              background-color 200ms var(--ease-out),
              color 200ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .plaque:hover { background: var(--ground-shade); }
}
/* Instant feedback: the interface heard you. */
.plaque:active { transform: scale(0.975); }

.plaque--commit {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
@media (hover: hover) and (pointer: fine) {
  .plaque--commit:hover { background: var(--accent); filter: saturate(1.12); }
}
.plaque--sm { padding: 0.7rem 1.1rem; font-size: 0.75rem; min-height: 44px; }
.band--dark .plaque { border-color: var(--panel-quiet); color: var(--panel-ink); background: transparent; }
@media (hover: hover) and (pointer: fine) {
  .band--dark .plaque:hover { background: rgba(247, 242, 230, 0.07); }
}
.band--dark .plaque--commit { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }

/* Awaiting its file: the plate is there, unpainted. */
.plaque.is-unset {
  background: var(--ground-shade);
  border: var(--w-hair) solid var(--ink-quiet);
  color: var(--ink-quiet);
  cursor: default;
}
.plaque.is-unset:hover { background: var(--ground-shade); }
.plaque.is-unset:active { transform: none; }
.socials a.is-unset { border-color: var(--panel-quiet); color: var(--panel-quiet); cursor: default; }
.socials a.is-unset:hover { background: none; border-color: var(--panel-quiet); }
.pending {
  font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-quiet);
}

/* --- 9. Figures: every image is a plate behind a window ----------------- */
/* The window clips; the plate inside is oversized so it can be moved and
   grown without ever showing an edge. */

.fig {
  position: relative;
  overflow: hidden;
  background: var(--ground-shade);
}
.fig img {
  width: 100%; height: 100%;
  object-fit: cover;
  will-change: transform;
}

/* --- 10. The hero: a sign mounted on the wall ------------------------- */

.hero { background: var(--ground); padding-top: clamp(0.75rem, 2vw, 1.5rem); }
.hero__wall { padding: 0 var(--pad) clamp(0.75rem, 2vw, 1.5rem); }

.hero__plate {
  position: relative;
  background: var(--panel);
  color: var(--panel-ink);
  min-height: min(66svh, 36rem);
  display: flex; flex-direction: column; justify-content: center;
  padding: clamp(1.75rem, 5vw, 4.5rem);
  overflow: hidden;
}

/* The wordmark is cut out of the plate; what shows through is brushed metal —
   the same anodised stock the letters further down the page are machined from.
   Two bright bands and two dark horizons, on a field three times the height of
   the text, so sliding it vertically drags the sheen across the letters the way
   a real metal sign catches the light as you walk past it.

   Every stop clears 5.3:1 on the night plate, so the dark horizons can cross a
   letter without it dropping out. */
.hero__word {
  font-family: "Archivo", system-ui, sans-serif;
  font-variation-settings: "wdth" 125, "wght" 900;
  /* The name is cut from the plate, so the plate clips it — 10vw alone was
     wider than the plate's inside measure at every width under 430px, and the
     last letter was being shaved. Measured: the fit ceiling is 12.6vw - 13px
     below about 520px, and 10vw above it. min() takes whichever binds, so the
     two meet without a step. */
  font-size: clamp(1.5rem, min(10vw, 12.6vw - 13px), 10rem);
  line-height: 0.82;
  letter-spacing: -0.05em;
  text-transform: uppercase;
  color: var(--panel-ink);
  background-image: var(--metal);
  background-size: 100% 320%;
  background-position: 50% 18%;
  background-repeat: no-repeat;
  /* Light spilling off a lit sign, not a glow effect: cream, wide, and faint
     enough that it never reads as a second colour. */
  text-shadow: 0 0 clamp(6px, 1.1vw, 18px) rgba(247, 242, 230, 0.16);
  width: max-content; max-width: 100%;
  will-change: clip-path, background-position;
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero__word {
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

.hero__slogan {
  font-family: "Noto Kufi Arabic", system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(1.5rem, 5vw, 3.6rem);
  line-height: 1.5;
  color: var(--panel-ink);
  text-shadow: 0.04em 0.045em 0 var(--blue);
  margin-top: clamp(0.9rem, 2.2vw, 1.6rem);
  /* RTL so the glyphs join correctly; aligned left so the slogan stacks
     under the wordmark instead of drifting to the far edge. */
  direction: rtl;
  text-align: left;
}
.hero__gloss {
  margin-top: 0.9rem;
  font-size: clamp(0.85rem, 0.8rem + 0.3vw, 1rem);
  letter-spacing: 0.04em;
  color: var(--panel-quiet);
}

.hero__sill { padding: 0 0 clamp(1.5rem, 4vw, 3rem); }
.hero__sill-in {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 1rem;
}
.hero__sill .rivet { border-color: var(--rule); color: var(--ink); }

@media (max-width: 40rem) {
  .hero__plate { min-height: min(54svh, 27rem); padding-block: clamp(2rem, 7vw, 3rem); }
  .hero__sill-in { flex-direction: column; align-items: stretch; }
  .hero__sill .plaque { justify-content: center; }
}

/* --- 11. The wall ------------------------------------------------------ */

.wall { padding-block: clamp(2.5rem, 5vw, 4rem); }
.wall__grid {
  display: grid; gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  align-items: end;
}
.wall__lede {
  font-size: clamp(1.25rem, 1rem + 1.1vw, 2rem);
  font-variation-settings: "wdth" 104, "wght" 650;
  letter-spacing: -0.02em; line-height: 1.12; max-width: 26ch;
}
.wall__sub { margin-top: 1.25rem; color: var(--ink-quiet); max-width: 46ch; }

.counts { display: flex; flex-wrap: wrap; gap: clamp(1.25rem, 3vw, 3rem); min-width: 0; }
.count__n {
  display: block;
  font-family: "Archivo", system-ui, sans-serif;
  font-variation-settings: "wdth" 120, "wght" 900;
  font-size: clamp(2.4rem, 5.6vw, 5rem);
  line-height: 0.82; letter-spacing: -0.045em;
}
.count__l {
  display: block; margin-top: 0.7rem;
  font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--ink-quiet);
  border-top: var(--w-rule) solid var(--rule); padding-top: 0.5rem;
}
.cta-row { display: flex; flex-wrap: wrap; gap: 0.9rem; margin-top: 2rem; }

@media (max-width: 54rem) { .wall__grid { grid-template-columns: 1fr; } }

/* --- 12. The marquee --------------------------------------------------- */
/* A painted band that never stops. Constant motion, so: linear. */

.marquee {
  background: var(--panel); color: var(--panel-ink);
  padding-block: clamp(0.9rem, 2vw, 1.4rem);
  overflow: hidden;
  border-block: var(--w-rule) solid var(--steel);
}
.marquee__track { display: flex; width: max-content; will-change: transform; }
.marquee__row {
  display: flex; align-items: center; gap: clamp(1.5rem, 4vw, 3rem);
  padding-right: clamp(1.5rem, 4vw, 3rem);
  flex: none;
}
.marquee__row span {
  font-family: "Archivo", system-ui, sans-serif;
  font-variation-settings: "wdth" 112, "wght" 800;
  font-size: clamp(1.4rem, 3.6vw, 3rem);
  text-transform: uppercase; letter-spacing: -0.02em;
  white-space: nowrap;
}
.marquee__row i {
  width: clamp(10px, 1.6vw, 16px); height: clamp(10px, 1.6vw, 16px);
  background: var(--panel-quiet); flex: none; border-radius: 50%;
}

/* --- 13. Service plates ------------------------------------------------ */

.plates {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0;
  border: var(--w-rule) solid var(--rule);
}
.plate {
  position: relative;
  padding: clamp(1.75rem, 4vw, 3rem);
  border-right: var(--w-hair) solid var(--rule);
  border-bottom: var(--w-hair) solid var(--rule);
  min-height: clamp(15rem, 26vw, 20rem);
  display: flex; flex-direction: column;
}
.plate:nth-child(2n) { border-right: 0; }
.plate:nth-child(n+3) { border-bottom: 0; }
.plate--1 { background: var(--panel); color: var(--panel-ink); }
.plate--2 { background: var(--blue); color: var(--cream); }
.plate--3 { background: var(--ground-shade); color: var(--ink); }
.plate--4 { background: var(--ground); color: var(--ink); }
.plate__name { font-size: clamp(1.6rem, 3.4vw, 2.6rem); margin-bottom: auto; }
.plate__list { margin-top: 2rem; display: grid; gap: 0.45rem; }
.plate__list li {
  font-size: 0.88rem; padding-top: 0.45rem;
  border-top: var(--w-hair) solid currentColor;
}

@media (max-width: 48rem) {
  .plates { grid-template-columns: 1fr; }
  .plate { border-right: 0 !important; border-bottom: var(--w-hair) solid var(--rule) !important; min-height: 0; }
  .plate:last-child { border-bottom: 0 !important; }
}

/* --- 14. L'atelier: the pinned plate that grows ------------------------ */

.atelier { position: relative; background: var(--panel); color: var(--panel-ink); }
.atelier__stage {
  height: 100svh; min-height: 34rem;
  display: grid; place-items: center;
  position: relative; overflow: hidden;
}
.atelier__fig { position: absolute; inset: 0; }
.atelier__fig img { width: 100%; height: 100%; object-fit: cover; }
/* The plate reads under the type without a wash of flat black over it. */
.atelier__veil {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(20,18,16,0.62), rgba(20,18,16,0.30) 45%, rgba(20,18,16,0.74));
}
.atelier__say {
  position: relative; text-align: center;
  padding: clamp(2rem, 5vw, 4rem) var(--pad);
  max-width: 52rem;
  /* The flat veil cannot guarantee contrast over an arbitrary photograph —
     a pale plate washes the type out. This scrim sits only behind the words,
     so the picture keeps its light and the text keeps its 8:1. */
  background: radial-gradient(ellipse 72% 62% at 50% 50%,
    rgba(20, 18, 16, 0.82) 0%,
    rgba(20, 18, 16, 0.62) 55%,
    rgba(20, 18, 16, 0) 100%);
}
.atelier__say h2 {
  font-size: clamp(2rem, 7vw, 5.5rem);
  margin-bottom: 0.5em;
}
.atelier__say p { color: var(--cream); margin-inline: auto; }

/* --- 15. The work: the pinned press ------------------------------------ */

/* Base state — and what a phone, reduced motion, or a page without
   JavaScript keeps: six plates in a mosaic with their captions under them.
   It is complete on its own. The pinned run only rearranges it. */

.travail__stage { padding-block: clamp(4.5rem, 10vw, 9rem); }
.travail__say h2.sign { font-size: clamp(2.1rem, 5.6vw, 4.8rem); margin-bottom: 0.6em; }

/* These three only mean something while the run is pinned. */
.travail__count, .travail__ticks, .board__edge { display: none; }

.boards {
  display: grid; grid-template-columns: repeat(6, 1fr);
  gap: var(--w-rule);
  background: var(--rule); border: var(--w-rule) solid var(--rule);
}
.board { position: relative; grid-column: span 2; background: var(--ground); }
.board--wide { grid-column: span 3; }
/* 5:4 everywhere, wide cell or narrow, and the same shape as the pinned
   window: the plate files are 5:4, so no cell ever crops artwork that was
   composed to fit one. */
.board__fig { aspect-ratio: 5 / 4; }
.board__cap { padding: 1rem 1.15rem 1.25rem; border-top: var(--w-hair) solid var(--rule); }
.board__cap h3 { font-size: 1.02rem; font-weight: 700; letter-spacing: -0.01em; }
.board__cap p { font-size: 0.82rem; color: var(--ink-quiet); margin-top: 0.2rem; }

@media (max-width: 60rem) { .boards { grid-template-columns: repeat(2, 1fr); } .board, .board--wide { grid-column: span 1; } }
@media (max-width: 40rem) { .boards { grid-template-columns: 1fr; } }

/* The press. The section holds the screen and the six plates are laid down
   over each other, each from its own top edge, with the press head running
   ahead of the ink. Same grammar as the hero wordmark — painted on from an
   edge — turned ninety degrees so the two cannot be confused, and nothing
   like the block dissolve in the manifesto. */

@media (min-width: 60rem) and (prefers-reduced-motion: no-preference) {
  .js-motion .travail__stage {
    height: 100vh; height: 100svh;
    padding-block: 0; display: grid; align-items: center; overflow: hidden;
    /* An auto track would size itself to the content and never reach the
       measure below; this makes the stage hand over its full width. */
    grid-template-columns: minmax(0, 1fr);
  }
  /* The page measure, not a wider one. A section that starts 48px left of
     every other section reads as a mistake, however good the reason — the
     grid is the backbone. The room the window needs comes out of the title
     instead: narrowed to 12ch it breaks into three lines, which lets it be
     *bigger* while taking a third of the width rather than a half.

     The left column is one block, centred against the plate. Pinning its two
     halves to the plate's top and bottom edges aligns beautifully on paper
     and leaves a hole in the middle in practice: there is simply not enough
     copy to span a 650px plate.

     The centring is free. A spanning item distributes its extra height
     *equally* between the two auto rows it crosses, so the row boundary lands
     at exactly the midpoint of the leftover space — pull the title down to it
     and push the index up to it and the pair is perfectly centred, with only
     the row gap between them. No measurement, no magic number. */
  .js-motion .travail__in {
    /* width: the auto inline margins .wrap carries stop a grid item from
       stretching, so without this the measure collapses to max-content. */
    display: grid; width: 100%;
    grid-template-columns: minmax(0, 0.72fr) minmax(0, 1.28fr);
    grid-template-areas: "say win" "meta win";
    grid-template-rows: auto auto;
    column-gap: clamp(2rem, 4vw, 4.5rem);
    row-gap: clamp(1.75rem, 3.2vw, 3rem);
  }
  .js-motion .travail__say { grid-area: say; align-self: end; }
  .js-motion #travail-boards { grid-area: win; }
  .js-motion .travail__meta { grid-area: meta; align-self: start; }

  /* Three lines by measure, not by luck. Set to the column width the title
     has to be small enough that "LE TRAVAIL, PAS" fits on one line; held to
     12ch it breaks after "TRAVAIL," and can be half as large again. */
  .js-motion .travail__say h2.sign {
    font-size: clamp(2rem, 3.3vw, 3.6rem);
    max-width: 14ch; margin-bottom: 0.45em;
  }
  .js-motion .travail__say .lede { margin-bottom: 0; }
  .js-motion .travail__meta .cta-row { margin-top: 1.5rem; }

  /* One window, the six sheets stacked inside it. */
  .js-motion #travail-boards {
    display: block; position: relative; gap: 0;
    aspect-ratio: 5 / 4; max-height: 76svh;
  }
  .js-motion #travail-boards .board,
  .js-motion #travail-boards .board--wide {
    position: absolute; inset: 0; grid-column: auto; overflow: hidden;
  }
  /* Every sheet above the first starts unprinted. Declared in CSS, not in
     JavaScript, so there is no frame in which all six are piled up. */
  .js-motion #travail-boards .board + .board {
    clip-path: inset(0 0 100% 0); will-change: clip-path;
  }
  .js-motion #travail-boards .board__fig,
  .js-motion #travail-boards .board--wide .board__fig {
    position: absolute; inset: 0; aspect-ratio: auto;
  }

  /* The label belongs to its sheet, so it is printed with it — as a band,
     not a wash. These plates are artwork on their own pale grounds, and a
     gradient scrim over one of those reads as a stain and swallows the bottom
     third of the work. The image files are built with their bottom fifth left
     clear for exactly this band. */
  .js-motion #travail-boards .board__cap {
    position: absolute; inset: auto 0 0 0;
    padding: clamp(0.8rem, 1.3vw, 1.15rem) clamp(1.1rem, 2.2vw, 1.8rem) clamp(0.9rem, 1.5vw, 1.3rem);
    color: var(--panel-ink); background: var(--panel);
    border-top: var(--w-rule) solid var(--rule);
  }
  .js-motion #travail-boards .board__cap h3 { font-size: clamp(1.05rem, 1.5vw, 1.45rem); }
  .js-motion #travail-boards .board__cap p { font-size: clamp(0.82rem, 0.95vw, 0.92rem); color: var(--panel-quiet); margin-top: 0.15rem; }

  /* The press head. A full-size box with a line along its top, pushed down
     the plate: the edge costs one transform and never a layout. */
  .js-motion .board__edge {
    display: block; position: absolute; inset: 0; z-index: 3;
    pointer-events: none; opacity: 0;
    border-top: 2px solid var(--panel);
    box-shadow: 0 1px 0 rgba(247, 242, 230, 0.62);
  }

  /* Which sheet you are on, in two readings: the number and the rule. */
  .js-motion .travail__count {
    display: block; margin-bottom: 0.8rem;
    font-family: "Archivo", system-ui, sans-serif;
    font-variation-settings: "wdth" 120, "wght" 500;
    font-size: 0.9rem; letter-spacing: 0.1em; color: var(--ink-quiet);
  }
  .js-motion .travail__count b {
    font-variation-settings: "wdth" 120, "wght" 900;
    font-size: 1.7rem; color: var(--ink); margin-right: 0.12em;
  }
  .js-motion .travail__ticks {
    display: flex; gap: 0.45rem; margin: 0; padding: 0; list-style: none;
  }
  .js-motion .travail__ticks li {
    width: 1.75rem; height: var(--w-rule);
    background: currentColor; opacity: 0.25;
    transition: opacity 250ms var(--ease-out);
  }
  .js-motion .travail__ticks li.on { opacity: 1; }
}

/* --- 16. Quatre temps: the pinned horizontal run ----------------------- */

.temps { background: var(--ground-shade); overflow: hidden; }
.temps__viewport { height: 100svh; min-height: 36rem; display: flex; align-items: center; }
.temps__track { display: flex; gap: clamp(1rem, 2.5vw, 2rem); padding-inline: var(--pad); will-change: transform; }
.etape {
  flex: none;
  width: clamp(16rem, 26vw, 24rem);
  background: var(--ground);
  border: var(--w-rule) solid var(--rule);
  display: flex; flex-direction: column;
}
.etape__fig { aspect-ratio: 9 / 11; }
.etape__body { padding: 1.25rem 1.35rem 1.6rem; }
.etape__n {
  font-family: "Archivo", system-ui, sans-serif;
  font-variation-settings: "wdth" 120, "wght" 900;
  font-size: 1.1rem; color: var(--ink-quiet); letter-spacing: 0.04em;
}
.etape__body h3 { font-size: clamp(1.2rem, 2.2vw, 1.6rem); margin: 0.35rem 0 0.5rem; }
.etape__body p { font-size: 0.88rem; color: var(--ink-quiet); }

.temps__head { padding-block: clamp(3rem, 7vw, 5rem) 0; }

/* --- 17. The manifesto ------------------------------------------------- */

/* A room of its own. The section holds the screen while the sentence lights
   itself word by word and the work beside it rebuilds in blocks — the way a
   plotter fills a shape, one pass at a time. */

.manifesto { background: var(--panel); color: var(--panel-ink); }
.manifesto__stage {
  display: grid; align-items: center;
  padding-block: clamp(4rem, 10vw, 8rem);
}
.manifesto__in { display: grid; gap: clamp(2.5rem, 6vw, 4rem); align-items: center; min-height: 0; }

.manifesto__text {
  font-family: "Archivo", system-ui, sans-serif;
  font-variation-settings: "wdth" 108, "wght" 750;
  font-size: clamp(1.6rem, 4.2vw, 3.6rem);
  line-height: 1.08; letter-spacing: -0.03em;
  text-transform: uppercase; max-width: 20ch;
}
.manifesto__text .w { color: rgba(247, 242, 230, 0.16); transition: color 300ms var(--ease-out); }
.manifesto__text .w.on { color: var(--panel-ink); }
.manifesto__sig {
  margin-top: clamp(1.5rem, 3vw, 2.5rem); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.17em; text-transform: uppercase; color: var(--panel-quiet);
}

.manifesto__side { min-height: 0; display: grid; gap: 1rem; align-content: center; }

/* Base state — also what a visitor without JavaScript, or with reduced
   motion, gets: the three plates as a column. Nothing depends on a script. */
.manifesto__fig {
  position: relative; margin: 0; min-height: 0;
  display: grid; gap: var(--w-rule);
  background: var(--rule); border: var(--w-rule) solid var(--rule);
}
@media (min-width: 48rem) {
  .manifesto__fig { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.manifesto__fig img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 4 / 5; object-fit: cover;
}
.manifesto__fig .screws { display: none; }

.manifesto__ticks { display: flex; gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
.manifesto__ticks li {
  width: 2.25rem; height: var(--w-rule);
  background: currentColor; opacity: 0.3;
  transition: opacity 250ms var(--ease-out);
}
.manifesto__ticks li.on { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  /* One window, the three plates stacked inside it. */
  .js-motion .manifesto__stage { height: 100vh; height: 100svh; }
  .js-motion .manifesto__fig {
    display: block; overflow: hidden;
    aspect-ratio: 4 / 5; max-height: 72svh; width: 100%;
  }
  .js-motion .manifesto__fig img {
    position: absolute; inset: 0; width: 100%; height: 100%; aspect-ratio: auto;
  }
  .js-motion .manifesto__fig .screws { display: block; z-index: 3; }
}

/* The blocks the picture rebuilds from. One layer per plate above the first. */
.pxl { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.pxl i {
  position: absolute; background-repeat: no-repeat;
  opacity: 0; transform: scale(0.84);
  transition: opacity 110ms linear, transform 170ms var(--ease-out);
}
.pxl i.on { opacity: 1; transform: none; }

@media (min-width: 60rem) and (prefers-reduced-motion: no-preference) {
  .js-motion .manifesto__in { grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); gap: clamp(3rem, 6vw, 6rem); }
  .js-motion .manifesto__text { max-width: 14ch; }
}

@media (max-width: 59.99rem) and (prefers-reduced-motion: no-preference) {
  /* Phone: the sentence first, the window takes whatever height is left over.
     Measured rather than guessed, so nothing can be pushed off the screen. */
  .js-motion .manifesto__stage { align-items: stretch; padding-block: clamp(4.5rem, 9vw, 6rem); }
  .js-motion .manifesto__in {
    min-height: 0;
    grid-template-rows: auto minmax(0, 1fr); align-content: stretch;
    gap: clamp(1.25rem, 4vw, 2rem);
  }
  /* align-items: center on the parent would shrink this to its content and
     leave the row empty; it has to claim the whole row. */
  .js-motion .manifesto__side {
    align-self: stretch; min-height: 0;
    grid-template-rows: minmax(0, 1fr) auto; align-content: stretch;
  }
  /* No percentage height: the box simply stretches into the row it was given,
     which is the only measurement guaranteed to be definite here. */
  .js-motion .manifesto__fig { aspect-ratio: auto; height: auto; max-height: none; min-height: 0; }
}

/* --- 18. The client rail ----------------------------------------------- */

/* Their names, on plates, screwed to a rail — the site's own plate language
   rather than a logo carousel. The strip's top rule is the mounting bar, each
   plate hangs from it on two screws, and the 2px gaps over a night ground are
   the same hairlines every other plate grid on this page is drawn with.

   It is the page's second travelling band and it is deliberately unlike the
   first: the slogan marquee is dark, type only, fast, and driven by scroll
   velocity; this one is light, made of framed objects, slow, constant, and
   stops under the cursor. Same page, different device.

   The base state — and reduced motion, and a page whose script never arrived —
   is the same plates wrapped into rows. Only the travel is an enhancement. */

.rail {
  position: relative; overflow: hidden;
  /* The lede's own 3rem is not enough against the section's bottom padding;
     without this the strip reads as pushed up into the heading. */
  margin-top: clamp(2rem, 5vw, 4rem);
  background: var(--rule);
  border-block: var(--w-rule) solid var(--rule);
}
.rail__track { display: flex; }
/* Base: the plates wrap inside the rail. The row needs a definite width to
   do that — left to size itself as a flex item it takes max-content, does not
   wrap at all, and the last plates are simply clipped away. */
.rail__row {
  display: flex; flex-wrap: wrap; justify-content: center;
  width: 100%; gap: var(--w-rule);
  margin: 0; padding: 0; list-style: none;
}
.splate {
  flex: none; position: relative;
  width: clamp(11rem, 18vw, 17rem);
  aspect-ratio: 3 / 2;
  background: var(--ground-shade);
}
/* The plate files are 3:2 on the white the logos were drawn on, so the image
   fills its plate exactly and there is no ground to match and no seam. */
.splate img { display: block; width: 100%; height: 100%; object-fit: cover; }

@media (prefers-reduced-motion: no-preference) {
  .js-motion .rail__track { width: max-content; will-change: transform; }
  .js-motion .rail__row {
    flex: none; width: auto; flex-wrap: nowrap; justify-content: flex-start;
    /* The trailing rule belongs to the row, so one copy butts against the next
       on exactly the same hairline — and the row's own width is then the loop
       distance, with nothing left over to drift by. */
    padding-right: var(--w-rule);
  }
}

/* --- 19. (Constantine reuses the atelier stage) ------------------------ */

/* --- 20. Webexpo ------------------------------------------------------- */

.lits { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--w-rule); background: var(--steel); border: var(--w-rule) solid var(--steel); }
.lit { background: var(--panel); color: var(--panel-ink); display: flex; flex-direction: column; }
.lit__fig { aspect-ratio: 10 / 7; }
.lit__body { padding: clamp(1.25rem, 3vw, 2rem); display: flex; flex-direction: column; gap: 0.9rem; flex: 1; }
.lit h3 { font-size: clamp(1.2rem, 2.4vw, 1.7rem); }
.lit p { font-size: 0.9rem; color: var(--panel-quiet); margin-bottom: auto; }
.lit__where {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.15em;
  text-transform: uppercase;
  border-top: var(--w-hair) solid var(--panel-quiet); padding-top: 0.8rem;
}
@media (max-width: 58rem) { .lits { grid-template-columns: 1fr; } }

/* --- 21. Les maisons: three houses, one street ------------------------- */
/* Each house is a different shop on the same street, so each brings its own
   enamel — taken from the company's own site, not invented here. These are the
   only colours outside the locked palette, and they belong to companies that
   are not BRIDGECOM. Measured on their own ground:
     Bridgecom    ink 16.03:1  accent 7.88:1
     Algeorithme  ink 15.13:1  accent 5.36:1
     QualyStudio  ink 17.96:1  accent 6.12:1
     Thisplay     ink 18.21:1  accent 5.48:1
   The hover state inverts to ground-on-accent, which is the same ratio. */

/* Bridgecom's own plate: the black enamel of the hero and the bronze of the
   logo's ring. The accent is the logo bronze #EFA66C and NOT the signal orange
   #E2570F — they are different colours doing different jobs, and keeping them
   apart is what lets orange go on meaning « this commits » everywhere else. */
.maison[data-maison="bridgecom"],
.maisons__field[data-maison="bridgecom"],
.maisons__ticks[data-maison="bridgecom"] {
  --m-ground: #141210; --m-ink: #F7F2E6; --m-accent: #EFA66C;
}

.maison[data-maison="algeorithme"],
.maisons__field[data-maison="algeorithme"],
.maisons__ticks[data-maison="algeorithme"] {
  --m-ground: #071A3D; --m-ink: #EAF1FF; --m-accent: #4C8DFF;
}
.maison[data-maison="qualystudio"],
.maisons__field[data-maison="qualystudio"],
.maisons__ticks[data-maison="qualystudio"] {
  --m-ground: #FFFFFF; --m-ink: #0E1726; --m-accent: #5B3DF5;
}
.maison[data-maison="thisplay"],
.maisons__field[data-maison="thisplay"],
.maisons__ticks[data-maison="thisplay"] {
  --m-ground: #050505; --m-ink: #F2F2F2; --m-accent: #FF2D20;
}

.maisons { background: var(--panel); color: var(--panel-ink); }
/* The section head sits on night, so its quiet text takes the dark-ground
   tone — enamel blue on night measures 2.0:1. */
.maisons .lede { color: var(--panel-quiet); }
.maisons__head { padding-block: clamp(4.5rem, 10vw, 9rem) clamp(2rem, 5vw, 3.5rem); }

.maisons__stage {
  position: relative;
  height: 100svh; min-height: 36rem;
  overflow: hidden;
}

.maisons__fields { position: absolute; inset: 0; }
.maisons__field { position: absolute; inset: 0; opacity: 0; background: var(--m-ground); }

.maisons__panels { position: absolute; inset: 0; display: grid; }
.maison {
  grid-area: 1 / 1;
  display: grid; align-content: center;
  color: var(--m-ink);
  opacity: 0;
}
.maison__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}
.maison__fig {
  aspect-ratio: 7 / 5;
  border: var(--w-rule) solid var(--m-accent);
  background: var(--m-ground);
}
/* Sized so the longest house name — Algeorithme — still fits its half of
   the band at every width. A brand name does not get to hyphenate. */
.maison__name { font-size: clamp(1.9rem, 4.6vw, 4.2rem); }
.maison__role {
  margin-top: 0.9rem; padding-top: 0.7rem;
  border-top: var(--w-rule) solid var(--m-accent);
  color: var(--m-accent);
  font-size: 0.78rem; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase;
}
.maison__what { margin-top: 1.1rem; color: var(--m-ink); max-width: 44ch; }
.maison__cta {
  margin-top: 1.75rem;
  background: transparent; color: var(--m-ink);
  border-color: var(--m-accent);
}
@media (hover: hover) and (pointer: fine) {
  .maison__cta:hover { background: var(--m-accent); color: var(--m-ground); }
}

/* ---- What makes each shopfront that company's -------------------------
   Taken from their own sites: Algeorithme's blue glow on navy, QualyStudio's
   white page and solid pill, Thisplay's black page, red hairline and the
   graph-paper grid it works on. */

/* Bridgecom — the sign from the top of the page, brought down. Same brushed
   metal in the name, same drilled plate, same square plaque. Nothing borrowed
   from anyone else, because this is the house the page belongs to. */
.maison[data-maison="bridgecom"] .maison__fig { position: relative; }
.maison[data-maison="bridgecom"] .maison__fig .screws { display: block; z-index: 2; }
.maison[data-maison="bridgecom"] .maison__name {
  background-image: var(--metal);
  background-size: 100% 190%;
  background-position: 50% 26%;
  background-repeat: no-repeat;
  text-shadow: 0 0 clamp(5px, 0.8vw, 14px) rgba(247, 242, 230, 0.16);
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .maison[data-maison="bridgecom"] .maison__name {
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

/* ---- Each house mounts its plate its own way --------------------------
   Bridgecom's is bolted to the wall with four drilled screws. The other three
   are fitted the way their own trade fits things, so the plate says whose it is
   before you read the name. All three are drawn in CSS on the figure itself —
   no second image, and they survive whatever photograph replaces the logo. */

/* Algeorithme — a bounding box, the way a technical drawing frames a part. */
.maison[data-maison="algeorithme"] .maison__fig::after {
  content: ""; position: absolute; inset: clamp(10px, 1.4vw, 18px);
  z-index: 2; pointer-events: none;
  --arm: clamp(14px, 2vw, 26px);
  background:
    linear-gradient(var(--m-accent) 0 0) 0 0 / var(--arm) 2px no-repeat,
    linear-gradient(var(--m-accent) 0 0) 0 0 / 2px var(--arm) no-repeat,
    linear-gradient(var(--m-accent) 0 0) 100% 0 / var(--arm) 2px no-repeat,
    linear-gradient(var(--m-accent) 0 0) 100% 0 / 2px var(--arm) no-repeat,
    linear-gradient(var(--m-accent) 0 0) 0 100% / var(--arm) 2px no-repeat,
    linear-gradient(var(--m-accent) 0 0) 0 100% / 2px var(--arm) no-repeat,
    linear-gradient(var(--m-accent) 0 0) 100% 100% / var(--arm) 2px no-repeat,
    linear-gradient(var(--m-accent) 0 0) 100% 100% / 2px var(--arm) no-repeat;
}

/* QualyStudio — their own four-pointed star, set once in the corner. */
.maison[data-maison="qualystudio"] .maison__fig::after {
  content: ""; position: absolute; z-index: 2; pointer-events: none;
  top: clamp(14px, 1.8vw, 24px); left: clamp(14px, 1.8vw, 24px);
  width: clamp(17px, 2.2vw, 28px); aspect-ratio: 1;
  background: var(--m-accent);
  clip-path: polygon(50% 0%, 59% 41%, 100% 50%, 59% 59%,
                     50% 100%, 41% 59%, 0% 50%, 41% 41%);
}

/* Thisplay — the recording lamp: the one thing that is lit while you shoot. */
.maison[data-maison="thisplay"] .maison__fig::after {
  content: ""; position: absolute; z-index: 2; pointer-events: none;
  top: clamp(14px, 1.8vw, 24px); left: clamp(14px, 1.8vw, 24px);
  width: clamp(24px, 3vw, 34px); aspect-ratio: 1;
  border: var(--w-hair) solid var(--m-accent); border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    var(--m-accent) 0 24%, transparent 24%);
}

/* Algeorithme — the hero glow, low and wide behind the panel. */
.maison[data-maison="algeorithme"]::before,
.maisons__field[data-maison="algeorithme"]::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 60% 55% at 72% 18%,
    rgba(76, 141, 255, 0.26) 0%, rgba(76, 141, 255, 0.08) 45%, transparent 72%);
}
.maison[data-maison="algeorithme"] { position: relative; }
.maison[data-maison="algeorithme"] .maison__grid { position: relative; z-index: 1; }

/* QualyStudio — a white page, a solid pill, softened corners. */
.maison[data-maison="qualystudio"] .maison__fig {
  /* Their site frames nothing: the photograph has to carry its own edge.
     Noted in REPLACE.md so the image supplied for this slot is chosen for it. */
  border: 0; border-radius: 0.9rem; overflow: hidden;
}
.maison[data-maison="qualystudio"] .maison__cta {
  border-radius: 999px; border-color: var(--m-ink);
  background: var(--m-ink); color: var(--m-ground);
}
@media (hover: hover) and (pointer: fine) {
  .maison[data-maison="qualystudio"] .maison__cta:hover {
    background: var(--m-accent); border-color: var(--m-accent); color: #FFFFFF;
  }
}

/* Thisplay — the graph paper their whole site is drawn on. */
.maisons__field[data-maison="thisplay"] {
  background-image:
    repeating-linear-gradient(to right, rgba(255, 45, 32, 0.13) 0 1px, transparent 1px 72px),
    repeating-linear-gradient(to bottom, rgba(255, 45, 32, 0.13) 0 1px, transparent 1px 72px);
}
.maison[data-maison="thisplay"] .maison__role,
.maison[data-maison="thisplay"] .maison__what {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.maison[data-maison="thisplay"] .maison__role { letter-spacing: 0.1em; }
.maison[data-maison="thisplay"] .maison__what { font-size: 0.86rem; line-height: 1.75; }
.maison[data-maison="thisplay"] .maison__fig { border-width: var(--w-hair); }

/* Where you are in the run of three. */
.maisons__ticks {
  position: absolute; left: 50%; bottom: clamp(1.25rem, 3vw, 2.5rem);
  transform: translateX(-50%);
  display: flex; gap: 0.6rem; z-index: 2;
}
.maisons__ticks li {
  width: 2.25rem; height: var(--w-rule);
  background: var(--m-ink, currentColor); opacity: 0.3;
  transition: opacity 250ms var(--ease-out), background-color 400ms var(--ease-out);
}
.maisons__ticks li.on { opacity: 1; }

@media (max-width: 54rem) {
  .maison__grid { grid-template-columns: 1fr; gap: 1.25rem; }
  .maison__fig { aspect-ratio: 16 / 10; }
  .maison__what { font-size: 0.92rem; }
}

/* Without the pin there is no crossfade, so the houses simply stack. */
@media (prefers-reduced-motion: reduce) {
  .maisons__stage { height: auto; min-height: 0; }
  .maisons__fields, .maisons__ticks { display: none; }
  /* The panels are absolutely stacked for the pin; without it they have to
     return to the flow or the stage collapses and all three disappear. */
  .maisons__panels { position: static; display: block; }
  .maison {
    position: static; opacity: 1;
    background: var(--m-ground);
    padding-block: clamp(2.5rem, 6vw, 4.5rem);
  }
}

/* --- 22. The door ------------------------------------------------------ */

.door { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); border: var(--w-rule) solid var(--rule); }
.door__info { padding: clamp(1.75rem, 4vw, 3rem); background: var(--panel); color: var(--panel-ink); }
.door__name { font-size: 1.4rem; }
.door__form { padding: clamp(1.75rem, 4vw, 3rem); background: var(--ground); }
.contact-list { display: grid; gap: 1.5rem; margin-top: 2rem; }
/* Direct children only. Unscoped, `.contact-list div` also caught the inner
   <div> that wraps each <dt>/<dd> pair and laid the label out beside its value
   instead of above it — four rows whose values all started at a different x,
   because the four labels are four different widths. */
.contact-list > a, .contact-list > div { display: flex; gap: 0.9rem; align-items: flex-start; text-decoration: none; }
/* A flex item will not shrink below its min-content width unless told to,
   and commercial@bridgecom.dz has no break opportunity — at 320 the row
   ran straight off the panel. */
.contact-list a > *, .contact-list > div > * { min-width: 0; }
.contact-list dd { overflow-wrap: anywhere; }
.contact-list svg { width: 20px; height: 20px; flex: none; margin-top: 2px; stroke: var(--panel-quiet); }
.contact-list a svg { stroke: var(--accent); }
.contact-list dt {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--panel-quiet); margin-bottom: 0.15rem;
}
.contact-list dd { font-size: 1rem; margin: 0; }
@media (hover: hover) and (pointer: fine) { .contact-list a:hover dd { color: var(--accent); } }

.field { display: grid; gap: 0.45rem; margin-bottom: 1.1rem; }
.field label { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-quiet); }
.field input, .field textarea {
  background: var(--ground-shade);
  border: var(--w-rule) solid var(--edge);
  padding: 0.85rem 1rem; min-height: 48px; font-size: 1rem;
  transition: border-color 200ms var(--ease-out), background-color 200ms var(--ease-out);
}
.field textarea { min-height: 7.5rem; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: var(--ink-quiet); }
.field input:hover, .field textarea:hover { background: var(--ground); }
.field :user-invalid { border-color: var(--accent); }
.field .err { font-size: 0.78rem; color: var(--accent); font-weight: 600; display: none; }
.field :user-invalid ~ .err { display: block; }
.form-note { font-size: 0.8rem; color: var(--ink-quiet); margin-top: 1rem; }

/* minmax(0, 1fr), not 1fr: a plain fr keeps an auto minimum, so one wide
   child pushes the column past the viewport. */
@media (max-width: 56rem) { .door { grid-template-columns: minmax(0, 1fr); } }

/* --- 23. Foot ---------------------------------------------------------- */

.foot { background: var(--panel); color: var(--panel-quiet); padding-block: clamp(2.5rem, 5vw, 4rem); border-top: var(--w-band) solid var(--steel); }
.foot__grid { display: flex; flex-wrap: wrap; gap: 1.5rem 2.5rem; align-items: center; justify-content: space-between; }
.foot a { text-decoration: none; }
.foot a:hover { color: var(--panel-ink); }
.foot-nav { display: flex; flex-wrap: wrap; gap: 0.35rem 1.4rem; }
.foot-nav a {
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  padding-block: 0.35rem; border-bottom: var(--w-hair) solid transparent;
}
.foot-nav a:hover { border-bottom-color: var(--accent); }
.socials { display: flex; gap: 0.6rem; }
.socials a {
  width: 44px; height: 44px; display: grid; place-items: center;
  border: var(--w-hair) solid var(--panel-quiet);
  transition: background-color 200ms var(--ease-out), border-color 200ms var(--ease-out), transform 160ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .socials a:not(.is-unset):hover { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
}
.socials a:not(.is-unset):active { transform: scale(0.95); }
.socials svg { width: 18px; height: 18px; }
.legal { font-size: 0.78rem; }

/* --- 24. Motion base states -------------------------------------------- */
/* Applied only once JS has taken charge of removing them, so nothing can be
   left hidden by timing or by a script that never ran. */

.js-motion .r-up { opacity: 0; transform: translateY(14px); }
.js-motion .r-fig img { transform: scale(1.18); }
.js-motion .r-plate { opacity: 0; transform: translateY(26px); }

@media (prefers-reduced-motion: reduce) {
  .js-motion .r-up,
  .js-motion .r-plate { opacity: 1; transform: none; }
  .js-motion .r-fig img { transform: none; }
}

/* SplitText masks each line so type rises out of its own edge. */
.split-mask { overflow: hidden; display: block; }

/* --- 25. Utility ------------------------------------------------------- */

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-ink);
  padding: 0.9rem 1.4rem; font-weight: 700;
}
.skip:focus { left: 0; }

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

@media print {
  .awning { display: none; }
  body { background: #fff; color: #000; }
}

/* --- 26. Quatre temps: the vertical fallback ---------------------------- */
/* The horizontal run only exists where there is room for it and where
   motion is welcome. Everywhere else the same panels simply stack. */

@media (max-width: 48rem) {
  .temps__viewport { height: auto; min-height: 0; padding-block: 2rem 3rem; }
  .temps__track { flex-direction: column; gap: var(--w-rule); }
  .etape { width: 100%; }
  .etape__fig { aspect-ratio: 16 / 10; }

  /* The portrait plate is cropped to a landscape window here, and cover takes
     the middle band by default — which is not where the subject sits in all
     four. Each one names its own focus so nothing important is cropped away.
     No effect above this width: at 9/11 the image is not cropped at all. */
  .etape:nth-child(1) .etape__fig img { object-position: 50% 64%; }
  .etape:nth-child(2) .etape__fig img { object-position: 50% 40%; }
  .etape:nth-child(3) .etape__fig img { object-position: 50% 87%; }
  .etape:nth-child(4) .etape__fig img { object-position: 50% 34%; }
}

@media (prefers-reduced-motion: reduce) {
  .temps__viewport { height: auto; min-height: 0; padding-block: 2rem 3rem; }
  .temps__track { flex-direction: column; gap: var(--w-rule); }
  .etape { width: 100%; }
  .atelier__stage { height: auto; min-height: 30rem; padding-block: clamp(4rem, 10vw, 8rem); }
  .atelier__fig { position: absolute; }
}
