/*
 * A2 design tokens — deals.perprops.com
 *
 * 🔴 THIS FILE IS A GENERATED-EQUIVALENT ARTIFACT. It is a DERIVED COPY of the
 * authoritative PerProps design system, which lives at
 *
 *     ~/projects/website-builder/sites/perprops/src/styles/tokens.css
 *
 * and which this lane may never edit (standing estate rule). A derived copy of a
 * policy is the two-copies-of-one-policy defect with a delay fuse, so it carries
 * a staleness gate per RS-ENG-004: `node scripts/tokens-drift.mjs` re-derives the
 * A1 block and FAILS on any drift — a token removed, a value changed, or an
 * addition here that is not DECLARED below. Editing the copied block by hand is
 * therefore not "allowed but discouraged"; it is refused by a check.
 *
 * To change a copied token: change it in the A1 source, then re-run the drift
 * gate here. To add an A2-only token: add it under @a2-additions AND to the
 * declared list in scripts/tokens-drift.mjs, which is the reviewable act.
 */

/* ── @derived-from: sites/perprops/src/styles/tokens.css :root ───────────── */
:root {
  /* Primary palette */
  --color-primary: #1B3A5C;
  --color-primary-dark: #0F2440;
  --color-primary-light: #2A5A8C;
  --color-accent: #FFCC00;
  --color-accent-hover: #E6B800;

  /* Neutrals */
  --color-white: #FFFFFF;
  --color-off-white: #F3F5F8;
  --color-light-gray: #EAF1FD;
  --color-mid-gray: #D5D6D7;
  --color-dark-gray: #333333;
  --color-text: #313131;
  --color-text-light: #555555;
  --color-black: #000000;

  /* Semantic */
  --color-success: #28A745;
  --color-error: #DC3545;

  /* Typography */
  --font-heading: 'Montserrat', 'Montserrat Fallback', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-body: 'Open Sans', 'Open Sans Fallback', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* Spacing scale */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  --space-2xl: 3rem;
  --space-3xl: 4rem;
  --space-4xl: 6rem;

  /* Layout */
  --max-width: 1200px;
  --max-width-narrow: 800px;
  --header-height: 80px;
  --border-radius: 8px;
  --border-radius-lg: 12px;

  /* Shadows */
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 8px 30px rgba(0, 0, 0, 0.12);

  /* Transitions */
  --transition-fast: 150ms ease;
  --transition-base: 250ms ease;
}

/* ── @a2-additions — declared, and only these ────────────────────────────── */
:root {
  /*
   * S1-BUG-02: "A1 is --header-height: 80px. A2 is 60px, expressed nowhere in
   * tokens.css." It is expressed here, on the A2 side, because that is the side
   * this lane is permitted to write. It is a DISTINCT name rather than an
   * override of --header-height so that a component reading the A1 token on a
   * shared surface cannot silently pick up the A2 value.
   */
  --header-height-a2: 60px;

  /*
   * Offer-status badge grounds (screen 14 §1.1). Stored as roles, not pasted
   * hex: the spec is explicit that "colours are placeholders for tokens.css
   * roles, not hex to be pasted". Every value here resolves to an existing A1
   * token except the four status-specific tints, which A1 has no equivalent of
   * because A1 has no offers.
   */
  --status-submitted-bg: var(--color-light-gray);
  --status-submitted-fg: var(--color-primary);
  --status-countered-bg: #FFF7DB;
  --status-countered-fg: #8A6D00;
  --status-accepted-bg: #E8F5EC;
  --status-accepted-fg: #1E7E34;
  --status-declined-bg: #FBE9EA;
  --status-declined-fg: #B02A37;
  --status-withdrawn-bg: var(--color-off-white);
  --status-withdrawn-fg: var(--color-text-light);
  --status-withdrawn-border: var(--color-mid-gray);

  /* Derived error red (Deal Feed requirement D4: act-by within 7 days). */
  --color-error-derived: #B02A37;

  /*
   * Focus ring for DARK grounds. Screen 9 §8 records the open defect: the design
   * system says "yellow rings stay for dark surfaces" while ZERO yellow outlines
   * exist in the file, and the one focus ring drawn on a dark ground measured
   * 1.63:1 against a 3:1 requirement. --focus-ring-dark is white, which is
   * 12.6:1 on --color-primary-dark and 9.8:1 on --color-primary, so the A2 dark
   * panels have a ring that MEASURES rather than one that was asserted.
   */
  --focus-ring-dark: var(--color-white);
  --focus-ring-light: var(--color-primary-light);
  --focus-ring-width: 3px;

  /* Touch-target floors (screen 9 §8, screen 15 AS-23). */
  --touch-min: 44px;
  --touch-min-mobile: 48px;
  --touch-primary-mobile: 52px;
}

/*
 * A2 component styles. Served from this origin as a real file, because
 * `style-src 'self'` (src/csp.ts) and this surface ships no inline <style>.
 *
 * Every colour is a token. A hex here would be a second copy of the design
 * system with no gate behind it — `scripts/tokens-drift.mjs` guards the token
 * file, and it cannot guard values that bypass it.
 */

* {
  box-sizing: border-box;
}

