
/* ============================================
   COMMON.CSS — Single source of truth
   For dev sites at:
     dev.horsforthlocallottery.co.uk
     dev.horsforthtownteam.co.uk
     dev.admin.horsforthtownteam.co.uk
   Mobile-first. Add new sections as components are built.
   Last cleaned: 2026-04-27
   ============================================ */

/* ============================================
   DESIGN TOKENS
   ============================================ */
:root {
  --hll-green: #1B3A2D;
  --hll-gold: #D4A843;
  --color-text: #1a1a1a;
  --color-text-inverse: #ffffff;
  --color-bg: #ffffff;
  --color-bg-muted: #f5f0e8;
  --font-heading: 'Ubuntu', system-ui, -apple-system, sans-serif;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 32px;
  --space-xxl: 48px;
  --tap-target: 44px;
  --hll-radius: 4px;
  --hll-transition-panel: transform 280ms ease;
  --hll-transition-hover: 150ms ease;
}

/* ============================================
   RESET / BASE
   ============================================ */
*, *::before, *::after { box-sizing: border-box; }
html { height: 100%; overflow-x: hidden; }
body { margin: 0; padding: 0; min-height: 100vh; display: flex; flex-direction: column; overflow-x: hidden; }
body {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  color: var(--color-text);
  background: var(--color-bg-muted);
}
h1, h2, h3, h4, h5, h6 { font-family: var(--font-heading); margin: 0 0 var(--space-md); }
a { color: inherit; }
img { max-width: 100%; display: block; }

/* ============================================
   PAGE MAIN (between header and footer/map)
   ============================================ */
main,
.page-main { flex: 1 0 auto; }

.shared-header,
.shared-map { flex-shrink: 0; }
.shared-footer { flex-shrink: 0; margin-top: auto; }

/* ============================================
   SHARED HEADER
   ============================================ */
.shared-header {
    background-color: var(--hll-green);
    position: sticky;
    top: 0;
    z-index: 900;
    width: 100%;
}

.shared-header__inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 var(--space-md);
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
}

.shared-header__hamburger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--tap-target);
    height: var(--tap-target);
    background: transparent;
    border: none;
    cursor: pointer;
    flex-shrink: 0;
    padding: 0;
    margin: 0;
    border-radius: var(--hll-radius);
    transition: background-color var(--hll-transition-hover);
}

.shared-header__hamburger:hover {
    background-color: rgba(255,255,255,0.1);
}

.shared-header__brand-link {
    color: inherit;
    text-decoration: none;
    display: inline-block;
    flex-grow: 1;
}

.shared-header__brand {
    color: var(--color-text-inverse);
    font-family: var(--font-heading);
    font-size: 24px;
    font-weight: 400;
    line-height: 1;
    letter-spacing: -0.01em;
    white-space: nowrap;
    flex: 1 1 auto;
}

.shared-header__brand strong {
    font-weight: 700;
}

.shared-header__login {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--tap-target);
    height: var(--tap-target);
    padding: 0;
    margin: 0;
    border: none;
    color: var(--color-text-inverse);
    text-decoration: none;
    flex-shrink: 0;
    border-radius: var(--hll-radius);
    transition: background-color var(--hll-transition-hover);
}

.shared-header__login:hover {
    background-color: rgba(255,255,255,0.1);
}

/* ============================================
   SHARED DRAWER (hamburger menu panel)
   ============================================ */
.hll-nav__backdrop {
    position: fixed;
    inset: 0;
    z-index: 9998;
    background: rgba(0, 0, 0, 0.45);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--hll-transition-hover);
}

.hll-nav__backdrop--open {
    opacity: 1;
    pointer-events: auto;
}

.hll-nav {
    position: fixed;
    top: 0;
    left: 0;
    width: 280px;
    max-width: 80vw;
    height: 100vh;
    background: var(--hll-green);
    color: var(--color-text-inverse);
    z-index: 9999;
    display: flex;
    flex-direction: column;
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.45);
    transform: translateX(-100%);
    transition: var(--hll-transition-panel);
    padding-top: 60px;
    overflow-y: auto;
}


.shared-drawer__panel,
.hll-nav__panel {
    background: var(--hll-green);
}
.hll-nav--open {
    transform: translateX(0) !important;
}

.hll-nav__header {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 1.25rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}

.hll-nav__close {
    background: none;
    border: none;
    color: rgba(255, 255, 255, 0.7);
    cursor: pointer;
    padding: 6px;
    border-radius: var(--hll-radius);
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
}

.hll-nav__close:hover {
    background: rgba(255, 255, 255, 0.1);
    color: var(--color-text-inverse);
}

.hll-nav__section {
    /* Vertical padding tightened (2026-05-04, third pass) so the gap
       between sections reads ~7px end-to-end. Was var(--space-sm) = 8px;
       now 2px. Horizontal padding (var(--space-md)) unchanged. */
    padding: 2px var(--space-md);
}

.hll-nav__section ul,
.hll-nav__section ol {
    list-style: none;
    padding-left: 0;
    margin: 0;
}

.hll-nav__item {
    margin: 0;
}

.hll-nav__section-title {
    font-family: var(--font-heading);
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    /* Margins tightened (2026-05-04, third pass). Was var(--space-md)
       top + var(--space-md) bottom = 16+16. Now 2px top + 8px bottom —
       the title sits close to the section above and gives just enough
       breathing room to the items below. */
    margin: 2px 0 var(--space-sm);
    color: rgba(255,255,255,0.6);
}

.hll-nav__section + .hll-nav__section {
    /* Inter-section gap halved twice now (2026-05-04). Was 4px (/4); halved
       to 2px (/8); halved again to 1px (/16). Intent: drawer reads as one
       continuous list, with a hairline divider only visible because the
       drawer background shows through between sections. */
    margin-top: calc(var(--space-md) / 16);
}

.hll-nav__link {
    display: block;
    padding: 6px 0 6px var(--space-sm);
    font-family: var(--font-body);
    font-size: 14px;
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: 0.01em;
    color: var(--color-text-inverse);
    text-decoration: none;
    transition: color var(--hll-transition-hover);
}

.hll-nav__link:hover {
    color: var(--color-text-inverse);
    text-decoration: underline;
}

/* ============================================
   SHARED FOOTER
   ============================================ */
.shared-footer {
    background-color: var(--hll-green);
    color: var(--color-text-inverse);
    width: 100%;
}

.shared-footer__inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: var(--space-lg) var(--space-md);
    text-align: center;
}

.shared-footer__intro {
    font-family: var(--font-body);
    font-size: 0.875rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.45);
    max-width: 700px;
    margin: 0 auto var(--space-lg);
}

.shared-footer__copyright {
    font-family: var(--font-body);
    font-size: 14px;
    color: rgba(255,255,255,0.85);
    margin: 0 0 6px;
}

.shared-footer__copyright-link {
    color: var(--color-text-inverse);
    text-decoration: underline;
    text-underline-offset: 2px;
    text-decoration-color: rgba(255,255,255,0.5);
    transition: text-decoration-color var(--hll-transition-hover);
}

.shared-footer__copyright-link:hover {
    text-decoration-color: rgba(255,255,255,1);
}

.shared-footer__credit {
    font-family: var(--font-body);
    font-size: 12px;
    color: rgba(255,255,255,0.7);
    margin: 0;
}

.shared-footer__credit-link {
    color: inherit;
    text-decoration: none;
}

.shared-footer__credit-link:hover {
    text-decoration: underline;
}

/* ============================================
   SHARED MAP
   ============================================ */
.shared-map {
    width: 100vw;
    height: 80vh;
    margin-left: calc(50% - 50vw);
    position: relative;
    display: block;
}

.shared-map .leaflet-container {
    width: 100%;
    height: 100%;
}

.shared-map + footer,
.shared-map + .shared-footer {
    margin-top: 0;
}
/* The + sibling rule above only matches when .shared-map is the
   IMMEDIATE previous sibling of the footer — but on both retailer page
   variants there's a <script> between them (legacy), or the map is
   nested inside <section class="template-section--map"> inside <main>
   (templated). In both cases body's flex-column + min-height:100vh
   plus .shared-footer { margin-top: auto } pushes the footer away
   from the map's bottom edge. :has() detects the map anywhere on the
   page and zeroes the footer's auto-margin — leaves the sticky-footer
   behavior intact on every other page (legal, login, etc.). */
body:has(.shared-map) .shared-footer {
    margin-top: 0;
}

/* ============================================
   LEGAL PAGE LAYOUT
   ============================================ */

.legal-page__body ul,
.legal-page__body ol,
.prose ul,
.prose ol {
  padding-left: var(--space-lg);
  margin: var(--space-md) 0;
}

.legal-page__body li,
.prose li {
  margin-bottom: var(--space-xs);
  line-height: 1.5;
}

.legal-page__body ul li,
.prose ul li {
  list-style: disc;
}

.legal-page__body ol li,
.prose ol li {
  list-style: decimal;
}

/* ============================================
   META TAGS (no visual rules — kept for grouping)
   ============================================ */

/* ============================================
   RESPONSIVE BREAKPOINTS
   Mobile-first. Add overrides only when needed.
   ============================================ */
@media (min-width: 600px) {
    .shared-header__brand { font-size: 24px; }
}

@media (min-width: 900px) {
    .shared-header__inner { gap: var(--space-md); }
    .shared-header__brand { font-size: 28px; }
}

/* ============================================
   SHARED HERO (parallax video)
   ============================================ */
.shared-hero {
  position: relative;
  width: 100vw;
  min-height: calc(100vh - 60px);
  overflow: hidden;
  display: flex;
  align-items: center;
}
.shared-hero__video {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  object-fit: cover;
  z-index: -2;
}
.shared-hero__scrim {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  /* Reduced 2026-05-11 from rgba(0,0,0,0.6) — the heavier scrim was
     muting the video too much. 0.4 keeps the white headline legible
     on both consumer hero videos while letting more of the underlying
     footage show through. Single shared rule — both townteam and
     lottery homepages use this verbatim via shared/views/hero.php. */
  background: linear-gradient(to right, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0) 100%);
  z-index: 1;
  pointer-events: none;
}
.shared-hero__frame {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: var(--space-xl) var(--space-md);
}
.shared-hero__content {
  max-width: 560px;
  color: #fff;
}
.shared-hero__headline {
  font-family: var(--font-heading);
  font-size: 56px;
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-sm);
  margin: 0 0 var(--space-md);
  color: #fff;
}
.shared-hero__tagline {
  font-family: var(--font-body);
  font-size: 20px;
  font-weight: 400;
  line-height: 1.05;
  margin: 0 0 var(--space-xs);
  color: rgba(255,255,255,0.95);
}
.shared-hero__ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  justify-content: flex-start;
}
@media (min-width: 600px) {
  .shared-hero__ctas { gap: var(--space-lg); }
}
/* .shared-hero__cta — DEPRECATED. Use .btn .btn--gold .btn--lg instead. */
.shared-hero__tagline:last-of-type { margin-bottom: var(--space-lg); }
.shared-hero__bignum {
  display: inline-block;
  font-size: 2em;
  font-weight: 800;
  line-height: 0.9;
  vertical-align: baseline;
}
@media (min-width: 900px) {
  .shared-hero__headline { font-size: 80px; }
  .shared-hero__tagline { font-size: 24px; }
}
@media (prefers-reduced-motion: reduce) {
  .shared-hero__video { display: none; }
  .shared-hero { background: var(--hll-green); }
}
/* ============================================
   BUTTONS
   Standard button system. Default form factor for
   over-dark backgrounds (hero, login, etc).
   New buttons: use .btn .btn--gold for filled CTA,
   .btn .btn--outline-light for outlined secondary.
   ============================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  padding: 14px 28px;
  font-family: var(--font-heading);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  border-radius: 3px;
  border: 1.5px solid transparent;
  cursor: pointer;
  min-height: 48px;
  transition: filter 0.15s ease, background 0.15s ease, color 0.15s ease;
  /* sentence case by default — no uppercase, no letter-spacing */
}
.btn:hover { filter: brightness(1.06); }
.btn:active { filter: brightness(0.94); }
.btn:focus-visible { outline: 2px solid var(--hll-gold); outline-offset: 2px; }

/* Filled gold variant — primary CTA on dark backgrounds */
.btn--gold {
  background: var(--hll-gold);
  color: var(--hll-green);
  border-color: var(--hll-gold);
}

/* Outlined white variant — secondary CTA on dark backgrounds */
.btn--outline-light {
  background: transparent;
  color: #fff;
  border-color: rgba(255, 255, 255, 0.85);
}
.btn--outline-light:hover,
.btn--outline-light:focus {
  background: #fff;
  color: var(--hll-green);
  border-color: #fff;
}

/* Larger size for hero CTAs */
.btn--lg {
  padding: 16px 32px;
  font-size: 17px;
  min-height: 52px;
}

@media (min-width: 900px) {
  .btn--lg { padding: 18px 36px; font-size: 18px; min-height: 56px; }
}


/* ============================================
   STATS BAR (lottery only)
   ============================================ */
.stats-bar {
  background: transparent;
  padding: 0 var(--space-md) var(--space-md);
  position: relative;
  z-index: 5;
  margin-top: -32px;
}
.stats-bar::before {
  content: "";
  position: absolute;
  inset: 32px 0 0 0;
  background: var(--color-bg-muted);
  z-index: -1;
}
.stats-bar__inner {
  max-width: 1200px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-sm);
  position: relative;
  z-index: 6;
}
.stats-bar__card {
  background: #fff;
  border: 3.5px solid var(--hll-green);
  border-radius: 8px;
  padding: var(--space-sm) var(--space-sm);
  text-align: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100%;
  min-height: 96px;
}
.stats-bar__value {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 32px;
  line-height: 1;
  color: var(--hll-green);
  margin-bottom: var(--space-sm);
}
.stats-bar__label {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--hll-green);
  line-height: 1.3;
}
@media (max-width: 599px) {
  .stats-bar__inner { grid-template-columns: repeat(3, 1fr); }
  .stats-bar__card:nth-child(4) { display: none; }
}
@media (min-width: 600px) {
  .stats-bar { margin-top: -37px; }
  .stats-bar::before { top: 37px; }
  .stats-bar__inner { gap: var(--space-md); }
  .stats-bar__value { font-size: 44px; }
  .stats-bar__card { padding: var(--space-sm) var(--space-md); min-height: 110px; }
}
@media (min-width: 900px) {
  .stats-bar { margin-top: -47px; }
  .stats-bar::before { top: 47px; }
  .stats-bar__value { font-size: 56px; }
  .stats-bar__label { font-size: 12px; }
  .stats-bar__card { padding: var(--space-md) var(--space-lg); min-height: 140px; }
}


