/*
  The business's own one-page site, /b/<slug> (D42, U1 docs/BOOKING_PAGE_UI_BRIEF.md).

  BUILT LIKE THE LANDING PAGE, ON PURPOSE (U1). Winmar: "can we make this sort of
  same with the landing page UI". The :root tokens, the type scale, the spacing
  rhythm, .wrap, .eyebrow, .go, the card rule and the single 900px breakpoint
  below are copied from landing/index.html, not reinvented --
  tests/test_site_css.py parses both files' :root blocks and fails the build if
  a shared token ever drifts. U3/U4 add the page's own layout and button rules
  on top of this same foundation; nothing here changes for either.

  STILL THE BUSINESS'S PAGE, NOT DMJOBBER'S (D42). No dmjobber mark, no --brand
  reference, no third-party host -- Inter is self-hosted here exactly as
  static/css/app.css already does it (B2, docs/BRAND_BRIEF.md), never Bunny
  Fonts, which the landing page itself uses only because it is a separate
  static site with no CSP of its own to protect.

  A SEPARATE FILE FROM app.css, DELIBERATELY. app.css is the signed-in owner's
  own tool -- 16px fixed, --gap, --tap 44px, one column always, --brand/--action.
  This is the business's public storefront, wearing the landing's fluid type
  scale and 900px layout instead. The two never load on the same page, so
  sharing a class name (.card, .button) between them is not a collision.
*/

/* B2, docs/BRAND_BRIEF.md: the same three self-hosted weights app.css uses --
   see static/fonts/inter/README.md for the subsetting command. Never a
   third-party font host on a page nothing else on /b/ loads from either. */
