/* Lucky Nails — class layer.
   The design system ships tokens as CSS custom properties but implements
   every component as React with inline style objects. This file is the
   class-based translation. Values come from tokens only; nothing here
   invents a color, radius, or duration. */

@import url("tokens/colors.css");
@import url("tokens/typography.css");
@import url("tokens/spacing.css");
@import url("tokens/radius.css");
@import url("tokens/elevation.css");
@import url("tokens/motion.css");
@import url("tokens/base.css");
@import url("tokens/fonts.css");

:root{--header-h:76px}
html{scroll-padding-top:calc(var(--header-h) + var(--space-4))}

/* ---------- Base overrides ----------
   tokens/base.css sets img{max-width:100%;display:block} with no
   height:auto, so an image rendered narrower than its natural width
   keeps its height attribute and squashes. Component rules that set an
   explicit height (.hero__img, .gallery__item img,
   .gallery__item--feature img) carry higher specificity than the bare
   element selector below and are unaffected. */
img{height:auto}

/* ---------- Layout primitives ---------- */
.wrap{max-width:var(--container-max);margin:0 auto;padding:0 var(--gutter-page-lg)}
.section{padding:var(--space-20) 0}
.section--sunken{background:var(--surface-sunken)}
.measure{max-width:var(--container-narrow)}
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-6)}
.stack-2>*+*{margin-top:var(--space-2)}
.stack-4>*+*{margin-top:var(--space-4)}
.stack-6>*+*{margin-top:var(--space-6)}

/* ---------- Typography roles ---------- */
.eyebrow{font:var(--type-eyebrow);letter-spacing:var(--ls-eyebrow);text-transform:uppercase;color:var(--brand-strong)}
.script{font-family:var(--font-script);font-weight:var(--fw-regular);line-height:1.15}
.lede{font-size:var(--fs-body-lg);line-height:var(--lh-relaxed);color:var(--text-body)}
.muted{color:var(--text-muted)}
/* --text-muted (#7a6c65) is 4.42:1 on --surface-sunken (#f5efe8) — under
   the 4.5:1 body-text needs. It still passes on the white/cream-50
   backgrounds .muted is used against elsewhere, so this stays scoped to
   sunken sections rather than changing .muted globally. (0,2,0) beats
   .muted's (0,1,0), so this wins on specificity, not source order.
   --ink-600 (#635651) is 6.17:1 on sunken. Deliberate deviation from the
   design system's own color pairing, same as the price-row__note fix. */
.section--sunken .muted{color:var(--ink-600)}

/* ---------- Accessibility ---------- */
.skip-link{position:absolute;left:var(--space-4);top:calc(var(--space-4) * -4);z-index:100;
  background:var(--surface-card);color:var(--text-link);padding:var(--space-3) var(--space-4);
  border-radius:var(--radius-md);box-shadow:var(--shadow-md);transition:top var(--dur-fast) var(--ease-standard)}
.skip-link:focus{top:var(--space-4)}
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ---------- Button — components/core/Button.jsx ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
  height:var(--control-h-md);padding:0 var(--space-6);font-family:var(--font-display);
  font-size:var(--fs-body-md);font-weight:var(--fw-semibold);letter-spacing:var(--ls-sign);
  border:var(--border-hairline) solid transparent;border-radius:var(--radius-pill);
  cursor:pointer;text-decoration:none;white-space:nowrap;transition:var(--transition-control)}
.btn--sm{height:var(--control-h-sm);padding:0 var(--space-4);font-size:var(--fs-body-sm)}
.btn--lg{height:var(--control-h-lg);padding:0 var(--space-8);font-size:var(--fs-body-lg)}
.btn--primary{background:var(--brand);color:var(--text-on-brand);box-shadow:var(--shadow-brand)}
.btn--primary:hover{background:var(--pink-700);color:var(--text-on-brand)}
.btn--secondary{background:var(--surface-card);color:var(--brand-strong);border-color:var(--border-brand)}
.btn--secondary:hover{background:var(--brand-tint)}
.btn--ghost{background:transparent;color:var(--brand-strong)}
.btn--ghost:hover{background:var(--brand-tint)}
.btn--dark{background:var(--surface-dark);color:var(--text-on-dark)}
.btn--dark:hover{background:var(--ink-900);color:var(--text-on-dark)}
.btn:active{transform:scale(.97)}
.btn:focus-visible{box-shadow:var(--shadow-focus)}

