/* ==========================================================================
   TruCare Dentistry — Design System
   Option B: Warm Boutique Serif
   Cream + jewel purple, Playfair Display headlines, Inter body.
   No gradients. No glassmorphism. Soft radii, hairline borders,
   barely-there warm shadows.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens + base — verbatim from the Task 3 brief. Do not edit this block;
   add new tokens/rules in the sections below instead.
   -------------------------------------------------------------------------- */

:root {
  --bg: #F8F5F0; --bg-alt: #EFE9E0; --card: #FFFDFA;
  --accent: #4A2A85; --accent-dark: #3A2168; --ink: #2B2433; --muted: #A39B8E;
  --border: #E8E2D8; --radius: 10px;
  --shadow: 0 2px 24px rgba(74, 42, 133, 0.06);
  --font-head: 'Playfair Display', Georgia, serif;
  --font-body: 'Inter', -apple-system, sans-serif;
}
* { box-sizing: border-box; margin: 0; }
html { scroll-behavior: smooth; }
body { background: var(--bg); color: var(--ink); font-family: var(--font-body);
  font-size: 1.0625rem; line-height: 1.7; -webkit-font-smoothing: antialiased; }
h1, h2, h3 { font-family: var(--font-head); font-weight: 500; line-height: 1.18; }
h1 { font-size: clamp(2.4rem, 5.5vw, 4rem); }
h2 { font-size: clamp(1.8rem, 3.5vw, 2.6rem); }
img { max-width: 100%; height: auto; display: block; }
.container { max-width: 1140px; margin: 0 auto; padding: 0 24px; }
.section { padding: clamp(64px, 9vw, 120px) 0; }
.section--alt { background: var(--bg-alt); }
.eyebrow { font-size: .78rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--accent); opacity: .75; margin-bottom: 14px; }

/* --------------------------------------------------------------------------
   2. Extended tokens
   -------------------------------------------------------------------------- */

:root {
  --radius-sm: 4px;
  --radius-lg: 18px;
  --shadow-lift: 0 10px 30px rgba(74, 42, 133, .12);
  --ease: cubic-bezier(.22, .61, .36, 1);
  --header-h: 84px;
  --danger: #A23B3B;
  --ink-rgb: 43, 36, 51;
  --accent-rgb: 74, 42, 133;
  --bg-rgb: 248, 245, 240;
  --danger-rgb: 162, 59, 59;
  /* --muted (#A39B8E) is ~2.6:1 on --bg/--card — fine for decorative use
     (placeholders) but fails WCAG AA for body copy. --muted-text is the same
     warm-taupe family, darkened to clear 4.5:1 on both --bg and --card. Use
     --muted-text for any real reading content; reserve --muted for hints. */
  --muted-text: #786E60;
}

/* --------------------------------------------------------------------------
   3. Base extras — resets, type scale, links, lists, selection, motion
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

h1, h2 { letter-spacing: -.01em; }
h3 { font-size: clamp(1.25rem, 2vw, 1.5rem); }
h4 { font-family: var(--font-body); font-weight: 600; font-size: 1.05rem; }
small { font-size: .875rem; color: var(--muted-text); }

ul, ol { list-style: none; }

a { color: inherit; text-decoration: none; }
main a:not([class]) {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: rgba(var(--accent-rgb), .35);
  text-underline-offset: 3px;
  transition: text-decoration-color .2s var(--ease);
}
main a:not([class]):hover,
main a:not([class]):focus-visible { text-decoration-color: var(--accent); }

main ul:not([class]), main ol:not([class]) {
  list-style: revert;
  margin: 16px 0;
  padding-left: 1.25em;
}
main ul:not([class]) li, main ol:not([class]) li { margin-bottom: 8px; }
main ul:not([class]) li::marker, main ol:not([class]) li::marker { color: var(--accent); }

button { font: inherit; background: none; border: none; cursor: pointer; }

::selection { background: var(--accent); color: var(--bg); }

/* Simple vertical rhythm inside common flow containers (base resets all
   margins to 0, so stacked children need an explicit gap). */
.hero__content > * + *,
.card > * + *,
.service-card > * + *,
.site-footer__col > * + * { margin-top: 14px; }
p + p { margin-top: 1em; }

/* Focus states — visible everywhere, keyboard only */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, summary:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Skip link — visually hidden until keyboard focus reaches it */
.skip-link {
  position: absolute;
  top: -72px; left: 16px; /* fully off-screen: the link renders ~53px tall, so -48px left a sliver visible */
  z-index: 1000;
  background: var(--accent);
  color: var(--bg);
  padding: 12px 20px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  transition: top .2s var(--ease);
}
.skip-link:focus { top: 16px; }

