/* ─────────────────────────────────────────────────────────────
   css/consent.css — cookie-consent banner + settings sheet (WS-B).

   GO-CONSENT-GATE-001. TOKENS ONLY — every color/font resolves to a
   --payoff-* custom property (dang-tokens.css + payoff-tokens.css). No new
   hex where a token exists.

   SELF-CONTAINED: the banner + sheet appear on ALL pages (LP / onboarding /
   app / auth-callback), but account.css (which owns .ma-overlay/.ma-sheet) is
   loaded only on SOME. So the sheet uses its own .consent-* classes — nothing
   here depends on account.css being present.

   EQUAL PROMINENCE: `.consent-btn` is a SINGLE rule shared by both banner
   actions AND both sheet actions. There is exactly one button style — no
   primary/ghost variant — so Allow and Decline are identical in size and weight
   by construction (the audited legal requirement).
   ───────────────────────────────────────────────────────────── */

/* ── Banner + neutral ack (fixed body-level sibling) ─────────────── */
.consent-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  /* ── THE BANNER STACKS BELOW EVERY APP MODAL ──────────────────
     This declaration was `z-index: 900`, annotated "Above page chrome,
     BELOW the settings sheet overlay (z 1000)". Both halves of that
     sentence were TRUE. What it did not say is that 900 is also above
     every APP modal: the layering ladder (payoff-tokens.css:291-300)
     puts --z-modal at 400 and only reaches 900 at --z-dev, so the
     banner outranked .app-overlay and .ma-overlay by 500.

     MEASURED (Massive-UAT gate, real Chromium, 390×844, cookie question
     unanswered), isolated by control against the real backend: the
     banner won hit-testing over the debt form's "Add this debt", and
     the SERVER confirmed the debt was never added — surviving 1 → 1.
     Same build with the banner answered: 1 → 2, added. "Save payments"
     and "Remove debt" were hit the same way. Not layout: there was no
     scrollable ancestor and scrollIntoView moved nothing (751 → 751).

     300 is a STACKING repair, deliberately NOT a fourth suppression
     toggle. A per-modal class toggle is a call-site fix that every
     future modal silently re-opens — which is exactly how C8/B-3's
     sign-in suppression (below) failed to reach these three surfaces.
     300 sits:
       • strictly BELOW --z-modal (400), so .app-overlay (app.css:443),
         .ma-overlay (app.css:942), myaccount.css:286 and done-only.css:256
         and everything inside them win hit-testing — for every modal that
         exists today and every one added later, with no call-site change;
       • ABOVE all page chrome in this codebase — --z-nav (80), the
         cinematic topbar (cine.css:378, 80), the marketing header
         (site.css:154, 60), account.css:57 (40) — so the banner stays
         visible and usable on the landing page, onboarding, the legal
         pages, the app and auth-callback;
       • BELOW .consent-overlay (1000), so the settings sheet still opens
         above the banner that launches it.

     A LITERAL, not a var(): this file declares itself SELF-CONTAINED
     (see the header), and an undefined custom property resolves z-index
     to `auto` — a fixed banner with no stacking level is a WORSE failure
     than the one being repaired. (All seven documents that load
     consent.css do load payoff-tokens.css first, verified; the literal
     is belt-and-braces, not a workaround for an absent token.) The
     RELATIONSHIP to the ladder is held by consentStacking.test.ts, which
     reads these files and compares, so a future ladder change cannot
     silently strand this number. */
  z-index: 300;
  box-sizing: border-box;
  background: var(--payoff-bg-warm);
  border-top: 0.5px solid var(--payoff-border);
  box-shadow: 0 -8px 28px rgba(23, 25, 27, 0.06);
  font-family: var(--payoff-font-sans);
  color: var(--payoff-text);
  /* Safe-area aware (notched phones). */
  padding: 16px max(16px, env(safe-area-inset-right)) max(16px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
}

