/*
 * FPS Popups — front-end styles.
 *
 * Written entirely with :where() so that ANY theme rule beats it. This plugin
 * has to live on themes it has never seen; losing a specificity fight is the
 * correct outcome, not a bug.
 *
 * Spacing is FOUR semantic values and nothing else:
 *   --fps-pp-sp-label 6   a label to the thing it labels
 *   --fps-pp-sp-tight 12  a heading to its body copy
 *   --fps-pp-sp-group 24  between semantic groups
 *   --fps-pp-sp-zone  32  across a divider / zone padding
 * A single uniform gap is what makes a card read as a stack of divs rather
 * than a typeset composition.
 *
 * Layout responds to the DIALOG's width via container queries, not the
 * viewport's — a 720px dialog inside an 820px tablet is fine side-by-side, and
 * a media query cannot express that. The container is the shell, never the
 * frame: a container cannot style itself.
 */

.fps-pp {
  --fps-pp-sp-label: 6px;
  --fps-pp-sp-tight: 12px;
  --fps-pp-sp-group: 24px;
  --fps-pp-sp-zone: 32px;

  /* one ramp from one root, so a text-scale control moves all of it */
  --fps-pp-root: 16px;
  --fps-pp-t-fine: calc(var(--fps-pp-root) * 0.75);
  --fps-pp-t-body: calc(var(--fps-pp-root) * 0.9);
  --fps-pp-t-sub: calc(var(--fps-pp-root) * 1.25);
  --fps-pp-t-h: calc(var(--fps-pp-root) * 1.75);

  /* two weight tokens only. Georgia ships 400/700, so 800/900/500 all
     collapse and any hierarchy built on weight vanishes on a serif brand. */
  --fps-pp-w-strong: 700;
  --fps-pp-w-body: 400;
  --fps-pp-track-h: 0;
  --fps-pp-lh-h: 1.15;

  --fps-pp-gutter: 24px;
  --fps-pp-max: 520px;
  --fps-pp-shadow-a: 0.26;
}

/* ── the dialog ─────────────────────────────────────────────────────────
   Reset the UA sheet, but NEVER `width`. The UA sets
   `inset-inline:0; width:fit-content; margin:auto`, and it is fit-content that
   lets margin:auto centre the box. Setting width:auto makes the dialog span the
   whole viewport with the content flush left — an invisible failure that looks
   like a layout bug.

   Also note `color: canvastext; background: canvas` in the UA sheet: those are
   SET values, not inherited, so the theme's colours are discarded unless we
   put them back explicitly. */
