/* Vitrine — landing template for local shops.
   Mobile-first. No build step. Every colour comes from a token so a site can be
   re-skinned from its JSON without touching this file. */

:root {
  --primary: #1E4D4F;
  --primary-ink: #FFFFFF;
  --accent: #3F6450;
  --bg: #F5F1E8;
  --surface: #FFFCF6;
  --fg: #16292A;
  --fg-soft: #4A5A56;
  --border: #DDD6C8;
  /* Open / closed, sent / failed, and a field in error. Theme tokens since
     2026-09-23: as literals they could not follow a dark palette, and the
     contrast check in render.py could not measure them. */
  --ok: #2E6B3F;
  --bad: #A3322A;

  --radius: 14px;
  --radius-sm: 9px;
  --btn-radius: 999px;
  --shadow-1: 0 1px 2px rgba(17, 12, 10, .05), 0 1px 3px rgba(17, 12, 10, .04);
  --shadow-2: 0 10px 30px -12px rgba(17, 12, 10, .22);
  --card-shadow: none;

  /* Skins override these. Every value here is what the original single
     template used, so the classique profile needs no skin file at all. */
  --h1: clamp(2.5rem, 11vw, 4.75rem);
  --h1-lh: 1.08;
  --h2: clamp(1.75rem, 6.5vw, 2.6rem);
  --display-weight: 400;
  --display-tracking: -0.015em;
  --body-size: 17px;
  --body-lh: 1.65;
  --sec-pad: 48px;
  --sec-pad-wide: 48px;
  --eyebrow-tracking: .12em;

  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px; --s9: 88px;

  --maxw: 1080px;
  --font-display: "Vitrine Display", Georgia, "Times New Roman", serif;
  --font-body: "Vitrine Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--body-size);
  line-height: var(--body-lh);
  -webkit-font-smoothing: antialiased;
}

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

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  line-height: 1.08;
  letter-spacing: var(--display-tracking);
  margin: 0;
}
/* 1.08 is display leading for a one-line h1. Section titles wrap on a phone,
   and at 1.08 two lines of h2 sat 2px apart (fb-0007, back on h2 at the
   2026-09-23 round check). h3 is small enough to need body-ish leading. */
h2 { line-height: 1.15; }
h3 { line-height: 1.25; }

a { color: inherit; }

.wrap { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 var(--s5); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

:where(a, button, input, textarea, select):focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ---------- preview ribbon (demo pages only) ---------- */

.preview-note {
  background: #1c1917;
  color: #fafaf9;
  font-size: 14px;
  line-height: 1.45;
  padding: var(--s3) var(--s5);
  text-align: center;
}
.preview-note strong { color: #fde68a; font-weight: 600; }

/* ---------- header ---------- */

.site-head {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.site-head .wrap {
  display: flex; align-items: center; gap: var(--s4);
  min-height: 62px;
}
.brand {
  font-family: var(--font-display);
  font-size: 20px;
  /* It inherited body leading, so "Volaillerie Saint-" / "Vincent" wrapped
     with a paragraph's gap between the two halves of one name. */
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-decoration: none;
  margin-right: auto;
}
.nav { display: none; gap: var(--s5); font-size: 15px; }
.nav a { text-decoration: none; color: var(--fg-soft); padding: var(--s2) 0; }
.nav a:hover { color: var(--fg); }
@media (min-width: 860px) { .nav { display: flex; } }

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

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s2);
  min-height: 48px;
  padding: 0 var(--s5);
  border-radius: var(--btn-radius);
  border: 1px solid transparent;
  font: inherit; font-size: 16px; font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: transform .18s ease, background-color .18s ease, box-shadow .18s ease;
}
.btn svg { width: 19px; height: 19px; flex: 0 0 auto; }
.btn:active { transform: scale(.97); }
.btn-primary { background: var(--primary); color: var(--primary-ink); box-shadow: var(--shadow-1); }
.btn-primary:hover { background: color-mix(in srgb, var(--primary) 88%, #000); }
.btn-ghost { background: var(--surface); color: var(--fg); border-color: var(--border); }
.btn-ghost:hover { border-color: color-mix(in srgb, var(--fg) 30%, var(--border)); }
.btn-sm { min-height: 42px; font-size: 15px; padding: 0 var(--s4); }
@media (prefers-reduced-motion: reduce) { .btn { transition: none; } .btn:active { transform: none; } }

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

.hero { padding: var(--s7) 0 var(--s6); }
.hero-grid { display: grid; gap: var(--s6); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 980px) {
  .hero { padding: var(--s8) 0 var(--s7); }
  .hero-grid { grid-template-columns: minmax(0, 1.55fr) minmax(280px, .95fr); gap: var(--s7); }
}

/* The card repeats what the hours section says further down. On a phone that
   would just be a second scroll of the same thing, so it only appears once
   there is a column standing empty next to the headline. */
.hero-card { display: none; }
@media (min-width: 980px) {
  .hero-card {
    display: block;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--s5);
    box-shadow: var(--shadow-2);
  }
  .hero-card-row { display: flex; gap: var(--s4); margin: 0 0 var(--s4); font-size: 16px; }
  .hero-card-row .k {
    flex: 0 0 92px;
    font-size: 12px; letter-spacing: .09em; text-transform: uppercase;
    font-weight: 600; color: var(--accent); padding-top: 4px;
  }
  .hero-card-row .v { color: var(--fg); font-variant-numeric: tabular-nums; }
  .hero-card-row .v a { text-decoration: none; font-weight: 600; }
  .hero-card .btn { width: 100%; }
}
.hero .eyebrow {
  font-size: 13px; font-weight: 600; letter-spacing: var(--eyebrow-tracking); text-transform: uppercase;
  color: var(--accent); margin: 0 0 var(--s4);
}
.hero h1 {
  font-size: var(--h1); line-height: var(--h1-lh);
  margin-bottom: var(--s4);
}
.hero .lede {
  font-size: clamp(1.06rem, 4vw, 1.3rem);
  color: var(--fg-soft);
  max-width: 34ch;
  margin: 0 0 var(--s5);
}
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--s3); }
.hero-actions .btn { flex: 1 1 auto; }
@media (min-width: 640px) { .hero-actions .btn { flex: 0 0 auto; } }

