/* ============================================================================
   Grind Barbershop — public site
   The Grind Barbershop Design System (claude.ai/design project "Grind
   Barbershop Design System"), applied to the public theme. Dark only:
   near-black ground, bone text, one copper accent, olive for small labels.
   Bebas Neue for every heading, Karla for everything else. Square corners,
   no shadows, almost no motion.

   The tokens and the element reset come first, from site/tokens.css (the
   design's own files, vendored verbatim). This file is the component layer:
   each block below is one of the design's components, as a class, and reads
   its values from those tokens — a raw number here is one the design itself
   leaves untokenised.

   Loaded by the `site` entrypoint, which only the public theme
   (templates/default) uses — admin, auth and error pages keep app.css /
   admin.css and never see these rules.
   ============================================================================ */

/* --- Page frame ----------------------------------------------------------- */

/* Keep the scrollbar's gutter when an overlay locks scrolling, so the page
   doesn't shift sideways under it — and the menu's close button lands exactly
   where the menu button was. */
html {
  scrollbar-gutter: stable;
}

/* The page must not scroll behind an open lightbox. */
html:has(dialog[open]) {
  overflow: hidden;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  overflow-x: hidden;
}

main {
  flex: 1 0 auto;
}

.skip-link {
  position: absolute;
  top: var(--space-3);
  left: var(--space-3);
  z-index: 100;
  padding: var(--space-4) var(--space-6);
  background: var(--accent);
  color: var(--on-accent);
  font-size: var(--text-control);
  font-weight: var(--weight-bold);
  transform: translateY(-200%);
}

.skip-link:focus {
  color: var(--on-accent);
  transform: none;
}

/* Photo that fills its positioned parent. */
.cover {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* --- Eyebrow · Label · TextLink ------------------------------------------- */

.eyebrow {
  display: block;
  color: var(--accent);
  font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
}

.eyebrow--rule {
  display: flex;
  align-items: center;
  gap: 12px;
}

.eyebrow--rule::before {
  content: '';
  flex: none;
  width: var(--rule-eyebrow-width);
  height: var(--hairline);
  background: var(--accent);
}

/* Olive, and only ever at this one size. */
.label {
  color: var(--olive);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
}

.text-link {
  display: inline-block;
  padding-bottom: var(--space-1);
  border-bottom: var(--hairline) solid var(--accent);
  color: var(--accent);
  font-size: var(--text-control);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-control);
  text-transform: uppercase;
}

.text-link:hover {
  border-color: var(--accent-hover);
  color: var(--accent-hover);
}

/* --- Button --------------------------------------------------------------- */

/* Hover brightens; there is no press state and no transition. */
.btn {
  display: inline-block;
  padding: var(--pad-btn);
  border: 0;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: var(--text-control);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-brand);
  line-height: 1.15;
  text-align: center;
  text-transform: uppercase;
}

.btn--primary {
  background: var(--accent);
  color: var(--on-accent);
}

.btn--primary:hover {
  background: var(--accent-hover);
  color: var(--on-accent);
}

.btn--ghost {
  border: var(--border-ghost);
  background: none;
  color: var(--text);
}

.btn--ghost:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Sizes are named for where they appear: compact in the header, small in the
   footer, large in the closing CTA band. */
.btn--compact {
  padding: var(--pad-btn-compact);
  letter-spacing: var(--tracking-control);
}

.btn--small {
  padding: var(--pad-btn-small);
  font-size: var(--text-eyebrow);
}

.btn--large {
  padding: var(--pad-btn-large);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-actions);
  padding-top: var(--space-2);
}

/* --- SiteHeader · Brand --------------------------------------------------- */

.site-header {
  position: relative;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6);
  padding: var(--space-7) var(--gutter-header);
  border-bottom: var(--border-hairline);
  background: var(--bg);
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  color: var(--text);
}

.brand:hover {
  color: var(--text);
}

.brand__logo {
  display: block;
  flex: none;
  width: auto;
  height: 46px;
}

/* An uploaded logo is whatever shape the operator's file is. It stands in for
   the shop name as well as the mark, so it gets more height than the mark
   alone; the width follows, capped so a wide wordmark can't crowd the nav. */
.brand__logo--custom {
  width: auto;
  max-width: min(180px, 34vw);
  height: 56px;
  object-fit: contain;
}

/* Truncates rather than run under the buttons if a width slips between the
   header's breakpoints. */
.brand__name {
  min-width: 0;
  overflow: hidden;
  font-family: var(--font-display);
  font-size: var(--display-brand);
  letter-spacing: var(--tracking-brand);
  line-height: 1;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
}

.site-nav__link {
  padding: var(--pad-nav-link);
  border-bottom: var(--underline-nav) solid transparent;
  color: var(--text-muted);
  font-size: var(--text-control);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-control);
  text-transform: uppercase;
}

.site-nav__link:hover,
.site-nav__link[aria-current="page"] {
  color: var(--text);
}

.site-nav__link[aria-current="page"] {
  border-bottom-color: var(--accent);
}