/*
 * 🔴 `hidden` MUST WIN, AND WITHOUT THIS IT DOES NOT.
 *
 * The UA stylesheet implements the attribute as `[hidden] { display: none }` —
 * an ordinary rule with no special weight. Any author rule that sets `display`
 * on the same element beats it, and this file has several (`.field` is
 * `display: block`). The sign-in card's code input carries `hidden` until a code
 * has actually been sent, and it rendered VISIBLE anyway: a field asking for a
 * one-time code the visitor has not been sent.
 *
 * `!important` is deliberate and is the one place it is used. `hidden` is not a
 * style, it is a statement that the element is not currently relevant, and a
 * component that sets `display` should not be able to overrule that by accident.
 */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--color-text);
  background: var(--color-off-white);
}

.skip {
  position: absolute;
  left: -9999px;
}

.skip:focus {
  left: var(--space-sm);
  top: var(--space-sm);
  z-index: 10;
  padding: var(--space-sm) var(--space-md);
  background: var(--color-white);
  color: var(--color-primary);
}

main:focus {
  outline: none;
}

/*
 * 🔴 V3-N2 — THE ROOT CAUSE WAS THAT THIS RULE DID NOT EXIST.
 *
 * "Create an account" computed `rgb(0,0,238)` — the browser default — at
 * desktop while rendering correctly at 390px. The mobile breakpoint carried a
 * scoped `.signin-footer a` colour and there was no desktop counterpart,
 * because this stylesheet had never set a link colour AT ALL. So the defect was
 * not one link: EVERY link on the surface not covered by a scoped rule was
 * default blue, which also breaks the estate's no-default-browser-control rule.
 *
 * The values are the design file's own globals (`a { color:#1B3A5C }`,
 * `a:hover { color:#2A5A8C }`), expressed as tokens. Fixing the base means the
 * scoped rules that DO exist — the navy panel's white links, the mobile
 * footer's `#EAF1FD` — stay as deliberate overrides rather than as the only
 * places a colour was ever stated.
 */
a {
  color: var(--color-primary);
}

a:hover {
  color: var(--color-primary-light);
}

/* ── Chrome ───────────────────────────────────────────────────────────── */

.chrome {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  min-height: var(--header-height-a2);
  padding: 0 var(--space-lg);
  background: var(--color-primary-dark);
}

.chrome nav {
  display: flex;
  gap: var(--space-lg);
}

.chrome a {
  display: flex;
  align-items: center;
  min-height: var(--touch-min);
  color: var(--color-white);
  text-decoration: none;
}

.chrome a[aria-current='page'] {
  text-decoration: underline;
  text-underline-offset: 4px;
}

:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-light);
  outline-offset: 2px;
}

/* ── Layout ───────────────────────────────────────────────────────────── */

.page {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: var(--space-xl) var(--space-lg);
}

.page-narrow {
  max-width: var(--max-width-narrow);
}

h1,
h2,
h3 {
  font-family: var(--font-heading);
  color: var(--color-primary);
}

.card {
  background: var(--color-white);
  border: 1px solid var(--color-mid-gray);
  border-radius: var(--border-radius);
  padding: var(--space-lg);
  margin-bottom: var(--space-lg);
}

.muted {
  color: var(--color-text-light);
}

.numeric {
  font-variant-numeric: tabular-nums;
}

/* ── Buttons ──────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--touch-min);
  padding: 0 var(--space-lg);
  border: 1px solid transparent;
  border-radius: var(--border-radius);
  font-family: var(--font-body);
  font-size: 1rem;
  cursor: pointer;
  text-decoration: none;
}

.btn-primary-accent {
  background: var(--color-accent);
  color: var(--color-primary-dark);
  font-weight: 700;
}

.btn-primary-navy {
  background: var(--color-primary);
  color: var(--color-white);
}

.btn-outline {
  background: var(--color-white);
  border-color: var(--color-primary);
  color: var(--color-primary);
}

.btn-text {
  background: none;
  border: none;
  color: var(--color-white);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.btn-danger-text {
  background: none;
  border: none;
  color: var(--color-error-derived);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ── Badges and chips ─────────────────────────────────────────────────── */

.badge {
  display: inline-block;
  padding: 2px var(--space-sm);
  border-radius: var(--border-radius);
  font-size: 0.875rem;
}

.badge-submitted {
  background: var(--status-submitted-bg);
  color: var(--status-submitted-fg);
}

.badge-countered {
  background: var(--status-countered-bg);
  color: var(--status-countered-fg);
}

.badge-accepted {
  background: var(--status-accepted-bg);
  color: var(--status-accepted-fg);
}

.badge-declined {
  background: var(--status-declined-bg);
  color: var(--status-declined-fg);
}

.badge-withdrawn {
  background: var(--status-withdrawn-bg);
  color: var(--status-withdrawn-fg);
}

.badge-outlined {
  border: 1px solid var(--status-withdrawn-border);
}

.chip {
  display: inline-block;
  padding: 2px var(--space-sm);
  border-radius: var(--border-radius);
  font-size: 0.8125rem;
}

.chip-fits {
  background: var(--color-light-gray);
  color: var(--color-primary);
}

/* BB8 — three redundant cues, NONE colour-only: the chip carries a glyph and a
   word, so the distinction survives greyscale and colour-blindness. */
.chip-inherited {
  border: 1px dashed var(--color-mid-gray);
  color: var(--color-text-light);
}

.chip-overridden {
  border: 1px solid var(--color-primary-light);
  color: var(--color-primary);
}