/* ---------- Card — components/core/Card.jsx ---------- */
.card{border-radius:var(--radius-lg);padding:var(--space-6);background:var(--surface-card);
  border:var(--border-hairline) solid var(--border-subtle);box-shadow:var(--shadow-md);
  transition:box-shadow var(--dur-base) var(--ease-standard),transform var(--dur-base) var(--ease-standard)}
.card--flat{box-shadow:none;border-color:var(--border-default)}
.card--tint{background:var(--surface-brand-soft);border-color:var(--pink-200);box-shadow:none}
.card--warm{background:var(--surface-warm);border-color:var(--amber-200);box-shadow:none}
.card--dark{background:var(--surface-dark);color:var(--text-on-dark);border-color:rgba(255,255,255,.08);box-shadow:none}
.card--interactive{cursor:pointer}
.card--interactive:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px)}

/* ---------- Badge — components/core/Badge.jsx ---------- */
/* white-space:nowrap is load-bearing — the pill is a fixed 24px tall, so a
   label that wraps to two lines spills out of its own background. */
.badge{display:inline-flex;align-items:center;gap:var(--space-1);height:24px;flex:0 0 auto;
  padding:0 var(--space-3);border-radius:var(--radius-pill);font-family:var(--font-display);
  font-size:var(--fs-caption);font-weight:var(--fw-bold);letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase;white-space:nowrap;background:var(--brand);color:var(--text-on-brand)}
.badge--soft{background:var(--brand-soft);color:var(--pink-800)}
.badge--warm{background:var(--amber-200);color:var(--amber-700)}
.badge--neutral{background:var(--surface-sunken);color:var(--text-muted)}

/* ---------- Tag — components/core/Tag.jsx ---------- */
.tag{display:inline-flex;align-items:center;gap:var(--space-2);height:32px;padding:0 var(--space-4);
  border-radius:var(--radius-pill);font-family:var(--font-body);font-size:var(--fs-body-sm);
  font-weight:var(--fw-medium);background:var(--surface-card);color:var(--text-body);
  border:var(--border-hairline) solid var(--border-default);cursor:pointer;
  transition:var(--transition-control)}
.tag:hover{background:var(--brand-tint)}
.tag--selected,.tag[aria-pressed="true"]{background:var(--brand);color:var(--text-on-brand);border-color:var(--brand)}
.tag--selected:hover,.tag[aria-pressed="true"]:hover{background:var(--brand)}
.tag:focus-visible{box-shadow:var(--shadow-focus)}

/* ---------- PriceRow — components/navigation/PriceRow.jsx ----------
   The 1px dashed rule is the one texture the system allows (readme,
   "Borders") — borrowed from printed menus. */
.price-row{display:flex;align-items:baseline;gap:var(--space-4);padding:var(--space-4) 0;
  border-bottom:var(--border-hairline) dashed var(--border-subtle)}
.price-row__main{flex:1;min-width:0}
/* Wrap so a nowrap badge drops onto its own line on narrow screens
   instead of crushing the service name beside it. */
.price-row__head{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap}
.price-row__name{font-family:var(--font-display);font-weight:var(--fw-semibold);
  font-size:var(--fs-body-lg);color:var(--text-heading)}
.price-row__desc{font:var(--type-caption);font-size:var(--fs-body-sm);color:var(--text-muted);margin-top:2px}
.price-row__side{text-align:right}
.price-row__price{font-family:var(--font-display);font-weight:var(--fw-bold);
  font-size:var(--fs-body-lg);color:var(--brand-strong)}
/* --text-faint is 3.14:1 on white, below the 4.5:1 needed for 12px text;
   PriceRow.jsx specifies --text-faint, so this is a deliberate deviation
   from the design system to fix an accessibility defect at the
   design-system level — --text-muted is 5.05:1 and passes. */