.site-header__phone {
  color: var(--text-muted);
  font-size: var(--text-control);
  letter-spacing: var(--tracking-phone);
  text-transform: uppercase;
}

/* --- MenuButton · SiteMenu ------------------------------------------------ */

.menu-button {
  position: relative;
  display: none;
  flex: none;
  width: var(--touch-target);
  height: var(--touch-target);
  padding: 0;
  border: var(--border-ghost);
  border-radius: var(--radius);
  background: none;
  color: var(--text);
  cursor: pointer;
}

.menu-button:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Three 20×2px bars; the close variant crosses the outer two. */
.menu-button__icon,
.menu-button__icon::before,
.menu-button__icon::after {
  position: absolute;
  width: 20px;
  height: 2px;
  background: currentColor;
}

.menu-button__icon {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.menu-button__icon::before,
.menu-button__icon::after {
  content: '';
  left: 0;
}

.menu-button__icon::before {
  top: -6px;
}

.menu-button__icon::after {
  top: 6px;
}

.menu-button--close .menu-button__icon {
  background: transparent;
}

.menu-button--close .menu-button__icon::before,
.menu-button--close .menu-button__icon::after {
  top: 0;
}

.menu-button--close .menu-button__icon::before {
  transform: rotate(45deg);
}

.menu-button--close .menu-button__icon::after {
  transform: rotate(-45deg);
}

/* A popover, so it sits in the top layer; this undoes the UA's small centred
   box and fills the viewport instead. Display is only set while open — the
   UA hides a closed popover, and an author `display` would override that. */
.site-menu {
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  border: 0;
  background: var(--bg);
  color: var(--text);
}

.site-menu:popover-open {
  display: flex;
  flex-direction: column;
}

/* The menu fades in. (The other animation is the split hero's photos.) */
@media (prefers-reduced-motion: no-preference) {
  .site-menu {
    opacity: 0;
    transition:
      var(--transition-overlay),
      overlay var(--dur-overlay) allow-discrete,
      display var(--dur-overlay) allow-discrete;
  }

  .site-menu:popover-open {
    opacity: 1;
  }

  @starting-style {
    .site-menu:popover-open {
      opacity: 0;
    }
  }
}

/* Same padding as the collapsed header, so the close button lands exactly
   where the menu button was. */
.site-menu__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 12px var(--gutter-header);
  border-bottom: var(--border-hairline);
}

.site-menu__nav {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-12) var(--gutter-header);
}

.site-menu__link {
  color: var(--text);
  font-family: var(--font-display);
  font-size: var(--display-menu-link);
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-snug);
  text-transform: uppercase;
}

.site-menu__link:hover {
  color: var(--accent-hover);
}

.site-menu__link[aria-current="page"] {
  color: var(--accent);
}

/* Pinned to the bottom of the overlay, in thumb reach. */
.site-menu__footer {
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
  margin-top: auto;
  padding: var(--space-8) var(--gutter-header) var(--space-11);
  border-top: var(--border-hairline);
}

.site-menu__phone {
  color: var(--text-muted);
  font-size: var(--text-body-base);
  letter-spacing: var(--tracking-phone);
}

/* The design collapses the header at --breakpoint-header (860px) for three
   nav links. The nav now lists every page the admin puts in it — six on a
   fresh install — and that row needs ~1060px, so it collapses here instead
   (media queries can't read the custom property). */
@media (max-width: 1099px) {
  html:has(.site-menu:popover-open) {
    overflow: hidden;
  }

  .site-header {
    flex-wrap: nowrap;
    gap: var(--space-3);
    padding-block: 12px;
  }

  .site-nav,
  .site-header__phone,
  .site-header__book-more {
    display: none;
  }

  .brand {
    gap: var(--space-4);
    margin-right: auto;
  }

  .brand__logo {
    height: 40px;
  }

  /* As tall as the 44px buttons beside it, so the bar doesn't grow. */
  .brand__logo--custom {
    width: auto;
    height: 44px;
  }

  .brand__name {
    font-size: 18px;
    letter-spacing: var(--tracking-control);
  }

  /* Same 44px as the menu button beside it. */
  .site-header__book {
    display: inline-flex;
    align-items: center;
    height: var(--touch-target);
    padding: 0 var(--space-6);
  }

  .menu-button {
    display: block;
  }
}

/* Past the breakpoint the row fits again; a menu left open while the window
   widened must not linger over it. */
@media (min-width: 1100px) {
  .site-menu:popover-open {
    display: none;
  }
}

/* Brand, Book and the menu button need ~357px; narrower than a 360px phone,
   Book gives way and stays reachable in the menu. */
@media (max-width: 359px) {
  .site-header__book {
    display: none;
  }
}

/* --- SplitHero ------------------------------------------------------------ */

.hero {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--col-min), 1fr));
  align-items: stretch;
  border-bottom: var(--border-hairline);
}

.hero__copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--gap-hero-copy);
  min-width: 0;
  padding: var(--pad-hero) var(--gutter);
}

.hero__title {
  font-size: var(--display-hero);
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-display);
  text-wrap: balance;
}