.status-line {
  display: inline-flex; align-items: center; gap: var(--s2);
  margin-top: var(--s5);
  font-size: 15px; font-weight: 600;
}
.status-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--fg-soft); flex: 0 0 auto; }
.status-open   .status-dot { background: var(--ok); }
.status-closed .status-dot { background: var(--bad); }
.status-open   { color: var(--ok); }
.status-closed { color: var(--bad); }
.status-line .status-detail { color: var(--fg-soft); font-weight: 400; }

/* ---------- sections ---------- */

section { padding: var(--sec-pad) 0; }
section + section { border-top: 1px solid var(--border); }
.section-title { font-size: var(--h2); margin-bottom: var(--s5); }
.section-intro { color: var(--fg-soft); max-width: 60ch; margin: calc(var(--s5) * -1 + var(--s3)) 0 var(--s6); }

/* ---------- services ---------- */

.cards { display: grid; gap: var(--s4); grid-template-columns: 1fr; }
@media (min-width: 700px) { .cards { grid-template-columns: repeat(2, 1fr); gap: var(--s5); } }
@media (min-width: 980px) { .cards.cards-3 { grid-template-columns: repeat(3, 1fr); } }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--s5);
}
.card .icon {
  width: 42px; height: 42px; border-radius: 11px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--accent) 14%, var(--surface));
  color: var(--accent);
  margin-bottom: var(--s4);
}
.card .icon svg { width: 22px; height: 22px; }
.card h3 { font-size: 1.25rem; margin-bottom: var(--s2); }
.card p { margin: 0; color: var(--fg-soft); font-size: 16px; }

/* ---------- hours ---------- */

.hours-layout { display: grid; gap: var(--s6); grid-template-columns: 1fr; }
@media (min-width: 880px) { .hours-layout { grid-template-columns: 1fr 1fr; gap: var(--s7); align-items: start; } }

.hours { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.hours th, .hours td { text-align: left; padding: var(--s3) 0; border-bottom: 1px solid var(--border); }
.hours th { font-weight: 500; color: var(--fg-soft); }
.hours td { text-align: right; font-weight: 500; }
.hours tr.today th, .hours tr.today td { color: var(--fg); font-weight: 700; }
.hours tr:last-child th, .hours tr:last-child td { border-bottom: 0; }
.hours-note { font-size: 15px; color: var(--fg-soft); margin-top: var(--s4); }

.map-frame {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}
.map-frame iframe { width: 100%; height: 300px; border: 0; display: block; }
.addr { font-style: normal; margin: 0 0 var(--s4); font-size: 17px; line-height: 1.6; }
.addr .label { display: block; font-size: 13px; letter-spacing: .1em; text-transform: uppercase; color: var(--accent); font-weight: 600; margin-bottom: var(--s2); }

/* ---------- photos ---------- */

.gallery { display: grid; gap: var(--s3); grid-template-columns: repeat(2, 1fr); }
@media (min-width: 760px) { .gallery { grid-template-columns: repeat(3, 1fr); gap: var(--s4); } }
.gallery figure { margin: 0; border-radius: var(--radius); overflow: hidden; background: var(--surface); }
.gallery img { aspect-ratio: 4 / 3; object-fit: cover; width: 100%; }
/* Two up on a phone, so an odd count left the last frame alone on its row
   with a hole beside it: three of the four gallery pages at 390 (2026-09-23).
   The last one takes the whole row instead, at half the height a doubled 4/3
   would give it. CSS rather than a count rule, because the count of a
   customer's photos is theirs (see build_portfolio). */
@media (max-width: 759px) {
  .gallery > :last-child:nth-child(odd) { grid-column: span 2; }
  .gallery > .photo-slot:last-child:nth-child(odd),
  .gallery > figure:last-child:nth-child(odd) img { aspect-ratio: 8 / 3; }
}

/* ⚠ `align-content` as well as `place-items`. The slot has TWO children (the
   camera and its label) and a grid's implicit rows STRETCH by default, so
   without this the two rows split the box in half: the icon floats in the top
   half and the caption in the bottom, with a void between them that reads as a
   broken image rather than an empty frame. `.planche-slot` had it from the
   start, which is why the portfolio slots never looked wrong. */
.photo-slot {
  aspect-ratio: 4 / 3;
  border: 1px dashed color-mix(in srgb, var(--fg) 28%, var(--border));
  border-radius: var(--radius);
  display: grid; place-items: center; align-content: center; text-align: center;
  padding: var(--s4);
  color: var(--fg-soft);
  font-size: 14px;
  background: color-mix(in srgb, var(--surface) 70%, var(--bg));
}
.photo-slot svg { width: 26px; height: 26px; margin: 0 auto var(--s2); display: block; opacity: .55; }

/* ---------- reviews ---------- */

.quotes { display: grid; gap: var(--s4); grid-template-columns: 1fr; }
@media (min-width: 820px) { .quotes { grid-template-columns: repeat(2, 1fr); gap: var(--s5); } }
.quote {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--s5);
}
.quote blockquote { margin: 0 0 var(--s4); font-size: 17px; }
.quote figcaption { font-size: 14px; color: var(--fg-soft); }
.stars { display: flex; gap: 2px; color: var(--accent); margin-bottom: var(--s3); }
.stars svg { width: 17px; height: 17px; }

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

.contact-layout { display: grid; gap: var(--s6); grid-template-columns: 1fr; }
@media (min-width: 880px) { .contact-layout { grid-template-columns: 5fr 6fr; gap: var(--s7); align-items: start; } }

