/* site.css — the reforge visual system.
   Minimalist editorial layout, glass surfaces, Urban Optics' measured brand.
   NOTE: no colour literal appears in this file by design — see tokens.css. */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink-body);
  font: var(--fs-body)/var(--lh-body) var(--sans);
  font-synthesis-weight: none;
  overflow-x: hidden;
}
img { max-width: 100%; height: auto; }
a { color: var(--accent-ink); text-underline-offset: 0.18em; text-decoration-thickness: 1px; }
a:hover { color: var(--accent); }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; border-radius: var(--r-sm); }
.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; }
.skip { position: absolute; left: -9999px; top: 0; z-index: 100; background: var(--deep); color: var(--on-deep); padding: 0.75rem 1.1rem; border-radius: 0 0 var(--r-sm) 0; }
.skip:focus { left: 0; }

.shell { width: min(100% - (var(--gutter) * 2), var(--shell)); margin-inline: auto; }

/* ── type ─────────────────────────────────────────────────────────────── */
h1, h2, h3, h4 { font-family: var(--serif); color: var(--ink); font-weight: 500; line-height: var(--lh-tight); letter-spacing: -0.015em; margin: 0 0 0.5em; font-variation-settings: 'SOFT' 0, 'WONK' 0; }
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); letter-spacing: -0.005em; }
p { margin: 0 0 1.15em; }
.kicker {
  font: 700 var(--fs-kicker)/1 var(--sans);
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 1.1rem; display: flex; align-items: center; gap: 0.7rem;
}
.kicker::after { content: ''; flex: 1; height: 1px; background: var(--line); }
.lede { font-size: var(--fs-lede); color: var(--ink-body); max-width: 56ch; }
.fine { font-size: var(--fs-small); color: var(--ink-mute); }

/* ── buttons ──────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  padding: 0.86rem 1.5rem; border-radius: var(--r-pill);
  font: 700 var(--fs-small)/1 var(--sans); letter-spacing: 0.04em;
  text-decoration: none; border: 1px solid transparent; cursor: pointer;
  transition: transform var(--dur) var(--ease), background-color var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.btn-solid { background: var(--accent); color: var(--white); }
.btn-solid:hover { background: var(--accent-ink); color: var(--white); transform: translateY(-2px); }
.btn-ghost { border-color: var(--line); color: var(--ink); background: var(--glass); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur); }
.btn-ghost:hover { border-color: var(--ink); color: var(--ink); transform: translateY(-2px); }

/* ── glass ────────────────────────────────────────────────────────────── */
[data-glass] {
  background: var(--glass);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--glass-brd);
  box-shadow: var(--glass-shadow);
}
@supports not (backdrop-filter: blur(1px)) { [data-glass] { background: var(--paper-2); } }

/* ── header ───────────────────────────────────────────────────────────── */
.site-head { position: sticky; top: 0; z-index: 50; border-inline: 0; border-top: 0; border-radius: 0; }
.head-in { display: flex; align-items: center; gap: clamp(1rem, 3vw, 2.4rem); padding: 0.85rem 0; }
.brand { display: flex; align-items: center; gap: 0.7rem; text-decoration: none; color: var(--ink); flex: none; }
/* The client's own logo lockup, replacing the drawn mark and the set wordmark.
   It is a STACKED lockup — glasses over URBAN OPTICS over EYECARE — so it needs
   height to stay legible, but the header's own height is set by the CTA button
   at 44.8px. 44px is therefore the most it can take without making the header
   taller; measured, the bar stays 72px from 1024 to 1440. */
.brand-logo { height: clamp(36px, 3.2vw, 44px); width: auto; display: block; }
/* The footer sits on --deep. The artwork is pure black on transparency, so it
   is inverted to white there and held at --on-deep's own 0.94. */
.foot-brand .brand-logo { filter: invert(1); opacity: 0.94; margin-bottom: 1rem; }
.nav { flex: 1; min-width: 0; }
.nav-l1 { list-style: none; display: flex; gap: clamp(0.6rem, 1.6vw, 1.5rem); margin: 0; padding: 0; }
.nav-l1 > li > a { font: 600 var(--fs-small)/1 var(--sans); color: var(--ink); text-decoration: none; padding: 0.5rem 0; display: inline-block; border-bottom: 1px solid transparent; }
.nav-l1 > li > a:hover { border-bottom-color: var(--accent); color: var(--ink); }
.has-menu { position: relative; }
/* A 720px panel centred on a nav item overflows the viewport when the item sits
   near the left edge. Widening the anchor to the whole nav keeps it on screen. */
.nav { position: relative; }
.has-menu:has(.menu-wide) { position: static; }
.menu-wide { left: 0; translate: 0 8px; }
.has-menu:hover .menu-wide, .has-menu:focus-within .menu-wide { translate: 0 0; }
.menu {
  position: absolute; top: calc(100% + 0.7rem); left: 50%; translate: -50% 0;
  width: min(92vw, 420px); padding: 1.3rem; border-radius: var(--r);
  opacity: 0; visibility: hidden; translate: -50% 8px;
  transition: opacity var(--dur) var(--ease), translate var(--dur) var(--ease), visibility var(--dur);
}
.has-menu:hover .menu, .has-menu:focus-within .menu { opacity: 1; visibility: visible; translate: -50% 0; }
/* A menu is read, not admired. At 58% the hero type showed straight through the
   panel and the links became unreadable (reported from the live preview), so the
   menu opts out of the glass fill and keeps only the blur behind it. */