/* ============================================
   TEXT SECTION (heading + paragraph, cream bg)
   ============================================ */
.text-section {
  background: var(--color-bg-muted);
  padding: var(--space-xl) var(--space-md);
}
.text-section__inner {
  max-width: 800px;
  margin: 0 auto;
  text-align: center;
}
.text-section__heading {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 28px;
  line-height: 1.1;
  color: var(--hll-green);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin: 0 0 var(--space-lg);
}
.text-section__body {
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  color: var(--color-text);
  margin: 0;
}
/* Adjacent-sibling spacing for multi-paragraph text-section blocks
   (consumers that pass more than one __body paragraph). Mirrors the
   richtext partial's > * + * pattern. Single-paragraph callers are
   unaffected — the adjacent combinator only fires when two body
   paragraphs sit next to each other. */
.text-section__body + .text-section__body { margin-top: var(--space-md); }
/* When a .cta-block is nested directly inside .text-section__inner
   (Town Team home: celebration intro → CTA box → trio paragraphs all
   in one cream container), give it breathing room above and let the
   following body paragraph clear it. Scoped to the parent so the
   stand-alone cta_block.php usage (.cta-section > .cta-block) is
   untouched. */
.text-section__inner > .cta-block { margin-top: var(--space-lg); }
.text-section__inner > .cta-block + .text-section__body { margin-top: var(--space-lg); }
@media (min-width: 600px) {
  .text-section { padding: var(--space-xxl) var(--space-md); }
}
@media (min-width: 900px) {
  .text-section { padding: 80px var(--space-md); }
  .text-section__heading { font-size: 36px; margin-bottom: var(--space-xl); }
  .text-section__body { font-size: 19px; line-height: 1.65; }
}


/* ============================================
   STEPS SECTION (numbered pills, cream bg)
   ============================================ */
.steps-section {
  background: var(--color-bg-muted);
  padding: 0 var(--space-md) 96px;
}
.steps-section__inner {
  max-width: 800px;
  margin: 0 auto;
  text-align: center;
}
.steps-section__heading {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 28px;
  line-height: 1.1;
  color: var(--hll-green);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin: var(--space-xxl) 0 var(--space-xl);
}
.steps-section__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
  justify-items: center;
}
@media (min-width: 700px) {
  .steps-section__list { gap: var(--space-lg); }
}
.steps-section__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-sm);
  max-width: 100%;
}
.steps-section__pill {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: var(--hll-gold);
  color: var(--hll-green);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Arial Black', 'Helvetica Neue', Arial, sans-serif;
  font-weight: 900;
  font-size: 56px;
  line-height: 1;
}
@media (min-width: 600px) {
  .steps-section__pill { width: 110px; height: 110px; font-size: 68px; }
}
.steps-section__label {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--hll-green);
  line-height: 1.3;
}
@media (min-width: 600px) { .steps-section__label { font-size: 15px; } }
@media (min-width: 900px) {
  .steps-section { padding: 0 var(--space-md) 160px; }
  .steps-section__heading { font-size: 36px; margin-bottom: var(--space-xxl); }
  .steps-section__pill { width: 140px; height: 140px; font-size: 88px; }
  .steps-section__label { font-size: 18px; }
}
/* ============================================
   ADMIN LANDING (full video background, centered content)
   ============================================ */
.admin-landing {
  position: relative;
  flex: 1 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.admin-landing__video {
  position: fixed;
  top: 60px;
  left: 0;
  width: 100vw;
  height: calc(100vh - 60px);
  object-fit: cover;
  z-index: -1;
}
.admin-landing__content {
  position: relative;
  z-index: 1;
  color: #fff;
  text-align: center;
}
@media (prefers-reduced-motion: reduce) {
  .admin-landing__video { display: none; }
  .admin-landing { background: var(--hll-green); }
}


/* ============================================
   ADMIN LOGIN BACKGROUND (full video, centered card)
   ============================================ */
.admin-login {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.admin-login__video {
  position: fixed;
  top: 0; left: 0;
  width: 100vw; height: 100vh;
  object-fit: cover;
  z-index: -2;
}
.admin-login__scrim {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.4);
  z-index: -1;
}
.admin-login__card {
  position: relative;
  z-index: 1;
  background: #fff;
  padding: var(--space-xl);
  border-radius: 8px;
  min-width: 320px;
  max-width: 400px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.3);
}
@media (prefers-reduced-motion: reduce) {
  .admin-login__video { display: none; }
  .admin-login { background: var(--hll-green); }
}


/* ============================================
   LOGIN PAGE (admin)
   ============================================ */
.login-page {
  position: relative;
  flex: 1 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100vh - 60px);
  overflow: hidden;
}
.login-page__video {
  position: fixed;
  top: 60px;
  left: 0;
  width: 100vw;
  height: calc(100vh - 60px);
  object-fit: cover;
  z-index: -1;
}
.login-page__card {
  position: relative;
  z-index: 1;
  background: #fff;
  padding: var(--space-xl);
  border: 2px solid var(--hll-green);
  border-radius: 10px;
  min-width: 320px;
  max-width: 720px;
  width: 100%;
  box-sizing: border-box;
  box-shadow: 0 12px 32px rgba(0,0,0,0.3);
}
.login-page__card,
.login-page__card * {
  color: var(--hll-green);
  font-family: var(--font-body);
}
.login-page__card h1,
.login-page__card h2 {
  font-family: var(--font-heading);
  font-weight: 700;
  margin: 0 0 var(--space-md);
}
.login-page__card label {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 14px;
  display: block;
  margin-bottom: var(--space-xs);
}
.login-page__card input[type=email],
.login-page__card input[type=text],
.login-page__card input[type=tel],
.login-page__card input[type=password] {
  font-family: var(--font-body);
  font-size: 16px;
  width: 100%;
  padding: 10px 12px;
  border: 1.5px solid var(--hll-green);
  border-radius: 4px;
  background: #fff;
  color: var(--color-text);
  margin-bottom: var(--space-md);
  box-sizing: border-box;
}
@media (prefers-reduced-motion: reduce) {
  .login-page__video { display: none; }
  .login-page { background: var(--hll-green); }
}


/* ============================================
   CTA BLOCK (dark green card on cream wrapper)
   ============================================ */
.cta-section {
  background: linear-gradient(to bottom, var(--color-bg-muted) 50%, #F8F6F1 50%);
  padding: 0 var(--space-md) var(--space-xl);
}
.cta-block {
  max-width: 1200px;
  margin: 0 auto;
  background: var(--hll-green);
  color: #fff;
  padding: calc(var(--space-md) * 2) var(--space-xl);
  text-align: center;
  border-radius: 8px;
}
.cta-block__heading {
  font-family: var(--font-heading);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: #fff;
  font-size: clamp(1.5rem, 4vw, 2.5rem);
  line-height: 1.15;
  margin: 0 0 var(--space-md);
}
.cta-block__body {
  font-family: var(--font-body);
  color: #fff;
  font-size: 1rem;
  line-height: 1.5;
  margin: 0;
}
.cta-block__btn {
  display: inline-block;
  margin-top: calc(var(--space-md) * 2);
  padding: 0.75em 2em;
  border: 2px solid #fff;
  color: #fff;
  background: transparent;
  border-radius: 10px;
  text-decoration: none;
  font-family: var(--font-body);
  font-weight: 500;
  transition: background 0.2s, color 0.2s;
}
.cta-block__btn:hover,
.cta-block__btn:focus {
  background: #fff;
  color: var(--hll-green);
}
@media (min-width: 600px) {
  .cta-section { padding: 0 var(--space-md) var(--space-xxl); }
  .cta-block { padding: var(--space-xxl) var(--space-xl); }
  .cta-block__btn { margin-top: calc(var(--space-lg) * 3); }
  .cta-block__body { font-size: 1.0625rem; }
}
@media (min-width: 900px) {
  .cta-block { padding: 80px var(--space-xxl); }
  .cta-block__body { font-size: 1.125rem; }
}


/* ============================================
   SUBSCRIBE FORM (page-width grid, no card chrome)
   ============================================ */
.subscribe-form-section {
  background: var(--color-bg-muted);
  padding: var(--space-xxl) var(--space-md);
}
.subscribe-form {
  position: relative;
  max-width: 900px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
  background: #fff;
  border: 2px solid var(--hll-green);
  border-radius: 12px;
  padding: var(--space-xl);
  box-sizing: border-box;
}
.subscribe-form__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.subscribe-form__label {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--color-text);
}
.subscribe-form__hint {
  font-weight: 400;
  font-size: 0.8125rem;
  color: rgba(0, 0, 0, 0.55);
  margin-left: var(--space-xs);
}
.subscribe-form__input {
  width: 100%;
  padding: 0.75rem var(--space-md);
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--color-text);
  background: #fff;
  border: 1.5px solid #cfcfcf;
  border-radius: 8px;
  box-sizing: border-box;
}
.subscribe-form__input:focus {
  outline: none;
  border-color: var(--hll-green);
  box-shadow: 0 0 0 3px rgba(27, 58, 45, 0.15);
}

/* ── .sponsor-grid__search ───────────────────────────────────────────
   Wrapper around a real-time search input rendered between the filter
   pills and the sponsor grid (e.g. /retailers on horsforthtownteam).
   Margin spaces it cleanly from .filter-bar above and the grid below;
   the input itself uses the canonical .subscribe-form__input chrome. */