.field { margin-bottom: var(--s4); }
.field label { display: block; font-size: 15px; font-weight: 600; margin-bottom: var(--s2); }
.field .hint { display: block; font-weight: 400; color: var(--fg-soft); font-size: 14px; margin-top: 2px; }
.field input, .field textarea {
  width: 100%;
  min-height: 48px;
  padding: var(--s3) var(--s4);
  font: inherit; font-size: 16px;
  color: var(--fg);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.field textarea { min-height: 140px; resize: vertical; }
.field input:focus, .field textarea:focus { border-color: var(--accent); }
.field .err { display: none; color: var(--bad); font-size: 14px; margin-top: var(--s2); font-weight: 500; }
.field.invalid .err { display: block; }
.field.invalid input, .field.invalid textarea { border-color: var(--bad); }

.hp { position: absolute; left: -9999px; opacity: 0; height: 0; }

.form-note { font-size: 14px; color: var(--fg-soft); margin: var(--s4) 0 0; }
.form-status { margin: var(--s4) 0 0; font-size: 16px; font-weight: 600; }
.form-status.ok  { color: var(--ok); }
.form-status.bad { color: var(--bad); }

/* ---------- footer ---------- */

.site-foot {
  border-top: 1px solid var(--border);
  padding: var(--s6) 0 calc(var(--s8) + env(safe-area-inset-bottom));
  font-size: 15px;
  color: var(--fg-soft);
}
.foot-grid { display: grid; gap: var(--s5); grid-template-columns: 1fr; }
@media (min-width: 760px) { .foot-grid { grid-template-columns: repeat(3, 1fr); } }
.site-foot h4 { font-family: var(--font-body); font-size: 14px; letter-spacing: .1em; text-transform: uppercase; color: var(--fg); margin: 0 0 var(--s3); }
.site-foot a { text-decoration: none; }
.site-foot a:hover { text-decoration: underline; }
/* The one footer link that goes somewhere on the page, so it has to look like
   a link. After `.site-foot a`, which it has to beat. */
.foot-hours a { text-decoration: underline; text-underline-offset: 3px; }
.colophon { margin-top: var(--s6); padding-top: var(--s4); border-top: 1px solid var(--border); font-size: 13px; }

/* ---------- sticky mobile action bar ---------- */

.action-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: flex; gap: var(--s2);
  padding: var(--s3) var(--s4) calc(var(--s3) + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--bg) 94%, transparent);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--border);
}
.action-bar .btn { flex: 1 1 0; min-width: 0; }
.action-bar .btn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (min-width: 860px) { .action-bar { display: none; } .site-foot { padding-bottom: var(--s7); } }

/* ---------- entrance motion (subtle, opt-out) ---------- */

@media (prefers-reduced-motion: no-preference) {
  .reveal { opacity: 0; transform: translateY(10px); transition: opacity .45s ease-out, transform .45s ease-out; }
  .reveal.in { opacity: 1; transform: none; }
}

/* ==========================================================================
   Section library. Everything below is shared by every skin; a skin file
   changes the variables above and adds its own one structural device.
   ========================================================================== */

@media (min-width: 900px) { section { padding: var(--sec-pad-wide) 0; } }

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

.hero-band { margin-top: var(--s6); }
.hero-band img { width: 100%; aspect-ratio: 21 / 9; max-height: 460px; object-fit: cover; }
/* The empty state is deliberately shorter than a real photo would be. At 21/9
   across a desktop viewport an empty dashed box reads as a broken image the
   size of a billboard.
   ⚠ `width: 100%` is what makes the height cap a cap rather than a WIDTH.
   Without it the box is sized from `max-height` back through the aspect ratio
   -- 300px tall means 686px wide -- so it sat left-aligned against the viewport
   edge, half the width of the band a real photo fills, which is exactly what
   made it read as broken. `align-content` for the same reason as `.photo-slot`
   above. */
.hero-slot {
  aspect-ratio: 16 / 7; max-height: 300px; width: 100%;
  display: grid; place-items: center; align-content: center; text-align: center; gap: var(--s2);
  border: 1px dashed color-mix(in srgb, var(--fg) 26%, var(--border));
  background: color-mix(in srgb, var(--surface) 70%, var(--bg));
  color: var(--fg-soft); font-size: 14px; padding: var(--s4);
}
.hero-slot svg { width: 28px; height: 28px; opacity: .55; }

/* An empty band is held to the page measure, and to a placeholder's height.
   Full-bleed is the media hero's device and a photograph should keep it, but an
   empty frame taking the same room is a 1425x300 dashed void running edge to
   edge past every other block on the page -- the one thing with no side gutters,
   which is exactly what makes it read as a broken image rather than as a frame
   waiting for a picture. Shrinking it is the same call base.css already made
   with max-height, carried the rest of the way.
   ⚠ `aspect-ratio: auto` and `max-height: none` both have to go, or the ratio
   fights min-height and the box springs back to 300px. */
.hero-band-empty { padding: 0 var(--s5); }
.hero-band-empty .hero-slot {
  aspect-ratio: auto; max-height: none; min-height: 148px;
  /* Minus the gutters, not just `--maxw`. `.wrap` puts its padding INSIDE its
     max-width, so a 1080 box centred in the band lines up with the wrap's border
     edge and comes out 24px wider on each side than the `.photo-slot` frames in
     the gallery. Two dashed placeholders on one page at two different widths is
     worse than either width on its own. */
  max-width: calc(var(--maxw) - var(--s5) * 2); margin: 0 auto;
  border-radius: var(--radius);
}

.hero-typographic .hero-rule {
  display: block; width: 64px; height: 1px;
  background: color-mix(in srgb, var(--fg) 45%, transparent);
  margin: var(--s5) 0;
}
.hero-editorial .since { color: var(--fg-soft); font-weight: 500; }
.hero-editorial .status-inline { margin-top: var(--s4); }
.hero-today {
  margin: var(--s2) 0 var(--s5);
  color: var(--fg-soft); font-size: 15px;
  font-variant-numeric: tabular-nums;
}
.hero-today strong { color: var(--fg); }

/* ---------- services price line ---------- */

.card-price {
  margin: var(--s3) 0 0;
  font-family: var(--font-display);
  font-size: 18px; font-weight: 600; color: var(--fg);
  font-variant-numeric: tabular-nums;
}

/* ---------- menu (la carte) ---------- */

.menu { display: grid; gap: var(--s6); grid-template-columns: 1fr; }
@media (min-width: 820px) { .menu { grid-template-columns: repeat(2, 1fr); gap: var(--s6) var(--s7); } }
/* A single group in a two-column grid leaves half the page empty and stretches
   the price leaders across it. One group gets one centred column. */
.menu-1 { grid-template-columns: 1fr; max-width: 620px; margin-inline: auto; }
@media (min-width: 820px) { .menu-1 { grid-template-columns: 1fr; } }
.menu-group h3 {
  font-size: 1.15rem; margin-bottom: var(--s3);
  padding-bottom: var(--s2); border-bottom: 1px solid var(--border);
}
.menu-list, .products, .brands { list-style: none; margin: 0; padding: 0; }
.menu-item { padding: var(--s3) 0; }
.menu-item, .p-head {
  display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap;
}
.mi-name, .p-name { font-weight: 600; }
.mi-leader {
  flex: 1 1 24px; min-width: 24px; align-self: center;
  border-bottom: 2px dotted var(--border);
}
.mi-price {
  font-family: var(--font-display); font-weight: 600;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.mi-desc, .p-desc {
  flex: 1 1 100%; margin: var(--s1) 0 0;
  color: var(--fg-soft); font-size: 15px;
}

/* ---------- products (spécialités) ---------- */

.product { padding: var(--s4) 0; border-bottom: 1px solid var(--border); }
.product:last-child { border-bottom: 0; }
.p-badge {
  font-size: 12px; font-weight: 600; letter-spacing: .04em;
  padding: 3px var(--s2); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 15%, var(--surface));
  color: var(--accent); white-space: nowrap;
}