.price-row__note{font:var(--type-caption);color:var(--text-muted)}

/* ---------- Tabs — components/navigation/Tabs.jsx ---------- */
.tabs{display:flex;gap:var(--space-6);border-bottom:var(--border-hairline) solid var(--border-subtle)}
.tab{border:none;background:transparent;cursor:pointer;padding:0 0 var(--space-3);
  font-family:var(--font-display);font-size:var(--fs-body-md);font-weight:var(--fw-medium);
  color:var(--text-muted);border-bottom:var(--border-thick) solid transparent;
  margin-bottom:-1px;transition:var(--transition-control)}
.tab--active,.tab[aria-selected="true"]{font-weight:var(--fw-bold);color:var(--brand-strong);border-bottom-color:var(--brand)}
.tab:focus-visible{box-shadow:var(--shadow-focus)}

/* ---------- Icons ---------- */
.icon{width:18px;height:18px;flex:0 0 auto;vertical-align:-3px}
.icon--lg{width:22px;height:22px}
.icon-circle{width:44px;height:44px;border-radius:var(--radius-pill);background:var(--brand-soft);
  color:var(--brand-strong);display:inline-flex;align-items:center;justify-content:center}

/* ---------- Header ----------
   Translucent + blur is permitted here and on modal scrims only. */
.site-header{position:sticky;top:0;z-index:30;background:rgba(251,247,243,.86);
  backdrop-filter:var(--blur-panel);-webkit-backdrop-filter:var(--blur-panel);
  border-bottom:var(--border-hairline) solid var(--border-subtle)}
.site-header .wrap{height:var(--header-h);display:flex;align-items:center;gap:var(--space-8)}
.logo{display:flex;align-items:center;gap:var(--space-3);text-decoration:none;flex:0 0 auto}
.logo__badge{width:44px;height:44px;flex:0 0 44px;border-radius:var(--radius-pill);
  background:var(--brand);box-shadow:inset 0 0 0 3px var(--logo-cream);
  display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--font-script);font-size:18px;line-height:1;color:var(--white);padding-top:3px}
.logo__name{font-family:var(--font-script);font-size:32px;line-height:1;color:var(--brand);padding-bottom:6px}
.site-nav{display:flex;gap:var(--space-6);margin-left:auto;align-items:center}
.site-nav a{font-family:var(--font-display);text-decoration:none}
.site-nav a:not(.btn){font-size:15px;color:var(--text-body)}
.site-nav a:not(.btn):hover{color:var(--brand-strong)}
/* The persistent booking CTA. Lives outside .site-nav so it survives the
   mobile nav collapse — see the comment on it in index.html. flex:0 0 auto
   stops the label wrapping when the row gets tight on a small phone. */
.header-book{flex:0 0 auto}
/* .nav-toggle is authored BEFORE .site-nav so that tabbing off it lands on
   the menu links it just opened — that source order has to stay. order:1
   moves it visually to the end of the row anyway, so the header reads
   logo | nav | Book | ☰ instead of the button overflowing past the edge. */
.nav-toggle{order:1}
.nav-toggle{display:none;margin-left:auto;background:transparent;border:none;cursor:pointer;
  color:var(--text-heading);padding:var(--space-2);min-width:var(--tap-min);min-height:var(--tap-min)}
.nav-toggle:focus-visible{box-shadow:var(--shadow-focus);border-radius:var(--radius-md)}

/* ---------- Footer ---------- */
.site-footer{background:var(--surface-dark);color:var(--text-on-dark);padding:var(--space-16) 0}
.site-footer a{color:var(--text-on-dark)}
.site-footer h3{color:var(--text-on-dark)}
/* --text-muted is 3.08:1 on --surface-dark, under the 4.5:1 body-text
   bar — it's a tone meant for light backgrounds. Scoped to the footer
   only; .muted stays --text-muted everywhere else it already passes.
   --cream-300 (10.47:1 here) is reused rather than introducing a fourth
   grey — it's already this file's "muted on dark" tone (.footer-bottom,
   below). Deliberate deviation from the design system's own pairing,
   same as .price-row__note and the sunken .muted/.rating fixes. */