@font-face {
  font-family: "Inter";
  src: url("../fonts/inter/Inter-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("../fonts/inter/Inter-SemiBold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("../fonts/inter/Inter-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  /* U1: copied verbatim from landing/index.html's own :root -- see
     tests/test_site_css.py, which fails the build the moment one of these
     stops matching. Typography/spacing refresh, 2026-09-22 (docs/DECISIONS.md,
     new D-number): --ink is headings only now, --ink-soft carries body text
     (see body{} and h1,h2,h3{} below) -- both re-verified >=7:1 on --page
     and --card. --radius is a card radius now; buttons are a hardcoded
     pill, 999px, unrelated to it. */
  --ink: #0B0707;
  --ink-soft: #3B3B3B;
  --page: #FBFAF7;
  --card: #FFFFFF;
  --line: #E7E3DA;
  --accent: #F2A23A;
  --accent-wash: #FDF1DE;
  /* Secondary text on the landing page's one ink band (.problem, 23 Sep 2026):
     11.4:1 on --ink. Carried here so site.css and landing/index.html keep one
     shared token set (tests/test_site_css.py). */
  --on-ink-soft: #C9C3B8;
  --radius: 24px;
  --tap: 48px;

  --step--1: clamp(14px, 0.85rem + 0.1vw, 15px);
  --step-0:  clamp(16px, 0.95rem + 0.2vw, 18px);
  --step-1:  clamp(19px, 1.05rem + 0.5vw, 22px);
  --step-2:  clamp(23px, 1.2rem + 1.1vw, 28px);
  --step-3:  clamp(28px, 1.35rem + 2.2vw, 38px);
  --step-4:  clamp(34px, 1.5rem + 4.4vw, 58px);

  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px; --s-5: 24px;
  --s-6: 32px; --s-7: 48px; --s-8: 64px; --s-9: 96px; --s-10: 128px;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: var(--step-0);
  font-optical-sizing: auto;
  line-height: 1.7;
  color: var(--ink-soft);
  background: var(--page);
  /* The page never scrolls sideways -- a long email address or a public link
     is one unbreakable "word" otherwise (the same rule app.css keeps). */
  overflow-x: hidden;
  overflow-x: clip;
  overflow-wrap: break-word;
}
a { color: var(--ink); text-decoration-color: var(--accent); text-decoration-thickness: 2px;
    text-underline-offset: 3px; transition: text-decoration-thickness 120ms ease-out; }
a:hover, a:focus-visible { text-decoration-thickness: 3px; }
:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }

.wrap { max-width: 1200px; margin: 0 auto; padding: 0 var(--s-4); }
/* Typography refresh: headings stay --ink (body's own colour is --ink-soft
   now) and drop to weight 500; line-height is per level, not shared, since
   h1 and h2 want different values now. */
h1, h2, h3 { letter-spacing: -0.02em; margin: 0; text-wrap: balance; color: var(--ink); }
h1 { font-size: clamp(36px, 4.4vw, 56px); font-weight: 500; line-height: 1.2; }
/* Fixed sizes, not a --step clamp: a hard 24/32px switch at 900px, not a
   fluid one. */
h2 { font-size: 24px; font-weight: 500; line-height: 1.4; }
h3 { font-size: var(--step-1); font-weight: 600; line-height: 1.15; }
p { margin: 0; max-width: 62ch; text-wrap: pretty; line-height: 1.7; }
.muted { color: var(--ink-soft); }
.tiny { color: var(--ink-soft); font-size: var(--step--1); }
.eyebrow { font-size: var(--step--1); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
           color: var(--ink-soft); margin-bottom: var(--s-2); }
/* U2's third named use of the business's own accent ("the hero band, the
   note panel, the section marker") -- a small dot before every eyebrow, the
   same shape landing/index.html's own .free::before already draws. */
.eyebrow::before { content: ""; display: inline-block; width: 6px; height: 6px;
                   border-radius: 50%; background: var(--accent); margin-right: var(--s-2); }

/* U1: the generic card recipe landing/index.html inlines per component
   (.steps li, .price-card) -- named once here, since a public page with
   services and photos needs it as a reusable class the landing never did.
   No border, no shadow (typography refresh, 2026-09-22) -- white on the
   page's own off-white is enough. Padding follows the same refresh: 24px
   (var(--s-5)) on phones, 32px (var(--s-6)) from 900px, in the media query
   below. */
.card { background: var(--card); border-radius: var(--radius);
        padding: var(--s-5); }

/* The landing's own primary button (U4 gives the hierarchy rule this earns
   its keep under: exactly one .go visible per screen region). Amber fill,
   ink text -- copied from landing/index.html's own .go, not reinvented.
   Pill (999px), not a 10px-rounded rectangle, 30px side padding -- the
   typography refresh's own button shape, shared with .outline below. */
.go { display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
      min-height: var(--tap); padding: 0 30px; border-radius: 999px; background: var(--accent);
      color: var(--ink); font-weight: 500; font-size: var(--step--1); text-decoration: none;
      transition: filter 120ms ease-out; }
.go:hover, .go:focus-visible { filter: brightness(0.96); }

/* U4: Directions and every contact chip -- outline, never filled, so the one
   filled .go per region still reads as the one thing to do. Same pill shape
   as .go (typography refresh, 2026-09-22) -- only the fill and the border
   tell the two apart. */
.outline { display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
           min-height: var(--tap); padding: 0 30px; border-radius: 999px;
           background: var(--card); border: 1px solid var(--line); color: var(--ink);
           font-weight: 500; font-size: var(--step--1); text-decoration: none; }
.outline:hover, .outline:focus-visible { border-color: var(--ink); }

/* U3/U4: Share and Copy link are plain text, never a button or a card --
   underlined links with icons, 48px tall so the tap target still meets --tap
   even though nothing is drawn as a button or outline chip. */
.text-link { display: inline-flex; align-items: center; gap: var(--s-2); min-height: var(--tap);
             color: inherit; font-weight: 600; font-size: var(--step--1); background: none;
             border: 0; padding: 0; cursor: pointer; font-family: inherit;
             text-decoration-color: var(--accent); text-decoration-thickness: 2px;
             text-underline-offset: 3px; }
.text-link svg { flex: none; }

/* the landing's own generic card recipe (.steps li, .price-card), named as a
   note panel: the empty-services message (U3) and anything else that wants
   the accent-wash surface without looking like a card of content. */
.note-panel { background: var(--accent-wash); }

/* ---------- header (U3) ---------- */
.bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  min-height: 64px; position: sticky; top: 0; z-index: 5; background: var(--page);
  border-bottom: 1px solid var(--line);
}
.bar-name { display: flex; align-items: center; gap: var(--s-2); font-weight: 700;
            font-size: var(--step-0); color: var(--ink); text-decoration: none; min-width: 0; }
