/**
 * Business Promo Badge — public widget  @version 1.0.0
 *
 * One fixed `.pm-root` bottom-RIGHT at z-index 420 — in the 400–450 floating-feature
 * budget, above the FAB (200) and sticky headers (100), below the toast (1000) and skip
 * link (2000). Bottom-LEFT is the ice cream truck (`.ic-root`, z-410) and both can be
 * live at once, so the two never share a corner. The root opens its own stacking context
 * (`isolation:isolate`) so the card's z-20 stays inside the band instead of leaking out.
 * Nothing joins page layout: fixed throughout, only transform/opacity animate.
 *
 * Every colour lives here, keyed off `data-pm-theme`, which the JS sets from a validated
 * token NAME. A hex string never travels over the wire, so an operator's text box can
 * neither fail contrast nor inject CSS. Two theme blocks — `default` (site tokens) and
 * `roots` (Quadra Roots, matching ads/quadra-roots-ad.html) — each with a dark variant.
 * Dark mode arrives two ways on this site, so every themed selector is paired.
 */

/* ---- anchor + geometry ---------------------------------------------------- */
.pm-root{
  /* --pm-lift clears .quick-actions-mobile, whose real measured height is 94px
     (0.75rem padding + a 3-button grid), not the ~80px it looks like. 110px =
     94px bar + a 16px gutter. Nothing hangs BELOW the badge box — the label sits
     INSIDE the pill deliberately, because the ice cream truck's "Click me" label
     hangs below its launcher and forced --ic-lift from 96px to 137px to keep it
     off the bar. If anything is ever added under this pill, this number must grow
     by that element's height or it slides under the bar. Measured, not estimated.
     Two knobs rather than one, because the desktop offset is a different problem
     (no bar at all, just a gutter): a page with no bottom bar sets --pm-lift,
     a page that wants the badge higher on wide screens sets --pm-lift-lg. */
  position:fixed;right:16px;bottom:calc(var(--pm-lift,110px) + env(safe-area-inset-bottom,0px));
  z-index:420;isolation:isolate;pointer-events:none;line-height:1.45;
  font-family:var(--font-body,-apple-system,system-ui,sans-serif)
}
.pm-root.pm-hide{display:none}
.pm-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip-path:inset(50%)}
/* >768px: the quick-actions bar is display:none, so only a gutter is needed. */
@media (min-width:769px){
  .pm-root{right:24px;bottom:calc(var(--pm-lift-lg,24px) + env(safe-area-inset-bottom,0px))}
  .pm-card{max-height:min(520px,calc(100vh - 120px))}
}

/* ---- theme: default (consumes the site tokens) ---------------------------- */
/* The pill is NOT plain --primary-blue: white on #1a73e8 measures 4.50:1, which
   clears the floor by 0.004 and would fail the moment anyone nudged the token.
   The gradient runs 5.6:1 → 7.8:1 against white instead. Same reasoning as
   .ic-go's deep strawberry. --primary-blue is still consumed, for the focus ring. */
