@import './site-fonts.css';
@import './site-tokens.css';
@import './site-motion.css';

/* ---- The rendered site inside the browser frame ---- */
.pmp-site {
  background: var(--st-surface);
  /* The font itself is set in site-tokens.css, which owns the base layer —
     `inherit` here resolved to the browser's serif once the site stopped
     living inside the app's stylesheet. */
  color: var(--st-ink);
  font-size: var(--st-text-base);
  line-height: var(--st-leading-body);
  /* Full-bleed section/nav/footer backgrounds are the point, but their
     *content* should not stretch edge to edge on a wide monitor the way a
     fixed small gutter alone would leave it — real sites clamp a reading
     width and centre it. `max()` keeps the fixed gutter as a floor on
     narrow screens and grows it to re-centre the content once the
     viewport passes roughly this width. */
  --site-content-max: 1200px;
}

.pmp-site__nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--st-space-4);
  padding-top: var(--st-space-4);
  padding-bottom: var(--st-space-4);
  padding-left: max(var(--st-gutter), calc(50% - var(--site-content-max) / 2));
  padding-right: max(var(--st-gutter), calc(50% - var(--site-content-max) / 2));
  border-bottom: 1px solid var(--st-line);
}

.pmp-site__brand {
  display: flex;
  align-items: center;
  gap: var(--st-space-2);
}

.pmp-site__logo {
  width: calc(var(--site-logo-size, 34px) * var(--site-logo-scale, 1));
  height: calc(var(--site-logo-size, 34px) * var(--site-logo-scale, 1));
  object-fit: contain;
  flex-shrink: 0;
}

.pmp-site__logo--square { border-radius: 0; }
.pmp-site__logo--rounded { border-radius: var(--st-radius-sm); }
.pmp-site__logo--circle { border-radius: 50%; }

.pmp-site__menu {
  display: flex;
  align-items: center;
  gap: var(--st-space-4);
  font-size: var(--st-text-sm);
  font-weight: 600;
}

.pmp-site__menu a {
  color: inherit;
  text-decoration: none;
}

.pmp-site__menu a:hover {
  text-decoration: underline;
}

/*
  `--left` needs no override — it is the flex default above (brand, then
  menu, then the CTA, left to right). `--center` switches to a three-column
  grid so the brand can sit dead-centre regardless of whether a menu or CTA
  is present either side of it.
*/
.pmp-site__nav--center {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
}

.pmp-site__nav--center .pmp-site__brand {
  grid-column: 2;
  justify-self: center;
}

.pmp-site__nav--center .pmp-site__menu {
  grid-column: 1;
  justify-self: start;
}

.pmp-site__nav--center .pmp-site__cta {
  grid-column: 3;
  justify-self: end;
}

.pmp-site__cta {
  /* The site's own accent, not the app chrome's — a partner's "WhatsApp us"
     button is their brand, not Herholdt's Premium Partner tooling. */
  background: var(--site-accent, var(--st-primary));
  color: var(--site-accent-ink, #fff);
  font-weight: 700;
  font-size: var(--st-text-sm);
  padding: 8px 14px;
  border-radius: var(--st-radius-sm);
  transition: filter 0.15s ease;
}

.pmp-site__cta:hover { filter: brightness(1.08); }

.pmp-site__hero {
  /* Scaled like every other section, so the Spacing control reaches the
     hero too — it used to be the one section that ignored it. */
  padding-top: calc(var(--st-section-y) * 1.35 * var(--st-section-scale));
  padding-bottom: calc(var(--st-section-y) * 1.35 * var(--st-section-scale));
  padding-left: max(var(--st-gutter), calc(50% - var(--site-content-max) / 2));
  padding-right: max(var(--st-gutter), calc(50% - var(--site-content-max) / 2));
  background: var(--site-hero-bg);
  color: var(--site-hero-ink);
  /* A hero that only wraps its copy reads as a banner; giving it a stage is
     most of what separates a landing page from a page with a title on it. */
  min-height: min(78vh, 720px);
  display: flex;
  align-items: center;
}

/* A partner's own photo behind the hero, in place of the theme's gradient.
   The scrim is tuned for the hero's own ink colour, not a flat black, so a
   light-on-photo hero (clean) and a dark-on-photo one (bold, warm) both stay
   readable without the photo washing out. */
.pmp-site__hero--photo {
  position: relative;
  background-size: cover;
  background-position: center;
  color: #fff;
}

.pmp-site__hero--photo::before {
  content: '';
  position: absolute;
  inset: 0;
  /* Accent-tinted rather than flat black so a photo hero still belongs to the
     partner's brand. `--site-overlay` scales the whole thing, defaulting to
     the 1.0 this had before the strength was adjustable. */
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--site-accent) 35%, black) 0%,
    color-mix(in srgb, var(--site-accent) 20%, transparent) 100%
  );
  opacity: calc(var(--site-overlay, 0.45) / 0.45);
}

/* A photo behind the hero makes it dark whatever the template's default hero
   was, so the ink token has to flip with it. Redefined on the element that
   carries the partner's choice, rather than patched onto each thing that
   reads it: the headline and lead already forced themselves white, but the
   ghost button derives from `--site-hero-ink` and so came out near-black on
   a dark photograph. Anything else deriving from the token is fixed by this
   too, including whatever gets added later. */
.pmp-site__hero--photo {
  --site-hero-ink: #ffffff;
}

.pmp-site__hero--photo .pmp-site__hero-content {
  position: relative;
  z-index: 1;
}

.pmp-site__hero--photo .pmp-site__kicker,
.pmp-site__hero--photo .pmp-site__lead {
  color: var(--site-text-override, rgba(255, 255, 255, 0.85));
}

/* Overridden per template below — bold has no visual pane at all, clean puts
   it beside the copy, warm floats it behind the copy as a soft shape. */
.pmp-site__hero-visual { display: none; }

.pmp-site__kicker {
  font-size: var(--st-text-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--site-text-override, color-mix(in srgb, var(--site-hero-ink) 75%, transparent));
  font-weight: 700;
}

.pmp-site__hero :is(h1, h2) {
  font-size: var(--st-text-hero);
  line-height: var(--st-leading-tight);
  /* Display type needs negative tracking to stop large glyphs reading as
     loose; body type does not. */
  letter-spacing: -0.03em;
  margin-top: var(--st-space-3);
  max-width: 20ch;
}

/* Reused outside the hero too — About's body, Pricing's, Contact's — where
   the section sits on a plain/tinted background, not the hero's own theme
   gradient. A readable muted grey is the right default there; the hero
   overrides it below for its own ink colour, which is only ever right
   against the hero's own background. */
/*
  Paragraph breaks survive to the page.

  Every multiline field — the About body, the hero and CTA sublines, a
  testimonial quote, an FAQ answer, a feature card's body — is stored with its
  newlines and rendered straight into HTML, where a newline is whitespace. Two
  paragraphs of an About story ran together as one block. `pre-line` honours
  the breaks the partner typed while still collapsing runs of spaces, so it
  cannot be used to fake layout.
*/
.pmp-site__lead,
.pmp-site__faqs p,
.pmp-site__card p,
blockquote/* A testimonial's rating and the person who gave it. */
.pmp-site__stars {
  margin: 0 0 var(--st-space-2);
  letter-spacing: 0.12em;
  color: var(--site-secondary, var(--site-accent, var(--st-primary)));
  font-size: var(--st-text-sm);
}

/* The unearned stars, drawn so four reads as four out of five rather than as
   four of some unknown total. */
.pmp-site__stars-rest {
  opacity: 0.24;
}

.pmp-site__quote-by {
  display: flex;
  align-items: center;
  gap: var(--st-space-3);
}

.pmp-site__avatar {
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
}

.pmp-site__card {
  white-space: pre-line;
}

.pmp-site__lead {
  margin-top: var(--st-space-3);
  /* Paragraph breaks the partner typed are kept, the same way a testimonial
     card already keeps them. Without this an About body written as three
     paragraphs in the editor published as one block: the editor edits it in a
     textarea, which shows the breaks, so the page disagreed with the box it
     was typed in. `pre-line` collapses runs of spaces and keeps newlines,
     which is what a wrapped paragraph needs. */
  white-space: pre-line;
  /* Yields to a section that has chosen its own ink — see
     `--site-text-override` in sectionStyle.ts. */
  color: var(--site-text-override, var(--st-ink-muted-2));
  font-size: var(--st-text-lg);
  max-width: var(--st-measure);
}

.pmp-site__hero .pmp-site__lead {
  color: var(--site-text-override, color-mix(in srgb, var(--site-hero-ink) 85%, transparent));
}

.pmp-site__buttons { display: flex; gap: var(--st-space-3); margin-top: var(--st-space-5); }