/* --------------------------------------------------------------------------
   4. Reveal — scroll-in animation.
   Hidden ONLY when JS has run (html.js) AND the user has no reduced-motion
   preference. With JS off, or reduced motion on, content is fully visible.
   main.js adds .is-visible as each element scrolls into view.

   The motion used to be a GSAP tween (gsap.fromTo + ScrollTrigger, 115KB of
   library on every page for this one effect). It is now a plain CSS
   transition driven by IntersectionObserver. --ease is
   cubic-bezier(.22,.61,.36,1), which is easeOutCubic — the same curve GSAP's
   "power2.out" produced — and the 28px offset and .9s duration are carried
   over unchanged, so the animation looks identical.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  html.js .reveal {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity .9s var(--ease), transform .9s var(--ease);
  }
  html.js .reveal.is-visible {
    opacity: 1;
    transform: none;
  }
}

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 15px 30px;
  font-family: var(--font-body);
  font-size: .95rem;
  font-weight: 600;
  letter-spacing: .01em;
  text-decoration: none;
  border: 1.5px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background-color .25s var(--ease), color .25s var(--ease),
    border-color .25s var(--ease), transform .25s var(--ease), box-shadow .25s var(--ease);
  white-space: nowrap;
}

.btn-primary {
  background: var(--accent);
  color: var(--bg);
  box-shadow: 0 1px 2px rgba(var(--ink-rgb), .1);
}
.btn-primary:hover, .btn-primary:focus-visible {
  background: var(--accent-dark);
  transform: translateY(-2px);
  box-shadow: var(--shadow-lift);
}
.btn-primary:active { transform: translateY(0); box-shadow: none; }

.btn-outline {
  background: transparent;
  color: var(--accent);
  border-color: var(--accent);
}
.btn-outline:hover, .btn-outline:focus-visible {
  background: var(--accent);
  color: var(--bg);
  transform: translateY(-2px);
}
.btn-outline:active { transform: translateY(0); }

.btn--sm { padding: 10px 20px; font-size: .86rem; }

/* --------------------------------------------------------------------------
   6. Header + navigation
   -------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(var(--bg-rgb), .88);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}
.site-header__inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  min-height: var(--header-h);
  padding-top: 8px;
  padding-bottom: 8px;
}
.site-header__logo { display: flex; align-items: center; flex-shrink: 0; }
.site-header__logo img { width: 168px; height: auto; }

.site-nav {
  display: flex;
  align-items: center;
  gap: 30px;
  margin-left: auto;
}
.site-nav__link {
  position: relative;
  font-size: .95rem;
  font-weight: 500;
  color: var(--ink);
  padding: 6px 1px;
}
.site-nav__link::after {
  content: '';
  position: absolute;
  left: 0; right: 100%; bottom: 0;
  height: 1.5px;
  background: var(--accent);
  transition: right .25s var(--ease);
}
.site-nav__link:hover::after,
.site-nav__link:focus-visible::after,
.site-nav__link[aria-current="page"]::after { right: 0; }
.site-nav__link[aria-current="page"] { color: var(--accent); }

.site-nav__cta { margin-left: 6px; }
/* Task 12 fix: at 861-1112px, .site-header__inner (logo + .site-nav in a
   nowrap flex row, no overflow handling) was narrower than the logo's own
   width plus .site-nav's natural content width (901px+ measured), so the
   overflow didn't wrap or scale down — it silently extended off the right
   edge, unreachable except by scrolling the whole PAGE horizontally (which
   also drags the logo and body content sideways with it). Verified via
   headless-Chrome screenshots at 900px/1050px: "Book a visit" and part of
   "More" were rendered entirely outside the viewport. Root cause is the nav
   bar itself not fitting, independent of any dropdown flyout being open
   (contrast with the .site-nav__dropdown:last-of-type right-anchor fix
   below, which only addresses a flyout PANEL overflowing — a different,
   already-handled issue). Fix: pushed the mobile-drawer breakpoint from
   860px to 1150px. getBoundingClientRect() on .site-nav__cta pinned the
   real minimum fit-width at exactly 1113px (an earlier pass here compared
   .site-nav's own width against .site-header__inner's full width and got
   949px — wrong, because that comparison didn't leave room for the logo
   sitting in the same flex row; rechecked against the CTA button's actual
   right edge instead). 1150px leaves ~37px of margin above that. All four
   nav-related 860px/861px breakpoints moved together to stay in sync; the
   unrelated .hero__inner and .site-footer__grid 860px breakpoints were left
   as-is. */

/* Mobile hamburger — pure checkbox toggle, no JS required.
   Expected markup:
   <input type="checkbox" id="nav-toggle" class="nav-toggle-input" aria-label="Menu">
   <label for="nav-toggle" class="nav-scrim" aria-hidden="true"></label>
   <label for="nav-toggle" class="nav-toggle" aria-hidden="true"><span></span><span></span><span></span></label>
   <nav class="site-nav">…</nav>

   The checkbox stays a direct sibling of .nav-scrim/.nav-toggle/.site-nav
   (NOT nested inside the label) so the plain ~ sibling combinator can drive
   the icon animation, scrim, and drawer — tested and confirmed reliable in
   this project's target/testing browsers. (An earlier draft nested the
   checkbox inside the label for automatic co-location and used :has() for
   the scrim/drawer instead of ~, but :has(...checked) did not reactively
   update computed styles in this project's verification browser even
   though the selector matched via querySelector — confirmed empirically,
   so :has() was dropped from this critical path.)

   Below 1150px (Task 12: was 860px) the checkbox is absolutely positioned to land exactly on the
   visible 44x44px hamburger icon. Its containing block is
   .site-header__inner (position: relative), which carries no horizontal
   padding of its own — .container's 24px padding is resolved one level up,
   so .site-header__inner's padding box (the box top/right resolve against)
   already starts inset from the viewport edge, flush with where the icon
   sits (it's the last flex item in that same row). So `right: 0` lands
   exactly on the icon; a `right: 24px` here double-counts that inset and
   lands the checkbox 24px to the icon's LEFT instead — confirmed via
   getBoundingClientRect() at 375px width (checkbox x=283-327 vs icon
   x=307-351 before this fix; dx=0 after). A plain top:50%/translateY(-50%)
   for the vertical axis also didn't match the icon's flex-centered
   position, hence the explicit top value below, empirically measured — if
   --header-h, the logo's rendered height, .site-header__inner's padding, or
   .container's padding change, re-measure and update these two values. */

.nav-toggle-input {
  display: none; /* non-tabbable on desktop, where there's nothing to toggle */
  position: absolute;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}