/* ── Deal feed ────────────────────────────────────────────────────────── */

.deal-row {
  display: grid;
  gap: var(--space-md);
  align-items: center;
  padding: var(--space-md);
  border-bottom: 1px solid var(--color-mid-gray);
  background: var(--color-white);
}

.deal-row-match {
  grid-template-columns: 1.5fr 1fr 1fr 1fr auto auto;
  border-left: 4px solid var(--color-primary);
}

.deal-row-other {
  grid-template-columns: 1.5fr 1fr 1fr 1fr auto auto;
}

.act-by-urgent {
  color: var(--color-error-derived);
}

.follow-form {
  margin: 0;
}

.follow-compact {
  min-height: var(--touch-min);
  padding: 0 var(--space-sm);
}

/* ── The buyer-terms panel (Deal Detail) ──────────────────────────────── */

.panel-navy {
  background: var(--color-primary-dark);
  color: var(--color-white);
  border-radius: var(--border-radius);
  padding: var(--space-lg);
}

.panel-navy .panel-price {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 2rem;
  font-variant-numeric: tabular-nums;
}

/* Accent as TEXT on navy, not a button (DD-08 counts yellow BUTTONS: 1). */
.panel-navy .panel-actby {
  color: var(--color-accent);
}

.detail-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: var(--space-xl);
}

/* ── Modal ────────────────────────────────────────────────────────────── */

.modal {
  border: 1px solid var(--color-mid-gray);
  border-radius: var(--border-radius-lg);
  padding: var(--space-lg);
  background: var(--color-white);
  max-width: 520px;
  width: 100%;
}

.modal-close {
  min-width: var(--touch-min);
  min-height: var(--touch-min);
}

/* ── Forms ────────────────────────────────────────────────────────────── */

.field {
  display: block;
  margin-bottom: var(--space-md);
}

.field-label {
  display: block;
  margin-bottom: var(--space-xs);
  font-weight: 600;
}

/* 16px prevents iOS zoom-on-focus (screen 9 §4.1). Keep it. */
.input {
  width: 100%;
  min-height: 48px;
  padding: 0 var(--space-md);
  font-size: 16px;
  font-family: var(--font-body);
  border: 1px solid var(--color-mid-gray);
  border-radius: var(--border-radius);
  background: var(--color-white);
  color: var(--color-text);
}

.input[aria-invalid='true'] {
  border: 2px solid var(--color-error);
}

.alert {
  padding: var(--space-md);
  border-radius: var(--border-radius);
  background: var(--status-declined-bg);
  color: var(--status-declined-fg);
  margin-bottom: var(--space-md);
}

.warn {
  padding: var(--space-md);
  border-radius: var(--border-radius);
  background: var(--status-countered-bg);
  color: var(--status-countered-fg);
}

.consent-row {
  display: flex;
  gap: var(--space-sm);
  align-items: flex-start;
  min-height: var(--touch-min-mobile);
  padding: var(--space-sm) 0;
}

.consent-row input[type='checkbox'] {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
}

/*
 * H11 — the way back off step 2. Spacing only: the control itself is the shared
 * `.btn.btn-outline`, so it cannot drift from every other secondary action on a
 * light surface.
 */
.step-back {
  margin: 0 0 var(--space-lg);
}

/*
 * A3 — the agreement panel is height-capped with internal scroll so the layout
 * survives the final document AT ANY LENGTH. P9-16 substitutes a 10× fixture.
 */
.agreement-panel {
  max-height: 200px;
  overflow-y: auto;
  border: 1px solid var(--color-mid-gray);
  border-radius: var(--border-radius);
  padding: var(--space-md);
  background: var(--color-white);
  font-size: 0.875rem;
  line-height: 1.65;
}

.agreement-panel:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-light);
}

/* ── Switches ─────────────────────────────────────────────────────────── */

.matrix-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: var(--space-md);
  align-items: center;
  min-height: var(--touch-min);
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--color-mid-gray);
}

/* 🔴 THE HIT TARGET IS THE BUTTON; THE PILL IS ONLY PAINT (H3, 2026-08-14).
   These switches measured 52×30 CSS px at 390px — 30 against the 44 minimum —
   in a 5×2 consent matrix where the two switches in a row sit --space-md apart.
   A mis-tap there does not miss, it toggles the NEIGHBOURING consent: the wrong
   event or the wrong channel. That is why this is not a cosmetic target miss.

   ⚠️ The row already met 44 (`.matrix-row { min-height: var(--touch-min) }`)
   and `render.ts` documented the control as having "a ≥44px row" — which was
   true, and was never the same claim as the CONTROL being 44. A row-level
   assertion cannot see a 30px button inside it.

   The button is now --touch-min tall and the 52×30 pill is painted by ::before,
   so the visual is byte-identical and only the interactive box grew. Deliberately
   NOT done with a ::after overlay: an overlay leaves `getBoundingClientRect()`
   reporting 30, so the control would still measure as failing and the next
   reader would "fix" it again. The element that receives the tap is the element
   that measures 44. */
.switch {
  width: 52px;
  height: var(--touch-min);
  min-height: var(--touch-min);
  padding: 0;
  border: none;
  background: none;
  position: relative;
  cursor: pointer;
}

/* the pill — paint only, no pointer semantics of its own */
.switch::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 52px;
  height: 30px;
  border-radius: 15px;
  background: var(--color-mid-gray);
}