.pmp-site__button {
  background: var(--site-accent, var(--st-primary));
  color: var(--site-accent-ink, #fff);
  font-weight: 700;
  font-size: var(--st-text-base);
  padding: var(--st-space-3) var(--st-space-5);
  border-radius: var(--st-radius-md);
  transition: filter 0.15s ease, transform 0.15s ease;
  cursor: pointer;
}

.pmp-site__button:hover {
  filter: brightness(1.08);
  transform: translateY(-1px);
  box-shadow: 0 6px 18px color-mix(in srgb, var(--site-accent, var(--st-primary)) 34%, transparent);
}

/* Keyboard focus was falling back to the browser's own outline, which reads as
   chrome rather than as part of the site. `:focus-visible`, so a mouse click
   does not draw it. */
.pmp-site__button:focus-visible,
.pmp-site__cta:focus-visible {
  outline: none;
  box-shadow: var(--st-focus-ring);
}

/*
  The secondary button.

  It used to derive from `--site-hero-ink` unconditionally, which is right in
  the hero — a light ink on the hero's dark backdrop — and invisible anywhere
  else: near-white text on a white card. The Contact section already had this,
  and pricing tiers made it three buttons instead of one. The default now
  belongs to the page, and the hero keeps its own.
*/
.pmp-site__button--ghost {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--site-accent, var(--st-primary)) 45%, transparent);
  color: var(--st-ink);
}

.pmp-site__button--ghost:hover {
  background: color-mix(in srgb, var(--site-accent, var(--st-primary)) 8%, transparent);
}

.pmp-site__hero .pmp-site__button--ghost {
  background: color-mix(in srgb, var(--site-hero-ink) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--site-hero-ink) 30%, transparent);
  color: var(--site-hero-ink);
}

.pmp-site__hero .pmp-site__button--ghost:hover {
  background: color-mix(in srgb, var(--site-hero-ink) 24%, transparent);
}

/*
  A solid button on an accent ground was the accent painted on the accent —
  the call to action disappeared into the panel built to carry it.

  The secondary is exactly the colour for this, and where there is none the
  button inverts onto the section's own ink instead, which is the classic
  treatment and needs no second hue.
*/
.pmp-site__section--accent .pmp-site__button:not(.pmp-site__button--ghost) {
  background: var(--site-secondary, var(--site-accent-ink, #fff));
  color: var(--site-secondary-ink, var(--site-accent, var(--st-primary)));
}

.pmp-site__section--accent .pmp-site__button:not(.pmp-site__button--ghost):hover {
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.22);
}

/* An accent-backed or photo-backed section is dark the way the hero is, so
   the light-ink treatment is right there too. */
.pmp-site__section--accent .pmp-site__button--ghost,
.pmp-site__section--photo .pmp-site__button--ghost {
  border-color: color-mix(in srgb, currentColor 40%, transparent);
  color: inherit;
}

.pmp-site__trust {
  display: flex;
  gap: var(--st-space-4);
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  padding: var(--st-space-4) var(--st-gutter);
  background: var(--st-surface-inset);
  border-bottom: 1px solid var(--st-line);
  font-size: var(--st-text-xs);
  color: var(--st-ink-muted);
}

.pmp-site__section {
  padding-top: calc(var(--st-section-y) * var(--st-section-scale));
  padding-bottom: calc(var(--st-section-y) * var(--st-section-scale));
  padding-left: max(var(--st-gutter), calc(50% - var(--site-content-max) / 2));
  padding-right: max(var(--st-gutter), calc(50% - var(--site-content-max) / 2));
}

/* Two plain sections in a row read as one over-tall section; the second
   loses its top padding so the rhythm stays one section's worth. */
.pmp-site__section:not([class*='--']) + .pmp-site__section:not([class*='--']) {
  padding-top: 0;
}
/* A tinted ground is the secondary colour's main job — but it reads
   `--st-surface-inset`, which is itself derived from the secondary in
   site-tokens.css. One override at the token level, rather than one per
   component that happens to use an inset surface. */
.pmp-site__section--tint {
  background: var(--st-surface-inset);
}

.pmp-site__section-wrap {
  position: relative;
}

.pmp-site__section-wrap--selected {
  outline: 2px solid var(--site-accent, var(--st-primary));
  outline-offset: -2px;
}

/* A single widget selected within an already-selected section — a tighter,
   dashed outline right on the widget itself (headline, button, whatever it
   is), so it reads as "this one specific thing" rather than repeating the
   section's own solid outline at a smaller scale. */
.pmp-site__widget--selected {
  outline: 2px dashed var(--site-accent, var(--st-primary));
  outline-offset: 4px;
  border-radius: 2px;
}

.pmp-site__button--selected {
  outline: 2px dashed var(--site-accent, var(--st-primary));
  outline-offset: 2px;
}

/* The selected section's own editing controls, anchored to it on the
   canvas rather than living in a side panel. Deliberately opaque and
   scrolling on its own: it sits over the section it edits, and a tall
   control set (a gallery picker, a long FAQ list) must not stretch the
   section underneath it out of shape. */
.pmp-site__section-controls {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 5;
  max-height: 60vh;
  overflow-y: auto;
  padding: 16px;
  background: var(--st-surface, #fff);
  border: 2px solid var(--site-accent, var(--st-primary));
  border-radius: 0 0 8px 8px;
  box-shadow: 0 12px 28px rgb(0 0 0 / 18%);
  text-align: left;
  color: var(--st-ink, #1a1a1a);
}

.pmp-site__section--accent {
  background: var(--site-accent, var(--st-primary));
  color: var(--site-accent-ink, #fff);
}

/* Accent text needs its own link/heading colour — the section's default ink
   assumes a light background, which an accent-coloured one is not. */
.pmp-site__section--accent :is(h1, h2, h3) {
  color: inherit;
}

/* The lead paragraph is a muted grey tuned for a white page, which on an
   accent or photo ground measured 1.42:1 — the headline and button beside it
   were 7.21:1, so the one line explaining the offer was the only thing a
   visitor could not read. Derived from the section's own ink rather than
   `currentColor`, which on the element setting the colour is circular.

   A video caption is the same grey with the same problem, so it joins this
   rule rather than getting a second one that could drift from it. Measured
   in a browser at 4.5:1 on the accent ground once it did. */
.pmp-site__section--accent .pmp-site__lead,
.pmp-site__section--photo .pmp-site__lead,
.pmp-site__section--accent .pmp-site__video-caption,
.pmp-site__section--photo .pmp-site__video-caption,
.pmp-site__section--video .pmp-site__video-caption {
  color: color-mix(in srgb, var(--site-accent-ink, #fff) 88%, transparent);
}

.pmp-site__section--photo {
  background-size: cover;
  background-position: center;
  position: relative;
  color: #fff;
}

/* A translucent scrim under the text, not a filter over the whole photo —
   the photo itself should still read at full clarity.

   A video background needs this more than a photo does, not less: a partner
   chooses a still and can see what they are getting, but footage changes
   brightness frame by frame, so the only safe assumption is that at some point
   it will be light where the headline is. Shipping the video background
   without extending this rule to it left a CTA headline sitting directly on
   moving footage, unreadable for half the loop — found by looking at a real
   page, and the reason `overlayStrength` appeared to do nothing on a video
   section. */
.pmp-site__section--photo::before,
.pmp-site__section--video::before {
  content: '';
  position: absolute;
  inset: 0;
  /* The 0.45 fallback is the fixed value this used before it was adjustable,
     so a photo section built earlier is unchanged. */
  background: rgb(0 0 0 / var(--site-overlay, 0.45));
  /*
    Three layers, and the order is the whole point: loop (0), scrim (1), copy
    (2).

    The scrim and the loop both sat at `z-index: 0` at first, and the loop is
    later in the DOM than a `::before`, so it painted straight over the scrim —
    the overlay control appeared to do nothing at all on a video section and a
    headline sat unreadable on bright footage. Same z-index is not the same as
    no z-index.
  */
  z-index: 1;
}

/*
  The copy sits above the scrim. Both backgrounds, not just video.

  When the scrim was raised to `z-index: 1` so a video loop could not paint
  over it, the video's own copy was lifted to 2 and the photo's was left at
  `position: relative` with `z-index: auto` — which is level 0. So on every
  photo-background section the scrim covered the words: washed out to read, and
  worse in the builder, swallowing the clicks that select and edit them. A
  partner could not edit a photo CTA at all.

  One rule for both now, because there is one scrim for both.
*/
.pmp-site__section--photo > *,
.pmp-site__section--video > *:not(.pmp-site__section-media) {
  position: relative;
  z-index: 2;
}

.pmp-site__section :is(h1, h2) {
  text-align: center;
  font-size: var(--st-text-2xl);
  line-height: var(--st-leading-snug);
  letter-spacing: -0.015em;
  margin-bottom: var(--st-space-6);
}

.pmp-site__cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--st-space-5);
}

.pmp-site__disclaimer {
  margin-top: var(--st-space-3);
  text-align: center;
  font-size: var(--st-text-xs);
  color: var(--st-danger);
}

.pmp-site__footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--st-space-2);
  padding: var(--st-space-8) var(--st-gutter);
  border-top: 1px solid var(--st-line);
  text-align: center;
  font-size: var(--st-text-xs);
  color: var(--st-ink-muted-2);
}

.pmp-site__card {
  padding: var(--st-space-5);
  border: 1px solid var(--st-line);
  /* Named so a template or a secondary colour can lift it without restating
     the whole shadow. */
  box-shadow: var(--site-card-shadow, none);
  border-radius: var(--st-radius-lg);
  background: var(--st-surface);
  /* Its own ink, not the section's. A card keeps its light surface on an
     accent, gradient or custom-coloured section, so inheriting that section's
     light ink put white text on a white card. */
  color: var(--st-ink);
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}

.pmp-site__card:hover {
  box-shadow: var(--site-card-shadow-hover, var(--st-shadow-md));
  transform: translateY(-2px);
  border-color: var(--st-line-strong);
}

.pmp-site__faqs details {
  border: 1px solid var(--st-line);
  border-radius: var(--st-radius-md);
  background: var(--st-surface);
  padding: 0 var(--st-space-5);
  margin-bottom: var(--st-space-3);
  transition: box-shadow 0.2s ease, border-color 0.2s ease;
}

.pmp-site__faqs details:hover {
  border-color: var(--st-line-strong);
  box-shadow: var(--site-card-shadow, var(--st-shadow-sm));
}

/* An open question stays visibly open, rather than the answer appearing to
   belong to whatever is under it. */
.pmp-site__faqs details[open] {
  border-color: color-mix(in srgb, var(--site-accent, var(--st-primary)) 35%, var(--st-line));
  box-shadow: var(--site-card-shadow, var(--st-shadow-sm));
}

.pmp-site__contact {
  padding: var(--st-section-y) max(var(--st-gutter), calc(50% - var(--site-content-max) / 2));
}

/* Info on the left (why/how to reach the business), the form on the right
   as its own raised card — a partner looking for the phone number and a
   partner ready to type a message are both served without either fighting
   the other for space, the way a single stacked column made them do. */
.pmp-site__contact-grid {
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  gap: var(--st-space-6);
  align-items: start;
  text-align: left;
}

.pmp-site__contact-info .pmp-site__buttons { margin-top: 24px; }
.pmp-site__contact-info .pmp-site__contact-details { justify-content: flex-start; }
.pmp-site__contact-info .pmp-site__social { justify-content: flex-start; }

.pmp-site__contact-form {
  display: flex;
  flex-direction: column;
  gap: var(--st-space-3);
  padding: var(--st-space-5);
  background: var(--st-surface);
  border: 1px solid var(--st-line);
  border-radius: var(--st-radius-md);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
}

.pmp-site__contact-form-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.pmp-site__contact-form-field label {
  font-size: var(--st-text-xs);
  font-weight: 700;
  color: var(--st-ink-muted-2);
}

.pmp-site__contact-form-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--st-space-3);
}

