/* =========================================================================
   Tandlægerne Rask & Bach – stylesheet
   One file. No framework. Tokens first, then base, then components.
   ========================================================================= */

/* -------------------------------------------------------------------------
   1. Tokens
   ---------------------------------------------------------------------- */
:root {
  /* Ground. The cool step is deep enough that the alternation reads
     from across the room, not only side by side. */
  --paper:        #ffffff;
  --paper-cool:   #e8efed;
  --paper-warm:   #fafcfb;

  /* Ink. Darkened one step so it still clears 4.5:1 on the deeper ground. */
  --ink:          #16201f;   /* near-black, faint green cast */
  --ink-2:        #47534f;   /* secondary prose (8.0:1 white / 6.9:1 cool) */
  --ink-3:        #5e6b67;   /* meta (5.6:1 white / 4.8:1 cool) */
  --line:         #d5dedb;   /* hairline */
  --line-strong:  #b6c4c0;

  /* Brand teal, three steps */
  --teal:         #00aca1;   /* the logo arc. Fills and rules, not text. */
  --teal-text:    #00706a;   /* 6.0:1 white / 5.1:1 cool – links */
  --teal-deep:    #0a5c57;   /* the committed band, white text 7.2:1 */
  --teal-tint:    #e3f1ef;   /* row hover */

  /* On the deep band */
  --on-deep:      #ffffff;
  --on-deep-2:    #cfe4e1;   /* tinted from the hue, never gray */
  --deep-line:    #2b6f6a;

  /* Depth. One token, tinted from the brand hue. Physical things only:
     the photograph and the map. Controls stay flat. */
  --lift: 0 22px 44px -22px rgba(8, 46, 43, 0.30),
          0 4px 12px -8px  rgba(8, 46, 43, 0.18);

  /* Ink footer */
  --floor:        #16201f;
  --on-floor:     #ffffff;
  --on-floor-2:   #9aa8a4;
  --floor-line:   #2c3a37;

  /* Type */
  --display: "Familjen Grotesk", "Helvetica Neue", Arial, sans-serif;
  --text:    "Schibsted Grotesk", "Helvetica Neue", Arial, sans-serif;

  /* Measure */
  --measure: 68ch;

  /* Geometry */
  --r:      3px;
  --r-lg:   6px;
  --gutter: clamp(1.25rem, 4vw, 2.5rem);
  --page:   1240px;
  --band:   clamp(4.5rem, 10vw, 9.5rem);   /* vertical section rhythm */

  /* Motion. --ease for feedback, --ease-out for arrivals. */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* -------------------------------------------------------------------------
   2. Reset + base
   ---------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 7.5rem;   /* clears the sticky header, plus air */
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--text);
  font-size: 1.0625rem;      /* 17px */
  line-height: 1.65;
  font-feature-settings: "kern" 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 500;
  margin: 0;
  text-wrap: balance;
  color: var(--ink);
}

/* The display steps carry the register. Body copy deliberately does not
   move with them – widening the interval is what reads as conviction. */
h1 {
  font-size: clamp(2.85rem, 1.5rem + 5.6vw, 6rem);
  line-height: 0.97;
  letter-spacing: -0.038em;
}

h2 {
  font-size: clamp(2.1rem, 1.45rem + 2.7vw, 3.75rem);
  line-height: 1.04;
  letter-spacing: -0.032em;
}

h3 {
  font-family: var(--text);
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: -0.005em;
}

