/* Production site styles — homepage, shared chrome, and polished production theme */

/* Register --fraunces-opsz-h1-var as an <integer> custom prop so Chrome
   115+ can interpolate it on the h1 font-variation-settings axis (the
   "opsz" axis animates from 14 → 144 as the H1 enters the viewport).
   Without @property the browser treats the custom prop as a string and
   the animation snaps. Firefox / older Safari ignore the registration
   and read the static fallback (--fraunces-opsz-h1-var: 144) declared
   on h1 — they render the display cut directly, which is the correct
   degrade path. */
@property --fraunces-opsz-h1-var {
  syntax: "<integer>";
  initial-value: 144;
  inherits: true;
}

/* Interpolate Fraunces SOFT on hover for price figures and FAQ summaries. */
@property --fraunces-soft-local {
  syntax: "<number>";
  initial-value: 50;
  inherits: false;
}

@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 100 900;
  font-variation-settings: normal;
  font-display: swap;
  src: url("assets/fonts/fraunces-latin.woff2") format("woff2-variations"),
       url("assets/fonts/fraunces-latin.woff2") format("woff2") tech("variations");
}

/* Metric-matched local fallbacks — one per weight so the browser matches
   the active weight exactly. Values from the OS/2 tables. */

@font-face {
  font-family: "Source Sans 3 Fallback";
  src: local("Arial");
  font-weight: 400;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
  size-adjust: 100%;
}

@font-face {
  font-family: "Source Sans 3 Fallback";
  src: local("Arial");
  font-weight: 600;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
  size-adjust: 100%;
}

@font-face {
  font-family: "Source Sans 3 Fallback";
  src: local("Arial");
  font-weight: 700;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
  size-adjust: 100%;
}

@font-face {
  font-family: "Source Serif 4 Fallback";
  src: local("Times New Roman");
  font-weight: 400;
  ascent-override: 88%;
  descent-override: 24%;
  line-gap-override: 0%;
  size-adjust: 100%;
}

@font-face {
  font-family: "Source Serif 4 Fallback";
  src: local("Times New Roman");
  font-weight: 600;
  ascent-override: 88%;
  descent-override: 24%;
  line-gap-override: 0%;
  size-adjust: 100%;
}

@font-face {
  font-family: "Source Serif 4 Fallback";
  src: local("Times New Roman");
  font-weight: 700;
  ascent-override: 88%;
  descent-override: 24%;
  line-gap-override: 0%;
  size-adjust: 100%;
}

@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("assets/fonts/source-sans-3-400.woff2") format("woff2");
}

@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("assets/fonts/source-sans-3-500.woff2") format("woff2");
}

@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("assets/fonts/source-sans-3-600.woff2") format("woff2");
}

@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("assets/fonts/source-sans-3-700.woff2") format("woff2");
}

@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("assets/fonts/source-serif-4-400.woff2") format("woff2");
}

@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("assets/fonts/source-serif-4-600.woff2") format("woff2");
}

@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("assets/fonts/source-serif-4-700.woff2") format("woff2");
}

:root {
  --ink: #171511;
  --paper: #f5efe4;
  --muted: rgba(23, 21, 17, 0.8);
  --line: rgba(23, 21, 17, 0.14);
  --accent: #9d3327;
  --accent-2: #0f6f60;
  --panel: rgba(255, 252, 244, 0.88);
  --shadow: 0 30px 90px rgba(32, 24, 14, 0.18);
  --font-sans: "Avenir Next", Avenir, "Helvetica Neue", Helvetica, sans-serif;
  --font-serif: Georgia, "Times New Roman", serif;
  --line-height: 1.5;
  color-scheme: light;
  font-family: var(--font-sans);
  font-size: 100%;
  line-height: var(--line-height);
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* Change 15: scroll-padding-bottom keeps focused elements above the
     mobile sticky CTA bar (~64-72px tall) so WCAG 2.4.11 (Focus Not
     Obscured, AA) is satisfied on /contact at 320-414px viewports.
     96px = bar + env(safe-area-inset-bottom) + 20px clearance. */
  scroll-padding-bottom: 96px;
}

body {
  min-width: 320px;
  margin: 0;
  color: var(--ink);
  background: var(--paper);
  overflow-x: hidden;
}

a {
  color: inherit;
  text-underline-offset: 0.2em;
}

p {
  text-wrap: pretty;
}

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

main,
section,
article,
aside,
div,
figure {
  min-width: 0;
}

h1,
h2,
h3,
p {
  margin: 0;
}

h1,
h2,
h3 {
  font-family: var(--font-serif);
  letter-spacing: 0;
  line-height: 1.06;
  overflow-wrap: break-word;
  text-wrap: balance;
}

/* Fraunces 4-axis: opsz 9-144, wght 100-900, SOFT 0-100, WONK 0-1.
   h1/h2 hit the display optical-size cut; h3 stays on Source Serif 4
   (text-grade serif). font-optical-sizing: auto is honoured by
   Chromium/Safari; Firefox ignores it but the explicit
   font-variation-settings still apply. */
.site-polished h1 {
  font-family: var(--font-serif);
  /* The opsz axis is driven by the scroll-tied custom prop registered
     with @property at the top of this file. Static fallback value is
     144 (the display cut). The keyframe below interpolates 14 → 144
     as the H1 enters the viewport. The SOFT / WONK axes are read from
     the redesigned tokens (per §2.2). */
  --fraunces-opsz-h1-var: 144;
  font-variation-settings:
    "wght" 600,
    "opsz" var(--fraunces-opsz-h1-var, var(--fraunces-opsz-h1-static)),
    "SOFT" var(--fraunces-soft-display),
    "WONK" var(--fraunces-wonk);
  font-optical-sizing: auto;
  /* Old-style figures + kerning + standard ligatures in the body prose
     rhythm (per §2.2 OpenType features). */
  font-feature-settings: var(--body-serif-features);
}

@supports (animation-timeline: view()) {
  .site-polished h1 {
    animation: fraunces-opsz-grow linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 60%;
  }
}

@keyframes fraunces-opsz-grow {
  from { --fraunces-opsz-h1-var: 14; }
  to   { --fraunces-opsz-h1-var: 144; }
}

@media (prefers-reduced-motion: reduce) {
  .site-polished h1 {
    animation: none;
    --fraunces-opsz-h1-var: 144;
  }
}

.site-polished h2 {
  font-family: var(--font-serif);
  font-variation-settings:
    "wght" 500,
    "opsz" var(--fraunces-opsz-h2-static),
    "SOFT" var(--fraunces-soft-display),
    "WONK" var(--fraunces-wonk);
  font-optical-sizing: auto;
  font-feature-settings: var(--body-serif-features);
}

.site-polished h3 {
  font-family: "Source Serif 4", "Source Serif 4 Fallback",
               "Source Han Serif TC", "PingFang TC",
               "Microsoft JhengHei", "Noto Serif CJK TC",
               Georgia, serif;
  font-variation-settings: normal;
  font-optical-sizing: auto;
}

/* rlh vertical rhythm — snaps headings, lists, and figures to one
   baseline grid. Base line-height lives in --line-height (1.5). */
h1 + *,
h2 + *,
h3 + * {
  margin-block-start: 0.5rlh;
}

h1,
h2,
h3 {
  margin-block-end: 0.5rlh;
}

p,
ul,
ol,
figure,
blockquote {
  margin-block-end: 1rlh;
}