.switch[aria-checked='true']::before {
  background: var(--color-primary);
}

.switch-knob {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 3px;
  width: 24px;
  height: 24px;
  border-radius: 12px;
  background: var(--color-white);
  transition: left var(--transition-fast);
}

.switch[aria-checked='true'] .switch-knob {
  left: 25px;
}

/* ── Offer timeline ───────────────────────────────────────────────────── */

.timeline {
  list-style: none;
  margin: 0;
  padding: 0;
  border-left: 2px solid var(--color-primary);
}

.timeline li {
  position: relative;
  padding: var(--space-sm) 0 var(--space-md) var(--space-lg);
}

.timeline-dot {
  position: absolute;
  left: calc(var(--space-lg) * -0.5 - 10px);
  width: 20px;
  height: 20px;
  border-radius: 10px;
  background: var(--color-white);
  border: 2px solid var(--color-primary);
  color: var(--color-primary);
  font-size: 0.75rem;
  line-height: 16px;
  text-align: center;
}

.timeline-dot-latest {
  background: var(--color-primary);
  color: var(--color-white);
  border: none;
  line-height: 20px;
}

.offer-row-needs-response {
  border-left: 4px solid var(--color-accent-hover);
}

/* ── Sign-in split ────────────────────────────────────────────────────── */

/*
 * 🔴 `min-height` IS THE VIEWPORT, NOT 620px.
 *
 * It was `min-height: 620px` — the pixel height of the design's `1100 × 620`
 * desktop FRAME, transcribed as if it were a layout rule. A frame size is the
 * canvas the mock was drawn on; it is not a constraint the built page inherits.
 * The consequence was measured on the live site at 1280×1500: the navy panel
 * ended at y=620 with 880px of bare body background beneath it and the card
 * floating at y=154 — which is exactly the "panel dies mid-viewport, card on
 * bare white" the owner reported. `100dvh` follows `100vh` so that mobile
 * browsers whose URL bar changes the viewport height do not leave a strip.
 *
 * 🔴 `minmax(0, …)` IS LOAD-BEARING, NOT TIDINESS. A grid track's automatic
 * minimum is `auto`, which resolves to the max-content contribution of its
 * largest item — here `.signin-card`'s 380px plus `.split-form`'s 2 × 32px
 * padding = 444px. At 390px that overflowed the viewport and put a HORIZONTAL
 * SCROLLBAR on the sign-in page, with the card clipped at the right edge; the
 * card's own `max-width: 100%` could not save it, because the TRACK had already
 * been sized to the card. `minmax(0, 1fr)` lets the track shrink below its
 * content's max-content size, which is what makes `max-width: 100%` mean
 * anything at all.
 */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
  min-height: 100vh;
  min-height: 100dvh;
}

.split-brand {
  display: flex;
  flex-direction: column;
  /* The design file: lockup TOP, statement MIDDLE, support line BOTTOM. */
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-2xl);
  background: var(--color-primary-dark);
  color: var(--color-white);
}

.split-brand a {
  color: var(--color-white);
}

/*
 * The lockup. Sized by WIDTH with `height: auto` so the intrinsic 356 × 148
 * raster is never distorted; the `width`/`height` attributes on the element
 * carry the intrinsic ratio so the box is reserved before the image loads and
 * the panel does not reflow. Screen 9 §4.1: 190px desktop, 150px mobile.
 *
 * ⚠️ There is no SVG logo in the estate (largest raster 1746×724), so this is a
 * bitmap by necessity, not by choice. Do not scale it UP past its intrinsic
 * width.
 */
/*
 * ── 9a / 9b brand: a favicon BADGE plus a TEXT wordmark ──────────────────
 *
 * 🔴 THIS REPLACES THE RASTER LOCKUP AND RETIRES ITS CSS INVERT. The lockup was
 * measured to be brand BLUE despite its filename, so putting it on navy needed
 * `filter: brightness(0) invert(1)` — a treatment that existed only because the
 * estate has no white lockup. Screens 9a/9b draw a white-backed badge and a TEXT
 * wordmark, which needs no treatment: text takes its colour from the cascade and
 * scales without a raster.
 */
.brand-mark {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-md);
}

.brand-badge {
  display: inline-flex;
  padding: 6px 8px;
  background: var(--color-white);
  border-radius: var(--border-radius);
}

.brand-badge img {
  height: 24px;
  width: auto;
  display: block;
}

.brand-wordmark {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 22px;
  letter-spacing: 1px;
  color: var(--color-white);
}

.brand-wordmark-tail {
  color: var(--color-light-gray);
  font-weight: 600;
}

.split-pitch {
  color: var(--color-light-gray);
  font-size: 15.5px;
  line-height: 1.65;
  max-width: 360px;
  margin: var(--space-md) 0 0;
}

.split-brand-foot {
  margin: 0;
  color: var(--color-light-gray);
  font-size: 13px;
}

.split-brand-foot a {
  color: var(--color-white);
  font-weight: 700;
}

.split-form {
  background: var(--color-off-white);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-xl);
}

/* `min()` rather than `width` + `max-width`: one declaration, and it cannot be
 * satisfied by a track that sized itself to the larger value first. The WIDTH
 * lives on the stack so the card and its footer are the same width by
 * construction rather than by two declarations agreeing. */