.nav-toggle-input:focus-visible {
  outline: none; /* ringed on .nav-toggle itself below, for a shape that matches the icon */
}
.nav-toggle-input:focus-visible ~ .nav-toggle {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.nav-toggle {
  display: none;
  position: relative;
  z-index: 96; /* stay above .site-nav (95) so it stays visible while the drawer is open */
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: 44px; height: 44px;
  border-radius: 8px;
  cursor: pointer;
  flex-shrink: 0;
}
.nav-toggle span {
  display: block;
  width: 22px; height: 2px;
  background: var(--ink);
  transition: transform .25s var(--ease), opacity .2s var(--ease);
}
.nav-scrim { display: none; }

@media (max-width: 1150px) {
  /* Task 12 fix: the mobile-nav breakpoint moved from 860px to 1150px — see
     the second Task 12 fix comment near .site-nav__cta below for why. */
  /* Task 12 fix: .site-nav below is `position: fixed; right: 0` pinned to
     the viewport edge, then hidden off-canvas via `transform:
     translateX(100%)`. Its PRE-transform box (pinned at the right edge,
     width up to 320px) still counts toward the page's scrollable overflow
     even though the POST-transform box is what's visible — confirmed via
     getBoundingClientRect() (right edge measured at viewport-width + 320)
     and a real headless-Chrome screenshot showing the hero heading and CTA
     buttons clipped at 375px/768px with a live horizontal scrollbar. Only
     matters at this breakpoint (where the drawer is fixed+transformed);
     desktop's static-flow nav never triggers it. */
  body { overflow-x: hidden; }

  .nav-toggle { display: flex; }

  .nav-toggle-input {
    display: block;
    top: 16px;  /* = .site-header__inner's 8px padding-top + the icon's own flex-centering slack */
    right: 0;   /* .site-header__inner's padding box is already inset by .container's padding — see comment above */
    width: 44px;
    height: 44px;
    z-index: 97; /* above the icon (96), so it's the real click/focus target */
  }

  .nav-scrim {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(var(--ink-rgb), .35);
    opacity: 0;
    pointer-events: none;
    transition: opacity .3s var(--ease);
    z-index: 90;
  }
  .nav-toggle-input:checked ~ .nav-scrim { opacity: 1; pointer-events: auto; }

  .site-nav {
    position: fixed;
    top: 0; right: 0;
    height: 100vh;
    width: min(320px, 86vw);
    margin-left: 0;
    z-index: 95;
    background: var(--card);
    border-left: 1px solid var(--border);
    box-shadow: -12px 0 40px rgba(var(--ink-rgb), .12);
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 2px;
    padding: calc(var(--header-h) + 8px) 28px 28px;
    transform: translateX(100%);
    transition: transform .35s var(--ease);
    overflow-y: auto;
  }
  .nav-toggle-input:checked ~ .site-nav { transform: translateX(0); }

  .site-nav__link {
    width: 100%;
    padding: 15px 0;
    border-bottom: 1px solid var(--border);
    font-size: 1.05rem;
  }
  .site-nav__link::after { display: none; }
  .site-nav__cta { margin: 18px 0 0; width: 100%; }

  .nav-toggle-input:checked ~ .nav-toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav-toggle-input:checked ~ .nav-toggle span:nth-child(2) { opacity: 0; }
  .nav-toggle-input:checked ~ .nav-toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
}

/* --------------------------------------------------------------------------
   6b. Nav dropdown — added in Task 5 (no dropdown existed in the Task 3
   design system). "Dental Services" and the overflow "More" group both
   need a submenu; there was no JS hook to add (main.js/Task 4 is frozen
   and doesn't mention dropdowns), so this follows the same
   works-without-JS philosophy as the accordion (§9): native
   <details>/<summary>, no scripting required.

   Expected markup, as a direct child of .site-nav (siblings of the plain
   .site-nav__link anchors):
   <details class="site-nav__dropdown">
     <summary class="site-nav__link site-nav__dropdown-toggle">Label
       <span class="site-nav__dropdown-caret" aria-hidden="true">&#8595;</span></summary>
     <div class="site-nav__dropdown-menu">
       <a class="site-nav__dropdown-link" href="…">…</a>
       …
     </div>
   </details>
   The summary reuses .site-nav__link for typography/hover-underline/
   mobile border-bottom, so no duplicate rules are needed for it.

   Behavior: below 1150px (Task 12: was 860px; inside the drawer) the native
   open/closed <details> state is the only mechanism — no CSS override — so
   it works identically to the accordion with zero JS. At 1151px+ (desktop inline
   nav) the menu is pulled out of flow into an absolutely positioned
   panel and shown on :hover/:focus-within *or* native [open] (so a
   tap/click still works for touch-trackpad hybrids); this requires
   forcing `display: flex` past the UA stylesheet's
   `details:not([open]) > :not(summary) { display: none }`, hence the
   !important below — it only applies inside the desktop media query.
   -------------------------------------------------------------------------- */

.site-nav__dropdown { position: relative; }
.site-nav__dropdown-toggle {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.site-nav__dropdown-toggle::-webkit-details-marker { display: none; }
.site-nav__dropdown-toggle::marker { content: ''; }
.site-nav__dropdown-caret {
  font-size: .7em;
  transition: transform .25s var(--ease);
}
.site-nav__dropdown[open] .site-nav__dropdown-caret { transform: rotate(180deg); }

.site-nav__dropdown-menu {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.site-nav__dropdown-link {
  display: block;
  font-size: .92rem;
  font-weight: 500;
  color: var(--ink);
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  transition: background-color .2s var(--ease), color .2s var(--ease);
}
.site-nav__dropdown-link:hover, .site-nav__dropdown-link:focus-visible {
  background: var(--bg-alt);
  color: var(--accent);
}
.site-nav__dropdown-link[aria-current="page"] { color: var(--accent); font-weight: 600; }

@media (min-width: 1151px) {
  .site-nav__dropdown-menu {
    display: flex !important; /* override UA details[not open] > :not(summary) { display:none } */
    position: absolute;
    top: calc(100% + 14px);
    left: 0;
    min-width: 230px;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift);
    padding: 10px;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    transition: opacity .2s var(--ease), transform .2s var(--ease), visibility .2s var(--ease);
    pointer-events: none;
    z-index: 110;
  }
  .site-nav__dropdown:hover .site-nav__dropdown-menu,
  .site-nav__dropdown:focus-within .site-nav__dropdown-menu,
  .site-nav__dropdown[open] .site-nav__dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
  }
  .site-nav__dropdown-link { white-space: nowrap; }
}

@media (max-width: 1150px) {
  .site-nav__dropdown-menu { padding: 2px 0 6px 14px; }
  .site-nav__dropdown-link { padding: 12px 0; border-bottom: 1px solid var(--border); font-size: 1rem; }
}

/* Right-anchor the LAST dropdown's flyout only. .site-nav__dropdown-menu is
   left:0 by default (§6b above), which is fine for "Dental Services" (it
   has room to grow rightward) but the "More" dropdown is the rightmost nav
   item, right before the CTA button, near .container's right edge — a
   left-anchored 230px-min-width flyout there can overflow past the
   viewport/container edge. (Task 12: this flyout-overflow fix is separate
   from — and still needed alongside — the 861-948px nav-bar-itself-overflows
   fix above; that band no longer reaches desktop nav layout at all now that
   the mobile breakpoint moved to 1150px, but the flyout could still overflow
   at e.g. 1151-1200px without this.) :last-of-type matches by tag name among
   siblings, and .site-nav__dropdown is the only element using the <details>
   tag inside .site-nav, so this targets exactly the last (second) dropdown
   without needing new markup or classes. Additive only —
   .site-nav__dropdown-menu's left:0 base rule above is untouched; this just
   overrides it for one specific dropdown. */
@media (min-width: 1151px) {
  .site-nav__dropdown:last-of-type .site-nav__dropdown-menu {
    left: auto;
    right: 0;
  }
}

/* --------------------------------------------------------------------------
   7. Hero
   -------------------------------------------------------------------------- */

.hero { position: relative; padding: clamp(48px, 8vw, 92px) 0 clamp(56px, 9vw, 100px); overflow: hidden; }
.hero__inner {
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  align-items: center;
  gap: clamp(32px, 5vw, 72px);
}
.hero__content p { color: var(--muted-text); max-width: 46ch; }
/* Task 12 fix: ".hero__content p" (element+class, 0-1-1) outranks the
   component rule ".eyebrow" (single class, 0-1-0), so any eyebrow that's a
   <p> inside .hero__content — most of them — silently lost its intended
   --accent color to --muted-text above. Harmless on its own, but stacked
   with .eyebrow's own opacity:.75 that blend measured 2.9:1 against --bg,
   failing WCAG AA (needs 4.5:1; --accent at the same opacity clears
   ~4.7–5.3:1 on --bg/--bg-alt/--card). Re-asserted at higher specificity
   (element+2 classes, 0-2-1) so it wins regardless of source order. */
.hero__content p.eyebrow { color: var(--accent); }
.hero__actions { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 32px; }
.hero__media {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow);
}
.hero__media img { width: 100%; transition: transform .8s var(--ease); }
.hero:hover .hero__media img { transform: scale(1.035); }