p { margin: 0 0 1.1em; max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

a { color: var(--teal-text); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { color: var(--ink); }

strong { font-weight: 600; }
address { font-style: normal; }

/* Copying the address or the phone number should still feel like the site.
   Forcing the ink colour keeps it legible on the dark grounds too. */
::selection { background: #a5e6df; color: var(--ink); }

/* Announced, not shown. */
.vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

:focus-visible {
  outline: 2px solid var(--teal-text);
  outline-offset: 3px;
  border-radius: 2px;
}
/* The ring has to be visible on the dark grounds too. */
.section--deep :focus-visible,
.site-footer :focus-visible { outline-color: #fff; }

.skip {
  position: absolute;
  left: 1rem; top: -4rem;
  z-index: 100;
  padding: 0.75rem 1.25rem;
  background: var(--ink);
  color: #fff;
  border-radius: var(--r);
  text-decoration: none;
  transition: top 0.15s var(--ease);
}
.skip:focus { top: 1rem; color: #fff; }

/* -------------------------------------------------------------------------
   3. Layout primitives
   ---------------------------------------------------------------------- */
.wrap {
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--band); }

.section--cool  { background: var(--paper-cool); }

/* The booking band is a counter, not a chapter: it runs shorter than the
   reading sections so it reads as an action, not a stop. */
.section--deep  {
  background: var(--teal-deep);
  color: var(--on-deep);
  padding-block: clamp(3.25rem, 6vw, 5rem);
}

.section--deep h2,
.section--deep h3 { color: var(--on-deep); }
.section--deep p  { color: var(--on-deep-2); }

/* Heading + lead pair. More space above a heading than below it. */
.lede {
  max-width: 46ch;
  margin-top: 1.5rem;
  font-size: clamp(1.125rem, 1.02rem + 0.55vw, 1.5rem);
  line-height: 1.45;
  letter-spacing: -0.012em;
  color: var(--ink-2);
}
.section--deep .lede { color: var(--on-deep-2); }

.section__head { margin-bottom: clamp(3rem, 6vw, 5rem); }

/* -------------------------------------------------------------------------
   4. Buttons + link marks
   ---------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 2.75rem;        /* 44px touch target */
  padding: 0.85rem 1.4rem;
  border: 1px solid transparent;
  border-radius: var(--r);
  font-family: var(--text);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.005em;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.16s var(--ease), color 0.16s var(--ease),
              border-color 0.16s var(--ease);
}

/* Primary: the logo's teal, dark ink on top (5.8:1) */
.btn--primary {
  background: var(--teal);
  color: var(--ink);
}
.btn--primary:hover,
.btn--primary:focus-visible {
  background: var(--teal-deep);
  color: #fff;
}

/* Primary sitting on the deep teal band: invert */
.section--deep .btn--primary {
  background: var(--teal);
  color: var(--ink);
}
.section--deep .btn--primary:hover,
.section--deep .btn--primary:focus-visible {
  background: #fff;
  color: var(--teal-deep);
}

.btn--quiet {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
}
.btn--quiet:hover,
.btn--quiet:focus-visible {
  border-color: var(--ink);
  color: var(--ink);
  background: transparent;
}

.btn--sm { padding: 0.6rem 1rem; font-size: 0.9375rem; }

/* The one action the page is built around, at the size it deserves. */
.btn--lg {
  padding: 1.15rem 2rem;
  font-size: 1.125rem;
  min-height: 3.5rem;
}

/* The arrow leaves with you. Small enough to still satisfy on the
   hundredth visit, never in the way of the click. */
.btn svg {
  flex: none;
  transition: transform 0.22s var(--ease);
}
.btn:hover svg,
.btn:focus-visible svg { transform: translate(2px, -2px); }

.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.875rem 1.25rem;
  margin-top: 2rem;
}

/* Phone shown as a real, tappable number, not a button */
.tel {
  display: inline-flex;
  align-items: flex-start;   /* icon keeps the first line when the label wraps */
  gap: 0.5rem;
  font-weight: 600;
  font-size: 1.0625rem;
  color: var(--ink);
  text-decoration: none;
  padding-block: 0.85rem;
}
.tel:hover { color: var(--teal-text); }
.tel svg { color: var(--teal-text); margin-top: 0.3em; }
.section--deep .tel { color: #fff; }
.section--deep .tel svg { color: var(--teal); }
.section--deep .tel:hover { color: var(--on-deep-2); }

/* -------------------------------------------------------------------------
   5. Header
   ---------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, 0.97);
  box-shadow: 0 1px 0 transparent;
  transition: box-shadow 0.25s var(--ease);
}
/* Feedback that you have left the top of the page, not decoration. */
.site-header[data-stuck="true"] {
  box-shadow: 0 1px 0 var(--line), 0 10px 22px -20px rgba(8, 46, 43, 0.30);
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: 6.5rem;
}

.brand { display: block; flex: none; line-height: 0; }
.brand img { width: 184px; }

.nav { position: relative; margin-left: auto; }

.nav__list {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav__link {
  display: block;
  position: relative;
  padding: 0.5rem 0.75rem;
  font-size: 1rem;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  border-radius: var(--r);
  transition: color 0.16s var(--ease);
}
.nav__link:hover { color: var(--ink); }

/* The mark for the section you are reading is the arc from the wordmark, not
   a straight rule. There is exactly one of it, and it travels: the brand's
   single gesture moves with you down the page rather than five separate marks
   blinking on and off. Continuity, not decoration.

   It arrives by drawing itself open from the centre (clip-path), the way a
   stroke is made. Squashing it with scaleX would flatten the curve. */
.nav__mark {
  position: absolute;
  left: 0;
  bottom: 0.05rem;
  width: 0;
  height: 0.45rem;
  border-bottom: 2px solid var(--teal);
  border-radius: 0 0 50% 50% / 0 0 100% 100%;
  opacity: 0;
  clip-path: inset(0 50% 0 50%);
  pointer-events: none;
  transition:
    transform 0.42s var(--ease-out),
    width     0.42s var(--ease-out),
    clip-path 0.34s var(--ease-out),
    opacity   0.22s linear;
}
.nav__mark[data-on="true"] {
  opacity: 1;
  clip-path: inset(0 0 0 0);
}

.nav__link[aria-current="true"] { color: var(--ink); }

/* Per-link marks are the mobile fallback only – the travelling mark needs a
   horizontal row to travel along. */
.nav__link::after { content: none; }

.header__cta { display: flex; align-items: center; gap: 2rem; flex: none; }
.header__tel { font-size: 1rem; padding-block: 0; }

.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-width: 2.75rem;
  min-height: 2.75rem;
  margin-left: auto;
  padding: 0.55rem 0.85rem;
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  font-family: var(--text);
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}

/* -------------------------------------------------------------------------
   6. Hero
   ---------------------------------------------------------------------- */
.hero {
  padding-top: clamp(3rem, 6vw, 6rem);
  padding-bottom: clamp(3.5rem, 7vw, 6.5rem);
  overflow-x: clip;            /* contains the bleed below */
}

.hero__grid {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4.5rem);
  align-items: center;
}

.hero__copy p { max-width: 40ch; }

.hero__figure { position: relative; margin: 0; }
.hero__figure img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r-lg);
  box-shadow: var(--lift);
}