/* C10, D71: contain, not cover, and no forced square box -- a wide wordmark
   logo (dmdent.com, not square) in a 28x28 cover box had both ends cropped
   to illegibility (Winmar's phone, 2026-09-22). height:28px with width:auto
   keeps the header row's own height, capped at 112px (4:1) so an extreme
   wordmark still cannot crowd the name and Book off a 360px screen; the
   img's own width/height ATTRIBUTES (templates/booking_public/_layout.html,
   business.html) are the upload's real size, so the browser still knows its
   aspect ratio before the file itself has loaded. */
.bar-logo { height: 28px; width: auto; max-width: 112px; border-radius: 6px; flex: none;
            object-fit: contain; }
.bar-name-text { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bar-links { display: none; align-items: center; gap: var(--s-5); font-size: var(--step--1);
             font-weight: 500; }
.bar-links a { color: var(--ink-soft); text-decoration: none; }
.bar-links a:hover, .bar-links a:focus-visible { color: var(--ink); }
.bar .go { padding: 0 var(--s-5); min-height: 40px; font-size: var(--step--1); flex: none; }

/* ---------- hero ---------- */
.hero { background: var(--accent-wash); padding-block: var(--s-7); }
.hero .wrap { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-3); }
.hero .sub { font-size: 18px; line-height: 1.7; margin: 0; max-width: 46em; color: var(--ink-soft); }
.hero .go { margin-top: var(--s-2); }
.site-open-now { font-weight: 600; margin: 0; color: var(--ink-soft); }
.hero-links { display: flex; flex-wrap: wrap; gap: var(--s-5); margin: 0; }

/* The cover photo: a plain, fixed-height banner ABOVE the hero, not an
   overlay behind it (booking page fix, 2026-09-22 -- U3's original overlay,
   with the photo AS the hero band and the name/tagline/button packed
   against its bottom edge on a dark scrim, sized the whole band to the
   photo's own height; once the band's height stopped depending on the
   image at all -- a FIXED band was the point of this fix -- that overlay
   text had nowhere near enough room and overflowed off the top of the
   visible photo). The hero below is identical whether or not a cover
   exists: same accent wash, same ink text, same layout.

   16:9 on phones, capped at 360px from 900px (the media query below),
   object-fit: cover so any shape still fills the band -- the server also
   center-crops every cover to 1600x900 (services/images.py:_cover_crop),
   but the box is pinned here too, so an image stored before that change
   still shows in the same band. `height: auto` is load-bearing: the <img>
   tag's own `height` ATTRIBUTE (page.cover_height, kept for layout-shift
   prevention) becomes a real, explicit CSS height at very low
   (presentational-hint) priority, and an explicit height leaves
   `aspect-ratio` nothing "auto" to compute -- without this override a
   1200x2000 upload rendered at literally 2000 CSS pixels tall, aspect-ratio
   declared or not. Found by the Playwright layout test, not by reading it.
*/
.hero-cover-img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9;
                  object-fit: cover; object-position: center; }

/* ---------- services ---------- */
.steps { list-style: none; margin: var(--s-5) 0 0; padding: 0; display: grid; gap: var(--s-4); }
.service-card h3 { margin-bottom: var(--s-1); font-weight: 600; }
.service-card .price { font-size: var(--step-2); font-weight: 500; letter-spacing: -0.5px; margin: var(--s-2) 0; }
.service-card .go { width: 100%; margin-top: var(--s-3); }

/* ---------- photos ---------- */
.gallery { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-2); margin-top: var(--s-4); }
/* The WHOLE photo, never a crop (2026-09-22: a portrait phone screenshot in a
   square tile lost its top and bottom under object-fit: cover). Every tile is
   still the same square, so the grid stays even, and the photo is fitted
   inside it (contain) on the page colour's own pale wash -- which reads as a
   frame whatever shape the upload is. Tap still opens it full size. */
.gallery-item { aspect-ratio: 1; overflow: hidden; border-radius: var(--radius); border: 0;
                padding: var(--s-2); background: var(--accent-wash); cursor: pointer;
                display: flex; align-items: center; justify-content: center; }
.gallery-item img { display: block; max-width: 100%; max-height: 100%; width: auto; height: auto;
                    object-fit: contain; border-radius: 8px; }