/* ---------- steps (le parcours) ---------- */

.steps { list-style: none; margin: 0; padding: 0; counter-reset: none; }
.step { display: flex; gap: var(--s4); padding-bottom: var(--s5); position: relative; }
.step:last-child { padding-bottom: 0; }
.step-n {
  /* Width and height are both explicit: on desktop the list turns into a
     column flex and a flex-basis would become the HEIGHT, which stretched the
     numerals into ellipses. */
  flex: 0 0 auto; width: 40px; height: 40px;
  display: grid; place-items: center;
  border: 2px solid var(--accent); border-radius: 50%;
  color: var(--accent); font-weight: 700; font-size: 15px;
  background: var(--bg);
  font-variant-numeric: tabular-nums;
}
/* The connector runs behind the numerals and stops at the last one. */
.step:not(:last-child)::before {
  content: ""; position: absolute; left: 19px; top: 40px; bottom: 8px;
  width: 2px; background: color-mix(in srgb, var(--accent) 35%, var(--border));
}
/* Headings inherit the display leading, 1.08, which is right for an h1 and much
   too tight once a 17px h3 wraps: on a phone every one of these titles wraps,
   and the two lines all but touch. Both sections are absent from `classique`,
   so the pixel contract does not see this. */
.step-b h3 { font-size: 1.15rem; line-height: 1.25; margin-bottom: var(--s2); }
.step-b p { margin: 0; color: var(--fg-soft); font-size: 16px; }
@media (min-width: 900px) {
  .steps { display: flex; gap: var(--s5); }
  .step { flex: 1 1 0; flex-direction: column; padding-bottom: 0; }
  .step:not(:last-child)::before {
    left: 48px; right: -24px; top: 19px; bottom: auto; width: auto; height: 2px;
  }
}

/* ---------- portfolio (la planche) ---------- */

.s-portfolio { overflow: hidden; }
.planche {
  display: grid; gap: 2px; margin-top: var(--s5);
  grid-template-columns: repeat(2, 1fr);
  grid-auto-flow: dense;
}
@media (min-width: 760px) { .planche { grid-template-columns: repeat(4, 1fr); } }
.planche-tile { margin: 0; position: relative; overflow: hidden; }
.planche-tile img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; }
.planche-tile:first-child { grid-column: span 2; grid-row: span 2; }
.planche-tile figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: var(--s3) var(--s4);
  font-size: 13px; color: #fff;
  background: linear-gradient(to top, rgba(0, 0, 0, .62), transparent);
}
.planche-slot {
  aspect-ratio: 4 / 5;
  display: grid; place-items: center; align-content: center; gap: var(--s2);
  text-align: center; padding: var(--s4);
  border: 1px dashed color-mix(in srgb, var(--fg) 26%, var(--border));
  background: color-mix(in srgb, var(--surface) 70%, var(--bg));
  color: var(--fg-soft); font-size: 13px;
}
.planche-slot:first-child { grid-column: span 2; grid-row: span 2; }
.planche-slot svg { width: 26px; height: 26px; opacity: .5; }
/* A plate with no photograph in it at all (build_portfolio adds the class).
   Held to the page measure and square tiles, the same call .hero-band-empty
   makes: full-bleed empty frames on ylann were ~1100px of dashed void on a
   phone (2026-09-23). The box matches .wrap: max-width with the padding
   inside it. The 2x2 tile's cap is phone-only: from 760px it shares its two
   rows with tiles 2 to 5, and a cap there would leave it short of them. */
.planche-empty { max-width: var(--maxw); margin-inline: auto; padding: 0 var(--s5); }
.planche-empty .planche-slot { aspect-ratio: 1 / 1; }
@media (max-width: 759px) {
  .planche-empty .planche-slot:first-child { aspect-ratio: auto; height: 260px; }
}

/* ---------- trust (garanties) ---------- */

.trust { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s5); }
@media (min-width: 820px) { .trust { grid-template-columns: repeat(2, 1fr); gap: var(--s5) var(--s7); } }
.trust-row { display: flex; gap: var(--s4); align-items: flex-start; }
.t-icon {
  flex: 0 0 40px; height: 40px; border-radius: var(--radius-sm);
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--accent) 13%, var(--surface));
  color: var(--accent);
}
.t-icon svg { width: 21px; height: 21px; }
.trust-row h3 { font-size: 1.1rem; line-height: 1.25; margin-bottom: var(--s2); }
.trust-row p { margin: 0; color: var(--fg-soft); font-size: 16px; }

/* ---------- faq ---------- */

