/* ===========================================
   Count The Beans — Shared Base Styles
   Loaded by all pages. Page-specific styles
   remain in each file's own <style> block.
   =========================================== */

/* ── Self-hosted fonts (Session 13) ──
   Variable fonts, subset to the same latin range Google's CDN served.
   Files live in docs/fonts/ with their OFL licences. Do not re-add the
   fonts.googleapis.com links to any page — these replace them. */
@font-face {
  font-family: 'DM Sans';
  src: url('../fonts/dmsans-var.woff2') format('woff2');
  font-weight: 100 1000;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'DM Sans';
  src: url('../fonts/dmsans-italic-var.woff2') format('woff2');
  font-weight: 100 1000;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'Playfair Display';
  src: url('../fonts/playfair-var.woff2') format('woff2');
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Playfair Display';
  src: url('../fonts/playfair-italic-var.woff2') format('woff2');
  font-weight: 400 900;
  font-style: italic;
  font-display: swap;
}

/* ── CSS Variables ── */
:root {
  /* Primary palette */
  --plum:        #644173;
  --plum-deep:   #3d1e4a;
  --plum-dark:   #2d1b36;
  --plum-light:  #e8d0f0;
  --blush:       #fdf5f9;
  --cream:       #fdf0f6;
  --rose:        #e8a0c8;
  --gold:        #f9d44a;
  --white:       #ffffff;
  --text-dark:   #2d1b36;
  --text-mid:    #7a5a6a;
  --text-light:  #c0a0b8;  /* light text on DARK backgrounds only (nav) — fails AA on white */
  --text-soft:   #8a6880;  /* muted text on LIGHT backgrounds — 4.8:1 on white, AA pass */
  /* Aliases used in legal pages */
  --dark:        #2d1b36;
  --light:       #f0e6f5;
  --mid:         #7a5a6a;
  --border:      #e8d0f0;

  /* ── Design tokens — promoted from index.html in Session 5 (CSS_SPEC B4/B5)
     so contact, faq, blog and the articles can all use them. index.html
     re-declares an identical block inline; that is harmless (same values)
     and will come out when the homepage is next touched. ── */

  /* Type scale — 1.25 ratio */
  --fs-100: 0.78rem;
  --fs-200: 0.875rem;
  --fs-300: 1rem;
  --fs-400: 1.25rem;
  --fs-500: 1.563rem;
  --fs-600: 1.953rem;
  --fs-700: 2.441rem;
  --fs-800: 3.052rem;

  /* Long-form body — locked by the brief, do not fold into the scale */
  --fs-body: 1.05rem;
  --lh-body: 1.85;
  --measure: 620px;

  /* Radii — three only */
  --r-sm: 4px;
  --r-md: 8px;
  --r-round: 50%;

  /* Borders — two only. 1.5px is gone; it renders inconsistently across DPRs */
  --bw-1: 1px;
  --bw-2: 2px;

  /* Elevation — three levels so the eye can tell what matters */
  --e-1: 0 1px 2px rgba(45,27,54,0.06);
  --e-2: 0 6px 20px rgba(45,27,54,0.10);
  --e-3: 0 18px 50px rgba(45,27,54,0.18);

  /* Section rhythm — deliberate alternation, not uniform padding */
  --sp-dense: 56px;
  --sp-open: 120px;
  --gutter: 48px;

  /* Footer text — solid tints, AA compliant on --plum-dark */
  --footer-text: #cbbcd2;
  --footer-link: #dccce2;

  /* Body text on the tinted grounds. --text-soft (#8a6880) is AA on
     white but drops to 4.34:1 on --cream and 4.48:1 on --blush.
     This is three steps darker and clears 4.5:1 on all three. */
  --text-soft-tint: #82607a;

  /* Fixed-nav clearance. Nav is 18px + 60px logo + 18px = 96px. */
  --nav-h: 96px;
}

/* ── Spacing rhythm — the full ladder. Sessions 6 and 7.

   Both of these steps started life inside index.html's own <style>, so
   ONLY the homepage got them. Every other page kept the desktop values
   all the way down: 120px of vertical padding and a 48px gutter on a
   phone. Session 6 moved the 700px step here; Session 7 moved the
   1100px step, and index.html no longer declares either.

   Order matters. Both are max-width, so on a 375px phone both match and
   the later block must be the tighter one — 1100px first, then 700px.

   --gutter also feeds `nav { padding: 18px var(--gutter) }`, so the
   1100px step is what keeps the nav in proportion on a tablet. Before
   this it sat at 48px on 13 pages and 32px on the homepage. ── */
@media (max-width: 1100px) {
  :root { --gutter: 32px; --sp-open: 88px; --sp-dense: 48px; }
}
@media (max-width: 700px) {
  :root { --gutter: 20px; --sp-open: 64px; --sp-dense: 40px; }
}

/* ── Reset ── */
*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* ── Base ── */
html {
  scroll-behavior: smooth;
}

/* ── Scrollbar ── */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: var(--plum-dark); }
::-webkit-scrollbar-thumb { background: var(--plum); border-radius: 3px; }