.site-footer .muted{color:var(--cream-300)}
.footer-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-8)}
/* min-height keeps each link a 44px tap target on mobile, same bar the
   header CTA holds. */
.social-row{display:flex;flex-wrap:wrap;gap:var(--space-6);margin-top:var(--space-10)}
.social-row a{display:inline-flex;align-items:center;gap:var(--space-2);min-height:44px}
.footer-bottom{margin-top:var(--space-10);padding-top:var(--space-6);
  border-top:var(--border-hairline) solid rgba(255,255,255,.12);
  font-size:var(--fs-body-sm);color:var(--cream-300)}

/* ---------- Hero ----------
   Source photo is 680x454, so the height is capped to limit upscaling.
   The left-to-right scrim is the design system's mandated treatment. */
.hero{position:relative;min-height:560px;display:flex;align-items:center;overflow:hidden;
  background:var(--surface-dark)}
.hero__img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center}
.hero__scrim{position:absolute;inset:0;
  background:linear-gradient(to right,rgba(36,26,23,.88) 0%,rgba(36,26,23,.72) 42%,rgba(36,26,23,.15) 100%)}
/* Two things this rule must NOT do, both because it shares an element
   with .wrap:
   1. Use the `padding` shorthand — .wrap's `padding:0 var(--gutter-page)`
      supplies the page gutter, and the shorthand would reset it to 0,
      leaving hero text flush against the screen edge on mobile.
   2. Narrow `max-width` — .wrap's `margin:0 auto` would then centre the
      narrower block, dragging the text into the middle of the photo and
      out from under the left-to-right scrim that exists to protect it.
   The 620px reading measure goes on the children instead, so the text
   stays left-aligned with every other section on the page. */
/* width:100% because .hero is a flex container — without it this flex item
   shrink-wraps to its content and .wrap's `margin:0 auto` centres that
   narrow box, not the 1160 container. */
.hero__body{position:relative;z-index:1;width:100%;padding-block:var(--space-20)}
.hero__body > *{max-width:620px}
.hero__name{font-family:var(--font-script);font-size:var(--fs-display-xl);line-height:1.05;
  color:var(--white);margin:0 0 var(--space-3)}
.hero h1{color:var(--white);font-size:var(--fs-display-md);margin:0 0 var(--space-4)}
.hero .lede{color:var(--cream-200)}
.hero .eyebrow{color:var(--amber-400)}
.cta-row{display:flex;gap:var(--space-3);flex-wrap:wrap;margin-top:var(--space-8)}
.hero__book-note{color:var(--cream-200);font-size:var(--fs-body-sm);margin:var(--space-3) 0 0}
.hero__meta{display:flex;gap:var(--space-6);flex-wrap:wrap;margin-top:var(--space-8);
  color:var(--cream-200);font-size:var(--fs-body-sm)}
.hero__meta span{display:inline-flex;align-items:center;gap:var(--space-2)}

/* ---------- Services section — layout wrappers only.
   .price-row and .tabs/.tab are Task 2 components; not redefined here. */
.section-head{display:flex;align-items:flex-end;justify-content:space-between;
  gap:var(--space-6);margin-bottom:var(--space-10)}
.section-head h2{margin-bottom:var(--space-2)}
/* A second heading inside one section (the interiors group, sitting under the
   work gallery) gets no top gap from the section padding — give it one. */
.gallery + .section-head{margin-top:var(--space-12)}
.tabpanel[hidden]{display:none}
.price-list{max-width:var(--container-narrow);margin-top:var(--space-8)}
/* Category photo at the top of each panel. It owns the gap below itself:
   the Extras panel opens on a bare .price-row rather than a .price-group,
   so leaning on the heading's own margin-top would leave that one panel
   with the photo jammed against the first row.
   ponytail: the sources are 4:3 and 16:9, cropped to the banner by
   object-fit. object-position is the calibration knob, set per photo
   below because each one's subject sits at a different height. */
.panel-photo{width:100%;aspect-ratio:16/6;object-fit:cover;border-radius:var(--radius-lg);
  margin-bottom:var(--space-8)}
