/* ==========================================================================
   LAURENCE AMAND — Dentistry · Orthodontics · Sleep apnea
   --------------------------------------------------------------------------
   Movement doctrine: motion comes and goes.
   One choreographed intro, one gentle settle per element as it enters,
   and then stillness. Nothing loops, nothing drifts, nothing follows you.
   ========================================================================== */

/* ==========================================================================
   FONTS — served from this domain, not from Google
   --------------------------------------------------------------------------
   These used to load from fonts.googleapis.com, which handed every visitor's
   IP address to Google on every page view. For a medical practice in the EU
   that is a transfer with no consent behind it and no need for it. The files
   are ours now; nothing third-party is touched to render a page.

   Both are variable, so one file covers a whole weight range. unicode-range is
   kept from Google's own stylesheet, so a browser fetches latin-ext only if the
   text calls for it — which for Dutch, French and English it does not.
   ========================================================================== */
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 300 500;
  font-display: swap;
  src: url('../fonts/jost-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 300 500;
  font-display: swap;
  src: url('../fonts/jost-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Newsreader';
  font-style: italic;
  font-weight: 400 500;
  font-display: swap;
  src: url('../fonts/newsreader-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Newsreader';
  font-style: italic;
  font-weight: 400 500;
  font-display: swap;
  src: url('../fonts/newsreader-italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../fonts/newsreader-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../fonts/newsreader-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --cream:        #F3EEE5;
  --cream-soft:   #EDE7DB;
  /* The ground under the patient sections. The old beige measured 1.066:1
     against the page cream — 1.000 being identical — so the change of place
     never registered. Sage is 1.19:1 and, unlike every other colour here,
     cool: it breaks the warm palette rather than extending it, which is what
     makes the section announce itself. Ink reads 10.8:1 on it. */
  --ground:       #D9DDD2;
  --cream-bright: #F8F4EC;
  --white:        #FFFFFF;   /* 1.16:1 vs page; ink 13.6:1 on it */
  --ink:          #2C261F;   /* 12.9:1 on cream — was #4A4139 at 8.6:1 */
  --ink-soft:     #3E372F;   /* body prose, 10.1:1 — was #6E655A at 5.0:1 */
  --ink-faint:    #544B40;   /* labels and meta, 7.4:1 — was #6B6257 at 5.2:1 */
  --hairline:     rgba(44, 38, 31, .18);
  --serif: "Newsreader", Georgia, serif;
  --sans:  "Jost", "Helvetica Neue", Arial, sans-serif;
  --ease-out: cubic-bezier(.22, 1, .36, 1);
  /* The dissolve needs a soft start, which --ease-out does not have: that
     curve is nearly vertical off the mark, so the photograph was half gone
     within half a second and the change announced itself. Symmetrical
     ease-in-out instead, so it begins and ends almost imperceptibly. */
  --ease-film: cubic-bezier(.45, .05, .55, .95);
  --header-h: 84px;
  /* ------------------------------------------------------------------------
     HEADER SCALE. One multiplier for the whole bar, so every part grows
     together and the proportions never change.

     It has to move with the window rather than being a flat step. The full
     bar — brand, five nav items, the language switcher and the button —
     measures 1219px in French, and the nav is still shown down to 1321,
     where only 1231px is available: twelve pixels of room. The container
     caps at 1440, so the most room the bar can ever have is 221px. Full size
     costs about 196 of those, which is why it arrives at 1536 and tapers to
     nothing at 1330 instead of being applied everywhere at once.

     A length, not a ratio: calc cannot add a plain number to a length, and
     an invalid custom property here silently hands the logo back to its
     width="96" HTML attribute. So --hdr-u runs 100px -> 118px and every
     header size is taken as a fraction of it.
     ------------------------------------------------------------------------ */
  --hdr-u: clamp(100px, calc(100px + (100vw - 1330px) * 0.0874), 118px);
  --nav-gap:   clamp(16px, calc(16px + (100vw - 1330px) * 0.0485), 26px);
  --header-top: 14px;     /* how far the bar floats below the top edge */
  /* How far the bar floats in from each side — but only while the container
     is not already touching the window. Below that there is no spare room,
     and a fixed inset would push the logo inward while every section below it
     ran to the edge. Measured off the container so the two cannot drift. */
  --header-inset: clamp(0px, (100vw - (var(--page-max) + var(--page-pad) * 2)) / 2, 32px);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }


:root {
  /* ------------------------------------------------------------------------
     THE GRID.

     One container for the whole site. Before this there were three: a 1360
     box, a band measured backwards from the chapter columns, and a fluid
     mode above 1700 with its own edge padding. They agreed at exactly one
     window width and drifted everywhere else, which is why a chapter's text
     could start 150px inside the page while the block beneath it started at
     the margin.

     --page-max  the widest the content is ever allowed to get
     --page-pad  the air between content and window; it grows with the window
                 instead of stepping at a breakpoint, so there is no width at
                 which the margin suddenly changes
     --gutter    the space between columns inside the container
     --sec       a section's standing top and bottom air

     The rule: nothing invents its own width or its own edge padding. A block
     that should read narrower caps its own text and stays on the container's
     left edge. It does not re-centre itself, because a second centre is a
     second left edge.
     ------------------------------------------------------------------------ */
  --page-max: 1440px;
  --page-pad: clamp(24px, calc(12px + 2.5vw), 96px);
  --gutter:   clamp(40px, 5vw, 96px);
  --sec:      clamp(72px, 7.5vw, 130px);
}

/* Every section inner is this box and only this box. */
.header__inner,
.hero__inner,
.services__grid,
.quote__inner,
.about__inner,
.detail-row,
.detail-extra,
.chapter-close-wrap,
.patient-info__inner,
.contact__inner,
.cform-band__inner,
.closing__inner,
.legal__inner {
  width: 100%;
  max-width: calc(var(--page-max) + var(--page-pad) * 2);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--page-pad);
  padding-right: var(--page-pad);
}

/* A container sitting inside another one already has its gutter. Paying it
   twice is what pushed the patient-information blocks 40px right of the
   heading above them. */
.patient-info__inner .detail-extra,
.patient-info__inner .chapter-close-wrap {
  max-width: none;
  padding-left: 0;
  padding-right: 0;
}


body {
  background: var(--cream);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

section { scroll-margin-top: calc(var(--header-h) + var(--header-top) + 10px); }

::selection { background: rgba(44, 38, 31, .14); }

/* ---------- utilities ---------- */

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--cream-bright);
  padding: 10px 18px;
  font-size: 13px;
  z-index: 200;
}
.skip-link:focus { left: 0; }

.eyebrow {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.btn {
  display: inline-block;
  /* Every other .btn is an <a>, which has no background of its own. The form's
     Send is a <button>, and a button with no background declared falls back to
     the browser's ButtonFace - grey on a desktop, and tinted blue by iOS, which
     is how it was spotted. appearance:none stops iOS applying its own chrome on
     top. Variants below set their own fills and still override this. */
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  font-family: var(--sans);
  font-size: 11.5px;
  font-weight: 400;
  letter-spacing: .22em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 999px;
  padding: 15px 34px;
  white-space: nowrap;
  transition: background-color .55s var(--ease-out), color .55s var(--ease-out), border-color .55s var(--ease-out);
}
.btn--outline:hover { background: var(--ink); color: var(--cream-bright); }
.btn--ghost { color: var(--cream-bright); border-color: rgba(248, 244, 236, .55); }
.btn--ghost:hover { background: var(--cream-bright); color: var(--ink); border-color: var(--cream-bright); }

/* Header CTA: a permanent action — warm ivory fill keeps it readable over
   the photograph, then lets go once the sticky header is ivory itself. */
.header .header__cta {
  background: rgba(248, 244, 236, .62);
  border-color: rgba(59, 52, 44, .85);
  /* Slimmer than a body button. At 15px/34px with .22em it ran 247px wide and
     pushed itself out of the bar in French, where the nav is 86px longer than
     in English (Apnée du sommeil, Dentisterie, Informations). */
  font-size: calc(var(--hdr-u) * .115);
  padding: 13px calc(var(--hdr-u) * .26);
  letter-spacing: .16em;
}
.header.is-scrolled .header__cta { background: rgba(248, 244, 236, 0); }
.header .header__cta:hover {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--cream-bright);
}

.textlink {
  display: inline-block;
  font-size: 11.5px;
  font-weight: 400;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 6px;
  transition: border-color .55s var(--ease-out);
}
.textlink:hover { border-color: var(--ink); }

.inline-link {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
  transition: border-color .55s var(--ease-out);
}
.inline-link:hover { border-color: var(--ink); }

/* ==========================================================================
   INTRO — the one choreographed moment
   ========================================================================== */

.intro {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--cream-bright);
  display: grid;
  place-items: center;
  transition: opacity 1.5s var(--ease-out);
}
.intro__brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  opacity: 0;
  transition: opacity 1.4s var(--ease-out);
}
/* height:auto is required, not decorative. Both <img> carry width/height
   attributes, which are presentational hints: setting only the CSS width left
   height:800px and height:253px in force, so the opening logo rendered 220x800
   and 460x253 — the tooth 3.6x too tall, the wordmark squashed flat. Invisible
   during development because ?still skips the intro entirely. */