.menu, .menu-wide { background: var(--menu-bg); }
.menu-note { font-size: var(--fs-small); color: var(--ink-mute); margin: 0 0 0.9rem; padding-bottom: 0.9rem; border-bottom: 1px solid var(--line-soft); }
.menu ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.15rem; max-height: 52vh; overflow-y: auto; }
/* A grouped item (Services) shows its clusters as headed columns. */
.menu-wide { width: min(94vw, 720px); display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.4rem 1.6rem; }
.menu-wide .menu-note { grid-column: 1 / -1; }
.menu-col > .menu-head { display: block; font: 700 var(--fs-kicker)/1 var(--sans); letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); text-decoration: none; padding: 0.3rem 0.55rem 0.5rem; }
.menu-col > .menu-head:hover { color: var(--accent-ink); }
.menu a { display: block; padding: 0.42rem 0.55rem; border-radius: var(--r-sm); font-size: var(--fs-small); color: var(--ink-body); text-decoration: none; }
.menu a:hover { background: var(--paper-2); color: var(--ink); }
.head-cta { display: flex; align-items: center; gap: 1rem; flex: none; }
.tel { font: 700 var(--fs-small)/1 var(--sans); color: var(--ink); text-decoration: none; white-space: nowrap; }
.burger { display: none; flex-direction: column; gap: 5px; background: none; border: 0; padding: 0.5rem; cursor: pointer; }
.burger span { width: 22px; height: 2px; background: var(--ink); display: block; transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease); }
.m-nav { display: none; }

/* ── hero ─────────────────────────────────────────────────────────────── */
.hero { position: relative; padding: calc(var(--band) * 0.9) 0 var(--band); }
.hero-home {
  background: var(--deep); color: var(--on-deep); overflow: hidden;
  padding: clamp(5rem, 12vw, 9.5rem) 0 clamp(4rem, 9vw, 7rem);
}
.hero-bg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0.26; z-index: 0;
}
.hero-home::after {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(70% 90% at 15% 20%, var(--deep-2), transparent 70%);
  mix-blend-mode: multiply;
}
.hero-home .shell { position: relative; z-index: 1; }
.hero-home h1 { color: var(--on-deep); font-size: var(--fs-display); max-width: 14ch; font-weight: 400; }
.hero-home .lede { color: var(--on-deep-mute); }
.hero-home .kicker { color: var(--on-deep-mute); }
.hero-home .kicker::after { background: var(--glass-dark-brd); }
.hero-cta { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 2.2rem; }
.hero-home .btn-ghost { background: var(--glass-dark); border-color: var(--glass-dark-brd); color: var(--on-deep); }
.hero-home .btn-ghost:hover { border-color: var(--on-deep); color: var(--on-deep); }
.hero-page { padding: clamp(2.2rem, 5vw, 4rem) 0 clamp(1.6rem, 3vw, 2.6rem); border-bottom: 1px solid var(--line-soft); }
.hero-page h1 { max-width: 20ch; }

/* ── breadcrumbs ──────────────────────────────────────────────────────── */
.crumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: 0.45rem; margin: 0 0 1.5rem; padding: 0; font-size: var(--fs-small); color: var(--ink-mute); }
.crumbs li + li::before { content: '/'; margin-right: 0.45rem; color: var(--line); }
.crumbs a { color: var(--ink-mute); text-decoration: none; }
.crumbs a:hover { color: var(--accent); }

/* ── bands + cards ────────────────────────────────────────────────────── */
.band { padding: var(--band) 0; }
.band-read { padding-top: 0; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1rem; }
.card { display: flex; flex-direction: column; gap: 0.5rem; padding: 1.6rem; border-radius: var(--r); text-decoration: none; color: var(--ink-body); transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.card:hover { transform: translateY(-4px); border-color: var(--accent); }
.card h3 { margin: 0; }
.card p { margin: 0; font-size: var(--fs-small); color: var(--ink-mute); }
.card-go { margin-top: auto; padding-top: 0.9rem; font: 700 var(--fs-kicker)/1 var(--sans); letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); }

/* ── reading layout ───────────────────────────────────────────────────── */
/* Column 1 is sized to the measure, not to 1fr: at 1180px a 1fr column was 828px
   wide holding a 670px measure, so 158px sat dead inside it and another 510px to
   the right of the prose. space-between puts that slack in the gutter, which
   leaves content on both edges. */
.layout-read { display: grid; grid-template-columns: minmax(0, var(--measure)) 300px; justify-content: space-between; gap: clamp(2rem, 4vw, 3.5rem); align-items: start; padding-bottom: var(--band); }
/* On 209 pages .layout-read sits directly under <main> and its padding-bottom IS
   the only spacer before the facts card. The home page is the one page that wraps
   it in .band, which already pays var(--band) top and bottom — so there the two
   stacked and the measured gap under the last section came to 253px against the
   101px every other page shows. Pay it once, in the band. */
.band > .layout-read { padding-bottom: 0; }
/* The rail precedes the prose in SOURCE order (so the prose is the last text in
   <main>); grid placement puts it back on the right visually. */