/* Every answer type but radio/checkbox gets the same box — those two are
   native controls sized to their own label, not a full-width field, so they
   are deliberately excluded here and styled separately below. */
.pmp-site__contact-form input:not([type='checkbox']):not([type='radio']),
.pmp-site__contact-form textarea,
.pmp-site__contact-form select {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--st-line);
  border-radius: var(--st-radius-sm);
  background: var(--st-surface-inset);
  color: inherit;
  font: inherit;
  transition: border-color 0.15s ease;
}

.pmp-site__contact-form input:not([type='checkbox']):not([type='radio']):focus,
.pmp-site__contact-form textarea:focus,
.pmp-site__contact-form select:focus {
  outline: none;
  border-color: var(--site-accent, var(--st-primary));
}

.pmp-site__contact-form textarea {
  min-height: 110px;
  resize: vertical;
}

/* A radio/checkbox question's fieldset — browser defaults give it its own
   inset border and legend, neither matching every other field's plain
   label-above-control layout. Stripped back to the same shape. */
.pmp-site__contact-form-group {
  border: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.pmp-site__contact-form-group legend {
  padding: 0;
  font-size: var(--st-text-xs);
  font-weight: 700;
  color: var(--st-ink-muted-2);
}

.pmp-site__contact-form-choice {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--st-text-sm);
  font-weight: 400;
  color: inherit;
}

.pmp-site__contact-form-choice input {
  width: auto;
  margin: 0;
  accent-color: var(--site-accent, var(--st-primary));
}

.pmp-site__contact-form button {
  align-self: flex-start;
  border: none;
  cursor: pointer;
}

.pmp-site__contact-form button:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

.pmp-site__contact-form-note {
  font-size: var(--st-text-xs);
  color: var(--st-ink-muted-2);
}

/* One field's own problem, under the control it belongs to — not a tooltip
   that vanishes on the next click. `--invalid` marks the control itself so the
   error is visible before it is read. */
.pmp-site__contact-form-field-error {
  margin-top: var(--st-space-2);
  color: var(--st-danger);
  font-size: var(--st-text-sm);
}

.pmp-site__contact-form-field--invalid :is(input, textarea, select) {
  border-color: var(--st-danger);
}

.pmp-site__contact-form-error {
  font-size: var(--st-text-xs);
  color: var(--st-danger);
}

@media (max-width: 720px) {
  .pmp-site__contact-grid { grid-template-columns: 1fr; }
}

.pmp-site__mark {
  width: calc(var(--site-logo-size, 34px) * var(--site-logo-scale, 1));
  height: calc(var(--site-logo-size, 34px) * var(--site-logo-scale, 1));
  background: var(--site-accent, var(--st-primary));
  color: var(--site-accent-ink, #fff);
  font-weight: 700;
  font-size: var(--st-text-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.pmp-site__mark--square { border-radius: 0; }
.pmp-site__mark--rounded { border-radius: var(--st-radius-sm); }
.pmp-site__mark--circle { border-radius: 50%; }

.pmp-site__caption {
  text-align: center;
  font-size: var(--st-text-xs);
  color: var(--site-text-override, var(--st-ink-muted));
  margin-bottom: var(--st-space-3);
}

.pmp-site__cta-banner {
  text-align: center;
}

/* Scoped to the plain choice so it cannot override an explicit one — see
   `sectionClassName`. */
.pmp-site__cta-banner.pmp-site__section--plain {
  background: var(--st-surface-inset);
}

.pmp-site__cta-banner .pmp-site__button {
  display: inline-block;
  margin-top: var(--st-space-2);
}

/* A portfolio grid, not a row of thumbnails: the first photo runs bigger, so
   whichever job the partner uploads first reads as the lead image rather
   than getting lost in a uniform row. */
.pmp-site__gallery-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 160px;
  gap: var(--st-space-3);
}

.pmp-site__gallery-grid > :first-child {
  grid-column: span 2;
  grid-row: span 2;
}

.pmp-site__gallery-empty {
  text-align: center;
  border: 1px dashed var(--st-line-strong);
  border-radius: var(--st-radius-md);
  margin: 0 26px 30px;
  padding: 30px 26px;
}

.pmp-site__gallery-photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--st-radius-md);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.pmp-site__gallery-photo:hover {
  transform: scale(1.02);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
}

@media (max-width: 640px) {
  .pmp-site__gallery-grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 130px; }
  .pmp-site__gallery-grid > :first-child { grid-row: span 1; }
}

/* A photo-led card grid, the same family as `.pmp-site__gallery-grid` —
   a partner's posts read better led by their cover photo than as a
   plain text list, especially for a mostly-visual trade like
   installations. */
.pmp-site__blog-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--st-space-4);
  list-style: none;
  margin: 0;
  padding: 0;
}

.pmp-site__blog-card {
  display: flex;
  flex-direction: column;
  gap: var(--st-space-2);
  text-align: left;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

/* A card title is a card title, not a section heading.
   `.pmp-site__section :is(h1, h2)` is (0,1,1) and sets the full 2xl display
   scale, which a bare `<h2>` in a blog card inherited — a three-word title
   ran to three lines at 42px inside a 380px card. Two classes deep so it
   outranks that without an `!important`. */
.pmp-site__blog-list .pmp-site__blog-card h2 {
  font-size: var(--st-text-lg);
  line-height: var(--st-leading-snug);
  margin: 0;
  /* The same rule centres section headings, which left every card with a
     centred title over a left-aligned date and excerpt. */
  text-align: inherit;
}

.pmp-site__blog-cover {
  width: 100%;
  height: 180px;
  object-fit: cover;
  border-radius: var(--st-radius-md);
}

.pmp-site__blog-cover--placeholder {
  background: var(--st-surface-inset);
}

@media (max-width: 640px) {
  .pmp-site__blog-list { grid-template-columns: 1fr; }
}

.pmp-site__blog-empty {
  text-align: center;
  border: 1px dashed var(--st-line-strong);
  border-radius: var(--st-radius-md);
  margin: 0 26px 30px;
  padding: 30px 26px;
}

/* Like/Unlike + the running count + Share, all in a row under the story
   and above the comments — the three ways to engage with a post that
   aren't leaving a comment. */
.pmp-site__blog-engagement {
  display: flex;
  align-items: center;
  gap: var(--st-space-3);
  margin: var(--st-space-4) 0;
  padding: var(--st-space-3) 0;
  border-top: 1px solid var(--st-line);
  border-bottom: 1px solid var(--st-line);
}

.pmp-site__blog-comments {
  margin-top: var(--st-space-5);
}

.pmp-site__blog-comment-list {
  list-style: none;
  margin: 0 0 var(--st-space-4);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--st-space-3);
}