.faq { max-width: 68ch; }
.faq-item { border-bottom: 1px solid var(--border); }
.faq-item summary {
  cursor: pointer; list-style: none;
  padding: var(--s4) 0; padding-right: var(--s6);
  font-weight: 600; position: relative;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-mark {
  position: absolute; right: 4px; top: 50%; width: 12px; height: 2px;
  background: var(--accent); transform: translateY(-50%);
}
.faq-mark::after {
  content: ""; position: absolute; inset: 0;
  background: var(--accent); transform: rotate(90deg);
  transition: opacity .18s ease;
}
.faq-item[open] .faq-mark::after { opacity: 0; }
.faq-a { padding: 0 0 var(--s4); color: var(--fg-soft); }
@media (prefers-reduced-motion: reduce) { .faq-mark::after { transition: none; } }

/* ---------- brands ---------- */

.brands { display: flex; flex-wrap: wrap; gap: var(--s2); }
.brands li {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: var(--s2) var(--s4); font-size: 15px; font-weight: 500;
  background: var(--surface);
}

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

.cta-band {
  background: var(--primary);
  color: var(--primary-ink);
  text-align: center;
  border-top: 0;
}
.cta-band h2 { font-size: var(--h2); margin-bottom: var(--s3); }
.cta-band p { margin: 0 auto var(--s5); max-width: 44ch; opacity: .88; }
.btn-oncta {
  background: var(--primary-ink); color: var(--primary);
  border-color: var(--primary-ink);
}
.btn-oncta:hover { background: color-mix(in srgb, var(--primary-ink) 88%, var(--primary)); }
/* `.cta-band .cta-num`, not `.cta-num`: `.cta-band p` above outranks a single
   class, so its bottom margin and .88 opacity won and the number sat ~8px
   under the button (2026-09-23). */
.cta-band .cta-num {
  margin: var(--s4) auto 0; font-family: var(--font-display);
  font-size: 1.4rem; font-variant-numeric: tabular-nums; opacity: 1;
}

/* ---------- signature hero (profiles.SIGNATURES) ----------
   The headline column and the signature's composition side by side from
   900px, stacked below it. The composition is the signature's to draw
   (template/signatures/<name>/); this only gives it a column. */

.hero-sig-grid { display: grid; gap: var(--s6); grid-template-columns: 1fr; align-items: center; }
@media (min-width: 900px) {
  .hero-sig-grid { grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: var(--s7); }
}
.hero-art { min-width: 0; }
.hero-art .sa-photo { width: 100%; height: auto; display: block; }

/* The band's second button (cta_band.link): same size, drawn in outline so
   the call stays the first choice. */
.btn-oncta-alt {
  /* The band's own text colour, whatever a signature painted the band:
     primary-ink on the default band, dark ink on a light card. */
  background: transparent; color: inherit;
  border-color: color-mix(in srgb, currentColor 60%, transparent);
  margin-left: var(--s2);
}
.btn-oncta-alt:hover { background: color-mix(in srgb, currentColor 10%, transparent); }

/* Officine — the appointment-led skin for trades with nothing to photograph.
   Device: the numbered parcours rail, and a full-width booking band. Softer
   corners and a two-layer shadow, because reassurance reads as calm. */

:root {
  --h1: clamp(2rem, 7.5vw, 3.3rem);
  --h1-lh: 1.12;
  --h2: clamp(1.5rem, 5vw, 2.1rem);
  --display-weight: 600;
  --display-tracking: -0.01em;
  --body-size: 17px;
  --body-lh: 1.7;
  --radius: 16px;
  --radius-sm: 12px;
  --btn-radius: 12px;
  --sec-pad: 48px;
  --sec-pad-wide: 64px;
  --shadow-1: 0 1px 2px rgba(18, 37, 46, .05);
  --shadow-2: 0 1px 2px rgba(18, 37, 46, .05), 0 12px 28px -14px rgba(18, 37, 46, .18);
  --card-shadow: 0 1px 2px rgba(18, 37, 46, .05), 0 6px 18px -12px rgba(18, 37, 46, .16);
  --eyebrow-tracking: .1em;
}

.card { box-shadow: var(--card-shadow); padding: var(--s5); }
.field input, .field textarea { border-radius: 12px; }

/* Nothing moves on hover here. A health page that jumps under the cursor reads
   as unserious, and the reveal on scroll is already enough motion. */
.btn:active { transform: none; }

.cta-band { border-radius: 0; }
.cta-num { font-size: 1.6rem; font-weight: 600; }

/* Signature: Ondes. A hearing cabinet for an older, often anxious reader:
   warm paper, deep teal ink, a muted sage for everything that is drawn. The
   one device is the ripple, rings spreading from a point: behind the door
   plate in the hero, around each numeral of the parcours, from the corner of
   the booking band. Large type, generous leading, nothing that moves much.
   Titles are a book face set large and flush left; there is no rule under
   them, the air does that work. */

/* Literata: a wide reading serif with a tall x-height, drawn for long reading
   on screens. Chosen over a condensed display face for an older reader. */
@font-face { font-family: 'Ondes Serif'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('fonts/literata-400.woff2') format('woff2'); }
@font-face { font-family: 'Ondes Serif'; font-style: italic; font-weight: 400; font-display: swap;
  src: url('fonts/literata-400i.woff2') format('woff2'); }
@font-face { font-family: 'Ondes Sans'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('fonts/albert-sans-400.woff2') format('woff2'); }
@font-face { font-family: 'Ondes Sans'; font-style: normal; font-weight: 600; font-display: swap;
  src: url('fonts/albert-sans-600.woff2') format('woff2'); }

:root {
  --font-display: 'Ondes Serif', Georgia, 'Times New Roman', serif;
  --font-body: 'Ondes Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --display-weight: 400;
  --display-tracking: -0.015em;
  --h1: clamp(2.5rem, 10.5vw, 4.4rem);
  --h1-lh: 1.1;
  --h2: clamp(1.95rem, 7.6vw, 2.9rem);
  --body-size: 18px;
  --body-lh: 1.65;
  --radius: 6px;
  --radius-sm: 4px;
  --btn-radius: 8px;
  --sec-pad: 64px;
  --sec-pad-wide: 112px;
  --shadow-1: none;
  --shadow-2: none;
  --card-shadow: none;
  --eyebrow-tracking: .02em;

  /* Drawn lines only, never text: the rings, the trace, the connectors. */
  --sage-line: #9FB7A6;
  --sage-soft: #C9D6CB;
  /* The parcours band. fg_soft on it measures 5.9:1, accent 5.4:1. */
  --sage-panel: #E4EAE1;
}

.preview-note { background: #122526; color: #F7F3EA; }
.preview-note strong { color: #F1DFA8; }

.site-head { background: color-mix(in srgb, var(--bg) 92%, transparent); }
.brand { font-size: 23px; letter-spacing: 0; line-height: 1.15; }
.nav a { color: var(--fg-soft); }

.btn { font-size: 17px; min-height: 52px; }
.btn-primary:hover { background: color-mix(in srgb, var(--primary) 86%, #000); }
.btn-ghost { background: transparent; border-color: color-mix(in srgb, var(--fg) 32%, transparent); }

/* ---- hero ---- */
.hero-sig { padding: var(--s7) 0 var(--s8); }
.hero .eyebrow {
  display: flex; align-items: center; gap: 10px;
  font-size: 16px; font-weight: 600; text-transform: none; color: var(--accent);
  margin-bottom: var(--s5);
}
/* The plate's trace, cut down to the size of a word, before the trade.
   (Quarter rings here read as a feed icon, so the trace it is.) */
.hero .eyebrow::before {
  content: ""; flex: 0 0 auto; width: 30px; height: 14px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 14'%3E%3Cpath d='M1 7H5C7 7 7 3 9 3S11 11 13 11S15 1 17 1S19 13 21 13S23 5 25 7H29' fill='none' stroke='%233F6450' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}
.hero h1 { margin-bottom: var(--s5); text-wrap: balance; }
.hero .lede { font-size: clamp(1.15rem, 4.6vw, 1.35rem); color: var(--fg-soft); max-width: 32ch; margin-bottom: var(--s6); }
.status-line { font-size: 16px; }
@media (min-width: 900px) {
  .hero-sig-grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); gap: var(--s8); align-items: center; }
}

/* The plate: a warm card with an engraved hairline inset from its edge. */
.ondes-plate {
  position: relative; margin: 0; overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: clamp(32px, 8vw, 48px) clamp(22px, 6vw, 40px) clamp(24px, 6vw, 36px);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--fg) 10%, transparent), 0 16px 26px -24px rgba(22, 41, 42, .35);
  color: var(--fg);
}
.ondes-plate::before {
  content: ""; position: absolute; inset: 9px; pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--sage-line) 55%, transparent);
  border-radius: 3px;
}
.ondes-rings {
  position: absolute; top: 0; right: 0; width: 76%; height: auto; max-width: 300px;
  /* The rings open from the corner and settle before the address and hours. */
  -webkit-mask-image: linear-gradient(to bottom, #000 38%, transparent 78%);
          mask-image: linear-gradient(to bottom, #000 38%, transparent 78%);
  color: color-mix(in srgb, var(--sage-soft) 55%, var(--sage-line)); pointer-events: none;
}
.ondes-plate .sa-photo { position: relative; border-radius: var(--radius-sm); margin-bottom: var(--s5); }
.ondes-name { position: relative; display: grid; gap: 2px; font-family: var(--font-display); }
.ondes-name-a { font-size: clamp(2.1rem, 9vw, 2.8rem); line-height: 1.1; color: var(--fg); }
.ondes-name-b { font-size: clamp(1.5rem, 6.4vw, 1.9rem); line-height: 1.15; font-style: italic; color: var(--primary); }
.ondes-trace { position: relative; display: block; width: 100%; height: 34px; margin: var(--s5) 0 var(--s4); color: var(--sage-line); }
.ondes-lines { position: relative; display: grid; gap: 0; }
.ondes-row {
  display: grid; grid-template-columns: 1fr; gap: 4px;
  padding: var(--s4) 0; border-top: 1px solid var(--border);
}
/* Label over value at every width: beside it, the plate's desktop column was
   too narrow and "14h – 18h" broke across two lines. */
.ondes-k {
  font-size: 13px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent); padding-top: 3px;
}
.ondes-v { font-size: 18px; line-height: 1.5; }
.ondes-week { list-style: none; margin: 0; padding: 0; font-variant-numeric: tabular-nums; }
.ondes-week li { display: grid; grid-template-columns: 5.4em 1fr; gap: 0 10px; font-size: 18px; line-height: 1.5; padding: 2px 0; }
.ondes-week .sa-d { font-weight: 600; }
.ondes-week .sa-h { color: var(--fg-soft); }

/* Phone: the plate comes first, cut to the name and its trace, so the first
   screen is the cabinet's door and not the shared skeleton. The address and
   the week leave it: #horaires and #contact give both in full below. */
@media (max-width: 899.98px) {
  .hero-sig { padding-top: var(--s5); }
  .hero-sig-grid { gap: var(--s6); }
  .hero-sig .hero-art { order: -1; }
  .ondes-plate { padding: 26px 22px 20px; }
  .ondes-rings { width: 58%; max-width: 210px; }
  .ondes-name-a { font-size: clamp(1.8rem, 7.8vw, 2.2rem); }
  .ondes-name-b { font-size: clamp(1.3rem, 5.6vw, 1.55rem); }
  .ondes-trace { height: 26px; margin: var(--s3) 0 0; }
  .ondes-lines { display: none; }
  .hero .eyebrow { margin-bottom: var(--s3); }
  .hero h1 { margin-bottom: var(--s4); }
}

/* ---- sections ---- */
section + section { border-top: 0; }
.section-title { text-align: left; font-size: var(--h2); line-height: 1.15; margin-bottom: var(--s6); max-width: 18ch; }
.section-title::after { content: none; }
.section-intro { text-align: left; margin-left: 0; font-size: 18px; }

/* Services: a ruled list, not a grid of boxes. The generator's icons go (one
   of them is an ear): the titles carry it. */
#services .cards { gap: 0; }
@media (min-width: 700px) { #services .cards { gap: 0 var(--s8); } }
#services .card {
  background: transparent; border: 0; border-radius: 0; box-shadow: none;
  border-top: 1px solid color-mix(in srgb, var(--fg) 22%, transparent);
  padding: var(--s5) 0 var(--s6);
}
#services .card .icon { display: none; }
#services .card h3 { font-size: clamp(1.45rem, 5.4vw, 1.7rem); line-height: 1.2; margin-bottom: var(--s2); }
#services .card p { font-size: 17.5px; max-width: 38ch; }
/* The one concrete promise in the list (the guarantee, last in the file)
   leaves the rules: a sage panel across the full width, rings from its corner,
   the title set larger in the italic. */
#services .card:last-child:not(:first-child) {
  grid-column: 1 / -1; margin-top: var(--s5);
  background-color: var(--sage-panel);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 300'%3E%3Cg fill='none' stroke='%239FB7A6'%3E%3Ccircle cx='300' cy='300' r='60' stroke-width='1.6'/%3E%3Ccircle cx='300' cy='300' r='110' stroke-width='1.4'/%3E%3Ccircle cx='300' cy='300' r='165' stroke-width='1.2'/%3E%3Ccircle cx='300' cy='300' r='225' stroke-width='1'/%3E%3C/g%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right bottom; background-size: 220px auto;
  border-top: 0; border-radius: 2px;
  padding: var(--s6) clamp(22px, 5vw, 40px) var(--s6);
}
#services .card:last-child:not(:first-child) h3 {
  font-style: italic; color: var(--primary); font-size: clamp(1.85rem, 7vw, 2.4rem); line-height: 1.15;
}
#services .card:last-child:not(:first-child) p { font-size: 19px; color: var(--fg); max-width: 34ch; }
/* On a phone the rings would run under the words: they close the panel instead. */
@media (max-width: 699px) {
  #services .card:last-child:not(:first-child) { background-size: 112px auto; padding-bottom: 96px; }
}