.panel-photo + .price-group{margin-top:0}
#panel-hands .panel-photo{object-position:center 70%}  /* hold the nails, drop the gloves */
#panel-waxing .panel-photo{object-position:center 90%} /* keep the whole wax pot */
/* The flat lay's arc of bottles is taller than any 16:6 window, so it gets
   cut whatever we do. High is the least-bad: the arc then runs off the
   bottom edge like a continuing pattern instead of being sliced mid-bottle. */
#panel-extras .panel-photo{object-position:center 25%}
.price-list .price-row:last-child{border-bottom:none}
/* Group headings inside a panel (Acrylic, Pink & white, …). The banner
   groups its menu this way; without them a 15-row panel reads as a wall.
   The preceding row keeps its rule as the group divider, so the heading
   needs top space only. */
.price-group{font:var(--type-eyebrow);letter-spacing:var(--ls-eyebrow);text-transform:uppercase;
  color:var(--brand-strong);margin-top:var(--space-8)}
.price-list > .price-group:first-child{margin-top:0}
.price-group__note{font-size:var(--fs-body-sm);margin-top:var(--space-2)}
.price-note{margin-top:var(--space-6);font-size:var(--fs-body-sm);color:var(--text-muted)}

/* ---------- Feature split ---------- */
.split{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-16);align-items:center}
.split img{border-radius:var(--radius-lg)}
.feature-list{list-style:none;padding:0;margin:var(--space-6) 0 0}
.feature-list li{display:flex;gap:var(--space-4);align-items:flex-start;margin-bottom:var(--space-5)}

/* ---------- Gallery ----------
   3-up with a 2:1 feature tile (readme, "Layout"). */
.gallery{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-4)}
.gallery__item{padding:0;border:none;background:none;cursor:pointer;border-radius:var(--radius-lg);
  overflow:hidden;display:block;transition:box-shadow var(--dur-base) var(--ease-standard),transform var(--dur-base) var(--ease-standard)}
.gallery__item:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px)}
.gallery__item:focus-visible{box-shadow:var(--shadow-focus)}
.gallery__item img{width:100%;height:100%;object-fit:cover;aspect-ratio:3/2}
.gallery__item--feature{grid-column:span 2;grid-row:span 2}
/* Order matters: same (0,1,1) specificity as .gallery__item img above,
   so this rule wins the tie only by being declared second. Do not
   reorder. */
.gallery__item--feature img{aspect-ratio:auto;height:100%}

/* ---------- Lightbox ----------
   The second and last permitted use of blur (readme). */
.lightbox{position:fixed;inset:0;z-index:50;width:100%;height:100%;max-width:100%;max-height:100%;
  box-sizing:border-box;padding:var(--space-8);background:var(--overlay-scrim);
  backdrop-filter:var(--blur-panel);-webkit-backdrop-filter:var(--blur-panel);border:none}
/* Gate on [open] so the UA's dialog:not([open]){display:none} still wins
   when closed. Setting display:flex unconditionally would force the
   dialog visible and require a second hidden-attribute workaround. */
.lightbox[open]{display:flex;align-items:center;justify-content:center}
.lightbox img{max-width:min(1000px,92vw);max-height:86vh;width:auto;border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lg)}
.lightbox__close{position:absolute;top:var(--space-6);right:var(--space-6);
  width:44px;height:44px;border-radius:var(--radius-pill);border:none;cursor:pointer;
  background:var(--surface-card);color:var(--text-heading);
  display:inline-flex;align-items:center;justify-content:center}
.lightbox__close:focus-visible{box-shadow:var(--shadow-focus)}

/* Instagram feed below the curated grid. No height or min-height: a dead
   feed must collapse rather than leave a gap (see the note in index.html).
   Behold renders into a *closed* shadow root, so nothing inside the widget
   is selectable from here — not even :has(). The heading and the spacing
   are therefore gated on data-loaded, which site.js sets from the widget's
   measured height. Hidden by default so that an empty feed leaves no
   orphaned "Latest from Instagram" label and no gap; a browser with JS off
   renders no widget and no heading either, which is the same right answer. */