/* ── SIGNUP TAKES INTERACTION PRIORITY (founder ruling C8/B-3) ────
   HISTORICAL CONDITION (preserved as measured, no longer current — see
   the stacking note above): the banner was a fixed body sibling at
   z-index 900; the sign-in modal's overlay is --z-auth (700). Measured
   in real Chromium the banner
   therefore painted over an OPEN sign-in sheet and won elementFromPoint
   at the centre of both "Continue with Google" and "Email me a link" —
   the signup actions were unclickable until the cookie question was
   answered. While an account-creation modal is open (auth/accountButton.js
   adds the class on open, removes it on close) the banner is SUPPRESSED,
   not answered: display:none takes it out of paint, hit-testing and the
   accessibility tree while the element stays mounted and the STORED
   consent choice is untouched — no implied accept/decline, mayTrack
   unchanged, no vendor request. It returns unchanged when the modal
   closes. Matching .consent-banner (not a specific instance) also covers
   a banner/ack re-mounted while the modal is open.

   STILL IN FORCE, NOW BELT-AND-BRACES. With the banner lowered to 300 the
   sign-in modal (--z-auth, 700) wins on stacking alone, so this rule is no
   longer the only thing standing between a visitor and the signup actions.
   It is a founder ruling's mechanism and is DELIBERATELY RETAINED — removing
   it is out of scope for this repair. Note the ordering: the suppression is
   `display: none`, which beats any z-index, so lowering the banner cannot
   weaken C8/B-3. */
body.dang-auth-modal-open .consent-banner { display: none !important; }

/* ── THE BANNER RESERVES ITS OWN SPACE (founder ruling C8/B-3) ────
   position:fixed means nothing in normal flow accounted for the banner.
   Measured on the onboarding welcome screen at 390×844: banner 398→844
   (446px, 53% of the viewport) covering "Get started", "Terms of
   Service" and "Privacy Notice", with document maxScroll 0 — the signup
   action and both legal links were literally unreachable unless you
   first answered the cookie question. consentBoot publishes the LIVE
   banner height as --dang-consent-inset and sets .dang-consent-inset on
   <html> while (and only while) a banner or ack is mounted, so the
   document reserves exactly that much at its bottom and every element
   can be scrolled clear of the banner. The moment a choice is made the
   class and the variable go away and the page is byte-identical to the
   locked design. */
html.dang-consent-inset body { padding-bottom: var(--dang-consent-inset, 0px); }

/* Compact band only: the full-viewport onboarding arc pins itself to
   100dvh, so its footer (CTA + legal links) sits at the very bottom
   where the banner lands. Shrinking the arc's minimum height by the
   reserved inset lets its flex layout pull the footer UP out from under
   the banner instead of relying on the reader to scroll. Scoped to the
   banner-showing state.

   SCOPE (C8-R1 FINAL AMENDMENT — F2): this block was `@media (max-width: 1024px)`.
   That upper bound was the P0. The banner is `position: fixed` at the bottom of
   the viewport at EVERY width, so it can collide at any width whose HEIGHT is
   short — and desktop windows routinely are (1366×660, 1280×720, 1440×780). With
   the bound in place the banner won the hit test on desktop welcome controls, and
   a real click on "Get started" wrote a consent record the visitor never gave.

   WHICH RULE DOES THE WORK, PER ARC (measured, PO C8R1A):
     • `body.dang-signup-priority .consent-banner { display: none }` reaches EVERY
       width and is the rule that CLOSES the P0. Confirmed `display:none` at
       1366×660, 1025×660, 1024×660 and 390×844, with consent unanswered.
     • the `.po-stage` yield and its `min-height` reach the COMPACT arc ONLY.
       `document.querySelector(".po-stage")` is null at 1025×660 and 1366×660: the
       desktop arc renders `.po-dt-chrome`/`.po-dt-card` (desktop-chrome.js:44,
       :82), not `.po-stage` (flow.js:626). On desktop both selectors are INERT.

   ERRATUM (F-A, PO C8R1A). The sentence that stood here claimed "the yield and
   the signup-priority suppression now apply wherever the banner can collide", and
   asserted that `.po-stage` "is pinned to 100dvh at every width". Both were
   MECHANISM CLAIMS NOBODY MEASURED — on desktop `.po-stage` does not exist at
   all. That is the THIRD unmeasured-mechanism claim in three rounds, and the
   first written INSIDE the erratum ordered to stop them, which is why it is
   recorded here instead of quietly rewritten. The suppression's reach — the part
   the P0 actually depends on — is measured above, not asserted. */
