/* site.css — the shared "chrome" of the PUBLIC marketing site (the pages served
   from frontend/public/: privacy, terms, policies, faq, checklist, gallery,
   index, pay). One source of truth for the fixed nav bar, the mobile menu, the
   footer and the marketing body treatment.

   Why this exists separately from components.css: the admin console's shared
   components (components.css) were tuned for the console and carry different
   values (panel tone, footer sizing, mobile-menu structure). The marketing site
   has its own coherent chrome — reused across all 8 public pages via the SSI
   partials (partials/header.html, partials/footer.html) for markup — so its
   styling lives here, once, with the marketing values (NOT the admin ones).

   Depends on tokens.css (palette/type) and base.css (reset/container/focus).
   Reveal/section motion comes from animations.css. Page-specific layout (hero,
   legal prose, cards, gallery, booking form) lives in each page's own <page>.css.
   Cascade per page: tokens.css -> base.css -> site.css -> animations.css -> <page>.css. */

/* The marketing surface panel is a warmer tone than the admin console's --panel
   token (#16140f). Re-tint it here, scoped to the marketing layer, so every
   marketing page can use var(--panel) and get #1a1815; the console (which never
   loads site.css) keeps the tokens.css default. This is the one token the
   marketing layer deliberately overrides. */
:root{--panel:#1a1815}

/* ---------- marketing body treatment ---------- */
/* base.css already sets background/colour/font-family/flex column; the marketing
   surface adds a lighter weight, prose line-height, smoothing and the two fixed
   decorative layers (a warm radial wash + a faint noise grain) that sit behind
   the content. .wrap raises the content above those z-index:0 layers. */
body{font-weight:300;line-height:1.6;-webkit-font-smoothing:antialiased}
body::before{content:"";position:fixed;inset:0;z-index:0;pointer-events:none;
  background:radial-gradient(120% 80% at 80% -10%, rgba(201,165,88,.10), transparent 55%),radial-gradient(90% 60% at 0% 100%, rgba(201,165,88,.05), transparent 50%);}
/* The wash breathes rather than sitting still: a nine-second cycle, a few percent of
   opacity and scale. Only transform and opacity, so it stays on the compositor and
   costs no layout. Desktop only - it is a background flourish, not worth a phone's
   battery - and off entirely for a reader who asked for less motion. */
@keyframes wash-breathe{from{opacity:.82;transform:scale(1)}to{opacity:1;transform:scale(1.045)}}
@media(min-width:761px) and (prefers-reduced-motion:no-preference){
  body::before{will-change:opacity,transform;animation:wash-breathe 9s var(--ease-standard) infinite alternate}
}
body::after{content:"";position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.035;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");}
.wrap{position:relative;z-index:1}

/* ---------- cross-document view transitions ---------- */
/* The MPA form: a navigation transitions only when BOTH documents carry the
   @view-transition rule, so scope is decided by who loads this file. The console never
   loads site.css and is opted out by absence; login.css DOES load it and opts itself
   back out (`navigation:none`) - a different chrome layer should just load.
   The opt-in sits inside the reduced-motion query, so a reader who asked for less
   motion gets no transition at all rather than an animation neutralised afterwards.
   The bar and the footer are NAMED, so they are captured as their own snapshot pairs
   and stay continuous while the content cross-fades underneath; unnamed they would
   belong to the root snapshot and the whole page, chrome included, would blink.
   Durations and easings are the shared tokens - nothing new is introduced here. */
@media(prefers-reduced-motion:no-preference){
  @view-transition{navigation:auto}
}
.site-nav{view-transition-name:chrome-nav}
footer{view-transition-name:chrome-footer}
/* A cross-fade with a slight vertical shift, in the direction of travel. The strip the
   shift exposes is the page canvas (--bg from base.css), never white: the old page's
   snapshot covers the viewport until the new one has faded in over it. */
@keyframes vt-page-out{to{opacity:0;transform:translateY(-8px)}}
@keyframes vt-page-in{from{opacity:0;transform:translateY(8px)}}
::view-transition-old(root){animation:vt-page-out var(--dur) var(--ease-standard) both}
::view-transition-new(root){animation:vt-page-in var(--dur) var(--ease-standard) both}

/* ---------- shared hero label (the gold eyebrow with side rules) ---------- */
/* Identical across the content pages (legal, faq, checklist). Pages that need a
   different eyebrow (e.g. index) redefine it in their own CSS, which loads after. */
.eyebrow{display:inline-flex;align-items:center;gap:12px;color:var(--gold);font-size:.74rem;letter-spacing:.34em;text-transform:uppercase;margin-bottom:24px}
.eyebrow::before,.eyebrow::after{content:"";width:30px;height:1px;background:var(--gold)}

/* ---------- numbered headings ---------- */
/* "01  Do I need to be home during the cleaning?" - the number is a COLUMN, and a
   heading that wraps keeps its later lines under the first line of TEXT rather than
   falling back under the number. Written once here because four marketing pages ship
   the same markup (h2 > span.num + text): the FAQ questions and the numbered clauses
   on privacy, terms and policies.

   Flexbox rather than text-indent on purpose: the column then never depends on how
   many digits the number has. The box is at LEAST --num-col wide and simply grows for
   a longer one, carrying its own text column with it - nothing here breaks the day a
   list passes 99. Pages keep their own face and size for .num; only the geometry is
   shared. */
:root{--num-col:26px;--num-gap:10px}
.numbered{display:flex;align-items:baseline;gap:var(--num-gap)}
.numbered .num{flex:0 0 auto;min-width:var(--num-col)}

.nav-links{display:flex;gap:34px;align-items:center}
.nav-links a{color:var(--ink-soft);text-decoration:none;font-size:.82rem;letter-spacing:.08em;text-transform:uppercase;transition:color .25s}
@media(hover:hover) and (pointer:fine){.nav-links a:hover{color:var(--ink)}}
.nav-cta{border:1px solid var(--gold);color:var(--gold)!important;padding:9px 20px;border-radius:2px;transition:all .25s}
@media(hover:hover) and (pointer:fine){.nav-cta:hover{background:var(--gold);color:var(--bg)!important}}
.nav-cta:active{background:var(--gold);color:var(--bg)!important}

/* ---------- CTA arrow (opt-in, marketing pages only) ---------- */
/* At rest a CTA is just its label, centred. On hover the label slides a little left and
   an arrow rides out from under it - motion only: the fill, the border and the colour of
   every button are untouched, so each keeps whatever skin its own rule gives it.
   The button MUST NOT change width or reflow, so the arrow is absolutely positioned off
   the END OF THE LABEL and contributes no layout at all. It is a child of the label
   rather than a sibling of it precisely so it travels WITH the text: anchoring it to the
   button instead would need the label's width, which no rule here can know.
   Opt-in by markup: a button gets this only by carrying the .btn-label/.btn-arrow spans,
   so Back, Cancel and other non-CTA buttons that share .btn-ghost cannot sprout an arrow
   pointing the wrong way. The arrow is aria-hidden - it is decoration, and the button
   already has its accessible name from the label text. */
.btn-label{position:relative;display:inline-block;
  transition:transform var(--dur) cubic-bezier(.4,0,.2,1)}
.btn-arrow{position:absolute;left:100%;top:50%;display:inline-flex;
  margin-left:6px;opacity:0;
  transform:translate(-4px,-50%);
  transition:opacity var(--dur) cubic-bezier(.4,0,.2,1),
             transform var(--dur) cubic-bezier(.4,0,.2,1)}
/* Drawn, not typed: the self-hosted Outfit subset does not carry U+2192, so a literal
   arrow character would silently fall back to a system face and render at a different
   weight than the label beside it. currentColor keeps it in the button's own ink. */
.btn-arrow svg{display:block;width:14px;height:14px;fill:none;stroke:currentColor;
  stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round}
@media(hover:hover) and (pointer:fine){
  .btn:hover .btn-label,.nav-cta:hover .btn-label{transform:translateX(-6px)}
  .btn:hover .btn-arrow,.nav-cta:hover .btn-arrow{opacity:1;transform:translate(0,-50%)}
}
/* Keyboard focus shows the same state, and OUTSIDE the hover guard on purpose:
   :focus-visible is modality-aware already, so it never sticks after a tap the way a
   bare :focus would. */
.btn:focus-visible .btn-label,.nav-cta:focus-visible .btn-label{transform:translateX(-6px)}
.btn:focus-visible .btn-arrow,.nav-cta:focus-visible .btn-arrow{opacity:1;transform:translate(0,-50%)}
@media(prefers-reduced-motion:reduce){
  .btn-label{transition:none;transform:none!important}
  .btn-arrow{display:none}
}
@media(max-width:760px){.nav-links .hide-sm{display:none}}

/* .mob-body and the .mob-primary item list are the chrome's - one owner for this menu
   and the console's, which used to carry their own copies with different numbers. What
   follows here is the marketing menu's own content: its CTAs, secondary links, socials
   and contacts. */
.mob-cta{display:inline-block;margin:22px 0 30px;border:1px solid var(--gold);color:var(--gold);
  padding:14px 30px;border-radius:2px;font-size:.87rem;letter-spacing:.1em;text-transform:uppercase;text-decoration:none;transition:all .22s}
@media(hover:hover) and (pointer:fine){.mob-cta:hover{background:var(--gold);color:var(--bg)}}
.mob-cta:active{background:var(--gold);color:var(--bg)}
.mob-cta-row{display:flex;gap:14px;margin:22px 0 30px}
.mob-cta-row .mob-cta{margin:0;flex:1 1 0;text-align:center}
.mob-divider{height:1px;background:var(--line-soft);margin-bottom:20px}
.mob-secondary{list-style:none;margin-bottom:24px}
.mob-secondary li a{display:block;color:var(--ink-dim);font-size:.82rem;
  letter-spacing:.07em;text-transform:uppercase;text-decoration:none;padding:7px 0;transition:color .2s}
@media(hover:hover) and (pointer:fine){.mob-secondary li a:hover{color:var(--ink-soft)}}
.mob-secondary li a:active{color:var(--ink-soft)}
.mob-socials{display:flex;gap:12px;margin-bottom:18px}
.mob-socials a{width:48px;height:48px;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--line);border-radius:50%;color:var(--ink-soft);text-decoration:none;transition:border-color .25s,color .25s}
.mob-socials a svg{width:20px;height:20px;fill:currentColor}
@media(hover:hover) and (pointer:fine){.mob-socials a:hover{border-color:var(--gold);color:var(--gold)}}
.mob-socials a:active{border-color:var(--gold);color:var(--gold)}
.mob-hours{font-size:.84rem;color:var(--ink-dim);letter-spacing:.04em;margin-bottom:10px}
/* The contact lines arrive from the shared SSI fragment, so they are styled by their
   position and not by a class the fragment cannot carry. */
.mob-contact{font-size:.92rem;line-height:1.9}
.mob-contact a{color:var(--ink-soft);text-decoration:none;transition:color .2s}
@media(hover:hover) and (pointer:fine){.mob-contact a:hover{color:var(--gold)}}
.mob-contact a:active{color:var(--gold)}
@media(max-width:760px){.nav-links{display:none}}
@media(max-width:760px){
  /* The top bar keeps the nav's geometry here too - it IS the nav in the open state. */
  .mob-cta{margin:12px 0 18px;padding:12px 28px}
  .mob-cta-row{margin:12px 0 18px}
  .mob-divider{margin-bottom:14px}
  .mob-secondary{margin-bottom:14px}
  .mob-socials{margin-bottom:12px}
  .mob-hours{margin-bottom:6px}
}

/* ---------- footer (markup: partials/footer.html) ---------- */
/* The top gap matches the bottom one OPTICALLY, not by padding: the brand's serif
   line box starts 4px above its glyphs, so 22px of padding reads as the same 27px
   the copyright line keeps below its own rule (24px padding + 2px of line box). */


/* ---------- inline links inside prose ---------- */
/* WCAG 1.4.1: a link surrounded by text must be distinguishable WITHOUT colour. Gold
   on our body ink measures 1.3-2.1:1 against the surrounding text - far under the 3:1
   a colour-only difference would need - so the underline is what makes it a link, not
   a decoration we can drop for tidiness.
   Scoped to links with NO class of their own: every styled link here (nav, footer,
   buttons, cards, chips, the trust marks) carries one and is a standalone control
   rather than a word inside a sentence, where an underline would be wrong. The
   underline is tuned rather than default - offset off the baseline and drawn in a
   softened gold - so it reads as typography instead of a browser default. */
p a:not([class]),li a:not([class]),dd a:not([class]),.lead a:not([class]){
  text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:.18em;
  text-decoration-color:rgba(217,189,126,.55)}
@media(hover:hover) and (pointer:fine){
  p a:not([class]):hover,li a:not([class]):hover,dd a:not([class]):hover,
  .lead a:not([class]):hover{text-decoration-color:currentColor}}

/* ---------- home page trust strip ---------- */
/* One quiet band between "how we work" and the coverage map. The BADGES are the
   chrome's component (chrome.css), shared with the footer and the Accessibility page;
   all this layer owns is the band and the three word-credentials beside them, so a
   future real credential is one <li> rather than a new design.
   Centred and wrapping rather than spaced apart: with five items the row has to
   collapse to two lines on a phone, and a justify-between row strands one item. */
.trust-strip{padding:34px 0;border-top:1px solid var(--line-soft);border-bottom:1px solid var(--line-soft)}
.trust-strip>.container{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:18px 30px}
.trust-creds{display:flex;flex-wrap:wrap;align-items:center;gap:16px 26px;list-style:none;margin:0;padding:0;
  font-size:.72rem;letter-spacing:.18em;text-transform:uppercase;color:var(--gold)}
/* The dot is the separator, so it belongs to the item and wraps with it - a run of
   text bullets between items strands one at the end of a wrapped line. */
.trust-creds li{display:flex;align-items:center;gap:9px}
.trust-creds li::before{content:"";flex:0 0 auto;width:5px;height:5px;border-radius:50%;background:currentColor}