.pmp-site__blog-comment {
  padding-bottom: var(--st-space-3);
  border-bottom: 1px solid var(--st-line);
}

.pmp-site__blog-comment:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.pmp-site__blog-comment-meta {
  font-size: var(--st-text-xs);
  color: var(--st-ink-muted-2);
  margin-bottom: 4px;
}

/* Sits inline in the article, not beside other content the way the
   Contact page's own form does — no raised-card shadow needed here. */
.pmp-site__blog-comment-form {
  box-shadow: none;
  max-width: 480px;
}

.pmp-site__blog-back {
  display: inline-block;
  border: none;
  background: none;
  padding: 0;
  margin-bottom: var(--st-space-3);
  color: var(--site-accent, var(--st-primary));
  cursor: pointer;
  font: inherit;
}

/*
  Prose a partner wrote, rather than copy laid out by a section.

  Everything else on a site is a component that sets its own spacing, so the
  base layer zeroes margins and list padding. A blog post is the exception: it
  is a stream of paragraphs, headings and bullets, and it has to read like one
  on the page as well as in the editor's preview. Scoped to this container so
  no section inherits it.
*/
.pmp-site__prose > * + * {
  margin-top: var(--st-space-3);
}

.pmp-site__prose p {
  max-width: var(--st-measure);
  line-height: var(--st-leading-body);
}

.pmp-site__prose h2 {
  margin-top: var(--st-space-5);
  font-size: var(--st-text-xl);
}

/* The markers and the indent the reset takes away — a list without either
   reads as a run of stray lines. */
.pmp-site__prose :is(ul, ol) {
  margin-top: var(--st-space-3);
  padding-left: var(--st-space-5);
  list-style: revert;
}

.pmp-site__prose li + li {
  margin-top: var(--st-space-2);
}

.pmp-site__prose img {
  max-width: 100%;
  height: auto;
  border-radius: var(--st-radius-md);
}

/* A link inside prose has to look like one: the base layer strips the
   underline site-wide, which is right for nav and buttons and wrong here. */
.pmp-site__prose a {
  text-decoration: underline;
}

.pmp-site__blog-detail-cover {
  width: 100%;
  max-height: 360px;
  object-fit: cover;
  border-radius: var(--st-radius-md);
  margin-bottom: var(--st-space-3);
}

.pmp-site__contact-details {
  display: flex;
  gap: var(--st-space-3);
  justify-content: center;
  margin-top: var(--st-space-2);
  font-size: var(--st-text-sm);
}

.pmp-site__contact-details a {
  color: inherit;
}

.pmp-site__social {
  display: flex;
  gap: var(--st-space-2);
  justify-content: center;
  margin-top: var(--st-space-3);
}

.pmp-site__contact-map {
  margin-top: var(--st-space-4);
  border-radius: var(--st-radius-md);
  overflow: hidden;
  aspect-ratio: 16 / 10;
  border: 1px solid var(--st-line);
}

.pmp-site__contact-map iframe {
  width: 100%;
  height: 100%;
  border: 0;
}

.pmp-site__social-icon {
  /* A circle, and nothing gets to stretch it into an oval: it is a flex item
     wherever it appears, and the default `align-items: stretch` would. */
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  color: #fff;
  transition: transform 0.15s ease;
}

.pmp-site__social-icon:hover {
  transform: translateY(-2px);
}

/* ==========================================================================
   Per-template layout. A template is a theme (theme.ts) plus the structural
   differences below — three genuinely different arrangements of the same
   sections, not one layout re-coloured three ways.
   ========================================================================== */

/* ---- Bold: full-bleed, centred, one big statement ---- */
.pmp-site--bold .pmp-site__nav {
  background: color-mix(in srgb, var(--site-accent) 22%, #10140f);
  color: #f4f6f5;
  border-bottom: none;
}

/* Menu links only — not `.pmp-site__cta`, which keeps its own accent
   background and white text. Scoping this to `.pmp-site__menu` rather than
   every `<a>` in the nav mattered: the old blanket `.pmp-site__nav a` rule
   had higher specificity than `.pmp-site__cta`'s own colour, so it won and
   inherited the dark strip's near-white text onto the CTA's white
   background underneath — invisible. */
.pmp-site--bold .pmp-site__menu a { color: inherit; }

.pmp-site--bold .pmp-site__hero {
  text-align: center;
  padding: 76px 34px;
}

.pmp-site--bold .pmp-site__hero-content { max-width: var(--site-content-max, 640px); margin: 0 auto; }
.pmp-site--bold .pmp-site__kicker { display: block; }
/* bold's hero is full-bleed centred rather than a copy column, so its
   headline runs a step larger than the shared scale and drops the measure. */
.pmp-site--bold .pmp-site__hero :is(h1, h2) {
  font-size: clamp(2.75rem, 1.9rem + 4vw, 5rem);
  max-width: none;
}
/* Hero-scoped, not every `.pmp-site__lead` in the template — About and
   Pricing's body text should not centre just because the hero's does. */
.pmp-site--bold .pmp-site__hero .pmp-site__lead { margin-left: auto; margin-right: auto; max-width: none; }
.pmp-site--bold .pmp-site__hero .pmp-site__buttons { justify-content: center; }

.pmp-site--bold .pmp-site__section :is(h1, h2) {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--st-text-lg);
}

.pmp-site--bold .pmp-site__cards { grid-template-columns: repeat(3, 1fr); }

.pmp-site--bold .pmp-site__card {
  border: none;
  border-left: 3px solid var(--site-accent);
  border-radius: 0;
  background: var(--st-surface-inset);
}

.pmp-site--bold .pmp-site__cta-banner.pmp-site__section--plain {
  background: color-mix(in srgb, var(--site-accent) 12%, var(--st-surface));
}

/* ---- Clean: a split hero, hairlines instead of cards, generous air ---- */
.pmp-site--clean .pmp-site__hero {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  align-items: center;
  gap: var(--st-space-5);
  padding: 64px 44px;
}

.pmp-site--clean .pmp-site__hero-visual {
  display: block;
  aspect-ratio: 4 / 3;
  border-radius: var(--st-radius-md);
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--site-accent) 30%, white),
    color-mix(in srgb, var(--site-accent) 6%, white)
  );
}

.pmp-site--clean .pmp-site__hero :is(h1, h2) { font-weight: 600; letter-spacing: -0.01em; }
.pmp-site--clean .pmp-site__button { border-radius: 999px; }

.pmp-site--clean .pmp-site__button--ghost {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--site-hero-ink) 30%, transparent);
}

.pmp-site--clean .pmp-site__section :is(h1, h2) {
  text-align: left;
  font-weight: 600;
}

.pmp-site--clean .pmp-site__cards { grid-template-columns: 1fr; gap: 0; }

.pmp-site--clean .pmp-site__card {
  border: none;
  border-top: 1px solid var(--st-line);
  border-radius: 0;
  padding: var(--st-space-4) 0;
  background: transparent;
}

.pmp-site--clean .pmp-site__cta-banner { text-align: left; }
.pmp-site--clean .pmp-site__cta-banner :is(h1, h2) { text-align: left; }

/* ---- Warm: rounded, framed, a floated shape behind the hero copy ---- */
.pmp-site--warm .pmp-site__nav {
  margin: 14px 20px 0;
  border: 1px solid var(--st-line);
  border-radius: 999px;
}

.pmp-site--warm .pmp-site__hero {
  position: relative;
  overflow: hidden;
  margin: 20px;
  border-radius: 28px;
  padding: 56px 42px;
}

.pmp-site--warm .pmp-site__hero-content { position: relative; z-index: 1; max-width: var(--site-content-max, 56ch); }

.pmp-site--warm .pmp-site__hero-visual {
  display: block;
  position: absolute;
  right: -60px;
  bottom: -80px;
  width: 240px;
  height: 240px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--site-accent) 55%, white);
  opacity: 0.3;
}

.pmp-site--warm .pmp-site__button { border-radius: 999px; }

.pmp-site--warm .pmp-site__card {
  border: none;
  border-radius: 20px;
  background: var(--st-surface-inset);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}

.pmp-site--warm .pmp-site__gallery-photo { border-radius: 20px; }
.pmp-site--warm .pmp-site__cta-banner { border-radius: 20px; margin: 0 4px; }

@media (max-width: 640px) {
  .pmp-site--clean .pmp-site__hero { grid-template-columns: 1fr; }
  .pmp-site--clean .pmp-site__hero-visual { order: -1; aspect-ratio: 16 / 9; }
}

/* ---- Accessibility floor ---- */

/* Keyboard focus. Every interactive element on a published site had no focus
   indication at all, so a keyboard visitor could not tell where they were.
   `:focus-visible` rather than `:focus` keeps it off mouse clicks. */
.pmp-site a:focus-visible,
.pmp-site button:focus-visible,
.pmp-site summary:focus-visible,
.pmp-site input:focus-visible,
.pmp-site textarea:focus-visible,
.pmp-site select:focus-visible {
  outline: 2px solid var(--site-accent, var(--st-primary));
  outline-offset: 2px;
  border-radius: var(--st-radius-sm);
  box-shadow: var(--st-focus-ring);
}