.layout-read > .prose { grid-column: 1; grid-row: 1; }
/* The rail is sticky, but a sticky element can only travel inside its own
   containing block. With align-items:start the aside was 636px tall in a 1666px
   row, so the rail slid out of view a third of the way down the article and left
   the right-hand column empty for the rest of it. Stretching the aside to the
   row gives the rail the whole article to follow the reader through. */
.layout-read > .aside { grid-column: 2; grid-row: 1; align-self: stretch; }
/* When there is no rail, do not hold 280px open for it and do not leave the prose
   hugging the left edge of a 1180px shell — centre the measure so the margins match. */
.layout-single { grid-template-columns: minmax(0, 1fr); }
.layout-single > .prose { grid-column: 1; margin-inline: auto; max-width: 74ch; }
.prose { max-width: var(--measure); }
/* The article's own trailing margin is not spacing anything — the layout below it
   is. Letting it through added another 51px under the last section. */
.prose > :last-child { margin-bottom: 0; }
.prose > h2 { margin-top: 2.4em; padding-top: 1.1rem; border-top: 1px solid var(--line-soft); }
.prose > h3 { margin-top: 1.9em; }
.prose > p:first-of-type { font-size: var(--fs-lede); }
.prose ul, .prose ol { padding-left: 1.15rem; margin: 0 0 1.3em; }
.prose li { margin-bottom: 0.5em; }
.prose li::marker { color: var(--accent); }
.prose img { border-radius: var(--r); margin: 1.6rem 0; }
.prose blockquote { margin: 2rem 0; padding: 0 0 0 1.4rem; border-left: 2px solid var(--accent); font-family: var(--serif); font-size: var(--fs-lede); color: var(--ink); }
.prose table { width: 100%; border-collapse: collapse; margin: 1.6rem 0; font-size: var(--fs-small); }
.prose th, .prose td { text-align: left; padding: 0.7rem 0.6rem; border-bottom: 1px solid var(--line-soft); }
.prose th { font: 700 var(--fs-kicker)/1.3 var(--sans); letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-mute); }
/* ── grids recovered from the source prose ────────────────────────────────
   The source builds these as a flat run of siblings; prose-grids.mjs reads the
   shape and wraps each run. Reading text stays at the measure, grids take the
   full column — which is what turns a tall ribbon down the left into a page. */
.page-home .layout-single > .prose { max-width: none; }
.page-home .prose > p,
.page-home .prose > ul:not([class]),
.page-home .prose > ol:not([class]),
.page-home .prose > h2,
.page-home .prose > h3 { max-width: var(--measure); }

.brand-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: clamp(1rem, 2vw, 1.6rem);
  margin: 2rem 0 2.6rem;
}
.brand-card { margin: 0; }
.brand-card img {
  width: 100%; aspect-ratio: 5 / 6; object-fit: cover;
  border-radius: var(--r); display: block; margin: 0;
}
.brand-card figcaption {
  margin-top: 0.7rem;
  font: 700 var(--fs-kicker)/1.3 var(--sans);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink);
}

.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(1.2rem, 2.5vw, 2.2rem);
  margin: 2rem 0 2.6rem;
}
.feature-card {
  padding: 1.6rem; border-radius: var(--r);
  background: var(--paper-2); border: 1px solid var(--line-soft);
}
.feature-card h3 {
  font: 700 var(--fs-kicker)/1.3 var(--sans);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 0.7rem;
}
.feature-card p { margin: 0; font-size: var(--fs-small); max-width: none; }

.prose img.prose-banner {
  width: 100%; max-width: none; border-radius: var(--r);
  display: block; margin: 1.8rem 0 2.2rem;
}
/* ── the connect strip ────────────────────────────────────────────────────
   The home page's prose OPENED with the source site's own header markup: a Yelp
   badge, four bare underlined links, the boxed logo, a second "Request an
   Appointment" and the tagline repeated in grey directly under a hero that
   already states it. prose-grids.mjs keeps every one of those - all six links,
   both images, the tagline - and arranges them here instead of leaving them
   stacked down the left as leaked source. The four social and review
   destinations appear NOWHERE else in the build, footer included.

   Hover states use the palette's own tokens rather than each network's brand
   hex. That keeps the strip inside the design system and keeps C13 honest:
   every colour here resolves to a token. */
/* A compact glass strip in the same language as the service cards: [data-glass]
   surface, the .kicker label with its hairline, and .btn-ghost-style pills.
   Scoped under .connect because it sits inside .prose, whose .prose img /
   .prose a / .prose ul (0,1,1) would otherwise out-specify it. */
.connect {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(1.2rem, 3.5vw, 2.6rem);
  align-items: center;
  margin: 0 0 clamp(2.2rem, 5vw, 3.4rem);
  padding: clamp(1rem, 2.4vw, 1.4rem) clamp(1.2rem, 3vw, 2rem);
  border-radius: var(--r);
}
.connect .c-brand {
  display: block; margin: 0; padding: 0 clamp(1.2rem, 3.5vw, 2.6rem) 0 0;
  border-right: 1px solid var(--line);
}
/* The black box is part of the logo BITMAP, not a border that can be turned off.
   unbox-logo.mjs measures and crops the file instead; nothing to trim here. */
