/* ==========================================================================
   The Dynamic Closer - VSL Landing Page
   Design system per PRD.md section 6. Deliberately austere.

   HARD RULES - do not violate these without re-reading PRD section 6:
   - Tan is NEVER text on white (2.9:1, fails). Use --tan-deep for that.
   - White is NEVER text on tan (2.9:1, fails).
   - Exactly TWO dark bands on the page: the hero and the final CTA.
   - No gradients, no glows, no animated buttons, no scroll animation,
     no counters that tick up. Restraint is the trust signal here.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Colour - sampled from the Dynamic Closer Method logo */
  --brand-green: #051f1e;   /* Body text, headlines, CTA fill, dark bands */
  --brand-tan: #cd8a53;     /* Accents ONLY. Never text on white.         */
  --bg: #ffffff;
  --text: #051f1e;
  --text-muted: #5c6f6e;    /* Green-tinted grey, not neutral grey */
  --text-on-dark: #ffffff;
  --tan-deep: #8c5528;      /* The only permitted tan for text on white */
  --border: #dde3e2;
  --border-tan: rgba(205, 138, 83, 0.3);
  --surface: #f5f7f6;       /* Light alternation. Max twice on the page. */

  /* Tan accents that sit on the dark bands */
  --tan-on-dark: #cd8a53;

  /* Type */
  --font: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;

  --h1: 32px;
  --h2: 24px;
  --body: 17px;
  --small: 13px;

  --lh-heading: 1.25;
  --lh-body: 1.6;

  /* Space - 8px base */
  --gutter: 24px;
  --section-y: 40px;
  --measure: 640px;

  --radius: 8px;
}

@media (min-width: 768px) {
  :root {
    /* Fluid from 768px up so the headline grows with the layout instead of
       sitting small beside a 1080px video. 40px at 768, 68px at 1920, capped
       there so it does not run away on a large monitor. */
    --h1: clamp(40px, calc(1.33rem + 2.43vw), 68px);
    --h2: clamp(30px, calc(1.2rem + 1.15vw), 44px);
    --body: 18px;
    --small: 14px;
    --section-y: 64px;
  }
}

/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;

  /* Room for the sticky mobile CTA bar so it never covers the footer. */
  padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px));
}

@media (min-width: 768px) {
  body {
    padding-bottom: 0; /* No sticky bar on desktop. */
  }
}

h1,
h2,
h3 {
  margin: 0;
  font-weight: 700;
  line-height: var(--lh-heading);
  letter-spacing: -0.02em;
}

h1 {
  font-size: var(--h1);
}

h2 {
  font-size: var(--h2);
}

h3 {
  font-size: 19px;
}

p {
  margin: 0;
}

strong {
  font-weight: 700;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

img,
svg {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
}

/* Visible focus everywhere. Accessibility, PRD section 7.
   --tan-deep, not --brand-tan: a focus ring is a non-text UI component and
   needs 3:1. Tan on white is 2.9:1 and fails. Deep tan clears it. */
:focus-visible {
  outline: 2px solid var(--tan-deep);
  outline-offset: 3px;
  border-radius: 2px;
}

/* --------------------------------------------------------------------------
   3. Layout
   -------------------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: var(--section-y);
}

/* Full-bleed dark band. Only two of these exist: the hero and the final CTA. */
.band-dark {
  background: var(--brand-green);
  color: var(--text-on-dark);
}

/* --------------------------------------------------------------------------
   4. Typography helpers
   -------------------------------------------------------------------------- */

.lede {
  font-size: var(--body);
  line-height: var(--lh-body);
}

.small {
  font-size: var(--small);
  line-height: 1.5;
}

.section-head {
  margin-bottom: 24px;
}

.section-head h2:not(:last-child) {
  margin-bottom: 8px;
}

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 56px;
  padding: 14px 24px;
  border: 1px solid var(--brand-green);
  border-radius: var(--radius);
  background: var(--brand-green);
  color: var(--text-on-dark);
  font-family: inherit;
  font-size: 18px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  /* No gradient, no shadow, no transform. A 10% lighten on hover, nothing else. */
  transition: background-color 120ms ease, border-color 120ms ease;
}