.signin-stack {
  width: min(380px, 100%);
}

/*
 * 🔴 V3-N1 — the primary action was 236px wide against a 316px sibling input,
 * and 44px tall against the design's 50px, because the `width: 100%` /
 * `min-height` pair lived ONLY in the mobile breakpoint and had no desktop
 * counterpart. Scoped to the card rather than to `.btn-primary-accent`
 * globally: other screens have primary buttons that are deliberately NOT full
 * width (Deal Detail's is one), and widening them all to fix this one would be
 * an unmeasured change to screens nobody reported.
 */
.signin-card .btn {
  width: 100%;
  min-height: 50px;
}

.signin-card {
  background: var(--color-white);
  border-radius: var(--border-radius-lg) var(--border-radius-lg) 0 0;
  padding: var(--space-xl);
  box-shadow: var(--shadow-md);
}

/*
 * Desktop: the footer is the bottom of the same white card — joined by a shared
 * width, a continued background and the card's lower corners, with a hairline
 * rule where the design draws a divider.
 * Mobile (below): it detaches onto the navy ground.
 */
.signin-footer {
  margin: 0;
  padding: var(--space-md) var(--space-xl) var(--space-lg);
  background: var(--color-white);
  /* The file's divider is #EAF1FD (--color-light-gray), not the mid grey. */
  border-top: 1px solid var(--color-light-gray);
  border-radius: 0 0 var(--border-radius-lg) var(--border-radius-lg);
  box-shadow: var(--shadow-md);
}

/* ── The refusal page ─────────────────────────────────────────────────────
 *
 * Same navy ground and same card as the gate, so a dead link lands somewhere
 * that is recognisably this portal rather than on a JSON blob. The markup is a
 * compile-time constant in `src/http/uniform-404.ts` — it carries no nonce and
 * no session, which is why it can be styled at all without breaking the
 * byte-identity the refusal depends on.
 */
/*
 * The design file draws this state as a navy BAR with the DEALS wordmark over a
 * light card — not as a full navy page with a big lockup, which is what shipped
 * first. The action is an OUTLINE button, not the yellow one: yellow marks the
 * single primary action on a screen (A-INV-16), and "back to the feed" from a
 * dead link is not that.
 */
.refusal-bar {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 44px;
  padding: 0 var(--space-md);
  background: var(--color-primary);
}

.refusal-mark {
  display: inline-flex;
  padding: 2px 4px;
  background: var(--color-white);
  border-radius: 5px;
}

.refusal-mark img {
  height: 15px;
  width: auto;
  display: block;
}

.refusal-wordmark {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 12px;
  letter-spacing: 1.5px;
  color: var(--color-white);
}

.refusal {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100vh - 44px);
  min-height: calc(100dvh - 44px);
  padding: var(--space-xl);
  background: var(--color-off-white);
}

.refusal-card {
  width: min(420px, 100%);
  text-align: center;
  background: var(--color-white);
  border: 1px solid var(--color-mid-gray);
  border-radius: var(--border-radius-lg);
  padding: var(--space-xl);
}

.refusal-card h1 {
  margin-bottom: var(--space-sm);
}

.refusal-card p {
  color: var(--color-text-light);
  margin-bottom: var(--space-lg);
}

/* ── Mobile ───────────────────────────────────────────────────────────── */

/* ── 9a card controls ────────────────────────────────────────────────────── */

.field-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: var(--space-xs);
}

.field-aside {
  font-size: 13px;
  font-weight: 700;
  text-underline-offset: 2px;
}

/*
 * The in-field SHOW/HIDE control (9a annotation C). 46px wide and full input
 * height, so it clears the touch floor without a second row — and the input
 * reserves space with padding-right, because a control overlapping the text a
 * visitor is typing is worse than no control at all.
 */
.input-affix {
  position: relative;
  display: block;
}

.input-affixed {
  padding-right: 56px;
}

.affix-toggle {
  position: absolute;
  right: 2px;
  top: 2px;
  bottom: 2px;
  width: 46px;
  min-height: var(--touch-min);
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 11px;
  color: var(--color-primary);
  border-radius: 6px;
}

.affix-toggle:hover {
  background: var(--color-off-white);
}

.consent-row-tight {
  margin-top: var(--space-sm);
}

.signin-cardfoot {
  margin: var(--space-md) 0 0;
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-light-gray);
  font-size: 13.5px;
  color: var(--color-text-light);
}

/* ── 9b · the application ────────────────────────────────────────────────── */

.apply-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: 14px var(--space-xl);
  background: var(--color-primary-dark);
  color: var(--color-light-gray);
  font-size: 13.5px;
}

.apply-bar a {
  color: var(--color-white);
  font-weight: 700;
}

.apply-bar .brand-mark {
  flex-direction: row;
  align-items: center;
  gap: var(--space-sm);
}

.apply-bar .brand-wordmark {
  font-size: 15px;
}

.apply-bar .brand-badge img {
  height: 20px;
}

.apply-page {
  background: var(--color-off-white);
  min-height: calc(100vh - 60px);
  min-height: calc(100dvh - 60px);
  padding: var(--space-2xl) var(--space-xl) var(--space-3xl);
  display: flex;
  justify-content: center;
}

.apply-col {
  width: min(640px, 100%);
}