/* ══════════════════════════════════════════════════════════
   SKIP LINK — first focusable element on every page.
   Session 5 (CSS_SPEC B6).
   ══════════════════════════════════════════════════════════ */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 300;
  background: var(--plum);
  color: var(--white);
  padding: 12px 20px;
  border-radius: 0 0 var(--r-sm) 0;
  font-size: var(--fs-200);
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { left: 0; }

/* ══════════════════════════════════════════════════════════
   FOCUS — every interactive element, every page.
   Before Session 5 only index.html had focus states; the
   other 13 pages had none at all. (CSS_SPEC B6)
   ══════════════════════════════════════════════════════════ */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid var(--rose);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

nav a:focus-visible,
.footer-links a:focus-visible {
  outline-color: var(--rose);
  outline-offset: 4px;
}

/* ══════════════════════════════════════════════════════════
   BEAN MARK — one component, three scales. Replaces
   .section-chip, the 01/02/03 numerals and the Feather icons.
   Geometry is lifted from .bean-accent in the nav wordmark,
   so it is literally the same shape.
   ══════════════════════════════════════════════════════════ */
.bean-mark {
  display: inline-block;
  position: relative;
  flex: none;
  background: var(--rose);
  border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
  transform: rotate(-15deg);
}
.bean-mark::after {
  content: '';
  position: absolute;
  background: rgba(255,255,255,0.35);
  border-radius: var(--r-round);
  transform: rotate(5deg);
}
.bean-mark--bullet { width: 9px; height: 13px; }
.bean-mark--bullet::after { top: 2px; left: 2px; width: 4px; height: 6px; }

.bean-mark--section { width: 15px; height: 22px; background: var(--plum); }
.bean-mark--section::after { top: 4px; left: 3px; width: 6px; height: 10px; }

.bean-mark--plum { background: var(--plum); }
.bean-mark--rose { background: var(--rose); }

/* Section mark sits where the uppercase pill used to */
.section-mark {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 22px;
}
.section-mark-rule {
  display: block;
  width: 40px;
  height: var(--bw-1);
  background: var(--plum-light);
}

/* One large anchor bean per section, at real size */
.bean-anchor {
  position: absolute;
  pointer-events: none;
  background: rgba(232,160,200,0.16);
  border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
  transform: rotate(-15deg);
}

/* ══════════════════════════════════════════════════════════
   SHARED NAV — one definition for every page.
   Session 5. The footer was consolidated in Session 4 for
   exactly this reason; the header had drifted into SEVEN
   different versions across 14 pages.
   ══════════════════════════════════════════════════════════ */
nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px var(--gutter);
  background: rgba(45, 27, 54, 0.96);
  backdrop-filter: blur(12px);
  border-bottom: var(--bw-1) solid rgba(232, 160, 200, 0.15);
  transition: padding 0.3s ease;
}

/* Nav shrink on scroll. Class toggled by js/nav.js (Session 7).
   Vertical padding ONLY — the horizontal value stays on --gutter so it
   keeps tracking every breakpoint. The old inline-style version this
   replaced hardcoded '48px' on both axes, which overrode the mobile
   nav's 20px and the 1100px --gutter of 32px. See the note in nav.js.

   Scoped to desktop: below 1101px the nav is already at its compact
   12px (see the mobile block further down), so there is nothing to
   shrink and the transition would only cause a jitter.

   Session 11: this moved 901px -> 1101px when the phone button became
   an 8th nav item. It must always match the mobile breakpoint below. */
@media (min-width: 1101px) {
  nav.is-scrolled {
    padding-top: 12px;
    padding-bottom: 12px;
  }
}

.nav-logo { display: flex; align-items: center; }

.nav-links {
  display: flex;
  gap: 32px;
  list-style: none;
  align-items: center;
  margin: 0;
  padding: 0;
}

.nav-links a {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-light);
  text-decoration: none;
  letter-spacing: 0.5px;
  transition: color 0.2s;
}
.nav-links a:hover,
.nav-links a.active { color: var(--rose); }

.nav-cta {
  background: var(--plum) !important;
  color: #fff !important;
  padding: 9px 22px !important;
  border-radius: var(--r-sm) !important;
  font-weight: 600 !important;
  transition: background 0.2s, transform 0.15s !important;
}
.nav-cta:hover {
  background: var(--rose) !important;
  color: var(--plum-dark) !important;
  transform: translateY(-1px);
}

/* ── PHONE BUTTON (Session 11) ──
   Teresa's call: the number has to be visible on every page at all
   times. Same geometry and font size as .nav-cta so the two read as a
   pair, inverted to blush-on-dark so the booking CTA stays the louder
   of the two. Kept inline in the row rather than on a second line —
   a second row would raise --nav-h, and every in-page anchor on all
   14 pages depends on that value for its scroll-margin-top.

   The href is the unformatted number and the label is the spaced one,
   so the dialler gets something it can call and Google gets the same
   string that appears on the Business Profile. */
.nav-phone {
  background: var(--blush) !important;
  color: var(--plum-dark) !important;
  padding: 9px 22px !important;
  border-radius: var(--r-sm) !important;
  font-weight: 600 !important;
  white-space: nowrap;
  transition: background 0.2s, transform 0.15s !important;
}
.nav-phone:hover {
  background: var(--rose) !important;
  color: var(--plum-dark) !important;
  transform: translateY(-1px);
}