.hero__lede {
  max-width: var(--measure-lede);
  color: var(--text-soft);
  font-size: var(--text-lede-hero);
  line-height: var(--leading-body-loose);
}

.hero__media {
  position: relative;
  min-height: clamp(380px, 52vw, 680px);
  border-left: var(--border-hairline);
}

/* Four photos take turns, six seconds each: every 1.5s fade brings the next
   one in over the last, and after the fourth the first comes back. Without
   motion only the first is shown, and the rest aren't loaded. */
.hero__media .cover + .cover {
  display: none;
}

@media (prefers-reduced-motion: no-preference) {
  .hero__media .cover + .cover {
    display: block;
    opacity: 0;
    animation: hero-photo 24s 4.5s infinite;
  }

  .hero__media .cover:nth-child(3) {
    animation-delay: 10.5s;
  }

  .hero__media .cover:nth-child(4) {
    animation-name: hero-photo-last;
    animation-delay: 16.5s;
  }
}

/* In, and out again once the next photo has covered it... */
@keyframes hero-photo {
  0% { opacity: 0; }
  6.25%, 31.25% { opacity: 1; }
  37.5%, 100% { opacity: 0; }
}

/* ...but nothing covers the last one: it fades out, showing the first again. */
@keyframes hero-photo-last {
  0% { opacity: 0; }
  6.25%, 25% { opacity: 1; }
  31.25%, 100% { opacity: 0; }
}

/* --- FullBleedHero -------------------------------------------------------- */

.hero-full {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: clamp(460px, 62vw, 720px);
  border-bottom: var(--border-hairline);
}

/* The scrim is what keeps the copy legible; never drop it. */
.hero-full__shade {
  position: absolute;
  inset: 0;
  background: var(--shade-hero);
}

.hero-full__copy {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  max-width: 900px;
  padding: var(--pad-hero-full) var(--gutter);
}

.hero-full__title {
  font-size: var(--display-hero-full);
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-display-tight);
}

.hero-full__lede {
  max-width: var(--measure-lede-wide);
  color: var(--text-light);
  font-size: var(--text-lede-hero-full);
  line-height: var(--leading-body-loose);
}

/* --- InfoStrip (address / hours / phone) ---------------------------------- */

/* As many equal columns as it has items. */
.info-strip {
  display: grid;
  grid-auto-columns: 1fr;
  grid-auto-flow: column;
  border-bottom: var(--border-hairline);
}

/* Not directly under a hero: ruled above as well. */
.info-strip--ruled {
  border-top: var(--border-hairline);
}

.info-strip__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
  padding: var(--pad-strip) var(--gutter-strip);
  border-right: var(--border-hairline);
}

.info-strip__item:last-child {
  border-right: 0;
}

.info-strip__value {
  font-size: var(--text-body-lg);
  line-height: var(--leading-normal);
}

/* Below --breakpoint-strip the columns stack, with rules between rather than
   wrapping two-and-one. */
@media (max-width: 689px) {
  .info-strip {
    grid-auto-flow: row;
  }

  .info-strip__item {
    border-right: 0;
  }

  .info-strip__item + .info-strip__item {
    border-top: var(--border-hairline);
  }
}

/* --- Section · PageIntro -------------------------------------------------- */

.section {
  display: flex;
  flex-direction: column;
  gap: var(--gap-section);
  padding: var(--pad-section) var(--gutter);
}

/* A section that opens a page, directly under the header. */
.section--page {
  gap: var(--space-12);
  padding-block: var(--pad-section-page);
}

.section__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-8);
}

.section__title {
  font-size: var(--display-section);
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-display-flat);
}

.page-intro {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  max-width: 640px;
}

.page-intro__title {
  font-size: var(--display-page);
  letter-spacing: 0.02em;
  line-height: var(--leading-display-page);
}

.page-intro__lede {
  color: var(--text-soft);
  font-size: var(--text-lede);
  line-height: var(--leading-body-loose);
}

.page-intro--compact {
  gap: 12px;
  max-width: none;
}

.page-intro--compact .page-intro__title {
  font-size: var(--display-page-compact);
  line-height: 0.96;
}

.page-intro--compact .page-intro__lede {
  max-width: var(--measure-copy);
  font-size: var(--text-body-lg);
  line-height: var(--leading-body);
}

/* --- CtaBand -------------------------------------------------------------- */

.cta-band {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-10);
  padding: var(--pad-band) var(--gutter);
  border-top: var(--border-hairline);
}

.cta-band__copy {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-width: 0;
}

.cta-band__title {
  font-size: var(--display-band);
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-snug);
}

.cta-band__text {
  max-width: var(--measure-copy);
  color: var(--text-soft);
  font-size: var(--text-body-lg);
  line-height: var(--leading-body);
}

/* --- ServiceList ---------------------------------------------------------- */

/* The price list: rows ruled in the accent — the nav's active underline at
   hairline weight — display-face names and prices, an olive duration label.
   The price sits level with the name at every width, right-aligned on a 96px
   column so the decimals line up. */
.service-list {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  border-top: var(--hairline) solid var(--accent);
  list-style: none;
}