.connect .c-logo { display: block; width: auto; height: clamp(44px, 5vw, 56px); margin: 0; max-width: none; border-radius: 0; }
.connect .c-side { display: flex; flex-direction: column; gap: 0.8rem; min-width: 0; }
.connect .c-label {
  margin: 0; max-width: none;
  font: 700 var(--fs-kicker)/1 var(--sans);
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--accent);
  display: flex; align-items: center; gap: 0.7rem;
}
.connect .c-label::after { content: ''; flex: 1; height: 1px; background: var(--line); }
.connect .c-chips {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.6rem;
}
.connect .c-chips li { margin: 0; padding: 0; }
.connect .c-chip {
  display: flex; align-items: center; justify-content: center; gap: 0.5rem; width: 100%; white-space: nowrap;
  margin: 0; padding: 0.6rem 1.05rem; min-height: 40px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  font: 700 var(--fs-small)/1 var(--sans); letter-spacing: 0.02em; text-decoration: none;
  transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.connect .c-chip svg { width: 16px; height: 16px; flex: 0 0 auto; fill: currentColor; }
/* an outline-drawn icon must keep fill:none — the rule above is a declaration
   and would otherwise beat the inline fill attribute, filling it solid */
.connect .c-chip svg[data-outline] { fill: none; }
.connect .c-yelp-img { height: 18px; width: auto; margin: 0; max-width: none; display: block; border-radius: 0; }
.connect .c-chip:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) { .connect .c-chip:hover { transform: none; } }
@media (max-width: 1000px) { .connect .c-chips { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 440px) {
  .connect .c-chips { display: flex; flex-wrap: wrap; }
  .connect .c-chips li { flex: 1 1 auto; }
  .connect .c-chip { padding-inline: 0.85rem; }
}
@media (max-width: 640px) {
  .connect { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  .connect .c-brand { padding: 0 0 1rem; border-right: 0; border-bottom: 1px solid var(--line); }
}
/* .c-ico: the 44px circle icon, reused outside this strip */
.c-ico {
  display: grid; place-items: center;
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--paper); border: 1px solid var(--line);
  color: var(--ink); text-decoration: none;
  transition: transform var(--dur) var(--ease), background-color var(--dur) var(--ease),
              color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.c-ico svg { width: 18px; height: 18px; fill: currentColor; }
/* an outline-drawn icon must keep fill:none — the rule above is a declaration
   and would otherwise beat the inline fill attribute, filling it solid */
.c-ico svg[data-outline] { fill: none; }
.c-ico:hover { transform: translateY(-2px); background: var(--accent); color: var(--white); border-color: transparent; }

/* ── editorial sections ───────────────────────────────────────────────────
   An eyebrow label and the heading it introduces are one unit, and grouping
   them also decides the layout. Running copy becomes a two-column block whose
   body fills the width a single column was leaving empty; a section holding a
   grid or a banner gets a tight full-width header instead of a label stranded
   above a gap. Both are what turns the reported empty right-hand half into
   something that reads as a page. */
/* The eyebrow now carries the same device as .kicker — a hairline running out
   to the edge — instead of the source's dash bitmap, which rendered as a 237x8
   grey bar floating above the label and read as a stray artifact. Every one of
   those images was alt="", so nothing was lost with them.
   The colour was --ink-mute, which measures 4.42:1 on --paper and 4.01:1 on
   --paper-2: below AA for 12px bold. --ink-body is 9.13:1 and 8.29:1. */
.ed-eyebrow {
  margin: 0 0 0.5rem; max-width: none;
  font: 700 var(--fs-kicker)/1.3 var(--sans);
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-body);
  display: flex; align-items: center; gap: 0.7rem;
}
.ed-eyebrow::after { content: ''; flex: 1; height: 1px; background: var(--line); }

/* Default: one column. An interior page's prose column is already only as wide
   as the measure, so splitting it there squeezed body copy to about 320px —
   worse than the gap it was meant to close. The header just sits tight above
   its section instead. */
.ed-split {
  margin: 3.2rem 0;
  padding-top: 1.8rem;
  border-top: 1px solid var(--line-soft);
}
.ed-split .ed-head { margin-bottom: 1rem; }
/* Two columns only where the container is full width and can carry them. */
.page-home .ed-split {
  display: grid;
  grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: start;
}
.page-home .ed-split .ed-head { margin-bottom: 0; }
.ed-split .ed-head h2, .ed-split .ed-head h3 { margin: 0; max-width: none; }
.ed-split .ed-body > :first-child { margin-top: 0; }
.ed-split .ed-body > :last-child { margin-bottom: 0; }
.ed-split .ed-body > p,
.ed-split .ed-body > ul,
.ed-split .ed-body > ol { max-width: none; }

.ed-wide { margin: 3.2rem 0; }
.ed-bar {
  padding-top: 1.8rem; margin-bottom: 1.4rem;
  border-top: 1px solid var(--line-soft);
}
.ed-bar h2, .ed-bar h3 { margin: 0; max-width: none; }
.ed-wide > p,
.ed-wide > ul:not([class]),
.ed-wide > ol:not([class]) { max-width: var(--measure); }
/* Both forms are needed: .prose img.prose-banner is (0,2,1) and out-specifies a
   bare .ed-wide > :last-child (0,2,0), which left 35px under a section that ends
   on a banner. The .prose-scoped selector is (0,3,0) and settles it. */
.ed-wide > :last-child,
.prose .ed-wide > :last-child { margin-bottom: 0; }

@media (max-width: 760px) {
  /* must out-specify .page-home .ed-split, or the two-column rule wins here */
  .page-home .ed-split { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .page-home .ed-split .ed-head { margin-bottom: 1rem; }
}

.idx { list-style: none; padding: 0; columns: 2; column-gap: 2rem; }
.idx li { break-inside: avoid; margin-bottom: 0.5em; }

/* ── sibling rail ─────────────────────────────────────────────────────── */
.rail { position: sticky; top: 6.5rem; }
.rail h2 { font: 700 var(--fs-kicker)/1.3 var(--sans); letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-mute); padding-bottom: 0.8rem; border-bottom: 1px solid var(--line); margin-bottom: 0.4rem; }
.rail ul { list-style: none; margin: 0; padding: 0; max-height: 60vh; overflow-y: auto; }
.rail a { display: block; padding: 0.55rem 0; font-size: var(--fs-small); color: var(--ink-body); text-decoration: none; border-bottom: 1px solid var(--line-soft); }
.rail a:hover { color: var(--accent); padding-left: 0.35rem; }

/* ── facts ────────────────────────────────────────────────────────────── */
.facts { margin: 0 auto var(--band); width: min(100% - (var(--gutter) * 2), var(--shell)); padding: clamp(1.8rem, 4vw, 3rem); border-radius: var(--r-lg); }
.facts-h { font-size: var(--fs-h2); margin-bottom: 1.6rem; }
.facts-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: clamp(1.4rem, 3vw, 2.6rem); }
.facts h3 { font: 700 var(--fs-kicker)/1 var(--sans); letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-mute); margin-bottom: 0.9rem; }
.facts p { font-size: var(--fs-small); }
.addr { font-style: normal; }
.hours { margin: 0; font-size: var(--fs-small); }
.hours > div { display: flex; justify-content: space-between; gap: 1rem; padding: 0.32rem 0; border-bottom: 1px solid var(--line-soft); }
.hours dt { color: var(--ink-mute); }
.hours dd { margin: 0; font-weight: 700; color: var(--ink); }
.facts-cta { display: flex; flex-direction: column; align-items: flex-start; gap: 0.6rem; }