/* ── HAMBURGER ── */
.nav-burger {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
  z-index: 101;
}
.nav-burger span {
  display: block;
  width: 24px;
  height: 2px;
  background: var(--rose);
  border-radius: 2px;
  transition: all 0.25s ease;
}
.nav-burger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-burger.open span:nth-child(2) { opacity: 0; }
.nav-burger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ── NAV: mobile ──
   Session 5 moved this 700px -> 900px: six links plus the CTA measure
   ~601px, and with the logo (~200px) and gutters the row needed ~897px.

   Session 11 moved it 900px -> 1100px. The phone button is an 8th item
   at ~130px, plus its 32px gap, so the row now needs ~1059px. Left at
   900px it would have cramped between 900 and 1059 — the same failure
   Session 5 fixed at 700px, and the same one Session 7 found when the
   nav.is-scrolled guard was left behind at a stale breakpoint.
   1100px is deliberate: it is the step --gutter already uses, so the
   nav collapses exactly where the page gutters tighten.

   If a nav item is ever added or removed, re-measure and move BOTH
   this and the min-width guard above. They must always be 1 apart. */
@media (max-width: 1100px) {
  nav { padding: 12px 20px; }
  .nav-burger { display: flex; }
  .nav-links {
    display: none;
    position: absolute;
    top: 100%; left: 0; right: 0;
    flex-direction: column;
    gap: 0;
    background: rgba(45,27,54,0.99);
    border-bottom: var(--bw-1) solid rgba(232,160,200,0.15);
    padding: 8px 0 16px;
  }
  .nav-links.open { display: flex; }
  .nav-links li { width: 100%; }
  .nav-links a { display: block; padding: 13px 24px; font-size: 15px; }
  .nav-cta { margin: 8px 24px 0; text-align: center; }
  /* Phone sits above the booking CTA in the dropdown — on a phone it is
     the lower-effort action of the two, so it reads first. */
  .nav-phone { margin: 8px 24px 0; text-align: center; }
}

/* ══════════════════════════════════════════════════════════
   ANCHOR CLEARANCE — the nav is fixed and 96px tall, so any
   in-page jump would otherwise land under it.
   ══════════════════════════════════════════════════════════ */
#main { scroll-margin-top: var(--nav-h); }
/* #book lands on the booking embed, whose section carries its own
   --sp-dense of top padding above the bean mark. Offsetting by the nav
   height alone pushed the bean ~70px below the header. Subtracting the
   section padding puts the bean mark itself 20px under the nav at every
   breakpoint — the token shrinks 56 -> 48 -> 40px as the viewport
   narrows, and this calc tracks it, so the result is a constant 116px. */
#book { scroll-margin-top: calc(var(--nav-h) - var(--sp-dense) + 20px); }
/* Session 11 — the two new CTA landing points. Every contact button on
   the site now aims at #contact, so it has to clear the fixed nav on
   contact.html and on the homepage band alike. #form is the embedded
   Google Form, which "contact form" links jump straight to. */
#contact { scroll-margin-top: calc(var(--nav-h) + 16px); }
#form    { scroll-margin-top: calc(var(--nav-h) + 16px); }

/* ══════════════════════════════════════════════════════════
   CONTACT DETAIL ITEM — bean + label + value.
   Used by the homepage contact band and by contact.html.
   Promoted here in Session 5 now that two pages need it.
   ══════════════════════════════════════════════════════════ */
.contact-item { display: flex; gap: 12px; align-items: flex-start; }
.contact-item .bean-mark { margin-top: 6px; }

.contact-label {
  font-size: var(--fs-100);
  font-weight: 700;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--text-mid);
  margin-bottom: 5px;
}

.contact-value {
  font-size: var(--fs-300);
  font-weight: 600;
  line-height: 1.5;
  color: var(--text-dark);
}
.contact-value a { color: var(--plum); text-decoration: none; }
.contact-value a:hover { text-decoration: underline; }
.contact-value .soft {
  display: block;
  font-size: var(--fs-200);
  font-weight: 400;
  color: var(--text-soft);
  margin-top: 3px;
}

/* ── Contact band — the rest of the homepage contact layout,
   promoted here in Session 6 so contact.html uses the identical
   thing rather than a second copy. index.html re-declares these
   inline with the same values; harmless, and it comes out when
   the homepage is next touched. ── */
.contact-lede {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-soft);
  max-width: var(--measure);
  margin-bottom: 40px;
}
.contact-band {
  display: grid;
  grid-template-columns: repeat(3, auto) max-content;
  gap: 36px;
  align-items: start;
  border-top: var(--bw-1) solid var(--plum-light);
  border-bottom: var(--bw-1) solid var(--plum-light);
  padding: 32px 0;
}
.contact-action { justify-self: end; align-self: center; }
.contact-more {
  margin-top: 26px;
  font-size: var(--fs-200);
  color: var(--text-soft);
}
.contact-more a { color: var(--plum); font-weight: 600; text-decoration: none; }
.contact-more a:hover { text-decoration: underline; }