.intro__tooth { width: min(32vw, 220px); height: auto; }
.intro__text { width: min(62vw, 460px); height: auto; }
.intro.is-holding .intro__brand { opacity: 1; }
.intro.is-letting .intro__brand { opacity: .04; transition-duration: 1.6s; }
.intro.is-leaving { opacity: 0; pointer-events: none; }
.intro.is-gone { display: none; }

/* ==========================================================================
   HEADER
   ========================================================================== */

.header {
  position: fixed;
  top: var(--header-top);
  left: var(--header-inset);
  right: var(--header-inset);
  z-index: 50;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 1.2s var(--ease-out), transform 1.2s var(--ease-out),
              background-color .6s var(--ease-out), box-shadow .6s var(--ease-out);
}
body.is-ready .header {
  opacity: 1;
  transform: none;
  /* quicker than the intro's 1.2s: this one answers a gesture */
  transition: transform .38s var(--ease-out),
              background-color .6s var(--ease-out),
              box-shadow .6s var(--ease-out);
}
/* Clears its own height, the gap it floats in, and the shadow beneath it. */
body.is-ready .header.is-hidden {
  transform: translateY(calc(-100% - var(--header-top) - 26px));
}

/* The surface sits on the INNER bar, not on .header, so it reads as a shape
   floating over the page rather than a band ruled across the top. */
/* .legal-page: those pages have no film behind the bar, and they do not load
   main.js — so is-scrolled is never added and the bar stayed transparent,
   with the text running underneath it as you scrolled. There is nothing for
   it to be transparent over there, so it simply always carries its surface. */
.header.is-scrolled .header__inner,
.legal-page .header__inner {
  background: rgba(255, 255, 255, .96);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  /* White needs a drawn edge in a way the beige did not: against the page
     cream the fill alone reads as a soft patch rather than a shape. */
  box-shadow: 0 8px 30px rgba(74, 65, 57, .10),
              inset 0 0 0 1px rgba(44, 38, 31, .16);
}

.header__inner {
  height: var(--header-h);
  border-radius: 22px;
  transition: background-color .6s var(--ease-out), box-shadow .6s var(--ease-out);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 22px;
}

.brand { display: flex; align-items: center; gap: 14px; text-decoration: none; color: var(--ink); }
.brand__mark {
  width: calc(var(--hdr-u) * .54);
  height: calc(var(--hdr-u) * .54);
  object-fit: contain;
}
.brand__text { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.brand__wordmark { display: none; }
.brand__name {
  font-family: var(--sans);
  font-size: calc(var(--hdr-u) * .16);
  font-weight: 400;
  letter-spacing: .24em;
  text-transform: uppercase;
  white-space: nowrap;
  text-align: center;
}
.brand__tagline {
  font-size: calc(var(--hdr-u) * .085);
  font-weight: 400;
  /* .2em rather than .26em: at 43 characters the French line
     ("Dentisterie · Orthodontie · Apnée du sommeil") is the widest element in
     the header, and every 0.01em costs it about 3.7px. */
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  white-space: nowrap;
  text-align: center;
}

/* The gap grows faster than the type does: at the old 16 the five items read
   as one run of letters rather than five destinations. */
.nav { display: flex; gap: var(--nav-gap); margin-left: 12px; }
.nav__link {
  /* 13px at .13em rather than 11.5px at .18em: wide tracking on small uppercase
     type pushes the glyphs apart until the word stops holding together. */
  font-size: calc(var(--hdr-u) * .13);
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-soft);
  position: relative;
  padding: 6px 0;
  white-space: nowrap;
  transition: color .5s var(--ease-out);
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .6s var(--ease-out);
}
.nav__link:hover { color: var(--ink); }
.nav__link:hover::after { transform: scaleX(1); transform-origin: left; }

.header__side { display: flex; align-items: center; gap: 18px; }

/* ---------- legal pages: where you are, and the way back ---------- */

/* The page you are on, sitting where the navigation sits on the home page —
   the same rhythm, and it stays put while the heading scrolls away. */
.header__page {
  font-size: calc(var(--hdr-u) * .13);
  font-weight: 400;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--ink-soft);
  white-space: nowrap;
}

/* The tooth already links home, but a mark is not an obvious way out. */
.header__back {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  font-size: calc(var(--hdr-u) * .115);
  letter-spacing: .13em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-soft);
  white-space: nowrap;
  transition: color .5s var(--ease-out);
}
.header__back:hover { color: var(--ink); }
.header__back-arrow { transition: transform .5s var(--ease-out); }
.header__back:hover .header__back-arrow { transform: translateX(-4px); }
.header__back:focus-visible { outline: 1px solid var(--ink); outline-offset: 6px; }

@media (max-width: 1080px) {
  /* The bar is brand, back and languages on a phone; the title is the first
     thing the page itself says, so it is the one that gives way. */
  .header__page { display: none; }
}
@media (max-width: 560px) {
  /* Only the arrow survives, and it keeps a name for screen readers. */
  .header__back span:not(.header__back-arrow) { position: absolute; left: -9999px; }
  .header__back-arrow { font-size: 17px; }
}

.lang { display: flex; align-items: center; gap: calc(var(--hdr-u) * .08); }
.lang__sep { color: var(--hairline); font-size: 11px; }
.lang__btn {
  font-family: var(--sans);
  font-size: calc(var(--hdr-u) * .11);
  font-weight: 400;
  letter-spacing: .12em;
  background: none;
  border: none;
  color: var(--ink-faint);
  cursor: pointer;
  padding: 4px 1px;
  border-bottom: 1px solid transparent;
  transition: color .5s var(--ease-out), border-color .5s var(--ease-out);
}
.lang__btn:hover { color: var(--ink); }
.lang__btn.is-active { color: var(--ink); border-bottom-color: var(--ink); }

.menu-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  width: 44px;
  height: 44px;
  background: none;
  border: none;
  cursor: pointer;
}
.menu-toggle span { display: block; height: 1px; width: 26px; background: var(--ink); margin: 0 auto; }

.mobile-menu {
  display: none;
  flex-direction: column;
  gap: 6px;
  padding: 14px var(--page-pad) 34px;
  background: rgba(255, 255, 255, .97);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: 0 1px 0 var(--hairline);
}
.mobile-menu__link {
  /* The open menu is the whole screen for a moment; it can afford to be read
     at arm's length rather than squinted at. */
  font-size: 15px;
  letter-spacing: .18em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  padding: 15px 0;
}
.mobile-menu .btn { margin-top: 14px; text-align: center; }

/* ==========================================================================
   HERO
   ========================================================================== */

.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: calc(var(--header-h) + var(--header-top) + 40px) 0 80px;
}
.hero__inner {
  position: relative;
  z-index: 1;
  display: grid;
  /* Narrower than it was. At 600 the headline ran to 47% of the frame, past
     the point where the side wash has faded to almost nothing, so its last
     words sat on bare footage. At 470 it ends around 38%, where the wash is
     still doing real work. */
  /* 505, not 600. At 600 the headline ran to 47% of the frame, past the point
     where the side wash has faded to almost nothing, so its last words sat on
     bare footage. 505 pulls it back to 40%, where the wash still does real
     work — and it is the narrowest that fits every language: two of the three
     headlines pin their closing words with a non-breaking space, and Dutch's
     "persoonlijk begeleid." is 501px at the largest hero size. Below that it
     overflows the column instead of wrapping. */
  grid-template-columns: minmax(0, 505px) 1fr;
}


.hero__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(36px, 3.95vw, 62px);
  line-height: 1.12;
  letter-spacing: -.005em;
  margin: 26px 0 22px;
  text-wrap: balance;
}

/* the three expertise areas — a quiet strip along the hero's lower edge,
   its columns aligned with the three-column section below */
/* The disciplines belong to their photographs. They used to be a nav pinned to
   the bottom of the hero, floating over whatever the film happened to be
   showing at that moment. Each label now heads its own card, aligned to the
   left edge of its picture. Phones are unaffected either way: section.services
   is display:none there, and the nav was hidden too. */
.service__label {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 14px;
}
.service__num {
  font-size: 10px;
  letter-spacing: .18em;
  color: var(--ink-faint);
  transition: color .5s var(--ease-out);
}
.service__name {
  font-size: 11.5px;
  font-weight: 400;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  transition: color .5s var(--ease-out);
}
.service:hover .service__name,
.service:hover .service__num { color: var(--ink); }

.hero__location {
  margin-top: 44px;
  font-size: 11px;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-decoration: none;
  display: inline-block;
  transition: color .5s var(--ease-out);
}
.hero__location:hover,
.hero__mail:hover { color: var(--ink); }
.loc-sep { margin: 0 .8em; }

