/* ── the type, served from this domain ──────────────────────
   SELF-HOSTED BECAUSE THE FONTS WERE THE LAYOUT SHIFT. Loaded from
   fonts.googleapis.com they arrived ~2.5s in on a throttled phone, and the
   masthead — two lines of the shop's name — was a different height in the
   fallback face than in Source Sans 3. When it swapped, the masthead shrank
   by 58px and every single thing below it jumped up: nav 132->74, main
   196->137. Measured CLS 0.076 on a phone. It reads as the page shaking.

   Three things it fixes at once: no third-party DNS and TLS before any text
   can render, no render-blocking stylesheet from another origin, and the swap
   window shrinks to a same-origin cache hit. The original plan said to bundle
   the fonts and that was lost somewhere; this is that debt paid.

   Only latin and devanagari subsets are here — Google served 45 faces across
   cyrillic, greek and vietnamese that this site can never render. unicode-range
   is kept, so the 236KB of Devanagari is fetched only by the pages that
   actually set Devanagari text, which is /visit and nothing else. */
@font-face {
  font-family: 'Noto Sans Devanagari';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/noto-sans-devanagari-400-devanagari.woff2') format('woff2');
  unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09;
}
@font-face {
  font-family: 'Noto Sans Devanagari';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/noto-sans-devanagari-400-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: 'Noto Sans Devanagari';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/noto-sans-devanagari-600-devanagari.woff2') format('woff2');
  unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09;
}
@font-face {
  font-family: 'Noto Sans Devanagari';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/noto-sans-devanagari-600-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: 'Source Sans 3';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/source-sans-3-400i-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: 'Source Sans 3';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/source-sans-3-400-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: 'Source Sans 3';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/source-sans-3-600-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: 'Source Serif 4';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/source-serif-4-400i-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: 'Source Serif 4';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/source-serif-4-400-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: 'Source Serif 4';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/source-serif-4-600-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;
}

/* Shri Balaji Eye Care & Opticals
   ---------------------------------------------------------------------------
   Colour is derived from the practice's own shopfront, not chosen for taste.
   The site's directions tell people to find the shop "under the sign with the
   spectacles on it" — so the palette is wayfinding before it is branding, and
   handing a patient the wrong colour to remember fails the one operational job
   this page has. The sign samples crimson-rose; this is that hue family
   disciplined into a system, not copied off the plastic.

   Type is large and spacing is on one scale because the audience skews older
   and lower-vision. Every value below is a floor, not a preference. */

/* BREAKPOINTS ARE IN px, DELIBERATELY, AND MUST STAY THAT WAY.
   A `rem` inside a media query resolves against the browser's INITIAL font
   size (16px) — it ignores the 19px set on :root. So `46rem` fired at 736px
   instead of the 874px intended, and every breakpoint on this site was landing
   about 15% early: the desktop two-column layout appeared at 760px and the
   mobile call bar vanished there, on a width that is still a phone held
   sideways. Regular lengths elsewhere in this file DO use the 19px root; only
   media queries have this rule.

   This is the same class of defect as the print sheet where `rem` resolved
   against the root rather than the 10.5pt set on body. It has now been
   introduced twice in this project. */

/* ── metric-matched fallbacks ───────────────────────────────
   SELF-HOSTING THE FONTS DID NOT FIX THE SHAKE, and the measurement said why:
   the masthead sat at 132px for 2.3 seconds and then collapsed to 74px the
   instant the fonts finished. The header telephone is `white-space: nowrap` and
   set in the serif, and in the FALLBACK serif it is too wide for the row — so
   it wrapped onto a second line, and unwrapped when the real font arrived. The
   page did not shake because the fonts were slow. It shook because the LAYOUT
   DEPENDED ON THEIR METRICS.

   MEASURED ON THE RUNNING TEXT, and the first attempt got this wrong in a way
   worth recording. The ratios were first taken from the masthead — SET IN ALL
   CAPS — and from the telephone number, which is digits and a dial symbol.
   Neither is representative, and both skewed high: sans came out 83.72% when
   the site's actual prose needs 92.4%, and serif came out 88.01% when the
   headline needs 79.5%. The fallback prose was therefore too narrow, so text
   WIDENED when the real face arrived and the review quote gained a line three
   seconds in. Cardinal-looking sample data, and precisely the blind spot.

   These are measured on the review quote, the hero lede and the h1 — the
   longest runs of real text on the site, which is what decides line counts.

   AND MEASURED AGAINST THE FACE THAT ACTUALLY RESOLVES. The sans figure was
   taken against Arial while `src: local('Helvetica Neue'), local('Arial')`
   resolves to Helvetica Neue on this machine — a 1% error, which sounds like
   nothing and is exactly one line break on a 90-character quote. The check that
   settles it is not the ratio but the OUTCOME: rendering the same string in
   'Sans Fallback' and in 'Source Sans 3' must give the same width. Sans is
   within 0.02%; serif is 90.82%, corrected the same way after 80% came out
   12% narrow.

   THE ascent-override AND descent-override VALUES ARE GONE. They were invented
   — three numbers written down that nobody measured — and every element here
   sets line-height explicitly, so they were buying nothing while looking like
   evidence. A guessed metric in a file full of measured ones is worse than no
   metric at all.

   So the fallbacks are scaled to occupy the width the real face will occupy.
   Text in the fallback is very slightly smaller than it would otherwise be;
   nothing moves when the swap happens, which is the trade worth making. */
@font-face {
  font-family: 'Serif Fallback';
  src: local('Georgia'), local('Times New Roman');
  size-adjust: 90.82%;
}
@font-face {
  font-family: 'Sans Fallback';
  src: local('Helvetica Neue'), local('Arial');
  size-adjust: 91.47%;
}