/* Breakpoints deliberately identical to index.html's inline copy
   (1100px / 700px), so the shared rules and the homepage agree
   exactly and nothing shifts when that inline copy is removed. */
@media (max-width: 1100px) {
  .contact-band { grid-template-columns: 1fr 1fr; gap: 28px; }
  .contact-action { justify-self: start; grid-column: 1 / -1; }
}
@media (max-width: 700px) {
  .contact-band { grid-template-columns: 1fr; gap: 24px; }
  .contact-action { grid-column: auto; }
  .contact-action .btn-primary { width: 100%; justify-content: center; }
}

/* ── Logo Images ──
   Heights keep logos proportional without
   increasing nav or footer height.          */

.nav-logo-img {
  height: 60px;
  width: auto;
  object-fit: contain;
  display: block;
}

.footer-logo-img {
  height: 60px;
  width: auto;
  object-fit: contain;
  display: block;
}

.tpb-logo {
  height: 95px;
  width: auto;
  object-fit: contain;
  display: block;
}

/* ══════════════════════════════════════════════════════════
   SHARED FOOTER — one definition for every page.
   Was previously re-declared inline on each page with
   variations; that was the mechanical cause of the footer
   drifting between pages.
   ══════════════════════════════════════════════════════════ */
footer {
  display: block;
  background: var(--plum-dark);
  padding: 24px 48px 18px;
  border-top: 1px solid rgba(232,160,200,0.1);
}

/* Three columns keep the links dead-centre now the logo has gone
   (the nav is pinned, so the logo is on screen anyway). */
.footer-top {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  max-width: 1200px;
  margin: 0 auto;
  width: 100%;
  gap: 24px;
}

/* NAV LINKS */
.footer-links {
  display: flex;
  justify-content: center;
  gap: 16px;
  flex-wrap: wrap;
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer-links a {
  font-size: 12px;
  color: #dccce2;            /* 10.43:1 on --plum-dark */
  text-decoration: none;
}

.footer-links a:hover {
  color: #e8a0c8;
}

.footer-links a:focus-visible {
  outline: 3px solid var(--rose);
  outline-offset: 4px;
}

/* BADGE ROW — 46px Xero badges, and the TPB portrait badge at 70px
   canvas so it renders the same 46px of visible logo beside them. */
.footer-badges {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 11px;
  flex-wrap: nowrap;
  margin-right: -40px;       /* clears the centred copyright beneath */
}

.footer-xero {
  height: 46px;
  width: auto;
  display: block;
  flex: none;
}

.footer-badges .tpb-logo {
  height: 70px;
  width: auto;
  flex: none;
  margin-left: 4px;
}

/* COPYRIGHT WRAPPER */
.footer-bottom {
  width: 100%;
  margin-top: -8px;
  padding-top: 0px;
  padding-bottom: 4px;
  border-top: 1px solid rgba(232,160,200,0.1);

  display: flex;
  justify-content: center;
}

/* SOCIAL LOGOS — left slot, mirroring the badge row on the right.
   Teresa's spec: Facebook sits the same distance from the left edge as
   the TPB badge does from the right, and LinkedIn sits the same distance
   from Facebook as the Xero migration badge does from TPB.

   The maths, so it is not re-derived: .footer-badges pulls out -40px on
   the right, so .footer-social pulls out -40px on the left. The gap
   between the last Xero badge and TPB is the 11px row gap plus the
   4px margin-left on .tpb-logo = 15px, so this row uses gap: 15px.

   Both logos are full-bleed like the Xero badges — no transparent
   padding — so 46px of canvas is 46px of visible logo and they line up
   without the negative-margin correction the TPB badge needs. */
.footer-social {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 15px;
  flex-wrap: nowrap;
  margin-left: -40px;
}

.footer-social img {
  height: 46px;
  width: auto;
  display: block;
  flex: none;
}

.footer-social a {
  display: flex;
  border-radius: var(--r-sm);
}

.footer-social a:focus-visible {
  outline: 3px solid var(--rose);
  outline-offset: 4px;
}

/* SOCIAL ICONS under the phone number — index.html and contact.html.
   Session 11: started life inline on index, moved here the moment it
   landed on a second page. 26px matches the cap height of
   .contact-value so the row sits square under the number. */
.contact-social {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
}
.contact-social a {
  display: flex;
  border-radius: var(--r-sm);
  transition: transform 0.15s, opacity 0.2s;
}
.contact-social a:hover { transform: translateY(-2px); opacity: 0.82; }
.contact-social a:focus-visible { outline: 3px solid var(--rose); outline-offset: 3px; }
.contact-social img { height: 26px; width: auto; display: block; }

/* COPYRIGHT TEXT */
.footer-copy-wrap {
  font-size: 12px;
  color: #cbbcd2;            /* 8.82:1 on --plum-dark, was 2.26:1 */
  letter-spacing: 0.3px;
  line-height: 1.6;
  text-align: center;
}

/* Footer at tablet and below */
@media (max-width: 700px) {
  .footer-top {
    grid-template-columns: 1fr;
    justify-items: center;
    gap: 18px;
  }
  /* .footer-spacer is gone — Session 11 replaced the empty left slot
     with .footer-social on all 14 pages. */
  .footer-badges { justify-content: center; gap: 9px; margin-right: 0; }
  .footer-xero { height: 38px; }
  .footer-badges .tpb-logo { height: 58px; }
  /* Stacked single column here, so the mirrored pull-out has nothing to
     mirror — centre the social row and drop it to the badge height. */
  .footer-social { justify-content: center; gap: 12px; margin-left: 0; }
  .footer-social img { height: 38px; }
}

/* FOOTER TEXT — legal pages (privacy, T&C) */
.footer-text {
  flex: 1;
  text-align: center;
}
.footer-text p {
  font-size: 0.8rem;
  color: rgba(255,255,255,0.3);
  line-height: 1.7;
}
.footer-text a {
  color: var(--rose);
  text-decoration: none;
}

/* ── MOBILE OPTIMIZATION: Small phones (≤480px) ── */
@media (max-width: 480px) {
  nav {
    padding: 12px 16px;
  }

  #hero, #pain, #why, #transform, #packages, #cta,
  #contact, #privacy, #about {
    padding-left: 16px;
    padding-right: 16px;
  }

  /* NOTE: the .contact-right iframe height lives in ONE place now —
     see "CONTACT FORM RESPONSIVE" at the foot of this file. Two rules
     used to set it here (400px) and there (950px); the later one won,
     so the 400px was dead code that read as if it were live. */

  .pkg-grid-bottom {
    width: 100%;
    margin-left: 0;
    margin-right: 0;
  }


  /* Footer — reduce padding (layout is handled by the grid above) */
  footer {
    padding: 16px 16px;
  }

  .footer-links {
    gap: 12px;
  }
}