/* The address, and the address she answers. She would rather be written to
   than telephoned, so the mail is on the first screen - at exactly the
   weight of the street beneath it, a reference rather than a second call to
   action competing with the appointment button. Its own line: the two
   together need 607px and the hero column is 505, so set side by side they
   would wrap anyway and leave the separator hanging. */
.hero__meta { margin-top: 44px; }
.hero__location { margin-top: 0; }
.hero__mail {
  display: block;
  margin-top: 11px;
  font-size: 11px;
  /* A step heavier than the street above it - 500 is the top of Jost here.
     She would rather be written to, so the address she answers carries
     slightly more weight than the one you drive to. */
  font-weight: 500;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-decoration: none;
  display: inline-block;
  transition: color .5s var(--ease-out);
}

/* --- cinematic background ------------------------------------------- */

/* A plain field behind the film, not a photograph. The still used to sit
   here as the poster, but it surfaced whenever the film was loading, paused
   or refused, and a portrait flashing in and out behind the headline reads as
   a fault. Cream is what the page is already made of, so an empty hero looks
   deliberate rather than broken — and it saves the 66KB the photograph cost
   on every first load. */
.hero__bg { position: absolute; inset: 0; background: var(--cream); }
.hero__bg img,
.hero__bg video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 62% 24%;
}
/* The hero's photograph is the fallback, not the poster. It waits hidden and
   appears only if no film is going to arrive — otherwise it downloads faster
   than the film can start and flashes in behind the headline for about a
   second on every first visit, which is exactly what the note above says the
   hero must not do. JS adds .show-still on every path where no film will
   play; <noscript> in the head covers the case where JS never runs at all.

   Scoped to #heroImage on purpose. In the chapters the photograph IS the
   poster and dissolving out of it is the intended effect — untouched. */
#heroImage { opacity: 0; transition: opacity .9s var(--ease-out); }
.hero__bg.show-still #heroImage { opacity: 1; }

#heroVideo { opacity: 0; transition: opacity 1.6s var(--ease-out); }
.hero__bg.has-video #heroVideo { opacity: var(--film-o, 1); transition: opacity .18s linear; }

.hero__scrim {
  position: absolute;
  inset: 0;
  transition: background .8s var(--ease-out);
  background:
    linear-gradient(90deg, rgba(243,238,229,.88) 0%, rgba(243,238,229,.5) 22%, rgba(243,238,229,.1) 44%, rgba(243,238,229,0) 60%),
    linear-gradient(0deg, rgba(243,238,229,.9) 0%, rgba(243,238,229,0) 12%);
}

/* A stronger scrim once a film is behind the words. The still was shot bright
   and blown out on the left, so the text had a pale field to sit on; footage
   cannot be relied on for that — this clip puts mid-grey concrete exactly
   where the address goes. The reach is unchanged, only the density, so the
   right-hand side of the frame stays completely clear. */
/* A wash in from the sides, clear through the middle.
   The reading column is held by the left, the strapline by the right, and the
   header by a shallow strip along the top. The centre of the frame carries
   nothing at all — the left is gone by 52%, which is where the picture opens
   up and stays open. */
.hero__bg.has-video .hero__scrim {
  background:
    /* the header */
    linear-gradient(180deg, rgba(243,238,229,.52) 0%, rgba(243,238,229,.20) 10%, rgba(243,238,229,0) 22%),
    /* the strapline */
    linear-gradient(270deg, rgba(243,238,229,.82) 0%, rgba(243,238,229,.44) 16%, rgba(243,238,229,0) 34%),
    /* the reading column */
    linear-gradient(90deg, rgba(243,238,229,.97) 0%, rgba(243,238,229,.80) 20%, rgba(243,238,229,.36) 38%, rgba(243,238,229,0) 52%);
}





/* ==========================================================================
   SERVICES — three quiet cards
   ========================================================================== */

.services {
  background: var(--white);
  padding: 14px 0 85px;
}
.services__grid {
  gap: 16px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
.service {
  padding: 0 0 44px;
  text-decoration: none;
  color: var(--ink);
  display: flex;
  flex-direction: column;
}
.service__media {
  position: relative;
  overflow: hidden;
  aspect-ratio: 3 / 2;
}
/* The photographs are plain rectangles. They used to dissolve into the ivory
   at top and bottom; the fade belongs to the film, not to the stills. */
.service__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.001);
  transition: transform 1.6s var(--ease-out);
}
.service:hover .service__media img { transform: scale(1.045); }
.service__text {
  font-size: 16px;
  color: var(--ink);
  margin: 28px 38px 24px;
  max-width: 300px;
}
.service__more {
  margin: auto 38px 0;
  align-self: flex-start;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--ink);
}
.service__more { transition: transform .6s var(--ease-out); }
.service:hover .service__more { transform: translateX(6px); }

/* ==========================================================================
   QUOTE
   ========================================================================== */

/* The same dark brown as the closing band. The line was floating on the page
   cream with nothing holding it; on its own ground it becomes a pause between
   the disciplines above and Laurence below. */
.quote {
  /* Sage, not the brown. The text has to turn with it: cream on sage is
     1.2:1 and unreadable, so the band carries the page's own ink, which
     measures 10.8:1 on this ground. */
  background: var(--ground);
  color: var(--ink);
  /* One line and a sentence do not need 104 above and below. */
  padding: 62px 0;
}
/* The band runs the full window and the words are centred in it on purpose.
   The measure is capped on the text, not by shrinking the container — so the
   band's own edges stay on the page's line. */
.quote__inner { text-align: center; }
.quote__line { max-width: 900px; margin-left: auto; margin-right: auto; }
.quote__line {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(28px, 3.1vw, 45px);
  line-height: 1.25;
  text-wrap: balance;
}
/* the "Smile." break is a mobile gesture — desktop keeps one line */
.quote__line br { display: none; }
.quote__sub {
  margin-top: 30px;
  font-size: 15px;
  /* 8.5:1 on the sage */
  color: var(--ink-soft);
  max-width: 560px;
  margin-left: auto;
  margin-right: auto;
}

/* ==========================================================================
   ABOUT
   ========================================================================== */

.about {
  background: var(--cream-soft);
  padding: 140px 0;
  overflow: hidden;
}
.about__inner {
  position: relative;
  display: grid;
  /* The diploma column starts further left and stops short of the page edge,
     so the frame is not sitting on the margin. The text is capped at 560
     anyway, so the narrower reading column costs it nothing. */
  /* The diploma is the point of this section, so its column is the wider of
     the two. The reading measure is capped at 560 anyway, so giving the
     document the extra room costs the text nothing. */
  /* The picture here is a portrait of Laurence, so the column is a portrait
     column. Narrower than the chapters', and the reading measure is capped at
     560 anyway, so the text gives up nothing. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 480px);
  gap: var(--gutter);
  align-items: start;
}
/* The tooth, stamped faintly into the beige sections — alternating sides,
   bleeding off the edge, sitting in the width the text does not use. The
   section clips it, so it runs to the edge and stops there rather than
   widening the page. */
.stamp {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 480px;
  height: auto;          /* else the height="800" attribute stretches it */
  opacity: .07;
  pointer-events: none;
  z-index: 0;
}
.stamp--left  { left: -140px; }
.stamp--right { right: -140px; }

/* Every section carries one, alternating down the page. The pattern is always
   the same: the block that holds the stamp is the positioned ancestor, so the
   tooth is placed against the CONTAINER's edge and follows it as the window
   changes; the full-width section around it does the clipping, so the shape
   runs off the window rather than widening the page.

   The two dark brown bands are left alone — the mark is a dark shape, and at
   this opacity on #3B342C there would be nothing to see.

   On a chapter the stamp always takes the side away from the picture, or it
   would sit behind it and be wasted. */
.detail-row,
.detail-extra,
.contact__inner,
.legal__inner { position: relative; }
.cform-band { overflow: hidden; }
.cform-band__inner { position: relative; }
.chapter-band { overflow: hidden; }
.detail     { overflow: hidden; }
.contact    { overflow: hidden; }
.legal      { overflow: hidden; }

/* Pure white gives the mark more contrast than the cream does, so it is held
   back there to read at the same strength. */
.chapter-band .stamp { opacity: .05; }
.about__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(31px, 3.25vw, 50px);
  line-height: 1.15;
  margin: 26px 0 34px;
}
.about__text {
  max-width: 560px;
  font-size: 15.5px;
  color: var(--ink-soft);
  margin-bottom: 22px;
}
.about__main .textlink { margin-top: 16px; }

/* The column holds the diploma alone now; the three entries moved under the
   text, where the wider column lets them stand side by side. */
.about__cv {
  padding-left: 0;
  /* the 56px inset that used to hold the diploma off the page edge is gone:
     the centred pair does that job now, and the inset only made it lopsided */
  padding-top: 76px;
}
.about__creds {
  display: flex;
  flex-wrap: wrap;
  gap: 40px 52px;
  margin-top: 46px;
  padding-top: 34px;
  border-top: 1px solid var(--hairline);
}
.about__creds .cv-item { flex: 0 1 auto; min-width: 150px; }