/* A row is a grid: the name and the price share the first line, and the tag
   and description run the full width below. The copy wrapper steps aside
   (display: contents) so its children are placed on the row's grid. */
.service-list__item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: var(--space-3) var(--space-4);
  padding: 22px 0;
  border-bottom: var(--hairline) solid var(--accent);
}

.service-list__copy {
  display: contents;
}

.service-list__copy > * {
  grid-column: 1 / -1;
}

.service-list__copy > .service-list__name {
  grid-column: 1;
  grid-row: 1;
}

.service-list__copy > .sale-tag {
  justify-self: start;
}

.service-list__name,
.service-list__price {
  font-family: var(--font-display);
  font-size: var(--display-prose-h3);
  letter-spacing: var(--tracking-title);
}

/* Under a category the name is an h4, which the base reset (h1–h3) misses:
   without this it keeps the browser's ~1.33em margins, which tripled each
   row's height, and a synthesised bold Bebas doesn't have. */
.service-list__name {
  margin: 0;
  font-weight: 400;
  line-height: var(--leading-snug);
}

.service-list__description {
  max-width: var(--measure-copy);
  color: var(--text-soft);
  font-size: var(--text-body-base);
  line-height: var(--leading-body);
}

.service-list__duration {
  flex: none;
}

.service-list__price {
  grid-column: 2;
  grid-row: 1;
  min-width: 96px;
  color: var(--text);
  line-height: var(--leading-snug);
  text-align: right;
}

/* A service on sale: its usual price struck through ahead of the price, a
   size down in a quieter colour, and a copper tag saying it won't last. */
.price-was {
  margin-right: 0.3em;
  color: var(--text-muted);
  font-size: 0.7em;
  text-decoration-thickness: 2px;
}

.sale-tag {
  align-self: flex-start;
  padding: var(--space-1) var(--space-3);
  background: var(--accent);
  color: var(--on-accent);
  font-size: var(--text-caption);
  font-weight: 700;
  letter-spacing: var(--tracking-caption);
  text-transform: uppercase;
}

/* Read by a screen reader, not shown: "Regular price" before a struck one. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* One heading of the price list — "Hair Services", "Add-ons" — over its
   rows. The heading takes the section-title face a size down, so a page of
   four groups reads as one list with signposts, not four sections. */
.price-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.price-group__title {
  font-size: var(--display-band);
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-snug);
}

/* --- Home: section intros, price cards, offer cards, visit ------------- */

.hero__title--long {
  font-size: var(--display-page-compact);
}


/* A section title with a sentence under it, on the left of a section head
   whose right side holds its "see all" link. */
.section__intro {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-width: 0;
}

.section__lede {
  max-width: var(--measure-copy);
  color: var(--text-soft);
  font-size: var(--text-body-lg);
  line-height: var(--leading-body);
}

/* The popular row: a few services as raised cards, name over price. */
.price-cards,
.offer-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--gap-grid);
  margin: 0;
  padding: 0;
  list-style: none;
}

.offer-cards {
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

.price-card,
.offer-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-8);
  border: var(--border-hairline);
  background: var(--surface);
}

.price-card__name,
.offer-card__title {
  font-family: var(--font-display);
  font-size: var(--display-prose-h3);
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-snug);
}

.price-card__price {
  color: var(--accent);
  font-family: var(--font-display);
  font-size: var(--display-band);
  line-height: 1;
}

.price-card__text,
.offer-card__text,
.offer-card__note {
  color: var(--text-soft);
  font-size: var(--text-body-base);
  line-height: var(--leading-body);
}

/* An offer card's deals as a short ruled list: name left, price right, the
   regular price struck through beside it. The button sits at the foot of the
   card whatever the card's length, so two cards' buttons line up. */
.offer-card__deals {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  border-top: var(--border-hairline);
  list-style: none;
}

.offer-card__deals li {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-4);
  padding: 10px 0;
  border-bottom: var(--border-hairline);
}

.offer-card__price {
  color: var(--accent);
  font-weight: var(--weight-bold);
}

.offer-card__price s {
  margin-left: var(--space-3);
  color: var(--text-soft);
  font-weight: normal;
}

.offer-card .actions {
  margin-top: auto;
}

/* Visit: the Contact page's copy-beside-map band, holding the address and
   the whole week's hours rather than a form. */
.visit__address,
.visit__hours {
  color: var(--text-light);
  font-size: var(--text-body-lg);
  line-height: var(--leading-body);
}

.visit__hours {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Forms that sit in an ordinary section rather than their own band (the
   Contact page's booking and message forms) keep a readable width. */
.section > .form {
  max-width: 820px;
}

/* --- Offers -------------------------------------------------------------- */

/* One section per offer: its title (with the last day, when it has one), a
   sentence, price cards, the small print and a button. The cards are raised
   panels in a wrapping row; the offer price is the loudest thing in each,
   the regular price a struck-through footnote under it. */
.offer {
  gap: var(--space-6);
}

.offer__copy {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  min-width: 0;
}

/* With a photo, words and photo side by side in the split hero's columns,
   stacking where two won't fit. The photo is cropped to one shape whatever
   was uploaded, so offers with photos line up down the page. */
.offer--photo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--col-min), 1fr));
  align-items: center;
  gap: var(--space-10);
}