/* Parcours: a sage band, the title and its intro on the left from 900px, the
   five steps down the right like numbered paragraphs of a letter. Each
   numeral sits in a ripple of two rings. */
#parcours { background: var(--sage-panel); }
#parcours .section-intro { color: var(--fg-soft); }
#parcours .steps { display: block; }
#parcours .step { display: flex; flex-direction: row; gap: var(--s5); padding-bottom: var(--s6); }
#parcours .step:last-child { padding-bottom: 0; }
#parcours .step-n {
  width: 54px; height: 54px; border: 1.5px solid var(--primary);
  background: var(--surface); color: var(--primary);
  font-family: var(--font-display); font-weight: 400; font-size: 1.7rem; line-height: 1;
  box-shadow: 0 0 0 7px var(--sage-panel), 0 0 0 8px var(--sage-line);
}
#parcours .step:not(:last-child)::before {
  left: 26px; top: 70px; bottom: 14px; width: 2px; height: auto; right: auto;
  background: repeating-linear-gradient(to bottom, var(--sage-line) 0 6px, transparent 6px 12px);
}
#parcours .step-b { padding-top: 10px; }
#parcours .step-b h3 { font-size: clamp(1.4rem, 5.4vw, 1.65rem); line-height: 1.2; margin-bottom: var(--s2); }
#parcours .step-b p { font-size: 17.5px; color: var(--fg-soft); max-width: 52ch; }
@media (min-width: 900px) {
  #parcours .wrap {
    display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr);
    column-gap: var(--s8); align-items: start;
  }
  #parcours .section-title { grid-column: 1; grid-row: 1; }
  #parcours .section-intro { grid-column: 1; grid-row: 2; margin: 0; }
  #parcours .steps { grid-column: 2; grid-row: 1 / span 3; display: block; }
  #parcours .step { flex-direction: row; padding-bottom: var(--s7); }
  #parcours .step:not(:last-child)::before { left: 26px; right: auto; top: 70px; bottom: 14px; width: 2px; height: auto; }
}