/* The diploma, at reference size, under the three entries it is the evidence
   for. The sage mount is the treatment the legal page already gives it, so
   the two read as one document seen at two sizes. Small on purpose: the
   portrait beside it keeps the weight, and the full-size scan is one click
   away rather than competing here. */
/* The document stands beside the paragraphs, in the room the reading measure
   leaves. It is sized by its height, not its width: its top sits on the cap
   of the first line and its bottom on the baseline of the last, so it rises
   and falls with the copy and with the language. No mount and no caption -
   the scan says DIPLOMA across the top itself, and the full-size one is a
   click away on the legal page.

   Everything here exists to keep the row governed by the TEXT. Three things
   will quietly hand that job to the picture instead, and then every language
   comes out the same size: an aspect-ratio on the link, an in-flow image, or
   a calc() height on the link. So the link is an empty stretched box and the
   image is taken out of flow inside it. */
.about__body {
  display: grid;
  /* Fixed, not auto: an auto track sizes itself from the picture, the picture
     from the row, and the row from the text - and the picture wins. */
  grid-template-columns: minmax(0, 1fr) clamp(240px, 24vw, 340px);
  align-items: stretch;
  gap: clamp(28px, 3vw, 48px);
  /* Matching the boxes is not matching what you see. A line box carries half
     its leading above the cap, and the descender plus half-leading below the
     baseline, so a picture set to the paragraph's box stands proud of the
     first line and hangs under the last. At 15.5px on 26.35 that is 7.8px at
     the top and 7.7px at the bottom; 8 trims both. */
  --copy-trim: 8px;
  /* The last paragraph's own 22px bottom margin used to sit inside the box,
     so the box ended 22px below the final baseline and the picture hung that
     far past it. It moves out here, where everything below keeps its spacing
     and the copy box now ends on the last line. */
  margin-bottom: 22px;
}
.about__copy { min-width: 0; }
.about__copy > .about__text:last-child { margin-bottom: 0; }
.cv-diploma {
  display: block;
  position: relative;      /* the frame the picture is measured against */
  align-self: stretch;     /* an empty box, so the text alone sets the row */
  transition: opacity .5s var(--ease-out);
}
.cv-diploma:hover { opacity: .76; }
.cv-diploma img {
  display: block;
  position: absolute;      /* out of flow, so it cannot size the row */
  left: 0;
  top: var(--copy-trim);
  height: calc(100% - var(--copy-trim) * 2);
  width: auto;             /* the height is given, so the ratio gives this */
  max-width: 100%;
  object-fit: contain;
  /* Left, so the edge facing the text never moves between languages; the
     slack from a shorter paragraph falls on the empty side. */
  object-position: left center;
  /* The sage mount, the same treatment the legal page gives the document.
     It is the image's own padding rather than a box around it: a wrapper
     would have to be told how wide the picture is, and nothing in CSS knows
     that. Painted inside the border box, so the mount's outer edges are the
     ones carrying the alignment, and the scan sits in the middle of it. */
  background: var(--ground);
  padding: 14px;
  box-sizing: border-box;
}

/* Stacked, there is no text height to match, so it sizes by width again. */
@media (max-width: 1080px) {
  .about__body { display: block; margin-bottom: 0; }
  .about__copy > .about__text:last-child { margin-bottom: 22px; }
  .cv-diploma { position: static; width: 230px; max-width: 62%; margin-top: 12px; }
  .cv-diploma img { position: static; height: auto; width: 100%; padding: 11px; }
}
.cv-item__year {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 23px;
  line-height: 1.2;
}
.cv-item__line { font-size: 13.5px; color: var(--ink-soft); margin-top: 6px; }
/* The diploma itself, under the three CV entries it stands behind.
   height:auto is load-bearing: the file carries width="1200" height="839"
   and those attributes act as CSS. */
/* The document in a heavy frame of its own — no mount board behind it, just
   a broad dark edge sitting directly on the paper. */
/* Laurence, in the About column. The diploma that used to share this frame
   now lives on the legal page, where a professional title and its evidence
   belong together; here the photograph simply stands on its own.

   The sage sits behind it rather than around it: the picture covers the panel
   completely, so the colour only shows while the file is still arriving —
   which is better than a white flash. */
.cv-portrait {
  margin: 0;
  max-width: none;
}
.cv-portrait picture { display: block; }
.cv-portrait img {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  background: var(--ground);
}

.cv-item__note {
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: 4px;
}

/* ==========================================================================
   DETAIL ROWS — disciplines in depth
   ========================================================================== */

.detail { padding: 40px 0 0; }

/* The three things Laurence actually does stand on white; everything that
   explains or follows them — the practical notes, the orthodontic journey,
   the patient-information bands — stays on the page's cream or its sage. The
   white is an accent on the chapters, so it is a band around each one rather
   than a background on .detail. Full-bleed, like the sage bands, so the
   colour runs to the window while the row inside keeps the container. */
.chapter-band { background: var(--white); }

.detail-row {
  padding-top: var(--sec);
  padding-bottom: var(--sec);
  display: grid;
  /* The copy takes the container's left edge and the picture is pinned to its
     right edge, so both ends of the row land on the page's line. The picture
     keeps its 460 — it is the smaller half deliberately, because at 1fr 1fr a
     4:3 frame stood taller than the text and rose above its own title. The
     copy column is free, and .detail-row__text caps the reading measure at
     520 inside it, so the slack falls between the two rather than outside
     them. That slack is what used to be an ever-widening column-gap. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 460px);
  row-gap: 90px;
  column-gap: var(--gutter);
  /* The picture used to stretch to the height of the words beside it, which
     sounds right and is not: the three chapters carry different amounts of
     copy, so the same 460 column came out 1.22 on dentistry, 1.05 on sleep
     apnea and 0.82 on orthodontics — a portrait, in a row of landscapes.
     They are one set of pictures and they should be one shape. */
  align-items: start;
}
.detail-row--flip { grid-template-columns: minmax(0, 460px) minmax(0, 1fr); }

.detail-row__num {
  font-size: 11px;
  letter-spacing: .3em;
  color: var(--ink-faint);
  margin-bottom: 26px;
}
.detail-row__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(28px, 2.75vw, 40px);
  line-height: 1.15;
  margin-bottom: 14px;
}
.detail-row__lead {
  font-family: var(--serif);
  font-style: italic;
  font-size: 18px;
  color: var(--ink-soft);
  margin-bottom: 24px;
}
.detail-row__text {
  font-size: 15px;
  color: var(--ink-soft);
  max-width: 520px;
  margin-bottom: 34px;
}

.facts { list-style: none; }

/* The aligner system the practice works with, closing the orthodontics column.
   A rule above it and room around it, so it reads as a credit rather than a
   fourth bullet. The logo is the official single-colour SVG at its own
   #373D42 — near enough the page ink to sit quietly, and left as the brand
   draws it rather than recoloured. */
.provider {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-top: 34px;
  padding-top: 26px;
  border-top: 1px solid var(--hairline);
  max-width: 520px;
}
.provider__label {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  white-space: nowrap;
}
/* width:auto is not enough here: the SVG carries width="1430" height="349",
   and those attributes act as CSS. The height must be released explicitly or
   the mark renders 349px tall. */
.provider__link {
  display: inline-flex;
  line-height: 0;
  transition: opacity .4s var(--ease-out);
}
.provider__link:hover { opacity: .62; }
.provider__link:focus-visible { outline: 1px solid var(--ink); outline-offset: 6px; }
.provider__logo {
  width: 168px;
  height: auto;
  flex-shrink: 0;
}
/* OrthoApnea's wordmark is 6.3:1 against ClearCorrect's 4.1, and carries no
   second line, so matching it by width would leave it visibly lighter. Sized
   to sit at the same optical weight instead. */
.provider__logo--oa { width: 156px; }
@media (max-width: 640px) {
  .provider { gap: 14px; margin-top: 28px; padding-top: 22px; }
  .provider__logo { width: 140px; }
}
.facts li {
  font-size: 11.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink);
  padding: 13px 0;
}

.detail-row__media {
  overflow: hidden;
  /* 3:2, the same shape as the three photographs at the top of the page, so a
     chapter's picture and its card are recognisably the same frame. */
  aspect-ratio: 3 / 2;
  /* Align the picture with the title rather than the top of the copy. The
     number above it is an 11px line at the 1.7 body leading plus its own 26px
     margin — exactly the distance from the top of the copy to the title. */
  margin-top: calc(11px * 1.7 + 26px);
}
.detail-row__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.001);
  transition: transform 1.8s var(--ease-out);
}
.detail-row:hover .detail-row__media img { transform: scale(1.04); }

/* The credit belongs to the picture, not to the prose. It is a logo: inside
   the copy it interrupted the reading, and under the film it closes the
   column and takes up the room the 3:2 frame leaves.

   Placed explicitly rather than by order, because the copy is the taller of
   the two and a third item left to flow would land underneath all of it. The
   copy spans both rows; the picture takes the first and the credit the
   second, so the credit sits immediately beneath the frame. */