.offer__photo {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.offer__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-4) var(--space-8);
}

.offer__text,
.offer__note {
  line-height: var(--leading-body);
}

/* The description keeps a reading measure. The note is a line of fine print,
   so it runs the section's width rather than break at the measure; it wraps
   only where the screen is narrower than the sentence. */
.offer__text {
  max-width: var(--measure-copy);
  color: var(--text-soft);
  font-size: var(--text-body-lg);
}

.offer__note {
  color: var(--text-soft);
  font-size: var(--text-body-base);
}

.offer-deals {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--gap-grid);
  margin: 0;
  padding: 0;
  list-style: none;
}

.offer-deal {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-8);
  border: var(--border-hairline);
  background: var(--surface);
}

.offer-deal__name {
  font-family: var(--font-display);
  font-size: var(--display-prose-h3);
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-snug);
}

.offer-deal__price {
  color: var(--accent);
  font-family: var(--font-display);
  font-size: var(--display-band);
  line-height: 1;
}

.offer-deal__regular {
  color: var(--text-soft);
  font-size: var(--text-body-base);
}

/* A highlighted offer, after the shop's review flyer: dark words on yellow, a
   red button, a row of round line icons, and the logo where the flyer has
   its photo. The one place the site leaves its dark palette, which is the
   point of highlighting it. */
.offer-banner {
  --banner-ink: #2A2623;
  --banner-red: #D7191F;
  --focus-color: var(--banner-ink);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--col-min), 1fr));
  align-items: center;
  gap: var(--space-10) clamp(24px, 5vw, 64px);
  padding: clamp(28px, 5vw, 64px);
  background: linear-gradient(135deg, #FFE45C 0%, #FFD21F 55%, #FFC20E 100%);
  color: var(--banner-ink);
}

.offer-banner__copy {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  min-width: 0;
}

.offer-banner__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--display-page-compact);
  font-weight: 400;
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-display);
  text-transform: uppercase;
}

.offer-banner__dates {
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-caption);
  text-transform: uppercase;
}

.offer-banner__text {
  max-width: var(--measure-copy);
  font-size: var(--text-lede);
  line-height: var(--leading-body);
}

.offer-banner__note {
  max-width: var(--measure-copy);
  font-size: var(--text-note);
  line-height: var(--leading-normal);
}

.offer-banner__button,
.offer-banner__button:hover {
  background: var(--banner-red);
  color: #FFFFFF;
}

.offer-banner__button:hover {
  background: #B51419;
}

.offer-banner__icons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

.offer-banner__icons li {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border: 2px solid var(--banner-ink);
  border-radius: 50%;
}

.offer-banner__icons svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.offer-banner__logo,
.offer-banner__photo {
  display: block;
  justify-self: center;
  width: 100%;
  max-width: 440px;
  height: auto;
}

.offer-banner__photo {
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* --- FAQ ----------------------------------------------------------------- */

/* Questions as hairline-ruled rows in the display face, each opening to its
   answer. The marker is a plus that turns into a minus — drawn with two
   pseudo-element bars rather than a glyph, so it sits on the same baseline
   in every font the display face falls back to. */
.faq__list {
  display: flex;
  flex-direction: column;
  border-top: var(--border-hairline);
}

.faq__item {
  border-bottom: var(--border-hairline);
}

.faq__question {
  position: relative;
  padding: 22px 48px 22px 0;
  font-family: var(--font-display);
  font-size: var(--display-prose-h3);
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-snug);
  list-style: none;
  cursor: pointer;
}

.faq__question::-webkit-details-marker {
  display: none;
}

.faq__question::before,
.faq__question::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 8px;
  width: 18px;
  height: 2px;
  background: var(--accent);
}

.faq__question::after {
  transform: rotate(90deg);
}

.faq__item[open] > .faq__question::after {
  transform: none;
}

.faq__question:hover {
  color: var(--accent);
}

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

.faq__answer {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
  max-width: var(--measure-copy);
  padding-bottom: 24px;
  color: var(--text-soft);
  font-size: var(--text-body-lg);
  line-height: var(--leading-body);
}

/* --- PhotoRow · Gallery · Lightbox ---------------------------------------- */

.photo-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-grid);
}

.photo-row__item {
  flex: 1 1 var(--tile-min);
  min-width: 0;
}

.photo-row__item img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

/* A grid, not a wrapping row: tiles on a short last row keep the size of the
   rest instead of stretching to fill it. */
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(var(--tile-min), 100%), 1fr));
  gap: var(--gap-grid);
}

.gallery__item {
  position: relative;
  min-width: 0;
  margin: 0;
}

/* Two columns only fit from here; below it a wide photo takes one like the rest. */
@media (min-width: 700px) {
  .gallery__item--wide {
    grid-column: span 2;
  }
}

.gallery__link {
  display: block;
  cursor: zoom-in;
}

.gallery__item img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