@media (max-width: 860px) {
  .hero__inner { grid-template-columns: 1fr; }
  .hero__media { order: -1; }
}

/* --------------------------------------------------------------------------
   8. Cards
   -------------------------------------------------------------------------- */

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 32px;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s var(--ease);
}
a.card { cursor: pointer; }
a.card:hover, a.card:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lift);
  border-color: rgba(var(--accent-rgb), .3);
}
.card h3 { margin-bottom: 4px; }
.card p { color: var(--muted-text); }
/* Task 12 fix: same ".eyebrow" specificity collision as .hero__content p
   above, for the (rarer) case of an eyebrow <p> inside .card. */
.card p.eyebrow { color: var(--accent); }

/* --------------------------------------------------------------------------
   9. Accordion — native <details>, works with JS disabled
   -------------------------------------------------------------------------- */

.accordion { border-top: 1px solid var(--border); }
.accordion__item { border-bottom: 1px solid var(--border); }
.accordion__item summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 22px 2px;
  font-family: var(--font-head);
  font-size: 1.08rem;
  font-weight: 500;
  color: var(--ink);
  transition: color .2s var(--ease);
}
.accordion__item summary::-webkit-details-marker { display: none; }
.accordion__item summary::after {
  content: '+';
  flex-shrink: 0;
  font-family: var(--font-body);
  font-size: 1.5rem;
  font-weight: 300;
  line-height: 1;
  color: var(--accent);
  transition: transform .3s var(--ease);
}
.accordion__item[open] summary::after { transform: rotate(45deg); }
.accordion__item summary:hover,
.accordion__item summary:focus-visible { color: var(--accent); }
.accordion__item > :not(summary) {
  padding: 0 2px 22px;
  color: var(--muted-text);
}

/* --------------------------------------------------------------------------
   10. Carousel — native scroll-snap baseline (works without JS via swipe /
   trackpad scroll). main.js (Task 4) enhances with autoplay, pointer-drag
   and dot sync by calling track.scrollTo(...).
   -------------------------------------------------------------------------- */

.carousel { position: relative; }
.carousel__track {
  display: flex;
  gap: 24px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  padding: 2px 2px 12px;
  scrollbar-width: none;
}
.carousel__track::-webkit-scrollbar { display: none; }
.carousel__slide {
  flex: 0 0 min(420px, 88vw);
  scroll-snap-align: start;
}
.carousel__dots {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 20px;
}
.carousel__dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--border);
  padding: 0;
  transition: background-color .25s var(--ease), transform .25s var(--ease);
}
.carousel__dot[aria-current="true"], .carousel__dot.is-active {
  background: var(--accent);
  transform: scale(1.25);
}

/* --------------------------------------------------------------------------
   11. Service grid / service cards — interactive explorer
   -------------------------------------------------------------------------- */

.service-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 24px;
}
.service-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 30px;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s var(--ease);
}
.service-card:hover, .service-card:focus-within {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lift);
  border-color: rgba(var(--accent-rgb), .3);
}
.service-card__icon { width: 44px; height: 44px; margin-bottom: 18px; }
.service-card__title { font-family: var(--font-head); font-size: 1.2rem; font-weight: 500; }
.service-card__summary { color: var(--muted-text); font-size: .96rem; margin-top: 8px; }

.service-card__toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 16px;
  padding: 0;
  color: var(--accent);
  font-weight: 600;
  font-size: .88rem;
}
.service-card__toggle::after {
  content: '\2193';
  transition: transform .3s var(--ease);
}
.service-card[data-expanded] .service-card__toggle::after { transform: rotate(180deg); }
/* No JS: card body is always open, so the toggle affordance is meaningless — hide it. */
html:not(.js) .service-card__toggle { display: none; }

.service-card__body {
  display: grid;
  grid-template-rows: 1fr; /* default: fully visible (no-JS baseline) */
}
html.js .service-card__body {
  grid-template-rows: 0fr; /* JS present: collapsed by default */
  transition: grid-template-rows .4s var(--ease);
}
html.js .service-card[data-expanded] .service-card__body { grid-template-rows: 1fr; }
.service-card__body-inner { overflow: hidden; }
.service-card__body-inner > * { padding-top: 16px; color: var(--muted-text); }