.ig-feed{text-align:center}
.ig-feed .eyebrow{display:none;margin-bottom:var(--space-6)}
.ig-feed[data-loaded="true"]{margin-top:var(--space-12)}
.ig-feed[data-loaded="true"] .eyebrow{display:block}

/* ---------- Reviews ----------
   Only ever rendered inside the sunken reviews section, so both colors
   below are fixed directly rather than scoped: --amber-600 is 2.35:1 on
   --surface-sunken (fails even the 3.0 large-text bar); --amber-700 is
   3.58:1 and passes. --text-muted is 4.42:1 here, under 4.5:1; --ink-600
   is 6.17:1. Both are deliberate deviations from the design system's own
   color pairings for this surface, same as the price-row__note fix. */
.rating{display:inline-flex;align-items:center;gap:var(--space-2);color:var(--amber-700);
  font-family:var(--font-display);font-weight:var(--fw-bold);font-size:var(--fs-heading-md)}
.rating__count{color:var(--ink-600);font-weight:var(--fw-medium);font-size:var(--fs-body-md)}
.quote{font-size:var(--fs-body-lg);line-height:var(--lh-relaxed);color:var(--text-body);margin:0}
.quote__by{margin-top:var(--space-4);font-size:var(--fs-body-sm);color:var(--text-muted)}

/* ---------- FAQ — native <details>/<summary>, no JS ---------- */
.faq{max-width:var(--container-narrow)}
.faq details{border-bottom:var(--border-hairline) solid var(--border-subtle);padding:var(--space-5) 0}
.faq summary{cursor:pointer;font-family:var(--font-display);font-weight:var(--fw-semibold);
  font-size:var(--fs-heading-sm);color:var(--text-heading);list-style:none;
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-4)}
.faq summary::-webkit-details-marker{display:none}
.faq summary:focus-visible{box-shadow:var(--shadow-focus);border-radius:var(--radius-sm)}
/* CSS-drawn chevron: two borders rotated to a corner, no icon/emoji.
   Rotates on open via transform, driven by tokenized duration/easing. */
.faq summary::after{content:"";flex:0 0 auto;width:8px;height:8px;
  border-right:var(--border-hairline) solid var(--text-heading);
  border-bottom:var(--border-hairline) solid var(--text-heading);
  transform:rotate(-45deg);transition:transform var(--dur-fast) var(--ease-standard)}
.faq details[open] summary::after{transform:rotate(45deg)}
.faq p{margin:var(--space-3) 0 0;color:var(--text-body)}

/* ---------- Map embed ----------
   Google's iframe brings its own visual language, so it gets the same
   card treatment as everything else on the page — 16px radius, hairline
   border, warm shadow — to keep it from reading as pasted in. */
.map-embed{margin-top:var(--space-10);border-radius:var(--radius-lg);overflow:hidden;
  border:var(--border-hairline) solid var(--border-subtle);box-shadow:var(--shadow-md);
  aspect-ratio:16/6;background:var(--surface-sunken)}
.map-embed iframe{width:100%;height:100%;border:0;display:block}
.map-note{margin-top:var(--space-3);font-size:var(--fs-body-sm)}

/* ---------- Visit row: storefront photo beside the map ----------
   The photo is portrait 3:4 and the map only reads well with width, so
   they share a row with the photo on a fixed 300px track. The photo keeps
   its natural height (img{height:auto} above), and that height sets the
   row; the map then stretches to match, which is why .map-embed's own
   16:6 aspect-ratio is dropped here — leaving it on would fight the
   stretch and collapse the iframe. */
.visit-row{display:grid;grid-template-columns:300px 1fr;gap:var(--space-6);
  align-items:stretch;margin-top:var(--space-10)}
/* box-sizing so the hairline border sits inside width:100% — without it the
   photo overhangs the map by 1px on each side once they stack. */
.visit-row .storefront{display:block;box-sizing:border-box;width:100%;height:auto;
  border-radius:var(--radius-lg);
  border:var(--border-hairline) solid var(--border-subtle);box-shadow:var(--shadow-md)}
.visit-row .map-embed{margin-top:0;aspect-ratio:auto}