/* A visitor who has asked their system for less motion gets none. This
   covers the hover transforms already here and is the floor the scroll and
   hover work later in the premium plan builds on. */
@media (prefers-reduced-motion: reduce) {
  .pmp-site *,
  .pmp-site *::before,
  .pmp-site *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .pmp-site__button:hover,
  .pmp-site__card:hover,
  .pmp-site__gallery-photo:hover,
  .pmp-site__social-icon:hover,
  .pmp-site__blog-card:hover,
  .pmp-site__blog-card:hover .pmp-site__blog-cover,
  .pmp-site__tier:hover {
    transform: none;
  }

  /* `startReveal` does not add these for a reduced-motion visitor, but if one
     changes the setting mid-visit the state must not be left hidden. The seam
     and parallax layers have their own floor in `site-motion.css`, beside the
     rules they undo. */
  .pmp-site__reveal {
    opacity: 1;
    transform: none;
  }

  /* The staggered children too — `startReveal` bails out before adding any
     class here, but the stylesheet must not depend on that to stay readable. */
  .pmp-site__reveal :is(.pmp-site__cards, .pmp-site__gallery-grid) > *,
  .pmp-site__hero.pmp-site__reveal .pmp-site__hero-content > * {
    opacity: 1;
    transform: none;
    transition: none;
    transition-delay: 0s;
  }
}

/* ---- The menu on a narrow screen ---- */

/* The active page reads as active — `aria-current` was already set on it and
   never styled, so a visitor could not tell where they were. */
.pmp-site__menu a[aria-current='page'] {
  color: var(--site-accent, var(--st-primary));
  font-weight: 700;
}

/* An accent-backed header is already the accent colour, so marking the active
   link with that same accent made it vanish into its own background — the one
   link a visitor most needs to see. The header's background travels as a class
   for this; it is applied inline, so there was nothing to scope against. */
.pmp-site__nav--bg-accent .pmp-site__menu a[aria-current='page'] {
  color: inherit;
}

.pmp-site__menu a[aria-current='page']::after {
  content: '';
  display: block;
  height: 2px;
  margin-top: 3px;
  border-radius: var(--st-radius-pill);
  background: currentColor;
}

/* Hidden above the breakpoint — the toggle only exists for the drawer. */
.pmp-site__menu-toggle {
  display: none;
  background: none;
  border: 0;
  padding: var(--st-space-2);
  cursor: pointer;
  color: inherit;
}

.pmp-site__menu-bars,
.pmp-site__menu-bars::before,
.pmp-site__menu-bars::after {
  display: block;
  width: 22px;
  height: 2px;
  border-radius: var(--st-radius-pill);
  background: currentColor;
}

.pmp-site__menu-bars::before,
.pmp-site__menu-bars::after {
  content: '';
  position: relative;
}

.pmp-site__menu-bars::before { top: -7px; }
.pmp-site__menu-bars::after { top: 5px; }

@media (max-width: 820px) {
  .pmp-site__menu-toggle { display: block; order: 3; }

  .pmp-site__nav { flex-wrap: wrap; }

  /* The centre variant lays the header out as a 3-column grid, which the
     wrap-based drawer below cannot live in — on a phone it becomes the same
     flex row every other variant already is. Above the breakpoint the
     toggle is `display: none` and generates no grid item at all, so the
     centred desktop layout is untouched. */
  .pmp-site__nav--center {
    display: flex;
    flex-wrap: wrap;
  }

  .pmp-site__nav--center .pmp-site__brand,
  .pmp-site__nav--center .pmp-site__menu,
  .pmp-site__nav--center .pmp-site__cta {
    grid-column: auto;
    justify-self: auto;
  }

  /* Collapsed by default, and a full-width panel below the header row when
     open — not an off-canvas overlay, which would need scroll locking and a
     focus trap for no gain at this size. */
  .pmp-site__menu {
    display: none;
    order: 4;
    flex-basis: 100%;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--st-space-4);
    padding: var(--st-space-5) 0 var(--st-space-3);
  }

  .pmp-site__menu--open { display: flex; }

  .pmp-site__menu a[aria-current='page']::after { display: none; }

  /* The WhatsApp CTA keeps its place beside the logo rather than dropping
     into the drawer — it is the header's whole job on a phone. */
  .pmp-site__cta { order: 2; }
}

/* ---- About with a photo ---- */

/* Single column until the partner picks a photo, so an About section built
   before the field existed is untouched. */
.pmp-site__about--right,
.pmp-site__about--left {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--st-space-8);
  align-items: center;
}

.pmp-site__about--left .pmp-site__about-copy { order: 2; }

.pmp-site__about-photo {
  width: 100%;
  /* Reserved before the file arrives, so the section does not jump as the
     photo loads — the same reason every other image here carries one. */
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--st-radius-lg);
  box-shadow: var(--st-shadow-md);
}

.pmp-site__about--right .pmp-site__lead,
.pmp-site__about--left .pmp-site__lead {
  /* The measure is the column's job here, not a character count. */
  max-width: none;
}

@media (max-width: 820px) {
  .pmp-site__about--right,
  .pmp-site__about--left {
    grid-template-columns: 1fr;
    gap: var(--st-space-5);
  }

  /* Copy first on a phone, whichever side the photo takes on desktop. */
  .pmp-site__about--left .pmp-site__about-copy { order: 0; }
}

/* A feature card's stand-in for an icon. Small and square so a photo reads
   as a mark rather than competing with the card's own words. */
.pmp-site__card-icon {
  width: 44px;
  height: 44px;
  object-fit: cover;
  border-radius: var(--st-radius-md);
  margin-bottom: var(--st-space-3);
}

/* ---- Pricing tiers ---- */

/* Absent until the partner adds a tier, so a pricing section built when this
   was a headline and a paragraph is untouched. Fixed columns rather than
   auto-fit: prices are meant to be compared across, and an orphaned card on
   its own row defeats that. */
.pmp-site__tiers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--st-space-5);
  margin-top: var(--st-space-8);
  align-items: start;
}

.pmp-site__tier {
  display: flex;
  flex-direction: column;
  gap: var(--st-space-3);
  padding: var(--st-space-6) var(--st-space-5);
  border: 1px solid var(--st-line);
  border-radius: var(--st-radius-lg);
  background: var(--st-surface);
  text-align: left;
}

/* The recommended option, lifted rather than recoloured: a partner's accent
   is already doing work elsewhere on the page, and a second accent block
   competes with the section's own call to action. */
.pmp-site__tier--featured {
  border-color: var(--site-accent, var(--st-primary));
  box-shadow: var(--st-shadow-md);
}

.pmp-site__tier h3 {
  font-size: var(--st-text-lg);
  text-align: left;
}

.pmp-site__tier-price {
  font-size: var(--st-text-2xl);
  line-height: var(--st-leading-tight);
  letter-spacing: -0.02em;
  font-weight: 700;
}

.pmp-site__tier-interval {
  font-size: var(--st-text-sm);
  font-weight: 400;
  color: var(--st-ink-muted-2);
  letter-spacing: 0;
}

.pmp-site__tier-features {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--st-space-2);
  font-size: var(--st-text-sm);
  color: var(--st-ink-muted);
}

.pmp-site__tier-features li {
  padding-left: 1.35em;
  position: relative;
}

/* A tick drawn in CSS rather than an icon font or an inline SVG per item —
   the published site should ship as little as it can get away with. */
.pmp-site__tier-features li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.45em;
  width: 0.5em;
  height: 0.28em;
  border-left: 2px solid var(--site-accent, var(--st-primary));
  border-bottom: 2px solid var(--site-accent, var(--st-primary));
  transform: rotate(-45deg);
}

/* The tier's own button sits at the card's foot whatever the feature count,
   so a row of cards lines its buttons up. */
.pmp-site__tier .pmp-site__button {
  margin-top: auto;
  align-self: flex-start;
}

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

/* Only once there is more than the identity line to show — a site built
   before `SiteContent.footer` existed keeps the single centred line. */
.pmp-site__footer--columns {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--st-space-8);
  align-items: start;
  text-align: left;
}

.pmp-site__footer-col {
  display: flex;
  flex-direction: column;
  gap: var(--st-space-2);
  font-size: var(--st-text-sm);
}

/* Every link in a footer column *except* a social icon, which is not a line of
   text and does not want a text link's treatment. `.pmp-site__footer-col a` is
   (0,1,1) and `.pmp-site__social-icon` is (0,1,0), so without this the column
   rule won on specificity: `width: fit-content` collapsed the 30px circle to
   the width of its glyph — a tall ellipse — and `color: inherit` turned the
   white mark grey on top of the platform's own brand colour. */
.pmp-site__footer-col a:not(.pmp-site__social-icon) {
  color: inherit;
  width: fit-content;
}

.pmp-site__footer-col a:not(.pmp-site__social-icon):hover {
  color: var(--site-accent, var(--st-primary));
}