/* -------------------------------------------------------------------------
   6b. The focal moment

   The page's one authored sequence. The headline wipes up out of its own
   baseline and the room opens left to right like an aperture, so the first
   thing the site does is show you the space. It runs once, on load, and
   only for a browser that got as far as the inline script in <head> – the
   .js gate means a failed or blocked script leaves the hero fully visible
   rather than blank.

   Clip-path rather than the usual fade-and-rise: a wipe is a typographic
   move, and the aperture is the room's own behaviour.
   ---------------------------------------------------------------------- */
@keyframes rb-wipe-up {
  from { clip-path: inset(100% 0 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

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

@keyframes rb-settle {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}

/* `backwards`, not `both`: the clip holds the start state through the delay,
   then drops off the element entirely when the sequence ends. Leaving an
   inset(0) parked on the headline would shave the tall glyphs, and would
   clip the photograph's own shadow. */
.js .hero__copy h1 {
  animation: rb-wipe-up 0.62s var(--ease-out) 0.04s backwards;
}
.js .hero__figure {
  animation: rb-aperture 0.72s var(--ease-out) 0.1s backwards;
}
.js .hero__copy .lede {
  animation: rb-settle 0.42s var(--ease-out) 0.26s backwards;
}
.js .hero__copy .actions {
  animation: rb-settle 0.42s var(--ease-out) 0.34s backwards;
}

/* -------------------------------------------------------------------------
   6c. Scroll reveals

   Same three materials as the hero – wipe, aperture, rise – so the page
   reveals in one vocabulary instead of a bolt-on fade-up. Headings wipe,
   photographic things open, everything else rises.

   Lists stagger because they are lists (four practical columns, six price
   groups, twelve staff). Section headings never stagger: one move.

   Gated on `.reveal`, which rb.js sets as its first statement. If the script
   never loads, or dies before that line, nothing is hidden. Each element
   reveals once and is then unobserved; `backwards` fill means no clip-path
   is left parked on a heading to shave its glyphs.
   ---------------------------------------------------------------------- */
@keyframes rb-rise {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

/* The hidden state is opacity, never clip-path – and that is load-bearing.
   IntersectionObserver measures the *clipped* rect, so an element sitting at
   `clip-path: inset(100%)` has zero intersecting area, never reports as
   intersecting, and would hang there invisible forever. Opacity does not
   clip, so the box stays observable. The clip lives only in the keyframes,
   which run after the class has already been added. */
.reveal [data-anim] { opacity: 0; }
.reveal [data-anim].is-in { opacity: 1; clip-path: none; }

.reveal [data-anim="rise"].is-in {
  animation: rb-rise 0.55s var(--ease-out) var(--d, 0ms) backwards;
}
.reveal [data-anim="wipe"].is-in {
  animation: rb-wipe-up 0.62s var(--ease-out) var(--d, 0ms) backwards;
}
.reveal [data-anim="aperture"].is-in {
  animation: rb-aperture 0.72s var(--ease-out) var(--d, 0ms) backwards;
}

/* -------------------------------------------------------------------------
   7. Practical strip – hours, phone, address, acute
   ---------------------------------------------------------------------- */
.practical { background: var(--paper-cool); border-block: 1px solid var(--line); }

.practical__grid {
  display: grid;
  gap: 0;
  padding-block: clamp(2rem, 4vw, 3rem);
}

/* Stacked by default: a rule between each pair. */
.practical__col + .practical__col {
  border-top: 1px solid var(--line);
  padding-top: clamp(1.75rem, 3vw, 2.25rem);
  margin-top: clamp(1.75rem, 3vw, 2.25rem);
}

.practical h3 { margin-bottom: 0.9rem; }

.hours {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.4rem 1.5rem;
  margin: 0;
  font-size: 1.0625rem;
  font-variant-numeric: tabular-nums;
}
.hours dt { color: var(--ink-3); }
.hours dd { margin: 0; color: var(--ink); font-weight: 500; }

/* Matches the hours sitting next to it in the same strip. The address and
   the out-of-hours number are read, not skimmed. */
.practical p { font-size: 1.0625rem; line-height: 1.55; color: var(--ink-2); }
.practical .tel { padding-block: 0.25rem; }

.acute {
  font-size: 1.0625rem;
}
.acute strong { display: block; color: var(--ink); }
/* An out-of-hours number split across two lines is a number you misdial. */
.acute a { white-space: nowrap; }

/* -------------------------------------------------------------------------
   8. Booking band
   ---------------------------------------------------------------------- */
.booking__grid {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4.5rem);
  align-items: start;
}

.booking__aside {
  padding-top: 1.5rem;
  border-top: 2px solid var(--teal);   /* the logo's teal, used structurally */
}
.booking__aside h3 { margin-bottom: 0.6rem; }
.booking__aside p { font-size: 0.9375rem; }
.booking__aside a { color: #fff; }
.booking__aside a:hover { color: var(--on-deep-2); }

/* -------------------------------------------------------------------------
   9. Prose columns – klinikken
   ---------------------------------------------------------------------- */
/* Asymmetric intro: heading block beside the main passage */
.split {
  display: grid;
  gap: clamp(2rem, 4vw, 3.5rem) clamp(2.5rem, 5vw, 5rem);
  align-items: start;
}

/* This is a sentence of body copy, not a caption. It reads at body size in
   the body colour; the step down from the lede is enough to rank it. */
.split__aside {
  margin-top: 1.5rem;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--ink-2);
  max-width: 46ch;
}

.topics-row {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-top: clamp(2.5rem, 5vw, 4rem);
  border-top: 1px solid var(--line);
}

.prose-cols {
  display: grid;
  gap: clamp(2rem, 4vw, 3.5rem) clamp(2.5rem, 5vw, 4.5rem);
  align-items: start;
}

/* Prose sub-headings take the display face, same step as the price groups,
   so the page has one heading language instead of two. */
.topic h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 1.5rem;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin-bottom: 0.9rem;
}
.topic p { color: var(--ink-2); max-width: 58ch; }

/* -------------------------------------------------------------------------
   10. Price list
   ---------------------------------------------------------------------- */
.prices { display: grid; gap: clamp(2rem, 4vw, 3rem) clamp(3rem, 6vw, 5rem); }

.price-group h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 1.5rem;
  letter-spacing: -0.02em;
  padding-bottom: 0.85rem;
  border-bottom: 2px solid var(--ink);
}

.price-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.5rem 2rem;
  align-items: baseline;
  padding: 0.95rem 0.75rem;
  margin-inline: -0.75rem;
  border-bottom: 1px solid var(--line);
  border-radius: var(--r);
  transition: background-color 0.14s var(--ease);
}
.price-row:last-child { border-bottom: 0; }