.micro {
  margin: 0 0 12px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.site-polished {
  --ink: var(--theme-ink, #171511);
  --paper: var(--theme-paper, #f5efe4);
  --muted: color-mix(in srgb, var(--ink) 80%, transparent);
  --line: color-mix(in srgb, var(--ink) 16%, transparent);
  --divider-line: color-mix(in srgb, var(--ink) 30%, transparent);
  --panel: var(--theme-panel, #fffaf0);
  --accent: var(--theme-accent, #9d3327);
  --accent-2: var(--theme-accent-2, #0f6f60);
  --font-sans: "Source Sans 3", "Source Sans 3 Fallback",
               "PingFang TC", "Microsoft JhengHei", "Noto Sans CJK TC",
               system-ui, sans-serif;
  --font-serif: "Fraunces", "Source Serif 4", "Source Serif 4 Fallback",
                "Source Han Serif TC", "PingFang TC",
                "Microsoft JhengHei", "Noto Serif CJK TC",
                Georgia, "Times New Roman", serif;
  --shadow: 0 20px 60px rgba(32, 24, 14, 0.12);
  --space-section: clamp(24px, 3.6vw, 43px);
  --surface-soft: color-mix(in srgb, var(--ink) 4%, var(--paper));
  --surface-strong: color-mix(in srgb, var(--ink) 7%, var(--paper));
  --paper-darker: color-mix(in srgb, var(--ink) 6%, var(--paper));
  --surface-dark: #1a1814;
  --surface-dark-ink: #f3ecdf;
  --topbar-height-sm: 56px;
  --topbar-logo-sm: 40px;
  --topbar-contact-height-sm: 36px;
  --topbar-contact-text-sm: 0.62rem;
  --topbar-brand-detail-sm: 0.55rem;
  /* 7-step fluid type scale, anchored 360/1240 px. vw + rem preferred value. */
  --type-h1: clamp(2rem, 1.818vw + 1.591rem, 3rem);
  --type-h2: clamp(1.5rem, 1.2vw + 0.97rem, 2.0625rem);
  --type-h3: clamp(1.25rem, 0.7vw + 0.92rem, 1.625rem);
  --type-lead: clamp(1.125rem, 0.55vw + 0.87rem, 1.4375rem);
  --type-body: clamp(1rem, 0.55vw + 0.84rem, 1.1875rem);
  --type-sm: clamp(0.875rem, 0.15vw + 0.82rem, 1rem);
  --type-xs: clamp(0.8125rem, 0.1vw + 0.78rem, 0.875rem);
  /* Legacy aliases — keep consumers of --type-page-title / --type-section-title working. */
  --type-page-title: var(--type-h1);
  --type-section-title: var(--type-h2);
  --line-height: 1.5;
  --reveal-distance: 16px;
  --transition-duration: 180ms;
  /* Focus ring colour. Was a 2-layer box-shadow stack in the pre-2026
     design; now a flat colour for the consolidated :focus-visible outline
     declared in the global rule below. --ring-on-dark covers the inverse
     case for on-dark surfaces (footer address card, mobile sticky CTA). */
  --ring: var(--accent);
  --ring-on-dark: color-mix(in srgb, var(--surface-dark-ink) 70%, transparent);
  /* ── 2026 redesign tokens ───────────────────────────────────
     oklch equivalents of the hex palette + Fraunces axis pinning
     (per docs/redesign-plan.md §2.1, §2.2). Hex values stay as
     fallbacks in :root above. Falls back to srgb mix if oklch()
     is unsupported (Safari < 15.4, FF < 113). */
  --ink-oklch: oklch(20.85% 0.009 60);
  --paper-oklch: oklch(94.46% 0.017 78);
  --accent-oklch: oklch(47.58% 0.143 29.74);
  --accent-2-oklch: oklch(43.69% 0.084 184.5);
  --accent-soft: oklch(from var(--accent-oklch) 88% 0.045 h);
  --accent-2-soft: oklch(from var(--accent-2-oklch) 88% 0.03 h);
  --panel-oklch: oklch(98.21% 0.021 84);
  --surface-dark-oklch: oklch(20.99% 0.008 60);
  --surface-dark-ink-oklch: oklch(93.31% 0.024 80);
  --paper-darker-oklch: oklch(from var(--paper-oklch) calc(L - 0.04) C H);
  /* Fraunces axis policy (2026-06-15, per docs/redesign-plan.md §2.2):
     - opsz: animates on the h1 view() timeline (14 → 144); static 144/72 elsewhere.
     - wght: 600 display (h1, prices), 500 sub-display (h2). No animation.
     - SOFT: 50 display, 30 prices; nudges +12–18 on hover/focus (FAQ, rates).
     - WONK: 1 site-wide. No animation. */
  --fraunces-soft-display: 50;
  --fraunces-soft-display-hover: 68;
  --fraunces-soft-prices: 30;
  --fraunces-soft-prices-hover: 46;
  --fraunces-wonk: 1;
  --fraunces-opsz-h1-static: 144;
  --fraunces-opsz-h2-static: 72;
  --fraunces-opsz-prices: 144;
  /* OpenType features — body prose gets old-style figures, rates & form
     numerics get tabular lining figures (per §2.2). */
  --body-serif-features: "onum" 1, "kern" 1, "liga" 1;
  --tabular-figures: "tnum" 1, "lnum" 1, "zero" 1;
  /* Motion durations (§2.4) — the responsive band is 100-250ms. */
  --transition-fast: 120ms;
  --transition-default: 180ms;
  --transition-slow: 250ms;
  --view-transition-duration: 220ms;
  --icon-home: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 3.2 3.5 11v10h5.5v-6.5h6V21H20.5V11L12 3.2zm0 2.6 6.5 5.2V19H15v-6.5H9V19H5.5v-8.2L12 5.8z'/%3E%3C/svg%3E");
  /* Spacing scale (§2.3) — keeps the rlh rhythm, names the steps. */
  --space-xs: 0.25rlh;
  --space-sm: 0.5rlh;
  --space-md: 1rlh;
  --space-lg: 1.5rlh;
  --space-margin: clamp(14px, 4vw, 54px);
  color: var(--ink);
  background: var(--paper);
}

/* Change 9: single global :focus-visible rule. Replaces the previous
   12 per-selector :focus-visible rules, which all re-stated the same
   box-shadow: var(--ring) value with slight offset differences. The
   2px / 2px outline is the 2025-2026 consensus (WCAG 2.4.11 Focus
   Not Obscured + 2.4.13 Focus Appearance). The :focus:not(:focus-visible)
   sibling suppresses the default browser ring on mouse click so keyboard
   focus is the only path that shows the indicator. Per-element overrides
   for offset or colour differences are kept below where needed. */
:focus-visible {
  outline: 2px solid var(--ring, var(--accent));
  outline-offset: 2px;
  border-radius: 4px;
}
:focus:not(:focus-visible) { outline: none; }

/* Dark-surface focus ring — used by the footer address card which sits
   on var(--surface-dark). The mobile sticky CTA link is on var(--accent)
   (red) so it needs a paper-coloured ring instead (see below). The
   box-shadow halo below is the 2026-06-15 robustness hardening
   (per docs/redesign-plan.md §4.2 / §6.2) — an opaque dark ring around
   the focus ring makes the contrast check survive any future palette
   change without anyone redoing the alpha math. */
.site-polished .footer-address--linked:focus-visible {
  outline-color: var(--ring-on-dark);
  box-shadow: 0 0 0 4px var(--ink);
}

/* Mobile sticky CTA link sits on var(--accent) (red). The default
   --ring: var(--accent) ring would be invisible on its own background,
   so override to the paper colour for WCAG 2.4.11 contrast. */
.site-polished .mobile-cta-sticky-link:focus-visible {
  outline-color: var(--paper);
}

.site-polished .skip-link {
  position: absolute;
  left: 12px;
  top: -48px;
  z-index: 50;
  padding: 10px 14px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--paper);
  font-size: 0.82rem;
  font-weight: 700;
  text-decoration: none;
}

.site-polished .skip-link:focus {
  top: 12px;
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: grid;
  min-height: 62px;
  grid-template-columns: 48px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: max(9px, env(safe-area-inset-top)) clamp(14px, 4vw, 54px) 9px;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: blur(18px);
}

.topbar a,
.topbar-contact a {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1;
  text-decoration: none;
  text-transform: uppercase;
}

.topbar .home-link {
  display: inline-flex;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--ink) 15%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--paper) 70%, transparent);
  box-shadow: 0 8px 22px color-mix(in srgb, var(--ink) 6%, transparent);
  transition:
    background-color var(--transition-default) ease,
    border-color var(--transition-default) ease,
    box-shadow var(--transition-default) ease,
    transform var(--transition-default) ease;
}

.topbar .home-link::before {
  content: "";
  width: 20px;
  height: 20px;
  background-color: var(--ink);
  mask: var(--icon-home) center / contain no-repeat;
  -webkit-mask: var(--icon-home) center / contain no-repeat;
  transition: background-color var(--transition-default) ease;
}

.topbar-brand {
  min-width: 0;
}

.topbar-brand-link {
  display: grid;
  gap: 2px;
  text-decoration: none;
  text-transform: none;
}

.topbar-brand-name {
  color: var(--ink);
  font-family: var(--font-serif);
  font-size: var(--type-sm);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.1;
}

.topbar-brand-detail {
  color: var(--muted);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  line-height: 1.2;
  text-transform: uppercase;
}

.topbar-contact {
  display: inline-flex;
  align-items: center;
  justify-self: end;
  gap: clamp(14px, 2vw, 20px);
}

.topbar-contact .topbar-contact-link {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  padding: 0 2px 1px;
  color: color-mix(in srgb, var(--ink) 92%, transparent);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1;
  text-decoration: none;
  text-transform: none;
  border: none;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  transition:
    color var(--transition-duration) ease,
    border-color var(--transition-duration) ease;
}

.topbar-contact .topbar-contact-link:hover,
.topbar-contact .topbar-contact-link:focus-visible {
  color: color-mix(in srgb, var(--accent) 82%, var(--ink));
  border-bottom-color: var(--accent);
  background: transparent;
}

.site-polished .topbar-contact-link--primary {
  color: color-mix(in srgb, var(--accent) 88%, var(--ink));
  border-bottom-color: var(--accent);
  font-weight: 700;
}

@media (max-width: 419px) {
  .topbar {
    min-height: var(--topbar-height-sm);
    grid-template-columns: var(--topbar-logo-sm) minmax(0, 1fr) auto;
  }

  .topbar-brand {
    display: none;
  }

  .topbar .home-link {
    width: var(--topbar-logo-sm);
    height: var(--topbar-logo-sm);
  }

  .topbar-brand-name {
    font-size: 0.95rem;
  }

  .topbar-brand-detail {
    display: none;
  }

  .topbar-contact .topbar-contact-link,
  .topbar-contact .topbar-page-label {
    min-height: var(--topbar-contact-height-sm);
    font-size: var(--type-sm);
  }
}

.topbar .home-link:hover,
.topbar .home-link:focus-visible {
  background: color-mix(in srgb, var(--accent-2) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent-2) 28%, transparent);
  box-shadow: 0 10px 24px color-mix(in srgb, var(--ink) 10%, transparent);
}

.topbar .home-link:hover::before,
.topbar .home-link:focus-visible::before {
  background-color: var(--accent);
}

/* Topbar nav links use the footer underline technique; home-link keeps
   the pill outline geometry for :focus-visible. */
.topbar .home-link:focus-visible {
  border-radius: 999px;
}

.topbar-contact .topbar-contact-link:focus-visible,
.topbar-brand-link:focus-visible {
  border-radius: 0;
}

/* Buttons are pill-shaped (border-radius: 999px); the 4px global ring
   border-radius would square the focus outline off. Override to keep
   the pill outline. */
.site-polished .button:focus-visible {
  border-radius: 999px;
}

.site-polished .topbar-contact-link--primary:hover,
.site-polished .topbar-contact-link--primary:focus-visible {
  color: var(--accent);
  border-bottom-color: color-mix(in srgb, var(--accent) 82%, #000);
  background: transparent;
}

.topbar-page-label {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  padding: 0 2px 1px;
  color: color-mix(in srgb, var(--accent) 82%, var(--ink));
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1;
  text-transform: none;
  cursor: default;
  border: none;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  border-bottom: 2px solid var(--accent);
}

.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;
}

/* ── Page intro ──────────────────────────────────── */
.page-intro {
  width: min(1180px, calc(100% - 40px));
  margin: 0 auto;
  padding: clamp(40px, 6vw, 45px) 0 clamp(16px, 3vw, 24px);
}

.page-intro h1 {
  font-size: var(--type-page-title);
  line-height: 1.02;
  margin-top: 8px;
}

.site-about-page .about-profile {
  width: min(1180px, calc(100% - 40px));
  margin-inline: auto;
  padding: clamp(20px, 3vw, 32px) 0 0;
  position: relative;
}

.site-about-page .about-profile::before {
  content: "";
  position: absolute;
  top: 0;
  left: 10%;
  width: 80%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--divider-line) 5%, var(--divider-line) 95%, transparent);
  transform-origin: left center;
}