.btn:hover {
  background: #143534; /* --brand-green, 10% lighter */
  border-color: #143534;
}

/* On the dark bands the button inverts so it stays visible against the green. */
.band-dark .btn {
  background: var(--bg);
  border-color: var(--bg);
  color: var(--brand-green);
}

.band-dark .btn:hover {
  background: #e8ecea;
  border-color: #e8ecea;
}

.band-dark :focus-visible {
  outline-color: var(--brand-tan);
}

.btn-block {
  max-width: 400px;
  margin-inline: auto;
}

/* --------------------------------------------------------------------------
   S1 - Hero
   -------------------------------------------------------------------------- */

.hero {
  padding-top: 40px;
  padding-bottom: 24px;
  text-align: center;
}

/* Vertical lockup: crest stacked over a two-line wordmark. It needs far more
   height than a horizontal lockup would - at 40px the words are unreadable.
   Sized by height with width:auto so the 645x420 intrinsic ratio is preserved
   and the space is reserved before the image loads. */
.hero__logo {
  height: 42px;
  width: auto;
  margin: 0 auto 14px;
}

/* Same width formula as .video__frame, so the headline and the player share
   their left and right edges on every screen. The headline is display type,
   not prose, so the 640px reading measure does not apply to it - the subhead
   below keeps its own 34em measure and stays readable. */
.hero .wrap {
  max-width: none;
  padding-inline: 0;
}

.hero__title {
  width: min(calc(100% - 2 * var(--gutter)), 1080px, calc(68vh * 16 / 9));
  margin-inline: auto;
  text-wrap: balance;
}

.hero__sub {
  max-width: 34em;
  margin: 20px auto 0;
  padding-inline: var(--gutter);
  color: rgba(255, 255, 255, 0.82);
}

.hero__sub strong {
  color: var(--text-on-dark);
}

@media (min-width: 768px) {
  .hero {
    padding-top: 56px;
    padding-bottom: 28px;
  }

  .hero__logo {
    height: 132px;
    margin-bottom: 32px;
  }
}

/* Short viewports (iPhone SE at 320x568): claw back vertical space so the
   headline still clears the fold. PRD section 5, S1 acceptance criteria. */
@media (max-width: 360px) {
  :root {
    --h1: 29px;
    --gutter: 20px;
  }

  .hero {
    padding-top: 36px;
  }

  .hero__logo {
    height: 36px;
    margin-bottom: 12px;
  }
}

/* --------------------------------------------------------------------------
   S2 - VSL player
   -------------------------------------------------------------------------- */

.video {
  padding-block: 24px 0;
}

/* The video is the page - everything else exists to get people into it, so it
   gets to be big. It breaks out of the 640px text column deliberately: that cap
   is a reading measure (PRD section 6, "never more than ~70 characters per
   line") and a video has no line length to protect. Prose stays at 640. */
.video .wrap {
  max-width: none;
  padding-inline: 0;
}

.video__label {
  max-width: var(--measure);
  margin: 0 auto 16px;
  padding-inline: var(--gutter);
  font-weight: 700;
  text-align: center;
}

/* Sized by whichever runs out first:
     100% - gutters   phones, where it stays exactly as before
     1080px           the hard ceiling on very wide monitors
     68vh -> width    keeps a 16:9 box from eating the whole screen height, so
                      the CTA underneath is always within reach. This is the one
                      that binds on a laptop and in mobile landscape.
   aspect-ratio still derives the height, so there is no layout shift. */
/* aspect-ratio, never a fixed height. Reserves its space before the embed
   loads, so there is no layout shift. PRD section 7: CLS < 0.05. */
.video__frame {
  position: relative;
  width: min(
    calc(100% - 2 * var(--gutter)),
    1080px,
    calc(68vh * 16 / 9)
  );
  margin-inline: auto;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--border);
}