.pm-root[data-pm-theme="default"]{
  --pm-pill:linear-gradient(135deg,#1765cc,#0f4fa8);--pm-pill-fg:#fff;
  --pm-pill-bd:rgba(255,255,255,.35);
  --pm-bg:var(--card-bg,#fff);--pm-fg:var(--text-primary,#1a1a1a);
  --pm-fg2:var(--text-secondary,#4a4a4a);--pm-line:var(--border-color,#edf2f7);
  --pm-accent:#0f4fa8;--pm-chip:#eef4fd;--pm-chip-fg:#0f4fa8;
  --pm-cta:linear-gradient(135deg,#1765cc,#0f4fa8);--pm-cta-fg:#fff;
  --pm-band:linear-gradient(90deg,#0f4fa8,#1765cc 55%,#4a9eff);
  --pm-display:var(--font-heading,system-ui,sans-serif);
  --pm-shade:rgba(15,23,42,.13);--pm-glow:rgba(15,23,42,.22)
}
/* The dark block sets its surface and ink EXPLICITLY rather than inheriting the
   site tokens, and that is deliberate — css/line-report.css carries the same note
   and the same scar. styles.css redefines --card-bg / --text-primary only under
   [data-theme="dark"]; it has no body.dark-mode block at all, and
   business-styles.css's body.dark-mode block flips --text-primary but NOT
   --card-bg. So a dark block that leaned on var(--card-bg,#2d3748) rendered a
   light card under the second mechanism while --pm-accent stayed dark-mode blue:
   measured 1.85:1 for the headline and 1.93:1 for the "Ends in" chip before these
   four literals existed. The light block above still consumes the tokens, which is
   where consuming them is safe. */
[data-theme="dark"] .pm-root[data-pm-theme="default"],
body.dark-mode .pm-root[data-pm-theme="default"]{
  --pm-pill:linear-gradient(135deg,#4a9eff,#2f7fe0);--pm-pill-fg:#08131f;
  --pm-pill-bd:rgba(8,19,31,.28);
  --pm-bg:#2d3748;--pm-fg:#f7fafc;--pm-fg2:#e2e8f0;--pm-line:#3d4a5c;
  --pm-accent:#8fc2ff;--pm-chip:#22314a;--pm-chip-fg:#cfe3ff;
  --pm-cta:linear-gradient(135deg,#4a9eff,#2f7fe0);--pm-cta-fg:#08131f;
  --pm-band:linear-gradient(90deg,#2f7fe0,#4a9eff 55%,#8fc2ff);
  --pm-shade:rgba(0,0,0,.35);--pm-glow:rgba(0,0,0,.5)
}

/* ---- theme: roots (Quadra Roots) ------------------------------------------ */
/* Palette from ads/quadra-roots-ad.html. One deliberate departure: that ad's
   --muted #8A8070 measures 3.60:1 on the #FBF7DD cream, which is fine for the
   9.5px all-caps eyebrow it is used for there and NOT fine for body copy, so the
   secondary text here is deepened to #6E6555 (5.3:1). Playfair Display is the
   ad's display face; loading a webfont for a corner badge is not worth the
   request, so --pm-display is a system serif stack with the same flavour. */
.pm-root[data-pm-theme="roots"]{
  --pm-pill:linear-gradient(135deg,#7A2148,#6A1B3D);--pm-pill-fg:#FEFCEF;
  --pm-pill-bd:rgba(254,252,239,.34);
  --pm-bg:#FBF7DD;--pm-fg:#46403F;--pm-fg2:#6E6555;--pm-line:#E8E0BC;
  --pm-accent:#6A1B3D;--pm-chip:#F4EFCE;--pm-chip-fg:#4A0F28;
  --pm-cta:linear-gradient(135deg,#7A2148,#6A1B3D);--pm-cta-fg:#FEFCEF;
  --pm-band:linear-gradient(90deg,#5F7A1E,#8CBF3F 45%,#6A1B3D);
  --pm-display:Georgia,"Iowan Old Style","Palatino Linotype","Times New Roman",serif;
  --pm-shade:rgba(74,15,40,.14);--pm-glow:rgba(74,15,40,.26)
}
/* Not an inversion: cream is a light-only ground, so dark mode moves to a deep
   plum-brown (#2A1720) lifted from the brand's own --plum-deep rather than a
   greyed-out cream, and the pill flips to the olive accent so it still reads as
   the same business at a glance. Body text 14.6:1, secondary 10.7:1. */
[data-theme="dark"] .pm-root[data-pm-theme="roots"],
body.dark-mode .pm-root[data-pm-theme="roots"]{
  --pm-pill:linear-gradient(135deg,#A8CF62,#8CBF3F);--pm-pill-fg:#4A0F28;
  --pm-pill-bd:rgba(74,15,40,.3);
  --pm-bg:#2A1720;--pm-fg:#F4EFCE;--pm-fg2:#D9CEA8;--pm-line:#462934;
  --pm-accent:#A8CF62;--pm-chip:#3A2130;--pm-chip-fg:#E8DCB4;
  --pm-cta:linear-gradient(135deg,#A8CF62,#8CBF3F);--pm-cta-fg:#4A0F28;
  --pm-band:linear-gradient(90deg,#6A1B3D,#5F7A1E 45%,#A8CF62);
  --pm-shade:rgba(0,0,0,.4);--pm-glow:rgba(0,0,0,.55)
}

/* ---- launcher pill -------------------------------------------------------- */
/* Text INSIDE the pill, which is what keeps --pm-lift at 110px. min-height 44px
   is the touch-target floor; the padding alone would give ~30px. */
.pm-btn{
  position:relative;display:inline-flex;align-items:center;gap:.4rem;
  min-height:44px;padding:.5rem .95rem;cursor:pointer;pointer-events:auto;
  white-space:nowrap;border-radius:9999px;border:1.5px solid var(--pm-pill-bd);
  background:var(--pm-pill);color:var(--pm-pill-fg);
  font:800 .9rem/1 var(--font-body,-apple-system,system-ui,sans-serif);
  letter-spacing:.01em;
  box-shadow:0 6px 18px var(--pm-glow),0 1px 3px rgba(15,23,42,.16);
  transition:transform .12s ease,box-shadow .15s ease
}
.pm-btn:active{transform:scale(.96)}
/* The glyph is theme decoration, so it lives in CSS `content` rather than in the
   JS template — same reasoning as .ic-cta::after. An empty <i> degrades to no
   glyph at all if this sheet fails to load, which is the mild failure mode. */
.pm-ico{display:inline-block;font-size:1.05rem;line-height:1;font-style:normal}
.pm-root[data-pm-theme="default"] .pm-ico::after{content:"\1F3F7"}
.pm-root[data-pm-theme="roots"] .pm-ico::after{content:"\1F331"}
/* Halo: a sibling, so it can exceed the pill without clipping it. */
.pm-ring{position:absolute;inset:-4px;border:3px solid currentColor;border-radius:9999px;
  color:var(--pm-accent);opacity:0;pointer-events:none}

/* ---- arrival / idle / exit ------------------------------------------------ */
.pm-arriving .pm-btn{animation:pm-slide .55s cubic-bezier(.22,.9,.3,1) both;will-change:transform}
.pm-arriving .pm-ring{animation:pm-ring .8s ease-out .45s forwards}
/* Only the small glyph breathes once parked, never the pill: a commuter reading
   a departure table must be able to ignore this thing completely. */
.pm-idle .pm-ico{animation:pm-breathe 3.6s ease-in-out infinite}
.pm-leaving .pm-btn{animation:pm-leave .4s cubic-bezier(.5,0,.9,.4) both}
@keyframes pm-slide{0%{opacity:0;transform:translate3d(30px,0,0) scale(.94)}
  20%{opacity:1}62%{transform:translate3d(-4px,0,0) scale(1.01)}100%{opacity:1;transform:none}}
@keyframes pm-ring{0%{opacity:.55;transform:scale(.9)}100%{opacity:0;transform:scale(1.22)}}
@keyframes pm-breathe{0%,100%{transform:translate3d(0,0,0)}50%{transform:translate3d(0,-2px,0)}}
@keyframes pm-leave{0%{opacity:1}100%{opacity:0;transform:translate3d(36px,0,0)}}
@keyframes pm-in{from{opacity:0}to{opacity:1}}

/* ---- card: absolute in .pm-root, flows UPWARD and LEFT off the one anchor -- */
.pm-card{
  position:absolute;right:0;bottom:calc(100% + 12px);z-index:20;overflow:hidden;
  width:min(320px,calc(100vw - 32px));display:flex;flex-direction:column;
  max-height:min(480px,calc(100vh - 200px));max-height:min(480px,calc(100dvh - 200px));
  pointer-events:auto;transform-origin:100% 100%;
  background:var(--pm-bg);color:var(--pm-fg);
  border:1px solid var(--pm-line);border-radius:1rem;box-sizing:border-box;
  box-shadow:0 18px 40px -12px var(--pm-glow),0 4px 12px rgba(15,23,42,.14)
}
.pm-card[hidden],.pm-card [hidden]{display:none}
.pm-card.pm-open{animation:pm-pop .26s cubic-bezier(.34,1.28,.64,1) both}
.pm-card.pm-closing{animation:pm-drop .17s ease-in both}
@keyframes pm-pop{0%{opacity:0;transform:translate3d(0,10px,0) scale(.93)}100%{opacity:1;transform:none}}
@keyframes pm-drop{0%{opacity:1}100%{opacity:0;transform:translate3d(0,6px,0) scale(.96)}}

.pm-band{flex:none;height:6px;background:var(--pm-band)}
.pm-hd{flex:none;display:flex;align-items:flex-start;gap:.5rem;padding:.7rem .7rem .35rem 1rem}
.pm-ttl{flex:1;margin:0;padding-top:8px;color:var(--pm-fg);
  font:600 1.05rem/1.2 var(--pm-display,var(--font-heading,system-ui,sans-serif))}
.pm-x{
  flex:none;width:44px;height:44px;margin:-4px -4px 0 0;padding:0;cursor:pointer;
  display:flex;align-items:center;justify-content:center;background:none;border:0;
  border-radius:50%;color:var(--pm-fg2);font-size:26px;line-height:1
}
.pm-bd{flex:1 1 auto;overflow-y:auto;padding:0 1rem .75rem}
.pm-bd > p{margin:0 0 .5rem}
.pm-ttl,.pm-head,.pm-detail,.pm-addr,.pm-codev{overflow-wrap:anywhere}
/* The offer itself — the one line somebody actually came for. */
.pm-head{font-size:1.15rem;font-weight:800;color:var(--pm-accent)}
.pm-detail{color:var(--pm-fg2);font-size:.92rem}
.pm-code{display:flex;align-items:center;gap:.45rem;font-size:.8rem;
  font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--pm-fg2)}
/* user-select:all so one tap selects the whole code — people retype these wrong. */
.pm-codev{padding:.28rem .55rem;border-radius:.375rem;user-select:all;
  background:var(--pm-chip);color:var(--pm-chip-fg);letter-spacing:.1em;
  font:700 .95rem/1.3 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.pm-ends{padding:.35rem .6rem;border-radius:.375rem;background:var(--pm-line);
  color:var(--pm-fg);font-size:.85rem;font-weight:700}
.pm-addr,.pm-over{color:var(--pm-fg2);font-size:.9rem}
.pm-over{font-weight:600;color:var(--pm-fg)}

/* ---- CTA ------------------------------------------------------------------ */
.pm-ft{flex:none;padding:.4rem 1rem 1rem}
.pm-cta{
  display:flex;align-items:center;justify-content:center;gap:.5rem;
  min-height:48px;padding:.6rem 1rem;font-size:1rem;font-weight:700;
  color:var(--pm-cta-fg);text-decoration:none;border-radius:.75rem;
  background:var(--pm-cta);box-shadow:0 4px 12px var(--pm-shade)
}
/* Chevron drawn from borders, not a glyph: an empty `content` is announced by
   nothing, whereas "→" gets read aloud as "rightwards arrow" by some readers. */
.pm-cta::after{content:"";width:.4em;height:.4em;margin-left:-.1em;
  border-top:2px solid currentColor;border-right:2px solid currentColor;
  transform:rotate(45deg) translate(-.06em,.06em)}
.pm-btn:focus-visible,.pm-x:focus-visible,.pm-cta:focus-visible{
  outline:3px solid var(--primary-blue,#1a73e8);outline-offset:3px}
.pm-x:focus-visible{outline-offset:-2px}
/* Hover gated on (hover:hover) so a tap does not leave a control stuck in its
   hover state after the finger lifts. */
@media (hover:hover){
  .pm-btn:hover{transform:translateY(-2px);box-shadow:0 10px 24px var(--pm-glow),0 2px 5px rgba(15,23,42,.2)}
  .pm-x:hover{background:var(--pm-line);color:var(--pm-fg)}
  .pm-cta:hover{filter:brightness(1.06);box-shadow:0 6px 18px var(--pm-glow)}
}
.pm-x,.pm-cta{transition:background .15s ease,color .15s ease,filter .15s ease,box-shadow .15s ease}

/* ---- reduced motion, not reduced function --------------------------------- */
/* Every loop and every travelling transform goes; arrival, exit and the card
   become plain opacity fades. Nothing is hidden and nothing stops working. */
@media (prefers-reduced-motion:reduce){
  .pm-arriving .pm-ring,.pm-idle .pm-ico{animation:none !important}
  .pm-ring{display:none}
  .pm-arriving .pm-btn,.pm-card.pm-open{animation:pm-in .18s linear both !important;will-change:auto}
  .pm-leaving .pm-btn,.pm-card.pm-closing{animation:pm-in .15s linear reverse both !important}
  .pm-btn,.pm-x,.pm-cta{transition:none}
  .pm-btn:hover{transform:none}
}