/* ── search ───────────────────────────────────────────────────────────── */
.search { display: grid; gap: 0.7rem; margin-bottom: 2rem; }
.search label { font: 700 var(--fs-kicker)/1 var(--sans); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-mute); }
.search input { padding: 0.9rem 1.1rem; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--white); color: var(--ink); font: var(--fs-body) var(--sans); }
.search .btn { justify-self: start; }
/* The stacked grid is right for the search PAGE's own form. The mini form is a
   footer utility: its label is .sr-only and therefore out of flow, so the grid
   holds exactly two items and they read better as one row than as a pill with a
   button orphaned underneath it. */
.search-mini { grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 0.5rem; margin-bottom: 0; }

/* ── preserved patient forms ──────────────────────────────────────────────
   The contact and registration forms ship from the source with every field,
   label and legend intact. They arrive with no styling of their own, so the
   design system gives them one — and gives every control a 44px box, which is
   what the responsive sweep measures. */
.pform { margin: 2rem 0; }
.pform ul, .pform ol { list-style: none; margin: 0 0 1.4rem; padding: 0; display: grid; gap: 1rem; }
.pform li { margin: 0; }
.pform fieldset { border: 0; border-top: 1px solid var(--line); margin: 2.4rem 0 1.4rem; padding: 1.4rem 0 0; }
.pform legend { font-family: var(--serif); font-size: var(--fs-h3); color: var(--ink); padding: 0; }
.pform label { display: block; font: 700 var(--fs-small)/1.4 var(--sans); color: var(--ink); margin-bottom: 0.4rem; }
.pform input, .pform select, .pform textarea {
  width: 100%;
  min-height: 44px;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--white);
  color: var(--ink);
  font: var(--fs-body)/1.4 var(--sans);
}
.pform textarea { min-height: 120px; resize: vertical; }
.pform input[type="checkbox"], .pform input[type="radio"] {
  /* A 44x44 border box with padding keeps the glyph a comfortable ~24px while the
     TARGET is the full 44px the sweep measures and a thumb actually needs. This
     is a 200-field medical intake form: large checkboxes are a usability win. */
  width: 44px; height: 44px; min-height: 44px; padding: 10px; margin: 0 0.4rem 0 0;
  vertical-align: middle; accent-color: var(--accent); flex: none;
}
/* A checkbox or radio is a 24px control in a 44px row, so the ROW is the target. */
.pform input[type="checkbox"] + label, .pform input[type="radio"] + label,
.pform label:has(input[type="checkbox"]), .pform label:has(input[type="radio"]) {
  display: inline-flex; align-items: center; min-height: 44px; font-weight: 400; margin: 0;
}
.pform input + label, .pform select + label, .pform textarea + label {
  font: 400 var(--fs-small)/1.4 var(--sans);
  color: var(--ink-mute);
  margin: 0.3rem 0 0;
}
/* Spam honeypots. Gravity Forms and Akismet ship decoy fields that the source
   hides with its own CSS; that CSS is gone, so they rendered — a stray "Δ"
   textarea and two "This field is for validation purposes and should be left
   unchanged" inputs sat visibly at the end of both patient forms. They are moved
   off-screen rather than deleted, which is how the source behaved and keeps the
   content parity the rebuild is measured on. Not display:none — a bot that reads
   CSS treats that as a tell.
   The decoy's sentence is a bare text node inside the <li>, not inside the label,
   so the CONTAINER has to move — hiding the input alone leaves the sentence.
   Both decoys duplicate a field that genuinely exists on the same form
   (registration: a second "Email" beside input_5; contact: a second "Phone"
   beside input_10), which is how they were identified. */