/* Scanning a two-column list, the eye loses the row between name and
   number. The tint carries it across. */
.price-row:hover { background: var(--teal-tint); }

/* What you are actually buying. It carries as much of the decision as the
   number does, so it reads at body size, not caption size. */
.price-row__what {
  font-size: 1.0625rem;
  line-height: 1.55;
  color: var(--ink-2);
  margin: 0;
  max-width: 46ch;
}
.price-row__what--only { color: var(--ink); }

.price-row__amount {
  font-family: var(--display);
  font-size: 1.375rem;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--ink);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.prices__note {
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-top: 1.25rem;
  border-top: 1px solid var(--line-strong);
}
.prices__note p {
  font-size: 0.9375rem;
  color: var(--ink-3);
  max-width: 62ch;
}

/* -------------------------------------------------------------------------
   11. Team
   ---------------------------------------------------------------------- */
.team {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: clamp(2.5rem, 4.5vw, 3.75rem) clamp(1.5rem, 2.5vw, 2.5rem);
  list-style: none;
  margin: 0;
  padding: 0;
}

.member img {
  width: 100%;
  max-width: 150px;
  aspect-ratio: 1;
  border-radius: 50%;
  /* No placeholder fill: the portraits are cut-outs, and a tinted disc
     shows through their soft edges as a halo. */
}