.gallery__item--wide img {
  aspect-ratio: 16 / 9;
}

.gallery__item--tall img {
  aspect-ratio: 8 / 9;
}

/* A hard capsule, not a gradient: the caption sits in a corner. */
.gallery__caption {
  position: absolute;
  left: 0;
  bottom: 0;
  padding: var(--pad-caption);
  background: var(--scrim-caption);
  color: var(--text-light);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-caption);
  pointer-events: none;
  text-transform: uppercase;
}

.lightbox {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: var(--gutter-lightbox);
  border: 0;
  background: var(--scrim-lightbox);
  cursor: zoom-out;
}

.lightbox[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}

.lightbox::backdrop {
  background: transparent;
}

.lightbox img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* --- Booking -------------------------------------------------------------- */

.booking {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--col-min), 1fr));
  align-items: start;
}

.booking__main {
  display: flex;
  flex-direction: column;
  gap: var(--space-10);
  min-width: 0;
  padding: var(--pad-booking) var(--gutter-booking);
}

.booking__aside {
  position: relative;
  align-self: stretch;
  min-height: clamp(320px, 42vw, 560px);
  border-left: var(--border-hairline);
}

.booking__shade {
  position: absolute;
  inset: 0;
  background: var(--shade-panel);
}

.booking__caption {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: clamp(24px, 3vw, 40px);
}

.booking__caption p {
  color: var(--text-light);
  font-size: var(--text-body-base);
  line-height: var(--leading-prose);
}

/* --- Field · BookingForm -------------------------------------------------- */

.form {
  display: flex;
  flex-direction: column;
  gap: var(--gap-form);
}

.form__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--col-min-field), 1fr));
  gap: var(--gap-grid);
}

/* 18px form gap + 14px = the design's 32px between the last field and the
   button. */
.form__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6);
  padding-top: var(--space-5);
}

.form__note {
  color: var(--text-faint);
  font-size: var(--text-note);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
}

.field__label {
  color: var(--text-muted);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

/* The copper border is the focus indicator, so the outline is dropped. */
.field__control {
  min-width: 0;
  padding: var(--pad-control);
  border: 1px solid var(--line-field);
  border-radius: var(--radius);
  outline: none;
  background: var(--surface);
  color: var(--text);
  font-size: var(--text-body-lg);
}

.field__control:focus {
  border-color: var(--accent);
}

/* --- Day strip (booking form) --------------------------------------------
   The days the shop is open, as pickable cards. A radio per day, so choosing
   one needs no JavaScript; the input itself is hidden and the label carries
   the whole appearance. A closed day stays on the strip, dimmed and
   disabled, because a visitor looking for Monday should be told Monday is
   shut rather than left hunting for it. */
/* A fieldset for the radio group's semantics, styled back to a plain field:
   the browser's default frame and legend indent would be the only box on
   the form. */
.daystrip {
  margin: 0;
  padding: 0;
  border: 0;
}

.daystrip > .field__label {
  padding: 0;
}

.daystrip__row {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  padding-bottom: var(--space-2);
  /* Cards stay legible on a phone; the strip scrolls instead of shrinking. */
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}

.daystrip__day {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  gap: 2px;
  align-items: center;
  min-width: 4.25rem;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line-field);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
  scroll-snap-align: start;
  text-align: center;
}

/* Hidden from sight, not from the keyboard or a screen reader. */
.daystrip__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

.daystrip__weekday {
  color: var(--text-muted);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.daystrip__date {
  color: var(--text);
  font-family: var(--font-display);
  font-size: var(--display-prose-h3);
  line-height: 1;
}

.daystrip__hours {
  color: var(--text-faint);
  font-size: var(--text-caption);
  white-space: nowrap;
}

.daystrip__day:hover:not(.is-closed) {
  border-color: var(--line-strong);
}

/* The copper border is the focus indicator on the rest of the form, so the
   card follows it rather than drawing its own outline. */
.daystrip__day:focus-within {
  border-color: var(--accent);
}

.daystrip__day:has(.daystrip__input:checked) {
  border-color: var(--accent);
  background: rgba(208, 138, 84, 0.12);
}

.daystrip__day:has(.daystrip__input:checked) .daystrip__weekday,
.daystrip__day:has(.daystrip__input:checked) .daystrip__hours {
  color: var(--accent);
}

.daystrip__day.is-closed {
  border-style: dashed;
  background: transparent;
  cursor: not-allowed;
  opacity: 0.45;
}

.daystrip__day.is-closed .daystrip__date {
  color: var(--text-muted);
}

select.field__control {
  cursor: pointer;
}

textarea.field__control {
  resize: vertical;
}

/* --- Contact · form beside the map ---------------------------------------
   The same main/aside band as the booking page: the form takes the reading
   column, the map takes the other half of a band that used to be empty from
   640px rightwards. `auto-fit` against --col-min is what stacks them on a
   narrow screen, so there is no breakpoint of its own here. */
/* Two equal halves side by side, divided by a hairline and stacking where
   two won't fit, with their buttons level at the foot of each; the map runs
   the full width beneath them. The Gallery page's close: what to choose,
   and where to come. */
.split-band {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--col-min), 1fr));
  gap: var(--hairline);
  border: var(--border-hairline);
  background: var(--line);
}