:root {
  /* ── ground and ink ─────────────────────────────────────── */
  --paper:    #FCFAF7;
  --paper-2:  #F5F0EA;   /* recessed: nav strip, footer, cards */
  --ink:      #1E1A1C;   /* warm near-black — a blue-black under crimson reads cold */
  --muted:    #6B5A5E;   /* warm secondary. 5.9:1 on --paper */
  --rule:     #E4DCD4;

  /* ── the practice's colour ──────────────────────────────── */
  --crimson:   #A81E3C;  /* white on it 7.2:1 · on paper 6.9:1 */
  --crimson-2: #8A1730;  /* hover, pressed */
  --rose:      #E8577A;  /* MARKS ONLY — 2.1:1 on crimson, never a word */

  /* One state colour, not two. An oxblood "closed" inside a crimson system
     makes the brand colour read as a warning, so closed has no colour at all. */
  --ok: #1B6B3A;

  /* ── type ───────────────────────────────────────────────── */
  --serif: "Source Serif 4", "Serif Fallback", Georgia, serif;
  --sans: "Source Sans 3", "Sans Fallback", -apple-system, BlinkMacSystemFont, sans-serif;
  --deva: "Noto Sans Devanagari", "Nirmala UI", "Kohinoor Devanagari", sans-serif;

  /* ── one spacing scale. Every margin below is a token. ──── */
  --s1: .25rem; --s2: .5rem;  --s3: .75rem; --s4: 1rem; --s5: 1.5rem;
  --s6: 2rem;   --s7: 3rem;   --s8: 4rem;   --s9: 6rem;

  /* Two text roles that kept being re-declared by hand and drifting apart.
     The same tracked uppercase label was set at .8rem, .78rem and .7rem in
     five places; the same small grey secondary line at .85rem in three. A
     0.4px difference between two identical labels is invisible one at a time
     and is exactly how a stylesheet stops being a system. */
  --label: .78rem;   /* tracked uppercase: eyebrows, dt, kickers */
  --meta:  .85rem;   /* small secondary: attributions, captions, dates */
  --small: .95rem;   /* just under body: descriptions, addresses, footer */

  --measure: 36rem;
  --shell: 58rem;
  --pad: clamp(1.25rem, 5vw, 2.5rem);
}

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

html { font-size: 19px; -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0; background: var(--paper); color: var(--ink);
  font-family: var(--sans); font-size: 1rem; line-height: 1.65;
  padding-bottom: var(--s9);
}
@media (min-width: 880px) { body { padding-bottom: 0; } }

/* ── type scale — the intervals, not the sizes, were the problem ── */

h1, h2, h3 { font-family: var(--serif); font-weight: 600; text-wrap: balance; }
h1 { font-size: clamp(2.1rem, 8vw, 3.2rem); line-height: 1.1; letter-spacing: -.02em; margin: 0 0 var(--s4); }
h2 { font-size: 1.75rem; line-height: 1.2; margin: var(--s7) 0 var(--s3); }
h3 { font-size: 1.3rem; line-height: 1.25; margin: var(--s5) 0 var(--s2); }

p, li { max-width: var(--measure); }
p { margin: 0 0 var(--s4); }
ul, ol { padding-left: var(--s5); }
li { margin-bottom: var(--s2); }

/* A lede is set TIGHTER than body, not looser. */
.lede { font-size: 1.25rem; line-height: 1.45; margin-bottom: var(--s5); }

.eyebrow {
  font-size: var(--label); line-height: 1.3; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  margin: 0 0 var(--s2);
}

a { color: var(--crimson); text-underline-offset: .18em; text-decoration-thickness: .07em; }
a:hover { color: var(--crimson-2); }
:focus-visible { outline: 3px solid var(--crimson); outline-offset: 3px; border-radius: 2px; }

/* ── Devanagari is an equal, not a caption ──────────────────
   Its effective x-height sits below Latin's at the same nominal size, so
   setting it smaller compounds an optical deficit that is already there. It
   goes UP, takes the same ink, and keeps room for its matras. Grey under black
   reads as a note about the English; same colour, same rank reads as the same
   statement said again. */
.hi {
  font-family: var(--deva);
  font-size: 1.05em;
  line-height: 1.85;
  color: inherit;
}

/* ── shell ──────────────────────────────────────────────── */