/* ── Small phones (≤480px) ── */
/* Session 9: the bare .blog-hero and .blog-wrap rules that used to sit
   here were deleted. Both were inert — .blog-layout .blog-hero and
   .blog-layout .blog-wrap (0,2,0) outrank a bare class (0,1,0) at every
   width, and a media query carries no specificity of its own. Any blog
   override placed here must be scoped .blog-layout / .blog-index to win. */
@media (max-width: 480px) {
  #back-to-top {
    right: 12px;
  }
}

/* .about-stats removed in Session 5 (CSS_SPEC B3). The homepage
   redesign replaced the stats block with the credentials band, so
   this rule — five !important declarations — matched nothing. */

/* ── BACK TO TOP BUTTON ── */
#back-to-top {
  position: fixed;
  right: 16px;
  bottom: 20px;
  z-index: 200;
  background: var(--plum);
  color: var(--white);
  border: none;
  border-radius: 50%;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(45,27,54,0.35);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease, background 0.2s ease, transform 0.2s ease;
  font-size: 20px;
  line-height: 1;
  font-weight: bold;
}

/* Shown only once the page has scrolled — class toggled by JS on each page */
#back-to-top.visible {
  opacity: 1;
  pointer-events: auto;
}

#back-to-top:hover {
  background: var(--rose);
  color: var(--plum-dark);
  transform: translateY(-2px);
}

#back-to-top:focus-visible {
  outline: 3px solid var(--rose);
  outline-offset: 3px;
}

/* ── CONTACT FORM RESPONSIVE — REMOVED, Session 6.
   This block sized the two iframes on contact.html (booking calendar
   and Google Form). Both embeds were replaced by links, so all six
   rules — .contact-right, .contact-right iframe, .booking-embed,
   .contact-form-embed — matched nothing. The Session 5 comment on the
   form rule said "if the embed goes, this rule goes with it"; it has.
   The CSS_SPEC B2 height conflict it fixed no longer exists.
   The form is still reachable — it opens on Google from the
   "contact form" link in the contact band. ── */

/* ══════════════════════════════════════════════════════════
   BUTTONS — one definition for every page. Session 6.

   Same reason the nav and footer were consolidated: .btn-primary
   had been written out separately in ELEVEN files and had already
   drifted — three paddings (14px 30px, 15px 30px, 15px 32px), two
   sidebar colours, two font sizes. These values are the homepage's,
   which is the design reference.

   Pages still carrying an inline copy override this harmlessly
   until each is cleaned up. Do NOT re-declare these in a page's
   inline <style> — that is what caused the nav and footer drift.

   Page-specific overrides that are legitimate and stay page-scoped:
     #cta .btn-primary / #faq-cta .btn-primary — invert to white,
     because a plum button on a plum ground disappears.
   ══════════════════════════════════════════════════════════ */
.btn-primary, .btn-ghost, .btn-quiet {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: 'DM Sans', sans-serif;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 15px 30px;
  border-radius: var(--r-sm);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: all 0.2s ease;
}
.btn-primary {
  background: var(--plum);
  border: var(--bw-2) solid var(--plum);
  color: #fff;
  box-shadow: var(--e-1);
}
.btn-primary:hover {
  background: var(--rose);
  border-color: var(--rose);
  color: var(--plum-dark);
  transform: translateY(-2px);
  box-shadow: var(--e-2);
}
.btn-ghost {
  background: transparent;
  border: var(--bw-2) solid rgba(232,160,200,0.4);
  color: var(--rose);
  font-weight: 600;
}
.btn-ghost:hover {
  border-color: var(--rose);
  background: rgba(232,160,200,0.1);
  transform: translateY(-2px);
}
/* Quiet secondary — a link, not a competing button */
.btn-quiet {
  background: none;
  border: none;
  padding: 15px 4px;
  color: var(--plum);
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--fs-300);
  font-weight: 600;
  border-bottom: var(--bw-2) solid var(--plum-light);
  border-radius: 0;
}
.btn-quiet:hover { border-bottom-color: var(--plum); }