/* Names take the display face, same as every other heading on the page.
   Two lines reserved so the roles line up across a row. */
.member h3 {
  font-family: var(--display);
  font-weight: 500;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin-top: 1.15rem;
  min-height: 2.3em;
}

.member p {
  margin: 0.4rem 0 0;
  font-size: 0.9375rem;
  line-height: 1.45;
  color: var(--ink-3);
}

/* -------------------------------------------------------------------------
   12. Contact + map
   ---------------------------------------------------------------------- */
.contact__grid {
  display: grid;
  gap: clamp(2rem, 4vw, 3.5rem);
  align-items: start;
}

.contact__details h3 { margin-bottom: 0.6rem; }
.contact__details p { font-size: 1rem; color: var(--ink-2); }
.contact__details address { font-style: normal; }

.contact__block + .contact__block {
  margin-top: 1.75rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--line);
}

.map {
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--paper-cool);
  box-shadow: var(--lift);
}
.map iframe {
  display: block;
  width: 100%;
  height: clamp(320px, 45vw, 460px);
  border: 0;
  filter: saturate(0.55) contrast(0.95);
}

/* -------------------------------------------------------------------------
   13. Footer
   ---------------------------------------------------------------------- */
.site-footer {
  background: var(--floor);
  color: var(--on-floor-2);
  padding-block: clamp(3.5rem, 7vw, 6rem) 2.5rem;
}