/* --------------------------------------------------------------------------
   12. Forms — inputs, selects, textareas, inline validation
   -------------------------------------------------------------------------- */

label {
  display: block;
  font-size: .92rem;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 8px;
}
input, select, textarea {
  display: block;
  width: 100%;
  padding: 13px 16px;
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
input::placeholder, textarea::placeholder { color: var(--muted); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .12);
}
textarea { min-height: 140px; resize: vertical; }
select { cursor: pointer; }

.form-group { margin-bottom: 22px; }

.form-error {
  display: block;
  margin-top: 8px;
  font-size: .86rem;
  color: var(--danger);
}
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--danger);
}
input[aria-invalid="true"]:focus, select[aria-invalid="true"]:focus, textarea[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 3px rgba(var(--danger-rgb), .14);
}

.form-note {
  font-size: .86rem;
  color: var(--muted-text);
  margin-top: 10px;
}

/* --------------------------------------------------------------------------
   13. Footer
   -------------------------------------------------------------------------- */

.site-footer {
  background: var(--ink);
  color: var(--bg);
  padding-top: clamp(56px, 8vw, 88px);
}
.site-footer a { color: var(--bg); }
.site-footer a:not([class]) {
  text-decoration-color: rgba(var(--bg-rgb), .4);
  text-decoration-line: underline;
  text-underline-offset: 3px;
}
.site-footer a:not([class]):hover,
.site-footer a:not([class]):focus-visible { text-decoration-color: var(--bg); }

.site-footer__grid {
  display: grid;
  grid-template-columns: 1.2fr 1fr 1fr;
  gap: 48px;
  padding-bottom: 56px;
  border-bottom: 1px solid rgba(var(--bg-rgb), .14);
}
.site-footer__heading {
  font-family: var(--font-head);
  font-size: 1.05rem;
  font-weight: 500;
  margin-bottom: 18px;
  color: var(--bg);
}
.site-footer__col p, .site-footer__col address { font-style: normal; color: rgba(var(--bg-rgb), .78); }
.site-footer__col > * + * { margin-top: 12px; }

.site-footer__hours { width: 100%; border-collapse: collapse; font-size: .92rem; }
.site-footer__hours td { padding: 4px 0; color: rgba(var(--bg-rgb), .78); }
.site-footer__hours td:last-child { text-align: right; color: var(--bg); }

.site-footer__links { display: flex; flex-direction: column; gap: 10px; }

.site-footer__bottom {
  padding: 26px 0;
  font-size: .85rem;
  color: rgba(var(--bg-rgb), .5);
  text-align: center;
}

@media (max-width: 860px) {
  .site-footer__grid { grid-template-columns: 1fr 1fr; gap: 40px; }
}
@media (max-width: 620px) {
  .site-footer__grid { grid-template-columns: 1fr; gap: 36px; }
}

/* --------------------------------------------------------------------------
   14. Task 4 additions — JS-driven state hooks
   main.js (Task 4) toggles `.is-scrolled` on `.site-header` once the page
   scrolls past the top, so the sticky/frosted header reads as "lifted"
   over scrolled content. Inert with JS off (class never gets added).
   -------------------------------------------------------------------------- */

.site-header { transition: box-shadow .25s var(--ease); }
.site-header.is-scrolled { box-shadow: var(--shadow); }

/* --------------------------------------------------------------------------
   15. Task 6 additions — homepage (index.html)
   Additive only; nothing above this line is modified. .hero__inner,
   .hero__content, .hero__media, .card and .service-grid (all defined
   above) are reused as-is for the homepage's alternating text+image rows
   and card grids — these three small rules cover the few bits those
   existing classes don't already provide.
   -------------------------------------------------------------------------- */

/* Vertical stack gap between the "who we are" / "our practice" / "our
   values" rows (each row reuses .hero__inner, which has no own margin). */
.feature-rows { display: flex; flex-direction: column; gap: clamp(56px, 8vw, 96px); }

/* Small caption under a .hero__media image (e.g. "Best dentist bloomingdale"
   below the "who we are" row's photo). Reuses the base `small` element
   styling (§3) for color/size; this just adds block layout + padding so it
   doesn't hug the rounded image edge above it. */
.media-caption { display: block; padding: 10px 4px 0; }

/* Light-theme hours table for the NAP/hours/directions section (§5 of this
   page) — .site-footer__hours (§13) is styled for the dark footer, so it
   can't be reused here. Same layout, --ink/--muted-text/--border instead. */
.hours-table { width: 100%; border-collapse: collapse; font-size: .96rem; }
.hours-table td { padding: 8px 0; border-bottom: 1px solid var(--border); color: var(--muted-text); }
.hours-table td:last-child { text-align: right; color: var(--ink); font-weight: 500; }

/* --------------------------------------------------------------------------
   16. Task 7 additions — about.html, membership.html, new-patient-special.html
   Additive only; nothing above this line is modified. Both new rules below
   are used only on membership.html.
   -------------------------------------------------------------------------- */

/* Big plan price inside a .card pricing tier (membership.html §3, "Adult
   Membership" / "Child Membership"). The trailing "/mo" unit is a nested
   <span> sized back down to body text so it doesn't compete with the number. */
.price { font-family: var(--font-head); font-size: 2.75rem; color: var(--accent); font-weight: 600; }
.price span { font-family: var(--font-body); font-size: 1rem; font-weight: 400; color: var(--muted-text); }

/* Highlighted callout box for the plan's 15% discount line (membership.html
   §2), so that verbatim sentence reads as a standout instead of blending
   into the surrounding paragraphs. */
.callout {
  background: rgba(var(--accent-rgb), .06);
  border: 1px solid rgba(var(--accent-rgb), .2);
  border-radius: var(--radius);
  padding: 18px 24px;
  margin: 24px 0;
}
.callout p { color: var(--ink); font-weight: 600; }