.video__frame wistia-player {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* Before the custom element upgrades, show Wistia's blurred swatch so the box
   is never an empty grey rectangle. Wistia's own snippet uses a
   padding-top:56.25% hack here; it is not needed and is deliberately omitted,
   because .video__frame already reserves 16:9. Adding it would double the
   height and reintroduce the layout shift the container exists to prevent. */
.video__frame wistia-player:not(:defined) {
  background: center / contain no-repeat
    url("https://fast.wistia.com/embed/medias/cl91tqlahu/swatch");
  filter: blur(5px);
}

.play-btn {
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  padding: 0;
  border: 2px solid var(--brand-tan);
  border-radius: 50%;
  background: var(--brand-green);
  cursor: pointer;
  transition: background-color 120ms ease;
}

.play-btn:hover {
  background: #143534;
}

.play-btn svg {
  width: 22px;
  height: 26px;
  margin-left: 4px; /* Optical centring of the triangle. */
  fill: var(--text-on-dark);
}

/* --------------------------------------------------------------------------
   S3 - Primary CTA
   -------------------------------------------------------------------------- */

.cta-primary {
  /* Bottom padding matters now that the dark booking band follows directly:
     without it the subhead would sit flush against the colour change. */
  padding-block: 32px 28px;
}

/* Same width formula as the headline and the player, so this block is not
   stranded in a 640px column while everything around it runs to 1080. */
.cta-primary .wrap {
  max-width: none;
  padding-inline: 0;
}

/* The wrap around this section is full-bleed so the heading can scale, which
   means the button needs its own gutters back - otherwise it runs to the screen
   edge on a phone. PRD S3: full width on mobile, with 24px side margins. */
.cta-primary .btn-block {
  width: min(calc(100% - 2 * var(--gutter)), 400px);
}

.cta-primary__head {
  width: min(calc(100% - 2 * var(--gutter)), 1080px, calc(68vh * 16 / 9));
  margin: 28px auto 0;
  text-align: center;
}

.cta-primary__head h2 {
  text-wrap: balance;
}

/* Scales with the layout rather than sitting at a fixed 18px: 18px at 768,
   24px at 1920, capped there. */
/* No text-wrap: balance here. Balancing evens out line lengths, which on a
   three-line block leaves ~90px unused on a 342px phone and pushes it to a
   third line. Letting it fill the measure gives fewer, fuller lines. It stays
   on the h2 above, where balance is what you want across two lines. */
/* 13px is what it takes to get this onto two lines on a phone: measured, 14px
   still wraps to three at 375. At 320 no readable size fits two lines - the box
   is only 280px - so that one viewport keeps three. */
.cta-primary__head p {
  max-width: 34em;
  margin: 12px auto 0;
  font-size: 13px;
  color: var(--text-muted);
}

@media (min-width: 768px) {
  .cta-primary__head p {
    font-size: clamp(18px, calc(0.875rem + 0.52vw), 24px);
  }
}

/* --------------------------------------------------------------------------
   S5 - Testimonials
   -------------------------------------------------------------------------- */

.testimonials {
  padding-block: 32px 40px;
}

/* Matches the video's width so the carousel lines up with the player above. */
.testimonials .wrap {
  max-width: none;
  padding-inline: 0;
}

.testimonials .section-head {
  width: min(calc(100% - 2 * var(--gutter)), 1080px);
  margin-inline: auto;
  text-align: center;
}

/* Full-bleed horizontal scroll on mobile so tiles can run to the screen edge,
   which reads as a carousel rather than a cut-off grid. */
/* Two abreast, flowing with the page.

   This was a horizontal carousel and is deliberately not any more: it now
   stacks and scrolls with the rest of the page, so there is no separate scroll
   surface to discover and nothing to swipe sideways.

   Two columns on a phone, four on a wide screen - at two columns a 1080px strip
   would give 530px tiles, about 940px tall each, which is far too big for a
   supporting section. Same 2-column phone layout as the screenshot wall below,
   so the two proof sections read as a pair. */
.testimonials__grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20px 12px;
  width: min(calc(100% - 2 * var(--gutter)), 1080px);
  margin-inline: auto;
}