.site-footer h3 { color: var(--on-floor); margin-bottom: 0.9rem; }
.site-footer p { color: var(--on-floor-2); font-size: 0.9375rem; }
.site-footer a { color: var(--on-floor); }
.site-footer a:hover { color: var(--teal); }

.footer__grid {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
  align-items: start;
}

.footer__block + .footer__block { margin-top: 2.25rem; }

.site-footer .hours dt { color: var(--on-floor-2); }
.site-footer .hours dd { color: var(--on-floor); }

.footer__links { margin: 0; padding: 0; list-style: none; font-size: 0.9375rem; }
.footer__links li + li { margin-top: 0.55rem; }

.footer__base {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  justify-content: space-between;
  margin-top: clamp(3rem, 6vw, 5rem);
  padding-top: 1.5rem;
  border-top: 1px solid var(--floor-line);
  font-size: 0.8125rem;
  color: var(--on-floor-2);
}

/* -------------------------------------------------------------------------
   14. Breakpoints
   ---------------------------------------------------------------------- */
@media (min-width: 40em) {
  /* Two up: rule between the rows only. */
  .practical__grid {
    grid-template-columns: repeat(2, 1fr);
    column-gap: clamp(1.5rem, 4vw, 3rem);
  }
  .practical__col + .practical__col {
    border-top: 0;
    padding-top: 0;
    margin-top: 0;
  }
  .practical__col:nth-child(n + 3) {
    border-top: 1px solid var(--line);
    padding-top: clamp(1.75rem, 3vw, 2.25rem);
    margin-top: clamp(1.75rem, 3vw, 2.25rem);
  }

  .prose-cols { grid-template-columns: repeat(2, 1fr); }
  .prices { grid-template-columns: repeat(2, 1fr); }
  .team { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
}

@media (min-width: 60em) {
  /* The room runs off the right edge of the screen instead of sitting in a
     column. Same composition, said at full volume. */
  .hero__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr); }
  .hero__figure {
    margin-right: calc(-1 * (max(0px, 100vw - var(--page)) / 2 + var(--gutter)));
  }
  .hero__figure img {
    aspect-ratio: 5 / 4;
    border-radius: var(--r-lg) 0 0 var(--r-lg);
  }

  /* Four up: vertical rules, first column flush with the page edge. */
  .practical__grid { grid-template-columns: repeat(4, 1fr); column-gap: 0; }
  .practical__col:nth-child(n + 3) {
    border-top: 0;
    padding-top: 0;
    margin-top: 0;
  }
  .practical__col + .practical__col {
    border-left: 1px solid var(--line);
    padding-inline-start: clamp(1.5rem, 2.5vw, 2.5rem);
  }
  .practical__col:not(:last-child) { padding-inline-end: clamp(1.5rem, 2.5vw, 2.5rem); }

  .booking__grid { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); }
  .split { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); }
  .contact__grid { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); }
  .footer__grid { grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.8fr) minmax(0, 0.8fr); }
}