/* Scroll-triggered bow sweep on section dividers — ::after arc travels the
   string (::before) as each section enters the viewport. */
.site-polished .rates-editorial::after,
.site-polished .bio-strip-section::after,
.site-polished .faq-teaser::after,
.site-polished .site-faq::after,
.site-about-page .about-profile::after,
.site-about-page .about-section::after {
  content: "";
  position: absolute;
  top: -8px;
  left: 10%;
  width: 28px;
  height: 12px;
  pointer-events: none;
  opacity: 0;
  border: 1.25px solid var(--divider-line);
  border-bottom: none;
  border-radius: 14px 14px 0 0;
  transform: rotate(-6deg);
}

/* Single editorial column. Portrait at the top of the column (full
   column width on mobile, capped at ~480px on desktop) and the
   bio copy below — no card chrome on the portrait. */
.site-about-page .about-portrait {
  display: block;
  margin: 0 auto clamp(24px, 4vw, 32px);
  max-width: 480px;
  overflow: hidden;
  border-radius: 16px;
}

.site-about-page .about-portrait img {
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
  aspect-ratio: 4 / 5;
  border-radius: 16px;
}

.site-about-page .about-portrait figcaption {
  margin-top: 10px;
  color: var(--muted);
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.site-about-page .about-copy {
  max-width: 62ch;
  margin: 0 auto;
}

.site-about-page .about-section {
  padding: clamp(20px, 3vw, 28px) 0 0;
  position: relative;
}

.site-about-page .about-section::before {
  content: "";
  position: absolute;
  top: 0;
  left: 10%;
  width: 80%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--divider-line) 5%, var(--divider-line) 95%, transparent);
  transform-origin: left center;
}

.site-about-page .about-section h2 {
  font-size: var(--type-section-title);
  line-height: 1.06;
}

.site-about-page .about-section p {
  margin-top: 12px;
  color: var(--muted);
  font-size: 1.05rem;
  line-height: 1.65;
}