#site-gallery-dialog { border: 0; border-radius: var(--radius); padding: var(--s-4); max-width: 90vw;
                       max-height: 90vh; background: var(--card); }
#site-gallery-dialog::backdrop { background: rgba(20, 20, 20, 0.7); }
#site-gallery-dialog img { display: block; max-width: 100%; max-height: 70vh; margin-top: var(--s-3); }

/* ---------- section headings: one consistent rhythm ---------- */
/* Booking page fix, 2026-09-22: Book a time, Our work, About, Opening
   hours, Where and Contact now share one size and one top gap, so nothing
   sits directly under the box above it -- previously none of the six had
   any top margin at all, and the second column of Hours/Where/Contact
   (Contact) touched the first column's own content directly once the
   .again-grid collapses to one column below 900px. An eyebrow, where the
   section has one (Services, Photos), carries the gap instead of the h2
   that follows it -- already snug against its own eyebrow via .eyebrow's
   own margin-bottom -- so the label and its heading stay visually paired;
   a bare h2 (About) or the first stack-block in a column (Opening hours,
   Contact) carries it directly. Where's own stack-block keeps its existing,
   tighter .stack-block + .stack-block gap from Opening hours, unchanged. */
.wrap > .eyebrow, .wrap > h2:first-child, .stack-block:first-child { margin-top: var(--s-7); }

/* C11, D71: the rhythm rule above was also matching the HERO's own first
   child -- .eyebrow, when there is a location line -- stacking its 48px
   margin-top on top of .hero's own padding-block (48px, or 80px from 900px)
   and leaving a wash-coloured gap between the cover photo and the location
   line big enough to read as broken (Winmar's phone, 2026-09-22). More
   specific than the rule above (three selectors deep, not two), so this
   wins regardless of source order. */
.hero .wrap > :first-child { margin-top: 0; }
/* Cover directly above hero: the hero's own top padding drops too -- the
   photo already separates it from the header, so the hero's usual gap (this
   is only ever ONE side, padding-top, not padding-block) would be doubling
   up on that same job. No cover: the hero keeps its full padding-block,
   unchanged, since it is then the first thing under the sticky header. */
.hero-cover-img + .hero { padding-top: var(--s-5); }

/* ---------- hours / where / contact ---------- */
.hours-table { width: 100%; border-collapse: collapse; margin-top: var(--s-3); }
.hours-table th, .hours-table td { text-align: left; padding: var(--s-1) 0; font-weight: 400; }
.hours-table td { text-align: right; font-variant-numeric: tabular-nums; }
.hours-today th, .hours-today td { font-weight: 700; }
.closures { list-style: none; margin: var(--s-2) 0 0; padding: 0; color: var(--ink-soft); }
.stack-block + .stack-block { margin-top: var(--s-6); }
.contact-row { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-3); }
.contact-row .outline { min-width: 140px; flex: 1 0 auto; }

/* ---------- inner pages: slots, the booking form, a booking's own status
   (booking page fix, 2026-09-22 -- U1 replaced app.css with this file on
   every /b/ page, but these templates were never rebuilt on it, so they
   rendered with none of it: no side padding, unstyled inputs, a default
   browser button, and a two-column list with no layout of its own at all) */

/* A row of tappable chips -- each available slot (as a start-end pair, so
   two adjacent slots never read as one continuous range), and the
   later/earlier-dates pager. Same shape as .contact-row, a different name:
   reusing "contact-row" here would describe the wrong thing to the next
   reader even though the CSS is identical. */
.slot-row { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-3); }
.slot-row .outline { flex: 0 1 auto; }

/* An inline message -- a rate limit, a slot that just went, quiet hours.
   role="note"/"status"/"alert" (set per message in the templates) carries
   the accessibility meaning; these three only carry the look. */
.notice { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
          padding: var(--s-3) var(--s-4); font-size: var(--step--1); margin-top: var(--s-3); }