.sponsor-grid__search {
  margin: var(--space-sm) 0 var(--space-md);
}
.subscribe-form__btn-row {
  display: flex;
  gap: var(--space-md);
  align-items: stretch;
}
.subscribe-form__btn-row .subscribe-form__btn {
  flex: 1 1 auto;
}
.subscribe-form__social {
  flex: 0 0 auto;
  aspect-ratio: 1 / 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  text-decoration: none;
  transition: background 0.2s, transform 0.1s;
}
.subscribe-form__social svg {
  width: 20px;
  height: 20px;
  display: block;
}
.subscribe-form__social--facebook { background: #1877F2; }
.subscribe-form__social--facebook:hover,
.subscribe-form__social--facebook:focus { background: #166FE5; }

.subscribe-form__btn {
  width: 100%;
  padding: 0.85rem var(--space-lg);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1rem;
  background: var(--hll-green);
  color: #fff;
  border: 2px solid var(--hll-green);
  border-radius: 10px;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
  transition: background 0.15s, color 0.15s;
}
.subscribe-form__btn:hover,
.subscribe-form__btn:focus {
  background: #14302a;
  border-color: #14302a;
}
.subscribe-form__btn--ghost {
  background: transparent;
  color: var(--hll-green);
}
.subscribe-form__btn--ghost:hover,
.subscribe-form__btn--ghost:focus {
  background: var(--hll-green);
  color: #fff;
  border-color: var(--hll-green);
}
.subscribe-form__alert {
  max-width: 1200px;
  margin: 0 auto var(--space-md);
  padding: var(--space-md);
  border-radius: 8px;
  font-family: var(--font-body);
  font-size: 0.9375rem;
}
.subscribe-form__alert--success { background: #e8f4ec; color: #14532d; border: 1px solid #b8dabe; }
.subscribe-form__alert--error   { background: #fce8e8; color: #7f1d1d; border: 1px solid #f0b0b0; }
.subscribe-form__alert--info    { background: #fef7e0; color: #5c4500; border: 1px solid #f0d068; }
@media (min-width: 600px) {
  .subscribe-form { grid-template-columns: 1fr 1fr; gap: var(--space-md) var(--space-lg); }
}


/* ============================================
   BUSINESS SCROLLER (horizontal, edge-to-edge)
   ============================================ */
.scroller-section {
  background: #fff;
  padding: calc(var(--space-lg) * 4) 0;
}
@media (min-width: 600px) {
  .scroller-section { padding: calc(var(--space-xl) * 4) 0; }
}
.scroller-section--inset {
  background: var(--color-bg-muted);
  padding: var(--space-xl) 0;
}
@media (min-width: 600px) {
  .scroller-section--inset { padding: var(--space-xxl) 0; }
  .scroller-section__title { margin-bottom: var(--space-xxl); }
}
.scroller-section--single-line .scroller__name {
  min-height: calc(1.2em + var(--space-md));
}
.scroller-section--inset-tight {
  background: var(--color-bg-muted);
  padding: calc(var(--space-xl) * 3) 0 18px;
}
@media (min-width: 600px) {
  .scroller-section--inset-tight { padding: calc(var(--space-xxl) * 3) 0 18px; }
  .scroller-section--inset-tight .scroller-section__title { margin-bottom: var(--space-xxl); }
}
/* Halve the top padding when a scroller follows the /subscribe form. */
.subscribe-form-section + .scroller-section--inset-tight {
  padding-top: calc(var(--space-xl) * 1.5);
}
@media (min-width: 600px) {
  .subscribe-form-section + .scroller-section--inset-tight {
    padding-top: calc(var(--space-xxl) * 1.5);
  }
}

.scroller-section__title {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: clamp(1.5rem, 4vw, 2rem);
  line-height: 1.15;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--hll-green);
  text-align: center;
  margin: 0 var(--space-md) var(--space-xl);
}
.scroller-section__title strong { font-weight: 800; }
.scroller-section__description {
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  color: var(--color-text);
  text-align: center;
  max-width: 760px;
  margin: 0 auto var(--space-xl);
  padding: 0 var(--space-md);
}
@media (min-width: 600px) {
  .scroller-section__description { font-size: 19px; line-height: 1.65; }
}
.scroller-section__title:has(+ .scroller-section__description) { margin-bottom: var(--space-md); }
@media (min-width: 600px) {
  .scroller-section--inset-tight .scroller-section__title:has(+ .scroller-section__description) { margin-bottom: var(--space-md); }
}
.scroller {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding: 0;
  scroll-padding-left: var(--space-md);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
@media (min-width: 600px) { .scroller { scroll-padding-left: var(--space-lg); } }
.scroller::-webkit-scrollbar { display: none; }
.scroller__inner {
  display: flex;
  gap: 18px;
}
.scroller__tile {
  flex: 0 0 calc((100vw - var(--space-md)*2 - 18px) / 1.6);
  aspect-ratio: 1 / 1;
  position: relative;
  scroll-snap-align: start;
  text-decoration: none;
  color: inherit;
}
.scroller__tile:first-child { margin-left: var(--space-md); }
.scroller__tile:last-child  { margin-right: var(--space-md); }
@media (min-width: 600px) {
  .scroller__tile { flex: 0 0 calc((100vw - var(--space-lg)*2 - 18px*4) / 4.5); }
  .scroller__tile:first-child { margin-left: var(--space-lg); }
  .scroller__tile:last-child  { margin-right: var(--space-lg); }
}
.scroller__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 8px;
  background: #e8e0d2;
  display: block;
}
.scroller__img--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-heading);
  font-size: 3rem;
  color: var(--hll-green);
  background: #e8e0d2;
}
.scroller__name {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  text-align: center;
  font-family: var(--font-body);
  color: var(--hll-green);
  background: var(--hll-gold);
  padding: var(--space-sm) var(--space-md);
  font-weight: 500;
  border-radius: 0 0 8px 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1.2;
  min-height: calc(2.4em + var(--space-md));
  box-sizing: border-box;
}
.scroller__name > span {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
}


/* ============================================
   LATEST DRAW RESULTS (white band, follows cta-section)
   ============================================ */
.latest-results-section {
  background: #F8F6F1;
  padding: var(--space-xxl) var(--space-md) calc(var(--space-xl) * 3);
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
@media (min-width: 600px) {
  .latest-results-section { padding-bottom: calc(var(--space-xxl) * 3); }
}
.latest-results-section__inner {
  max-width: 760px;
  width: 100%;
  margin: 0 auto;
  text-align: center;
}
.latest-results-section .scroller-section__title { margin-bottom: var(--space-md); }
.latest-results__pills {
  list-style: none;
  margin: var(--space-xl) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
}
@media (min-width: 600px) {
  .latest-results__pills { grid-template-columns: 1fr 1fr; gap: 18px; }
  .latest-results__pill:first-child { grid-column: 1 / -1; }
}
.latest-results__pill {
  background: #fff;
  border: 3.5px solid var(--hll-green);
  border-radius: 8px;
  padding: var(--space-sm) var(--space-sm);
  text-align: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  min-height: 96px;
}
.latest-results__pill-number {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 32px;
  line-height: 1;
  color: var(--hll-green);
  margin-bottom: var(--space-sm);
}
.latest-results__pill-number strong { font-weight: 700; }
.latest-results__pill-label {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--hll-green);
  line-height: 1.3;
}
.latest-results__pill-retailer {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 10px;
  color: var(--hll-green);
  line-height: 1.3;
  margin-top: var(--space-xs);
  text-transform: none;
  letter-spacing: 0;
}
.latest-results__pill-retailer strong { font-weight: 700; }
.latest-results__pill-amount {
  display: inline-block;
  margin-left: var(--space-sm);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--hll-green);
}
@media (min-width: 600px) {
  .latest-results__pill { padding: var(--space-sm) var(--space-md); min-height: 110px; }
  .latest-results__pill-number { font-size: 44px; }
}
@media (min-width: 900px) {
  .latest-results__pill { padding: var(--space-md) var(--space-lg); min-height: 140px; }
  .latest-results__pill-number { font-size: 56px; }
  .latest-results__pill-label { font-size: 12px; }
  .latest-results__pill-retailer { font-size: 12px; }
}


/* ============================================
   SUBSCRIBE/CONTACT form: extra scaffolding
   ============================================ */
.subscribe-form__heading {
  grid-column: 1 / -1;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 28px;
  line-height: 1.1;
  color: var(--hll-green);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  text-align: center;
  margin: var(--space-md) 0 var(--space-md);
}
.subscribe-form__heading-sub {
  grid-column: 1 / -1;
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  color: var(--color-text);
  text-align: center;
  margin: 0;
}
.subscribe-form__step-indicator {
  grid-column: 1 / -1;
  text-align: center;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.8125rem;
  color: var(--hll-green);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0;
}
.subscribe-form__field--full { grid-column: 1 / -1; }
.subscribe-form__textarea {
  width: 100%;
  padding: 0.75rem var(--space-md);
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--color-text);
  background: #fff;
  border: 1.5px solid #cfcfcf;
  border-radius: 8px;
  box-sizing: border-box;
  min-height: 200px;
  resize: vertical;
}
.subscribe-form__textarea:focus {
  outline: none;
  border-color: var(--hll-green);
  box-shadow: 0 0 0 3px rgba(27, 58, 45, 0.15);
}
.subscribe-form__actions {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
@media (min-width: 600px) {
  .subscribe-form__actions { flex-direction: row; justify-content: flex-end; }
  .subscribe-form__actions .subscribe-form__btn { flex: 0 0 auto; min-width: 160px; }
}
/* Forced-stack modifier — keeps buttons one-per-row at every viewport
   (overrides the desktop row rule above). Used by the sponsor edit
   forms where the action set is a Save / Back pair that should always
   present as a vertical stack with Save on top. */
.subscribe-form__actions.subscribe-form__actions--stacked {
  flex-direction: column;
  align-items: stretch;
}
.subscribe-form__actions.subscribe-form__actions--stacked .subscribe-form__btn {
  width: 100%;
  flex: 0 0 auto;
  min-width: 0;
}
.subscribe-form__honeypot {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

.shared-footer__nav {
    font-family: var(--font-body);
    font-size: 14px;
    color: rgba(255,255,255,0.85);
    margin: 0 0 var(--space-md);
}
.shared-footer__nav-link {
    color: var(--color-text-inverse);
    text-decoration: underline;
    text-underline-offset: 2px;
    text-decoration-color: rgba(255,255,255,0.5);
}
.shared-footer__nav-link:hover { text-decoration-color: rgba(255,255,255,1); }

/* ============================================
   PROSE BLOCK (shared CMS-rendered article body)
   ============================================ */
body .shared-prose {
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.65;
  color: var(--hll-green);
  max-width: 760px;
  margin: 0 auto;
  padding: 0 var(--space-md);
}
body .shared-prose p { margin: 0 0 1.25em; }
body .shared-prose p:last-child { margin-bottom: 0; }
body .shared-prose h2 {
  font-family: var(--font-body);
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--hll-green);
  margin: 2em 0 0.5em;
  line-height: 1.2;
}
body .shared-prose h3 {
  font-family: var(--font-body);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--hll-green);
  margin: 1.75em 0 0.5em;
  line-height: 1.25;
}
body .shared-prose ul, body .shared-prose ol {
  margin: 0 0 1.25em;
  padding-left: 1.5em;
}
body .shared-prose li { margin-bottom: 0.5em; }
body .shared-prose a {
  color: var(--hll-green);
  text-decoration: underline;
  text-underline-offset: 2px;
}
body .shared-prose strong { font-weight: 700; }
@media (min-width: 600px) {
  body .shared-prose { font-size: 19px; line-height: 1.7; padding: 0 var(--space-lg); }
  body .shared-prose h2 { font-size: 1.75rem; }
}

/* ─────────────────────────────────────────────────────────────
 * INTRO BAND  — gold full-width section header
 * Used on: lottery /retailers, lottery /accepts-hpound, town team /retailers
 * ───────────────────────────────────────────────────────────── */
.intro-band {
  background: var(--hll-gold);
  padding: var(--space-xxl) var(--space-md);
}
.intro-band__inner {
  max-width: 800px;
  margin: 0 auto;
  text-align: center;
}
.intro-band__title {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: clamp(1.5rem, 4vw, 2rem);
  line-height: 1.15;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--hll-green);
  margin: 0 0 var(--space-md);
}
.intro-band__title strong { font-weight: 800; }
.intro-band__body {
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  color: var(--hll-green);
  margin: 0;
}
@media (min-width: 600px) {
  .intro-band__body { font-size: 19px; line-height: 1.65; }
}

/* ─────────────────────────────────────────────────────────────
 * LISTING PAGE wrapper + SPONSOR GRID
 * Used on: lottery /retailers, lottery /accepts-hpound, town team /retailers
 * ───────────────────────────────────────────────────────────── */
.listing-page {
  background: #F8F6F1;
  padding: var(--space-xl) 0 0;
  flex: 1 0 auto;
}
.sponsor-grid-section {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 var(--space-md) var(--space-xxl);
}
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin: 0 0 var(--space-lg);
  justify-content: flex-start;
}
@media (min-width: 600px) {
  .filter-bar { justify-content: flex-end; gap: var(--space-md); }
}
.btn--sm {
  padding: 8px 16px;
  font-size: 14px;
  min-height: 36px;
}
.btn--outline-green {
  background: transparent;
  color: var(--hll-green);
  border-color: var(--hll-green);
}
.btn--outline-green:hover, .btn--outline-green:focus {
  background: var(--hll-green);
  color: #fff;
}
.sponsor-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}
@media (min-width: 600px) {
  .sponsor-grid { grid-template-columns: repeat(5, 1fr); }
}
.sponsor-grid__tile {
  position: relative;
  aspect-ratio: 1 / 1;
}
.sponsor-grid__media {
  position: relative;
  width: 100%;
  height: 100%;
}
.sponsor-grid__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 8px;
  background: #e8e0d2;
  display: block;
}
.sponsor-grid__img--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-heading);
  font-size: 3rem;
  color: var(--hll-green);
}
.sponsor-grid__name {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--hll-gold);
  color: var(--hll-green);
  padding: var(--space-sm) var(--space-md);
  border-radius: 0 0 8px 8px;
  text-align: center;
  font-family: var(--font-body);
  font-weight: 500;
  line-height: 1.2;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(2.4em + var(--space-md));
  box-sizing: border-box;
}
.sponsor-grid__name > span {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
}
.sponsor-grid__add {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  border: 2px dashed var(--hll-gold);
  border-radius: 8px;
  background: rgba(212, 168, 67, 0.05);
  color: var(--hll-green);
  text-decoration: none;
  text-align: center;
  padding: var(--space-md);
  box-sizing: border-box;
  transition: background 0.15s;
}
.sponsor-grid__add:hover, .sponsor-grid__add:focus {
  background: rgba(212, 168, 67, 0.18);
}
.sponsor-grid__add-icon {
  font-family: var(--font-heading);
  font-size: 2.5rem;
  font-weight: 800;
  line-height: 1;
  color: var(--hll-gold);
  margin-bottom: var(--space-sm);
}
.sponsor-grid__add-label {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.875rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* FOUC defence — Alpine clears x-cloak on init */
[x-cloak] { display: none !important; }

/* ============================================
   GHOST BORDER NUKE — /become-a-retailer & similar
   Ensures the only direct children of subscribe-form-section that
   render are the form card itself, the validation alert, and the
   heading. Anything else (errant Alpine x-show wrapper, stray
   leftover nav row, btn-row used outside the form, etc.) is hidden
   so it cannot leak a border, divider, or padded background outside
   the white form card. Belt-and-braces — added 2026-04-29.
   ============================================ */
.subscribe-form-section > *:not(.subscribe-form):not(.subscribe-form__alert):not(.subscribe-form__heading):not(.subscribe-form-section__body) { display: none !important; }
/* Defence: any descendant of the form that Alpine hid via x-show must
   stay hidden against any future CSS rule that tries to set display. */
.subscribe-form [x-show][style*="display: none"],
.subscribe-form [x-show][style*="display:none"] { display: none !important; }
/* Defence: stale btn-row or wrapper classes that may have been used
   inadvertently around step buttons get neutralised — no border, no
   background. */
.subscribe-form-section .subscribe-form__btn-row { border: 0 !important; background: transparent !important; }

/* ============================================
   RETAILER / ORGANISATION DETAIL — added 2026-04-29
   Used by sponsor.php (lottery) + retailer.php (townteam) via the
   shared /var/www/shared/views/retailer_detail.php partial.
   Mobile-first. Tokens: --hll-green, --hll-gold, --color-bg-muted,
   --color-text, --space-xs..xxl, --font-heading, --font-body.
   ============================================ */
.retailer-detail-section {
    background: var(--color-bg-muted);
    padding: var(--space-xl) var(--space-md);
}
.retailer-detail__inner {
    max-width: 720px;
    margin: 0 auto;
}
.retailer-detail__card {
    background: #fff;
    border: 2px solid var(--hll-green);
    border-radius: 12px;
    padding: var(--space-lg);
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}
.retailer-detail__row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-sm);
    margin: 0;
    font-family: var(--font-body);
    font-size: 1rem;
    line-height: 1.5;
    color: var(--color-text);
}
.retailer-detail__icon {
    flex: 0 0 auto;
    color: var(--hll-green);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    margin-top: 2px;
}
.retailer-detail__text {
    flex: 1 1 auto;
    word-break: break-word;
}
.retailer-detail__text--link {
    color: var(--hll-green);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.retailer-detail__text--link:hover,
.retailer-detail__text--link:focus {
    color: var(--hll-gold);
}
.retailer-detail__social {
    display: flex;
    gap: var(--space-sm);
    margin-top: var(--space-xs);
}
.retailer-detail__social-link {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: var(--hll-green);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    transition: background 0.15s;
}
.retailer-detail__social-link:hover,
.retailer-detail__social-link:focus {
    background: var(--hll-gold);
}
.retailer-detail__card--404 {
    text-align: center;
}
.retailer-detail__notfound-title {
    font-family: var(--font-heading);
    font-weight: 700;
    color: var(--hll-green);
    margin: 0 0 var(--space-sm);
    font-size: 1.5rem;
}
.retailer-detail__notfound-body {
    margin: 0 0 var(--space-md);
    color: var(--color-text);
}

/* ── Media scroller ── */
.retailer-media-section {
    background: var(--color-bg-muted);
    padding: 0 0 var(--space-xl);
}
.retailer-media__inner {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 var(--space-md);
}
.retailer-media__scroller {
    display: flex;
    gap: var(--space-sm);
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: var(--space-sm);
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
}
.retailer-media__slide {
    flex: 0 0 auto;
    width: min(72vw, 280px);
    aspect-ratio: 1 / 1;
    background: #000;
    border: 0;
    padding: 0;
    border-radius: 12px;
    overflow: hidden;
    cursor: pointer;
    position: relative;
    scroll-snap-align: start;
}
.retailer-media__slide:focus {
    outline: 3px solid var(--hll-gold);
    outline-offset: 2px;
}
.retailer-media__el {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.retailer-media__badge {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 2.5rem;
    text-shadow: 0 2px 6px rgba(0,0,0,0.5);
    pointer-events: none;
}

/* ── Modal ── */
.retailer-media__modal {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.85);
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-md);
    box-sizing: border-box;
}
.retailer-media__modal-close {
    position: absolute;
    top: var(--space-md);
    right: var(--space-md);
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: 0;
    background: rgba(255,255,255,0.92);
    color: var(--hll-green);
    font-size: 1.75rem;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}
.retailer-media__modal-close:hover,
.retailer-media__modal-close:focus {
    background: var(--hll-gold);
    color: #fff;
}
.retailer-media__modal-stage {
    max-width: min(96vw, 1100px);
    max-height: 90vh;
    display: flex;
    align-items: center;
    justify-content: center;
}
.retailer-media__modal-media {
    max-width: 100%;
    max-height: 90vh;
    border-radius: 8px;
    box-shadow: 0 8px 36px rgba(0,0,0,0.4);
}