/* Garanties: two things to know before coming, on one notice, the way a
   cabinet pins them by the door. */
#garanties .trust {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: calc(var(--s6) + 18px) clamp(24px, 5vw, 44px) var(--s4);
  gap: 0;
  max-width: 920px;
  box-shadow: 0 1px 0 color-mix(in srgb, var(--fg) 10%, transparent);
}
/* Same engraved hairline as the door plate. */
#garanties .trust::before {
  content: ""; position: absolute; inset: 9px; pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--sage-line) 55%, transparent);
}
#garanties .trust::after {
  content: "À savoir"; position: absolute; top: calc(var(--s5) + 4px); left: clamp(24px, 5vw, 44px);
  font-size: 13px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--accent);
}
#garanties .trust-row { padding: var(--s5) 0; gap: var(--s4); }
#garanties .trust-row + .trust-row { border-top: 1px dashed color-mix(in srgb, var(--fg) 22%, transparent); }
/* The generator's icons go: a heart beside "une marche" read as a feeling
   on a notice about a step. The hairline and the label carry the panel. */
#garanties .t-icon { display: none; }
#garanties .trust-row h3 { font-size: clamp(1.45rem, 5.5vw, 1.7rem); line-height: 1.2; }
#garanties .trust-row p { font-size: 17.5px; }
@media (min-width: 820px) {
  #garanties .trust { grid-template-columns: 1fr 1fr; gap: 0 var(--s7); }
  #garanties .trust-row + .trust-row { border-top: 0; border-left: 1px dashed color-mix(in srgb, var(--fg) 22%, transparent); padding-left: var(--s7); }
}