.notice-good { background: #eaf6ec; border-color: #a9d9b3; color: #1e5631; }
.notice-bad { background: #fdecea; border-color: #f3b4ab; color: #7a271a; }

/* A booking's own status, at a glance, before the sentence explaining it. */
.badge { display: inline-flex; align-items: center; padding: var(--s-1) var(--s-3);
         border-radius: 999px; font-size: var(--step--1); font-weight: 600;
         background: var(--card); border: 1px solid var(--line); }
.badge-good { background: #eaf6ec; border-color: #a9d9b3; color: #1e5631; }
.badge-bad { background: #fdecea; border-color: #f3b4ab; color: #7a271a; }
.badge-warn { background: var(--accent-wash); border-color: var(--accent); }

/* A two-column label/value list -- a booking's own details, on its status
   and cancel pages. */
.rows { display: flex; flex-direction: column; gap: var(--s-3); }
.row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-4); }
.row-sub { color: var(--ink-soft); font-size: var(--step--1); }
.amount { font-weight: 700; font-variant-numeric: tabular-nums; }

/* An empty state -- no slots free in the range shown. */
.empty { color: var(--ink-soft); padding: var(--s-4) 0; }

/* Labels above full-width inputs, at least var(--step-0)'s own 16px floor
   so focusing one never makes iOS Safari zoom the page in. Scoped to bare
   elements, not a .field wrapper class: site.css is /b/ pages only (never
   the signed-in app's own forms), so styling input/select/textarea
   directly here cannot leak anywhere it would be unwelcome. */
label { display: block; font-weight: 600; font-size: var(--step--1); margin: var(--s-4) 0 var(--s-1); }
label:first-child { margin-top: 0; }
.required { color: var(--ink-soft); font-weight: 400; }
input, select, textarea {
  display: block; width: 100%; min-height: var(--tap); padding: 0 var(--s-3);
  border-radius: 10px; border: 1px solid var(--line); background: var(--card);
  color: var(--ink); font-size: var(--step-0); font-family: inherit;
}
textarea { min-height: calc(var(--tap) * 2); padding-block: var(--s-2); }
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 3px solid var(--ink); outline-offset: 1px;
}
.field-error { color: #b3261e; font-size: var(--step--1); margin: var(--s-1) 0 0; }
form .go, form .outline { margin-top: var(--s-4); }

/* ---------- footer ---------- */
/* Booking page fix, 2026-09-22: `padding: var(--s-5) 0` -- the shorthand --
   was overriding .wrap's own `padding: 0 var(--s-4)` on the same element
   (the footer carries both classes), zeroing its side padding and running
   the text to the screen edge. `padding-block` touches only the vertical
   half of the property, so .wrap's horizontal padding survives. */
.site-footer { margin-top: var(--s-7); padding-block: var(--s-5); border-top: 1px solid var(--line); }

/* U5: one breakpoint, the landing's own. */
@media (min-width: 900px) {
  .wrap { padding: 0 var(--s-5); }
  .bar-links { display: flex; }
  /* Typography refresh, 2026-09-22: the hero is the page's first section
     (80px top, the spec's "first section" figure); every other section
     keeps the 40-48px range already set by the base/mobile rule. */
  .hero { padding-top: 80px; padding-bottom: var(--s-7); }
  /* C11, D71: the cover-adjacent hero's own smaller top padding, from 900px. */
  .hero-cover-img + .hero { padding-top: var(--s-6); }
  h2 { font-size: 32px; }
  .card { padding: var(--s-6); }
  .steps { grid-template-columns: repeat(2, 1fr); }
  .gallery { grid-template-columns: repeat(3, 1fr); }
  /* At 900px+ the 16:9 box alone would keep growing with the viewport's own
     width (a full-bleed image, no .wrap); capped here instead, same as a
     typical hero banner -- object-fit: cover still fills whatever box this
     leaves cleanly, on every image already or ever uploaded. */
  .hero-cover-img { max-height: 360px; }
  .again-grid { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: var(--s-8); align-items: start; }
}

/* U6/the landing's own rule: no entrance animation. Motion stays limited to
   the hover/focus transitions declared above. */
@media (prefers-reduced-motion: reduce) {
  * { transition: none; }
}

/* DMJ-247 F470 (D1443): this layout does not load app.css, so the tap floor's `link-line` is said here too:
   a paragraph of links is a target as tall as this site's buttons (--tap). The booking pages are phone-first,
   so it is not limited to a phone (templates/booking_public). */
.link-line > a:not(.outline) { display: inline-flex; align-items: center; min-height: var(--tap); }