/* Mobile navigation */
@media (max-width: 59.999em) {
  .nav-toggle { display: inline-flex; }

  /* Full-screen panel. It sits under the sticky header (z-50) so the logo,
     the booking button and the close control stay put and stay usable, and
     over everything else so no page content shows through.

     visibility rather than display, so it can be transitioned; it is delayed
     out on close so the wipe finishes before the panel leaves the tree. */
  /* z-index here is relative to the header's own stacking context, not the
     page: the panel is a descendant of .site-header (z-50), so it already
     covers everything outside the header. Inside it, the logo, the booking
     button and the close control must out-stack the panel or the menu
     cannot be closed. */
  .nav {
    position: fixed;
    inset: 0;
    z-index: 1;
    background: var(--paper);
    padding: 6.75rem var(--gutter) 2rem;
    overflow-y: auto;
    overscroll-behavior: contain;
    visibility: hidden;
    opacity: 0;
    clip-path: inset(0 0 100% 0);
    transition:
      opacity 0.24s var(--ease-out),
      clip-path 0.42s var(--ease-out),
      visibility 0s linear 0.42s;
  }
  .nav[data-open="true"] {
    visibility: visible;
    opacity: 1;
    clip-path: inset(0 0 0 0);
    transition:
      opacity 0.18s linear,
      clip-path 0.42s var(--ease-out),
      visibility 0s;
  }

  .nav__list { flex-direction: column; align-items: stretch; gap: 0; }

  /* Menu type takes the display face, like every other heading here. */
  .nav__link {
    font-family: var(--display);
    font-size: clamp(1.5rem, 6vw, 2rem);
    font-weight: 500;
    letter-spacing: -0.02em;
    padding: 0.7rem 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0;
    opacity: 0;
    transform: translateY(12px);
    transition:
      opacity 0.3s var(--ease-out),
      transform 0.38s var(--ease-out),
      color 0.16s var(--ease);
  }
  .nav[data-open="true"] .nav__link { opacity: 1; transform: none; }

  /* The links follow the panel in, one after the other. */
  .nav[data-open="true"] li:nth-child(1) .nav__link { transition-delay: 0.12s; }
  .nav[data-open="true"] li:nth-child(2) .nav__link { transition-delay: 0.17s; }
  .nav[data-open="true"] li:nth-child(3) .nav__link { transition-delay: 0.22s; }
  .nav[data-open="true"] li:nth-child(4) .nav__link { transition-delay: 0.27s; }
  .nav[data-open="true"] li:nth-child(5) .nav__link { transition-delay: 0.32s; }
  /* Stacked menu: the mark cannot travel, so each link carries its own and
     draws it left to right. */
  .nav__mark { display: none; }

  .nav__link::after {
    content: "";
    position: absolute;
    left: 0;
    right: auto;
    width: 2.25rem;
    height: 0.4rem;
    bottom: 0.35rem;
    border-bottom: 2px solid var(--teal);
    border-radius: 0 0 50% 50% / 0 0 100% 100%;
    clip-path: inset(0 100% 0 0);
    transition: clip-path 0.34s var(--ease-out);
  }
  .nav[data-open="true"] .nav__link[aria-current="true"]::after {
    clip-path: inset(0 0 0 0);
    transition-delay: 0.42s;
  }

  .site-header { position: sticky; }
  .site-header__inner { position: relative; min-height: 5.25rem; gap: 0.75rem; }
  .brand img { width: 148px; }
  .header__cta .header__tel { display: none; }

  /* Booking button and menu control travel together to the right edge, clear
     of the wordmark. */
  .header__cta { order: 3; margin-left: auto; }
  .nav-toggle { order: 4; margin-left: 0; }

  /* Above the panel, so they stay visible and tappable while it is open. */
  .brand,
  .header__cta,
  .nav-toggle { position: relative; z-index: 2; }

  /* Hamburger out, cross in. */
  .nav-toggle__close { display: none; }
  .nav-toggle[aria-expanded="true"] .nav-toggle__open { display: none; }
  .nav-toggle[aria-expanded="true"] .nav-toggle__close { display: block; }

  /* The page behind must not scroll while the panel is over it. `overflow:
     hidden` alone lets the viewport clamp the scroll offset, so closing the
     menu drops the visitor hundreds of pixels from where they opened it.
     Fixing the body holds the position; JS carries the offset in `top`.
     The header is pinned explicitly, because `sticky` has nothing to stick
     to once the body has stopped scrolling. */
  body.nav-open {
    position: fixed;
    left: 0;
    right: 0;
    width: 100%;
    overflow: hidden;
  }
  body.nav-open .site-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
  }
}