.pmp-site__footer--columns .pmp-site__social {
  justify-content: flex-start;
  margin-top: var(--st-space-2);
}

/* Spans every column and sits under them — programme attribution and the
   partner's own legal line are page-wide, not one column's business. */
/* The number reads as data next to the channel that labels it. */
.pmp-site__footer-number {
  color: var(--st-ink-muted);
}

.pmp-site__footer-legal {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--st-space-3);
  padding-top: var(--st-space-5);
  border-top: 1px solid var(--st-line);
  font-size: var(--st-text-xs);
  color: var(--st-ink-muted-2);
}

.pmp-site__footer:not(.pmp-site__footer--columns) .pmp-site__footer-legal {
  justify-content: center;
  border-top: 0;
  padding-top: 0;
}

@media (max-width: 820px) {
  .pmp-site__footer--columns {
    grid-template-columns: 1fr;
    gap: var(--st-space-6);
  }
}

/* ---- Trust logos ---- */

/* Uniform optical sizing: a row of logos at their own natural sizes reads as
   a mistake. Greyscale until hover keeps a wall of competing brand colours
   from pulling attention off the partner's own. */
.pmp-site__trust--logos {
  gap: var(--st-space-6);
}

.pmp-site__trust-logo {
  height: 34px;
  width: auto;
  max-width: 140px;
  object-fit: contain;
  filter: grayscale(1);
  opacity: 0.75;
  transition: filter 0.2s ease, opacity 0.2s ease;
}

.pmp-site__trust-logo:hover {
  filter: grayscale(0);
  opacity: 1;
}

.pmp-site__contact-hours {
  font-size: var(--st-text-sm);
  color: var(--st-ink-muted);
  margin-top: var(--st-space-3);
}

/* ---- Hero variants ---- */

/* `standard` has no class of its own, so a hero built before variants existed
   renders exactly as it did. Each variant below only overrides what it must. */

/*
  A hero variant has to beat the template's own hero layout, not sit inside it.
  `clean` lays its hero out as a two-column grid, so "centred" was centring
  text inside a 405px column beside a decorative pane rather than centring the
  hero. Same shape as the CTA alignment bug: a rule written for one context
  outranking a partner's explicit choice.
*/
.pmp-site .pmp-site__hero--centred {
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
  grid-template-columns: none;
}

/* The template's decorative pane is not part of a centred hero. */
.pmp-site .pmp-site__hero--centred .pmp-site__hero-visual {
  display: none;
}

/* A grid or flex child will not shrink below its content by default, so a
   headline sized from the viewport can push its own column wider than the
   space available. */
.pmp-site__hero-content,
.pmp-site__about-copy {
  min-width: 0;
}

.pmp-site__hero :is(h1, h2) {
  overflow-wrap: break-word;
}

/* Each variant keeps its own width as the fallback, so nothing moves until a
   partner actually picks a Content width. */
.pmp-site__hero--centred .pmp-site__hero-content {
  max-width: var(--site-content-max, 760px);
  margin-inline: auto;
}

.pmp-site__hero--centred :is(h1, h2) {
  max-width: none;
}

.pmp-site__hero--centred .pmp-site__lead {
  margin-inline: auto;
}

.pmp-site__hero--centred .pmp-site__buttons {
  justify-content: center;
}

/* The photo takes its own column rather than sitting behind the copy, so a
   partner's own photo is the subject instead of a backdrop the text has to
   survive. */
.pmp-site__hero--split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--st-space-8);
  align-items: center;
}

.pmp-site__hero-photo {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--st-radius-lg);
  box-shadow: var(--st-shadow-lg);
}

/* Split shows the photo as an element, so the section keeps the template's
   own background and needs neither the scrim nor the forced white ink that
   a photo *backdrop* does. */
.pmp-site__hero--split.pmp-site__hero--photo {
  color: var(--site-hero-ink);
  background: var(--site-hero-bg);
}

.pmp-site__hero--split.pmp-site__hero--photo::before {
  content: none;
}

.pmp-site__hero--split.pmp-site__hero--photo .pmp-site__kicker,
.pmp-site__hero--split.pmp-site__hero--photo .pmp-site__lead {
  color: var(--site-text-override, color-mix(in srgb, var(--site-hero-ink) 85%, transparent));
}

/* Full-bleed photo: taller than the shared minimum, copy anchored low the way
   an editorial cover sits. */
.pmp-site .pmp-site__hero--fullPhoto {
  display: flex;
  flex-direction: column;
  grid-template-columns: none;
  /* A column's cross axis is horizontal, so `align-items` no longer places
     the copy vertically here — `justify-content` does, and without it the
     copy sat at the top of a full-height hero however tall the partner made
     it. The base hero is a row, where its own `align-items: center` already
     did this; the variant changed the axis and lost it. */
  justify-content: center;
}

.pmp-site .pmp-site__hero--fullPhoto .pmp-site__hero-visual {
  display: none;
}

.pmp-site__hero--fullPhoto {
  min-height: min(88vh, 860px);
  /* `align-items: flex-end` used to sit here, from when this variant was a
     row and it meant "against the bottom of the photo". As a column it would
     mean "against the right", and it was outranked by
     `.pmp-site--clean .pmp-site__hero` anyway — dead, and a trap for whoever
     read it next. */
}

.pmp-site__hero--fullPhoto .pmp-site__hero-content {
  max-width: var(--site-content-max, 820px);
}

@media (max-width: 820px) {
  .pmp-site__hero--split {
    grid-template-columns: 1fr;
    gap: var(--st-space-5);
  }
}

/* ---- Feature card columns ---- */

/* `auto` stays `auto-fit`, which is what every features section has used; the
   fixed counts exist because auto-fit orphans an odd card on a wide screen. */
.pmp-site__cards--two { grid-template-columns: repeat(2, 1fr); }
.pmp-site__cards--three { grid-template-columns: repeat(3, 1fr); }
.pmp-site__cards--four { grid-template-columns: repeat(4, 1fr); }

@media (max-width: 900px) {
  .pmp-site__cards--three,
  .pmp-site__cards--four,
  /* bold pins three columns of its own (see its template block below) and
     never collapsed them — a phone got a three-column card grid overflowing
     the viewport by about 70px. Caught by driving the real page at 390px. */
  .pmp-site--bold .pmp-site__cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 640px) {
  .pmp-site__cards--two,
  .pmp-site__cards--three,
  .pmp-site__cards--four,
  .pmp-site--bold .pmp-site__cards {
    grid-template-columns: 1fr;
  }
}

/*
  Editable things look editable.

  The canvas borrows Canva's model — click the thing you want to change — and
  that model only works because everybody has used Canva. A partner who clicks
  nothing sees nothing, so a widget says it is clickable before it is clicked
  rather than only after.

  The class is added by `widgetProps` only when a selection handler exists,
  which is only ever the builder — a published site has nothing to select and
  never grows these. Drawn with `box-shadow` rather than `outline` so it
  follows a rounded corner and cannot change layout.
*/
.pmp-site__widget--hoverable:hover {
  cursor: pointer;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--site-accent, var(--st-primary)) 38%, transparent);
  border-radius: 3px;
}

/* The one already selected does not also need the hover cue. */
.pmp-site__widget--selected:hover {
  box-shadow: none;
}

/* ---- Motion ---- */

/* The entrance, seam and parallax layers live in `site-motion.css`, imported
   at the top of this file. They are template-authored and token-driven; what
   stays here is the component-level hover polish below, which belongs beside
   the components it decorates. */

/* A photo drifting in under its own frame — cheap, and the one hover effect
   that reads as "there is more to see here" rather than decoration. */
.pmp-site__gallery-photo,
.pmp-site__blog-cover {
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

.pmp-site__gallery-photo:hover,
.pmp-site__blog-card:hover .pmp-site__blog-cover {
  transform: scale(1.04);
}

/* The frame has to clip, or the scale just overflows its neighbours. */
.pmp-site__gallery-grid,
.pmp-site__blog-card {
  overflow: hidden;
}

.pmp-site__blog-card {
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}

.pmp-site__blog-card:hover {
  box-shadow: var(--st-shadow-md);
  transform: translateY(-2px);
}

.pmp-site__tier {
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}

.pmp-site__tier:hover {
  box-shadow: var(--st-shadow-md);
  transform: translateY(-2px);
}

/* ---- A sticky header ---- */

/* On by default: a visitor deep in a long page should not have to scroll back
   up to navigate or to call. It carries its own background once stuck, because
   the template's header is often transparent over the hero. */
.pmp-site__nav--sticky {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--st-surface);
  backdrop-filter: saturate(1.4) blur(8px);
}

/* An inline background (the tint and accent choices) is set on the element and
   beats the rule above, which is what should happen — a partner who chose a
   header colour keeps it while stuck. */

/* ---- FAQ ---- */

.pmp-site__faqs summary {
  cursor: pointer;
  font-weight: 600;
  padding: var(--st-space-4) 0;
  /* The marker is drawn below, so the browser's own triangle would be a second
     one in a different style. */
  list-style: none;
  display: flex;
  justify-content: space-between;
  gap: var(--st-space-4);
  align-items: center;
}