.pform li:has(> input[id="input_12_93"]),
.pform li:has(> input[id="input_10_12"]),
.pform p:has(textarea[name="ak_hp_textarea"]) {
  position: absolute !important;
  left: -9999px !important;
  top: 0;
}
.pform input:focus-visible, .pform select:focus-visible, .pform textarea:focus-visible { border-color: var(--focus); }
.pform button[type="submit"] { margin-top: 1.4rem; }

/* ── footer ───────────────────────────────────────────────────────────── */
.site-foot { background: var(--deep); color: var(--on-deep-mute); padding: var(--band) 0 2rem; }
/* An auto-fit repeat() cannot sit beside a flexible track, so the previous
   "1.4fr repeat(auto-fit, minmax(150px,1fr))" was invalid: the browser dropped the
   whole declaration and the footer collapsed to a single 1180px column with all
   seven blocks stacked down the left edge. Measured on the live preview.
   One auto-repeat only, with the brand block spanning two tracks. */
.foot-in { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: clamp(1.6rem, 3vw, 2.6rem) clamp(1.4rem, 2.5vw, 2.2rem); }
.site-foot h2 { font: 700 var(--fs-kicker)/1 var(--sans); letter-spacing: 0.16em; text-transform: uppercase; color: var(--on-deep); margin-bottom: 1.1rem; }
.site-foot a { color: var(--on-deep-mute); text-decoration: none; }
.site-foot a:hover { color: var(--on-deep); }
.site-foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.45rem; font-size: var(--fs-small); }
.site-foot .hours > div { border-bottom-color: var(--glass-dark-brd); }
.site-foot .hours dt { color: var(--on-deep-mute); }
.site-foot .hours dd { color: var(--on-deep); }
.foot-tag { font: 400 1.25rem/1.25 var(--serif); color: var(--on-deep); margin-bottom: 1rem; }
.foot-brand p { font-size: var(--fs-small); }
.foot-legal { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1rem; margin-top: var(--band); padding-top: 1.6rem; border-top: 1px solid var(--glass-dark-brd); font-size: var(--fs-small); }
.foot-legal ul { display: flex; flex-wrap: wrap; gap: 1.2rem; }
.foot-legal p { margin: 0; }

/* ── responsive ───────────────────────────────────────────────────────── */
@media (max-width: 1024px) {
  .layout-read { grid-template-columns: minmax(0, 1fr); }
  .layout-read > .prose { grid-column: 1; grid-row: 1; }
  .layout-read > .aside { grid-column: 1; grid-row: 2; }
  .rail { position: static; }
  .rail ul { max-height: none; columns: 2; column-gap: 2rem; }
  .rail li { break-inside: avoid; }
}
@media (max-width: 900px) {
  .nav, .head-cta .btn { display: none; }
  .burger { display: flex; }
  .head-in { justify-content: space-between; }
  .m-nav { display: block; border-top: 1px solid var(--line-soft); max-height: 70vh; overflow-y: auto; padding: 1rem var(--gutter) 2rem; }
  .m-nav[hidden] { display: none; }
  .m-nav > ul { list-style: none; margin: 0; padding: 0; }
  .m-nav strong { display: block; font: 700 var(--fs-kicker)/1 var(--sans); letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-mute); margin: 1.2rem 0 0.5rem; }
  .m-nav ul ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.1rem; }
  .m-nav a { display: block; padding: 0.5rem 0; color: var(--ink-body); text-decoration: none; border-bottom: 1px solid var(--line-soft); }
}
/* ── touch targets ────────────────────────────────────────────────────────
   WCAG 2.5.5. The responsive sweep measured 128 controls under 44x44 at 390 and
   768px — list links in the menu, footer, rail and breadcrumb, plus the brand,
   the telephone and the burger. Each gets a real 44px box on touch widths.
   Inline links inside prose keep their typography and gain the height through
   block padding, which grows the hit rectangle without moving a single line. */
@media (max-width: 834px) {
  .m-nav a, .menu a, .nav-l1 > li > a,
  .foot-col a, .site-foot ul a, .foot-legal ul a,
  .rail a, .crumbs a, .idx a {
    min-height: 44px;
    min-width: 44px;
    display: flex;
    align-items: center;
  }
  .crumbs ol { align-items: center; }
  .brand, .tel { min-height: 44px; min-width: 44px; display: flex; align-items: center; }
  .burger { min-width: 44px; min-height: 44px; align-items: center; justify-content: center; }
  .search input, .search .btn { min-height: 44px; min-width: 44px; }
  .btn { min-height: 44px; min-width: 44px; }
  /* Links set INSIDE a sentence keep their typography. The hit rectangle is grown
     with padding and the growth is cancelled by an equal negative margin, so the
     target gets bigger while not a single line of text moves. */
  .prose a, .foot-brand p a, .facts p a {
    padding-block: 0.85rem;
    padding-inline: 0.42rem;
    margin-inline: -0.42rem;
  }
  .prose li { margin-bottom: 0.75em; }
}