@media (min-width: 768px) {
  .testimonials__grid {
    grid-template-columns: repeat(4, 1fr);
    gap: 28px 20px;
  }
}

/* Only the first four show initially - two rows on a phone, one on a wide
   screen - and "See more" reveals the rest. The hidden tiles are display:none,
   so their posters are never fetched until they are revealed. */
.testimonial.is-hidden {
  display: none;
}

.testimonials__more {
  display: block;
  width: min(calc(100% - 2 * var(--gutter)), 260px);
  min-height: 48px;
  margin: 24px auto 0;
  padding: 12px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font-family: inherit;
  font-size: var(--small);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 120ms ease;
}

.testimonials__more:hover {
  background: var(--surface);
}

.testimonial {
  scroll-snap-align: start;
}

/* 9:16 tile. Same aspect-ratio approach as the main player. */
.testimonial__frame {
  position: relative;
  aspect-ratio: 9 / 16;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 0;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--border);
  overflow: hidden;
  cursor: pointer;
}

/* Poster frame cut from the video itself. */
.testimonial__frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A poster of a face is busy; the play button needs a scrim to stay legible. */
.testimonial__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(5, 31, 30, 0.28);
}

.testimonial__frame .play-btn {
  position: relative;
  z-index: 1;
}

.testimonial__frame wistia-player {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  z-index: 2;
}

.testimonial__frame .play-btn {
  width: 56px;
  height: 56px;
}

.testimonial__frame .play-btn svg {
  width: 17px;
  height: 20px;
}

/* Caption sits BELOW the tile, never overlaid. PRD section 5, S5. */
.testimonial__caption {
  padding-top: 14px;
}

.testimonial__name {
  font-weight: 700;
  line-height: 1.3;
}

/* Deliberately not a link. PRD D2: every clickable thing that is not the CTA is
   a leak, and an Instagram link here would send a warm visitor off the page. */