/* Narrow phones: the booking button stays, the menu shrinks to its icon.
   Losing the primary action to save 40px would be the wrong trade. */
@media (max-width: 27em) {
  .site-header__inner { gap: 0.5rem; }
  .brand img { width: 132px; }   /* the widest that clears the gutter at 320px */
  .nav-toggle { padding: 0.55rem 0.7rem; }
  .nav-toggle__label { display: none; }
  .header__cta .btn { padding-inline: 0.8rem; }
}

/* Narrow phones: the price sits under what it buys, not squeezed beside it. */
@media (max-width: 34.999em) {
  .price-row { grid-template-columns: 1fr; gap: 0.2rem; }
  .price-row__amount { white-space: normal; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0.001ms !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0.001ms !important;
  }
  /* Land on the finished state, do not play toward it. */
  .js .hero__copy h1,
  .js .hero__figure,
  .js .hero__copy .lede,
  .js .hero__copy .actions { animation: none !important; }

  /* Nothing is ever hidden waiting for a scroll that will not animate. */
  .reveal [data-anim] {
    opacity: 1 !important;
    clip-path: none !important;
    animation: none !important;
    transform: none !important;
  }
}

/* -------------------------------------------------------------------------
   15. Print

   People print a dentist's price list, and they write the opening hours on
   the fridge. Neither should come out as a wall of teal with a dead button
   in the middle of it. Screen chrome drops away, the paper keeps the facts,
   and nothing gets cut in half across a page break.
   ---------------------------------------------------------------------- */
@media print {
  *, *::before, *::after {
    background: transparent !important;
    color: #000 !important;
    box-shadow: none !important;
    text-shadow: none !important;
  }

  html { font-size: 10.5pt; scroll-padding-top: 0; }
  body { line-height: 1.4; }

  @page { margin: 16mm 14mm; }

  /* Screen-only furniture. */
  .skip,
  .nav,
  .nav-toggle,
  .header__cta,
  .hero__figure,
  .map,
  .booking__aside,
  .footer__block--links,
  .btn { display: none !important; }

  .site-header { position: static; }
  .site-header__inner { min-height: 0; padding-block: 0 0.5rem; }
  .brand img { width: 46mm; }

  /* Paper has its own rhythm. */
  .section, .hero { padding-block: 0.9rem !important; }
  .section--cool, .section--deep, .site-footer, .practical {
    border-top: 0.4pt solid #999;
  }
  .section__head { margin-bottom: 0.9rem; }
  .lede { margin-top: 0.5rem; }

  h1 { font-size: 22pt; line-height: 1.05; }
  h2 { font-size: 14pt; line-height: 1.15; }
  .lede { font-size: 10.5pt; }
  .price-group h3, .topic h3 { font-size: 11.5pt; }
  .price-row__amount { font-size: 10.5pt; }
  .member h3 { font-size: 10pt; min-height: 0; }
  .hours { font-size: 10pt; }

  /* Nothing splits across a page. */
  .price-group,
  .member,
  .practical__col,
  .topic,
  .contact__block { break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }

  /* Portraits cost toner; keep the faces, lose the poster size. */
  .member img { max-width: 22mm; }
  .team { gap: 0.9rem; }

  .price-group h3 { border-bottom-width: 1pt; }
  .price-row { border-bottom: 0.4pt solid #bbb; padding-block: 0.35rem; }

  a { text-decoration: underline; }

  /* The booking address is useless as a button on paper and essential as
     a line of text. */
  .print-url { display: block !important; font-size: 10pt; }
}

.print-url { display: none; }