.pmp-site__faqs summary::-webkit-details-marker {
  display: none;
}

/* A plus that becomes a minus — the clearest possible "there is more here",
   and two pseudo-elements rather than an icon file. */
.pmp-site__faqs summary::after {
  content: '';
  flex: none;
  width: 12px;
  height: 12px;
  background:
    linear-gradient(var(--site-accent, var(--st-primary)) 0 0) center / 100% 2px no-repeat,
    linear-gradient(var(--site-accent, var(--st-primary)) 0 0) center / 2px 100% no-repeat;
  transition: transform 0.2s ease;
}

.pmp-site__faqs details[open] summary::after {
  /* Rotating to hide the vertical stroke leaves the horizontal one: a minus. */
  background: linear-gradient(var(--site-accent, var(--st-primary)) 0 0) center / 100% 2px no-repeat;
  transform: rotate(180deg);
}

.pmp-site__faqs details p {
  padding-bottom: var(--st-space-4);
  max-width: var(--st-measure);
  color: var(--st-ink-muted);
}

/* ---- A blog cover reserves its space ---- */

/* Without this the grid jumps as each cover arrives, which is the single most
   visible kind of layout shift on a list page. */
.pmp-site__blog-cover {
  aspect-ratio: 16 / 10;
  object-fit: cover;
  width: 100%;
}

@media (prefers-reduced-motion: reduce) {
  .pmp-site__faqs summary::after {
    transition: none;
  }
}

/* ---- Per-section size ---- */

/* A section given a minimum height centres its content within it. Flex rather
   than `place-content`, because the section's children are a normal stack and
   grid would change how they lay out. */
.pmp-site__section--sized {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.pmp-site__section--align-centre {
  text-align: center;
}

/* Left has to be stated, not assumed. The CTA banner centres itself, so a
   partner choosing Left needs a rule that outranks it rather than the absence
   of one. */
.pmp-site__section--align-left {
  text-align: left;
}

.pmp-site__section--align-left .pmp-site__lead {
  margin-inline: 0;
}

.pmp-site__section--align-left .pmp-site__buttons,
.pmp-site__section--align-left .pmp-site__cards {
  justify-content: flex-start;
}

/*
  A partner's alignment choice has to beat the template's own. `clean`
  left-aligns its section headings at two classes deep, which silently won
  against the inherited `text-align` above — so choosing Centre did nothing
  there and nowhere else. Found by comparing the four templates in a browser,
  not by reading the stylesheet.
*/
.pmp-site .pmp-site__section--align-left :is(h1, h2, h3),
.pmp-site .pmp-site__section--align-left .pmp-site__lead,
.pmp-site .pmp-site__section--align-left .pmp-site__caption {
  text-align: left;
}

.pmp-site .pmp-site__section--align-centre :is(h1, h2, h3),
.pmp-site .pmp-site__section--align-centre .pmp-site__lead,
.pmp-site .pmp-site__section--align-centre .pmp-site__caption {
  text-align: center;
}

.pmp-site__section--align-centre .pmp-site__lead {
  margin-inline: auto;
}

.pmp-site__section--align-centre .pmp-site__buttons,
.pmp-site__section--align-centre .pmp-site__cards {
  justify-content: center;
}

/* A centred section's cards keep their own left-aligned text — centring the
   words inside a card as well makes a paragraph hard to read. */
.pmp-site__section--align-centre .pmp-site__card {
  text-align: left;
}

/* ---- Background patterns ---- */

/* Drawn in CSS rather than shipped as an image: a texture is worth nothing if
   it costs a request. Both sit above the background and below the content. */
.pmp-site__section--dots,
.pmp-site__section--grid {
  position: relative;
}

.pmp-site__section--dots::after,
.pmp-site__section--grid::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
}

.pmp-site__section--dots::after {
  background-image: radial-gradient(currentColor 1px, transparent 1px);
  background-size: 22px 22px;
  opacity: 0.12;
}

.pmp-site__section--grid::after {
  background-image:
    linear-gradient(currentColor 1px, transparent 1px),
    linear-gradient(90deg, currentColor 1px, transparent 1px);
  background-size: 44px 44px;
  opacity: 0.07;
}

/* The content has to sit above the texture. `position: relative` on the
   children rather than a z-index war with the scrim. */
.pmp-site__section--dots > *,
.pmp-site__section--grid > * {
  position: relative;
  z-index: 1;
}

/* ---- Gallery layouts ---- */

/* Uniform: equal tiles, no lead image — a straight catalogue of work. */
.pmp-site__gallery-grid--uniform {
  grid-auto-rows: 200px;
}

.pmp-site__gallery-grid--uniform > :first-child {
  grid-column: span 1;
  grid-row: span 1;
}

/* Masonry via CSS multi-column, not a JS layout pass: it reflows on its own at
   any width and costs no script. The trade is that reading order runs down
   each column rather than across, which for a wall of photos is immaterial. */
.pmp-site__gallery-grid--masonry {
  display: block;
  /* Three, not four. Multi-column balances by total height and a photo cannot
     be broken, so a realistic gallery of six tiles balances into three columns
     and leaves the fourth standing empty — a dead strip down the right of the
     section. Three fills cleanly at six and at the eight-photo maximum. */
  columns: 3;
  column-gap: var(--st-space-5);
  grid-auto-rows: auto;
}

.pmp-site__gallery-grid--masonry > * {
  break-inside: avoid;
  margin-bottom: var(--st-space-5);
  grid-column: auto;
  grid-row: auto;
}

.pmp-site__gallery-grid--masonry .pmp-site__gallery-photo {
  height: auto;
  aspect-ratio: auto;
}

@media (max-width: 900px) {
  .pmp-site__gallery-grid--masonry { columns: 2; }
  .pmp-site__gallery-grid--uniform { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 640px) {
  .pmp-site__gallery-grid--masonry { columns: 1; }
  .pmp-site__gallery-grid--uniform { grid-template-columns: repeat(2, 1fr); }
}

/* ---- The slider ---- */

.pmp-site__gallery-slider {
  position: relative;
}

/* Scroll-snap does the work, so this swipes on a phone and scrolls on a
   trackpad with no JavaScript at all. The buttons below only add a pointer
   affordance for a mouse. */
.pmp-site__gallery-track {
  display: flex;
  gap: var(--st-space-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* Room for the focus ring, which would otherwise clip against the overflow. */
  padding: 4px;
  /* Firefox and the standards track; WebKit keeps its own below. */
  scrollbar-width: thin;
}

.pmp-site__gallery-track::-webkit-scrollbar {
  height: 6px;
}

.pmp-site__gallery-track::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, currentColor 25%, transparent);
  border-radius: var(--st-radius-pill);
}

.pmp-site__gallery-track img {
  flex: 0 0 auto;
  width: min(78%, 520px);
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--st-radius-lg);
  scroll-snap-align: center;
}

.pmp-site__gallery-controls {
  display: flex;
  justify-content: flex-end;
  gap: var(--st-space-2);
  margin-top: var(--st-space-3);
}

.pmp-site__gallery-controls button {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--st-line-strong);
  background: var(--st-surface);
  color: var(--st-ink);
  font-size: var(--st-text-lg);
  line-height: 1;
  cursor: pointer;
}

.pmp-site__gallery-controls button:hover {
  border-color: var(--site-accent, var(--st-primary));
}

@media (prefers-reduced-motion: reduce) {
  .pmp-site__gallery-track { scroll-behavior: auto; }
}

/* ---- The hero background slideshow ---- */

/* Slide one is the section's own CSS background; these are the ones after it.
   They start transparent and are faded in by `startHeroSlideshow` — with no
   script they stay transparent and the visitor sees slide one, which is the
   hero as it has always been. */
.pmp-site__hero-slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.2s ease-in-out;
  /* Under the scrim and the copy, above the section's own background. */
  z-index: 0;
}

.pmp-site__hero-slide--in {
  opacity: 1;
}

/* The scrim and the copy have to sit above the slides. */
.pmp-site__hero--photo::before {
  z-index: 1;
}

.pmp-site__hero-content {
  position: relative;
  z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
  .pmp-site__hero-slide {
    transition: none;
  }
}

/* Everything added by the modern pass, off for anyone who asked for less
   motion. The hover styling stays; only the movement goes. */
@media (prefers-reduced-motion: reduce) {
  .pmp-site__card,
  .pmp-site__button,
  .pmp-site__faqs details {
    transition: none;
  }

  .pmp-site__card:hover,
  .pmp-site__button:hover {
    transform: none;
  }
}

/* ---- Video ---- */

/*
  A background loop, behind everything the section says.

  Absolutely positioned and `object-fit: cover`, which is the only way to make
  a video behave like `background-size: cover` — a video's intrinsic aspect
  ratio otherwise letterboxes it inside the section.

  `z-index: 0` with the content above it rather than a negative index: a
  negative one escapes the section's stacking context and slides under the
  page background, which is a black rectangle where the hero used to be.
*/
/*
  The frame the backdrop lives in.

  It clips, so the section does not have to. An oversized parallaxed loop has
  to be clipped by something, and the obvious something — `overflow: hidden` on
  the section — also clips the section's copy, which is how a hero headline
  ended up cut off mid-word. The frame has no text in it and can clip freely.
*/
.pmp-site__section-media {
  position: absolute;
  inset: 0;
  display: block;
  overflow: hidden;
  z-index: 0;
  /* The scrim (a ::before) and the copy both sit above this. */
  pointer-events: none;
}