.apply-lede {
  font-size: 15px;
  color: var(--color-text-light);
  line-height: 1.6;
  margin: 0 0 var(--space-lg);
}

.apply-card {
  display: grid;
  gap: 18px;
  background: var(--color-white);
  border-radius: var(--border-radius-lg);
  box-shadow: var(--shadow-md);
  padding: var(--space-xl);
}

.apply-pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-md);
}

/*
 * Pick-any CHIPS, not a multi-select (9b annotation A: "a buyer covering 4
 * states shouldn't fight a multi-select"). They are real checkboxes with the
 * box visually hidden, so the control is a checkbox to a screen reader and to
 * the form POST, and only its PAINT is a chip.
 */
/*
 * A `<fieldset>` is the right ELEMENT for the state group — it gives the chips a
 * programmatic label via `<legend>` — but its default border and padding are
 * browser chrome the design does not draw, and the estate's no-default-control
 * rule forbids shipping them. Reset the paint, keep the semantics.
 */
fieldset.field {
  border: 0;
  padding: 0;
  margin: 0 0 var(--space-md);
  min-inline-size: 0;
}

fieldset.field > legend {
  padding: 0;
  margin-bottom: var(--space-sm);
}

.chip-set {
  display: flex;
  gap: var(--space-sm);
  flex-wrap: wrap;
}

.chip-choice input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.chip-choice span {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  padding: 10px 14px;
  border-radius: 999px;
  border: 1px solid var(--color-mid-gray);
  background: var(--color-white);
  color: var(--color-primary);
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
}

.chip-choice input:checked + span {
  border: 2px solid var(--color-primary);
  background: var(--color-light-gray);
}

.chip-choice input:focus-visible + span {
  outline: var(--focus-ring-width) solid var(--focus-ring-light);
  outline-offset: 2px;
}

.apply-consequences {
  background: var(--color-off-white);
  border-radius: var(--border-radius);
  padding: 12px var(--space-md);
  font-size: 13px;
  color: var(--color-text-light);
  line-height: 1.6;
}

.apply-fineprint {
  font-size: 12.5px;
  color: var(--color-text-light);
  text-align: center;
}

.apply-received {
  width: min(560px, 100%);
  background: var(--color-white);
  border-radius: var(--border-radius-lg);
  box-shadow: var(--shadow-md);
  padding: var(--space-2xl);
  text-align: center;
}

.apply-tick {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--status-accepted-bg);
  color: var(--status-accepted-fg);
  font-size: 28px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto var(--space-md);
}

.apply-next {
  background: var(--color-off-white);
  border-radius: var(--border-radius);
  padding: 14px 18px;
  font-size: 13.5px;
  color: var(--color-text-light);
  text-align: left;
  line-height: 1.7;
}

/* ── 9b state picker: grouped checkboxes + a filter that only hides rows ──── */

.state-picker {
  display: grid;
  gap: var(--space-md);
}

.state-group {
  border: 1px solid var(--color-mid-gray);
  border-radius: var(--border-radius);
  padding: var(--space-md);
  margin: 0;
  min-inline-size: 0;
}

.state-group > legend {
  padding: 0 var(--space-xs);
  margin: 0;
}

/*
 * A scrolling list rather than 41 rows of page: the group is capped and scrolls
 * internally, so the form stays one screen and the filter has somewhere to act.
 */
.state-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-xs);
  max-height: 220px;
  overflow-y: auto;
  margin-top: var(--space-sm);
}

.state-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: var(--touch-min);
  padding: 0 var(--space-xs);
  cursor: pointer;
  border-radius: var(--border-radius);
}

.state-row:hover {
  background: var(--color-off-white);
}

.state-row input {
  width: 20px;
  height: 20px;
  margin: 0;
  accent-color: var(--color-primary);
  flex-shrink: 0;
}

.state-row span {
  font-size: 14px;
}

.state-row strong {
  font-family: var(--font-heading);
  margin-right: var(--space-xs);
}

.state-selectall {
  min-height: var(--touch-min);
  font-size: 13px;
  padding: 0 var(--space-md);
}

.state-nomatch {
  margin: 0;
}