/* ══════════════════════════════════════════════════════════
   SPEC LIST — label on the left, detail on the right.
   Session 6. Started life as .key-dates in blog.html's sidebar;
   promoted here so the article pages can use the same thing for
   "Key differences at a glance" and "Quick reference".

   Two real columns, so the labels and the detail line up instead
   of each label's width shoving its own text along. No bullet —
   the label IS the marker.

   --spec-label sets the label column. Override it per use rather
   than writing a second component:
     .spec-list--tight   78px, for short dates in the sidebar
     default             150px min / 32% max, for prose labels
   ══════════════════════════════════════════════════════════ */
.spec-list { list-style: none; padding: 0; margin: 0; }
.spec-list li {
  display: grid;
  grid-template-columns: minmax(150px, 32%) 1fr;
  align-items: start;
  gap: 4px 16px;
  padding: 10px 0;
  border-bottom: var(--bw-1) solid var(--plum-light);
}
.spec-list li:last-child { border-bottom: none; padding-bottom: 0; }
/* Kills the bean bullet inherited from .checklist-box — in a grid
   the ::before would become a third column and break the alignment. */
.spec-list li::before { content: none !important; display: none !important; }
.spec-list .spec-label {
  font-size: var(--fs-200);
  font-weight: 700;
  color: var(--plum);
  line-height: 1.5;
}
.spec-list .spec-text {
  font-size: var(--fs-200);
  color: var(--text-soft-tint);
  line-height: 1.6;
}
.spec-list--tight li { grid-template-columns: 78px 1fr; }
.spec-list--tight .spec-label { font-size: var(--fs-100); white-space: nowrap; padding-top: 2px; }

/* Below this the two columns are too narrow to read — stack them,
   label above detail, and let long labels wrap. */
@media (max-width: 560px) {
  .spec-list li { grid-template-columns: 1fr; gap: 2px; }
  .spec-list--tight li { grid-template-columns: 1fr; }
  .spec-list--tight .spec-label { white-space: normal; }
}

/* ══════════════════════════════════════════════════════════
   BLOG — SHARED. blog.html + the 8 article pages. Session 8.

   Both page types now carry <body class="blog-layout …"> plus
   a second class saying which one they are:
       blog.html         → blog-layout blog-index
       docs/blog/*.html  → blog-layout article-page

   Session 6 pulled the 8 article pages' byte-identical <style>
   blocks into one definition. blog.html was deliberately left
   out at the time because its values differed. Session 8 found
   that only 6 of the 29 shared rules actually differed on
   purpose — the other 23 were the sixth instance of the
   paste-per-page pattern, and one of them had already
   diverged into a live bug (see .blog-wrap below).

   What belongs here: anything true of BOTH an index and an
   article. Anything else goes in the scoped blocks underneath.
   ══════════════════════════════════════════════════════════ */
body.blog-layout {
  font-family: 'DM Sans', sans-serif;
  color: var(--text-dark);
  background: var(--white);
  overflow-x: hidden;
}

/* ── Hero ── */
.blog-layout .blog-hero {
  background: var(--plum-dark);
  padding: 160px var(--gutter) 60px;
  position: relative;
  overflow: hidden;
}
/* Real bean at real size, replacing the two stacked radial
   gradients — same treatment as contact.html and faq.html */
.blog-layout .blog-hero .bean-anchor {
  width: 300px; height: 430px;
  right: -60px; top: -80px;
  background: rgba(232,160,200,0.12);
}
.blog-layout .blog-hero-inner { max-width: 760px; position: relative; z-index: 1; }
/* Section mark on a dark ground — rose bean, tinted rule */
.blog-layout .blog-hero .bean-mark--section { background: var(--rose); }
.blog-layout .blog-hero .section-mark-rule { background: rgba(232,160,200,0.4); }
.blog-layout .blog-hero h1 {
  font-family: 'Playfair Display', serif;
  font-size: clamp(1.8rem, 4vw, var(--fs-700));
  font-weight: 900; line-height: 1.15;
  color: var(--white); margin-bottom: 12px;
}
.blog-layout .blog-hero h1 em { font-style: italic; color: var(--rose); }

/* ── Layout ──
   The padding is tokenised deliberately. blog.html used to
   hardcode `72px 48px` while its own hero used var(--gutter),
   so between 901–1100px the heading sat 32px from the edge
   and the cards below it sat 48px — a visible 16px step.
   Same bug class as the Session 7 nav one: a hardcoded copy
   of a token stops tracking the moment the token moves. ── */