.detail-row__copy  { grid-column: 1; grid-row: 1; }
/* Only a chapter that HAS a credit needs the copy to span two rows. Spanning
   it everywhere cost dentistry 238px of nothing: with no credit, row two held
   no content, but grid still reserved a share of the copy's height for it and
   then added the 90px row gap on top. The section ran 889px for 583px of
   content. */
.detail-row:has(.provider) .detail-row__copy { grid-row: 1 / span 2; }
.detail-row__media { grid-column: 2; grid-row: 1; }
.detail-row .provider { grid-column: 2; grid-row: 2; align-self: start; }

/* Dentistry and sleep apnea carry the large pictures; orthodontics keeps the
   standard 460, so the three chapters do not all behave the same way down the
   page. The column is a clamp rather than a fixed width so it gives room back
   on a narrower desktop instead of squeezing the reading column: full size
   from about 1430 and never below the 460 orthodontics uses.

   Only the column changes. The 3:2 stays, so all three frames are the same
   shape; the right edge, the top and the copy are untouched. Above the phone
   breakpoint only — below it the row is a single column and this would fight
   the stacked layout. */
@media (min-width: 1081px) {
  #dentistry,
  #sleep-apnea { grid-template-columns: minmax(0, 1fr) minmax(0, clamp(460px, 41vw, 585px)); }
}

.detail-row--flip .detail-row__copy  { grid-column: 2; }
.detail-row--flip .detail-row__media { grid-column: 1; }
.detail-row--flip .provider          { grid-column: 1; }

/* ==========================================================================
   INFORMATION
   ========================================================================== */

.contact { padding: 110px 0 120px; }

/* Laurence at work, inside the contact column rather than above it. Sitting
   full width above the address it read as a banner; here it belongs to the
   block it sits in. */
/* The form comes first, on its own sage band directly under the heading:
   writing to the practice is what most people came to this section to do, so
   it is not left at the foot of it. The photograph, the address and the
   navigation follow on one line beneath, on the white again.

   Three stops on the same grid, so the left edge never moves. */
.contact__col--intro   { grid-column: 1; grid-row: 1; }
.contact__portrait     { grid-column: 1; grid-row: 1; }
.contact__col--details { grid-column: 2; grid-row: 1; }
.contact__col--nav     { grid-column: 3; grid-row: 1; }

/* The heading and the form are one cream block, so there is no band edge
   between them to pay for - just a section's worth of air. The sage picks
   up underneath, full bleed, the way every other ground on the page does. */
.contact            { padding: 110px 0 0; }
.contact--find      { background: var(--ground); padding: 104px 0 118px; }

.contact__portrait {
  margin: 0;              /* it starts on the same line as the address */
  overflow: hidden;       /* the drift scales inside the frame, not past it */
  /* It fills its track and runs the full height of the row, so its top and
     bottom edges land on the same lines as the rule beside it. The crop does
     the work instead of a fixed ratio: the row is as tall as the address
     column, and the picture is cut to it rather than setting it. */
  max-width: none;
  align-self: stretch;
}
.contact__portrait img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.contact__inner {
  display: grid;
  grid-template-columns: 1.3fr 1.05fr .75fr;
  /* A real row gap. At 0 the photograph started on the same line as the other
     two columns but hard against the paragraph above it, so the left column
     read as one block of weight while the other two opened with a rule and a
     label. The gap belongs to the row, so all three still start together. */
  gap: 44px 56px;
  align-items: start;
}
/* The rules run the whole row rather than stopping at the last line of text,
   so the picture, the address and the navigation are held between the same
   two horizontal lines. align-items on the grid is start, which would cut
   each rule to its own content and leave three different edges. */
.contact__col--details,
.contact__col--nav {
  border-left: 1px solid var(--hairline);
  padding-left: 56px;
  align-self: stretch;
}
.contact__col--details { padding-right: 26px; }

.contact__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(34px, 3.45vw, 52px);
  margin: 22px 0 20px;
}
.contact__text { font-size: 15px; color: var(--ink-soft); max-width: 420px; }

/* The opening line reads as one sentence, not two. In the three-column grid
   the intro track is 517px and the line needs 699 in English, 760 in French,
   617 in Dutch - so it broke after "reach" and left "the practice." stranded
   on a line of its own.

   Spanning costs nothing: since the form and the practical details became
   their own sections, the two tracks beside this one are empty. Desktop only;
   below 1081 the grid is a single column and 420 is the right measure. */
@media (min-width: 1081px) {
  .contact:not(.contact--find) .contact__col--intro { grid-column: 1 / -1; }
  .contact__text { max-width: 800px; }
}

.c-group { padding: 4px 0 22px; }
.c-group + .c-group { border-top: 1px solid var(--hairline); padding-top: 24px; }
.c-group:first-child { padding-top: 8px; }
.c-row {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  padding: 7px 0;
  min-width: 0;
}
.c-ic {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin-top: 3px;
  color: var(--ink-faint);
}
.c-ic svg { width: 100%; height: 100%; display: block; }
.c-link, .c-text {
  font-size: 15px;
  color: var(--ink);
  text-decoration: none;
  line-height: 1.65;
  transition: color .5s var(--ease-out);
  min-width: 0;
  overflow-wrap: break-word;
}
.addr-dot { display: none; }
.addr-br2 { display: inline; }
a.c-link:hover { color: var(--ink-soft); }
.c-link--ext { color: var(--ink-soft); }
.c-link--ext:hover { color: var(--ink); }

.mini-label {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 14px;
}

/* A section eyebrow is not a column label. These head a whole block, so they
   are larger than the titles beneath them and stand well clear of them — at
   14px with 14px under it, "En pratique" was two pixels bigger and lighter in
   colour than "Avant votre visite" directly below, which is no hierarchy at
   all. 64px of clearance is what the patient-information eyebrows already
   use; this brings the rest into line with them. */
.notes > .mini-label,
.patient-info__eyebrow {
  font-size: 18px;
  color: var(--ink);
}
.notes > .mini-label { margin-bottom: 64px; }

/* navigation + legal column */
.contact__nav { display: flex; flex-direction: column; gap: 13px; margin-bottom: 34px; }
.contact__nav a {
  font-size: 16px;
  color: var(--ink-soft);
  text-decoration: none;
  transition: color .5s var(--ease-out);
}
.contact__nav a:hover { color: var(--ink); }
.contact__legal {
  display: flex;
  flex-direction: column;
  gap: 9px;
  font-size: 13px;
  color: var(--ink-faint);
}
/* These were plain spans naming pages that did not exist. They are links now. */
.contact__legal a {
  color: var(--ink-faint);
  text-decoration: none;
  transition: color .4s var(--ease-out);
}
.contact__legal a:hover { color: var(--ink); }

/* ==========================================================================
   CLOSING — the one dark, still moment
   ========================================================================== */

.closing { background: #3B342C; color: var(--cream-bright); padding: 130px 0; }
.closing__inner { text-align: center; }
.closing__inner > * { max-width: 820px; margin-left: auto; margin-right: auto; }
.closing__tagline {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(22px, 2.6vw, 34px);
  line-height: 1.3;
  margin-bottom: 44px;
  text-wrap: balance;
}



/* ==========================================================================
   MOTION — reveals settle once, then rest
   ========================================================================== */

/* <picture> is a wrapper only: it must not become a layout box */
/* display:contents stops <picture> creating a box that breaks object-fit on
   the <img> inside it. But it also promotes the picture's <source> elements
   into the parent, and a grid or flex container blockifies them into real
   items: invisible, zero-height, and holding a cell. That put a 776x0 phantom
   in the first cell of .about__inner, pushing the text into the narrow column
   and the CV onto a second row, and added a phantom flex item to .brand.
   A <source> is never rendered content, so it is never a layout item. */
picture { display: contents; }
picture > source { display: none; }

/* ---------- film layer ----------
   The photograph is the poster. A film, where one exists, fades in over it
   while the section is watched, and stops the moment it is not. If no film
   is ever supplied, the still simply stays. */
/* Only the figures need a positioning context; .hero__bg already has one,
   and blanket-setting position here collapsed it to 0x0. */
figure[data-film] { position: relative; }
[data-film] > video.film {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.6s var(--ease-out);
}
/* --film-o is 0 while the photograph stands and rises to the film's cap once
   the section has been looked at for a moment. The photograph does the exact
   inverse, so one dissolves into the other.

   2.8s, not the .18s this used to run at: that was a per-frame value chased
   by the scrollbar, where any real duration would have lagged behind the
   scroll. It is set once now, so the dissolve can take its time. At under two
   seconds it still announced itself as a transition; at this length you do
   not catch it starting, you simply notice the photograph has become film.
   The same duration carries it back to the photograph on the way out. */
[data-film].has-film > video.film { opacity: var(--film-o, 1); }
[data-film].has-film > picture img,
[data-film].has-film > img { opacity: calc(1 - var(--film-o, 1)); }
[data-film].has-film > video.film,
[data-film].has-film > picture img,
[data-film].has-film > img { transition: opacity 2.8s var(--ease-film); }

/* ---------- scroll drift ----------
   Section media settles as it travels: slightly large on the way in, its own
   size by the time it leaves. Scale only. A translate would need the picture
   overscanned first, or it shows an edge at rest — which would re-crop every
   photograph on the site for the sake of a few pixels of movement.

   Carried on the <picture> and the film, never on the <img>: the hover scale
   lives on the <img>, so the two multiply instead of overwriting each other.
   A non-replaced inline element ignores transform, hence display:block.
   No transition — the value is repainted per frame. */
/* Painted every frame, so no transition: a duration here would lag the
   scroll. Defaults to 1, which is what reduced motion and a missing script
   both leave it at. */
[data-fade] { opacity: var(--fade, 1); }

[data-drift] > picture { display: block; height: 100%; }
[data-drift] > picture,
[data-drift] > video.film {
  transform: translate3d(0, var(--drift-y, 0px), 0) scale(var(--drift-s, 1));
  transform-origin: 50% 50%;
}
@media (prefers-reduced-motion: reduce) {
  [data-drift] > picture,
  [data-drift] > video.film { transform: none; }
}


html.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .85s var(--ease-out), transform .85s var(--ease-out);
  transition-delay: var(--d, 0s);
}
html.js .reveal.in { opacity: 1; transform: none; }