.split-band__half {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  min-width: 0;
  padding: clamp(24px, 4vw, 56px);
  background: var(--bg);
}

.split-band__half .actions {
  margin-top: auto;
}

.split-band__map {
  display: block;
  width: 100%;
  height: clamp(280px, 34vw, 440px);
  border: var(--border-hairline);
  filter: invert(1) hue-rotate(180deg) saturate(0.55) brightness(0.82) contrast(1.05);
}

.contact {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--col-min), 1fr));
  align-items: stretch;
  /* The rule between form and map is the grid's own gap, showing the
     container's ground through a 1px channel, rather than a border on the
     aside. `auto-fit` decides when the two stack, and it does that on the
     container's width with no media query to match — so a border plus a
     breakpoint would drift the moment either moved, leaving a vertical rule
     under a stacked panel. A gap turns with the grid for free. */
  gap: 1px;
  background: var(--line);
}

.contact__main,
.contact__aside {
  background: var(--bg);
}

.contact__main {
  display: flex;
  flex-direction: column;
  gap: var(--space-10);
  min-width: 0;
  padding: var(--pad-booking) var(--gutter-booking);
}

/* A shop with no address has no aside, so the form would otherwise sit in a
   half-width column with nothing beside it. Give it the whole band back and
   hold it to a readable measure.

   The ground goes back to the page's own: with one child there is no gap to
   show a rule through, and the line colour would otherwise paint the whole
   strip of track the 820px column does not cover. */
.contact--formonly {
  background: none;
}

.contact--formonly .contact__main {
  max-width: 820px;
}

.contact__aside {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* The embed comes back as Google's light map, which would be the one white
   rectangle on a near-black site. Inverting and rotating the hue puts it in
   the theme's register — roads dark, labels light — without a styling API or
   a key. Contrast is pulled back slightly because a straight invert renders
   the label text at a harsher white than anything else on the page. */
.contact__map {
  flex: 1;
  width: 100%;
  min-height: clamp(280px, 34vw, 460px);
  border: 0;
  filter: invert(1) hue-rotate(180deg) saturate(0.55) brightness(0.82) contrast(1.05);
}

.contact__where {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: clamp(24px, 3vw, 40px);
  border-top: var(--border-hairline);
}

.contact__address {
  color: var(--text-light);
  font-size: var(--text-body-base);
  line-height: var(--leading-prose);
}

/* The Contact page's opening when the shop's booking form can be framed:
   the intro and the hours on the left, the form the full height on the
   right. Stacked, the form comes straight after the intro's buttons and the
   hours follow it, which is what the areas are for: the hours come last in
   the source too, but sit beside the form only on a wide screen. */
.contact-book {
  display: grid;
  grid-template-areas: "intro" "form" "hours";
  gap: var(--space-12);
}

.contact-book__intro {
  grid-area: intro;
}

.contact-book__hours {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  grid-area: hours;
}

/* The form is the booking site's own page, light whatever the theme: a
   hairline holds its edge against the dark ground. It scrolls inside the
   frame, so the frame only needs to be tall enough to show a step whole. */
.contact-book__form {
  display: block;
  grid-area: form;
  width: 100%;
  min-width: 0;
  height: 100%;
  min-height: clamp(560px, 80vh, 720px);
  border: var(--border-hairline);
  background: #fff;
}

@media (min-width: 1100px) {
  .contact-book {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    grid-template-rows: auto 1fr;
    grid-template-areas: "intro form" "hours form";
    gap: clamp(48px, 5vw, 72px) clamp(48px, 5vw, 96px);
  }
}

/* --- Notice · DraftBanner ------------------------------------------------- */

/* The one feedback treatment: success, error, warning and info all look
   alike. */
.notice {
  padding: var(--pad-notice);
  border-left: var(--rule-accent);
  background: var(--notice-bg);
  color: var(--notice-text);
  font-size: var(--text-body-sm);
}

.notice--flash {
  margin: var(--space-6) var(--gutter) 0;
}

.draft-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-5);
  padding: var(--space-4) var(--gutter-header);
  border-bottom: var(--border-hairline);
  background: var(--notice-bg);
  color: var(--notice-text);
  font-size: var(--text-body-sm);
}

.draft-banner__tag {
  color: var(--accent);
  font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-caption);
  text-transform: uppercase;
}

.draft-banner__link {
  margin-left: auto;
  font-weight: var(--weight-bold);
}

/* --- Prose (Markdown page body) ------------------------------------------- */

.page-content {
  max-width: var(--measure-prose);
  color: var(--text-soft);
  font-size: var(--text-lede);
  line-height: var(--leading-prose);
}

.page-content > * {
  margin: 0;
}

.page-content > * + * {
  margin-top: 1em;
}

.page-content h2,
.page-content h3 {
  color: var(--text);
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-snug);
}

.page-content h2 {
  font-size: var(--display-prose-h2);
}

.page-content h3 {
  font-size: var(--display-prose-h3);
}