@media (width <= 700px) {
  .detail-grid {
    grid-template-columns: 1fr;
  }

  /*
   * Screen 9 §4.3 / mirror line 137: mobile 390 is "navy ground, white lockup
   * 150px, white card" — ONE navy page, not a navy band above an off-white one.
   * The form section therefore takes the same ground as the brand section, so
   * the two rows read as a single surface however the viewport height is split
   * between them.
   *
   * `minmax(0, 1fr)`, not `1fr`, for the reason recorded at `.split`: the bare
   * keyword leaves the track's automatic minimum at max-content and puts a
   * horizontal scrollbar on a 390px phone.
   */
  .split {
    grid-template-columns: minmax(0, 1fr);
  }

  .split-brand {
    padding: var(--space-xl) var(--space-md) var(--space-md);
    gap: var(--space-sm);
  }

  /* Screen 9 §4.3: "white lockup 150px", centred on the navy ground. Only the
   * lockup centres — the positioning statement stays left-aligned, as drawn. */
  .brand-lockup {
    width: 150px;
    margin: 0 auto var(--space-sm);
  }

  /*
   * The footer LEAVES the card here: on mobile the design puts
   * "New buyer? Create an account" below the white box, on the navy ground, in
   * --color-light-gray (#EAF1FD). Everything that made it look like part of the
   * card on desktop is unset rather than overridden piecemeal, so a future
   * change to the desktop treatment cannot leak a white strip onto the navy.
   */
  .signin-card {
    border-radius: var(--border-radius-lg);
  }

  /*
   * D-2 — the file's mobile frame is LIGHTER than the desktop panel: lockup,
   * card, footer. No positioning statement and no support line. Hidden rather
   * than removed from the markup, so the desktop panel keeps one source.
   */
  .split-brand h1,
  .split-brand p {
    display: none;
  }

  .signin-footer {
    padding: var(--space-md) 0 0;
    background: none;
    border-top: none;
    border-radius: 0;
    box-shadow: none;
    text-align: center;
    color: var(--color-light-gray);
  }

  .signin-footer a {
    color: var(--color-light-gray);
  }

  /*
   * 9a mobile: a navy header carrying the CENTRED badge + wordmark, then a
   * LIGHT #F3F5F8 sheet with 20px top radii that holds the form. The previous
   * mobile was navy all the way down with a white card; the file draws a light
   * sheet, and the sheet is what makes the rounded top read as a sheet at all.
   */
  .split-brand {
    padding: 28px var(--space-lg) 20px;
    text-align: center;
  }

  .brand-mark {
    align-items: center;
    gap: var(--space-sm);
  }

  .brand-wordmark {
    font-size: 17px;
  }

  .brand-badge img {
    height: 22px;
  }

  .split-pitch,
  .split-brand-foot {
    display: none;
  }

  .split-form {
    background: var(--color-off-white);
    border-radius: 20px 20px 0 0;
    padding: var(--space-lg);
    align-items: start;
  }

  .signin-card {
    background: none;
    box-shadow: none;
    padding: 0;
  }

  .deal-row-match,
  .deal-row-other {
    grid-template-columns: 1fr;
    gap: var(--space-sm);
  }

  .btn {
    min-height: var(--touch-min-mobile);
  }

  .btn-primary-accent,
  .btn-primary-navy {
    min-height: var(--touch-primary-mobile);
    width: 100%;
  }

  .agreement-panel {
    height: 250px;
    max-height: none;
  }

  .sticky-bar {
    position: sticky;
    bottom: 0;
    display: flex;
    gap: var(--space-sm);
    padding: var(--space-sm);
    background: var(--color-white);
    border-top: 1px solid var(--color-mid-gray);
  }
}

/*
 * Screen 9 §8 records the inherited defect: "yellow rings stay for dark
 * surfaces" was stated while ZERO yellow outlines existed in the design file,
 * and the one ring drawn on a dark ground measured 1.63:1 against a 3:1
 * requirement. Dark grounds get the WHITE ring, which measures 12.6:1 on
 * --color-primary-dark and 9.8:1 on --color-primary.
 *
 * ⚠️ It sits at the END of the file on purpose. These selectors are more
 * specific than `.btn` and `.split-brand a`, and stylelint's
 * `no-descending-specificity` refused the earlier placement — a real ordering
 * hazard, not a style preference: a later, LESS specific rule silently loses,
 * which is how a focus ring comes to be defined and never rendered.
 */
.chrome a:focus-visible,
.chrome .btn:focus-visible,
.panel-navy a:focus-visible,
.panel-navy button:focus-visible,
.split-brand a:focus-visible,
.split-brand button:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-dark);
  outline-offset: 2px;
}

/*
 * The secondary line under the primary action — the design's "Forgot password?"
 * slot. It holds different content in the two branches (a reset link when
 * password is a first factor, an explanation of the code when it is not), so it
 * is styled once and its CONTENT varies rather than its layout.
 */
.signin-secondary {
  margin: var(--space-sm) 0 0;
  padding: 4px 0;
  text-align: center;
}

.signin-secondary a {
  text-underline-offset: 2px;
}


/* ── The stale-tab retry notice (round 8) ────────────────────────────────────
 * Injected by the server after the shell anchor, so it inherits `.card` and
 * needs only the accent that marks it as an interruption rather than content.
 * Deliberately not `position: fixed` — a person who scrolled away from it has
 * read it, and a banner that follows you is a banner people learn to dismiss.
 */
.notice {
  border-left: 4px solid var(--color-accent);
  margin-bottom: var(--space-lg);
}


/* ══ BS-1 · 9a's OR divider, Google secondary, and the passwordless link ═════
 *
 * 🔴 EVERY VALUE IS A TOKEN, and the two controls below are DELIBERATELY NOT
 * new button variants. `.btn-google` and `.btn-link` are modifiers ON `.btn`,
 * so they inherit the estate's focus ring, min-height and transition by
 * construction rather than by someone remembering to copy them
 * (~/.claude/rules/ui-consistency.md).
 *
 * ⚠️ `.btn-link` EXISTS BECAUSE `.btn-text` IS WHITE. `.btn-text` is
 * `color: var(--color-white)` — correct on the navy chrome bar it was written
 * for and INVISIBLE on the white sign-in card. Reusing it here would have
 * shipped the `logo-white.webp` defect a third time: a control that renders,
 * loads, and cannot be read.
 */
.or-divider {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin: var(--space-xs) 0;
}

.or-rule {
  height: 1px;
  flex: 1;
  background: var(--color-mid-gray);
}