/* Bounds the hero image on membership.html, whose crawled hero graphic
   (membership-plan-details.jpg, 1000x1294) is a tall text-heavy pricing
   flyer — not the ~3:2 landscape photo the other hero sections use. §7's
   .hero__media img rule sets width:100% with no height cap, so at the
   ~492px desktop hero column that portrait ratio would render ~636px
   tall (vs. ~328px for a normal 400x267 hero photo) — dramatically
   over-tall. Fixed by capping height instead of cropping: object-fit:cover
   would slice into the flyer's headline, pricing columns, or QR code,
   all of which carry real information (the same info is also reproduced
   as accessible HTML text/cards elsewhere on the page, so this image is a
   supporting visual, not the only source — a "contained" scale-down is
   safe here). width/height stay auto so the browser's normal replaced-
   element sizing (intrinsic ratio, constrained by max-height here and by
   the existing global `img { max-width:100% }` from §3 on narrow
   viewports) does the work; margin-inline centers whatever width results
   under the cap, and --card gives that surrounding space a soft frame
   instead of empty page background. */
.hero__media--portrait { background: var(--card); }
.hero__media--portrait img {
  width: auto;
  height: auto;
  max-height: 480px;
  margin-inline: auto;
}

/* --------------------------------------------------------------------------
   17. Task 9 additions — reviews.html, dental-videos.html
   Additive only; nothing above this line is modified. .card, .service-grid,
   .btn, and the .carousel/.carousel__track/.carousel__slide/.carousel__dots
   markup contract (§10, unmodified) are all reused as-is. The rules below
   only add: the review-quote typography/attribution layout shared by the
   carousel slides and the full review list, the Google/Yelp rating stat
   badges, the "Leave us a review" CTA band's centered layout, and the
   dental-videos click-to-load video facade (thumbnail + play button in a
   fixed-ratio box, so the script-injected iframe drops into the exact same
   box with zero layout shift).
   -------------------------------------------------------------------------- */

/* Review quote — shared by carousel slides (.carousel__slide.card) and the
   full list (.card); styles the inner blockquote/figcaption only, not the
   card chrome itself (that's .card, §8). Quote text carries no literal
   quotation-mark characters (kept verbatim-clean for diffing); the opening
   mark is this purely decorative ::before instead. */
.review-quote blockquote::before {
  content: '\201C';
  display: block;
  font-family: var(--font-head);
  font-size: 3rem;
  line-height: 1;
  color: rgba(var(--accent-rgb), .28);
  margin-bottom: 4px;
}
.review-quote blockquote p { color: var(--ink); }
.review-quote figcaption { margin-top: 16px; display: flex; flex-direction: column; gap: 2px; }
.review-quote cite { font-style: normal; font-weight: 600; color: var(--ink); }
.review-quote__meta { font-size: .85rem; color: var(--muted-text); }

/* Google/Yelp rating badges (reviews.html) — the crawled widget's own
   "5.0 (148)" / "4.9 (21)" figures, shown as plain display copy only.
   Intentionally never emitted as Review/AggregateRating JSON-LD. */
.review-stats { display: flex; flex-wrap: wrap; gap: 40px; margin: 28px 0 40px; }
.review-stat { display: flex; align-items: baseline; gap: 10px; }
.review-stat__score { font-family: var(--font-head); font-size: 2rem; font-weight: 600; color: var(--accent); }
.review-stat__score span { font-family: var(--font-body); font-size: .92rem; font-weight: 400; color: var(--muted-text); }
.review-stat__label { color: var(--muted-text); font-size: .95rem; }

/* "Leave us a review" CTA band (reviews.html). */
.cta-band .container { text-align: center; }
.cta-band p { color: var(--muted-text); max-width: 52ch; margin: 12px auto 0; }
.cta-band .btn { margin-top: 28px; }

/* Dental video click-to-load facade (dental-videos.html). The no-JS
   baseline is a plain <a href> to the real YouTube watch URL (real
   navigation, opens in a new tab); the page-scoped inline script at the
   bottom of dental-videos.html replaces it in place with a same-sized
   iframe on click. 4:3 matches the i.ytimg.com hqdefault.jpg thumbnail's
   native 480x360 so there's no swap-time layout jump. */
.video-card { display: flex; flex-direction: column; gap: 16px; }
.video-facade {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--ink);
}
.video-facade img { width: 100%; height: 100%; object-fit: cover; }
.video-facade iframe { width: 100%; height: 100%; border: 0; display: block; }
.video-facade__play {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 62px; height: 62px;
  border-radius: 50%;
  background: rgba(var(--ink-rgb), .55);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color .2s var(--ease), transform .2s var(--ease);
}
.video-facade__play::after {
  content: '';
  width: 0; height: 0;
  border-style: solid;
  border-width: 12px 0 12px 20px;
  border-color: transparent transparent transparent #fff;
  margin-left: 4px; /* optical centering: the triangle's visual weight leans left */
}
.video-facade:hover .video-facade__play,
.video-facade:focus-visible .video-facade__play {
  background: var(--accent);
  transform: translate(-50%, -50%) scale(1.08);
}

/* --------------------------------------------------------------------------
   18. Task 10 additions — contact.html
   Additive only; nothing above this line is modified. .hero__inner,
   .hero__content, .hours-table, .btn, and .form-group/.form-error/
   .form-note (§12) are reused as-is for the location column and the
   appointment form. The rules below only add: a top-aligned modifier for
   the location/form two-column row (the base .hero__inner is
   `align-items:center`, meant for photo+text hero rows — wrong here since
   the location column runs taller than the form), a responsive embed box
   for the Google Map iframe (sized via aspect-ratio, same technique as the
   dental-videos video facade in §17), a visually-hidden utility for the
   honeypot field, and content-based show/hide for the field errors and
   form status region so their `role="alert"` (already in the markup at
   first paint, per contact.html's inline script comment) announces via a
   widely-supported reveal pattern instead of reserving empty space on
   every page load.
   -------------------------------------------------------------------------- */

.contact-grid { align-items: start; }

.map-embed {
  aspect-ratio: 16 / 10;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--card);
}
.map-embed iframe { width: 100%; height: 100%; border: 0; display: block; }

.form-error:empty { display: none; }