.blog-layout .blog-wrap {
  max-width: 1100px; margin: 0 auto;
  padding: var(--sp-dense) var(--gutter) var(--sp-open);
  display: grid; grid-template-columns: 1fr 320px;
  gap: 64px; align-items: start;
}

/* ── Article meta ── */
.blog-layout .article-meta {
  display: flex; align-items: center; gap: 16px;
  margin-bottom: 14px; flex-wrap: wrap;
}
.blog-layout .article-tag {
  font-size: var(--fs-100); font-weight: 700;
  letter-spacing: 2px; text-transform: uppercase;
  color: var(--plum); background: var(--plum-light);
  padding: 4px 12px; border-radius: var(--r-sm);
}
.blog-layout .article-tag.rose { background: rgba(232,160,200,0.2); color: var(--plum-deep); }
.blog-layout .article-date,
.blog-layout .article-read { font-size: var(--fs-100); color: var(--text-soft); font-weight: 500; }

/* ══════════════════════════════════════════════════════════
   BLOG INDEX ONLY — blog.html. Session 8.
   The hero is larger because it is an index, not an article.
   ══════════════════════════════════════════════════════════ */
.blog-index .blog-hero { padding: 160px var(--gutter) 80px; }
.blog-index .blog-hero .bean-anchor {
  width: 340px; height: 490px;
  right: -70px; top: -90px;
}
.blog-index .blog-hero h1 {
  font-size: clamp(2.4rem, 5vw, var(--fs-800));
  line-height: 1.1; margin-bottom: 20px;
  max-width: 90ch;
}
.blog-index .blog-hero p {
  font-size: var(--fs-body); line-height: 1.8;
  color: rgba(255,255,255,0.65);
  max-width: 520px; font-weight: 300;
}

/* Scoped to .article-card, NOT to `article`. On this page
   <article> is a listing card; on an article page it is the
   post body. Same tag, two different jobs — one shared
   selector would either shrink the posts to card size or
   inflate the card blurbs to body size. */
.blog-index .article-card h2 {
  font-family: 'Playfair Display', serif;
  font-size: clamp(1.6rem, 3vw, var(--fs-600));
  font-weight: 900; line-height: 1.2;
  color: var(--text-dark); margin-bottom: 16px;
}
.blog-index .article-card h2 em { font-style: italic; color: var(--plum); }
.blog-index .article-card p {
  font-size: var(--fs-300); line-height: 1.85;
  color: var(--text-mid); margin-bottom: 18px;
}

/* ══════════════════════════════════════════════════════════
   ARTICLE PAGES ONLY — the 8 files in docs/blog/.
   Post body typography and the in-article callouts.
   ══════════════════════════════════════════════════════════ */
.article-page .blog-hero .back-link {
  font-size: var(--fs-200);
  color: rgba(255,255,255,0.55);
  text-decoration: none;
}
.article-page .blog-hero .back-link:hover { color: var(--rose); }

/* ── Body copy ── */
.article-page article h2 {
  font-family: 'Playfair Display', serif;
  font-size: clamp(1.6rem, 3vw, var(--fs-600));
  font-weight: 900; line-height: 1.2;
  color: var(--text-dark); margin-bottom: 16px;
}
.article-page article h2 em { font-style: italic; color: var(--plum); }
.article-page article h3 {
  font-family: 'Playfair Display', serif;
  font-size: var(--fs-400); font-weight: 700;
  color: var(--text-dark); margin: 36px 0 12px;
}
.article-page article p {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-mid);
  margin-bottom: 18px;
}
.article-page article strong { color: var(--text-dark); font-weight: 600; }

/* ── Callouts. The 4px left accents are a deliberate design
   device, not a border weight, so they stay 4px/3px — the
   --bw-1/--bw-2 tokens govern hairlines and outlines. ── */
.article-page .checklist-box {
  background: var(--cream);
  border-left: 4px solid var(--plum);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  padding: 28px 32px; margin: 28px 0;
}
.article-page .checklist-box .box-title {
  font-family: 'Playfair Display', serif;
  font-size: var(--fs-400); font-weight: 700;
  color: var(--text-dark); margin-bottom: 16px;
}
.article-page .checklist-box ul { list-style: none; }
.article-page .checklist-box ul li {
  font-size: var(--fs-300); line-height: 1.7;
  color: var(--text-soft-tint);   /* AA on --cream; --text-mid is borderline */
  padding: 8px 0; display: flex; align-items: flex-start; gap: 12px;
  border-bottom: var(--bw-1) solid var(--plum-light);
}
.article-page .checklist-box ul li:last-child { border-bottom: none; }
/* Was a ✦ glyph. Now the same bean bullet the homepage uses. */
.article-page .checklist-box ul li::before {
  content: ''; flex-shrink: 0; margin-top: 7px;
  width: 9px; height: 13px; background: var(--plum);
  border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
  transform: rotate(-15deg);
}

.article-page .alert-box {
  background: rgba(100,65,115,0.06);
  border: var(--bw-1) solid var(--plum-light);
  border-radius: var(--r-md);
  padding: 24px 28px; margin: 28px 0;
  display: flex; gap: 16px; align-items: flex-start;
}
/* Was a ⚠️ emoji. The homepage carries zero emoji. */
.article-page .alert-box .bean-mark { margin-top: 5px; }
.article-page .alert-box p { margin-bottom: 0; }