/* The band: deep teal, flush left, rings opening from its right corner. */
.cta-band {
  background-color: var(--primary);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 600'%3E%3Cg fill='none' stroke='%23FFFFFF' stroke-opacity='.13'%3E%3Ccircle cx='600' cy='600' r='90' stroke-width='2'/%3E%3Ccircle cx='600' cy='600' r='160' stroke-width='1.8'/%3E%3Ccircle cx='600' cy='600' r='240' stroke-width='1.6'/%3E%3Ccircle cx='600' cy='600' r='330' stroke-width='1.4'/%3E%3Ccircle cx='600' cy='600' r='430' stroke-width='1.2'/%3E%3Ccircle cx='600' cy='600' r='540' stroke-width='1'/%3E%3C/g%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right bottom;
  background-size: min(560px, 110vw) auto;
  text-align: left;
  padding: var(--s8) 0;
}
.cta-band h2 { font-size: clamp(2.3rem, 9.5vw, 3.6rem); line-height: 1.15; margin-bottom: var(--s3); }
.cta-band p { margin-left: 0; opacity: 1; color: #D8E6DE; font-size: 19px; }
.btn-oncta { background: var(--surface); color: var(--primary); border-color: var(--surface); }
.btn-oncta:hover { background: #fff; }
.cta-band .cta-num { margin: var(--s4) 0 0; font-size: 1.9rem; font-weight: 400; color: #FFFFFF; letter-spacing: .01em; }

/* Horaires and contact: bigger figures, firmer fields. */
.hours { font-size: 18px; }
.hours th, .hours td { padding: 14px 0; border-bottom-color: var(--border); }
.hours th { color: var(--fg-soft); }
.hours tr.today th, .hours tr.today td { color: var(--primary); }
.hours-note { font-size: 16px; }
.addr { font-size: 18px; }
.addr .label { color: var(--accent); }
.map-frame { border-radius: var(--radius); }

.field label { font-size: 16px; }
.field input, .field textarea {
  border-radius: var(--radius-sm);
  border-color: color-mix(in srgb, var(--fg) 34%, var(--border));
  background: var(--surface); font-size: 17px; min-height: 52px;
}
#contact .section-intro { font-size: 18px; }
/* Under the call button, the plate's trace, so the column ends on the device. */
@media (min-width: 880px) {
  #contact .contact-layout > div:first-child::after {
    content: ""; display: block; width: min(320px, 100%); height: 36px; margin-top: var(--s7);
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 36'%3E%3Cpath d='M0 18 H70 C78 18 80 12 86 12 S94 24 100 24 S108 6 116 6 S126 30 134 30 S144 2 152 2 S162 34 170 34 S180 8 188 8 S196 26 204 26 S212 14 218 14 S226 20 232 20 S240 17 246 18 H320' fill='none' stroke='%239FB7A6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") left center / 100% 100% no-repeat;
  }
}

.site-foot { border-top: 1px solid var(--border); }
.site-foot h4 { color: var(--accent); }
.action-bar { background: color-mix(in srgb, var(--bg) 95%, transparent); }

/* ---- the map, quiet: the colour taken down toward the cabinet's sage. ---- */
.map-frame iframe { filter: saturate(.35) sepia(.12) hue-rotate(40deg) brightness(1.02); }

/* ---- contact, desktop: the heading, the words and the call ride in the
   left column beside the form (sticky), so the column is never an empty
   half of the page; name and e-mail share a line. ---- */
@media (min-width: 880px) {
  #contact .wrap { display: grid; grid-template-columns: 5fr 6fr; grid-template-rows: auto 1fr; column-gap: var(--s8); align-items: start; }
  #contact .section-title { grid-column: 1; grid-row: 1; }
  #contact .contact-layout { display: contents; }
  #contact .contact-layout > div { grid-column: 1; grid-row: 2; position: sticky; top: 110px; }
  #contact form { grid-column: 2; grid-row: 1 / span 2; display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--s4); }
  #contact form > * { grid-column: 1 / -1; min-width: 0; }
  #contact form > .field:nth-of-type(1) { grid-column: 1; }
  #contact form > .field:nth-of-type(2) { grid-column: 2; }
}

/* ---- the call, as a sound wave ----
   Pointing at a call button (only where there is a pointer) or tabbing to
   it draws one faint ring round it, the hero's rings in small; pressing it
   sends that ring out and away, once. Nothing moves until touched. */
.btn-primary, .btn-oncta { position: relative; -webkit-tap-highlight-color: transparent; }
.btn-primary::after, .btn-oncta::after {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  border: 2px solid var(--primary); opacity: 0;
}
@media (hover: hover) {
  .btn-primary:hover::after, .btn-oncta:hover::after { opacity: .45; transform: scale(1.03, 1.14); }
}
.btn-primary:focus-visible::after, .btn-oncta:focus-visible::after { opacity: .45; transform: scale(1.03, 1.14); }
@media (prefers-reduced-motion: no-preference) {
  .btn-primary::after, .btn-oncta::after { transition: opacity .3s ease-out, transform .3s ease-out; }
  .btn-primary:active::after, .btn-oncta:active::after { animation: ondes-ping .6s ease-out; }
  @keyframes ondes-ping { from { opacity: .7; transform: scale(1); } to { opacity: 0; transform: scale(1.12, 1.6); } }
}

/* ---- the lines move like sound (owner, fb-0097) ----
   Last in the file on purpose: the band and the guarantee panel set their
   rings as a background further up, and this has to take it off them, or
   the moving copy is drawn over the still one (owner, 2026-09-30: the band
   looked doubled).
   The waves and rings are the page's device, so they are what moves; words,
   prices and the hours never do. Two slow loops and one drawing:
   - the rings open from their corner, one after another, as a sound spreads
     (hero plate, the guarantee panel, the band): ondes-ripple, 7.5s;
   - the plate's trace runs through like a scope, the pulse crossing the
     plate and coming back round: ondes-scope, 12s, two copies of the path
     so the loop has no seam;
   - the dotted line between the steps draws down once, when the steps
     scroll into view (the page's own .reveal/.in), never again. */
@media (prefers-reduced-motion: no-preference) {
  .ondes-rings circle {
    transform-box: view-box; transform-origin: 400px 0;
    animation: ondes-ripple 7.5s ease-in-out infinite;
  }
  .ondes-rings circle:nth-child(2) { animation-delay: .35s; }
  .ondes-rings circle:nth-child(3) { animation-delay: .7s; }
  .ondes-rings circle:nth-child(4) { animation-delay: 1.05s; }
  .ondes-rings circle:nth-child(5) { animation-delay: 1.4s; }
  .ondes-rings circle:nth-child(6) { animation-delay: 1.75s; }
  @keyframes ondes-ripple {
    0%, 100% { transform: scale(1); opacity: 1; }
    30% { transform: scale(1.22); opacity: .1; }
    31% { transform: scale(.94); opacity: .15; }
    55% { transform: scale(1); opacity: 1; }
  }
  .ondes-trace-run { animation: ondes-scope 12s linear infinite; }
  @keyframes ondes-scope { from { transform: translateX(0); } to { transform: translateX(-320px); } }
  /* The two ring backgrounds become their own layer so they can move. */
  #services .card:last-child:not(:first-child),
  .cta-band { position: relative; isolation: isolate; overflow: hidden; background-image: none; }
  #services .card:last-child:not(:first-child)::before,
  .cta-band::before {
    content: ""; position: absolute; z-index: -1; right: 0; bottom: 0; pointer-events: none;
    background-repeat: no-repeat; background-position: right bottom; background-size: 100% auto;
    transform-origin: right bottom; animation: ondes-ripple 7.5s ease-in-out infinite;
  }
  #services .card:last-child:not(:first-child)::before {
    width: 220px; height: 220px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 300'%3E%3Cg fill='none' stroke='%239FB7A6'%3E%3Ccircle cx='300' cy='300' r='60' stroke-width='1.6'/%3E%3Ccircle cx='300' cy='300' r='110' stroke-width='1.4'/%3E%3Ccircle cx='300' cy='300' r='165' stroke-width='1.2'/%3E%3Ccircle cx='300' cy='300' r='225' stroke-width='1'/%3E%3C/g%3E%3C/svg%3E");
  }
  .cta-band::before {
    width: min(560px, 110vw); height: min(560px, 110vw);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 600'%3E%3Cg fill='none' stroke='%23FFFFFF' stroke-opacity='.13'%3E%3Ccircle cx='600' cy='600' r='90' stroke-width='2'/%3E%3Ccircle cx='600' cy='600' r='160' stroke-width='1.8'/%3E%3Ccircle cx='600' cy='600' r='240' stroke-width='1.6'/%3E%3Ccircle cx='600' cy='600' r='330' stroke-width='1.4'/%3E%3Ccircle cx='600' cy='600' r='430' stroke-width='1.2'/%3E%3Ccircle cx='600' cy='600' r='540' stroke-width='1'/%3E%3C/g%3E%3C/svg%3E");
  }
  #parcours .steps.reveal .step:not(:last-child)::before { transform: scaleY(0); transform-origin: center top; }
  #parcours .steps.reveal.in .step:not(:last-child)::before { transform: none; transition: transform 1.1s ease-out .35s; }
  #parcours .steps.reveal.in .step:nth-child(2)::before { transition-delay: .9s; }
  #parcours .steps.reveal.in .step:nth-child(3)::before { transition-delay: 1.45s; }
}