/* PATIENT INFORMATION — one full-width, slightly darker editorial chapter:
   the band spans the viewport, the text keeps a controlled reading width */
.patient-info { background: var(--ground); padding: 90px 0 58px; overflow: hidden; }
.patient-info__inner { position: relative; }
.patient-info__eyebrow { display: block; margin-bottom: 64px; }
.patient-info .qa-block { margin-top: 48px; }
/* Everything in here is already inside the container, so it inherits the
   page's left edge and adds nothing of its own. These blocks used to carry a
   second max-width and a second 40px gutter, which is what set them 40px
   right of the eyebrow above them. */
.patient-info .detail-extra { padding-top: 0; padding-bottom: 0; }
.patient-info .qa-list { margin-left: 0; margin-right: 0; }

/* detail extras — editorial continuation blocks inside each speciality */
/* These blocks now sit on their own ground, between a white chapter above
   and a sage band below, so they need air at BOTH ends. The old 10px top was
   written when everything was one continuous cream and there was no edge to
   stand away from: it put the heading hard against the colour change while
   108px of white sat above it. Same value top and bottom, so the block reads
   as its own field rather than as the tail of the chapter before it. */
.detail-extra {
  padding-top: var(--sec);
  padding-bottom: var(--sec);
}
/* A chapter-close that is a direct child of a full-bleed section has no
   column of its own, so it is a container in its own right. It also carries
   the air beneath it: the sage band begins immediately after, and without
   this the closing line sat directly on the band's edge. */
.chapter-close-wrap { padding-bottom: var(--sec); }
.journey { padding-bottom: 30px; }


/* practical notes (dentistry) */
.notes__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 48px; }
/* Before your visit / After your treatment / When you need help. At 12px and
   regular weight these read as captions; they are the heads of their columns
   and sit one step below the 18px section label above them. */
.note__title {
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 14px;
}
.note p { font-size: 14.5px; color: var(--ink-soft); }

/* chapter endings — a quiet body-font closing line that ends a chapter.
   One sentence on one line: it is a closing remark, not a paragraph, and the
   560 measure broke it into two parts that read as a block of copy. The
   longest of them is the French orthodontics line at 948px, so it holds a
   single line down to about a 1024 window and wraps by itself below that. */
.chapter-close {
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink-soft);
  max-width: none;
  margin: 56px 0 0;
}

/* the orthodontic journey */
.journey__title, .qa-block__title, .understanding__title {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(22px, 2.4vw, 33px);
  line-height: 1.2;
  margin-bottom: 34px;
}
/* Blocks inside a band stay flush with the band's left edge. Centring them
   individually gave every block its own left — the title at one place, the
   paragraph at another, the columns at a third. The band is what is centred;
   what sits in it lines up. */
.journey__list { list-style: none; max-width: 900px; }
.journey__step {
  display: grid;
  grid-template-columns: 64px 230px 1fr;
  gap: 24px;
  align-items: baseline;
  padding: 15px 0;
}
.journey__num { font-size: 11px; letter-spacing: .3em; color: var(--ink-faint); }
.journey__name {
  font-size: 11.5px;
  font-weight: 400;
  letter-spacing: .22em;
  text-transform: uppercase;
}
.journey__desc { font-size: 15px; color: var(--ink-soft); }

/* patient questions — a quiet accordion, no boxes */
/* Each list is only as wide as its own longest question, so the markers line
   up in a column that sits just past the text rather than out at the page
   edge. Both blocks have a list now, so each finds its own column. */
.qa-list {
  width: max-content;
  max-width: 100%;
}
.qa__q {
  width: 100%;
  display: flex;
  /* Markers align on one vertical line per block, set by the longest question
     in that block — not by the width of the page, which put them 1237px from
     "Pijn". The list carries the width; the button fills it. */
  justify-content: space-between;
  align-items: baseline;
  gap: 30px;
  background: none;
  border: 0;
  cursor: pointer;
  padding: 13px 0;
  text-align: left;
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 500;
  color: var(--ink);
}
.qa__q:focus-visible { outline: 1px solid var(--ink); outline-offset: 4px; }
.qa__mark {
  font-family: var(--sans);
  font-weight: 300;
  font-size: 19px;
  color: var(--ink-faint);
  transition: transform .5s var(--ease-out);
}
.qa.is-open .qa__mark { transform: rotate(45deg); }
.qa__a {
  display: grid;
  /* The list is sized to max-content so the markers line up just past the
     longest question. Without this the collapsed answers set that width
     instead — the aftercare block measured 721 wide on questions no longer
     than "Voeding", putting its markers 534px out. Zero width contributes
     nothing to the intrinsic size; min-width fills the row once settled. */
  width: 0;
  min-width: 100%;
  grid-template-rows: 0fr;
  transition: grid-template-rows .6s var(--ease-out);
}
.qa.is-open .qa__a { grid-template-rows: 1fr; }
/* The answer keeps a reading width of its own. The list is deliberately as
   narrow as its questions so the markers line up close to them, and in the
   aftercare block that is only 217px — far too narrow to read a paragraph in.
   The inner overflows that column, which is harmless: .qa__a does not clip,
   and the hidden overflow here is what hides the text while the row is
   collapsed, not something that trims it sideways. */
.qa__a-inner {
  overflow: hidden;
  width: 640px;
  max-width: calc(100vw - 48px);
}
.qa__a p {
  padding: 2px 40px 24px 0;
  font-size: 15px;
  color: var(--ink-soft);
  max-width: 640px;
}

/* understanding sleep apnea */
.understanding__def { max-width: 640px; font-size: 15.5px; color: var(--ink-soft); }
.understanding__grid {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 70px;
  margin-top: 44px;
}
/* Symptoms / Treatment head their own columns under the serif title, so they
   carry the same weight as a section label rather than the 14px of a caption:
   at 14 in the soft ink they were quieter than the body copy beneath them. */
.understanding__col .mini-label {
  display: block;
  font-size: 18px;
  color: var(--ink);
  margin-bottom: 14px;
}
.understanding__t { font-size: 15px; color: var(--ink-soft); margin-bottom: 16px; max-width: 560px; }
.understanding__t:last-child { margin-bottom: 0; }

/* aftercare — the existing extraction instructions, folded into dentistry */
.aftercare .qa__q { font-size: 19px; padding: 13px 0; }
.after-list { list-style: none; max-width: 760px; }
.after-list li { font-size: 14.5px; color: var(--ink-soft); padding: 6px 0; }
.aftercare__close { margin-top: 28px; font-size: 14.5px; color: var(--ink-soft); max-width: 640px; }

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */

@media (max-width: 900px) {
  .patient-info { padding: 66px 0 62px; }
  .patient-info__eyebrow { margin-bottom: 44px; }
  .patient-info .qa-block { margin-top: 64px; }
  .contact__inner { grid-template-columns: 1fr; gap: 0; }
  .contact__col--intro,
  .contact__portrait,
  .contact__col--details,
  .contact__col--nav { grid-column: 1; grid-row: auto; }
  /* Stacked: no row to stretch to and no rule to meet, so the square comes
     back and the picture sizes itself again. */
  .contact__portrait { max-width: none; margin-top: 36px; align-self: start; }
  .contact__portrait img { height: auto; aspect-ratio: 1 / 1; }
  .contact__col--details,
  .contact__col--nav {
    border-left: 0;
    padding-left: 0;
    padding-right: 0;
    border-top: 1px solid var(--hairline);
    margin-top: 36px;
    padding-top: 32px;
  }
  .notes__grid { grid-template-columns: 1fr; gap: 36px; }
  .journey__step { grid-template-columns: 48px 1fr; row-gap: 4px; }
  .journey__desc { grid-column: 2; }
  .understanding__grid { grid-template-columns: 1fr; gap: 40px; }
}