@media all {
  html.dang-consent-inset .po-stage {
    min-height: calc(100dvh - var(--dang-consent-inset, 0px));
  }

  /* ── THE WELCOME SCREEN TAKES INTERACTION PRIORITY (C8-R1) ────
     Reserving space was not enough: measured in real Chromium at
     390×844 and 320×640 with consent unanswered and scrollY 0, the
     banner still won elementFromPoint on "Terms of Service", "Privacy
     Notice" and "I already have an account" (and at 320 on "Get
     started" too), and real clicks on all of them were swallowed.
     Nothing about the account-creation surface may be gated behind a
     cookie answer, so while the welcome screen is on screen the banner
     is SUPPRESSED, not answered — exactly as it already is under an
     open sign-in modal (see the rule at the top of this file).
     onboarding/flow.js adds the class on the welcome screen and removes
     it on every other screen, so the banner RETURNS the moment the
     visitor advances.

     `display: none` takes the banner out of paint, hit-testing AND the
     accessibility tree while the element stays mounted; no consent
     state is read or written, no choice is implied, `mayTrack` is
     unchanged and no vendor request can fire. With the banner gone its
     reserved inset must go too, or the page would keep a phantom
     209/231px of bottom padding and a shortened stage — so both are
     neutralised for exactly as long as the suppression lasts, which
     also restores the arc to the locked design's own full-viewport
     geometry.

     ── ERRATUM (C8-R1 FINAL AMENDMENT, founder-ordered) ──────────────
     The sentence that stood here was:

       "Desktop is deliberately untouched (its 77px banner never
        collided: 4/4 controls clear at first paint, measured)."

     IT WAS FALSE, and it is preserved verbatim above rather than
     rewritten, because a comment that silently corrects itself is not
     evidence. The Product-Owner measured, in real Chromium with consent
     unanswered:

       1280×720   1/4 controls clear   "Get started" hit-tested to
                                       BUTTON.consent-btn "Allow", and a
                                       real click WROTE
                                       {"choice":"accepted",...} — a
                                       consent record the visitor never
                                       gave (a fabricated record).
       1440×780   1/4 clear            MacBook Air 13"; both legal links
                                       unreachable at max scroll.
       1366×660   0/4 clear            no visible way to start onboarding
       1366×625   0/4 clear            at any scroll position.

     No measurement supporting "4/4 clear" was ever run. This is the
     SECOND false measurement claim in two rounds; the replacement text
     above therefore states the LAYOUT RULE and asserts no measurement.
     The rule now applies at every width — see the SCOPE note above.

     SECOND-ORDER ERRATUM (F-B, PO C8R1A re-measure). The annotation
     "no visible way to start onboarding at any scroll position" on the
     1366×660 / 1366×625 rows is preserved above as written, but it is
     FALSE on this build. It measured DOCUMENT scroll — genuinely 0 —
     and missed the welcome card's OWN inner scroller (`.po-body`,
     onboarding.css:411-421): one ordinary wheel gesture scrolls it
     180px, after which all four controls are on screen and each WINS
     its own hit test. The "0/4 clear" counts are correct AT FIRST PAINT
     and only at first paint. Auth controls sitting below first paint at
     short desktop heights are PRE-EXISTING (onboarding.css:379-394),
     are neither introduced nor fixed by this diff, and are carried as
     an open item rather than claimed closed here. */
  body.dang-signup-priority .consent-banner { display: none !important; }
  html.dang-consent-inset body.dang-signup-priority { padding-bottom: 0; }
  html.dang-consent-inset body.dang-signup-priority .po-stage { min-height: 100dvh; }
}

.consent-banner__inner {
  max-width: 1080px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}

.consent-banner__body {
  margin: 0;
  flex: 1 1 320px;
  min-width: 240px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--payoff-text-2);
}

.consent-banner__actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
}

.consent-banner__more {
  flex-basis: auto;
}

/* Neutral GPC acknowledgment — title above the body copy. */
.consent-ack__title {
  margin: 0 0 4px;
  flex-basis: 100%;
  font-family: var(--payoff-font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--payoff-text);
  letter-spacing: -0.2px;
}

/* ── The ONE equal-prominence action button (banner + sheet share this) ── */
.consent-btn {
  /* fixed structural size → Allow and Decline are identical by construction */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 120px;
  height: 44px;
  padding: 0 20px;
  box-sizing: border-box;
  font-family: var(--payoff-font-sans);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.2px;
  line-height: 1;
  cursor: pointer;
  border-radius: 12px;
  border: 1px solid var(--payoff-coral);
  background: var(--payoff-coral);
  color: #fff;
}

.consent-btn:hover { background: var(--payoff-coral-press); border-color: var(--payoff-coral-press); }
.consent-btn:active { transform: scale(0.985); }
.consent-btn:focus-visible {
  outline: 2px solid var(--payoff-coral);
  outline-offset: 2px;
}