.article-page .pull-quote {
  border-left: 3px solid var(--rose);
  padding: 16px 24px; margin: 32px 0;
}
.article-page .pull-quote p {
  font-family: 'Playfair Display', serif;
  font-size: var(--fs-400); font-style: italic;
  color: var(--plum-dark); line-height: 1.6; margin-bottom: 0;
}

/* ── Inline pointer ── */
.article-page .sidebar-pointer {
  display: flex; align-items: center; gap: 16px;
  background: var(--cream);
  border: var(--bw-1) solid var(--plum-light);
  border-radius: var(--r-md);
  padding: 24px 28px; margin: 40px 0;
  text-decoration: none;
  transition: border-color 0.2s, background 0.2s;
}
.article-page .sidebar-pointer:hover { border-color: var(--plum); background: rgba(100,65,115,0.04); }
.article-page .sidebar-pointer .pointer-text {
  font-family: 'DM Sans', sans-serif;
  font-size: var(--fs-300); color: var(--text-dark);
  font-weight: 500; line-height: 1.5;
}
.article-page .sidebar-pointer .pointer-text span { color: var(--plum); font-weight: 700; }

/* ── Sidebar — shared ── */
.blog-layout .sidebar-card {
  background: var(--cream);
  border: var(--bw-1) solid var(--plum-light);
  border-radius: var(--r-md);
  padding: 28px; margin-bottom: 28px;
}
/* Plum on BOTH page types as of Session 8. It was cream on the
   articles and plum on the index; Teresa's call was to match the
   index. One plum block in a column of cream cards is the
   pattern the index already used. */
.blog-layout .sidebar-cta {
  background: var(--plum);
  border-radius: var(--r-md);
  padding: 28px; margin-bottom: 28px;
  text-align: center;
}
.blog-layout .sidebar-cta h4 {
  font-family: 'Playfair Display', serif;
  font-size: var(--fs-400); font-weight: 700;
  color: var(--white); margin-bottom: 10px;
}
.blog-layout .sidebar-cta p {
  font-size: var(--fs-200);
  color: rgba(255,255,255,0.7);
  line-height: 1.65; margin-bottom: 18px;
}
.blog-layout .sidebar-title {
  font-family: 'Playfair Display', serif;
  font-size: var(--fs-300); font-weight: 700;
  color: var(--text-dark); margin-bottom: 16px;
  padding-bottom: 10px;
  border-bottom: var(--bw-1) solid var(--plum-light);
}
.blog-layout .sidebar-card ul { list-style: none; padding: 0; }
.blog-layout .sidebar-card ul li a {
  font-size: var(--fs-200); color: var(--plum);
  text-decoration: none; font-weight: 500;
  line-height: 1.5; display: block;
}
.blog-layout .sidebar-card ul li a:hover { text-decoration: underline; }

/* The index's Key Dates card uses the shared .spec-list, so its
   plain lists need :not(.spec-list) — .sidebar-card ul li is
   more specific than .spec-list li and would otherwise win on
   padding and borders. The article sidebars hold no .spec-list,
   so theirs is unconditional. */
.article-page .sidebar-card ul li { padding: 10px 0; border-bottom: var(--bw-1) solid var(--plum-light); }
.article-page .sidebar-card ul li:last-child { border-bottom: none; }
.blog-index .sidebar-card ul:not(.spec-list) li { padding: 8px 0; border-bottom: var(--bw-1) solid var(--plum-light); }
.blog-index .sidebar-card ul:not(.spec-list) li:last-child { border-bottom: none; padding-bottom: 0; }

/* .btn-primary itself comes from the shared BUTTONS block above.
   Only the full-width sidebar variant is declared here. It sits
   on a plum card, so the button inverts — rose on plum rather
   than plum on white, exactly as the homepage does on its dark
   sections. Both CTA links on the article pages carry
   .btn-primary; an unstyled <a> is not legible on this ground. */
.blog-layout .sidebar-cta .btn-primary {
  display: flex; justify-content: center; width: 100%;
  text-align: center; white-space: normal;
  background: var(--rose); border-color: var(--rose); color: var(--plum-dark);
  padding: 15px 24px; margin-top: 4px;
}
.blog-layout .sidebar-cta .btn-primary:hover {
  background: var(--white); border-color: var(--white); color: var(--plum-dark);
}

@media (max-width: 900px) {
  .blog-layout .blog-wrap { grid-template-columns: 1fr; gap: 48px; }
  .blog-layout .blog-hero { padding: 140px 24px 48px; }
  /* The index hero carries a lede paragraph under the h1, so it
     keeps a little more room beneath it. */
  .blog-index .blog-hero { padding: 130px 24px 60px; }
}

/* ══════════════════════════════════════════════════════════
   REDUCED MOTION — every page. (CSS_SPEC B7.)
   Must come last so it beats the scroll-behavior and the
   transitions declared above it. Before Session 5 only
   index.html honoured this preference.
   ══════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  html { scroll-behavior: auto; }
  .hero-copy, .hero-portrait { animation: none; opacity: 1; transform: none; }
}