/* ── Detail-mode map override ── */
.shared-map--detail {
    width: 100%;
    height: 320px;
    margin: 0;
}
@media (min-width: 700px) {
    .shared-map--detail { height: 420px; }
    .retailer-media__slide { width: 320px; }
}

/* ============================================
   TILE LINKS — re-enabled 2026-04-29
   Restore click-through on businesses scroller and sponsor grid tiles
   now that /retailer/{slug} detail pages exist on both sites.
   ============================================ */
.sponsor-grid__link {
    display: block;
    width: 100%;
    height: 100%;
    text-decoration: none;
    color: inherit;
    position: relative;
}
.sponsor-grid__link:focus {
    outline: 3px solid var(--hll-gold);
    outline-offset: 2px;
    border-radius: 8px;
}
.scroller__tile:focus {
    outline: 3px solid var(--hll-gold);
    outline-offset: 2px;
    border-radius: 8px;
}

/* ============================================
   LEGAL PAGES — added 2026-04-29
   Town team privacy / terms / cookies / accessibility used to be wrapped
   in Tailwind utility containers (max-w-[1080px], max-w-3xl). The unified
   menu work moved them onto shared chrome where Tailwind is not loaded,
   so those wrappers became no-ops and the prose ran flush to the viewport
   edge. This block restores the constrained-and-padded layout, and styles
   h1 (which .shared-prose otherwise leaves un-themed).
   Used by: <section class="legal-page-section"> > .shared-prose
   ============================================ */
.legal-page-section {
    background: #fff;
    padding: var(--space-xl) var(--space-md);
}
@media (min-width: 700px) {
    .legal-page-section { padding: var(--space-xxl) var(--space-lg); }
}
body .legal-page-section .shared-prose {
    max-width: 760px;
    margin: 0 auto;
}
body .shared-prose h1 {
    font-family: var(--font-heading);
    font-weight: 700;
    color: var(--hll-green);
    font-size: 2rem;
    line-height: 1.15;
    margin: 0 0 var(--space-md);
}
@media (min-width: 700px) {
    body .shared-prose h1 { font-size: 2.5rem; }
}
body .shared-prose table {
    width: 100%;
    border-collapse: collapse;
    margin: var(--space-md) 0;
    font-size: 0.95rem;
}
body .shared-prose th,
body .shared-prose td {
    text-align: left;
    padding: var(--space-sm);
    border: 1px solid #e5e7eb;
    vertical-align: top;
}
body .shared-prose thead {
    background: #f3f4f6;
}
body .shared-prose th {
    font-weight: 700;
    color: var(--color-text);
}

/* ============================================
   LEGAL / TEXT PAGES (shared) — added 2026-04-29
   Ports the .hll-page-hero + .hll-prose styling that lottery's public.css
   defines, so town team can use the same markup and get matched layout
   without loading public.css. Variables are mirrored from lottery's :root.
   ============================================ */
:root {
    --hll-bg:          #FAFAFA;
    --hll-bg-white:    #FFFFFF;
    --hll-bg-gray:     #F5F5F5;
    --hll-bg-dark:     #1B3A2D;
    --hll-bg-dark-alt: #162F24;
    --hll-text:        #1A1A1A;
    --hll-text-light:  #5A5A6E;
    --hll-text-muted:  #8E8E9E;
    --hll-border:      #E2E2E8;
    --hll-space-xs:    0.25rem;
    --hll-space-sm:    0.5rem;
    --hll-space-md:    1rem;
    --hll-space-lg:    1.5rem;
    --hll-space-xl:    2rem;
    --hll-space-2xl:   3rem;
    --hll-space-3xl:   4rem;
}

.hll-page-hero {
    background: #F5F0E8;
    color: #1B3A2D;
    padding: var(--hll-space-2xl) var(--hll-space-md) var(--hll-space-xl);
    text-align: center;
}
.hll-page-hero__inner {
    max-width: 640px;
    margin: 0 auto;
}
.hll-page-hero__title {
    font-family: var(--font-heading);
    font-size: 2.25rem;
    font-weight: 800;
    line-height: 1.2;
    margin: 0 0 var(--hll-space-xs);
    letter-spacing: -0.02em;
}
.hll-page-hero__subtitle {
    font-family: var(--font-body);
    font-size: 1rem;
    color: rgba(27, 58, 45, 0.65);
    margin: 0;
}

.hll-prose {
    background: #fff;
    padding: var(--hll-space-2xl) var(--hll-space-md) var(--hll-space-3xl);
    font-family: var(--font-body);
}
.hll-prose__inner {
    max-width: 720px;
    margin: 0 auto;
}
.hll-prose__updated {
    font-size: 0.8125rem;
    color: var(--hll-text-muted);
    margin-bottom: var(--hll-space-xl);
}
.hll-prose h2 {
    font-family: var(--font-heading);
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--hll-text);
    margin-top: var(--hll-space-2xl);
    margin-bottom: var(--hll-space-sm);
}
.hll-prose h2:first-child,
.hll-prose__updated + h2 {
    margin-top: 0;
}
.hll-prose h3 {
    font-family: var(--font-heading);
    font-size: 1.0625rem;
    font-weight: 700;
    color: var(--hll-text);
    margin-top: var(--hll-space-lg);
    margin-bottom: var(--hll-space-xs);
}
.hll-prose p {
    font-size: 0.9375rem;
    line-height: 1.7;
    color: var(--hll-text-light);
    margin-bottom: var(--hll-space-md);
}
.hll-prose p:last-child {
    margin-bottom: 0;
}
.hll-prose a {
    color: var(--hll-green);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.hll-prose a:hover {
    color: var(--hll-bg-dark);
}
.hll-prose strong {
    color: var(--hll-text);
    font-weight: 600;
}

/* Bullet indent — fixes flush-left lists in legal copy. */
.hll-prose ul,
.hll-prose ol {
    padding-left: 1.5em;
    margin: 0 0 var(--hll-space-md);
}
.hll-prose ul { list-style: disc; }
.hll-prose ol { list-style: decimal; }
.hll-prose li {
    margin-bottom: var(--hll-space-sm);
    color: var(--hll-text-light);
    font-size: 0.9375rem;
    line-height: 1.7;
}

.hll-prose__table {
    width: 100%;
    border-collapse: collapse;
    margin: var(--hll-space-lg) 0;
    font-size: 0.875rem;
}
.hll-prose__table th,
.hll-prose__table td {
    text-align: left;
    padding: var(--hll-space-sm) var(--hll-space-md);
    border-bottom: 1px solid var(--hll-border);
}
.hll-prose__table th {
    font-weight: 600;
    color: var(--hll-text);
    background: var(--hll-bg);
}
.hll-prose__table td {
    color: var(--hll-text-light);
}

/* ============================================
   RETAILER DETAIL — v2 patch (2026-04-29)
   - Pill matches intro_band width (800px) and stretches full width
   - Business name h2 inside pill
   - Description block under pill, paragraph-aware
   - Media tile corners 4px (was 12px)
   - Modal stage 1:1 square, image fits inside via object-fit:contain
   - Social row spaced 3x from preceding row
   ============================================ */
.retailer-detail__inner {
    max-width: 800px;
    margin: 0 auto;
}
.retailer-detail__card {
    width: 100%;
    box-sizing: border-box;
}
.retailer-detail__name {
    font-family: var(--font-heading);
    font-weight: 700;
    color: var(--hll-green);
    font-size: 1.5rem;
    line-height: 1.2;
    margin: 0 0 var(--space-sm);
}
@media (min-width: 700px) {
    .retailer-detail__name { font-size: 1.875rem; }
}

.retailer-detail__social {
    margin-top: calc(var(--space-md) * 3);
}

.retailer-detail__description {
    margin-top: var(--space-lg);
    font-family: var(--font-body);
}
.retailer-detail__description p {
    margin: 0 0 var(--space-md);
    line-height: 1.65;
    color: var(--color-text);
}
.retailer-detail__description p:last-child {
    margin-bottom: 0;
}

/* Sharper media tiles. */
.retailer-media__slide {
    border-radius: 4px;
}

/* Modal: enforce square stage and contained media. */
.retailer-media__modal-stage {
    width: min(96vw, 720px);
    aspect-ratio: 1 / 1;
    max-height: 90vh;
    background: #000;
    border-radius: 4px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}
.retailer-media__modal-media {
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    object-fit: contain;
    display: block;
    border-radius: 0;
    box-shadow: none;
}
@media (min-width: 900px) {
    .retailer-media__modal-stage { width: min(80vh, 720px); }
}

/* ============================================
   .btn AUTHORITATIVE — added 2026-04-29
   The shared-prose / hll-prose link rules apply text-decoration:underline
   to anchor descendants. When a CTA <a class="btn"> sits inside prose
   (e.g. /get-involved Get-in-touch button), the prose rule wins on
   specificity and the button renders underlined.
   These selectors crank specificity to override prose, and !important
   makes the no-underline rule authoritative regardless of where the
   button is placed in the DOM.
   ============================================ */
.btn,
a.btn,
body .shared-prose a.btn,
body .shared-prose .btn,
body .hll-prose a.btn,
body .hll-prose .btn {
    text-decoration: none !important;
}
.btn:hover,
.btn:focus,
a.btn:hover,
a.btn:focus,
body .shared-prose a.btn:hover,
body .shared-prose a.btn:focus,
body .hll-prose a.btn:hover,
body .hll-prose a.btn:focus {
    text-decoration: none !important;
}

/* ============================================
   RETAILER DETAIL — v3 patch (2026-04-29)
   - Gold intro_band removed from retailer detail; page now starts at the
     white pill so add a small top breathing space.
   - Media scroller no longer appears as a separate gray section; pull it
     inline with the rest of the content (max-width: 800px to match pill).
   ============================================ */
.retailer-detail-section {
    padding-top: var(--space-xl);
}
.retailer-media-section {
    background: transparent;
    padding: 0 0 var(--space-xl);
}
.retailer-media__inner {
    max-width: 800px;
    margin: 0 auto;
    padding: 0 var(--space-md);
}

/* ============================================
   RETAILER MEDIA — full-bleed override (2026-04-29)
   Pill and description stay in the 800px column. Only the image carousel
   goes edge-to-edge using the same break-out trick as .scroller (homepage
   businesses).
   ============================================ */
.retailer-media__inner {
    max-width: none;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    padding: 0;
}
.retailer-media__scroller {
    padding-left: var(--space-md);
    padding-right: var(--space-md);
    scroll-padding-left: var(--space-md);
}
@media (min-width: 700px) {
    .retailer-media__scroller {
        padding-left: var(--space-lg);
        padding-right: var(--space-lg);
        scroll-padding-left: var(--space-lg);
    }
}

/* ============================================
   RETAILER DETAIL — v4 patch (2026-04-29)
   1. Description typography matches .scroller-section__description body
      type (Inter, 17px / 19px desktop, 1.6 / 1.65 line-height).
   2. Margin above the image carousel so it isn't flush against prose.
   3. Modal stage uses max-width: min(90vw, 90vh) per spec — square never
      exceeds either viewport axis.
   ============================================ */

/* (1) Description typography — clones .scroller-section__description body
        rules but without the centering / 760px constraint that would
        conflict with the 800px content column. */
.retailer-detail__description {
    font-family: var(--font-body);
    font-size: 17px;
    line-height: 1.6;
    color: var(--color-text);
    margin-top: var(--space-lg);
}
.retailer-detail__description p { margin: 0 0 1em; }
.retailer-detail__description p:last-child { margin-bottom: 0; }
@media (min-width: 600px) {
    .retailer-detail__description { font-size: 19px; line-height: 1.65; }
}

/* (2) Breathing room above the edge-to-edge carousel. */
.retailer-media-section {
    margin-top: var(--space-xxl);
}

/* (3) Modal stage — square within either viewport axis, image contained. */
.retailer-media__modal-stage {
    width: auto;
    height: auto;
    max-width: min(90vw, 90vh);
    max-height: min(90vw, 90vh);
    aspect-ratio: 1 / 1;
}
.retailer-media__modal-media {
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    object-fit: contain;
}

/* ============================================
   RETAILER MEDIA MODAL — square enforcement v5 (2026-04-29)
   Earlier patches set width:auto / max-width without a concrete width, so
   aspect-ratio:1/1 had nothing to derive from and the stage collapsed to
   the image's intrinsic dimensions. This patch sets a concrete square
   (min of 90vw / 90vh on both axes) and cancels every earlier max-* cap
   with !important so the cascade can't re-collapse the box.
   ============================================ */
.retailer-media__modal-stage {
    width:      min(90vw, 90vh) !important;
    height:     min(90vw, 90vh) !important;
    max-width:  none !important;
    max-height: none !important;
    aspect-ratio: 1 / 1 !important;
    box-sizing: border-box !important;
    background: #000 !important;
    border-radius: 4px !important;
    overflow: hidden !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
}
.retailer-media__modal-media {
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    max-height: none !important;
    object-fit: contain !important;
    display: block !important;
    border-radius: 0 !important;
    box-shadow: none !important;
}

/* ============================================
   LOGIN VIDEO — coverage fix (2026-04-29)
   Earlier `position: fixed; top: 60px; height: calc(100vh - 60px); z-index: -1`
   put the video behind the body's stacking context, leaving visible gaps when
   the page extended past one viewport (footer area, scrollbar). Re-anchor to
   the .login-page parent (which already has position: relative; overflow:
   hidden; min-height: calc(100vh - 60px)) — the video now fills that box
   exactly, no z-index trickery. !important to outrank the original rule.
   ============================================ */
.login-page__video {
    position: absolute !important;
    inset: 0 !important;
    top: 0 !important;
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
    z-index: 0 !important;
}
.login-page__card {
    position: relative;
    z-index: 1;
}

/* ============================================
   RETAILER CAROUSEL — margin tweak (2026-04-29)
   Reduce gap above the carousel to match the inter-image gutter (18px).
   ============================================ */
.retailer-media-section {
    margin-top: 18px !important;
}

/* ============================================
   RETAILER DETAIL — capability section + info modal (2026-04-29)
   - Margin between social row and capability text rows
   - Click-through modal explaining each capability
   ============================================ */
.retailer-detail__capabilities {
    margin-top: var(--space-md);
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}

.retailer-detail__modal {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.7);
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-md);
    box-sizing: border-box;
}
.retailer-detail__modal-card {
    background: #fff;
    border-radius: 10px;
    border: 2px solid var(--hll-green);
    padding: var(--space-xl);
    max-width: 480px;
    width: 100%;
    box-sizing: border-box;
    position: relative;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
}
.retailer-detail__modal-close {
    position: absolute;
    top: var(--space-sm);
    right: var(--space-sm);
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 0;
    background: transparent;
    color: var(--hll-green);
    font-size: 1.75rem;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}