.testimonial__handle {
  margin-top: 2px;
  font-size: var(--small);
  line-height: 1.35;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   Standalone CTA blocks (after the testimonials, and after the screenshots)
   -------------------------------------------------------------------------- */

.cta-block {
  padding-block: 0 32px;
}

/* --------------------------------------------------------------------------
   Client result screenshots

   CSS columns rather than a grid: screenshots arrive at wildly different
   heights and aspect ratios, and columns let them tile without cropping or
   leaving a ragged last row. Adding more images needs no layout change.
   -------------------------------------------------------------------------- */

.results {
  padding-block: 0 32px;
}

.results .wrap {
  max-width: none;
  padding-inline: 0;
}

.results__grid {
  width: min(calc(100% - 2 * var(--gutter)), 1400px);
  margin-inline: auto;
}

/* A dense wall: many small screenshots rather than a few large ones.

   Two things are true at once here. The screenshots arrive at 13 different
   natural widths (679-1206px), so forcing them all to the column width scales
   each by a different amount and the text inside them ends up at inconsistent
   sizes - that is what made the earlier version look wrong. But the point of
   this section is volume, not legibility of any single image.

   So: masonry columns for density, and within a column each image is sized in
   proportion to its own natural width against the widest screenshot. Every
   image is therefore at an identical zoom, and a narrower screenshot simply
   renders narrower, which is what it is. */
.results__grid {
  --results-ref: 1206;
  columns: 2;
  column-gap: 10px;
}

.results__grid img {
  display: block;
  width: calc(100% * var(--w) / var(--results-ref));
  /* --results-ref is 1206 because that is the iPhone screenshot width and 138
     of the 163 are exactly that. One is a 2268px desktop capture, which the
     formula would size at 188% of the column - max-width clamps that single
     outlier to the column instead of letting it overflow. */
  max-width: 100%;
  height: auto;
  margin: 0 auto 10px;
  border-radius: 4px;
  border: 1px solid var(--border);
  /* Never let a screenshot be split across two columns. */
  break-inside: avoid;
}

@media (min-width: 768px) {
  .results__grid {
    columns: 4;
    column-gap: 14px;
  }

  .results__grid img {
    margin-bottom: 14px;
  }
}

@media (min-width: 1200px) {
  .results__grid {
    columns: 5;
    column-gap: 16px;
  }

  .results__grid img {
    margin-bottom: 16px;
  }
}

/* --------------------------------------------------------------------------
   S10 - Final CTA
   -------------------------------------------------------------------------- */

/* Focused programmatically when a CTA scrolls here, so screen readers follow.
   That focus should not paint a ring - the keyboard ring on real controls is
   untouched. */
.cta-final:focus {
  outline: none;
}

.cta-final {
  /* Tighter than --section-y: this band is dominated by the calendar, so the
     usual section padding made the green run much longer than it needed to. */
  padding-block: 28px 24px;
  text-align: center;
}

@media (min-width: 768px) {
  .cta-final {
    padding-block: 44px 40px;
  }
}

/* Like the video, the calendar is not prose and should not be held to the
   640px reading measure - it needs room for a two-column month view. */
.cta-final .wrap {
  max-width: none;
  padding-inline: 0;
}

.cta-final__head {
  max-width: var(--measure);
  margin: 0 auto 20px;
  padding-inline: var(--gutter);
}

/* Calendly broadcasts its own height by postMessage (calendly.page_height), so
   main.js resizes the slot to fit whatever step is showing. The values below are
   only the reservation used before the first message arrives, so nothing jumps
   on load.

   This matters because the steps differ enormously: on a phone the calendar
   reports ~770px and the details form ~1950px. Any single fixed height either
   scrolls inside the iframe on the form or leaves a huge empty band on the
   calendar. Auto-sizing avoids both, and it also makes the one- versus
   two-column layout switch a non-issue, since the height follows whatever
   Calendly decides to render.

   --cal-scale shrinks the widget slightly. Everything below is expressed in
   logical (pre-scale) pixels; painted height is logical x scale. */
.booking-inline {
  --cal-h: 900px;
  --cal-scale: 0.9;
  width: min(calc(100% - 2 * var(--gutter)), 1080px);
  margin-inline: auto;
  height: calc(var(--cal-h) * var(--cal-scale));
  overflow: hidden;
}

.booking-inline iframe {
  display: block;
  width: calc(100% / var(--cal-scale));
  height: var(--cal-h);
  border: 0;
  transform: scale(var(--cal-scale));
  transform-origin: top left;
}

@media (min-width: 1000px) {
  .booking-inline {
    --cal-h: 660px;
  }
}

/* --------------------------------------------------------------------------
   S11 - Footer
   -------------------------------------------------------------------------- */

.footer {
  padding-block: 32px 40px;
  text-align: center;
  font-size: var(--small);
  line-height: 1.6;
  color: var(--text-muted);
}

/* Green-on-white variant of the crest, derived from the full lockup: the tan
   fill becomes brand green and the linework becomes white, so it reads on a
   light background. The tan-on-green lockup cannot be used here - it would
   float as a dark rectangle (PRD section 6). */
.footer__mark {
  height: 44px;
  width: auto;
  margin: 0 auto 24px;
}

/* Long-form legal text. Left-aligned rather than following the footer's
   centring - centred text this long is genuinely hard to read, and this block
   has to be readable to do its job. */
.footer__disclaimer {
  max-width: 46em;
  margin: 24px auto 0;
  padding-top: 24px;
  border-top: 1px solid var(--border);
  text-align: left;
}

.footer__disclaimer p + p {
  margin-top: 14px;
}

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

.footer__copyright {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.footer__meta {
  margin-top: 16px;
}

.footer__legal {
  margin-top: 14px;
}

.footer__legal a {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--border);
}

.footer__legal a:hover {
  color: var(--text);
  text-decoration-color: currentColor;
}

/* --------------------------------------------------------------------------
   S12 - Sticky mobile CTA bar
   -------------------------------------------------------------------------- */

.sticky-cta {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  padding: 8px var(--gutter);
  padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
  background: var(--bg);
  border-top: 1px solid var(--border);

  /* Hidden until the user scrolls past the player, and hidden again whenever
     S3 or S10 is on screen so there is never a doubled CTA. Managed in main.js. */
  transform: translateY(110%);
  visibility: hidden;
  transition: transform 180ms ease, visibility 180ms;
}

.sticky-cta.is-visible {
  transform: translateY(0);
  visibility: visible;
}

.sticky-cta .btn {
  min-height: 48px;
  font-size: 17px;
}

/* Desktop: no sticky bar. It is clutter on a wide screen. */
@media (min-width: 768px) {
  .sticky-cta {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   Cookie consent banner

   Small, bottom of screen, two buttons - PRD section 9. Sits above the sticky
   CTA bar and pushes it out of the way while it is up, so the two never stack
   on top of each other on a phone.
   -------------------------------------------------------------------------- */

.consent {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  /* Maximum stacking. Not a decorative choice: hosts and third parties inject
     floating widgets at very high z-indexes - Netlify's "Powered by" badge sits
     at 2147483645 and landed exactly on top of the Accept button, making the
     banner impossible to action. Consent UI must always be reachable, so this
     sits above anything else on the page. */
  z-index: 2147483647;
  background: var(--brand-green);
  color: var(--text-on-dark);
  border-top: 1px solid rgba(205, 138, 83, 0.35);
  padding: 14px var(--gutter);
  padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
}

.consent__inner {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: var(--measure);
  margin-inline: auto;
}

.consent__text {
  font-size: var(--small);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.86);
}

.consent__text a {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.consent__actions {
  display: flex;
  gap: 10px;
}

.consent__btn {
  flex: 1;
  min-height: 44px;
  padding: 10px 18px;
  border-radius: var(--radius);
  font-family: inherit;
  font-size: var(--small);
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 120ms ease;
}

.consent__btn--yes {
  border: 1px solid var(--bg);
  background: var(--bg);
  color: var(--brand-green);
}

.consent__btn--yes:hover {
  background: #e8ecea;
}

/* Declining must be as easy as accepting - a hidden or greyed-out refusal is
   not valid consent under UK GDPR. */
.consent__btn--no {
  border: 1px solid rgba(255, 255, 255, 0.45);
  background: transparent;
  color: var(--text-on-dark);
}

.consent__btn--no:hover {
  background: rgba(255, 255, 255, 0.1);
}

/* While the banner is up the sticky CTA would sit underneath it. */
.consent-open .sticky-cta {
  display: none;
}

@media (min-width: 768px) {
  .consent__inner {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    max-width: 1080px;
  }

  .consent__actions {
    flex: 0 0 auto;
  }

  .consent__btn {
    flex: 0 0 auto;
    min-width: 132px;
  }
}

/* --------------------------------------------------------------------------
   Build-time placeholder marker.
   Every node carrying this class is scaffolding waiting on a real asset.
   Search the project for "is-placeholder" to find everything still outstanding.
   -------------------------------------------------------------------------- */

.is-placeholder {
  font-size: 12px;
  line-height: 1.4;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Marker only, no styles of its own. Sits on any element that must be deleted
   or replaced before launch. */
.is-placeholder-block {
}

/* --------------------------------------------------------------------------
   Legal pages
   -------------------------------------------------------------------------- */

.legal {
  padding-block: 56px 80px;
}

.legal h1 {
  margin-bottom: 8px;
}

.legal__updated {
  margin-bottom: 40px;
  font-size: var(--small);
  color: var(--text-muted);
}

.legal h2 {
  margin-top: 40px;
  margin-bottom: 12px;
  font-size: 20px;
}

.legal p + p {
  margin-top: 16px;
}

.legal__back {
  display: inline-block;
  margin-top: 48px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
  font-size: var(--small);
  color: var(--text-muted);
}

/* Blocks in the legal pages still waiting on real copy. */
.legal__todo {
  margin-top: 16px;
  padding: 20px;
  border: 1px solid var(--border-tan);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: var(--small);
  line-height: 1.6;
  color: var(--text-muted);
}

/* --------------------------------------------------------------------------
   Utilities
   -------------------------------------------------------------------------- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