@media (max-width: 640px) {
  .idx { columns: 1; }
  .rail ul { columns: 1; }
  .foot-legal { flex-direction: column; }
  .hero-home h1 { max-width: none; }
}

/* ── the office-hours notice ──────────────────────────────────────────────
   A native <dialog> opened with showModal(). The top layer clears the preview
   banner (z-index 200) and the sticky .site-head (50) without this component
   owning a z-index at all, and focus containment comes from the platform
   rather than from a hand-rolled trap. The markup is built in scripts/site.js
   — the note there records the source modal verbatim.

   The panel takes .menu's OPAQUE fill, not the glass one, for the reason
   already recorded against .menu: at 58% the page type showed straight through
   and the copy became unreadable. Same evidence, same decision.

   Reused rather than re-declared: .kicker, .c-ico, <dl class="hours">, .btn,
   and the bare h2 element rule. Only the deviations are written here. */
.notice {
  /* every pixel of padding lives on the card, so a click whose target IS the
     dialog can only have landed on the backdrop */
  padding: 0; border: 0; background: none; overflow: visible;
  width: min(100% - (var(--gutter) * 2), 420px);
  max-height: calc(100vh - 2rem);
  color: var(--ink-body);
}
.notice::backdrop { background: var(--scrim); }
.notice-card {
  display: block; max-height: calc(100vh - 2rem); overflow-y: auto;
  /* On a short viewport the card scrolls. Without this, a wheel gesture that
     reaches the card's end chains to the document behind it — measured 260px of
     background scroll at 380x360 — because the script deliberately lets wheel
     events through while the card itself can still scroll. */
  overscroll-behavior: contain;
  padding: clamp(1.4rem, 4vw, 2rem);
  background: var(--menu-bg);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--glass-shadow);
}
/* Three columns, not flex: a spacer the width of the close control balances it,
   so the logo centres on the CARD rather than on the space left beside the
   button. Everything stays in normal flow — no absolute positioning to come
   loose when the card scrolls on a short viewport. */
.notice-top {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) 44px;
  align-items: center; gap: 0.75rem; margin-bottom: 1.3rem;
}
/* the masthead here is the full logo, not the header's smaller instance: the
   card has the width to carry the stacked lockup at a legible size, and it sits
   level with the 44px close control. */
.notice-top .brand-logo { grid-column: 2; height: 52px; margin-inline: auto; }
/* .c-ico supplies the 44px circle, the border, the hover and the 18px svg.
   This is only the reset a <button> needs that an <a> does not. */
.notice-x { grid-column: 3; padding: 0; font: inherit; cursor: pointer; }
/* --accent measures 4.45:1 on --paper and misses AA for 12px bold by 0.05.
   --accent-ink is 9.12:1. An operational notice is not the place to miss. */
.notice .kicker { color: var(--accent-ink); margin-bottom: 0.9rem; }
.notice h2 { margin: 0 0 0.4rem; }
/* the source set this at 30px bold, level with the headline. A dateline for an
   already-effective change reads as a mistake at that weight; the same words,
   set as a dateline, read as "in force since". The words are untouched. */
.notice-when { margin: 0 0 1.2rem; font: 700 var(--fs-small)/1.45 var(--sans); letter-spacing: 0.02em; color: var(--ink-body); }
/* the hours ARE the payload here, so they are set at body size, and dt takes
   --ink-body (9.13:1) rather than .hours' --ink-mute (4.42:1, fails AA) */
.notice .hours { font-size: var(--fs-body); }
.notice .hours dt { color: var(--ink-body); }
/* the hours are the last thing in the card, so the card's own padding closes it */
.notice .hours > div:last-child { border-bottom: 0; }
.notice #notice-d > :last-child { margin-bottom: 0; }

.notice[open] { animation: sr-rise calc(var(--dur) * 0.5) var(--ease) both; }
/* a DISTINCT name, not sr-rise reversed: re-parameterising a finished
   animation does not restart it, so the exit would land instantly in its
   filled after-phase and never fire animationend. The JS does not wait on
   animationend either — it closes on a timer — so neither half can strand a
   modal open, invisible, with focus trapped. */
.notice.is-closing { animation: notice-out calc(var(--dur) * 0.35) var(--ease) both; }
@keyframes notice-out { from { opacity: 1; } to { opacity: 0; transform: translateY(6px); } }

/* the resting state is already visible, so nulling the animation cannot hide
   the dialog. Holding ::backdrop explicitly matters: a blanket opacity:1 here
   would paint the scrim solid and black the page out instead of dimming it. */
@media (prefers-reduced-motion: reduce) {
  .notice[open], .notice.is-closing { animation: none !important; }
  .notice::backdrop { animation: none !important; background: var(--scrim); }
}
/* There is deliberately no `html { overflow: hidden }` scroll lock here.
   Setting overflow on the root destroys the viewport scroll container, which
   unpins every position:sticky element — measured: entering at /#facts-h, the
   sticky .site-head dropped 4268px out of view while the modal was open and
   popped back on dismissal. scripts/site.js blocks the input instead. */