.retailer-detail__modal-close:hover,
.retailer-detail__modal-close:focus {
    background: var(--hll-green);
    color: #fff;
}
.retailer-detail__modal-title {
    font-family: var(--font-heading);
    font-weight: 700;
    color: var(--hll-green);
    font-size: 1.25rem;
    line-height: 1.2;
    margin: 0 var(--space-xl) var(--space-sm) 0;
}
.retailer-detail__modal-body {
    font-family: var(--font-body);
    color: var(--hll-green);
    font-size: 1rem;
    line-height: 1.55;
    margin: 0;
}

/* ============================================
   RETAILER DESCRIPTION FONT — defensive enforcement (2026-04-29)
   Earlier patches set font-family: var(--font-body) on the description
   wrapper but children <p> elements inherit by default — and that
   inheritance can be silently broken by any stale CSS in a browser cache.
   Bump specificity (body-prefix) and add !important on BOTH the wrapper
   AND the <p> children so the brand body font is guaranteed regardless of
   cascade order or cached older rules.
   ============================================ */
body .retailer-detail__description,
body .retailer-detail__description p {
    font-family: var(--font-body) !important;
}

/* ============================================
   LATEST RESULTS CAROUSEL — added 2026-04-29
   Horizontal scroll-snap carousel, one panel per historical draw.
   Native swipe via overflow-x; arrow buttons + indicator below.
   ============================================ */
.latest-results__carousel {
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    /* hide native scrollbar */
    scrollbar-width: none;
}
.latest-results__carousel::-webkit-scrollbar { display: none; }

.latest-results__panel {
    flex: 0 0 100%;
    width: 100%;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    box-sizing: border-box;
    padding: 0 var(--space-md);
}
@media (min-width: 700px) {
    .latest-results__panel { padding: 0 var(--space-lg); }
}

/* In-paragraph month link + retailer pill link — green, underlined, brand-coloured. */
.latest-results__month-link,
.latest-results__pill-retailer-link {
    color: var(--hll-green);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.latest-results__month-link:hover,
.latest-results__month-link:focus,
.latest-results__pill-retailer-link:hover,
.latest-results__pill-retailer-link:focus {
    color: var(--hll-gold);
}

/* Carousel nav controls */
.latest-results__nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-md);
    margin-top: var(--space-md);
}
.latest-results__nav-btn {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 1.5px solid var(--hll-green);
    background: #fff;
    color: var(--hll-green);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, color 0.15s, opacity 0.15s;
}
.latest-results__nav-btn:hover:not(:disabled),
.latest-results__nav-btn:focus:not(:disabled) {
    background: var(--hll-green);
    color: #fff;
}
.latest-results__nav-btn:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}
.latest-results__nav-indicator {
    font-family: var(--font-body);
    font-size: 0.875rem;
    color: var(--color-text);
    min-width: 8em;
    text-align: center;
}

/* ============================================
   ITEM A — login card mobile gutter (2026-04-29)
   .login-page__card with min-width: 320px would overflow narrow viewports.
   Add side padding on the page wrapper, drop the min-width, and constrain
   max-width to 480px so the card sits comfortably centred on every screen.
   ============================================ */
.login-page {
    padding: 0 var(--space-md) !important;
}
.login-page__card {
    min-width: 0 !important;
    max-width: min(100%, 480px) !important;
    margin: 0 auto !important;
}

/* ============================================
   ITEM B — bottom-of-section gap to footer (2026-04-29)
   The .scroller-section and .sponsor-grid-section had 48–128px bottom
   padding. Tighten to 18px so the last tile/row sits a sensible gutter
   away from the footer (matches inter-tile gap).
   ============================================ */
.scroller-section,
.sponsor-grid-section,
.retailer-media-section {
    padding-bottom: 18px !important;
}

/* ============================================
   FORM LABEL ALIGNMENT (2026-04-29)
   When two .subscribe-form__field sit side-by-side and one label wraps to a
   second line (e.g. "Ticket from (optional)"), the inputs misalign. Reserve
   2.6em min-height on every .subscribe-form__label so single-line labels
   leave room equivalent to a two-line label, keeping inputs Y-aligned.
   Applies across /collections, /ticket-drops, /claim, /contact, /add-business,
   /become-a-retailer.
   ============================================ */
.subscribe-form__label {
    min-height: 2.6em;
    display: flex;
    align-items: flex-end;
}

/* ============================================
   ADMIN DRAWER — grouped sections + sticky logout (2026-04-29)
   Each conceptual block (Dashboard / Operations / Business / User / Logout)
   is its own .hll-nav__section. Bump margin between consecutive grouped
   sections so the visual gap reads as separation. Logout is pinned to the
   bottom of the drawer via flex margin-top:auto.
   ============================================ */
.hll-nav {
    display: flex;
    flex-direction: column;
    min-height: 100%;
}
.hll-nav__section--grouped + .hll-nav__section--grouped {
    /* Halved twice now (2026-05-04) to match the generic rule. Was 4px;
       then 2px; now 1px. */
    margin-top: calc(var(--space-xs) / 4);
}
.hll-nav__section--logout {
    margin-top: auto !important;
}

/* ============================================================
   ADMIN PENDING-ITEMS NOTIFICATION BAR (.shared-notify)
   White pills with dark-green border, sit between header and main.
   Hidden when all counts = 0. Container is transparent.
   ============================================ */
.shared-notify {
    background: transparent;
    max-width: 900px;
    margin: 0 auto var(--space-sm);
    padding: var(--space-sm) var(--space-md) 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    align-items: stretch;
    box-sizing: border-box;
}
@media (min-width: 700px) {
    .shared-notify {
        flex-direction: row;
        flex-wrap: wrap;
        gap: var(--space-md);
        align-items: center;
        padding: var(--space-sm) var(--space-md) 0;
    }
}
.shared-notify__row {
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: var(--space-sm);
    background: #ffffff;
    color: var(--hll-green);
    border: 2px solid var(--hll-green);
    border-radius: 12px;
    padding: var(--space-sm) var(--space-md);
    font-family: var(--font-body);
    font-size: 0.95rem;
    line-height: 1.3;
}
.shared-notify__count {
    font-weight: 700;
    background: var(--hll-green);
    color: #fff;
    padding: 2px 10px;
    border-radius: 999px;
    font-size: 0.85rem;
    min-width: 28px;
    text-align: center;
    line-height: 1.2;
}
.shared-notify__link {
    color: var(--hll-green);
    text-decoration: underline;
    font-weight: 600;
    margin-left: var(--space-xs);
}
.shared-notify__link:hover {
    color: #0d2418;
}

/* ============================================================
   ADD-MEDIA CTA TILE inside .scroller carousel (admin edit form)
   ============================================ */
.scroller__tile--add {
    background: #fff;
    border: 2px dashed var(--hll-green);
    border-radius: 8px;
    display: flex !important;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-xs);
    color: var(--hll-green);
    text-decoration: none;
    padding: var(--space-md);
    text-align: center;
}
.scroller__tile--add:hover, .scroller__tile--add:focus {
    background: #FFF8E1;
    border-style: solid;
}
.scroller__add-icon {
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 56px;
    line-height: 1;
    color: var(--hll-green);
}
.scroller__add-label {
    font-family: var(--font-body);
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--hll-green);
    line-height: 1.3;
}

/* ============================================================
   STATEMENT (DataTables) — typography override
   DataTables CDN ships its own font stack; clamp it back to brand.
   ============================================ */
#statementTable,
#statementTable_wrapper,
#statementTable_wrapper .dataTables_filter,
#statementTable_wrapper .dataTables_filter input,
#statementTable_wrapper .dataTables_info,
#statementTable_wrapper .dataTables_length,
#statementTable_wrapper .dataTables_length select,
#statementTable_wrapper .dataTables_paginate,
#statementTable_wrapper .dataTables_paginate .paginate_button,
#statementTable thead th,
#statementTable tbody td {
    font-family: var(--font-body) !important;
    color: var(--hll-green);
}
#statementTable thead th {
    font-family: var(--font-heading) !important;
    font-weight: 700;
    white-space: nowrap !important;
}

/* ============================================================
   ADMIN LISTING TYPOGRAPHY — applied inside .subscribe-form-section__body
   so listings (signups, claims, statement, future) inherit brand stack
   regardless of any third-party CSS (DataTables, etc).
   ============================================ */
.subscribe-form-section__body,
.subscribe-form-section__body p,
.subscribe-form-section__body span,
.subscribe-form-section__body td,
.subscribe-form-section__body th,
.subscribe-form-section__body input,
.subscribe-form-section__body select,
.subscribe-form-section__body button,
.subscribe-form-section__body a {
    font-family: var(--font-body);
}
.subscribe-form-section__body h1,
.subscribe-form-section__body h2,
.subscribe-form-section__body h3 {
    font-family: var(--font-heading);
}

/* Slim listing rows used by /signups-pending and /claims-pending —
   one row = name | date | Review. Keep scannable, mobile-stacks. */
.hll-listing {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}
.hll-listing__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-md);
    padding: var(--space-sm) var(--space-md);
    background: #fff;
    border: 1.5px solid var(--hll-green);
    border-radius: 8px;
}
.hll-listing__name {
    font-family: var(--font-body);
    font-weight: 600;
    color: var(--hll-green);
    font-size: 1rem;
    flex: 1 1 200px;
}
.hll-listing__date {
    font-family: var(--font-body);
    color: var(--color-text);
    opacity: 0.75;
    font-size: 0.88rem;
    flex: 0 0 auto;
}
.hll-listing__cta {
    flex: 0 0 auto;
    text-decoration: none;
}
.hll-listing__empty {
    padding: var(--space-md);
    background: #fff;
    border: 1.5px solid var(--hll-green);
    border-radius: 8px;
    color: var(--color-text);
    font-family: var(--font-body);
    margin: 0;
}
@media (min-width: 600px) {
    .hll-listing__row {
        flex-wrap: nowrap;
    }
}

/* Delete button styling for /claims-pending and /signups-pending rows. */
.btn--danger {
    background: #c0392b;
    color: #fff;
    border: 2px solid #c0392b;
}
.btn--danger:hover, .btn--danger:focus {
    background: #962d21;
    border-color: #962d21;
    color: #fff;
}
.hll-listing__delete-form {
    margin: 0;
    flex: 0 0 auto;
}
.hll-listing__delete {
    cursor: pointer;
    font-family: var(--font-body);
    font-size: 0.88rem;
    padding: 6px 12px;
    border-radius: 4px;
}

/* ============================================================
   ADMIN TYPOGRAPHY OVERRIDE
   Scoped to body[data-site="admin"]. Forces brand stack on every
   element so legacy admin.css (--adm-font on h1/h2/buttons) and
   any other CSS source can't drift the typography.
   ============================================ */
body[data-site="admin"],
body[data-site="admin"] input,
body[data-site="admin"] select,
body[data-site="admin"] textarea,
body[data-site="admin"] button,
body[data-site="admin"] table,
body[data-site="admin"] th,
body[data-site="admin"] td,
body[data-site="admin"] p,
body[data-site="admin"] span,
body[data-site="admin"] a,
body[data-site="admin"] li,
body[data-site="admin"] label,
body[data-site="admin"] legend,
body[data-site="admin"] code,
body[data-site="admin"] em,
body[data-site="admin"] strong {
    font-family: var(--font-body) !important;
}
body[data-site="admin"] h1,
body[data-site="admin"] h2,
body[data-site="admin"] h3,
body[data-site="admin"] h4,
body[data-site="admin"] h5,
body[data-site="admin"] h6 {
    font-family: var(--font-heading) !important;
}

/* Carousel that visually fuses with the preceding .latest-results-section
   (same cream bg, no top padding so the blocks read as one). */
.scroller-section--flush-results {
  background: #F8F6F1;
  padding-top: 0;
}
.latest-results-section:has(+ .scroller-section--flush-results) {
  padding-bottom: var(--space-md);
}
@media (min-width: 600px) {
  .latest-results-section:has(+ .scroller-section--flush-results) {
    padding-bottom: var(--space-lg);
  }
}

/* Canonical subscribe-form checkbox row. Used for opt-in style choices
   (e.g. /become-a-retailer's "Accept the Horsforth Pound"). Keeps the
   chrome aligned with the rest of the form: Inter body, dark-green focus
   ring, generous tap target. */
.subscribe-form__check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  background: rgba(245, 240, 232, 0.45);
  border: 1.5px solid #e6dfd2;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.subscribe-form__check:hover {
  background: rgba(245, 240, 232, 0.85);
  border-color: var(--hll-green);
}
.subscribe-form__check-input {
  margin-top: 4px;
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  accent-color: var(--hll-green);
  cursor: pointer;
}
.subscribe-form__check-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
}
.subscribe-form__check-title {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--color-text);
  line-height: 1.4;
}
.subscribe-form__check-sub {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 0.8125rem;
  color: rgba(0, 0, 0, 0.6);
  line-height: 1.45;
}
.subscribe-form__check--locked {
  opacity: 0.55;
  cursor: not-allowed;
  background: #f7f5f0;
}
.subscribe-form__check--locked:hover {
  background: #f7f5f0;
  border-color: #e6dfd2;
}
.subscribe-form__check--locked .subscribe-form__check-input { cursor: not-allowed; }

/* ============================================
   MOBILE GUTTER — tighten outer page padding on
   narrow viewports so content sits ~12px from the
   screen edge, matching the grid-tile gap. Applies
   to every page using .subscribe-form-section
   (admin listings + admin forms + public subscribe).
   ============================================ */
@media (max-width: 600px) {
  .subscribe-form-section {
    padding: var(--space-lg) 12px;
  }
}
/* ── .lottery-winner-callout — rotating latest-winner strip ───────
   Single-line callout below the story-so-far row with one winner
   sentence at a time. Vanilla JS in the same file rotates through
   data-rotate-interval ms (5000 by default) with a 300ms fade. Sits on
   the same cream muted background as the surrounding sections so it
   reads as a continuation of the stats strip. */
.lottery-winner-callout {
  background: var(--color-bg-muted);
  padding: var(--space-md);
  text-align: center;
}
.lottery-winner-callout__text {
  max-width: 800px;
  margin: 0 auto;
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--color-text);
  transition: opacity 0.3s ease;
}
.lottery-winner-callout__text.is-fading {
  opacity: 0;
}
.lottery-winner-callout__data {
  display: none;
}