.pmp-site__section-media > video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The section establishes the stacking context the backdrop sits in — and
   nothing else. It deliberately does not clip. */
.pmp-site__hero,
.pmp-site__section--video {
  position: relative;
}

.pmp-site__hero > *:not(.pmp-site__section-media),
.pmp-site__section--video > *:not(.pmp-site__section-media) {
  position: relative;
  z-index: 1;
}

/* ---- A video a visitor chooses to watch ---- */

.pmp-site__video-frame,
.pmp-site__video-facade {
  display: block;
  width: 100%;
  /* Reserved before anything loads, so the page does not jump when it does. */
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--st-radius-lg);
  background: var(--st-surface-inset) center / cover no-repeat;
  margin-top: var(--st-space-5);
}

.pmp-site__video-facade {
  position: relative;
  cursor: pointer;
  padding: 0;
}

/* The play button. Drawn rather than an image — one less request, and it
   scales with the frame instead of blurring. */
.pmp-site__video-play {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(56px, 7vw, 84px);
  height: clamp(56px, 7vw, 84px);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  transition: background 0.2s ease, transform 0.2s ease;
}

/* A triangle from a border, centred by the optical offset a triangle needs —
   its visual centre is left of its bounding box. */
.pmp-site__video-play::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 54%;
  transform: translate(-50%, -50%);
  border-style: solid;
  border-width: 0.62em 0 0.62em 1.05em;
  border-color: transparent transparent transparent #fff;
  font-size: clamp(56px, 7vw, 84px);
}

.pmp-site__video-facade:hover .pmp-site__video-play {
  background: var(--site-accent, var(--st-primary));
  transform: translate(-50%, -50%) scale(1.06);
}

.pmp-site__video-facade:focus-visible {
  outline: none;
  box-shadow: var(--st-focus-ring);
}

.pmp-site__video-caption {
  margin-top: var(--st-space-3);
  color: var(--site-text-override, var(--st-ink-muted));
  font-size: var(--st-text-sm);
}

/* Only the partner ever sees this — a visitor's page renders no frame at all
   for a section with no playable link. */
.pmp-site__video-empty {
  margin-top: var(--st-space-5);
  padding: var(--st-space-6);
  border: 1px dashed var(--st-line-strong);
  border-radius: var(--st-radius-lg);
  color: var(--st-ink-muted);
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .pmp-site__video-facade:hover .pmp-site__video-play {
    transform: translate(-50%, -50%);
  }
}

/* ---- Rows edited on the canvas ---- */

/*
  A row's own delete, revealed on hover.

  Positioned against the row, so it travels with it and needs no measuring —
  the opposite trade from the toolbars, which float and must be positioned,
  because a toolbar belongs to a selection and this belongs to a specific box
  that is already on screen.

  Hidden with opacity rather than `display`, so it does not reflow the card as
  it appears and cannot be missed by a mouse moving quickly across a grid.
*/
.pmp-site__row {
  position: relative;
}

.pmp-site__row-remove {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--st-line-strong);
  border-radius: 50%;
  background: var(--st-surface);
  color: var(--st-ink-muted);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.pmp-site__row:hover .pmp-site__row-remove,
.pmp-site__row-remove:focus-visible {
  opacity: 1;
}

.pmp-site__row-remove:hover,
.pmp-site__row-remove:focus-visible {
  color: var(--st-danger);
  border-color: var(--st-danger);
}

/*
  The add control, always visible.

  Unlike the delete: a partner looking at a two-card section has to be able to
  discover that a third is possible, and there is nothing to hover over to find
  that out. Dashed and quiet, so it reads as a slot rather than as content.
*/
.pmp-site__row-add {
  display: block;
  width: 100%;
  margin-top: var(--st-space-5);
  padding: var(--st-space-4);
  border: 1px dashed var(--st-line-strong);
  border-radius: var(--st-radius-md);
  background: transparent;
  color: var(--st-ink-muted);
  font-size: var(--st-text-sm);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.pmp-site__row-add:hover {
  border-color: var(--site-accent, var(--st-primary));
  color: var(--site-accent, var(--st-primary));
  background: color-mix(in srgb, var(--site-accent, var(--st-primary)) 6%, transparent);
}

.pmp-site__row-add:focus-visible {
  outline: none;
  box-shadow: var(--st-focus-ring);
}

/* On an accent or photo ground the muted grey and the hairline both vanish —
   the same problem the lead paragraph has there, and the same fix. */
.pmp-site__section--accent .pmp-site__row-add,
.pmp-site__section--photo .pmp-site__row-add,
.pmp-site__section--video .pmp-site__row-add {
  border-color: color-mix(in srgb, var(--site-accent-ink, #fff) 45%, transparent);
  color: color-mix(in srgb, var(--site-accent-ink, #fff) 88%, transparent);
}

/* ---- A floating WhatsApp button ---- */

/*
  Pinned to the corner of every page, above everything.

  `z-index: 30` clears the sticky header's 20 — with a lower value the button
  slid under the header the moment a visitor scrolled a page whose header is
  stuck, which is precisely the state it exists for.

  The offsets are `max()` against the safe area rather than plain rems: on a
  phone with a home indicator or a rounded corner, a flat `1rem` puts the
  button under the system gesture bar, where a tap either does nothing or
  dismisses the browser.

  WhatsApp green is fixed, not the partner's accent. It is a recognisable mark
  and repainting it in a partner's brand colour makes it a mystery circle —
  the one place on the site where borrowing the brand palette costs more than
  it earns.
*/
.pmp-site__floating-cta {
  position: fixed;
  right: max(1.25rem, env(safe-area-inset-right, 0px));
  bottom: max(1.25rem, env(safe-area-inset-bottom, 0px));
  z-index: 30;
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  background: #25d366;
  color: #fff;
  box-shadow: 0 6px 20px rgb(0 0 0 / 28%);
  transition: transform 160ms ease, box-shadow 160ms ease;
}

.pmp-site__floating-cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 26px rgb(0 0 0 / 34%);
}

.pmp-site__floating-cta:focus-visible {
  outline: none;
  /* The site's own focus ring plus a white gap, because the ring's colour can
     land close to the green and vanish into it. */
  box-shadow: 0 0 0 3px #fff, var(--st-focus-ring);
}

/* The button is a fixed overlay, so nothing in the flow reserves room for it —
   it lands on top of whatever the last thing on the page happens to be, which
   is the footer's own WhatsApp link and legal line. This buys back the space
   it covers. Scoped to `--has-floating-cta` so a site without the button is
   not given a stray gap at the bottom of every page. */
.pmp-site--has-floating-cta .pmp-site__footer {
  padding-bottom: calc(3.5rem + 2.5rem);
}

@media (prefers-reduced-motion: reduce) {
  .pmp-site__floating-cta {
    transition: none;
  }
  .pmp-site__floating-cta:hover {
    transform: none;
  }
}

/* ---- A hero set to "Short" ----

   `sectionSizeStyle` gives a section a minimum height, and for a hero that
   minimum is already beneath what its padding and headline take up on their
   own — so choosing Short did nothing visible, and read as broken. These
   take the hero down to the choice: less vertical padding, a headline a step
   smaller, and a smaller visual pane where a template has one. Scoped to the
   hero and to Short only; Tall and Full are floors, and floors work.

   At the end of the file so they win every tie with the template rules above
   at the same specificity. */
.pmp-site__hero.pmp-site__section--height-short {
  padding-top: calc(var(--st-section-y) * 0.6 * var(--st-section-scale));
  padding-bottom: calc(var(--st-section-y) * 0.6 * var(--st-section-scale));
}

.pmp-site__hero.pmp-site__section--height-short :is(h1, h2),
.pmp-site--bold .pmp-site__hero.pmp-site__section--height-short :is(h1, h2) {
  font-size: var(--st-text-2xl);
}

/* bold sets its hero padding in pixels, outranking the shared rule. */
.pmp-site--bold .pmp-site__hero.pmp-site__section--height-short {
  padding-top: calc(var(--st-section-y) * 0.6 * var(--st-section-scale));
  padding-bottom: calc(var(--st-section-y) * 0.6 * var(--st-section-scale));
}

.pmp-site--clean .pmp-site__hero.pmp-site__section--height-short .pmp-site__hero-visual {
  aspect-ratio: 16 / 9;
  max-height: 220px;
}

.pmp-site--warm .pmp-site__hero.pmp-site__section--height-short .pmp-site__hero-visual {
  width: 160px;
  height: 160px;
}

.pmp-site__hero--fullPhoto.pmp-site__section--height-short {
  /* The variant's own 88vh stage would otherwise beat Short's floor. */
  min-height: clamp(280px, 38vh, 400px);
}