/* ── the brand showcase ───────────────────────────────────────────────────
   "Designer Brand Names" sat on the same --paper as the prose above it with
   four unframed photographs and a heading that, being a link, inherited
   a{color:--accent-ink} plus an underline and read as a raw hyperlink.

   The section now has its own ground. --paper-2 is the system's existing tint
   (it is what .menu a:hover uses), so the block reads as a display case without
   introducing a surface the system does not already own. Deliberately NOT
   [data-glass]: glass is for panels floating over imagery, and .menu already
   records that a translucent fill over live type measured unreadable. */
.ed-showcase {
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(1.5rem, 3vw, 2.5rem);
}
/* the section ground replaces the hairline that separated it from the prose */
.ed-showcase .ed-bar { padding-top: 0; border-top: 0; margin-bottom: 1.5rem; }
.ed-showcase .ed-eyebrow::after { background: var(--line); }

/* a heading that happens to link is still a heading: --ink, no underline, and
   a CSS-drawn chevron for the affordance. content:'' — a shape, not a string,
   so there is nothing for a screen reader to read out. */
.ed-showcase h2 { margin: 0; }
.ed-showcase h2 a {
  color: var(--ink); text-decoration: none;
  display: inline-flex; align-items: center; gap: 0.55rem;
}
.ed-showcase h2 a::after {
  content: ''; flex: none; width: 0.34em; height: 0.34em;
  border-right: 2px solid var(--accent); border-top: 2px solid var(--accent);
  transform: rotate(45deg);
  transition: transform var(--dur) var(--ease);
}
.ed-showcase h2 a:hover { color: var(--accent-ink); }
.ed-showcase h2 a:hover::after { transform: rotate(45deg) translate(3px, -3px); }

/* One frame per card, not five. The tile is the frame; the photograph runs to
   its edges and the caption sits on a ground of its own beneath a hairline, so
   four tonally unrelated photographs (one bright pink, one on white, two
   mid-tone) are reconciled by a common mount rather than by filtering the
   client's own photography. */
.ed-showcase .brand-grid { margin: 0; }
.ed-showcase .brand-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
  /* a two-line caption ("LILLY PULITZER" wraps at phone widths) otherwise left
     its neighbour's tile ending short of it; the caption takes the slack */
  display: flex; flex-direction: column;
}
.ed-showcase .brand-card img { border-radius: 0; }
.ed-showcase .brand-card figcaption {
  margin: 0; padding: 0.8rem 0.9rem; flex: 1;
  border-top: 1px solid var(--line-soft);
  color: var(--ink); display: flex; align-items: center; gap: 0.55rem;
}
.ed-showcase .brand-card figcaption::before {
  content: ''; flex: none; width: 14px; height: 2px; background: var(--accent);
}

/* ── the feature stage ────────────────────────────────────────────────────
   FEATURING was an eyebrow and a bare banner, which is why it read as a stray
   image rather than a feature. It now sits on --deep, the same ground as the
   hero and the footer, which is what makes it register as a spotlight without
   adding a single word the client did not write.

   Contained rather than full-bleed on purpose: `margin-inline: calc(50% - 50vw)`
   would work, but only because body{overflow-x:hidden} clips the scrollbar
   width out of 100vw — a coupling this component should not depend on. */
.ed-feature {
  background: var(--deep);
  border-radius: var(--r-lg);
  padding: clamp(1.3rem, 3vw, 2.2rem);
}
.ed-feature .ed-eyebrow {
  color: var(--on-deep);            /* 15.87:1 on --deep */
  margin: 0 0 1.1rem;
}
/* the paper hairline is invisible here; the dark-surface border token is not */
.ed-feature .ed-eyebrow::after { background: var(--glass-dark-brd); }
/* (0,3,0) — out-specifies .prose img.prose-banner (0,2,1), and matches only
   inside .ed-feature, so the other 20 pages carrying .prose-banner are untouched */
.prose .ed-feature .prose-banner {
  margin: 0; border-radius: var(--r); display: block;
}

@media (max-width: 640px) {
  .ed-showcase, .ed-feature { border-radius: var(--r); }
  /* auto-fit's 190px floor leaves room for one column inside the showcase's own
     padding at 390, which stacked the four plates 1823px deep. Two up holds the
     photographs at ~149px — the same size they take two-up anywhere else — and
     halves the scroll. */
  .ed-showcase .brand-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Card imagery: full-bleed to the card's edges, above the heading */
.card, .feature-card { overflow: hidden; }
.card-media {
  display: block; width: calc(100% + 3.2rem); max-width: none; height: auto;
  aspect-ratio: 3 / 2; object-fit: cover;
  margin: -1.6rem -1.6rem 0.9rem;
}
.card .card-media { transition: transform var(--dur) var(--ease); }
.card:hover .card-media { transform: scale(1.03); }
@media (prefers-reduced-motion: reduce) { .card:hover .card-media { transform: none; } }
/* (0,2,0) out-specifies .prose img (0,1,1), which would reset margin and radius */
.feature-card .card-media { margin: -1.6rem -1.6rem 1.2rem; border-radius: 0; }