/* ---------- Responsive — spec D9 ----------
   The design system documents desktop only (1160px container, 48px
   gutters, 3-up grids) and defines no breakpoints. Everything below is
   an extension. Type scale, radii, and motion stay on the tokens. */
@media (max-width:900px){
  .wrap{padding:0 var(--gutter-page)}
  .section{padding:var(--space-16) 0}
  .grid-3,.footer-grid{grid-template-columns:1fr}
  .split{grid-template-columns:1fr;gap:var(--space-10)}
  .gallery{grid-template-columns:repeat(2,1fr)}
  .gallery__item--feature{grid-column:span 2;grid-row:auto}
  .gallery__item--feature img{aspect-ratio:3/2}

  .hero{min-height:auto}
  .hero__body{padding-block:var(--space-16)}
  .hero__name{font-size:var(--fs-display-lg)}
  .hero h1{font-size:var(--fs-heading-lg)}
  .hero__scrim{background:linear-gradient(to right,rgba(36,26,23,.9) 0%,rgba(36,26,23,.78) 100%)}

  /* Header row is now logo | Book | hamburger, since .site-nav drops out of
     flow here. The auto margin moves off .nav-toggle and onto .header-book,
     otherwise the two auto margins split the free space between them and
     neither ends up flush right. The tighter gap is what buys the button
     room to sit next to the logo at 360px. */
  .site-header .wrap{gap:var(--space-2)}
  .nav-toggle{display:inline-flex;align-items:center;justify-content:center;margin-left:0}
  .header-book{margin-left:auto;min-height:var(--tap-min)}
  .site-nav{display:none;position:absolute;top:var(--header-h);left:0;right:0;
    flex-direction:column;align-items:stretch;gap:var(--space-2);
    padding:var(--space-4) var(--gutter-page) var(--space-6);
    background:var(--surface-page);
    border-bottom:var(--border-hairline) solid var(--border-subtle);
    box-shadow:var(--shadow-md)}
  .site-nav[data-open="true"]{display:flex}
  .site-nav a:not(.btn){padding:var(--space-3) 0;min-height:var(--tap-min);display:flex;align-items:center}
  .site-nav .btn{margin-top:var(--space-2);min-height:var(--tap-min)}
  .section-head{flex-direction:column;align-items:flex-start}
  /* 16:6 is unreadably short on a phone — go closer to square. */
  .map-embed{aspect-ratio:4/3;margin-top:var(--space-8)}
  /* Stack the visit row. The .visit-row .map-embed rule above outranks the
     bare .map-embed reset on the line above, so 4/3 is restated here. */
  .visit-row{grid-template-columns:1fr;gap:var(--space-8)}
  .visit-row .storefront{max-width:360px;margin:0 auto}
  .visit-row .map-embed{aspect-ratio:4/3}
}

@media (max-width:560px){
  /* Three things now share the header row on a phone: logo, booking CTA,
     hamburger. "Book online" is trimmed to "Book" (the calendar icon
     carries the rest) and the script logotype gives up a few points —
     together that is the ~25px needed to fit 375px without the row
     overflowing. The badge and the name both stay; the CTA is not worth
     dropping the brand for. */
  .hide-sm{display:none}
  .logo__name{font-size:26px}
  .logo__badge{width:38px;height:38px;flex:0 0 38px;font-size:15px}
  /* Header-only gutter, narrower than the page's 24px. Scoped here rather
     than shrinking --gutter-page, which would re-inset every section on
     the page to buy room for one row. */
  .site-header .wrap{padding-inline:var(--space-4)}
  .gallery{grid-template-columns:1fr}
  .gallery__item--feature{grid-column:span 1}
  .cta-row .btn{width:100%}
  /* Same reason as .map-embed above: a 16:6 band is a sliver on a phone. */
  .panel-photo{aspect-ratio:16/9}
}

/* Below 360px (iPhone SE 1st gen and similar) the logo, the booking CTA and
   the hamburger cannot all fit, and the row starts scrolling sideways. The
   script wordmark yields — the LN badge still carries the brand, and every
   width at or above 360 keeps both. Deliberately 359 so the very common
   360px Android class is unaffected. */
@media (max-width:359px){
  .logo__name{display:none}
}