.or-word {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 1px;
  color: var(--color-text-light);
}

.btn-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
}

.btn-google svg {
  flex-shrink: 0;
}

/*
 * The passwordless secondary. Link-shaped so 9a keeps ONE yellow primary
 * (A-INV-16), but a real `<button>` so it is announced as an action and is
 * keyboard-reachable in form order.
 */
.btn-link {
  background: none;
  border: 0;
  color: var(--color-primary);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 13.5px;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;

  /* A 44px target even though it paints as text — a link-shaped control is
   * still a control, and this one is the only way a passwordless buyer gets in. */
  min-height: var(--touch-min);
  padding: 0 var(--space-sm);
}

.btn-link:hover {
  color: var(--color-primary-light);
}

.signin-alt {
  justify-self: center;
}

/* ══ Screen 9c · the onboarding wizard ══════════════════════════════════════
 *
 * The chrome bar is `.apply-bar` — 9b's, unchanged — so this wizard inherits
 * its navy, its brand mark and its mobile treatment rather than defining a
 * second header that drifts from it. Only the PROGRESS strip below it is new.
 */
.wiz-bar {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 14px var(--space-xl);
  background: var(--color-white);
  border-bottom: 1px solid var(--color-mid-gray);
  font-size: 13px;
  color: var(--color-text-light);
}

.wiz-step {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 13px;
  color: var(--color-primary-dark);
}

.wiz-track {
  display: flex;
  gap: 6px;
  flex: 1;
  max-width: 300px;
}

.wiz-seg {
  height: 6px;
  border-radius: 3px;
  flex: 1;
  background: var(--color-mid-gray);
}

.wiz-seg-on {
  background: var(--color-primary);
}

/*
 * The SMS consent panel — 9c annotation C draws it navy-outlined on the pale
 * ground so the one unticked box reads as an invitation rather than as fine
 * print. It is emphasis, NEVER a pre-tick: the box below it ships unchecked and
 * the server records nothing unless it is checked.
 */
.sms-panel {
  border: 2px solid var(--color-primary);
  border-radius: var(--border-radius-lg);
  padding: var(--space-md) 18px;
  background: var(--color-light-gray);
}

.sms-panel .consent-row {
  margin: 0;
}

.sms-reason {
  font-size: 12.5px;
  color: var(--color-text);
  margin: 6px 0 0;

  /* Aligned under the label, not under the checkbox: it explains the choice,
   * it is not a second choice. */
  padding-left: 38px;
}

/*
 * The step-3 bridge's required band. `.warn` already carries the countered
 * amber pair; this only adds the leading emphasis run so "Signing is required."
 * reads as the sentence it is.
 */
.wiz-required strong {
  font-family: var(--font-heading);
}

.wiz-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
}

/*
 * The ✉ badge on the verify and check-your-email cards. `.apply-tick` is the
 * green ✓ for a completed act; this is the informational blue sibling, and the
 * two are separate classes because "we did something" and "we are waiting for
 * you" are different states and must not paint the same.
 */
.apply-tick-info {
  background: var(--color-light-gray);
  color: var(--color-primary-light);
  font-weight: 400;
  font-size: 26px;
}

/* ── BS-4 U1/U5 — the Deals/Listings tabs, the buy-box scope toggle, and the
   LOCKED matrix row.

   Every value below is a token or an existing sibling pattern: the tabs reuse
   `.btn-outline`'s border and `--color-primary`, the toggle reuses `.btn`
   spacing, and the locked chip reuses `.chip`. Nothing here introduces a hex
   literal or a bespoke control (`~/.claude/rules/ui-consistency.md`).

   ⚠️ ZERO ACCENT on this screen. A-INV-16 reserves `--color-accent` for
   `Make an Offer` on Deal Detail, and the current tab is distinguished by a
   navy underline + weight rather than by yellow. */
.tabs {
  display: flex;
  gap: var(--space-lg);
  border-bottom: 1px solid var(--color-mid-gray);
  margin-bottom: var(--space-lg);
}

.tab {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  padding: var(--space-sm) var(--space-xs);
  color: var(--color-text-light);
  text-decoration: none;
  border-bottom: 3px solid transparent;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.tab:hover {
  color: var(--color-primary);
}

/* Two redundant cues, not colour alone: the weight changes with the border, so
   the current tab survives greyscale. Same discipline as `.chip-inherited`. */
.tab-current {
  color: var(--color-primary);
  font-weight: 600;
  border-bottom-color: var(--color-primary);
}

.toggle {
  display: inline-flex;
  gap: var(--space-sm);
  margin-bottom: var(--space-lg);
}

/* The selected half of the one-tap toggle. It is the `.btn-outline` sibling
   inverted — navy fill, white text — which is exactly `.btn-primary-navy`'s
   treatment, so the two selected states in the portal look like one system. */
.btn-toggle-on {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-white);
}

/* A locked matrix row (owner §3.2 "offer responses ALWAYS").
   ⚠️ The row is tinted and its CHANNEL SWITCHES ARE NOT DISABLED — the lock is
   on the event filter only, and dimming the switches would tell the buyer they
   cannot turn off texts for this row, which is both false and the copy-side of
   the TCPA rule. */
.matrix-row-locked {
  background: var(--color-off-white);
}

.chip-locked {
  background: var(--color-light-gray);
  color: var(--color-primary);
}