/* ── .lottery-winner-callout--quote — modifier for the templated quotes
   section. Section spans full viewport width with the canonical
   horizontal page gutter (matches richtext); the quote TEXT and dot
   row are centred inside and constrained to the 720px reading width
   (also matches richtext). Reuses base callout chrome (background via
   inline style, .__text fade transition, rotator JS) and adds:
     - font-family / font-size / font-weight consumption from CSS custom
       properties set inline by the partial (so admin's per-section
       typography choices apply per-instance)
     - <cite> styled smaller + italic so attribution reads as a tag

   Selector chains .template-section.lottery-winner-callout--quote to
   bump specificity to 0,2,0 — the base .template-section rule (further
   down the file) sets padding: 0 with equal single-class specificity
   and would otherwise clobber the gutter on cascade order. */
.template-section.lottery-winner-callout--quote {
  padding: 0 var(--space-md);
}
.lottery-winner-callout--quote .lottery-winner-callout__text {
  max-width: 720px;
  margin: 0 auto;
  font-family: var(--quote-font-family, var(--font-body));
  font-size:   var(--quote-font-size, 22px);
  font-weight: var(--quote-font-weight, 400);
  line-height: 1.4;
}
.lottery-winner-callout--quote .lottery-winner-callout__dots {
  max-width: 720px;
  margin-left: auto;
  margin-right: auto;
}
.lottery-winner-callout__cite {
  display: block;
  margin-top: var(--space-xs);
  font-size: 0.85em;
  font-style: italic;
  font-weight: 400;
  opacity: 0.7;
}

/* ── .subscribe-form__row — canonical multi-input repeating row.
   Stacked on mobile, columns on desktop. Modifiers set the column
   ratio per use-case (e.g. --quotes for the quotes editor's
   text / attribution / remove triplet). Mirrors the inline grid
   pattern in admin/Views/admin/reimbursement_form.php's notes
   repeater, lifted into reusable chrome. */
.subscribe-form__row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-sm);
  align-items: center;
  margin-bottom: var(--space-sm);
}
@media (min-width: 600px) {
  .subscribe-form__row--quotes { grid-template-columns: 2fr 1fr auto; }
}
.subscribe-form__row-remove {
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1.5px solid var(--hll-green);
  border-radius: 50%;
  background: transparent;
  color: var(--hll-green);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  font-family: var(--font-body);
  justify-self: end;
}
.subscribe-form__row-remove:hover,
.subscribe-form__row-remove:focus {
  background: var(--hll-green);
  color: #fff;
}

/* Contact pill — page-constrained reading width with horizontal gutter
   matching richtext. Section keeps horizontal inset only; vertical
   breathing room comes from .template-section--spacer rows. The pill
   itself is constrained to the templated reading column (720px, same
   as richtext) by scoping .retailer-detail__inner here — the legacy
   detail page's 800px inner rule is unaffected.

   Selector intentionally chains .template-section.template-section--contact-pill
   to bump specificity to 0,2,0 — the base .template-section { padding: 0 }
   rule lives further down the file and would otherwise clobber the
   horizontal gutter on cascade. */
.template-section.template-section--contact-pill {
  padding: 0 var(--space-md);
}
.template-section.template-section--contact-pill .retailer-detail__inner {
  max-width: 720px;
}

/* ── .retailer-detail__card--contactpill — modifier used by the
   templated contact_pill section. Reads inline CSS custom properties
   the partial sets per-instance (bg / border / radius / font /
   alignment) so admin's per-section choices apply without touching
   the base .retailer-detail__card rule (legacy detail page is
   unaffected). Falls back to canonical defaults when a var is unset. */
.retailer-detail__card--contactpill {
  background: var(--contactpill-bg, #fff);
  border-color: var(--contactpill-border, var(--hll-green));
  border-radius: var(--contactpill-radius, 12px);
  text-align: var(--contactpill-text-align, left);
  font-family: var(--contactpill-font-family, var(--font-body));
  font-size:   var(--contactpill-font-size, 16px);
  font-weight: var(--contactpill-font-weight, 400);
}
/* Row alignment follows the configured text-align: left (default flex
   alignment), centre (icon+text centred together), right (icon trails). */
.retailer-detail__card--contactpill .retailer-detail__row {
  font-family: inherit;
  font-size:   inherit;
  font-weight: inherit;
  justify-content: var(--contactpill-row-justify, flex-start);
}
.retailer-detail__card--contactpill[style*="--contactpill-text-align: center"] .retailer-detail__row { justify-content: center; }
.retailer-detail__card--contactpill[style*="--contactpill-text-align: right"]  .retailer-detail__row { justify-content: flex-end; }
.retailer-detail__card--contactpill .retailer-detail__name {
  text-align: inherit;
  font-family: var(--contactpill-font-family, var(--font-heading));
}
/* Optional social row inside the pill: align with the rest of the
   pill's text alignment and add a touch of top margin like the legacy
   detail page does. */
.retailer-detail__social--contactpill {
  margin-top: var(--space-md);
  justify-content: flex-start;
}
.retailer-detail__card--contactpill[style*="--contactpill-text-align: center"] .retailer-detail__social--contactpill { justify-content: center; }
.retailer-detail__card--contactpill[style*="--contactpill-text-align: right"]  .retailer-detail__social--contactpill { justify-content: flex-end; }

/* Share button — same canonical .retailer-detail__social-link chrome,
   but rendered as <button> so it can fire JS instead of a navigation.
   Reset the user-agent button defaults so visually it's identical to
   the <a> social links. The share JS lives in townteam/public/js/public.js
   and is delegated off [data-share-action]. */
button.retailer-detail__social-link {
  border: 0;
  padding: 0;
  font: inherit;
  cursor: pointer;
}

/* ── .hll-toast — small fixed-position pill that confirms a copy-link
   action when navigator.share is unavailable. Centred at the bottom
   of the viewport, fades in for a couple of seconds, fades out. JS
   toggles --visible. */
.hll-toast {
  position: fixed;
  left: 50%;
  bottom: var(--space-lg);
  transform: translate(-50%, 0.5rem);
  background: var(--hll-green);
  color: #fff;
  padding: var(--space-sm) var(--space-md);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 0.875rem;
  font-weight: 500;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
  z-index: 9999;
}
.hll-toast--visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* ── .retailer-detail__social-link--templatebar — modifier used by
   the templated contact_bar section. Lets the section configure icon
   size + corner radius via inline CSS custom properties without
   touching the canonical 40×40 hll-green-circle base rule (which the
   legacy retailer detail page still uses). The glyph scales with the
   container at 50% so it stays visually balanced from 24px up to 96px. */
.retailer-detail__social--templatebar {
  justify-content: center;
  padding: var(--space-md) 0;
  margin: 0;
  flex-wrap: wrap;
}
.retailer-detail__social-link--templatebar {
  width:  var(--contactbar-icon-size, 48px);
  height: var(--contactbar-icon-size, 48px);
  border-radius: var(--contactbar-icon-radius, 50%);
}
.retailer-detail__social-link--templatebar svg {
  width:  50%;
  height: 50%;
}

/* ── .template-section--header — single-line centred page header.
   Template-level only (matches spacer + quotes pattern). Inline CSS
   custom properties on the section (--header-font-family/-size/-weight)
   set by the partial; consumed here so admin's per-section typography
   choices apply per-instance.

   Selector chains .template-section.template-section--header to bump
   specificity to 0,2,0 — the base .template-section { padding: 0 }
   rule lives further down the file at equal single-class specificity
   and would otherwise clobber the padding on cascade order. */
.template-section.template-section--header {
  padding: var(--space-md);
  text-align: center;
}
.template-section__header {
  margin: 0;
  font-family: var(--header-font-family, var(--font-heading, var(--font-body)));
  font-size:   var(--header-font-size, 36px);
  font-weight: var(--header-font-weight, 400);
  line-height: var(--header-line-height, 1.2);
  /* --header-text-colour is only emitted by the partial when the admin
     sets a hex. Unset → falls through to the inherit fallback, which
     resolves to the surrounding canonical --color-text rule. Avoids
     hardcoding #1B3A2D / etc. */
  color: var(--header-text-colour, var(--color-text));
}

/* Auto-rotator JS — applied via the data-rotate-interval attribute.
   Reads the JSON-LD-style <script> child for the callout array, swaps
   .lottery-winner-callout__text innerHTML on each tick with a fade.
   Self-skips when the callout list has 0–1 items. */

/* ── .lottery-stats-carousel — horizontal scroll-snap stats strip ──
   Replaces the two stacked .stats-bar rows with one horizontally-
   scrolling carousel; six tiles split across two pages, three tiles
   per page. The carousel controller in public.js auto-advances every
   5s, pauses on touch/hover, syncs the dot indicators, and re-resumes
   ~8s after the user stops interacting. Tile chrome (.stats-bar__card
   / __value / __label) is unchanged — only the wrapping container
   and dot indicators are new chrome. Same pattern at all breakpoints
   per the carousel spec (no device-specific layout fork). */
.lottery-stats-carousel-section {
  padding: 0 var(--space-md) var(--space-md);
  position: relative;
  z-index: 5;
  margin-top: -32px;
}
.lottery-stats-carousel-section::before {
  content: "";
  position: absolute;
  inset: 32px 0 0 0;
  background: var(--color-bg-muted);
  z-index: -1;
}
.lottery-stats-carousel {
  max-width: 1200px;
  margin: 0 auto;
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  position: relative;
  z-index: 6;
}
.lottery-stats-carousel::-webkit-scrollbar {
  display: none;
}
.lottery-stats-carousel__page {
  flex: 0 0 100%;
  display: flex;
  gap: var(--space-sm);
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.lottery-stats-carousel__page .stats-bar__card {
  flex: 1 1 0;
  min-width: 0;
}

/* Dot indicators — small, hll-green-tinted, click-to-jump. The active
   dot has a filled background; idle dots are bordered hollow circles.
   Shared between the homepage stats-carousel and the templated quotes
   section (.lottery-winner-callout__dots / __dot). One rule, two
   namespaces — keep both selectors aligned. */
.lottery-stats-carousel__dots,
.lottery-winner-callout__dots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-top: var(--space-sm);
}
.lottery-stats-carousel__dot,
.lottery-winner-callout__dot {
  width: 10px;
  height: 10px;
  padding: 0;
  border-radius: 50%;
  border: 1.5px solid var(--hll-green);
  background: transparent;
  cursor: pointer;
  transition: background 0.15s ease;
  appearance: none;
}
.lottery-stats-carousel__dot.is-active,
.lottery-stats-carousel__dot:hover,
.lottery-stats-carousel__dot:focus-visible,
.lottery-winner-callout__dot.is-active,
.lottery-winner-callout__dot:hover,
.lottery-winner-callout__dot:focus-visible {
  background: var(--hll-green);
}

@media (min-width: 600px) {
  .lottery-stats-carousel-section { margin-top: -37px; }
  .lottery-stats-carousel-section::before { top: 37px; }
}
@media (min-width: 1024px) {
  .lottery-stats-carousel-section { margin-top: -47px; }
  .lottery-stats-carousel-section::before { top: 47px; }
}

/* ── .subscribe-form__steps — canonical multi-step wizard progress ──
   Horizontal row of numbered dots, one per step. Active dot is filled
   hll-green; completed dots show a checkmark on a green background;
   future dots are hollow with their step number visible. Reusable for
   any multi-step form built on the .subscribe-form chrome (claim
   wizard, future onboarding flows, etc.). Sits above the heading and
   below the optional step-indicator text line. */
.subscribe-form__steps {
  list-style: none;
  margin: 0 0 var(--space-md);
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--space-sm);
}
.subscribe-form__step-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1.5px solid var(--hll-green);
  background: #fff;
  color: var(--hll-green);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
}
.subscribe-form__step-dot--active {
  background: var(--hll-green);
  color: #fff;
}
.subscribe-form__step-dot--done {
  background: var(--hll-green);
  color: #fff;
  border-color: var(--hll-green);
}

/* ── .subscribe-form__summary — review-row dl pattern ─────────────
   Used on the final review step of a multi-step form. Two-column dt/dd
   pairs (label + value) with the same border + padding rhythm as
   .subscribe-form__field rows. Reusable. */
.subscribe-form__summary {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.subscribe-form__summary-row {
  display: grid;
  grid-template-columns: 100px 1fr;
  gap: var(--space-sm);
  align-items: baseline;
  padding: var(--space-xs) 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
.subscribe-form__summary-row:last-child {
  border-bottom: 0;
}
.subscribe-form__summary-label {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--color-text);
  opacity: 0.7;
}
.subscribe-form__summary-value {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--color-text);
  word-break: break-word;
}

/* ── .subscribe-form__checklist — numbered "what happens next" list ──
   Reusable canonical pattern for any post-submission confirmation screen
   built on the .subscribe-form chrome. Numbers are rendered in hll-green
   in a circular badge to match .subscribe-form__step-dot rhythm. */
.subscribe-form__checklist {
  list-style: none;
  counter-reset: hll-checklist;
  margin: 0 0 var(--space-md);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.subscribe-form__checklist > li {
  counter-increment: hll-checklist;
  position: relative;
  padding-left: calc(28px + var(--space-sm));
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--color-text);
}
.subscribe-form__checklist > li::before {
  content: counter(hll-checklist);
  position: absolute;
  left: 0;
  top: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--hll-green);
  color: #fff;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  line-height: 28px;
  text-align: center;
}

/* ── .subscribe-form__note — block-level small-print note ──
   Sits below structured content (e.g. checklist) to call out
   inbox/spam-folder reminders without competing with the heading.
   Distinct from .subscribe-form__hint (which is an inline label suffix). */
.subscribe-form__note {
  margin: 0 0 var(--space-md);
  padding: var(--space-sm) var(--space-md);
  font-family: var(--font-body);
  font-size: 0.875rem;
  line-height: 1.5;
  color: rgba(0, 0, 0, 0.65);
  background: rgba(0, 0, 0, 0.03);
  border-left: 3px solid var(--hll-green);
  border-radius: 4px;
}

/* ── .subscribe-form__contact-links — list of mailto/contact links ──
   Used in confirmation/help sections of .subscribe-form pages.
   Vertical stack with subtle separators, no bullets. */