.form-status:empty { display: none; }
.form-status { margin-top: 20px; font-weight: 600; }
.form-status--success { color: var(--accent); }
.form-status--error { color: var(--danger); }
.form-status a { color: inherit; text-decoration-line: underline; }

/* Standard clip-based visually-hidden utility (honeypot field + its
   label) — hidden from sighted users via CSS; hidden from assistive tech
   via aria-hidden set directly on the <input> itself in the markup
   (tabindex="-1" there too), not on this wrapper, so there's no
   focusable-element-inside-an-aria-hidden-ancestor conflict. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   19. Final polish — last review-fix pass before delivery. Additive only;
   nothing above this line is modified except two authorized, measured
   corrections noted inline where they occur (§6's checkbox `right` value
   and its adjacent comment). Everything else here is new.
   -------------------------------------------------------------------------- */

/* Reduced-motion clamp. §3 above already sets `scroll-behavior: auto`
   under prefers-reduced-motion, but that only covers scroll-linked
   motion — it left every CSS *transition* (drawer slide, service-card
   grid expand, button/card hover lifts, nav dropdown flyout, scrim fade,
   etc.) still animating at full duration. This is the standard global
   clamp: it forces every transition/animation to an imperceptible
   0.01ms rather than removing them individually, so it can't drift out
   of sync as new transitions get added later. `!important` is
   necessary here — it's the only way to override component-level
   duration values without hunting down and duplicating every one.
   Verified this doesn't break the checkbox-hack nav (styles.css §6): the
   drawer's OPEN/CLOSED state is driven by the `:checked` selector match
   itself, not by the transition — clamping the transition just makes it
   snap instantly instead of sliding, the toggle still works. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/* Carousel pause control (reviews.html) — a small round toggle button
   sitting under the dots, wired up in js/main.js's initCarousel (search
   "Final polish: pause control"). Gives keyboard/touch users an explicit,
   persistent way to stop the autoplay (WCAG 2.2.2) beyond the existing
   hover/focus pause. No-JS baseline: the button does nothing without the
   script that wires its click handler, and there's no autoplay running
   to pause anyway (autoplay itself is JS-driven), so it's hidden until
   html.js confirms the script has run. */
html:not(.js) .carousel__pause { display: none; }
.carousel__pause {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px; height: 34px;
  margin: 14px auto 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--card);
  color: var(--accent);
  font-size: .85rem;
  line-height: 1;
  transition: background-color .2s var(--ease), border-color .2s var(--ease);
}
.carousel__pause:hover, .carousel__pause:focus-visible {
  background: var(--bg-alt);
  border-color: rgba(var(--accent-rgb), .3);
}

/* --------------------------------------------------------------------------
   20. Post-launch feedback (round 1) — owner-requested adjustments.
   - Nav labels must never wrap to two lines ("Membership Plan" etc.).
   - Homepage hero title runs a step smaller than the global h1 scale.
   - Phone numbers are plain text now (owner request); the old "Call ..."
     buttons became <span class="btn ...">, which need a neutral cursor.
   -------------------------------------------------------------------------- */
.site-nav__link { white-space: nowrap; }
.hero-title--home { font-size: clamp(2rem, 3.4vw, 2.75rem); }
/* The old "Call <number>" CTAs are now inert <span class="btn ...">, since
   phone numbers are no longer hyperlinked. Keep the visual chip, but strip
   every affordance that implies clickability — otherwise it reads as a
   button that does nothing when pressed. */
span.btn { cursor: default; }
span.btn:hover, span.btn:focus-visible {
  transform: none;
  box-shadow: none;
  background: transparent;
  color: var(--accent);
}
/* One-line nav labels made the desktop nav ~160px too wide for the 1092px
   container content box. Compress spacing/type slightly so the full nav
   (8 items + CTA) fits on one line at every viewport above the 1150px
   drawer breakpoint. Measured: nav 1071px + logo 168px vs 1092px avail;
   after compression nav ≈ 915px. */
@media (min-width: 1151px) {
  .site-nav { gap: 18px; }
  .site-nav__link { font-size: .88rem; }
  .site-nav__cta { padding: 9px 16px; font-size: .85rem; }
}

/* --------------------------------------------------------------------------
   21. 360-degree office tour — about.html hero.

   The static poster and the panorama stage are stacked in the same box. The
   stage sits on top at opacity 0 and fades in only once pannellum reports the
   panorama has decoded, so a slow or failed download never shows a half-built
   viewer. Both layers share the poster's 3:2 box, which is what keeps the
   hero from reflowing when the swap happens.
   -------------------------------------------------------------------------- */