/* ── Tertiary "Cookie settings" text link (NOT an equal button) ── */
.consent-link {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  margin: 0;
  cursor: pointer;
  font-family: var(--payoff-font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--payoff-text-2);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.consent-link:hover { color: var(--payoff-text); }
.consent-link:focus-visible {
  outline: 2px solid var(--payoff-coral);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ── Narrow viewport: stack copy over actions; actions fill the row ── */
@media (max-width: 640px) {
  .consent-banner__inner { flex-direction: column; align-items: stretch; gap: 12px; }
  /* C8/B-3 — the banner's phone height was a CSS bug, and it was the whole
     reason the banner swallowed the signup action. `.consent-banner__body`
     carries `flex: 1 1 320px` for the WIDE layout, where the container is a
     ROW and 320px is a width. This block flips the container to a COLUMN, so
     that same declaration became a 320px MINIMUM HEIGHT plus flex-grow on a
     paragraph whose text is only ~84px tall — measured in real Chromium the
     body box was 320px and the whole banner 446px at both 390 and 320, i.e.
     53% and 70% of the viewport. Resetting the item to its content size in
     the stacked layout is the correct expression of the same design; copy,
     classes, order and the equal-prominence buttons are all untouched. */
  .consent-banner__body { flex: 0 1 auto; min-width: 0; }
  .consent-banner__actions { justify-content: stretch; }
  .consent-btn { flex: 1 1 0; min-width: 0; }
  .consent-banner__more { text-align: center; }
}

/* ── Settings sheet — self-contained .ma-sheet-equivalent ────────── */
.consent-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000; /* above the banner (z 300) — the sheet the banner launches */
  background: rgba(23, 25, 27, 0.42);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
}
@media (min-width: 641px) {
  .consent-overlay { align-items: center; padding: 24px; }
}

.consent-sheet {
  position: relative;
  box-sizing: border-box;
  width: 100%;
  max-width: 460px;
  max-height: 90vh;
  overflow-y: auto;
  background: var(--payoff-card);
  color: var(--payoff-text);
  font-family: var(--payoff-font-sans);
  border: 0.5px solid var(--payoff-border);
  border-radius: 20px 20px 0 0;
  box-shadow: 0 -12px 48px rgba(23, 25, 27, 0.18);
  padding: 24px 22px calc(24px + env(safe-area-inset-bottom));
}
@media (min-width: 641px) {
  .consent-sheet { border-radius: 20px; box-shadow: 0 24px 64px rgba(23, 25, 27, 0.22); }
}

.consent-sheet__close {
  position: absolute;
  top: 16px;
  right: 16px;
  appearance: none;
  border: 0;
  background: transparent;
  padding: 4px 6px;
  cursor: pointer;
  font-family: var(--payoff-font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--payoff-text-3);
}
.consent-sheet__close:hover { color: var(--payoff-text); }
.consent-sheet__close:focus-visible { outline: 2px solid var(--payoff-coral); outline-offset: 2px; border-radius: 6px; }

.consent-sheet__title {
  margin: 0 0 10px;
  font-family: var(--payoff-font-display);
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.4px;
  color: var(--payoff-text);
}

.consent-sheet__intro {
  margin: 0 0 18px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--payoff-text-2);
}

/* The one precise purpose row + its current-state pill. */
.consent-sheet__purpose {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 14px 0;
  border-top: 0.5px solid var(--payoff-border);
  border-bottom: 0.5px solid var(--payoff-border);
  margin-bottom: 16px;
}
.consent-sheet__purpose-main { flex: 1 1 auto; }
.consent-sheet__purpose-label {
  font-size: 14px;
  font-weight: 600;
  color: var(--payoff-text);
  margin-bottom: 3px;
}
.consent-sheet__purpose-sub {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--payoff-text-3);
}

.consent-state {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 4px 9px;
  border-radius: 999px;
  background: var(--payoff-bg-warm);
  color: var(--payoff-text-3);
  border: 0.5px solid var(--payoff-border);
  white-space: nowrap;
}
.consent-state--on {
  background: var(--payoff-sage-bg);
  color: var(--payoff-sage-deep);
  border-color: var(--payoff-sage-line);
}
.consent-state--off {
  background: var(--payoff-coral-bg);
  color: var(--payoff-coral-press);
  border-color: var(--payoff-coral-bg);
}

.consent-sheet__gpc {
  margin: 0 0 16px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--payoff-text-2);
  background: var(--payoff-sage-tint);
  border: 0.5px solid var(--payoff-sage-line);
  border-radius: 12px;
  padding: 12px 14px;
}

.consent-sheet__actions {
  display: flex;
  gap: 10px;
  margin-bottom: 16px;
}
.consent-sheet__actions .consent-btn { flex: 1 1 0; min-width: 0; }

.consent-sheet__foot {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--payoff-text-3);
}