.subscribe-form__contact-links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.subscribe-form__contact-links > li {
  font-family: var(--font-body);
  font-size: 0.95rem;
}
.subscribe-form__contact-links > li > a {
  color: var(--hll-green);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.subscribe-form__contact-links > li > a:hover,
.subscribe-form__contact-links > li > a:focus {
  text-decoration: none;
}

/* ── .template-section* — enhanced listing template chrome ─────────
   Used by /retailer/{slug} when sponsors.template_slug is set. The
   public retailer_templated.php view loops sections from a JSON
   template and includes a per-type partial under
   shared/views/template_sections/. Each partial wraps in a single
   <section class="template-section template-section--{type}"> with
   the section's background_colour applied inline. */
.template-section {
  position: relative;
  width: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}
/* Placeholder text shown when a section's data is missing (e.g. empty
   carousel, missing video source, no map coords). Horizontal padding
   only — no vertical, since spacers are the canonical mechanism for
   adding vertical breathing room between sections. */
.template-section__placeholder {
  margin: 0;
  padding: 0 var(--space-md);
  text-align: center;
  font-family: var(--font-body);
  color: rgba(0, 0, 0, 0.55);
  font-size: 0.95rem;
}

/* Video — full viewport. Object-fit cover so it fills regardless of
   intrinsic aspect ratio. The section element holds a centred "Loading…"
   pill behind the iframe/<video>; once the player renders its content
   the pill is covered. No JS required — the iframe/video sits at z-index
   1 with a transparent backdrop until the player paints. */
/* Section height tracks the embed's natural 16:9 ratio at full viewport
   width — avoids the letterboxed whitespace bands that 100vh produced
   on landscape video. The iframe / <video> child fills 100%×100% of the
   section, so the actual paint area equals the section box. If a future
   template needs a different ratio (e.g. 9:16 vertical), add a
   section-level aspect_ratio field and inline it via custom property. */
.template-section--video {
  width: 100%;
  aspect-ratio: 16 / 9;
}
/* Breakpoint-aware sizing for the templated video section. The partial
   emits TWO pairs of vars: --video-aspect-mobile/desktop AND
   --video-height-mobile/desktop. When the admin picks a fixed ratio
   (16:9, 9:16, etc.) the partial emits the ratio + height:auto, and
   aspect-ratio governs (width:100% × ratio). When the admin picks
   'viewport' the partial emits aspect-ratio:auto + height:100vh, and
   the explicit height wins per CSS rules (an explicit dimension always
   wins over aspect-ratio computation). Compound selector (0,2,0)
   overrides the single-class default above. Mirrors the hero
   --hero-aspect-* pattern so any structural fix to the hero cascade
   can be ported here verbatim — the only video-specific piece is the
   height var, which is video-only because hero already gets viewport
   sizing from its inner .shared-hero__frame chrome. */
.template-section.template-section--video {
  aspect-ratio: var(--video-aspect-mobile, 16 / 9);
  height: var(--video-height-mobile, auto);
}
@media (min-width: 600px) {
  .template-section.template-section--video {
    aspect-ratio: var(--video-aspect-desktop, 16 / 9);
    height: var(--video-height-desktop, auto);
  }
}
.template-section--video::before {
  content: 'Loading…';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 0;
  padding: 6px 16px;
  border-radius: 999px;
  background: var(--hll-cream, #f4f0e6);
  color: rgba(0, 0, 0, 0.55);
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  pointer-events: none;
}
.template-section__video,
.template-section__video-iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  position: relative;
  z-index: 1;
}
.template-section__video {
  object-fit: cover;
}

/* Map — 80vh height applies to the LEAFLET DIV, not the wrapping
   section. When the sponsor has no usable coords the partial renders
   only a placeholder paragraph and the section collapses to natural
   height. Otherwise the .__map div spans 80vh. Per-section dom id keeps
   multiple maps independent. Inherits .shared-map .leaflet-container
   sizing rules from common.css elsewhere. */
/* .template-section__map removed 2026-05-09: the templated map section
   now delegates to the canonical /shared/views/map.php component, which
   renders a `<div id="shared-map" class="shared-map shared-map--detail">`
   styled by .shared-map / .shared-map--detail (line 344 / 1845). The
   inner div this rule used to style no longer exists. */

/* Templated carousel section consumes the canonical .scroller chrome
   verbatim (lines 1180–1247) — same family used by winners_scroller.php,
   latest_results.php, and the admin sponsor media tile grid. No
   bespoke .template-section__carousel rules — the partial just emits
   .scroller / __inner / __tile / __img markup and the chrome carries
   it. The .template-section--carousel class on the <section> stays
   as a semantic hook for future targeting; no rules under it today. */

/* Richtext — page-constrained reading width. Matches the typography of
   other body copy on the site. Section keeps horizontal inset only —
   vertical breathing room comes from .template-section--spacer rows. */
.template-section--richtext {
  padding: 0 var(--space-md);
}
.template-section__richtext {
  max-width: 720px;
  margin: 0 auto;
  font-family: var(--richtext-font-family, var(--font-body));
  font-size: 1rem;
  line-height: 1.6;
  color: var(--color-text);
}
/* Compound selector for font-size + font-weight — bumps specificity
   to 0,2,0 so this rule survives any future base-rule edit ordering
   (mirrors the .template-section.template-section--contact-pill /
   --header pattern shipped on 2026-05-10). Both vars have defensive
   fallbacks so the canonical 16px / 400 still wins on consumers that
   haven't set the per-section overrides. */
.template-section.template-section--richtext .template-section__richtext {
  font-size:   var(--richtext-font-size, 16px);
  font-weight: var(--richtext-font-weight, 400);
  text-align:  var(--richtext-text-align, left);
  /* --richtext-text-colour is only emitted by the partial when the
     admin sets a hex. Unset → falls through to inherit from the
     surrounding canonical --color-text on .template-section__richtext
     (line 3553), so existing sections render unchanged. */
  color:       var(--richtext-text-colour, inherit);
}
/* Zero direct-child block margins inside __richtext so adjacent richtext
   sections butt flush. Without this, the default UA margins on the
   FIRST and LAST <p>/<h*> children are trapped inside the section's BFC
   (.template-section { overflow: hidden; }) and inflate the section box
   by 1em top + 1em bottom -- at font_size_px = 48 that is ~96px of
   phantom whitespace per join. Inter-sibling rhythm restored on the
   second rule below. Compound selector raises specificity to 0,2,1 so
   this survives any future base-rule edit ordering, mirroring the
   .template-section.template-section--cta .cta-block__body pattern
   (lines 3891-3892) shipped on 2026-05-11. Scope is richtext-only --
   no other section type's vertical metrics are touched. */
.template-section.template-section--richtext .template-section__richtext > * {
  margin-top: 0;
  margin-bottom: 0;
}
.template-section.template-section--richtext .template-section__richtext > * + * {
  margin-top: var(--space-sm);
}
.template-section__richtext h1,
.template-section__richtext h2,
.template-section__richtext h3 {
  font-family: var(--font-heading, var(--font-body));
  line-height: 1.2;
  color: var(--color-text);
}
.template-section__richtext a { color: var(--hll-green); text-decoration: underline; }
.template-section__richtext img { max-width: 100%; height: auto; }

/* Spacer — explicit vertical breathing room between other sections.
   Heights are passed in via the inline --spacer-h-m / --spacer-h-d
   custom properties (DATA, set in the partial), with sane fallbacks
   here. Mobile rule applies up to 599px; desktop from 600px. */
.template-section--spacer {
  width: 100%;
  height: var(--spacer-h-m, 40px);
}
@media (min-width: 600px) {
  .template-section--spacer {
    height: var(--spacer-h-d, 80px);
  }
}

/* ── Cookie consent banner ─────────────────────────────────────────
   Bottom-fixed, full viewport on mobile, max-width container on
   desktop. Two buttons of equal visual weight (no dark patterns).
   Reuses .subscribe-form__btn / --ghost; only the wrapper chrome
   here is new — there's no existing fixed-position banner pattern
   on the site to inherit from. JS lives in /js/cookie-consent.js
   and toggles the [hidden] attribute via #hll-cookie-banner. */
.hll-cookie-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 9000;
  background: #fff;
  border-top: 2px solid var(--hll-green);
  padding: var(--space-md);
  box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.08);
}
.hll-cookie-banner[hidden] { display: none; }
.hll-cookie-banner__inner {
  max-width: 1200px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.hll-cookie-banner__heading {
  font-family: var(--font-heading);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--hll-green);
  margin: 0 0 var(--space-xs);
}
.hll-cookie-banner__body {
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--color-text);
  margin: 0;
}
.hll-cookie-banner__link {
  color: var(--hll-green);
  text-decoration: underline;
}
.hll-cookie-banner__actions {
  display: flex;
  gap: var(--space-md);
}
.hll-cookie-banner__actions .subscribe-form__btn { flex: 1 1 0; width: auto; }
@media (min-width: 700px) {
  .hll-cookie-banner__inner {
    flex-direction: row;
    align-items: center;
    gap: var(--space-lg);
  }
  .hll-cookie-banner__copy { flex: 1 1 auto; }
  .hll-cookie-banner__actions { flex: 0 0 auto; }
  .hll-cookie-banner__actions .subscribe-form__btn { min-width: 140px; }
}

/* ── Scroller tile media modifier ─────────────────────────────────
   Used by the admin sponsor edit form's MEDIA section. Each existing
   media item is a tile that contains BOTH a click-to-view link and a
   small overlay delete form positioned top-right. Tiles are also
   draggable for reorder (drag/drop JS in add_business_form.php POSTs
   to /admin/sponsors/{id}/media/reorder). The base .scroller__tile
   chrome (sizing, margins, focus) is inherited untouched — this
   modifier only adds positioning + the overlay children. */
.scroller__tile--media {
  position: relative;
  cursor: grab;
}
.scroller__tile--media:active { cursor: grabbing; }
.scroller__tile-link {
  display: block;
  width: 100%;
  height: 100%;
  text-decoration: none;
}
.scroller__tile-delete {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 2;
  margin: 0;
}
.scroller__tile-delete-btn {
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: 50%;
  border: 0;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s;
}
.scroller__tile-delete-btn:hover,
.scroller__tile-delete-btn:focus {
  background: var(--hll-green);
  outline: none;
}

/* Manage-this-business pencil on templated retailer pages. Reuses the
   canonical .retailer-detail__social-link 40×40 hll-green-circle chrome
   conceptually, but the templated page has no contact card to attach to
   so it floats top-right. Cross-domain link to /claim/{slug} on the
   lottery host — same destination as the legacy retailer_detail.php
   pencil. */
.retailer-templated { position: relative; }
.retailer-templated__manage {
  position: absolute;
  top: var(--space-md);
  right: var(--space-md);
  z-index: 5;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--hll-green);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  transition: background 0.15s;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.retailer-templated__manage:hover,
.retailer-templated__manage:focus {
  background: var(--hll-gold);
  color: var(--hll-green);
}

/* ── .template-section--hero — templated full-bleed hero ───────────
   Reuses the canonical .shared-hero / __video / __scrim / __content
   / __headline / __tagline / __ctas chrome verbatim. Adds two
   inline-CSS-var-driven knobs: scrim opacity and text alignment.
   The hero's own scrim element (.template-section--hero__scrim) sits
   inside the section and consumes --hero-overlay-opacity. Text
   alignment via --hero-text-align (CSS var inside the section style
   attribute) — attribute selectors mirror the contact_pill pattern. */
.template-section--hero {
  position: relative;
  overflow: hidden;
}
/* Override the legacy .shared-hero__video chrome which uses
   `position: fixed; top:0; left:0; width:100vw; height:100vh;
   z-index:-2` (designed for the legacy full-page homepage hero
   where the video sits as a viewport background behind the body's
   transparent backdrop). On the templated retailer page the section
   has its own body chrome and stacking contexts that hide a fixed
   z-index:-2 element — the video needs to live INSIDE the section
   instead. Compound selector (specificity 0,2,0) beats the base
   rule (0,1,0) regardless of source order. Video sits at z-index:0
   above the section's background-image poster (the poster IS the
   section's CSS background, no stacking) and below the scrim
   (z-index:1) and frame content (z-index:2). */
.template-section--hero .shared-hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}
.template-section--hero__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, var(--hero-overlay-opacity, 0.4));
  z-index: 1;
  pointer-events: none;
}
.template-section--hero .shared-hero__content {
  text-align: var(--hero-text-align, left);
}
.template-section--hero[style*="--hero-text-align: center"] .shared-hero__ctas { justify-content: center; }
.template-section--hero[style*="--hero-text-align: right"]  .shared-hero__ctas { justify-content: flex-end; }

/* Aspect ratio + vertical alignment for the templated hero. Mobile
   default (≤599px) consumes --hero-aspect-mobile; desktop (≥600px)
   switches to --hero-aspect-desktop. The frame inside the section
   becomes a flex column whose align-items is driven by the
   --hero-content-vertical-align var so heading/sub/CTA can pin to
   the top, centre, or bottom of the hero rectangle. Compound
   selector (specificity 0,2,0) beats the base .shared-hero__frame
   (0,1,0) and the legacy .template-section--hero (0,1,0) without
   touching either of those base rules. */
.template-section.template-section--hero {
  aspect-ratio: var(--hero-aspect-mobile, 16/9);
  /* Canonical .shared-hero sets `min-height: calc(100vh - 60px)` for
     the legacy parallax homepage hero. On the templated hero that
     min-height defeats aspect-ratio (the larger of min-height and the
     ratio-derived height wins), so the section stays roughly
     viewport-tall regardless of the admin's chosen ratio. Reset it
     here so aspect-ratio governs height; the legacy .shared-hero
     used by /shared/views/hero.php is untouched. */
  min-height: 0;
}
@media (min-width: 600px) {
  .template-section.template-section--hero {
    aspect-ratio: var(--hero-aspect-desktop, 16/9);
  }
}
/* When the admin picks 'auto' for a breakpoint, the partial emits
   `aspect-ratio: auto` which CSS treats as "no fixed ratio" and the
   hero collapses to its content height + whatever max-height the
   surrounding chrome implies. Existing legacy hero rendering (no var
   set) falls back to the 16/9 default above — no visual regression. */
.template-section.template-section--hero .shared-hero__frame {
  display: flex;
  flex-direction: column;
  justify-content: var(--hero-content-vertical-align, center);
  height: 100%;
  box-sizing: border-box;
}

/* ── .template-section--cta — templated configurable CTA pill ──────
   Reuses the canonical .cta-block / __heading / __body / __btn chrome
   verbatim. Section partial emits inline CSS vars that override the
   baked-in .cta-block colours/fonts (--hll-green pill, white text,
   uppercase Ubuntu heading). Compound selectors (specificity 0,2,0)
   beat the base .cta-block rules (0,1,0) regardless of source order
   — same pattern used by --contact-pill, --header, --richtext, --hero.
   width_mode: --full keeps the canonical 1200px max-width; --constrained
   reins the pill into the 720px reading column shared with richtext.

   Horizontal gutter is breakpoint-aware: --cta-gutter-mobile drives
   the base rule (<600px); --cta-gutter-desktop overrides at ≥600px.
   The partial emits both vars on the section's inline style. Existing
   CTAs without the mobile field fall back to the desktop value in
   the PHP sanitiser, so no visual regression. */