/* About page — word-by-word scroll reveal on the teaching-philosophy paragraph.
   site.js wraps each word in a <span> when view() timelines are supported
   and prefers-reduced-motion is off; each span fades in as it enters the
   viewport. No JS / no view() / reduced-motion → plain paragraph text. */
@supports (animation-timeline: view()) {
  .site-about-page .scroll-reveal-words span {
    opacity: 0.16;
    animation: scroll-reveal-word linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 35%;
  }
}

@keyframes scroll-reveal-word {
  to {
    opacity: 1;
  }
}

.site-about-page .about-section a {
  color: var(--accent-2);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

.site-about-page .about-section a:hover,
.site-about-page .about-section a:focus-visible {
  color: var(--accent);
}

.site-about-page .about-response {
  margin-top: 16px;
}

.site-about-page .about-cta {
  margin-top: clamp(24px, 4vw, 32px);
}

.lead {
  max-width: 42rem;
  color: var(--muted);
  font-size: var(--type-lead);
  line-height: 1.56;
}

.button {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  padding: 0 18px;
  border: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  border-radius: 999px;
  background: var(--surface-strong);
  color: var(--ink);
  font-size: 0.86rem;
  font-weight: 700;
  text-decoration: none;
  transition:
    transform 180ms ease,
    background-color 180ms ease,
    border-color 180ms ease,
    color 180ms ease,
    box-shadow 180ms ease;
}

.button:hover {
  background: color-mix(in srgb, var(--ink) 12%, var(--paper));
  border-color: color-mix(in srgb, var(--ink) 20%, transparent);
  transform: translateY(-2px);
  box-shadow: 0 8px 22px color-mix(in srgb, var(--ink) 10%, transparent);
}

/* Click feedback — settle the hover lift on mousedown so the press feels
   physical. Pairs with the 180ms transition on .button; reduced-motion
   users get an instant snap because the global guard at the bottom of
   this file collapses transition-duration to 0.01ms. */
.button:active {
  transform: translateY(0);
  box-shadow: 0 2px 6px color-mix(in srgb, var(--ink) 8%, transparent);
  transition-duration: 80ms;
}

/* .button is pill-shaped; the global :focus-visible rule's 4px
   border-radius would square the outline off, so override to keep
   the pill geometry. The outline itself is inherited from the
   global rule. */

.button.primary {
  border-color: transparent;
  background: var(--accent);
  color: var(--paper);
}

.button.primary:hover,
.button.primary:focus-visible {
  background: color-mix(in srgb, var(--accent) 82%, #000);
  border-color: transparent;
  box-shadow: 0 10px 28px color-mix(in srgb, var(--accent) 35%, transparent);
}

.button.ghost {
  background: transparent;
  border-color: color-mix(in srgb, var(--ink) 18%, transparent);
}

.button.ghost:hover,
.button.ghost:focus-visible {
  background: var(--surface-strong);
}

.button.on-dark {
  background: color-mix(in srgb, var(--surface-dark-ink) 10%, var(--surface-dark));
  border-color: color-mix(in srgb, var(--surface-dark-ink) 16%, transparent);
  color: var(--surface-dark-ink);
}

.button.on-dark:hover,
.button.on-dark:focus-visible {
  background: color-mix(in srgb, var(--surface-dark-ink) 18%, var(--surface-dark));
  border-color: color-mix(in srgb, var(--surface-dark-ink) 30%, transparent);
}

/* Change 10: form-field focus highlight. The .field and .contact-fieldset
   rules themselves live in contact.css; this rule reaches across the
   stylesheet boundary and only triggers on keyboard focus (not mouse
   click). Transition duration reads from the --transition-duration
   token (180ms default) so all on-site transitions sit in the 100-250ms
   responsive band (motion §2.1). */
.field:has(input:focus-visible),
.field:has(select:focus-visible),
.field:has(textarea:focus-visible),
.contact-fieldset:has(:focus-visible) {
  border-color: var(--ring, var(--accent));
  transition: border-color var(--transition-duration, 180ms) ease;
}

/* ── Hero (editorial full-bleed) ──────────────────────────────
   Asymmetric layout: text left (~42%), image right (~58%) on
   desktop. The h1 is wired to the scroll-driven opsz animation
   declared on .site-polished h1 above; this block only handles the
   geometry. The hand-tuned <br> in the markup is the one design
   decision the browser must not override. */
.site-polished .hero {
  display: grid;
  width: min(1180px, calc(100% - 40px));
  margin: 0 auto;
  padding: clamp(40px, 6vw, 88px) 0 clamp(32px, 5vw, 56px);
  grid-template-columns: minmax(0, 1fr);
  row-gap: clamp(20px, 3vw, 32px);
  align-items: stretch;
}

@media (min-width: 900px) {
  .site-polished .hero {
    grid-template-columns: minmax(0, 0.42fr) minmax(0, 0.58fr);
    column-gap: clamp(28px, 4vw, 64px);
    align-items: center;
  }
}

.site-polished .hero-copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
}

.site-polished .hero-copy h1 {
  font-size: var(--type-page-title);
  line-height: 1.0;
  letter-spacing: -0.01em;
  /* text-wrap: balance lets the browser pick the cleanest 2-line break
     for "Private cello lessons in Central, Hong Kong" at any column
     width — at wide viewports it lands on "Private cello lessons /
     in Central, Hong Kong"; at narrow viewports it reflows onto more
     lines rather than overflowing the column. Replaces an earlier
     <br>-in-markup + text-wrap: nowrap + max-width: 14ch pair that
     caused the H1 to clip behind the hero figure between 600-1023px. */
  text-wrap: balance;
}

.site-polished .hero-lead {
  margin-top: clamp(16px, 2vw, 20px);
  max-width: 36rem;
  color: var(--muted);
  font-size: var(--type-lead);
  line-height: 1.45;
}

.site-polished .hero-cta {
  margin-top: clamp(24px, 3vw, 32px);
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.site-polished .hero-cta .button {
  min-height: 48px;
  padding: 0 26px;
}

/* The hero figure replaces the old .hero-photo. It keeps the same
   round-cornered, paper-tinted photo chrome, but the layout positions
   it on the right of the hero on desktop. */
.site-polished .hero-figure {
  position: relative;
  overflow: hidden;
  margin: 0;
  border: 1px solid var(--line);
  border-radius: 24px;
  background: #111;
  box-shadow: var(--shadow);
  min-height: clamp(320px, 44vw, 520px);
}

.site-polished .hero-figure img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 5%;
  filter: var(--photo-filter, none);
}

/* ── Rates — editorial price list ─────────────────────────────
   No card chrome. Two equal-width columns separated by a 1px
   vertical hairline on desktop; the same hairline becomes a 1px
   horizontal rule between the two stacked rates on mobile. */
.site-polished .rates-editorial {
  width: min(1180px, calc(100% - 40px));
  margin-inline: auto;
  padding: clamp(24px, 4vw, 48px) 0;
  position: relative;
}

.site-polished .rates-editorial::before {
  content: "";
  position: absolute;
  top: 0;
  left: 10%;
  width: 80%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--divider-line) 5%, var(--divider-line) 95%, transparent);
  transform-origin: left center;
}

.site-polished .rates-editorial .rates-heading {
  font-size: var(--type-section-title);
  line-height: 1.06;
}

.site-polished .rates-editorial .rates-intro {
  margin-top: clamp(10px, 1.5vw, 14px);
  max-width: 62ch;
  color: var(--muted);
  font-size: var(--type-body);
  line-height: 1.55;
}