.skip { position: absolute; left: -9999px; top: 0; z-index: 99; background: var(--crimson); color: #fff; padding: var(--s3) var(--s4); }
.skip:focus { left: 0; }

.wrap { max-width: var(--shell); margin: 0 auto; padding: 0 var(--pad); }

/* ── the masthead is the shop's own signboard ───────────────
   A patient has already walked past the real one. It is a lit white panel
   carrying, in this order: an eye at the left, the name in two stacked lines
   of bold crimson capitals, "Since-1990" in a tan pill, and a pair of
   spectacles at the right. The header reproduces that arrangement rather than
   inventing a wordmark — same reason the palette came off the sign, and the
   directions on /visit tell people to look for it. */
.masthead { background: #fff; border-bottom: 1px solid var(--rule); }
.masthead .wrap { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3) var(--s4); padding-top: var(--s3); padding-bottom: var(--s3); }

/* 44px MINIMUM, measured on the rendered box. Both of these sat under it —
   the signboard link at 38 and the header telephone at 33 — and the telephone
   is the likeliest tap on the entire site. Neither had any padding at all;
   they were as tall as their text happened to be. */
.board { display: flex; align-items: center; gap: var(--s3); text-decoration: none; min-height: 44px; }
.board .ico { width: 26px; height: 26px; flex: none; }
.board .ico .k { stroke: var(--crimson); }
.board .ico .s, .board .ico .a { stroke: #B9AFA6; }
.board-name {
  font-family: var(--sans); font-weight: 700;
  font-size: clamp(.92rem, 3.4vw, 1.15rem); line-height: 1.08;
  letter-spacing: .015em; text-transform: uppercase; color: var(--crimson);
}
/* The tan pill, sampled off the sign at #CFAB90 and darkened until its own
   text carries — the photograph's value is a lit panel at night, not ink. */
.board-since {
  flex: none; background: #B08A63; color: #fff;
  font-size: .62rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  padding: .2rem .55rem; border-radius: 999px; white-space: nowrap;
}
@media (max-width: 479px) { .board .ico:last-of-type, .board-since { display: none; } }

/* One row, never a hamburger — this audience should not have to go looking for a
   menu. Desktop shows the full six labels; under 880px the short labels swap in and
   the gap tightens, which cuts the row from ~700px to ~405px. That still overruns a
   phone by ~15px, so overflow-x, the right-edge fade and scroll-snap stay as the cue
   for the sliver that remains — but now only "Reviews" peeks, not five of six. */
nav.primary { border-bottom: 1px solid var(--rule); background: var(--paper-2); }
nav.primary ul {
  display: flex; flex-wrap: nowrap; overflow-x: auto; gap: var(--s5);
  list-style: none; margin: 0 auto; padding: 0 var(--pad); max-width: var(--shell);
  scroll-snap-type: x proximity; scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, #000 88%, transparent);
          mask-image: linear-gradient(to right, #000 88%, transparent);
}
nav.primary ul::-webkit-scrollbar { display: none; }
nav.primary li { max-width: none; margin: 0; flex: none; scroll-snap-align: start; }
nav.primary a {
  display: block; padding: var(--s3) 0; white-space: nowrap;
  font-size: 1rem; color: var(--ink); text-decoration: none;
  border-bottom: 3px solid transparent;
}
nav.primary a:hover { color: var(--crimson); }
nav.primary a[aria-current="page"] { border-bottom-color: var(--crimson); font-weight: 600; }
nav.primary .nav-short { display: none; }
@media (max-width: 879px) {
  nav.primary ul { gap: var(--s2); padding-left: var(--s3); padding-right: var(--s3); }
  nav.primary a { font-size: .85rem; }
  nav.primary .nav-full { display: none; }
  nav.primary .nav-short { display: inline; }
}
@media (min-width: 880px) {
  nav.primary ul { -webkit-mask-image: none; mask-image: none; }
}

main { padding-top: var(--s5); padding-bottom: var(--s8); }
/* A full-bleed band as the first child needs no gap above it — the strip of
   paper between the nav and the hero read as a rendering fault. */
main > .hero:first-child, main > .pagehead:first-child { margin-top: calc(var(--s5) * -1); }

/* ── the credential strip — the reason anyone should choose them,
       above the fold, before a single paragraph ───────────── */
.creds {
  margin: 0 0 var(--s4); padding: 0;
  font-size: var(--small); line-height: 1.5; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted);
  max-width: var(--measure);
}
.creds b { color: var(--ink); font-weight: 600; }

/* ── status: one line of text, no box ───────────────────── */

.status { display: flex; align-items: baseline; gap: var(--s2); font-size: var(--small); color: var(--muted); margin: 0 0 var(--s5); max-width: var(--measure); }
/* Closed has no dot at all — an absent signal, not a second coloured one.
   It is also display:none rather than transparent, or it leaves a hanging
   indent where the open state's dot would have been. */
.status .dot { display: none; width: .55rem; height: .55rem; border-radius: 50%; flex: none; align-self: center; }
.status[data-state="open"] .dot { display: block; background: var(--ok); }
.status[data-state="open"] b { color: var(--ok); font-weight: 600; }
.status b { font-weight: 600; color: var(--ink); }

.rating { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2); font-size: var(--small); color: var(--muted); margin: var(--s5) 0 0; max-width: var(--measure); }
.rating strong { color: var(--ink); font-weight: 600; }
/* Drawn, not typed. Typed asterisk-stars sit on the text baseline instead of
   aligning to the number they qualify, and their weight fights Source Sans. */
.stars { display: inline-flex; gap: 2px; color: var(--crimson); flex: none; }
.stars svg { width: 20px; height: 20px; display: block; fill: currentColor; }

/* ── the credential band ────────────────────────────────── */

.band { background: var(--crimson); color: #fff; margin: var(--s7) 0; }
.band .wrap { padding-top: var(--s7); padding-bottom: var(--s7); }
.band h2 { color: #fff; margin-top: 0; font-size: 1.6rem; }
.band a { color: #fff; }
.band .who { padding-top: var(--s5); }
.band .name { font-family: var(--serif); font-size: 1.45rem; font-weight: 600; color: #fff; margin: 0 0 var(--s2); }
/* Rose is the neon's colour and it is a MARK, never a word — 2.1:1 on crimson. */
.band .line {
  margin: 0 0 var(--s1); color: #F7E6E9; max-width: var(--measure);
  padding-left: var(--s4); position: relative;
}
.band .line::before { content: ""; position: absolute; left: 0; top: .72em; width: 12px; height: 3px; background: var(--rose); }
@media (min-width: 880px) {
  .band .whos { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s7); }
  .band .wrap { padding-top: var(--s8); padding-bottom: var(--s8); }
}

/* ── cards, steps, facts ────────────────────────────────── */

/* Four hairline-bordered white boxes with an underlined link trailing each
   paragraph is on every dental, optical and physio template ever shipped, and
   the link wrapped mid-sentence on a phone. This is the same four things as a
   list of linked rows: the whole row is the target, the title carries the
   weight, and nothing is boxed. */
.services { list-style: none; margin: var(--s5) 0; padding: 0; border-top: 1px solid var(--rule); }
.services li { margin: 0; max-width: none; border-bottom: 1px solid var(--rule); }
.services a {
  display: grid; grid-template-columns: 40px 1fr; gap: var(--s1) var(--s4);
  padding: var(--s4) 0; text-decoration: none; color: var(--ink);
}
.services .ico { grid-row: 1 / 3; align-self: start; margin-top: .15rem; }
.services a:hover .t { color: var(--crimson); }
.services .t { font-family: var(--serif); font-size: 1.25rem; font-weight: 600; line-height: 1.25; }
.services .t::after { content: " →"; color: var(--crimson); font-family: var(--sans); font-size: .9em; }
.services .d { color: var(--muted); font-size: var(--small); max-width: var(--measure); }
@media (min-width: 880px) {
  .services { display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--s7); }
  .services li:nth-child(-n+2) { border-top: 0; }
}

/* ── the drawings ───────────────────────────────────────────
   Two stroke weights only, in a strict 2:1 ratio, and non-scaling so a drawing
   keeps the same line whether it renders at 40px or 600px. .s is structural,
   .a auxiliary, .k the one crimson element that marks what the drawing is
   about. Nothing is filled and rose never appears — a drawing that needs
   colour to be understood fails the reader it was drawn for. */
.ico { display: block; flex: none; }
.ico path, .ico circle { fill: none; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.ico .s { stroke: var(--ink); stroke-width: 2.5; }
.ico .a { stroke: var(--ink); stroke-width: 1.25; }
.ico .k { stroke: var(--crimson); stroke-width: 2.5; }
.services a:hover .ico .s, .services a:focus-visible .ico .s { stroke: var(--crimson-2); }

/* ── the masthead's own call to action ──────────────────────
   The number was reachable only by scrolling to the rail or the footer. It is
   now in the header on every page, at every scroll position, on every width. */
.mast-cta {
  margin-left: auto; font-family: var(--serif); font-weight: 600;
  font-size: 1.05rem; color: var(--crimson); text-decoration: none;
  white-space: nowrap;
  display: flex; align-items: center; min-height: 44px;
}
.mast-cta::before { content: "☎ "; font-family: var(--sans); font-size: .9em; }
.mast-cta:hover { color: var(--crimson-2); }

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

/* One opening, identical on every inner page — one line, in the page's own
   voice. It was set in --muted while it was only the page's name; now that it
   is the page's claim it takes full ink and the weight to match. */
.pagehead { padding-top: var(--s5); padding-bottom: var(--s5); }
.pagehead h1 { font-size: clamp(1.85rem, 5.6vw, 2.6rem); line-height: 1.12; letter-spacing: -.015em; margin: 0; max-width: 20ch; }

/* ═══ the marketing surfaces ════════════════════════════════
   A patient deciding where to take their eyes wants three things fast: can I
   trust these people, can I come today, and what will it cost me to find out.
   The pages below sell; the inner pages explain. */

/* The hero was a night photograph of the shopfront with type laid over it.
   It said "here is the building"; a patient wants to know who is going to look
   at their eyes. It is now the only human image on the site, cut out and set
   against the practice's own crimson — the picture and the claim side by side
   rather than the claim fighting the picture for legibility. */
.hero { background: var(--crimson); color: #fff; }
.hero .wrap { display: grid; gap: var(--s5); padding-top: var(--s7); padding-bottom: var(--s7); align-items: center; }
/* Two columns only when there is genuinely room for both. Below this the image is larger stacked than it would be
   split — at 760 a 28rem text column left it 152px, smaller than on a phone. */
@media (min-width: 1120px) {
  /* The hero is the one band that must NOT be confined to --shell. Capped
     there, the image column was a fixed 451px at 1280, 1600 and 1920 alike —
     it never grew, so it filled 37% of the hero's height and left a 514px void
     beside the text on a wide screen. The text column stays aligned with every
     other section on the site; the image takes everything to the right edge. */
  .hero .wrap {
    max-width: none; padding-right: 0;
    padding-left: max(var(--pad), calc((100vw - var(--shell)) / 2 + var(--pad)));
    grid-template-columns: minmax(0, 22rem) 1fr;
    gap: var(--s5);
    padding-top: var(--s6); padding-bottom: 0;
    align-items: end;
  }
}
.hero .kicker { color: #F3C7D0; font-size: var(--label); font-weight: 600; letter-spacing: .12em; text-transform: uppercase; margin: 0 0 var(--s3); }
.hero h1 { color: #fff; font-size: clamp(2.1rem, 6.4vw, 3.4rem); line-height: 1.05; letter-spacing: -.022em; margin: 0 0 var(--s3); max-width: 15ch; }
.hero-lede { color: #F8E9EC; font-size: clamp(1.05rem, 3vw, 1.25rem); margin: 0 0 var(--s5); max-width: 30ch; }
.hero .actions { margin: 0 0 var(--s4); }
.hero-status { color: #EBD4D9; margin: 0; }
.hero-status b { color: #fff; }
.hero-status[data-state="open"] b, .hero-status[data-state="open"] .dot { color: #9DE8B8; background: none; }
.hero-status[data-state="open"] .dot { background: #9DE8B8; }
/* The hero cycles between examinations, all four cut on the same 1000x546
   canvas so they stack without the band changing height.

   ALL FOUR ARE ANCHORED TO THE SAME TWO EDGES — subject flush to the right of
   its canvas, flush to the bottom. Anything else and the frames appear to jump
   sideways as they change: one of them sat 88px short of the right edge and
   read as pushed out of place next to the three that bled off it. The empty
   canvas is always on the LEFT, which is where the headline is, so a narrower
   photograph costs nothing.

   THE EDGES ARE FEATHERED WHERE THE FRAME CUTS A PERSON, and only there.
   Bottom, because every frame is cropped at the chest. Left, because the wide
   frames run out of canvas mid-torso. TOP, because two of the frames are cut
   through the hair — measured, 301 opaque pixels along hero-1's top row and 118
   along hero-2's — and the image box starts 130px BELOW the top of the crimson
   band, so that cut is a straight horizontal line floating in open colour with
   nothing to explain it. It was the most visible edge on the hero and the last
   one to be feathered, because it is the one edge a photograph is not usually
   cropped at.

   Not the right edge: that one is at the viewport boundary, so the figure
   genuinely runs off the page and needs no help looking like it. Verified
   rather than assumed — the box measures flush at 1280, 1440, 1728 and 1920.

   Two gradients, intersected. `mask-composite: intersect` is the standard
   property; `-webkit-mask-composite: source-in` is the older WebKit spelling of
   the same thing and both are needed — they are NOT the same keyword set, and
   writing `intersect` in the -webkit- property silently disables the mask. */
.hero-shots {
  position: relative; width: 100%; aspect-ratio: 1000 / 546;
  justify-self: end; align-self: end;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 9%, #000 93%, transparent 100%),
                      linear-gradient(to right, transparent 0, #000 7%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 9%, #000 93%, transparent 100%),
                      linear-gradient(to right, transparent 0, #000 7%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}
/* BOTH FRAMES ARE CUT-OUT PEOPLE ON A TRANSPARENT GROUND, so no transition
   that has two of them painting at the same instant can work: wherever the top
   frame is transparent — which is most of the box — the one beneath it shows
   through, and the reader sees two examinations superimposed. A cross-fade
   makes that a two-second double exposure; holding the outgoing frame opaque
   underneath makes it permanent. Both were built here and both were wrong.

   So the change is a CUT. Exactly one frame ever paints. It is also the
   quietest option available: nothing moves, nothing dims, and the picture is
   simply different the next time the reader glances at it. */
.hero-shots img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; object-position: bottom right;
  opacity: 0;
}
.hero-shots img.on { opacity: 1; }
/* Before the script runs — and if it never does — the first frame is visible. */
.hero-shots img:first-child { opacity: 1; }
.hero-shots img:first-child.off { opacity: 0; }
/* BELOW THE TWO-COLUMN BREAKPOINT THE HERO STACKS, and the reason the frames
   are right-anchored — the empty canvas falls on the left, where the headline
   is, so a narrow photograph costs nothing — stops being true. With the
   headline ABOVE rather than beside, that canvas is just a hole, and it is a
   different size in every frame: two of the four floated against a gutter of
   nearly 40% while the other two filled the width, so the subject appeared to
   jump position AND scale on every change. The reason was sound; it was
   load-bearing on a layout that only exists above 1120px.

   Taller box, and cover instead of contain. Scale is then driven by height,
   which means the window into the image is 728px wide IN IMAGE COORDINATES at
   every viewport width — the crop is identical on a 320px phone and a 768px
   tablet. It begins at x=271, and the four subjects begin at 286, 1, 51 and
   304: the two narrow frames fill the box with a few pixels to spare, and the
   two wide ones give up only a man's back and the edge of a chair. Nothing is
   cropped vertically, because height is what sets the scale.

   THIS BLOCK MUST STAY BELOW `.hero-shots img`. Written above it, the
   aspect-ratio applied and object-fit did not — a media query adds NO
   specificity, so the unconditional rule 13 lines further down won. The box
   grew taller while the image still contained, which made the empty space
   worse rather than better and looked like the fix had simply failed. */
@media (max-width: 1119px) {
  .hero-shots { justify-self: center; max-width: 32rem; aspect-ratio: 4 / 3; }
  .hero-shots img { object-fit: cover; object-position: right bottom; }
}

/* The same mechanism above the breakpoint, turned down. Here the empty canvas
   DOES fall beside the headline, so it is not a hole and the frames need not
   fill — but the four subjects begin at wildly different places (x = 286, 1,
   51, 304), so the picture's left edge jumped 236px between frames.

   1.7 is not a taste; it is the largest crop the photographs allow. Measured
   per band: hero-2's head begins at x=120, and the window at 1.7 opens at 72 —
   48px clear. At 1.6 it opens at 126 and takes 6px off his hair. So the cost of
   another 40px of alignment is a man's scalp, and the answer is no.

   What it buys, at 1440: the two narrow frames' gutters fall 222 to 179 and 236
   to 194, while the two wide ones give up 59px and 17px of shirt and chair.
   That is a fifth of the jump, not all of it. The rest is in the photographs —
   hero-4 is a close portrait and hero-1 is a two-person mid shot, so the faces
   differ in scale by nearly 2:1 and no crop can equalise that. */
@media (min-width: 1120px) {
  .hero-shots { aspect-ratio: 1.7; }
  .hero-shots img { object-fit: cover; object-position: right bottom; }
}


.trust { background: var(--crimson); color: #fff; }
.trust .wrap { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s5) var(--s4); padding-top: var(--s6); padding-bottom: var(--s6); }
@media (min-width: 760px) { .trust .wrap { grid-template-columns: repeat(4, 1fr); } }
.trust b { display: block; font-family: var(--serif); font-size: 2.4rem; line-height: 1; }
.trust span { display: block; font-size: var(--small); color: #F3DDE1; margin-top: var(--s1); overflow-wrap: break-word; }

/* ── every grid item may shrink ─────────────────────────────
   A GRID ITEM'S DEFAULT min-width IS auto, which means "never narrower than my
   longest word". Any grid whose column gets narrower than a word inside it
   therefore stops shrinking and pushes the whole page sideways instead.

   Found THREE TIMES in one afternoon's audit, in three unrelated components:
   the trust band at 195px ("appointments", 75px over), the practitioner cards
   at 195px ("Formerly"), and the locator's six labels at 320px ("society",
   13px over — a width real phones actually are). Patching the first one is
   what let the second and third ship, so what is written down is the rule.

   Listed explicitly rather than applied with `* > *`: min-width:0 on every
   element in the document would also strip it from flex items, where auto is
   what makes a scroll container behave. A new grid goes on this list. */
:where(
  .hero .wrap, .trust .wrap, .people, .fig-labels, .shot, .facts > div,
  .band .whos, .services, .person, .layout, .foot .wrap, .cert-grid
) > * { min-width: 0; }

/* And once a column CAN be narrower than its longest word, that word has to be
   allowed to break, or it merely overflows its own box instead of the page. */
.trust span, .pcard p, .fig-labels b, .fig-labels span { overflow-wrap: break-word; }

h2.big { font-size: clamp(1.9rem, 5.5vw, 2.6rem); line-height: 1.12; letter-spacing: -.015em; max-width: 18ch; margin-top: var(--s8); }

/* Two columns from the smallest phone. Stacked, a pair of 3:4 slots at full
   width is ~880px of empty rectangle before a reader reaches a single word
   about either man — the placeholders would out-shout the people. */
.people { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); margin: var(--s6) 0; }
@media (min-width: 760px) { .people { gap: var(--s7); } }
.pcard h3 { margin: var(--s3) 0 var(--s1); font-size: 1.15rem; }
@media (min-width: 760px) { .pcard h3 { font-size: 1.4rem; } }
.pcard .role { color: var(--crimson); font-weight: 600; font-size: var(--small); margin: 0 0 var(--s2); }
.pcard p { margin: 0; color: var(--muted); font-size: var(--small); }


.more { font-weight: 600; text-decoration: none; }
.more:hover { text-decoration: underline; }

.close { background: var(--crimson); color: #fff; margin-top: var(--s8); }
.close .wrap { padding-top: var(--s8); padding-bottom: var(--s8); }
.close h2 { color: #fff; margin-top: 0; font-size: clamp(1.9rem, 6vw, 2.8rem); }
.close p { color: #F6E7EA; }

/* ── the proof band ─────────────────────────────────────────
   The one thing every page has in the same place: a patient's words, tinted,
   between the page's content and its closing call. It is what gives all six
   pages the same rhythm — paper, then colour, then the crimson close. */
.proof { background: var(--paper-2); border-block: 1px solid var(--rule); margin-top: var(--s8); }
.proof .wrap { padding-top: var(--s8); padding-bottom: var(--s8); }
.proof blockquote, .proof .proof-more { grid-column: auto; }
.proof blockquote { margin: 0; }
.proof blockquote p { font-family: var(--serif); font-size: clamp(1.4rem, 4.4vw, 2rem); line-height: 1.28; margin: 0 0 var(--s4); max-width: 22ch; }
.proof footer { font-size: var(--meta); color: var(--muted); }
.proof footer b { color: var(--ink); }
.proof-more { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin: var(--s6) 0 0; font-size: var(--small); }

/* ── figures: a drawing, and its words underneath ───────────
   The labels are HTML columns beneath the SVG, aligned to it, never <text>
   inside it — see the note in build.mjs. */
.fig { margin: var(--s5) 0 var(--s6); max-width: var(--measure); }
/* The sign is evidence, not decoration — it prints both careers in the
   practice's own crimson. It gets the full width, not the reading measure. */
.sign { max-width: none; }
.sign img { width: 100%; height: auto; display: block; }
.dwg { display: block; width: 100%; height: auto; }
.dwg path, .dwg circle { fill: none; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.dwg .s { stroke: var(--ink); stroke-width: 2.5; }
.dwg .a { stroke: var(--ink); stroke-width: 1.25; }
.dwg .k { stroke: var(--crimson); stroke-width: 2.5; }

.fig-labels { display: grid; gap: var(--s2) var(--s3); margin-top: var(--s2); }
.fig-labels.three { grid-template-columns: repeat(3, 1fr); text-align: center; }
.fig-labels.row { grid-template-columns: repeat(6, 1fr); text-align: center; }
.fig-labels b { display: block; font-size: 13px; line-height: 1.25; }
.fig-labels span { display: block; font-size: 12px; color: var(--muted); line-height: 1.3; }
.fig-labels.row div:nth-child(5) b { color: var(--crimson); }
.fig figcaption { margin-top: var(--s3); }
/* The translation is a second sentence, not a continuation of the first. Left
   inline it ran straight on from the English mid-line, which reads as one
   garbled bilingual paragraph rather than the same thing said twice. */
.fig figcaption .hi { display: block; margin-top: var(--s2); }

/* ── a machine, photographed ────────────────────────────────
   Drawings are right for optics — a lens in section is a diagram of an idea.
   They are wrong for machinery: a patient is about to put their face into this
   object and wants to see the object, not a schematic of it.

   The background is cut out in the FILE, not with clip-path. A clip-path plate
   was tried first and earned nothing: a light panel on a light ground made the
   angle invisible, and it added a box around an object whose whole appeal is
   that it has no box. Baked alpha is also the accurate cut — the machine's
   outline is a curve, and a polygon approximating it would show its corners. */
/* Side by side from the smallest phone. Stacked, a portrait machine shot at
   19rem wide runs about 475px tall, and three of them on one page is 1,400px
   of scrolling before the text is counted — which is what made this page nine
   screens long. Cutting words did almost nothing; the pictures were the page. */
.shot { margin: var(--s6) 0; display: grid; grid-template-columns: 38% 1fr; gap: var(--s4); align-items: center; }
.shot img { width: 100%; height: auto; display: block; }
.shot figcaption { margin: 0; }

/* A WHITE machine on a WHITE background cannot be cut out. The autorefractor
   and the lensmeter are light grey, so a flood fill separates them from the
   paper behind. The slit lamp is white paintwork, and every fuzz value that
   removed the background also ate the illumination tower and chewed the base
   plate. multiply drops the white into whatever the page ground is and leaves
   the machine's shading intact — exact rather than approximate. It works
   because the ground is near-white; on the crimson band it would not. */
.shot img.multiply { mix-blend-mode: multiply; }

@media (min-width: 880px) {
  .shot { grid-template-columns: 1fr 1fr; gap: var(--s7); }
  .shot img { max-width: 17rem; margin-inline: auto; }
  .shot-flip img { grid-column: 2; grid-row: 1; }
  .shot-flip figcaption { grid-column: 1; grid-row: 1; }
  .shot-wide img { max-width: 24rem; }
}

/* ── the urgent panel ───────────────────────────────────────
   Not a drawing, deliberately — prettifying a list of emergency symptoms is
   the wrong instinct. But the one safety-critical passage on the site read
   exactly like a paragraph about opening hours, which is worse. The crimson
   rule is the entire signal, and this is the only place on the site where
   crimson borders a block of text, which is what makes it legible as
   "this one is different". No icon. No warning triangle. */
.urgent { background: var(--paper-2); border-left: 3px solid var(--crimson); padding: var(--s5); margin: var(--s5) 0; max-width: var(--measure); }
.urgent ul { list-style: none; padding: 0; margin: 0 0 var(--s4); }
.urgent li { position: relative; padding-left: var(--s5); margin-bottom: var(--s2); }
.urgent li::before { content: ""; position: absolute; left: 0; top: .55em; width: 10px; height: 10px; background: var(--crimson); }
.urgent p:last-child { margin: 0; }

/* ── the journey ────────────────────────────────────────────
   ONE list, not two. A horizontal overview strip above a vertical list of the
   same five steps said the same thing twice, and six stations across a 343px
   phone was cramped besides. The steps ARE the overview now, and each carries
   the machine that does it — which is also where three full-size figures went.
   Vertical at every width: a journey reads down, and the two stages that are
   conversations rather than machines simply have no picture. */
.route-dur { font-size: var(--label); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin: var(--s5) 0 0; }

/* ── the spine ──────────────────────────────────────────────
   The same drawing as the route marker at another scale: a rule with stations
   on it. Station five is a ring, so the journey ends the same way twice. */
.steps { list-style: none; padding: 0; margin: var(--s5) 0; counter-reset: step; position: relative; }
.steps::before {
  content: ""; position: absolute; left: 1.05rem; top: 1.05rem; bottom: 1.05rem;
  width: 2px; background: var(--crimson);
}
.steps li {
  counter-increment: step; position: relative;
  padding: 0 0 var(--s6) var(--s7); max-width: var(--measure);
  display: flex; gap: var(--s4); align-items: center;
}
.steps .st { flex: 1; min-width: 0; }
.steps li > .thumb { flex: none; }
/* Capped in BOTH axes. Height alone let the trial frame — the one landscape
   object — eat 130px of a 343px column and squeeze the sentence beside it to
   six words a line. */
.steps .thumb img { max-height: 58px; max-width: 84px; width: auto; display: block; }
@media (min-width: 880px) { .steps .thumb img { max-height: 104px; max-width: 150px; } }
.steps .thumb img.multiply { mix-blend-mode: multiply; }
/* The journey ends the way the marker used to promise: an open ring. */
.steps li.done { counter-increment: none; padding-bottom: 0; }
.steps li.done::before {
  content: ""; background: var(--paper); color: var(--crimson);
  box-shadow: inset 0 0 0 2.5px var(--crimson), 0 0 0 6px var(--paper);
}
.steps li.done b { color: var(--muted); font-family: var(--sans); font-size: var(--label); letter-spacing: .08em; text-transform: uppercase; align-self: center; }
.steps li::before {
  content: counter(step); position: absolute; left: 0; top: 0;
  width: 2.1rem; height: 2.1rem; border-radius: 50%;
  background: var(--crimson); color: #fff;
  font-family: var(--serif); font-size: 1rem;
  display: grid; place-items: center;
  box-shadow: 0 0 0 6px var(--paper); z-index: 1;
}

.steps b { display: block; font-family: var(--serif); font-size: 1.15rem; margin-bottom: var(--s1); }

/* Hairlines stop where the text stops. They used to run the full shell width
   while their content ended at the measure, trailing ~320px into empty space. */
.facts { border-top: 1px solid var(--rule); margin: var(--s5) 0 0; max-width: var(--measure); }
.facts > div { display: grid; gap: var(--s1) var(--s5); padding: var(--s3) 0; border-bottom: 1px solid var(--rule); }
@media (min-width: 640px) { .facts > div { grid-template-columns: 9rem 1fr; } }
.facts dt { font-size: var(--label); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.facts dd { margin: 0; max-width: none; }
.facts dd .hi { display: block; }

/* ── quotes ─────────────────────────────────────────────── */

.quote { border-left: 3px solid var(--crimson); padding: 0 0 0 var(--s4); margin: var(--s6) 0; max-width: var(--measure); }
.quote p { font-size: 1.05rem; line-height: 1.6; margin: 0 0 var(--s2); }
.quote footer { font-size: var(--meta); color: var(--muted); }
.quote footer b { font-family: var(--serif); font-size: var(--small); color: var(--ink); font-weight: 600; }

/* The one that carries the page, set apart from the six that support it. */
.quote-lead { border-left-width: 4px; }
.quote-lead p { font-family: var(--serif); font-size: 1.35rem; line-height: 1.4; }

/* ── a practitioner ─────────────────────────────────────────
   Two columns so the record sits beside the account of it rather than being
   buried as bold runs inside paragraphs. The portrait belongs at the head of
   the left column; until one exists the record stands there alone, which is
   why the column is not sized around an image that has not been taken. */
.person { margin: var(--s7) 0; }
.person h2 { margin-top: 0; }
.person + .person { border-top: 1px solid var(--rule); padding-top: var(--s7); }
@media (min-width: 880px) {
  /* The record beside the account, and the account gets the full measure.
     5fr/7fr with three children dropped the biography into column 1 row 3 —
     C. D. Sharma's story, the reason this page exists, set 293px wide with
     400px of blank paper next to it. */
  .person { display: grid; grid-template-columns: 17rem 1fr; gap: var(--s3) var(--s7); align-items: start; }
  .person h2 { grid-column: 1 / -1; }
  .person .cv { grid-column: 1; }
  .person > div:last-child { grid-column: 2; }
}
.person figure { margin: 0 0 var(--s4); }

.cv { margin: 0; border-top: 1px solid var(--rule); }
.cv > div { padding: var(--s3) 0; border-bottom: 1px solid var(--rule); }
.cv dt { font-size: var(--label); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: var(--s1); }
.cv dd { margin: 0; font-size: 1rem; max-width: none; }

/* ── the desktop rail ───────────────────────────────────────
   Above 1200px the reading column stops around 800px and the rest of the
   window was empty paper. This fills it with the things a stranger actually
   came for, and keeps the telephone number on screen at every scroll position
   — which on desktop it previously was not, at any scroll position.

   It is display:none below the breakpoint rather than reflowed underneath:
   every item in it already appears in the page or the footer on a phone, and
   a second copy stacked mid-article would be duplication, not responsiveness. */
.rail { display: none; }

@media (min-width: 1200px) {
  .layout {
    max-width: 66rem;
    display: grid; grid-template-columns: minmax(0, 1fr) 18rem;
    gap: var(--s7); align-items: start;
  }
  .rail { display: block; position: sticky; top: var(--s5); }
  /* The rail carries the open/closed line, so the copy in the article body is
     a second answer to a question already answered two inches to the right.
     Targets the body column only — the rail's own is a sibling of this div. */
  .layout > div .status { display: none; }
}

.rail > * + * { margin-top: var(--s4); }
.rail .call {
  display: block; font-family: var(--serif); font-size: 1.75rem; font-weight: 600;
  line-height: 1.1; color: var(--crimson); text-decoration: none;
}
.rail .call:hover { color: var(--crimson-2); }
.rail .call .lbl { display: block; font-family: var(--sans); font-size: var(--label); font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: var(--s1); }
.rail .wa { display: block; font-family: var(--serif); font-size: 1.2rem; font-weight: 600; text-decoration: none; }
.rail .status { margin: 0; }
.rail address { font-style: normal; font-size: var(--small); color: var(--muted); line-height: 1.55; padding-top: var(--s4); border-top: 1px solid var(--rule); }
.rail address b { color: var(--ink); font-weight: 600; display: block; }
.rail .go { display: block; font-size: var(--small); font-weight: 600; text-decoration: none; }

/* ── reviews ────────────────────────────────────────────────
   Seven identical accent-ruled italic serif blocks stacked vertically read as
   one long monotone, and the review worth the whole page looked exactly like
   "Great experience". Italic also costs legibility precisely for the readers
   this site is calibrated around, so the quotes are roman. */
.score { display: flex; align-items: baseline; gap: var(--s4); flex-wrap: wrap; margin: 0 0 var(--s5); }
.score .n { font-family: var(--serif); font-size: 4rem; font-weight: 600; line-height: 1; color: var(--ink); }
.score .of { font-size: 1.1rem; color: var(--muted); }
.score .of a { display: block; }

.revs { margin: var(--s5) 0; }
@media (min-width: 900px) { .revs { columns: 2; column-gap: var(--s5); } }
.rev {
  background: #fff; border: 1px solid var(--rule); padding: var(--s5);
  margin: 0 0 var(--s5); break-inside: avoid;
}
.rev .stars { margin-bottom: var(--s3); }
.rev p { font-size: 1.05rem; line-height: 1.6; margin: 0 0 var(--s3); max-width: none; }
.rev .by { font-family: var(--serif); font-size: var(--small); font-weight: 600; }
.rev .when { font-size: var(--meta); color: var(--muted); }

/* ── the pulled statement — the site's thesis, out of the body ── */
.thesis {
  font-family: var(--serif); font-size: 1.65rem; line-height: 1.3; font-weight: 600;
  margin: var(--s7) 0; max-width: 30rem; color: var(--ink);
}

/* ── buttons ────────────────────────────────────────────── */

.actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin: var(--s5) 0; }
.btn {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: 3.2rem; padding: var(--s3) var(--s5);
  font-family: var(--sans); font-size: 1.05rem; font-weight: 600;
  text-decoration: none; border: 2px solid var(--crimson);
}
.btn-primary { background: var(--crimson); color: #fff; }
.btn-primary:hover { background: var(--crimson-2); border-color: var(--crimson-2); color: #fff; }
.btn-ghost { background: transparent; color: var(--crimson); }
.btn-ghost:hover { background: var(--paper-2); color: var(--crimson-2); }

/* These MUST follow .btn. Declared before it, .btn's `border:` shorthand
   resets their border-color to --crimson, which on the crimson hero renders a
   crimson border on a crimson ground — a button with no visible edge. */
.btn-light { background: #fff; color: var(--crimson); border-color: #fff; }
.btn-light:hover { background: var(--paper-2); color: var(--crimson-2); border-color: var(--paper-2); }
.btn-outline { background: transparent; color: #fff; border-color: rgba(255,255,255,.75); }
.btn-outline:hover { background: rgba(255,255,255,.12); color: #fff; }


/* ── photos ─────────────────────────────────────────────── */

figure { margin: var(--s6) 0; }
figure img { width: 100%; height: auto; display: block; }
figcaption { font-size: var(--meta); color: var(--muted); margin-top: var(--s2); max-width: var(--measure); }

/* No cover-crop and no max-height. Letterboxing the shopfront to 30rem cut the
   shop itself out of the bottom of the frame and left a strip of night sky, and
   it rendered the locator shot as a near-black bar. With two photographs in
   existence, the treatment must not be what destroys them. */
.photoband { margin: var(--s6) 0 0; }
.photoband figcaption { max-width: var(--shell); margin-inline: auto; padding: var(--s2) var(--pad) 0; }
.wrap .photoband { margin: var(--s5) 0; }
.wrap .photoband figcaption { padding-inline: 0; }

.map { width: 100%; height: 22rem; border: 1px solid var(--rule); display: block; }

/* ── mobile call bar ────────────────────────────────────── */

.callbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: grid; grid-template-columns: 1fr 1fr;
  border-top: 1px solid var(--rule); background: var(--paper);
  padding-bottom: env(safe-area-inset-bottom);
  box-shadow: 0 -2px 12px rgba(30,26,28,.10);
}
.callbar a {
  min-height: 3.75rem; display: flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: 1.05rem; text-decoration: none;
  color: #fff; background: var(--crimson);
}
/* WhatsApp carries equal weight — for a large share of these patients it is
   the channel they actually use. */
.callbar a + a { background: var(--crimson-2); border-left: 1px solid rgba(255,255,255,.25); }
.callbar[hidden] { display: none; }
@media (min-width: 880px) { .callbar { display: none; } }

/* ── footer ─────────────────────────────────────────────── */

.foot { border-top: 1px solid var(--rule); background: var(--paper-2); font-size: var(--small); margin-top: var(--s8); }
.foot .wrap { padding-top: var(--s6); padding-bottom: var(--s6); display: grid; gap: var(--s5); }
@media (min-width: 760px) { .foot .wrap { grid-template-columns: 1.4fr 1fr; } }
.foot p { margin: 0 0 var(--s1); color: var(--muted); }
.foot strong { color: var(--ink); }
.foot ul { list-style: none; margin: 0; padding: 0; }
.foot li { margin-bottom: var(--s1); }


/* ── the certificates ───────────────────────────────────────
   Four framed documents, photographed on the wall they hang on. They are
   evidence, so they are shown as photographs of objects rather than cropped to
   the paper — a scan reads as a file, a frame on a wall reads as a place.

   Two columns from the smallest phone, like .people, and for the same reason:
   stacked at full width, four portrait photographs are most of a screen each
   and the reader scrolls past 2,000px of certificate before reaching the
   sentence that says why any of it matters.

   The four crops are deliberately NOT one aspect ratio — three portrait, one
   landscape. `align-items: start` lets each keep its own height instead of
   letterboxing the landscape one into a portrait box, and the ragged bottom
   edge is what a real wall looks like. */
.cert-grid {
  list-style: none; padding: 0; margin: var(--s5) 0 var(--s6);
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4);
  align-items: start;
}
@media (min-width: 760px) { .cert-grid { gap: var(--s6); } }
.cert-grid img { width: 100%; height: auto; display: block; border-radius: 2px; }
.cert-grid figcaption { margin-top: var(--s2); }
/* The institution wraps; the credential name is the line that must not be
   broken mid-word into an unreadable stack in a 150px column. */
.cert-grid b { display: block; font-size: 13px; line-height: 1.3; overflow-wrap: break-word; }
.cert-grid span { display: block; font-size: 12px; color: var(--muted); line-height: 1.35; overflow-wrap: break-word; }