.template-section.template-section--cta {
  padding-left:  var(--cta-gutter-mobile, 16px);
  padding-right: var(--cta-gutter-mobile, 16px);
}
@media (min-width: 600px) {
  .template-section.template-section--cta {
    padding-left:  var(--cta-gutter-desktop, 16px);
    padding-right: var(--cta-gutter-desktop, 16px);
  }
}
.template-section.template-section--cta .cta-block {
  background: var(--cta-pill-bg, var(--hll-green));
  color:      var(--cta-text-colour, #fff);
  /* Override the canonical .cta-block's bake-in padding (32px vert /
     32px horiz mobile → 48 / 32 tablet → 80 / 48 desktop, designed
     for the standalone homepage hero CTA). The padding shorthand
     here replaces ALL FOUR SIDES with the per-section --cta-pill-padding
     knob (0-200 px, default 48) so each pill carries its own breathing
     room around the content. Setting pill_padding_px=0 with section
     gutter_px=0 makes the pill content (including the image cell)
     truly flush to the viewport edges. Adjacent sections still stack
     flush at the section level — the gap between two CTAs is now
     exactly 2 × pill_padding_px (one bottom + one top), nothing more. */
  padding: var(--cta-pill-padding, 48px);
}
.template-section.template-section--cta .cta-block__heading {
  font-family: var(--cta-heading-font-family, var(--font-heading));
  font-size:   var(--cta-heading-font-size, 28px);
  font-weight: var(--cta-heading-font-weight, 700);
  color:       var(--cta-text-colour, #fff);
}
.template-section.template-section--cta .cta-block__body {
  font-family: var(--cta-body-font-family, var(--font-body));
  font-size:   var(--cta-body-font-size, 16px);
  font-weight: var(--cta-body-font-weight, 400);
  color:       var(--cta-text-colour, #fff);
}
/* Body is now a <div> wrapper containing one or more <p> blocks emitted
   by TemplateService::nlToParagraphs (textarea-typed paragraph breaks
   on \n\n). Reset margins so the wrapper stays flush, then add
   inter-paragraph spacing only between adjacent blocks. */
.template-section.template-section--cta .cta-block__body p { margin: 0; }
.template-section.template-section--cta .cta-block__body p + p { margin-top: 0.75em; }
.template-section.template-section--cta .cta-block__btn {
  font-family:  var(--cta-button-font-family, var(--font-body));
  font-size:    var(--cta-button-font-size, 16px);
  font-weight:  var(--cta-button-font-weight, 500);
  color:        var(--cta-button-colour, #fff);
  border-color: var(--cta-button-colour, #fff);
}
.template-section.template-section--cta .cta-block__btn:hover,
.template-section.template-section--cta .cta-block__btn:focus {
  background: var(--cta-button-colour, #fff);
  color:      var(--cta-pill-bg, var(--hll-green));
}
.template-section--cta--constrained .cta-block { max-width: 720px; }

/* ── Mailing list signup form inside a CTA-style pill ────────────
   The mailing_list_signup partial reuses the full .cta-block chrome
   verbatim (it emits the .template-section--cta class alongside
   .template-section--mailing-list-signup), so the colours / fonts /
   layout vars all apply. These rules only style the form-specific
   elements: the email-input + Subscribe-button row and the inline
   success / error message. The button itself uses .cta-block__btn
   so its colour and typography come from the same --cta-button-*
   vars the rest of the CTA family already consumes. */
.cta-block__signup-form {
  display: flex;
  /* Stack vertically (mobile-first) so the email input and Subscribe
     button each take the full pill width. The form only has 2 visible
     children (input + button) plus the inline message — column gives
     them matched widths without a media query. */
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-sm);
  margin-top: var(--space-md);
  width: 100%;
}
.cta-block__signup-label {
  /* Visually-hidden label — kept in the DOM for screen readers; the
     input's placeholder shows the visible cue. */
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}
.cta-block__signup-email {
  /* Full pill width — column layout means flex doesn't shrink this. */
  width: 100%;
  min-width: 0;
  padding: 0.7em 1em;
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.3;
  color: var(--color-text, #1B3A2D);
  background: #fff;
  border: 2px solid var(--cta-button-colour, #fff);
  border-radius: 10px;
  box-sizing: border-box;
}
.cta-block__signup-email:focus {
  outline: 2px solid var(--cta-button-colour, #fff);
  outline-offset: 2px;
}
.cta-block__signup-submit {
  /* Inherits .cta-block__btn chrome (font, border). With the column
     layout the gap on .cta-block__signup-form handles the spacing
     above the button — kill the bake-in margin-top from .cta-block__btn
     so the gap is the only thing pushing it down from the input.
     width:100% matches the input's full pill width above it. */
  margin-top: 0 !important;
  width: 100%;
}
.cta-block__signup-message {
  flex: 1 1 100%;
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.4;
  color: var(--cta-text-colour, #fff);
  min-height: 0;
}
.cta-block__signup-message:empty { display: none; }
.cta-block__signup-message--error { font-weight: 600; }
/* Inside the with-image content cell, the form sits at the bottom of
   the column (sibling to heading + body). justify-content:space-between
   on the content cell pushes the form to the bottom edge of the image,
   matching the same alignment the CTA button uses. */
.cta-block--with-image .cta-block__content > .cta-block__signup-form {
  margin-top: auto;
}

/* ── CTA with image — side-by-side at desktop, stacked at mobile ─────
   When the admin sets an image_path AND image_position 'left' or
   'right', the partial wraps heading+body+button in .cta-block__content
   and adds a .cta-block__image sibling. Source order is always
   image-first (so flex-direction: column at mobile lands the image on
   top regardless of position). At desktop, --image-right uses
   row-reverse to flip the visual order. The text-only layout (no
   image, position 'none') stays untouched — the partial doesn't
   render the --with-image modifier at all in that case. */
.template-section.template-section--cta .cta-block--with-image {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-lg);
  text-align: left;
}
.template-section.template-section--cta .cta-block--with-image .cta-block__image {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Media cell child — applies to <img>, <iframe> (YouTube/Vimeo embed)
   and <video> (uploaded MP4). The cell name stays .cta-block__image
   for CSS-stability across the image-and-video work, but the rules
   target all three element types so the aspect ratio and sizing are
   consistent regardless of which media type the admin picked. */
.template-section.template-section--cta .cta-block--with-image .cta-block__image img,
.template-section.template-section--cta .cta-block--with-image .cta-block__image iframe,
.template-section.template-section--cta .cta-block--with-image .cta-block__image video {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 6px;
  border: 0;
  /* Aspect ratio reuses the same enum as the image section type.
     'auto' (the inherited default) collapses the rule so the media
     renders at natural size. Fixed ratios crop via object-fit:cover
     (no-op on <iframe> but harmless). */
  aspect-ratio: var(--cta-image-aspect, auto);
  object-fit: cover;
}
/* When the aspect-ratio is fixed, height:auto can collapse iframe
   children since iframe has no intrinsic content height; force the
   container's computed height to apply so the iframe fills its
   parent's aspect-locked box. */
.template-section.template-section--cta .cta-block--with-image .cta-block__image iframe {
  height: 100%;
}
.template-section.template-section--cta .cta-block--with-image .cta-block__content {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  text-align: center;
}
@media (min-width: 600px) {
  /* Desktop — image and text sit side-by-side at ~50/50. row-reverse
     flips visual order for --image-right while source order (image
     first) stays unchanged. align-items:stretch makes both cells take
     the full row height (image determines the height; content cell
     stretches to match), so the content cell can use space-between
     to land its heading at the image's top edge and its button at
     the image's bottom edge — heading top, body middle, button bottom.
     Mobile keeps the base justify-content:center (the desktop override
     is scoped here, so the stacked-vertical mobile layout flows
     naturally with the existing gap). */
  .template-section.template-section--cta .cta-block--with-image {
    flex-direction: row;
    align-items: stretch;
  }
  .template-section.template-section--cta .cta-block--image-right {
    flex-direction: row-reverse;
  }
  .template-section.template-section--cta .cta-block--with-image .cta-block__image,
  .template-section.template-section--cta .cta-block--with-image .cta-block__content {
    flex: 1 1 50%;
    min-width: 0;
  }
  .template-section.template-section--cta .cta-block--with-image .cta-block__content {
    text-align: left;
    align-items: flex-start;
    justify-content: space-between;
  }
}

/* ── Media-only CTA collapse ───────────────────────────────────
   When the section has media but every text component is empty,
   the partial emits .cta-block.cta-block--media-only instead of
   the .cta-block--with-image flex wrapper. The media fills the
   full content width inside the existing --cta-pill-padding
   (pill padding still applies — set pill_padding_px to 0 in the
   editor if edge-to-edge media is wanted). --cta-image-aspect
   continues to drive the aspect-ratio so 16:9 gives a 16:9 box
   and auto gives natural ratio. Specificity stays at (0,2,0)
   for the wrapper rule so the higher-specificity (0,3,*) rules
   for --with-image continue to win on the side-by-side path. */
.template-section--cta .cta-block--media-only {
  display: block;
}
.template-section--cta .cta-block--media-only img,
.template-section--cta .cta-block--media-only iframe,
.template-section--cta .cta-block--media-only video {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 6px;
  border: 0;
  aspect-ratio: var(--cta-image-aspect, auto);
  object-fit: cover;
}
.template-section--cta .cta-block--media-only iframe {
  height: 100%;
}

/* ── .template-section--image — single configurable image ────────
   Pure template-level chrome. The partial emits inline CSS vars for
   aspect ratio, plus padding-left/right via the gutter and a
   background_colour. width_mode--constrained reins the figure into
   the 720px reading column shared with richtext + the constrained
   CTA. No bespoke namespace beyond .template-section--image and its
   __image-figure / __image-img / __image-link / __image-caption
   children — same naming pattern as __richtext. */
.template-section--image {
  /* padding-top/bottom matches the other section types so adjacent
     sections breathe consistently. */
  padding-top: var(--space-lg);
  padding-bottom: var(--space-lg);
}
.template-section__image-figure {
  margin: 0;
  max-width: 1200px;
  margin-left: auto;
  margin-right: auto;
}
.template-section--image--constrained .template-section__image-figure {
  max-width: 720px;
}
.template-section__image-link {
  display: block;
  text-decoration: none;
}
.template-section__image-img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--image-aspect-ratio, auto);
  /* When aspect-ratio is set to a fixed value, object-fit: cover
     ensures the image fills the box rather than letterboxing. With
     'auto' (the default), aspect-ratio collapses and object-fit
     has nothing to act on — image renders at natural size. */
  object-fit: cover;
  border-radius: 4px;
}
.template-section__image-caption {
  margin-top: var(--space-sm);
  font-family: var(--font-body);
  font-size: 0.875rem;
  color: rgba(0, 0, 0, 0.6);
  text-align: center;
  line-height: 1.4;
}

/* Page background music toggle — floating bottom-right pill, ~44x44.
   Visible whenever the templated page emits <audio data-bgm>; controlled
   by the bgm bootstrap in townteam/public/js/public.js. Sits above
   section chrome but below the cookie banner (which uses z-index 1000+
   in this stylesheet). */
.bgm-toggle {
  position: fixed;
  right: var(--space-md, 1rem);
  bottom: var(--space-md, 1rem);
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 999px;
  background: var(--hll-green);
  color: var(--color-text-inverse, #fff);
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  transition: background-color var(--hll-transition-hover, 160ms ease),
              transform var(--hll-transition-hover, 160ms ease);
  /* Above the cookie banner (z-index 9000) so the user can always
     mute, even when the banner is showing. Other fixed elements in
     common.css top out at 9999 (modals/overlays) — we sit just below
     those so we don't fight modal chrome. */
  z-index: 9500;
  -webkit-tap-highlight-color: transparent;
}
.bgm-toggle:hover { transform: translateY(-1px); }
.bgm-toggle:focus-visible {
  outline: 2px solid var(--hll-green);
  outline-offset: 2px;
}
.bgm-toggle.bgm-toggle--active {
  background: var(--hll-green);
}
.bgm-toggle__icon {
  display: block;
  pointer-events: none;
}

/* On-page music control buttons — admin-configurable MUSIC GOES ON /
   MUSIC GOES OFF pair rendered at the top of the public templated
   retailer page when bgmusic_show_buttons is true. Reuses the
   canonical pill chrome (.subscribe-form__btn family) for visual
   consistency with the rest of the templated page. Compound-selector
   --visible (0,2,0) keeps the future "hidden" variant cheap to layer. */
.bgm-controls {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-sm, 0.5rem);
  padding: var(--space-md, 1rem);
  background: transparent;
}
.bgm-controls.bgm-controls--visible {
  display: flex;
}
.bgm-controls__btn {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  cursor: pointer;
  padding: 0.6em 1.4em;
  border-radius: 999px;
  font-family: var(--font-heading);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: var(--hll-green);
  color: var(--color-text-inverse, #fff);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
  transition: transform var(--hll-transition-hover, 160ms ease),
              opacity var(--hll-transition-hover, 160ms ease);
  -webkit-tap-highlight-color: transparent;
}
.bgm-controls__btn:hover { transform: translateY(-1px); }
.bgm-controls__btn:focus-visible {
  outline: 2px solid var(--hll-green);
  outline-offset: 2px;
}
.bgm-controls__off {
  background: rgba(0, 0, 0, 0.65);
}

/* Music Controls section — positionable variant. Wraps the canonical
   .bgm-controls pill row in a full-bleed template-section so it picks
   up section ordering, drag-drop reorder, and per-section background
   colour like every other section type. Per-section overrides for
   font / alignment / pill padding come in through CSS custom
   properties set inline on the <section>. Compound-selector
   (0,2,0) per Stu's reuse rules — keeps room for state variants. */
.template-section.template-section--music-controls {
  display: block;
  padding: var(--space-md, 1rem) var(--space-lg, 1.5rem);
}
.template-section.template-section--music-controls .bgm-controls {
  justify-content: var(--mc-justify, center);
  padding: 0;
}
.template-section.template-section--music-controls .bgm-controls__btn {
  font-family: var(--mc-font-family, var(--font-heading));
  font-size: var(--mc-font-size, 22px);
  font-weight: var(--mc-font-weight, 700);
  padding: calc(var(--mc-pill-padding, 24px) / 2) var(--mc-pill-padding, 24px);
  text-align: var(--mc-text-align, center);
}
.template-section.template-section--music-controls .bgm-controls__on {
  color: var(--mc-text-colour, var(--color-text-inverse, #fff));
}