dialog:where(.fps-pp) {
  color: var(--fps-pp-ink, #1d2327);
  background: var(--fps-pp-surface, #fff);
  border: none;
  padding: 0;
  width: fit-content;
  max-width: calc(100vw - 2 * var(--fps-pp-gutter));
  border-radius: var(--fps-pp-radius, 6px);
  overflow: hidden;
  font-family: inherit;
  box-shadow:
    0 18px 44px -14px rgba(9, 14, 20, var(--fps-pp-shadow-a)),
    0 4px 14px -6px rgba(9, 14, 20, calc(var(--fps-pp-shadow-a) * 0.5));

  /* dvh, not vh — mobile browser chrome would otherwise crop it. The engine
     overrides --fps-pp-vvh from visualViewport, because dvh does NOT shrink
     when the iOS keyboard opens and the submit button ends up underneath it
     with no way to scroll. */
  margin: auto;
  max-height: calc(var(--fps-pp-vvh, 100dvh) - 2 * var(--fps-pp-gutter));

  /* Optically centred. margin-block:auto splits free space evenly by
     definition, so the bias has to be a transform. */
  transform: translateY(var(--fps-pp-shift, -3vh));
  transition: opacity 0.16s ease, transform 0.16s ease;
}

dialog:where(.fps-pp)[data-tall="1"] { --fps-pp-shift: 0; }

@starting-style {
  dialog:where(.fps-pp)[open] {
    opacity: 0;
    transform: translateY(calc(var(--fps-pp-shift, -3vh) + 14px));
  }
}

dialog:where(.fps-pp).is-closing {
  opacity: 0;
  transform: translateY(calc(var(--fps-pp-shift, -3vh) + 8px));
}

dialog:where(.fps-pp)::backdrop {
  background: rgba(9, 14, 20, 0.5);
  backdrop-filter: blur(10px);
}

/* Reduced motion means REDUCE, not remove — an instant materialisation is more
   startling than a short fade, not less. !important because the host theme's
   motion.css forces animation-duration on *. */
@media (prefers-reduced-motion: reduce) {
  dialog:where(.fps-pp) {
    transition: opacity 0.13s ease !important;
    transform: translateY(var(--fps-pp-shift, -3vh)) !important;
  }
  @starting-style {
    dialog:where(.fps-pp)[open] { opacity: 0; transform: translateY(var(--fps-pp-shift, -3vh)); }
  }
}

/* ── shell + frame ── */
.fps-pp .fps-pp__shell {
  width: min(calc(100vw - 2 * var(--fps-pp-gutter)), var(--fps-pp-max));
  max-height: calc(var(--fps-pp-vvh, 100dvh) - 2 * var(--fps-pp-gutter));
  container-type: inline-size;
  container-name: fpspp;
  display: flex;
  /* The shell carries the theme's content class (Settings → Advanced) so
     rules scoped to it reach the copy inside. Its own box rules must not:
     a 65ch max-width or a gutter on .entry-content would squeeze the dialog. */
  max-width: none;
  margin: 0;
  padding: 0;
}

.fps-pp .fps-pp__frame {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  max-width: none;
  margin: 0;
  max-height: calc(var(--fps-pp-vvh, 100dvh) - 2 * var(--fps-pp-gutter));
  position: relative;
  overflow: hidden;
  isolation: isolate;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fps-pp-ink) 9%, transparent);
}

/* ── zones ──
   The action zone is a SIBLING of the scrolling body, so the CTA can never
   scroll out of view and its top border is a full-bleed divider for free. */