.site-polished .rates-list {
  display: grid;
  grid-template-columns: 1fr;
  margin: clamp(28px, 4vw, 44px) 0 0;
}

@media (min-width: 600px) {
  .site-polished .rates-list {
    grid-template-columns: 1fr 1fr;
    /* The 1px vertical hairline between the two rates. The two
       columns are gapless; a centred 1px line lives in the gutter. */
    column-gap: 0;
    position: relative;
  }

  .site-polished .rates-list::before {
    content: "";
    position: absolute;
    top: 12px;
    bottom: 12px;
    left: 50%;
    width: 1px;
    background: var(--line);
    transform: translateX(-0.5px);
  }
}

.site-polished .rates-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: clamp(8px, 1.5vw, 14px) 0;
}

@media (min-width: 600px) {
  .site-polished .rates-item:first-child {
    padding-right: clamp(20px, 3vw, 32px);
  }

  .site-polished .rates-item:last-child {
    padding-left: clamp(20px, 3vw, 32px);
  }
}

@media (max-width: 599px) {
  /* On mobile the two rates stack and the hairline becomes a horizontal
     rule above the second rate. The hairline + the shared gutter keep
     the two rates visually paired without using a card chrome. */
  .site-polished .rates-item + .rates-item {
    margin-top: 8px;
    padding-top: clamp(20px, 3vw, 28px);
    border-top: 1px solid var(--line);
  }
}

.site-polished .rates-length {
  margin: 0;
  color: var(--muted);
  font-family: var(--font-sans);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.site-polished .rates-amount {
  margin: 0;
  display: flex;
  align-items: baseline;
}

.site-polished .rates-amount-value {
  --fraunces-soft-local: var(--fraunces-soft-prices);
  font-family: var(--font-serif);
  font-size: clamp(2.25rem, 4.5vw, 3.5rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums lining-nums slashed-zero;
  font-feature-settings: var(--tabular-figures);
  font-variation-settings:
    "wght" 600,
    "opsz" var(--fraunces-opsz-prices),
    "SOFT" var(--fraunces-soft-local),
    "WONK" var(--fraunces-wonk);
  transition: --fraunces-soft-local var(--transition-default) ease;
}

@media (hover: hover) and (pointer: fine) {
  .site-polished .rates-amount-value:hover {
    --fraunces-soft-local: var(--fraunces-soft-prices-hover);
  }
}

.site-polished .rates-amount-value:focus-visible {
  --fraunces-soft-local: var(--fraunces-soft-prices-hover);
}

.site-polished .rates-bestfor {
  margin: 0;
  max-width: 30ch;
  color: var(--muted);
  font-size: var(--type-sm);
  line-height: 1.55;
}

.site-polished .rates-cta {
  margin-top: clamp(28px, 4vw, 40px);
}

.site-polished .rates-cta .button {
  min-height: 48px;
  padding: 0 26px;
}

/* ── About the lessons — bio + portrait strip ──────────────── */
.site-polished .bio-strip-section {
  width: min(1180px, calc(100% - 40px));
  margin-inline: auto;
  padding: clamp(28px, 4vw, 48px) 0 clamp(20px, 3vw, 32px);
  position: relative;
}

.site-polished .bio-strip-section::before {
  content: "";
  position: absolute;
  top: 0;
  left: 10%;
  width: 80%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--divider-line) 5%, var(--divider-line) 95%, transparent);
  transform-origin: left center;
}

.site-polished .bio-strip-heading {
  font-size: var(--type-section-title);
  line-height: 1.06;
}

.site-polished .bio-strip-copy {
  margin-top: clamp(14px, 2vw, 18px);
  max-width: 64ch;
  color: var(--muted);
  font-size: var(--type-body);
  line-height: 1.6;
}

.site-polished .bio-strip-section .credentials-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin: clamp(18px, 2.5vw, 24px) 0 0;
  padding: 0;
  list-style: none;
  color: var(--muted);
  font-size: var(--type-sm);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.site-polished .bio-strip-section .credential {
  display: inline;
  padding: 0;
  border: 0;
  background: transparent;
  font-size: inherit;
  color: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
}

.site-polished .bio-strip-section .credential-sep {
  color: var(--line);
  font-size: inherit;
  user-select: none;
}

.site-polished .bio-strip-section .credential-text strong {
  color: var(--ink);
  font-family: var(--font-sans);
  font-weight: 700;
  letter-spacing: 0.06em;
}

/* The full-bleed portrait image strip. The cello-studio photo is
   natively 1080:1350 (4:5, taller than wide), so to make it a
   horizontal band we crop the bottom of the image with object-fit:
   cover + object-position: center bottom. The strip's height is
   ~280-360px on desktop, ~200px on mobile. */
.site-polished .bio-strip {
  position: relative;
  overflow: hidden;
  margin: clamp(28px, 4vw, 44px) 0 0;
  aspect-ratio: 16 / 5;
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 16px;
  background:
    linear-gradient(
      135deg,
      color-mix(in srgb, var(--paper) 70%, var(--panel)) 0%,
      color-mix(in srgb, var(--surface-soft) 80%, var(--paper)) 100%
    );
}

@media (max-width: 600px) {
  .site-polished .bio-strip {
    aspect-ratio: 4 / 3;
  }
}

.site-polished .bio-strip img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* cello-studio photo (1080x1350, 4:5): the subject's face sits
     slightly above the vertical centre, and the cello body fills
     the lower band. A 16:5 strip shows the middle ~25% of the
     source; anchor the visible band ~30% from the top so the face
     is centred in the strip with the upper cello visible. */
  object-position: center 30%;
  filter: var(--photo-filter, none);
}

.site-polished .bio-strip-readmore {
  margin: clamp(14px, 2vw, 20px) 0 0;
  font-size: var(--type-sm);
}

.site-polished .bio-strip-readmore a {
  color: var(--accent-2);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent-2) 35%, transparent);
  padding-bottom: 1px;
  transition: color 180ms ease, border-color 180ms ease;
}

.site-polished .bio-strip-readmore a:hover,
.site-polished .bio-strip-readmore a:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ── Common questions (FAQ teaser, refit to <details>) ─────── */
.site-polished .faq-teaser {
  width: min(1180px, calc(100% - 40px));
  margin-inline: auto;
  padding: clamp(24px, 4vw, 40px) 0;
  position: relative;
}

.site-polished .faq-teaser::before {
  content: "";
  position: absolute;
  top: 0;
  left: 10%;
  width: 80%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--divider-line) 5%, var(--divider-line) 95%, transparent);
  transform-origin: left center;
}

.site-polished .faq-teaser h2 {
  font-size: var(--type-section-title);
  line-height: 1.06;
}

.site-polished .faq-teaser-list {
  display: grid;
  gap: 0;
  margin: clamp(20px, 3vw, 28px) 0 0;
}

.site-polished .faq-teaser-item {
  border-top: 1px solid var(--line);
}

.site-polished .faq-teaser-item:last-child {
  border-bottom: 1px solid var(--line);
}

.site-polished .faq-teaser-item summary {
  --fraunces-soft-local: var(--fraunces-soft-display);
  display: flex;
  min-height: 48px;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: clamp(14px, 2vw, 18px) 0;
  color: var(--ink);
  font-family: var(--font-serif);
  font-size: var(--type-h3);
  font-weight: 600;
  line-height: 1.25;
  font-variation-settings:
    "wght" 600,
    "opsz" var(--fraunces-opsz-h2-static),
    "SOFT" var(--fraunces-soft-local),
    "WONK" var(--fraunces-wonk);
  cursor: pointer;
  list-style: none;
  transition:
    color var(--transition-default) ease,
    --fraunces-soft-local var(--transition-default) ease;
}