/* The nav collapses to the burger well before the rest of the layout does.
   The brand (name plus the three disciplines beneath it), five nav items, the
   language switcher and the appointment button need about 1320px of window to
   sit on one line. French needs ~1400: its nav runs 86px longer than the
   English, and its tagline is the longest of the three. Everything hidden
   here is in the burger menu, including the Doctena link. */
@media (max-width: 1320px) {
  .nav { display: none; }
  .header__cta { display: none; }
  .menu-toggle { display: flex; }
  .mobile-menu.is-open { display: flex; }
  .brand, .lang, .menu-toggle { flex-shrink: 0; }
}

@media (max-width: 1080px) {
  .nav { display: none; }
  .header__cta { display: none; }
  /* the language selector stays visible between brand and hamburger;
     neither may shrink into the other */
  .brand, .lang, .menu-toggle { flex-shrink: 0; }
  .header__inner { gap: 14px; border-radius: 0; }
  /* full width and flush once the nav collapses to a burger */
  :root { --header-top: 0px; --header-inset: 0px; }
  .lang__btn { font-size: 10.5px; }
  .lang { gap: 7px; }
  .header__side { gap: 18px; }
  .menu-toggle { display: flex; }
  .mobile-menu.is-open { display: flex; }

  .hero__inner { grid-template-columns: 1fr; gap: 40px; }
  .hero__copy { max-width: 520px; }
  .hero__scrim {
    background:
      linear-gradient(90deg, rgba(243,238,229,.92) 0%, rgba(243,238,229,.68) 40%, rgba(243,238,229,.28) 68%, rgba(243,238,229,0) 92%),
      linear-gradient(0deg, rgba(243,238,229,1) 0%, rgba(243,238,229,0) 14%);
  }
  .hero { padding-bottom: 96px; }

  .services__grid { grid-template-columns: 1fr; }

  .about__inner { grid-template-columns: 1fr; gap: 60px; }
  /* stacked, there is no neighbouring column to stand clear of */
  .about__cv { padding-top: 0; padding-right: 0; }
  .about__creds { gap: 30px 40px; margin-top: 38px; }
  .cv-portrait { margin-top: 34px; }
  /* Stacked, the copy fills the column, so every stamp goes to the far side
     and sits at the same inset whichever way it was placed on desktop. */
  .stamp, .stamp--left, .stamp--right { left: auto; right: -180px; }

  /* Mobile reading order: number, title, PHOTOGRAPH, then the reading.
     Stacked, the copy block used to put a lead, a paragraph and a list between
     the title and the image, so the photograph only arrived after a wall of
     text. The copy is a single <div>, so it is flattened with display:contents
     and its children are ordered individually. Each already carries its own
     margin-bottom, hence gap:0 — the grid gap would otherwise space every
     paragraph 44px apart. */
  .detail-row,
  .detail-row--flip { grid-template-columns: 1fr; gap: 0;
                      padding-top: 80px; padding-bottom: 80px; }
  .detail-row__copy { display: contents; }
  /* One column here, so the desktop row/column placement is dropped and the
     order values below do the work again. */
  .detail-row__copy,
  .detail-row__media,
  .detail-row .provider { grid-column: auto; grid-row: auto; }
  .detail-row__num   { order: 1; }
  .detail-row__title { order: 2; margin-bottom: 30px; }
  .detail-row__media,
  .detail-row--flip .detail-row__media { order: 3; margin: 0 0 34px; }
  .detail-row__media { aspect-ratio: 4 / 3; min-height: 0; }
  .detail-row__lead  { order: 4; }
  .detail-row__text  { order: 5; }
  .detail-row .facts { order: 7; }
  /* display:contents flattens the copy here and every child is placed by hand.
     Without its own order the credit defaults to 0 and jumps above the
     section number and the heading. */
  .detail-row .provider { order: 8; }


}

@media (max-width: 640px) {
  :root { --header-h: 68px; }
  .brand__mark { width: 34px; height: 34px; }
  /* width:auto is load-bearing. The <img> carries width="1600" height="253", and an
     HTML width attribute is a presentational hint (= width:1600px). Setting only the
     height left that hint in force, so the wordmark rendered 1600px wide and 24px tall:
     stretched flat and 1300px off the side of a phone. Desktop never showed it because
     .brand__wordmark is display:none there. */
  /* Scales with the viewport because .brand cannot flex-shrink (see the rule above).
     At a fixed 24px the row needed 402px, so the burger hung 3px off a 375px iPhone. */
  /* The wordmark image has the three disciplines baked into it, and at the
     height that fits a phone they come out about 4px tall - present but
     unreadable, which is worse than absent. The line cannot be rescued here:
     there are 139px between the mark and the language switcher, and the
     tagline needs 199px in English and 230px in French even at 8px.

     So the phone gets the name as real text at a size you can actually read,
     and drops the tagline. The disciplines are still in the menu, in the
     services section and in the page title. */
  .brand__wordmark { display: none; }
  .brand__tagline { display: none; }
  .brand__name {
    display: block;
    font-size: clamp(10.5px, 3.1vw, 13px);
    letter-spacing: .18em;
  }
  .header__inner { gap: 12px; }
  .lang__btn { font-size: 9.5px; letter-spacing: .1em; }
  .lang__sep { font-size: 9px; }
  .lang { gap: 5px; }
  .header__side { gap: 14px; }
  /* Mobile recomposition: the photograph stands alone on top (face clear
     of any text or gradient), typography settles on cream below her chin,
     overlapping only the photo's soft lower fade. */
  .hero {
    min-height: auto;
    padding: calc(var(--header-h) + 12px) 0 0;
    flex-direction: column;
    align-items: stretch;
  }
  .hero__bg {
    position: relative;
    inset: auto;
    width: 100%;
    height: min(56vh, 430px);
  }
  .hero__bg img, .hero__bg video { object-position: 60% 18%; }
  /* The copy is pulled up 64px to sit on the photograph's faded lower edge,
     so that edge has to actually fade. The desktop scrim is written as
     `.hero__bg.has-video .hero__scrim` (0,3,0) and beats a bare
     `.hero__scrim` here (0,1,0) whatever the order, so the fade vanished the
     moment the film started and the picture ended in a hard line through the
     headline - fine on the still, broken on the film. Matching the
     specificity is the fix. */
  .hero__bg .hero__scrim,
  .hero__bg.has-video .hero__scrim {
    background: linear-gradient(0deg, rgba(243,238,229,1) 0%, rgba(243,238,229,.96) 6%, rgba(243,238,229,.55) 18%, rgba(243,238,229,0) 34%);
  }
  .hero__inner { margin-top: -64px; }
  .hero__title { font-size: clamp(26px, 7.4vw, 33px); }
  .hero__actions .btn { padding: 12px 26px; font-size: 10.5px; }
  .hero__location { line-height: 2; }
  .loc-sep--first { display: none; }
  .loc-street { display: block; }
  .hero__mail { letter-spacing: .22em; }
  .contact        { padding: 64px 0 0; }
  .contact--find  { padding: 62px 0 72px; }
  .contact__col--details,
  .contact__col--nav {
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid var(--hairline);
    margin-top: 34px;
    padding-top: 30px;
  }
  .addr-dot { display: inline; }
  .addr-br2 { display: none; }
  .services { display: none; }
  /* the "Smile." statement lives on desktop only — on mobile the tagline
     already carries that message */
  .quote { display: none; }
  .stamp { width: 300px; opacity: .05; }
  .contact { padding: 70px 0 80px; }
  .closing { padding: 90px 0; }
  .detail-row { padding-top: 64px; padding-bottom: 64px; }
}

@media (max-width: 360px) {
  .brand__mark { width: 30px; height: 30px; }
  .menu-toggle { width: 36px; }
  .lang__btn { font-size: 9px; }
  .lang { gap: 4px; }
  .header__inner { gap: 10px; }
}

/* ==========================================================================
   REDUCED MOTION — the quietest mode
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; animation: none !important; }
  html.js .reveal { opacity: 1; transform: none; }
  .intro { display: none; }
  .header { opacity: 1; transform: none; }
}

/* Wide screens need no special case any more. The container's max-width and
   its growing padding carry the page past 1700 on their own; the old block
   here switched to max-width:none and a separate --edge, which is how a
   third grid got into the site. */

/* ==========================================================================
   WRITING TO THE PRACTICE
   --------------------------------------------------------------------------
   On the sage, between the contact details and the closing band. The fields
   are rules, not boxes: a bordered input would be the first piece of chrome
   on the site and would read as a different website.
   ========================================================================== */

/* The form stays on the cream, continuous with the heading above it: one
   block for writing to the practice. The sage starts underneath, at the
   reference block, so the colour change marks the end of the conversation
   rather than interrupting it. */