.pano {
  position: relative;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--bg-alt);
}
.pano__poster {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pano__stage {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity .6s var(--ease);
}
.pano.is-live .pano__stage { opacity: 1; }

/* Hide the poster once the panorama is painted. `visibility` rather than
   `display` so the .pano box keeps its measured height either way. The hero's
   hover zoom (.hero:hover .hero__media img) still applies to the poster while
   it is the only thing showing, which is the pre-existing behaviour; it has
   nothing to act on afterwards, and the canvas is not an <img>. */
.pano.is-live .pano__poster { visibility: hidden; }

/* Drag affordance. Sits above the canvas, dismisses on first click, and is
   the keyboard route into the viewer — so it keeps a real focus ring. */
.pano__cue {
  position: absolute;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border: 0;
  border-radius: 999px;
  background: rgba(var(--bg-rgb), .92);
  color: var(--accent);
  font-family: var(--font-body);
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .01em;
  /* Absolutely positioned at left:50%, so the browser shrink-wraps this to
     the remaining half-width and breaks the label over two lines. Keep it on
     one line and let the translate do the centring. */
  white-space: nowrap;
  cursor: grab;
  box-shadow: var(--shadow-lift);
  transition: background .25s var(--ease), transform .25s var(--ease);
}
/* `display: inline-flex` above outranks the UA's `[hidden] { display: none }`,
   so the cue would stay on screen with JS or WebGL unavailable — a button
   that does nothing. Restore the hidden behaviour explicitly. */
.pano__cue[hidden] { display: none; }
.pano__cue:hover { background: var(--card); transform: translateX(-50%) translateY(-2px); }
.pano__cue:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.pano__cue-icon { font-size: 1.05rem; line-height: 1; }

.pano__stage:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* pannellum paints its own dark chrome (load spinner, error banner, drag
   cursor box). Only the canvas is wanted here — the poster underneath is a
   better loading state than a grey box with a progress bar. */
.pano .pnlm-container { background: transparent; }
.pano .pnlm-load-box,
.pano .pnlm-about-msg,
.pano .pnlm-panorama-info { display: none !important; }

@media (max-width: 860px) {
  /* Touch drag inside the viewer must not hijack vertical page scrolling. */
  .pano__stage { touch-action: pan-y; }
  /* The hero media is barely 320px wide on a phone — scale the pill down so
     it reads as a hint rather than a banner across the photo. */
  .pano__cue { bottom: 10px; padding: 7px 13px; font-size: .76rem; }
}

/* --------------------------------------------------------------------------
   22. Bare hero media — the homepage hero illustration only.

   .hero__media is a card: hairline-rounded, clipped, with a soft shadow. That
   is right for a photograph, but the homepage hero is a transparent-background
   illustration that should read as part of the page. The shadow
   (0 2px 24px rgba(74,42,133,.06)) paints *outside* the box and darkens the
   cream around it to ~#F2EEEC, which is exactly what makes a visible frame
   appear even though the image itself has a real alpha channel.

   Dropping the shadow and the radius lets it sit directly on --bg. The hover
   zoom goes too: scaling a free-floating logo on hover reads as a glitch
   rather than the subtle photo push it was designed for.

   Scoped to this one element — every other .hero__media (about.html's 360
   viewer, the alternating rows further down this page) keeps the card.
   -------------------------------------------------------------------------- */

.hero__media--bare {
  box-shadow: none;
  border-radius: 0;
  overflow: visible;
}
.hero:hover .hero__media--bare img { transform: none; }

/* --------------------------------------------------------------------------
   23. Homepage hero tagline — matched to the .eyebrow colour.

   "Quality Care, Gentle Touch." takes the same treatment as the
   "Bloomingdale, IL" eyebrow above it: --accent at 75% opacity. Repeating
   the mechanism rather than hardcoding the blended result (#765DA0) means
   the two stay locked together if --accent is ever retuned.

   Contrast on --bg lands at ~5:1, clearing WCAG AA for both normal and
   large text, so the opacity costs nothing in legibility here.
   -------------------------------------------------------------------------- */

.hero-title__tagline {
  color: var(--accent);
  opacity: .75;
  /* Half the practice name's size. `.75em` is relative to the parent <h1>,
     whose own size is a clamp() — so this tracks that curve at every
     viewport instead of needing a second clamp kept in sync by hand. */
  font-size: .75em;
}

/* --------------------------------------------------------------------------
   24. Card artwork scale — cutout illustrations inside a .card.

   A photo should fill the card's content width, but a transparent cutout
   (no background of its own) usually wants breathing room around it instead.
   .card--art shrinks the image and keeps it centred.

   >>> TO RESIZE: change the ONE number below. <<<
   100% = full card width (the old behaviour), 75% = a quarter smaller,
   50% = half. Nothing else needs touching.
   -------------------------------------------------------------------------- */

.card--art {
  --card-art-scale: 75%;        /* dentist + tooth CTA card */
}
.card--promo {
  --card-art-scale: 85%;        /* $99 New Patient Special card */
}
.card--art picture img {
  width: var(--card-art-scale);
  height: auto;
  margin-inline: auto;   /* stays centred at any scale */
}

/* --------------------------------------------------------------------------
   25. Membership hero logo mark.

   The source art is only 250x247 — there is no larger copy anywhere in the
   project (logo.jpg holds the same mark at the same size). It is doubled to
   491px so it stays crisp on retina, which caps the honest display size at
   roughly 245 CSS px. Letting .hero__media's default width:100% stretch it
   across the ~488px column would upscale it ~2x and visibly soften the
   edges, so it is capped and centred instead.
   -------------------------------------------------------------------------- */

.hero__media--logo img {
  width: min(245px, 70%);
  height: auto;
  margin-inline: auto;
}

/* --------------------------------------------------------------------------
   26. Promo card footers — the three cards in the homepage hero.

   Each card ends with a short call-to-action line ("Details", "Read our
   Reviews", "Book an Appointment Today"). The grid already stretches all
   three cards to equal height, but the image heights and heading line-counts
   differ, so those lines landed 64px apart vertically and read as misaligned.

   Making the card a flex column lets `margin-top: auto` on that last line
   absorb the leftover space, pinning it to the bottom of every card
   regardless of what sits above it.

   Scoped to this one grid: .card is also used for the video cards on
   dental-videos.html and the link tiles on 404.html, where a bottom-pinned
   trailing line would not be wanted.
   -------------------------------------------------------------------------- */

.service-grid--promo .card {
  display: flex;
  flex-direction: column;
}
.service-grid--promo .card > p:last-child {
  margin-top: auto;        /* outranks `.card > * + *` margin-top: 14px */
  padding-top: 16px;
  text-align: center;
}

/* --------------------------------------------------------------------------
   27. Contact form privacy notice.

   A disclaimer has to be read to work, so it gets more weight than
   .form-note's small muted text - but not .callout's bold block either,
   which would read as an error. Soft accent tint, accent rule on the left,
   full --ink text for contrast, with only the instruction itself in bold.
   -------------------------------------------------------------------------- */

/* `p.` prefix is deliberate: the form sits inside .hero__content, whose
   `.hero__content p` rule [0,1,1] would otherwise beat a bare class [0,1,0]
   and turn this muted grey - the same collision the Task 12 fixes above
   resolve. Tying the specificity lets source order decide in our favour. */
p.form-privacy {
  max-width: none;          /* span the textarea, not the 46ch prose measure */
  margin-top: 12px;
  padding: 12px 16px;
  background: rgba(var(--accent-rgb), .06);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  font-size: .9rem;
  line-height: 1.55;
  color: var(--ink);
}
p.form-privacy strong { font-weight: 600; }