.site-polished .faq-teaser-item summary::-webkit-details-marker {
  display: none;
}

.site-polished .faq-teaser-item summary::after {
  flex-shrink: 0;
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--ink) 18%, transparent);
  background: var(--surface-soft);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1;
  content: "+";
  transition: background-color 180ms ease, color 180ms ease;
}

.site-polished .faq-teaser-item summary:hover {
  color: var(--accent);
}

@media (hover: hover) and (pointer: fine) {
  .site-polished .faq-teaser-item summary:hover {
    --fraunces-soft-local: var(--fraunces-soft-display-hover);
  }
}

.site-polished .faq-teaser-item summary:focus-visible {
  --fraunces-soft-local: var(--fraunces-soft-display-hover);
}

.site-polished .faq-teaser-item summary:hover::after {
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  color: var(--accent);
}

.site-polished .faq-teaser-item[open] summary::after {
  content: "−";
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

.site-polished .faq-teaser-item[open] summary {
  padding-bottom: 0;
}

.site-polished .faq-teaser-item > p {
  margin: 0;
  padding: 0 0 clamp(14px, 2vw, 18px);
  max-width: 60ch;
  color: var(--muted);
  font-size: 0.98rem;
  line-height: 1.6;
}

.site-polished .faq-teaser-item > p a {
  font-weight: 700;
}

.site-polished .faq-teaser-action {
  margin: clamp(20px, 3vw, 28px) 0 0;
  font-size: var(--type-sm);
}

.site-polished .faq-teaser-action a {
  color: var(--accent-2);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent-2) 35%, transparent);
  padding-bottom: 1px;
  transition: color 180ms ease, border-color 180ms ease;
}

.site-polished .faq-teaser-action a:hover,
.site-polished .faq-teaser-action a:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ── Footer map link (margin override; colours in .site-footer block) ── */
.site-polished .footer-map-link {
  margin: clamp(12px, 2vw, 16px) 0 0;
  font-size: var(--type-sm);
}

.site-polished .site-faq {
  width: min(1180px, calc(100% - 40px));
  margin-inline: auto;
  padding: clamp(20px, 3vw, 32px) 0;
  position: relative;
}

.site-polished .site-faq::before {
  content: "";
  position: absolute;
  top: 0;
  left: 10%;
  width: 80%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--divider-line) 5%, var(--divider-line) 95%, transparent);
  transform-origin: left center;
}

.site-polished .site-faq h2 {
  font-size: var(--type-section-title);
  line-height: 1.06;
}

.site-polished .faq-list {
  display: grid;
  gap: clamp(16px, 2.5vw, 22px);
  margin-top: clamp(18px, 3vw, 28px);
}

/* Change 19: visual cue for .faq-group headings. The previous treatment
   was a small uppercase label with no divider; the new treatment gives
   each group a top border and section-title sizing so sighted users get
   a clear "new section starts here" signal — important for the change-17
   UX where each group's first item is open by default. The :first-child
   override keeps the very first group flush to the FAQ section header
   (no top border / margin) so the visual cue fires only between groups. */
.site-polished .faq-group {
  grid-column: 1 / -1;
  margin-block-start: 1.5rlh;
  padding-block-start: 0.5rlh;
  border-block-start: 1px solid var(--line);
  font-size: var(--type-section-title);
  color: var(--ink);
}

.site-polished .faq-group:first-child {
  margin-block-start: 0;
  padding-block-start: 0;
  border-block-start: 0;
}

.site-polished .faq-item {
  padding-top: clamp(16px, 2.5vw, 20px);
  border-top: 1px solid var(--line);
}

.site-polished .faq-item:first-child {
  padding-top: 0;
  border-top: 0;
}

.site-polished .faq-item h3 {
  font-size: var(--type-h3);
  line-height: 1.25;
}

.site-polished .faq-item p {
  margin-top: 10px;
  color: var(--muted);
  font-size: 1rem;
  line-height: 1.62;
}

.site-polished .faq-item a {
  font-weight: 700;
}

.site-polished details.faq-item {
  padding-top: 0;
  border-top: 1px solid var(--line);
}

.site-polished details.faq-item:first-of-type {
  border-top: 0;
}

.site-polished details.faq-item summary {
  --fraunces-soft-local: var(--fraunces-soft-display);
  display: flex;
  min-height: 44px;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: clamp(14px, 2.5vw, 18px) 0;
  color: var(--ink);
  font-family: var(--font-serif);
  font-size: var(--type-h3);
  font-weight: 700;
  line-height: 1.25;
  font-variation-settings:
    "wght" 700,
    "opsz" var(--fraunces-opsz-h2-static),
    "SOFT" var(--fraunces-soft-local),
    "WONK" var(--fraunces-wonk);
  cursor: pointer;
  list-style: none;
  transition:
    color var(--transition-default) ease,
    --fraunces-soft-local var(--transition-default) ease;
}

.site-polished details.faq-item summary::-webkit-details-marker {
  display: none;
}

.site-polished details.faq-item summary::after {
  flex-shrink: 0;
  display: inline-grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--ink) 18%, transparent);
  background: var(--surface-soft);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1;
  content: "+";
  transition:
    background-color 180ms ease,
    border-color 180ms ease,
    color 180ms ease,
    transform 240ms ease;
}

.site-polished details.faq-item summary:hover {
  color: var(--accent);
}

@media (hover: hover) and (pointer: fine) {
  .site-polished details.faq-item summary:hover {
    --fraunces-soft-local: var(--fraunces-soft-display-hover);
  }
}

.site-polished details.faq-item summary:focus-visible {
  --fraunces-soft-local: var(--fraunces-soft-display-hover);
}

.site-polished details.faq-item summary:hover::after {
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  color: var(--accent);
}

/* FAQ summary focus state inherits the global :focus-visible rule. The
   4px global border-radius is fine for the summary box (it doesn't fight
   the chevron, which is positioned separately). */

.site-polished details.faq-item[open] summary::after {
  content: "−";
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

.site-polished details.faq-item[open] summary:hover::after {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--paper);
}

.site-polished details.faq-item[open] summary {
  padding-bottom: 0;
}

.site-polished details.faq-item > p {
  margin-top: 0;
  padding-bottom: clamp(14px, 2.5vw, 18px);
}

/* Change 18: smooth-height FAQ <details> open/close.
   interpolate-size: allow-keywords is scoped to <main> per motion
   research advice — setting it on :root risks animating an unintended
   transition: height elsewhere. ::details-content is a <details>-
   scoped pseudo (Baseline in Chrome 131+, Safari 18.4+); Firefox
   falls back to the snap behaviour, which is the correct degrade
   path. The 250ms duration sits in the responsive band (motion §2.4). */
main {
  interpolate-size: allow-keywords;
}
::details-content {
  height: 0;
  overflow: clip;
  transition:
    height 0.25s ease,
    content-visibility 0.25s ease allow-discrete;
}
[open]::details-content {
  height: auto;
}
@media (prefers-reduced-motion: reduce) {
  ::details-content { transition: none; }
}

.site-footer {
  margin-top: clamp(32px, 5vw, 64px);
  border-top: 1px solid var(--line);
  background: var(--surface-dark);
  color: color-mix(in srgb, var(--surface-dark-ink) 92%, transparent);
  padding:
    clamp(32px, 5vw, 56px)
    max(20px, env(safe-area-inset-right))
    calc(clamp(24px, 4vw, 40px) + env(safe-area-inset-bottom))
    max(20px, env(safe-area-inset-left));
}