.page-content > * + h2,
.page-content > * + h3 {
  margin-top: 1.4em;
}

.page-content strong {
  color: var(--text);
}

.page-content a {
  border-bottom: var(--hairline) solid currentColor;
}

.page-content ul,
.page-content ol {
  padding-left: 1.2em;
}

.page-content blockquote {
  padding-left: var(--space-6);
  border-left: var(--rule-accent);
  color: var(--text-light);
}

.page-content img {
  display: block;
  height: auto;
}

.page-content hr {
  border: 0;
  border-top: var(--border-hairline);
}

/* --- SiteFooter ----------------------------------------------------------- */

.site-footer {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  align-items: start;
  gap: var(--space-10);
  padding: var(--pad-footer) var(--gutter);
  border-top: var(--border-hairline);
}

.site-footer__col {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
  color: var(--text-soft);
  font-size: var(--text-body-base);
  font-style: normal;
}

.site-footer__brand {
  gap: 0;
}

/* The mark sits centred over the shop's name rather than flush with its first
   letter. Centring it in the column would put it in the middle of the whole
   grid track, so mark and name share a stack that shrinks to the name — then
   "centre" means the width of the name, which is what it should mean. */
.site-footer__mark {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  width: fit-content;
}

.site-footer__logo {
  display: block;
  width: auto;
  height: 64px;
  margin-bottom: var(--space-5);
}

/* Alone in the stack, with no name below it, so tall enough that a
   wordmark's small print reads. */
.site-footer__logo--custom {
  width: auto;
  max-width: 220px;
  height: 110px;
  object-fit: contain;
}

.site-footer__name {
  color: var(--text);
  font-family: var(--font-display);
  font-size: var(--display-brand-footer);
  letter-spacing: var(--tracking-brand);
  text-transform: uppercase;
}

.site-footer__tagline {
  padding-top: var(--space-3);
  color: var(--text-faint);
  font-size: var(--text-body-sm);
}

/* The footer's only button is the ghost one; the copper fill stays in the
   header and the CTA band. */
.site-footer__cta {
  align-items: flex-start;
  gap: var(--space-4);
}

/* Column headings are h2 for the outline but read as the design's label,
   so they take the body face rather than the headings' display one. */
.site-footer__heading {
  margin-bottom: var(--space-3);
  font-family: var(--font-body);
  font-weight: normal;
}

.site-footer__about {
  max-width: 32ch;
  padding-top: var(--space-5);
  color: var(--text-soft);
  font-size: var(--text-body-sm);
  line-height: var(--leading-body);
}

.site-footer__col a:hover {
  color: var(--accent-hover);
}

.site-footer__directions {
  color: var(--accent);
}

/* The copyright runs under every column, ruled off from them. */
.site-footer__legal {
  grid-column: 1 / -1;
  padding-top: var(--space-8);
  border-top: var(--border-hairline);
  color: var(--text-faint);
  font-size: var(--text-note);
}

/* --- Error page ----------------------------------------------------------- */

/* The design's Error page template: the status numeral set oversized in
   copper, centred in whatever height the header and footer leave. */
.error-page {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: clamp(72px, 11vw, 150px) var(--gutter);
}

/* Ruled 64px grid, masked to an ellipse so it fades out well before the
   edges rather than stopping at them. */
.error-page__grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(242, 237, 231, 0.055) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(242, 237, 231, 0.055) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: radial-gradient(ellipse 78% 68% at 50% 32%, #000 26%, transparent 78%);
  mask-image: radial-gradient(ellipse 78% 68% at 50% 32%, #000 26%, transparent 78%);
}

/* Copper cast behind the numeral, thrown from above it. */
.error-page__glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(52% 46% at 50% 6%, rgba(208, 138, 84, 0.2) 0%, transparent 66%);
}

.error-page__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: 620px;
  text-align: center;
}

/* Olive, not the eyebrow's usual copper: the numeral below is already
   copper, and two of them stacked is one too many. */
.error-page__eyebrow {
  color: var(--olive);
  font-weight: var(--weight-bold);
}

.error-page__code {
  display: block;
  margin-top: 14px;
  color: var(--accent);
  font-family: var(--font-display);
  font-size: clamp(116px, 24vw, 240px);
  font-weight: var(--weight-regular);
  letter-spacing: 0.01em;
  line-height: 0.78;
}

.error-page__rule {
  display: block;
  width: 64px;
  height: 1px;
  margin-top: clamp(28px, 4vw, 40px);
  background: var(--line-strong);
}

.error-page__title {
  margin-top: clamp(26px, 4vw, 36px);
  font-size: var(--display-section);
  letter-spacing: var(--tracking-title);
  line-height: var(--leading-display-flat);
}

.error-page__lede {
  max-width: var(--measure-copy);
  margin-top: var(--space-6);
  color: var(--text-soft);
  font-size: var(--text-lede);
  line-height: var(--leading-body-loose);
}

.error-page__actions {
  justify-content: center;
  margin-top: clamp(28px, 4vw, 38px);
  padding-top: 0;
}