.cform-band { padding: 76px 0 104px; }
.cform__eyebrow { display: block; margin-bottom: 18px; }
.cform__lead { font-size: 15px; color: var(--ink-soft); max-width: 560px; margin-bottom: 14px; }
/* Some people will never fill in a form. The address is offered before the
   fields rather than after them, so that choice costs nothing. */
.cform__alt { font-size: 15px; color: var(--ink-soft); margin-bottom: 52px; }
.cform__alt .c-link { color: var(--ink); }

.cform { position: relative; }
.cform__row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 34px; }
.cform__field { display: block; margin-bottom: 34px; }
.cform__label {
  display: block;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 10px;
}
.cform input,
.cform textarea {
  width: 100%;
  font-family: var(--sans);
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--ink);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  border-radius: 0;          /* iOS rounds inputs on its own */
  padding: 8px 0;
  transition: border-color .5s var(--ease-out);
}
.cform textarea { resize: vertical; min-height: 120px; }
.cform input:hover,
.cform textarea:hover { border-bottom-color: rgba(44, 38, 31, .38); }
.cform input:focus,
.cform textarea:focus { outline: none; border-bottom-color: var(--ink); }
/* The rule alone is not a focus indicator for anyone navigating by keyboard. */
.cform input:focus-visible,
.cform textarea:focus-visible { outline: 1px solid var(--ink); outline-offset: 5px; }
/* Only once it has been left and is wrong — not while it is being typed. */
.cform input:user-invalid,
.cform textarea:user-invalid { border-bottom-color: #8C3B2E; }

/* The honeypot. Off-screen rather than display:none, which some bots read. */
.cform__trap { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

.cform__foot {
  display: flex;
  align-items: center;
  gap: 40px;
  flex-wrap: wrap;
  margin-top: 10px;
}
.cform__send { flex-shrink: 0; cursor: pointer; }
.cform__send[disabled] { opacity: .5; cursor: default; }
.cform__note { font-size: 12.5px; line-height: 1.6; color: var(--ink-faint); max-width: 420px; }

/* The answer to the send, and it has to be unmissable. It used to sit under
   the button at the weight of the small print beside it, and it arrived at
   the same instant every field emptied - so the whole event read as "nothing
   happened and my text is gone". It now takes the sage, at the measure of
   the lead above the form, and the script brings it into view. */
.cform__status {
  margin-top: 30px;
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--ink);
  max-width: 560px;
  background: var(--ground);
  padding: 20px 24px;
  border-left: 2px solid var(--ink);
}
/* Nothing to say, nothing to show: no empty panel sitting under the button. */
.cform__status:empty {
  margin-top: 0;
  padding: 0;
  background: none;
  border-left: 0;
}
.cform__status.is-bad {
  color: #8C3B2E;
  border-left-color: #8C3B2E;
}
/* It is given focus so a screen reader lands on it, but it is a paragraph,
   not a control, and a ring around it would read as something to operate. */
.cform__status:focus { outline: none; }

/* ==========================================================================
   THE ANSWER TO THE SEND
   --------------------------------------------------------------------------
   Over the page, because a line in the flow loses to a whole page. A native
   dialog: the focus trap, the Escape key and the inertness of everything
   behind it are the browser's job, not ours.
   ========================================================================== */
.cdialog {
  border: 0;
  padding: 0;
  background: none;
  /* The reset zeroes every margin, which takes the browser's own centring of
     a modal dialog with it and leaves it in the top left corner. */
  margin: auto;
  max-width: min(440px, calc(100vw - 48px));
  color: var(--ink);
}
.cdialog::backdrop {
  background: rgba(44, 38, 31, .42);
}
.cdialog__panel {
  background: var(--cream-bright, #FFFFFF);
  padding: clamp(32px, 5vw, 44px);
  /* The sage down the edge, as on the bands: the practice answering. */
  border-left: 2px solid var(--ink);
}
.cdialog.is-bad .cdialog__panel { border-left-color: #8C3B2E; }
.cdialog__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(25px, 2.6vw, 32px);
  line-height: 1.2;
  margin: 0 0 16px;
}
.cdialog.is-bad .cdialog__title { color: #8C3B2E; }
.cdialog__text {
  font-size: 15.5px;
  line-height: 1.65;
  color: var(--ink-soft);
  margin: 0 0 32px;
}
.cdialog__close { display: inline-block; }

/* It arrives rather than appears. Nothing moves for anyone who asked for
   less motion - it is simply there.

   No fill-mode, deliberately. "both" holds the opening frame until the
   animation runs, and the opening frame is opacity 0 - so anything that
   stops the animation starting leaves the one message the patient has to
   see invisible. Without it the dialog's resting state is simply visible,
   and the animation is decoration that can fail without taking the message
   with it. */
@media (prefers-reduced-motion: no-preference) {
  .cdialog[open] { animation: cdialog-in .42s var(--ease-film); }
  .cdialog[open]::backdrop { animation: cdialog-fade .42s var(--ease-film); }
}
/* Position only, never opacity. A fade has a frame at zero, and anything
   that stalls the animation - a throttled or background tab, a stepped-on
   frame - parks the message there and the patient is told nothing. Moving
   it cannot make it invisible: the worst a stalled transform can do is
   leave it 14px low. */
@keyframes cdialog-in {
  from { transform: translateY(14px); }
  to   { transform: none; }
}
@keyframes cdialog-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (max-width: 900px) {
  .cform-band { padding: 54px 0 72px; }
  .cform__row { grid-template-columns: 1fr; gap: 0; }
  .cform__lead { margin-bottom: 38px; }
  .cform__foot { gap: 24px; }
  /* 16px or iOS zooms the page in the moment the field is focused. */
  .cform input,
  .cform textarea { font-size: 16px; }
}

/* ==========================================================================
   LEGAL PAGES — privacy, cookies, legal information
   --------------------------------------------------------------------------
   Three standalone pages. Each carries all three languages in the markup and
   shows one, so the text is there whether or not the script runs.
   ========================================================================== */
.legal { padding: calc(var(--header-h) + 70px) 0 0; }
/* The legal pages sit on the same container as the home page, so their text
   starts on the same line. The narrow measure is capped on the content, not
   on the box. */
.legal__inner > * { max-width: 820px; }
.legal__eyebrow { display: block; margin-bottom: 20px; }

/* The diploma, filed where the professional title is stated. Modest: it is
   evidence on a legal page, not a display piece. Mounted on the sage so the
   scan has a ground — a white document on a cream page floats. */
.legal__diploma {
  margin: 26px 0 34px;
  max-width: 520px;
  background: var(--ground);
  padding: clamp(18px, 4%, 30px);
}
.legal__diploma picture { display: block; }
.legal__diploma img { display: block; width: 100%; height: auto; }
.legal__diploma figcaption {
  margin-top: 16px;
  font-size: 10.5px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-align: center;
}
.legal h1 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(30px, 3.2vw, 46px);
  line-height: 1.15;
  margin-bottom: 14px;
}
.legal__meta {
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 8px;
}
.legal__intro { font-size: 16px; margin: 22px 0 10px; max-width: 640px; }
.legal h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(19px, 1.8vw, 25px);
  line-height: 1.25;
  margin: 52px 0 14px;
}
.legal h3 {
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 26px 0 10px;
}
.legal p, .legal li { color: var(--ink-soft); max-width: 640px; }
.legal p { margin-bottom: 14px; }
.legal ul { list-style: none; margin: 0 0 18px; }
.legal li { position: relative; padding-left: 20px; margin-bottom: 9px; }
.legal li::before {
  content: "";
  position: absolute;
  left: 2px; top: 12px;
  width: 6px; height: 1px;
  background: var(--ink-faint);
}
.legal a { color: var(--ink); text-underline-offset: 3px; }

/* what the site stores, as a plain two-column list */
.legal__store { border-top: 1px solid var(--hairline); margin: 18px 0 24px; max-width: 640px; }
.legal__store div {
  display: grid;
  grid-template-columns: 150px 1fr;
  gap: 24px;
  padding: 14px 0;
  border-bottom: 1px solid var(--hairline);
}
.legal__store dt { font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink); }
.legal__store dd { font-size: 15px; color: var(--ink-soft); }

/* anything the practice still has to supply is marked, not invented */
.todo {
  background: rgba(44, 38, 31, .07);
  border-bottom: 1px dashed var(--ink-faint);
  padding: 1px 5px;
  color: var(--ink);
  font-style: normal;
}

.legal__foot {
  border-top: 1px solid var(--hairline);
  margin-top: 72px;
  padding: 30px 0 90px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px 34px;
  font-size: 11.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
}
.legal__foot a { color: var(--ink-faint); text-decoration: none; }
.legal__foot a:hover, .legal__foot a[aria-current] { color: var(--ink); }

@media (max-width: 640px) {
  .legal { padding-top: calc(var(--header-h) + 40px); }
  .legal__store div { grid-template-columns: 1fr; gap: 4px; }
}