.footer-inner {
  width: min(1180px, 100%);
  margin: 0 auto;
}

.footer-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.75fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 48px);
  align-items: start;
}

.footer-col {
  min-width: 0;
}

.footer-eyebrow {
  margin: 0 0 clamp(10px, 1.5vw, 14px);
  color: var(--accent);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.footer-brand-name {
  margin: 0 0 4px;
  color: color-mix(in srgb, var(--surface-dark-ink) 98%, transparent);
  font-family: var(--font-serif);
  font-size: var(--type-h3);
  line-height: 1.15;
}

.footer-studio-name {
  margin: 0 0 8px;
  color: color-mix(in srgb, var(--surface-dark-ink) 88%, transparent);
  font-family: var(--font-serif);
  font-size: var(--type-sm);
  line-height: 1.3;
}

.footer-studio-name strong {
  font-weight: 700;
}

.footer-address-line {
  margin: 0;
  display: block;
  max-width: 36rem;
  color: color-mix(in srgb, var(--surface-dark-ink) 72%, transparent);
  font-size: 0.95rem;
  line-height: 1.55;
}

.footer-address-line + .footer-address-line {
  margin-top: 0;
}

.footer-nav {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}

.footer-nav a {
  color: color-mix(in srgb, var(--surface-dark-ink) 92%, transparent);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  padding-bottom: 1px;
  transition:
    color var(--transition-duration) ease,
    border-color var(--transition-duration) ease;
}

.footer-nav a:hover,
.footer-nav a:focus-visible {
  color: color-mix(in srgb, var(--accent) 75%, var(--surface-dark-ink));
  border-bottom-color: var(--accent);
}

.footer-lessons-summary {
  margin: 0;
  max-width: 28ch;
  color: color-mix(in srgb, var(--surface-dark-ink) 72%, transparent);
  font-size: 0.95rem;
  line-height: 1.55;
}

.footer-lessons-rates {
  margin: 10px 0 0;
  color: color-mix(in srgb, var(--surface-dark-ink) 88%, transparent);
  font-size: var(--type-sm);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.footer-cta {
  margin: clamp(16px, 2.5vw, 22px) 0 0;
}

.footer-map-link {
  margin: clamp(12px, 2vw, 16px) 0 0;
}

.footer-map-link a,
.footer-external a {
  color: color-mix(in srgb, var(--surface-dark-ink) 92%, transparent);
  font-size: var(--type-sm);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  padding-bottom: 1px;
  transition:
    color var(--transition-duration) ease,
    border-color var(--transition-duration) ease;
}

.footer-map-link a:hover,
.footer-map-link a:focus-visible,
.footer-external a:hover,
.footer-external a:focus-visible {
  color: color-mix(in srgb, var(--accent) 75%, var(--surface-dark-ink));
  border-bottom-color: var(--accent);
}

.footer-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
  margin-top: clamp(28px, 4vw, 40px);
  padding-top: clamp(18px, 2.5vw, 24px);
  border-top: 1px solid color-mix(in srgb, var(--surface-dark-ink) 16%, transparent);
}

.footer-external {
  margin: 0;
  color: color-mix(in srgb, var(--surface-dark-ink) 58%, transparent);
  font-size: var(--type-xs);
  line-height: 1.5;
}

.site-footer a:focus-visible {
  outline-color: var(--ring-on-dark);
}

@media (max-width: 820px) {
  .footer-grid {
    grid-template-columns: 1fr 1fr;
  }

  .footer-col--studio {
    grid-column: 1 / -1;
  }
}

@media (max-width: 520px) {
  .footer-grid {
    grid-template-columns: 1fr;
    gap: clamp(24px, 5vw, 32px);
  }

  .footer-meta {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* Legacy footer address styles (contact page variants) */
.footer-address {
  min-width: 0;
  padding-top: clamp(18px, 3vw, 34px);
  font-style: normal;
}

.footer-address strong {
  display: block;
  color: var(--ink);
  font-family: var(--font-serif);
  font-size: var(--type-h2);
  line-height: 1.08;
}

.footer-address span,
.footer-address p {
  color: var(--muted);
  line-height: 1.62;
}

.footer-address span,
.footer-address .footer-address-line {
  display: block;
  max-width: 42rem;
  margin-top: 10px;
}

.footer-address .footer-address-line + .footer-address-line {
  margin-top: 4px;
}

.footer-address p {
  margin-top: 12px;
}

.footer-address a {
  color: inherit;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

.site-polished .footer-address--linked {
  display: block;
  padding: 18px 20px;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: color-mix(in srgb, var(--panel) 70%, transparent);
  text-decoration: none;
  transition:
    border-color 180ms ease,
    background-color 180ms ease;
}

.site-polished .footer-address--linked:hover,
.site-polished .footer-address--linked:focus-visible {
  border-color: color-mix(in srgb, var(--accent-2) 40%, var(--line));
  background: color-mix(in srgb, var(--accent-2) 8%, var(--panel));
  box-shadow: 0 0 0 4px var(--ink);
}

.site-polished .footer-address--linked strong,
.site-polished .footer-address--linked span {
  color: inherit;
}

/* Sticky mobile CTA — only shown at narrow viewports after the hero scrolls away */
.mobile-cta-sticky {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 40;
  display: block;
  padding: 8px 12px calc(8px + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid var(--line);
  box-shadow: 0 -8px 24px color-mix(in srgb, var(--ink) 8%, transparent);
  transform: translateY(110%);
  transition: transform 200ms ease;
}

/* When the sticky bar is visible, reserve space at the bottom of the page
   so the last section's content can be scrolled past it. The bar is ~64px
   tall on a typical phone; add a little extra for the safe-area inset. */
body:has(.mobile-cta-sticky.is-visible) {
  padding-bottom: 80px;
}

.mobile-cta-sticky[hidden] {
  display: none;
}

.mobile-cta-sticky.is-visible {
  transform: none;
}

.mobile-cta-sticky-link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 18px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--paper);
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-decoration: none;
}

.mobile-cta-sticky-link:hover,
.mobile-cta-sticky-link:focus-visible {
  background: color-mix(in srgb, var(--accent) 82%, #000);
}

@media (min-width: 641px) {
  .mobile-cta-sticky {
    display: none !important;
  }
}

/* §Micro-interactions — scroll-driven string divider (bow sweep).
   The ::before hairline draws left-to-right; ::after arc sweeps the same
   span once the section enters the viewport. Static end-state when view()
   is unsupported or reduced-motion is on. */
@supports (animation-timeline: view()) {
  .site-polished .rates-editorial::before,
  .site-polished .bio-strip-section::before,
  .site-polished .faq-teaser::before,
  .site-polished .site-faq::before,
  .site-about-page .about-profile::before,
  .site-about-page .about-section::before {
    animation: string-draw linear both;
    animation-timeline: view();
    animation-range: entry -5% cover 24%;
  }

  .site-polished .rates-editorial::after,
  .site-polished .bio-strip-section::after,
  .site-polished .faq-teaser::after,
  .site-polished .site-faq::after,
  .site-about-page .about-profile::after,
  .site-about-page .about-section::after {
    animation: bow-sweep linear both;
    animation-timeline: view();
    animation-range: entry -5% cover 24%;
  }
}

@keyframes string-draw {
  from {
    transform: scaleX(0);
    opacity: 0.3;
  }
  to {
    transform: scaleX(1);
    opacity: 1;
  }
}

@keyframes bow-sweep {
  from {
    left: 10%;
    opacity: 0;
    transform: rotate(-10deg);
  }
  10% {
    opacity: 0.55;
  }
  90% {
    opacity: 0.55;
  }
  to {
    left: calc(90% - 28px);
    opacity: 0;
    transform: rotate(10deg);
  }
}

/* Change 7: scroll-driven reveal via animation-timeline: view().
   Compositor-only, 4 lines of CSS replacing the previous JS IntersectionObserver
   (initReveal in site.js — to be removed by the JS agent in a follow-up).
   The view() timeline is Baseline in Chrome 115+ / Edge 115+ / Safari 18.4+;
   Firefox 144+ behind a flag — the @supports fallback disables the animation
   and shows the end state immediately. The 30% cover range stops the
   animation once the section is ~30% into the viewport, avoiding the
   "animate for the whole time on screen" busy default. */
.reveal {
  animation: reveal-up linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

@keyframes reveal-up {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .reveal { animation: none; opacity: 1; }
}

@supports not (animation-timeline: view()) {
  .reveal { animation: none; opacity: 1; }
}

@media (max-width: 900px) {
  /* Hero collapses to single column under 900px: text on top, image below. */
  .site-polished .hero {
    grid-template-columns: 1fr;
  }

  .site-polished .hero .hero-figure {
    width: 100%;
    max-width: 480px;
    aspect-ratio: 4 / 3;
    margin: 0 auto;
    border-radius: 18px;
  }

  .site-polished .hero .hero-figure img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 5%;
  }
}

@media (max-width: 640px) {
  .topbar-page-label {
    min-height: 44px;
    padding: 0 2px 1px;
    font-size: var(--type-sm);
    letter-spacing: 0.01em;
  }

  .topbar {
    padding: max(9px, env(safe-area-inset-top)) max(14px, env(safe-area-inset-right)) 9px
      max(14px, env(safe-area-inset-left));
    gap: 8px;
  }

  /* At ≤640 the topbar switches to safe-area-aware padding and a tighter gap.
     The 419px media block below handles per-element small-phone sizing
     (smaller logo, smaller brand text, smaller contact buttons). */

  .site-polished details.faq-item summary {
    min-height: 48px;
  }

  .footer-address--linked {
    padding: 16px 18px;
    line-height: 1.55;
  }

  /* CTA buttons: full-width on mobile so the tap target is generous. */
  .site-polished .hero .hero-cta .button,
  .site-polished .rates-editorial .rates-cta .button {
    width: 100%;
  }
}

/* Change 11: extended prefers-reduced-motion guard. The 0.01ms override
   trick (instead of `none`) preserves any transitionend / animationend
   listener contracts — W3C WAI technique C39. .reveal gets opacity:1
   so the content is never hidden in reduced-motion mode. The h1 opsz
   scroll-tied animation is killed (the static 144 fallback applies
   via the explicit rule on .site-polished h1 above). The view-transition
   { navigation: none } block for change 8 lives in a separate
   @view-transition declaration owned by the HTML agent — do not
   duplicate here. */

/* ── 2026 redesign signature moves ───────────────────────────────
   Per docs/redesign-plan.md §3 (macro grid), §4 (signature moves),
   §4.4 (prefers-contrast). Implemented in CSS only; the only markup
   change for the cross-doc view transition (move 4.2) is the inline
   style="view-transition-name: hero-photo" on the hero/portrait img. */

/* §3.1 — Page-level macro grid. The default auto-placed sections sit
   on the wide [content-start / content-end] track (the full content
   area minus the page margins); sections that want the 60ch reading
   column opt-in via grid-column: text-start / text-end; the hero opts
   into the full-bleed [full-start / full-end] track. The 1fr gutters
   collapse to zero on narrow viewports, so the layout is responsive
   without media queries. */
.site-polished main {
  display: grid;
  grid-template-columns:
    [full-start]    var(--space-margin)
    [content-start] minmax(0, 1fr)
    [text-start]    minmax(0, 60ch)
    [text-end]      minmax(0, 1fr)
    [marginalia-start] minmax(0, 18ch)
    [content-end]   var(--space-margin)
    [full-end];
  row-gap: var(--space-section); /* sole inter-section spacing — sections must not also set margin-block-end */
}

/* Default placement for every <section> in <main>: it spans the wide
   content area. Sections that want to live in the 60ch reading column
   or full-bleed override this in their own rules. */
.site-polished main > section {
  grid-column: content-start / content-end;
}

/* Full-bleed override: the hero spans the full row. */
.site-polished main > section.hero {
  grid-column: full-start / full-end;
}

/* §4.1 — Paper grain via feTurbulence. Inline data-URL'd SVG tiled on the
   body at ~3% opacity. Visible on close inspection, invisible at reading
   distance. mix-blend-mode: multiply keeps the grain from brightening the
   page; z-index 0 keeps it above the paper fill and below page content. */
body.site-polished::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.09 0 0 0 0 0.07 0 0 0 0 0.05 0 0 0 0.08 0'/></filter><rect width='100%' height='100%' filter='url(%23g)'/></svg>");
  background-repeat: repeat;
  opacity: 0.03;
  mix-blend-mode: multiply;
}

/* §4.5 — Subgrid for the credentials row on the bio strip. Falls
   back to the existing flex layout if subgrid is unsupported. */
@supports (grid-template-columns: subgrid) {
  .site-polished .bio-strip-section .credentials-row {
    display: grid;
    grid-template-columns: subgrid;
    grid-column: text-start / text-end;
    gap: 0 1.5rem;
    align-items: baseline;
  }
}

/* §4.2 — Cross-document view transition: hero-photo morph between
   / and /about. Gated by @supports(cross-document: auto) and
   prefers-reduced-motion: no-preference + min-width: 768px. */
@media (prefers-reduced-motion: no-preference) and (min-width: 768px) {
  @supports (cross-document: auto) {
    @view-transition { navigation: auto; }
  }
}

/* The view-transition-name is set inline on the hero/portrait img
   (style="view-transition-name: hero-photo"). The default cross-fade
   is 250ms; we don't need to override it (close to the
   --view-transition-duration token). object-fit: cover on the
   ::view-transition-group prevents aspect-ratio warping during the
   morph (per CSS-Tricks 18 May 2026 gotchas). */
::view-transition-group(img) {
  object-fit: cover;
}
::view-transition-old(img),
::view-transition-new(img) {
  object-fit: cover;
}

/* §4.4 — prefers-contrast: more block. Bumps body text to pure ink,
   bumps hairlines to 1.5px, removes color-mix washes on focus rings. */
@media (prefers-contrast: more) {
  .site-polished {
    --ink: #000;
    --muted: #000;
    --line: #000;
  }
  .site-polished * {
    border-color: currentColor;
  }
  .site-polished hr,
  .site-polished .rates-item + .rates-item {
    border-width: 1.5px;
  }
}

@media (forced-colors: active) {
  .site-footer {
    background: Canvas;
    color: CanvasText;
    border-top: 1px solid CanvasText;
  }

  .footer-meta {
    border-top-color: CanvasText;
  }

  .footer-eyebrow,
  .footer-nav a,
  .footer-map-link a,
  .footer-external a {
    color: LinkText;
  }

  .footer-nav a:hover,
  .footer-nav a:focus-visible,
  .footer-map-link a:hover,
  .footer-map-link a:focus-visible,
  .footer-external a:hover,
  .footer-external a:focus-visible {
    color: LinkText;
  }
}
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
    transition-duration: 0.01ms !important;
  }

  .reveal {
    opacity: 1;
  }

  .site-about-page .scroll-reveal-words span {
    opacity: 1;
  }
}