.fps-pp .fps-pp__body {
  padding: var(--fps-pp-sp-zone);
  padding-bottom: var(--fps-pp-sp-tight);
  display: flex;
  flex-direction: column;
  gap: var(--fps-pp-sp-group);
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.fps-pp .fps-pp__action {
  padding: var(--fps-pp-sp-group) var(--fps-pp-sp-zone) var(--fps-pp-sp-zone);
  padding-bottom: calc(var(--fps-pp-sp-zone) + env(safe-area-inset-bottom, 0px));
  flex-shrink: 0;
}

/* Only tint when it actually holds a form. A lone button in a tinted footer
   band reads as an OS dialog rather than a popup. */
.fps-pp .fps-pp__action[data-zone="1"] {
  background: color-mix(in srgb, var(--fps-pp-ink) 3%, var(--fps-pp-surface));
  border-top: 1px solid color-mix(in srgb, var(--fps-pp-ink) 10%, transparent);
}

@container fpspp (max-width: 400px) {
  .fps-pp .fps-pp__body { padding: 24px 24px 12px; gap: 20px; }
  .fps-pp .fps-pp__action { padding: 16px 24px 24px; }
}

/* ── close ──
   Pinned to the frame, not the body, so it survives internal scrolling.
   Offset derived from the padding token rather than a magic number. */
.fps-pp .fps-pp__close {
  position: absolute;
  top: calc(var(--fps-pp-sp-tight) + 2px);
  inset-inline-end: calc(var(--fps-pp-sp-tight) + 2px);
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: color-mix(in srgb, var(--fps-pp-surface) 88%, transparent);
  color: var(--fps-pp-body);
  cursor: pointer;
  z-index: 20;
}
.fps-pp .fps-pp__close svg { width: 18px; height: 18px; }
.fps-pp .fps-pp__close:hover { background: color-mix(in srgb, var(--fps-pp-ink) 10%, var(--fps-pp-surface)); color: var(--fps-pp-ink); }
.fps-pp .fps-pp__close:focus-visible { outline: 3px solid var(--fps-pp-accent); outline-offset: -3px; }

/* ── type ──
   Measure caps in ch, because the unit is font-relative and therefore
   self-corrects for a typeface we have never seen. Tracking defaults to 0:
   negative tracking is a large-size grotesque correction and actively damages
   a serif face. */
.fps-pp .fps-pp__eyebrow {
  font-size: var(--fps-pp-t-fine);
  font-weight: var(--fps-pp-w-strong);
  color: var(--fps-pp-accent);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.fps-pp .fps-pp__h {
  font-size: var(--fps-pp-t-h);
  line-height: var(--fps-pp-lh-h);
  font-weight: var(--fps-pp-w-strong);
  color: var(--fps-pp-ink);
  letter-spacing: var(--fps-pp-track-h);
  text-wrap: balance;
  max-width: 24ch;
  margin: 0;
}

.fps-pp .fps-pp__p {
  font-size: var(--fps-pp-t-body);
  line-height: 1.62;
  color: var(--fps-pp-body);
  font-weight: var(--fps-pp-w-body);
  max-width: 52ch;
  text-wrap: pretty;
  margin: 0;
}
/* The body can hold a few paragraphs and a short list (the editor's
   formatting). Spacing stays on the plugin's own scale. */
.fps-pp .fps-pp__p > * { margin: 0; }
.fps-pp .fps-pp__p > * + * { margin-top: var(--fps-pp-sp-label); }
.fps-pp .fps-pp__p ul,
.fps-pp .fps-pp__p ol { padding-left: 1.25em; }
.fps-pp .fps-pp__p li + li { margin-top: 2px; }
.fps-pp .fps-pp__p a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.fps-pp .fps-pp__p strong, .fps-pp .fps-pp__p b { font-weight: var(--fps-pp-w-strong); }

.fps-pp .fps-pp__fine {
  font-size: var(--fps-pp-t-fine);
  line-height: 1.55;
  color: var(--fps-pp-muted-ink);
  max-width: 44ch;
  margin: var(--fps-pp-sp-tight) 0 0;
}

/* eyebrow → heading is a label pair, so it uses the label gap, not the group gap */
.fps-pp .fps-pp__lede { display: flex; flex-direction: column; gap: var(--fps-pp-sp-tight); }
.fps-pp .fps-pp__lede .fps-pp__eyebrow + .fps-pp__h { margin-top: calc(var(--fps-pp-sp-label) - var(--fps-pp-sp-tight)); }

.fps-pp .fps-pp__badge {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  flex-shrink: 0;
  background: color-mix(in srgb, var(--fps-pp-accent) 13%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fps-pp-accent) 30%, transparent);
}

/* Full width, but flex: a theme button with a fixed height and no vertical
   padding (BidCopilot's .btn) centres its label with flex, and display:block
   left the text stuck to the top edge. */
.fps-pp .fps-pp__cta { display: flex; align-items: center; justify-content: center; text-align: center; }

/* The plugin's own button, used only when the theme has no button class to
   borrow (Settings → Brand & Type). Accent fill, text colour picked for
   contrast against it, corner radius from the brand. */
.fps-pp .fps-pp-btn {
  display: block;
  padding: 12px 20px;
  border: 1px solid var(--fps-pp-accent);
  border-radius: var(--fps-pp-radius, 6px);
  background: var(--fps-pp-accent);
  color: var(--fps-pp-on-accent, #fff);
  font: inherit;
  font-weight: var(--fps-pp-w-strong);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}
.fps-pp .fps-pp-btn:hover { filter: brightness(0.93); }
/* A rescued form button keeps the form's own layout, not full width. */
.fps-pp form .fps-pp-btn { display: inline-block; width: auto; min-height: 44px; }
.fps-pp .fps-pp-btn:focus-visible { outline: 2px solid var(--fps-pp-accent); outline-offset: 3px; }
.fps-pp .fps-pp__form { max-width: 100%; }

/* The decline control is the only dismissal in the thumb zone, so it renders
   on coarse pointers rather than at a screen width. */
.fps-pp .fps-pp__decline { display: none; }
@media (pointer: coarse) {
  .fps-pp .fps-pp__decline {
    display: block;
    width: 100%;
    min-height: 44px;
    padding: 13px 0;
    margin-top: var(--fps-pp-sp-tight);
    background: none;
    border: none;
    color: var(--fps-pp-muted-ink);
    font: inherit;
    font-size: var(--fps-pp-t-body);
    font-weight: var(--fps-pp-w-strong);
    cursor: pointer;
  }
  .fps-pp .fps-pp__decline:hover { color: var(--fps-pp-ink); }
}

.fps-pp .fps-pp__live {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── centred variants (the gate, two-step, call or book) ── */
.fps-pp.is-centred .fps-pp__body { align-items: center; text-align: center; }
.fps-pp.is-centred .fps-pp__action { text-align: center; }
.fps-pp.is-centred .fps-pp__h,
.fps-pp.is-centred .fps-pp__p,
.fps-pp.is-centred .fps-pp__fine { margin-inline: auto; }
.fps-pp.is-centred .fps-pp__lede { align-items: center; }

/* Numerals: lining and tabular, always. Georgia ships OLD-STYLE figures by
   default, so a countdown's "04" renders with a short zero next to a
   full-height four and jumps around inside its cell. */
.fps-pp [data-fps-num] { font-variant-numeric: lining-nums tabular-nums; }

/* ── short viewport (landscape phone) ──
   Measured: a one-field Gravity Forms block is ~264px of a 358px frame, which
   leaves under 100px of pitch. Tighten the zones and drop decoration. */
/* :root:not(.fps-pv) — the admin's scaled preview is a short frame standing
   in for a full desktop window, so it must not take the short-viewport path. */
@media (max-height: 470px) {
  :root:not(.fps-pv) .fps-pp {
    --fps-pp-sp-zone: 18px;
    --fps-pp-sp-group: 14px;
    --fps-pp-sp-tight: 8px;
    --fps-pp-shift: 0;
  }
  :root:not(.fps-pv) .fps-pp .fps-pp__eyebrow,
  :root:not(.fps-pv) .fps-pp .fps-pp__badge { display: none; }
}

/* ── scroll lock ──
   A class on <html>, reference-counted by the engine — never an inline style
   on <body>. The host theme's mobile drawer sets body.style.overflow = "" when
   it closes, which would unlock the page while a dialog is still open. */
html.fps-pp-locked { overflow: hidden; }
html { scrollbar-gutter: stable; }

/* ═══════════════════════════════════════════════════════════════════════
   ENGINES, SLOTS AND COMPONENTS
   ═══════════════════════════════════════════════════════════════════════ */

/* ── media ──
   Declared ratio per placement. Content must never define the image's shape,
   or the same photo crops differently on every template and after every copy
   edit. Directional light and grain sit over the fill so a generated surface
   reads as a photograph; `.is-photo` opts a real image out, because those
   effects fight a photo's own lighting. */
.fps-pp .fps-pp__media,
.fps-pp .fps-pp__bg {
  background-color: color-mix(in srgb, var(--fps-pp-ink) 8%, var(--fps-pp-surface));
  background-size: cover;
  background-image: var(--fps-pp-img, none);
  background-position: var(--fps-pp-focal, 50% 40%);
  background-repeat: no-repeat;
  position: relative;
  flex-shrink: 0;
}

.fps-pp .fps-pp__media::before,
.fps-pp .fps-pp__bg::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(118% 88% at 20% 8%, rgba(255, 255, 255, 0.2), transparent 58%),
    radial-gradient(96% 78% at 90% 94%, rgba(0, 0, 0, 0.36), transparent 56%);
  pointer-events: none;
}

.fps-pp .fps-pp__media::after {
  content: "";
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
  pointer-events: none;
}

/* top banner: a ratio capped in absolute terms, because a fixed 168px eats a
   quarter of a phone screen before any content */
.fps-pp .fps-pp__frame[data-slot="top"] .fps-pp__media {
  width: 100%;
  aspect-ratio: 16 / 6;
  max-height: 148px;
  min-height: 92px;
}

.fps-pp .fps-pp__col { display: flex; flex-direction: column; flex: 1; min-height: 0; }

/* Side-by-side engages on the DIALOG's width, not the viewport's. A 720px
   dialog inside an 820px tablet is fine side-by-side, and a media query cannot
   express that. */
@container fpspp (min-width: 560px) {
  .fps-pp .fps-pp__frame[data-engine="split"][data-slot^="side"] { flex-direction: row; }
  .fps-pp .fps-pp__frame[data-engine="split"][data-slot="side-end"] { flex-direction: row-reverse; }
  .fps-pp .fps-pp__frame[data-engine="split"][data-slot^="side"] .fps-pp__media {
    width: 46%;
    align-self: stretch;
    aspect-ratio: auto;
    max-height: none;
  }
  .fps-pp .fps-pp__frame[data-engine="split"] .fps-pp__body { padding-top: calc(var(--fps-pp-sp-zone) * 1.25); }
  .fps-pp .fps-pp__frame[data-engine="split"] .fps-pp__h { max-width: 30ch; }
}

/* Below that width a side placement becomes a top banner, automatically —
   the author sets it once and it does the right thing on a phone. */
@container fpspp (max-width: 559.9px) {
  .fps-pp .fps-pp__frame[data-slot^="side"] .fps-pp__media {
    width: 100%;
    aspect-ratio: 16 / 6;
    max-height: 148px;
    align-self: auto;
  }
}

/* Whole image (Look → "Show the whole image"): the panel takes the
   picture's own shape (--fps-pp-ratio), so a graphic with words or a subject
   near its edges is not cropped. The pop-up grows to fit it; only copy taller
   than the picture still crops it. Photos are better left to the focal
   point. */
.fps-pp .fps-pp__frame[data-slot] .fps-pp__media.is-whole {
  aspect-ratio: var(--fps-pp-ratio, 16 / 6);
  background-position: 50% 50%;
  min-height: 0;
}
@container fpspp (max-width: 559.9px) {
  .fps-pp .fps-pp__frame[data-slot] .fps-pp__media.is-whole:not(.has-mobile) {
    width: 100%;
    max-height: 40vh;
  }
}

/* Phone image. When the author added one, a phone-sized pop-up shows it
   instead, whole and at its own shape (--fps-pp-ratio-m), on top of the
   content: it was made for this width, so nothing is cropped or recentred.
   Capped at 40% of the screen height so the copy stays in view. Only the
   image that applies is downloaded. */
@container fpspp (max-width: 559.9px) {
  .fps-pp .fps-pp__frame .fps-pp__media.has-mobile,
  .fps-pp .fps-pp__frame .fps-pp__bg.has-mobile {
    background-image: var(--fps-pp-img-m);
    background-position: 50% 50%;
  }
  /* [data-slot] for specificity: it has to beat the banner rules above. */
  .fps-pp .fps-pp__frame[data-slot] .fps-pp__media.has-mobile {
    order: -1;
    width: 100%;
    aspect-ratio: var(--fps-pp-ratio-m, 16 / 6);
    min-height: 0;
    max-height: 40vh;
    align-self: auto;
  }
}

/* portrait: a cover or product mockup as the panel, never a floating box
   alongside a full-bleed one */
@container fpspp (min-width: 560px) {
  .fps-pp .fps-pp__frame[data-slot="portrait"] .fps-pp__media {
    width: 40%;
    align-self: stretch;
    aspect-ratio: auto;
  }
  .fps-pp .fps-pp__frame[data-slot="portrait"] { flex-direction: row; }
}

/* ── backdrop ──
   ONE scrim, steeply curved, dark only where the type sits. A flat wash across
   the whole image dulls the photograph you chose it for. */
.fps-pp .fps-pp__frame[data-engine="backdrop"] { min-height: 420px; }
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__bg { position: absolute; inset: 0; z-index: 0; }
.fps-pp .fps-pp__scrim {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(
    to top,
    rgba(8, 12, 18, 0.82) 0%,
    rgba(8, 12, 18, 0.62) 30%,
    rgba(8, 12, 18, 0.1) 58%,
    transparent 100%
  );
}
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__col { position: relative; z-index: 1; justify-content: flex-end; }
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__body { flex: 0 1 auto; }
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__h { color: #fff; }
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__p,
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__fine { color: rgba(255, 255, 255, 0.9); }
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__eyebrow { color: rgba(255, 255, 255, 0.78); }
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__action { background: transparent; border-top: none; padding-top: 0; }
/* The CTA must never be the accent over accent-derived media, or the primary
   action disappears into the background. */
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__cta { background: #fff; color: #111; border-color: #fff; }
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__close { background: rgba(0, 0, 0, 0.5); color: #fff; }

/* ── staged ── */
.fps-pp .fps-pp__step { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.fps-pp .fps-pp__step[hidden] { display: none; }

/* ── surface art ──
   Every treatment derives from the accent, so there is no author-chosen colour
   and nothing can go off-brand. Scoped to the pitch zone so it can never
   stripe across a form, and so the divider stays a real boundary. */
.fps-pp .fps-pp__body { position: relative; }
.fps-pp .fps-pp__body > * { position: relative; z-index: 1; }

.fps-pp .fps-pp__frame[data-art]:not([data-art="none"]) .fps-pp__body::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.fps-pp .fps-pp__frame[data-art="dots"] .fps-pp__body::before {
  background-image: radial-gradient(circle, color-mix(in srgb, var(--fps-pp-accent) 62%, transparent) 1.4px, transparent 1.5px);
  background-size: 15px 15px;
  -webkit-mask-image: radial-gradient(115% 88% at 100% 0%, #000 0%, transparent 62%);
  mask-image: radial-gradient(115% 88% at 100% 0%, #000 0%, transparent 62%);
}

.fps-pp .fps-pp__frame[data-art="hatch"] .fps-pp__body::before {
  background-image: repeating-linear-gradient(48deg, color-mix(in srgb, var(--fps-pp-accent) 42%, transparent) 0 1px, transparent 1px 10px);
  -webkit-mask-image: radial-gradient(100% 80% at 0% 100%, #000 0%, transparent 66%);
  mask-image: radial-gradient(100% 80% at 0% 100%, #000 0%, transparent 66%);
}

.fps-pp .fps-pp__frame[data-art="arcs"] .fps-pp__body::before {
  background-image: repeating-radial-gradient(
    circle at 100% 0%,
    transparent 0 30px,
    color-mix(in srgb, var(--fps-pp-accent) 26%, transparent) 30px 31px
  );
  -webkit-mask-image: radial-gradient(circle at 100% 0%, #000 0%, transparent 72%);
  mask-image: radial-gradient(circle at 100% 0%, #000 0%, transparent 72%);
}

.fps-pp .fps-pp__frame[data-art="bloom"] .fps-pp__body::before {
  background: radial-gradient(
    circle at 100% 0%,
    color-mix(in srgb, var(--fps-pp-accent) 14%, transparent),
    transparent 60%
  );
}

/* ── badge + mark ── */
.fps-pp .fps-pp__badge {
  display: flex;
  align-items: center;
  justify-content: center;
}
.fps-pp .fps-pp__badge .fps-pp__icon { width: 26px; height: 26px; color: var(--fps-pp-accent); }

/* A client's own mark sits bare and smaller — a logo inside a tinted circle
   reads as an app icon in a bubble. */
.fps-pp .fps-pp__mark {
  width: 46px;
  height: 46px;
  flex-shrink: 0;
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
}

/* ── countdown ──
   Contained cells with ink numerals on a faint accent tint. Solid near-black
   tiles out-weigh both the heading and the CTA, which inverts the hierarchy;
   an inline whisper reads as insignificant. This is the middle. */
.fps-pp .fps-pp__cd {
  display: flex;
  align-self: stretch;
  align-items: stretch;
  border: 1px solid color-mix(in srgb, var(--fps-pp-ink) 14%, transparent);
  border-radius: calc(var(--fps-pp-radius, 6px) * 0.7);
  overflow: hidden;
  background: color-mix(in srgb, var(--fps-pp-accent) 7%, var(--fps-pp-surface));
  font-variant-numeric: lining-nums tabular-nums;
}
.fps-pp .fps-pp__cd > div {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 8px 6px 7px;
}
.fps-pp .fps-pp__cd b {
  font-size: var(--fps-pp-t-sub);
  font-weight: var(--fps-pp-w-strong);
  color: var(--fps-pp-ink);
  line-height: 1;
  letter-spacing: var(--fps-pp-track-h);
}
.fps-pp .fps-pp__cd span {
  font-size: calc(var(--fps-pp-t-fine) * 0.8);
  color: var(--fps-pp-muted-ink);
  font-weight: var(--fps-pp-w-strong);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  margin-top: 4px;
}
.fps-pp .fps-pp__cd i { width: 1px; background: color-mix(in srgb, var(--fps-pp-ink) 12%, transparent); }

.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__cd {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.3);
}
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__cd b { color: #fff; }
.fps-pp .fps-pp__frame[data-engine="backdrop"] .fps-pp__cd span { color: rgba(255, 255, 255, 0.75); }

/* ── voucher ──
   The offer value is the hero, so it gets a bounded zone with a torn lower edge
   rather than being large accent type floating on white. */
.fps-pp .fps-pp__voucher {
  align-self: stretch;
  text-align: center;
  padding: 22px 20px 20px;
  background: color-mix(in srgb, var(--fps-pp-accent) 8%, var(--fps-pp-surface));
  border: 1px solid color-mix(in srgb, var(--fps-pp-accent) 24%, transparent);
  border-radius: calc(var(--fps-pp-radius, 6px) * 0.9);
  display: flex;
  flex-direction: column;
  gap: var(--fps-pp-sp-label);
  position: relative;
}
.fps-pp .fps-pp__voucher::after {
  content: "";
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 0;
  border-bottom: 2px dashed color-mix(in srgb, var(--fps-pp-accent) 30%, transparent);
}
.fps-pp .fps-pp__offer {
  font-size: calc(var(--fps-pp-root) * 3.5);
  font-weight: var(--fps-pp-w-strong);
  line-height: 0.94;
  color: var(--fps-pp-accent);
  letter-spacing: var(--fps-pp-track-h);
}
.fps-pp .fps-pp__offer.is-long { font-size: calc(var(--fps-pp-root) * 1.9); line-height: 1.05; }
.fps-pp .fps-pp__terms {
  font-size: var(--fps-pp-t-fine);
  color: var(--fps-pp-muted-ink);
  font-weight: var(--fps-pp-w-strong);
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

/* ── coupon code ──
   Monospaced so O/0 and I/1 are told apart; a dashed edge so it reads as the
   thing to take. Gift mode prints it in the body; with a form it arrives inside
   Gravity Forms' confirmation. */
.fps-pp .fps-pp__code {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--fps-pp-sp-tight);
  margin-top: var(--fps-pp-sp-tight);
  padding: 8px 8px 8px 14px;
  border: 2px dashed color-mix(in srgb, var(--fps-pp-accent) 45%, transparent);
  border-radius: var(--fps-pp-radius, 6px);
  background: var(--fps-pp-surface);
}
.fps-pp .fps-pp__code-value {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--fps-pp-t-sub);
  font-weight: var(--fps-pp-w-strong);
  color: var(--fps-pp-ink);
  letter-spacing: 0.06em;
  overflow-wrap: anywhere;
}
.fps-pp .fps-pp__copy {
  flex: none;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--fps-pp-accent);
  border-radius: var(--fps-pp-radius, 6px);
  background: transparent;
  color: var(--fps-pp-ink);
  font: inherit;
  font-size: var(--fps-pp-t-body);
  font-weight: var(--fps-pp-w-strong);
  cursor: pointer;
}
.fps-pp .fps-pp__copy:focus-visible { outline: 2px solid var(--fps-pp-accent); outline-offset: 2px; }

@container fpspp (max-width: 400px) {
  .fps-pp .fps-pp__offer { font-size: calc(var(--fps-pp-root) * 2.75); }
  .fps-pp .fps-pp__offer.is-long { font-size: calc(var(--fps-pp-root) * 1.55); }
}

/* ── quote ── */
.fps-pp .fps-pp__rating { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.fps-pp .fps-pp__stars { display: inline-flex; gap: 3px; color: var(--fps-pp-stars, #9a6f00); }
.fps-pp .fps-pp__stars .fps-pp__icon { width: 22px; height: 22px; }
.fps-pp .fps-pp__score {
  font-size: var(--fps-pp-t-sub);
  font-weight: var(--fps-pp-w-strong);
  color: var(--fps-pp-ink);
  letter-spacing: var(--fps-pp-track-h);
}
.fps-pp .fps-pp__source {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--fps-pp-t-fine);
  color: var(--fps-pp-muted-ink);
}
.fps-pp .fps-pp__source .fps-pp__icon { width: 15px; height: 15px; }
.fps-pp .fps-pp__quote {
  font-size: var(--fps-pp-t-sub);
  line-height: calc(var(--fps-pp-lh-h) + 0.28);
  color: var(--fps-pp-ink);
  font-weight: var(--fps-pp-w-body);
  max-width: 32ch;
  text-wrap: pretty;
  margin: 0;
}
.fps-pp .fps-pp__rule {
  width: 44px;
  height: 1px;
  background: color-mix(in srgb, var(--fps-pp-ink) 12%, transparent);
  border: none;
  margin: 0;
}
.fps-pp .fps-pp__attr {
  font-size: var(--fps-pp-t-fine);
  color: var(--fps-pp-muted-ink);
  font-weight: var(--fps-pp-w-strong);
  margin: 0;
}
.fps-pp .fps-pp__proof { font-size: var(--fps-pp-t-fine); color: var(--fps-pp-muted-ink); margin: 0; }

/* ── two equal choices ──
   Equal weight is a requirement on a legal gate, not a style preference: a
   visually dominant affirmative is the nudge regulators object to. */
.fps-pp .fps-pp__two { display: flex; gap: var(--fps-pp-sp-tight); }
.fps-pp .fps-pp__two > * { flex: 1; }
@container fpspp (max-width: 380px) {
  .fps-pp .fps-pp__two { flex-direction: column; }
}
.fps-pp .fps-pp__choice {
  min-height: 52px;
  padding: 14px 18px;
  border-radius: calc(var(--fps-pp-radius, 6px) * 0.8);
  border: 1.5px solid color-mix(in srgb, var(--fps-pp-ink) 24%, transparent);
  background: var(--fps-pp-surface);
  color: var(--fps-pp-ink);
  font: inherit;
  font-size: var(--fps-pp-t-body);
  font-weight: var(--fps-pp-w-strong);
  cursor: pointer;
}
.fps-pp .fps-pp__choice:hover {
  border-color: var(--fps-pp-accent);
  background: color-mix(in srgb, var(--fps-pp-accent) 7%, var(--fps-pp-surface));
}
.fps-pp .fps-pp__choice:focus-visible { outline: 3px solid var(--fps-pp-accent); outline-offset: 2px; }

/* ── custom HTML ──
   Author markup gets the zone padding and nothing else. Its own <style> block
   was scoped to this campaign on save, so it cannot leak out. */
.fps-pp .fps-pp__body--custom { display: block; }

/* ── success ── */
.fps-pp .gform_confirmation_message { font-size: var(--fps-pp-t-body); line-height: 1.6; color: var(--fps-pp-body); }

/* The plugin styles NO form controls. The host theme owns them — on
   fps-base-theme-01 with `!important` throughout, which cannot be overridden
   from here and shouldn't be: it means a form inside the popup looks identical
   to a form anywhere else on the site, for free, on every client. Containment
   only. */
.fps-pp .fps-pp__form { max-width: 100%; }
.fps-pp .fps-pp__form .gform_wrapper { margin: 0; }
