/* ==========================================================================
   Propertybuyer, design layer: the paid segment landing pages.
   --------------------------------------------------------------------------
   Serves every page under "New Builds/". Google Ads and Performance Max
   traffic, noindex while this is a proof of concept.

   REQUIRES assets/css/tokens.css and assets/css/base.css, in that order,
   loaded before this file. This file declares no tokens and holds no raw
   values. npm run check fails on a raw hex anywhere in here.

   Split out of styles.css on 21 August 2026. The location pages have their
   own layer at assets/css/locations.css and the two are never loaded
   together. Anything that turns out to be genuinely common to both belongs
   in base.css, and promoting it there is a decision to record in
   shared/segments.json rather than a refactor to do quietly.

   The previous home buyers design was removed on 17 August 2026 at the
   client's direction. It is recoverable from git history at d5251d3 if any
   part of it is wanted back.

   The two standing rules apply to every edit. Never invent a value: if it is
   not in Figma Variables, stop and ask. Never soften a constraint because a
   brief seems to want it softened.
   ========================================================================== */

/* ==========================================================================
   Design layer, home buyers page
   Built 17 August 2026 from Paper "BUYER 1440". Desktop values are read
   straight off the artboard; 390 values follow the mobile-390 token mode.
   ========================================================================== */
/* ==========================================================================
   Hero
   ========================================================================== */

.hero { background: var(--bg-brand); color: var(--text-on-brand); }
.hero .wrap {
  display: flex;
  flex-direction: column;
  gap: var(--gap-xl);
  padding-top: 88px;
  padding-bottom: 96px;
}
.hero-grid { display: flex; align-items: center; gap: var(--gap-xl); }
.hero-copy { display: flex; flex-direction: column; gap: var(--gap); flex: 1; min-width: 0; }
.hero-copy-top { display: flex; flex-direction: column; align-items: flex-start; gap: var(--gap-lg); }
/* 24px between the H1 and the avatar cluster, per Paper (gap-y --space-gap). */
.hero-title-block { display: flex; flex-direction: column; gap: var(--gap); }

.hero h1 { color: var(--text-on-brand); }

/* The H1 breaks in different places at each viewport. Desktop carries the
   home up onto the first line; at 390 that line is wider than the 350 the
   column allows, so it splits again after "Buy your" and the H1 runs four
   lines. Both sets of breaks ship in the markup and the viewport removes
   the pair it does not want, which keeps the heading a single text node so
   its character count still measures the way paper/buyer-copy.md records it. */
.br--desktop { display: inline; }
.br--mobile { display: none; }

.hero__subhead {
  font-size: var(--type-h3-card);
  font-weight: var(--weight-regular);
  line-height: 1.4;
  letter-spacing: var(--tracking-body);
  color: var(--text-on-brand);
}
.hero-body {
  font-size: var(--type-body);
  font-weight: var(--weight-body);
  line-height: 1.6;
  letter-spacing: var(--tracking-body);
  max-width: 470px;
  color: var(--text-on-brand-muted);
}
.hero-sub { display: flex; flex-direction: column; gap: var(--gap-sm); }

/* Avatar comparison: two buyer cards, "vs", one selling agent card. */
.hero-vs { display: flex; align-items: center; gap: var(--gap-sm); padding-bottom: var(--space-gutter); flex-wrap: wrap; }
.hero-vs-pair { display: flex; align-items: flex-start; gap: var(--gap-xs); }
.avatar-card {
  position: relative;
  padding: var(--gap-xs);
  border-radius: var(--radius-card);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-avatar-card);
  background: var(--bg-frost);
  transition: rotate 380ms cubic-bezier(0.22, 1, 0.36, 1), box-shadow 380ms ease-out;
}
.avatar-card--l { rotate: -4.8deg; }
.avatar-card--r { rotate: 7.7deg; }
.avatar-card--vs { rotate: 3.4deg; }
/* The two buyer cards frame in the brand terracotta, the selling agent
   card keeps the neutral frost. Requested 18 Aug 2026. */
.avatar-card--l, .avatar-card--r { background: var(--action-primary); }
.avatar-card:hover { rotate: 0deg; }
.avatar-card__img {
  width: 90px;
  height: 90px;
  border-radius: var(--radius-avatar);
  overflow: clip;
  position: relative;
  background: var(--bg-page);
}
.avatar-card__img img { width: 100%; height: 100%; object-fit: cover; }
.avatar-card__img::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 57%;
  background: var(--fade-brand-deep);
}
.avatar-card--vs .avatar-card__img img { filter: grayscale(100%); }
/* Centre of the mark sits 30.5px below the card centre, per Paper, so it
   rides the photo fade rather than hiding in the card padding. */
.avatar-card__mark {
  position: absolute;
  left: 50%;
  top: calc(50% + 30.5px);
  translate: -50% -50%;
  z-index: 1;
}
.hero-vs__vs {
  font-family: var(--family-display);
  font-weight: var(--weight-display);
  font-size: var(--type-h2);
  line-height: 1.1;
  color: var(--text-on-brand);
}

/* --- Hero form card -------------------------------------------------------- */
/* The card itself is the shared capture component in base.css. Only its
   hero composition lives here: the fixed desktop width, full width below
   1024 in the responsive block. */

.hero .lead-card { width: 460px; flex-shrink: 0; }


/* --- Awards marquee --------------------------------------------------------- */

.hero-awards { display: flex; align-items: center; gap: var(--tap-min); }
.hero-awards__label {
  font-size: var(--type-button);
  font-weight: var(--weight-regular);
  line-height: 1.4;
  letter-spacing: var(--tracking-body);
  width: 203px;
  flex-shrink: 0;
  color: var(--text-on-brand);
}
.hero-awards__strip { display: flex; align-items: center; flex: 1; min-width: 0; }
.hero-awards__strip > svg { flex-shrink: 0; }
.laurel-cap--l { rotate: 180deg; }

.marquee {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  /* Vertical room so the badge drop shadows never clip; the mask fades the
     badges in and out of both edges. */
  padding-block: 12px;
  -webkit-mask-image: var(--mask-marquee);
  mask-image: var(--mask-marquee);
}
.marquee__track {
  display: flex;
  align-items: center;
  width: max-content;
}
/* The loop shifts by exactly one badge set, and page.js clones the set until
   the remainder still covers the strip at the far end of the shift. The old
   rule shifted a flat -50% of a hand-duplicated track, which only looks
   seamless when one copy is already wider than the strip: at 1440 the closer
   ticker ran dry 440px early and the hero strip 107px early. Only animates
   once the script has measured and filled, so a no-JS page shows a static
   row rather than a half-empty sliding one. */
.marquee.is-looping .marquee__track {
  animation: marquee var(--marquee-duration, 48s) linear infinite;
}
.badge { width: 80px; height: 80px; flex-shrink: 0; filter: var(--shadow-badge); }
.badge img, .badge svg { width: 100%; height: 100%; object-fit: contain; }
.badge--reb { display: flex; align-items: center; justify-content: center; }
.badge--reb img { width: 96%; height: 74%; }

@keyframes marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(var(--marquee-shift, 50%) * -1)); }
}

/* ==========================================================================
   Testimonials
   ========================================================================== */

.testimonials { background: var(--bg-page); }
.testimonials .wrap { display: flex; flex-direction: column; gap: var(--tap-min); }
.testimonials-head { display: flex; justify-content: space-between; gap: var(--gap-lg); flex-wrap: wrap; }
.testimonials-head h2 { max-width: 460px; }
.testimonials-head .muted { max-width: 460px; }
.testimonials-head__intro { display: flex; flex-direction: column; gap: 10px; }

.rating-rows { display: flex; flex-direction: column; gap: var(--gap-sm); align-self: flex-start; }
.rating-row { display: flex; align-items: center; justify-content: space-between; gap: var(--gap); }
.rating-row__place { display: flex; align-items: center; gap: var(--gap-sm); }
.rating-row__place span { color: var(--text-muted); }
.rating-row__score { display: flex; align-items: center; gap: var(--gap-sm); }
.rating-row__score strong { font-weight: var(--weight-semibold); }
.stars { display: flex; }
.stars svg { width: var(--space-gutter); flex-shrink: 0; }

.story-cards { display: flex; gap: var(--space-gutter); }
.story-card {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-card);
  overflow: clip;
  /* A transparent shadow of the same geometry as the base, so the hover
     shadow grows into place instead of appearing from none. */
  box-shadow: 0 0 0 0 transparent;
  transition: transform 320ms ease-out, box-shadow 320ms ease-out;
}
.story-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-cta-panel); }
.story-card__media {
  position: relative;
  height: 342px;
  background: var(--bg-brand);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  overflow: clip;
}
.story-card__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.story-card__media::after { content: ""; position: absolute; inset: 0; background: var(--fade-photo-dark); }
.story-card__privacy {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--gap-xs);
  opacity: 0.9;
  font-size: var(--type-micro);
  font-weight: var(--weight-body);
  line-height: 16px;
  color: var(--text-on-brand);
}
/* Hidden at the client's request, 18 Aug 2026. Hidden, not deleted: the
   markup and the rule above stay intact, so removing this one line brings
   the badge back. It sits after the base rule rather than before it because
   the two selectors have equal specificity and source order decides.
   The story photographs come from the client's own published success-story
   decks, so hiding the label conceals nothing. */
.story-card__privacy { display: none; }
.story-card__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-gutter);
  padding: 24px 24px var(--gap-lg);
  background: var(--bg-surface);
}
.story-card__text { display: flex; flex-direction: column; gap: var(--gap-xs); }
.story-card__text h3 { line-height: 1.25; }

/* Developer page only, 28 Aug 2026. The three development sites are real and
   confidential, so the photograph ships whole and the blur is applied here
   rather than baked into the file: the same source becomes a clear crop by
   dropping one class, once a client agrees a site can be named.

   The scale is what keeps the blur honest. A blurred image samples outside
   its own edges and feathers them, so without it the card would carry a
   pale border on all four sides. */
.story-card__media--private img { filter: blur(8px); scale: 1.08; }
.story-card__confidential {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--space-gutter);
  opacity: 0.9;
  padding: var(--gap-sm) var(--gap) var(--gap-sm) var(--space-gutter);
  border: 1px solid var(--border-info);
  border-radius: var(--radius-card);
  background: var(--bg-brand);
  font-size: var(--type-small);
  font-weight: var(--weight-regular);
  line-height: 16px;
  color: var(--text-on-brand);
}
.story-card__confidential svg { flex-shrink: 0; }

/* The summary and the purchase record share one box that takes the leftover
   height, so the figures sit on the same line across all three cards however
   long the summary runs. Without it a short summary floats its stats up and
   the row reads as three unrelated cards.

   It fixes the headings too, which is why the commercial page adopted it on
   31 Aug 2026. .story-card__body is space-between, so with no flow box the
   whole text block bottom-aligns and the card with the shortest text hangs
   its heading below the other two. Giving the text block the leftover height
   pins the heading under the pill instead. Commercial only as at 31 Aug: the
   buyer and investor pages still carry the plain arrangement. */
.story-card__text:has(.story-card__flow) { flex: 1; }
.story-card__flow {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-gutter);
}

/* The purchase record under the summary, two or three figures per card.
   The numbers stay at type/body and semibold rather than type/stat: the
   card already spends its weight on the photograph and the heading, and a
   third loud element would flatten the order. Value reads above its label,
   with the markup left in term then definition order. */
.story-stats {
  display: flex;
  gap: var(--gap-sm);
  margin: var(--gap-xs) 0 0;
  padding-top: var(--gap-sm);
  border-top: 1px solid var(--border-default);
}
.story-stat { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.story-stat dd {
  order: 1;
  margin: 0;
  font-size: var(--type-body);
  font-weight: var(--weight-semibold);
  line-height: 1.2;
  color: var(--text-heading);
}
.story-stat dt {
  order: 2;
  font-size: var(--type-micro);
  font-weight: var(--weight-body);
  line-height: 1.3;
  color: var(--text-muted);
}

/* Confidentiality treatment for the developer page, 27 Aug 2026. Development
   acquisitions are not disclosed, so the band carries a notice and each
   client name is taped over. Both selectors are additive: no other page in
   this family carries the markup, so nothing else changes.

   The tape is a background rather than a blur, because a CSS blur on live
   text is reversible by anyone who opens devtools, and these are real client
   names. Transparent text keeps the name in the accessibility tree for a
   screen reader, which is the same exposure as the tape being lifted, so the
   name is also removed from selection. If the name must not reach the DOM at
   all, it has to come out of the markup instead. See storyNameRedaction.

   NOTE, 31 Aug 2026: .redacted and .story-stat[hidden] now match nothing on
   any page. The client removed the developer card bodies, which were the only
   markup using them. Both are kept rather than deleted, the same call as
   .story-card__privacy above, because the cards are a client decision that
   has already changed twice. .stories-notice is still live. */
.stories-notice {
  max-width: 760px;
  margin: 0;
}
/* Same trap as .lead-step and .steps-nav: .story-stat is display: flex, which
   outranks the UA [hidden] rule, so hiding one stat needs saying explicitly. */
.story-stat[hidden] { display: none; }
.redacted {
  background: var(--text-heading);
  color: transparent;
  border-radius: 2px;
  padding: 0 0.15em;
  -webkit-user-select: none;
  user-select: none;
}

/* ==========================================================================
   The Truth
   ========================================================================== */

.truth { background: var(--bg-surface-alt); position: relative; overflow: clip; }
.truth::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 900px;
  background: url("../img/texture-wall-1.webp") center / cover;
  mix-blend-mode: multiply;
  pointer-events: none;
}
.truth .wrap { display: flex; flex-direction: column; gap: var(--gap-xl); position: relative; }
.truth-head { display: flex; align-items: center; gap: var(--gap-lg); }
.truth-head__copy { display: flex; flex-direction: column; gap: var(--gap-lg); flex: 1; }
.truth-title { display: flex; flex-direction: column; max-width: 580px; }
.truth-title .line-2 { color: var(--text-body); }
.truth-head .lede { max-width: 420px; color: var(--text-body); }
.truth-cta { display: flex; flex-direction: column; align-items: flex-start; gap: var(--gap-sm); }
.truth-head__group { display: flex; flex-direction: column; gap: var(--space-gutter); align-items: flex-start; }

.truth-visual { position: relative; width: 540px; height: 543px; flex-shrink: 0; }
.truth-visual__docs {
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
  width: 115%;
  max-width: none;
}
.truth-visual__medal {
  position: absolute;
  left: 15px; top: 133px;
  width: 100px;
  height: 100px;
  rotate: -15deg;
  transition: rotate 420ms cubic-bezier(0.22, 1, 0.36, 1);
}
.truth-visual:hover .truth-visual__medal { rotate: -5deg; }

/* Cards stack on scroll: each card is sticky, the next slides over the last. */
.truth-cards { display: flex; flex-direction: column; gap: var(--gap); }
.truth-card {
  position: sticky;
  display: flex;
  align-items: center;
  gap: var(--gap-xl);
  padding: var(--gap-lg);
  border-radius: var(--radius-card);
  background: var(--bg-page);
  border: 1px solid var(--border-default);
  box-shadow: var(--shadow-card-stack);
  overflow: clip;
}
.truth-card:nth-child(1) { top: 96px; }
.truth-card:nth-child(2) { top: 112px; }
.truth-card:nth-child(3) { top: 128px; }
.truth-card:nth-child(4) { top: 144px; }
/* The developer page runs five cards where the other three run four. The
   rule is inert on a page with four. */
.truth-card:nth-child(5) { top: 160px; }

.truth-card__media { position: relative; width: 361px; height: 359px; flex-shrink: 0; overflow: clip; }
.truth-card__media > img:first-child { width: 100%; height: 100%; object-fit: cover; }
.truth-card__media::before {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 54%;
  background: var(--fade-photo-dark);
  z-index: 1;
}
.truth-card__copy { display: flex; flex-direction: column; gap: var(--gap-lg); max-width: 540px; }
.truth-card__copy .swoosh { flex-shrink: 0; }
.truth-card__copy h2 {
  font-family: var(--family-text);
  font-size: var(--type-h2);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-heading);
  line-height: 1.25;
  color: var(--text-heading);
}
.truth-card__copy .group { display: flex; flex-direction: column; gap: var(--space-gutter); }

/* ==========================================================================
   Stats band
   ========================================================================== */

.stats { background: var(--grad-band-teal); color: var(--text-on-brand); position: relative; overflow: clip; }
.stats::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
  width: 100%;
  min-width: 1440px;
  height: 100%;
  min-height: 961px;
  background: url("../img/texture-wall-2.webp") center / cover;
  mix-blend-mode: multiply;
  opacity: 0.8;
  pointer-events: none;
}
.stats .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-xl);
  position: relative;
}

.stats-visual { position: relative; width: 550px; height: 405px; flex-shrink: 0; }
.stats-visual__photo { position: absolute; left: 0; top: 16px; width: 550px; height: 373px; object-fit: cover; }
.stats-visual__medal { position: absolute; left: 358px; top: 190px; width: 100px; height: 100px; }
.stat { position: absolute; display: flex; flex-direction: column; align-items: center; gap: var(--gap-sm); }
.stat--awards { left: 0; bottom: 0; }
.stat--properties { left: 199px; top: 0; }
.stat--reviews { right: 0; bottom: 0; }
.stat__num {
  font-family: var(--family-display);
  font-weight: var(--weight-display);
  font-size: 20px;
  line-height: 1.25;
  text-align: center;
  color: var(--text-on-brand);
}
.stat__label {
  font-size: var(--type-micro);
  font-weight: var(--weight-regular);
  line-height: 1.6;
  text-transform: uppercase;
  text-align: center;
  color: var(--text-on-brand);
}

.stats-copy { display: flex; flex-direction: column; gap: var(--gap-lg); }
.stats-copy__head { display: flex; flex-direction: column; gap: var(--space-gutter); }
.stats-copy h2 { color: var(--text-on-brand); max-width: 460px; }
.stats-points { display: flex; flex-direction: column; gap: var(--gap-sm); }
.stats-point { display: flex; align-items: flex-start; gap: var(--gap-sm); }
.stats-point__laurels { display: flex; gap: var(--gap-xs); flex-shrink: 0; }
.stats-point p { color: var(--text-on-brand); }
.stats-cta { display: flex; flex-direction: column; align-items: flex-start; gap: var(--gap-sm); }

/* The reviews band trades the three aggregate claims for the city ratings
   the testimonials header already carries. Those rows were drawn for a light
   surface: the place name uses text/muted and the stars are filled
   bg/prestige, both of which vanish on teal/700. The gold matches the
   laurels this band used before, so the section keeps the colour it had. */
.rating-rows--on-brand .rating-row__place span { color: var(--text-on-brand-muted); }
.rating-rows--on-brand .rating-row__score strong { color: var(--text-on-brand); }
.rating-rows--on-brand .stars svg path { fill: var(--text-prestige); }
.stats-cta .btn-note { color: var(--text-on-brand-muted); }

.review-pill {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  width: fit-content;
  padding: var(--gap-sm) var(--gap) var(--gap-sm) var(--space-gutter);
  border-radius: var(--radius-card);
  backdrop-filter: blur(4px);
  background: var(--bg-frost-subtle);
}
.review-pill__stars {
  font-size: var(--type-h3-card);
  font-weight: var(--weight-regular);
  letter-spacing: 0.1em;
  line-height: 30px;
  color: var(--text-highlight);
}
.review-pill a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  color: var(--text-on-brand);
  text-decoration: underline 1px;
  text-underline-offset: 4px;
  transition: text-underline-offset 140ms ease;
}
.review-pill a:hover { text-underline-offset: 6px; }

/* ==========================================================================
   Steps band
   ========================================================================== */

.steps { background: var(--bg-page); }
.steps .wrap { display: flex; flex-direction: column; gap: var(--gap-xl); }
.steps-head { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
.steps-head h2 { max-width: 580px; }
.steps-head .order { text-decoration: underline 1px; text-underline-offset: 4px; color: var(--text-body); }

/* The five cards ride a horizontal rail. Four fill the container exactly
   (4 x 275 + 3 x 20 = 1160) and the fifth overflows the right edge, so
   there is visibly more to pull into view. The rail is a native scroller;
   page.js adds mouse drag and the arrow buttons below it. */
.steps-row {
  display: flex;
  gap: var(--space-gutter);
  overflow-x: auto;
  scrollbar-width: none;
  cursor: grab;
  /* Room for the hover lift, which the scroll container would otherwise
     clip at its padding box. */
  padding-top: 8px;
  margin-top: -8px;
}
.steps-row::-webkit-scrollbar { display: none; }
.steps-row.is-dragging { cursor: grabbing; }
/* While the pointer is dragging the rail, the cards must not eat the move
   events or fire their hover lift mid-drag. */
.steps-row.is-dragging .step-card { pointer-events: none; }
.step-card {
  flex: 0 0 275px;
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  padding: var(--gap);
  border-radius: var(--radius-card);
  color: var(--text-on-brand);
  transition: transform 320ms ease-out;
}
.step-card:hover { transform: translateY(-4px); }
.step-card--1 { background: var(--bg-brand); }
.step-card--2 { background: var(--bg-brand-deep); }
.step-card--3 { background: var(--action-primary-hover); }
.step-card--4 { background: var(--action-primary); }
.step-card__text { display: flex; flex-direction: column; gap: 8px; }
.step-card h3 {
  font-size: var(--type-h3-card);
  font-weight: var(--weight-medium);
  color: var(--text-on-brand);
}
.step-card p { font-size: var(--type-small); }
/* Scoped to the text block so the "Step N" pill keeps its text-body ink. */
.step-card--1 .step-card__text p, .step-card--2 .step-card__text p { color: var(--text-on-brand-muted); }
/* margin-top:auto pins the photo to the foot of the card. The cards stretch
   to a common height, so this puts all four photos on one lane the way Paper
   draws them; without it the lane shifts by up to 30px as the body copy
   above wraps to a different number of lines. */
.step-card__img { margin-top: auto; height: 212px; overflow: clip; background: var(--bg-page); }
.step-card__img img { width: 100%; height: 100%; object-fit: cover; transition: scale 600ms cubic-bezier(0.22, 1, 0.36, 1); }
.step-card:hover .step-card__img img { scale: 1.04; }
.step-card__swoosh { position: absolute; right: 14px; top: 30px; }

.steps-nav { display: flex; justify-content: center; gap: var(--gap-sm); }
/* Same trap as .lead-step: display:flex outranks the UA [hidden] rule, so
   page.js hiding the nav on an unscrollable rail would not take. */
.steps-nav[hidden] { display: none; }
.steps-nav__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-brand);
  background: none;
  color: var(--text-heading);
  cursor: pointer;
  transition: background-color 160ms ease-out, opacity 160ms ease-out;
}
.steps-nav__btn:hover:not(:disabled) { background: var(--bg-info-subtle); }
.steps-nav__btn:disabled { opacity: 0.35; cursor: default; }

/* ==========================================================================
   Services band
   Teal band with the same wall texture treatment as the stats band, from
   Paper "BUYER 1440 v2", 19 Aug 2026.
   ========================================================================== */

.services { background: var(--bg-brand); color: var(--text-on-brand); position: relative; overflow: clip; }
/* The texture is a 1440x960 still, so it is centred and stretched to the
   full height of the band. A top-anchored fixed height cut off partway
   down once the cards made the section taller than the export. */
.services::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  translate: -50% -50%;
  width: 100%;
  min-width: 1440px;
  height: 100%;
  min-height: 960px;
  background: url("../img/texture-wall-2.webp") center / cover;
  mix-blend-mode: multiply;
  opacity: 0.8;
  pointer-events: none;
}
.services .wrap { display: flex; flex-direction: column; gap: var(--gap-xl); position: relative; }
.services-head { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-lg); flex-wrap: wrap; }
.services-head__intro { display: flex; flex-direction: column; align-items: flex-start; gap: var(--gap-lg); }
/* Paper sets this H2 at type/stat, a step above the section scale. */
.services h2 { font-size: var(--type-stat); color: var(--text-on-brand); }
.services-cta { display: flex; flex-direction: column; align-items: center; gap: var(--gap-sm); }
.services-cta .btn-note { color: var(--text-on-brand-muted); }

.service-cards { display: flex; gap: var(--space-gutter); }
.service-card { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--gap); }
.service-card__img { height: 400px; overflow: clip; background: var(--bg-page); }
.service-card__img img { width: 100%; height: 100%; object-fit: cover; transition: scale 600ms cubic-bezier(0.22, 1, 0.36, 1); }
.service-card:hover .service-card__img img { scale: 1.04; }
.service-card__text {
  display: flex;
  flex-direction: column;
  gap: var(--gap-xs);
  border-top: 2px solid var(--border-muted);
  padding-top: var(--gap-sm);
}
.service-card__text h3 { font-size: var(--type-h3-card); font-weight: var(--weight-semibold); color: var(--text-on-brand); }
.service-card__text p { color: var(--text-on-brand-muted); }

/* ==========================================================================
   Auction
   ========================================================================== */

.auction { background: var(--bg-surface-alt); position: relative; overflow: clip; padding-block: 160px; }
.auction::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 900px;
  background: url("../img/texture-wall-3.webp") center / cover;
  mix-blend-mode: multiply;
  opacity: 0.8;
  pointer-events: none;
}
.auction > * { position: relative; }
.auction-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--gap-lg);
  max-width: 690px;
  margin-inline: auto;
  padding-inline: var(--space-gutter);
  text-align: center;
}
.auction-head h2 { font-size: var(--type-stat); line-height: 1.25; }
.auction-head .sub strong { font-weight: var(--weight-semibold); }
.auction-head .sub p { max-width: var(--measure-intro); }

.auction-strip { margin-block: 80px 0; overflow: hidden; }
.auction-strip__row {
  display: flex;
  gap: var(--gap-sm);
  justify-content: center;
  width: max-content;
  margin-inline: auto;
}
.auction-photo {
  position: relative;
  width: 573px;
  height: 680px;
  border-radius: var(--radius-card);
  overflow: clip;
  background: var(--bg-photo-fallback);
  flex-shrink: 0;
}
.auction-photo > img:first-child { width: 100%; height: 100%; object-fit: cover; filter: blur(0.5px); }

.auction-moves {
  display: flex;
  flex-direction: column;
  gap: 80px;
  max-width: var(--container);
  margin: 80px auto 0;
  padding-inline: var(--space-gutter);
}
.moves-row { display: flex; gap: var(--gap-xl); }
.move { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--gap); padding-top: 14px; }
.move svg { flex-shrink: 0; }
.move__text { display: flex; flex-direction: column; gap: 8px; }
.move__text::before { content: ""; display: block; width: 100%; height: 2px; background: var(--border-brand); margin-bottom: 8px; }

.auction-cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-xl);
  padding: var(--gap) var(--gap-lg);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-cta-panel);
  background: var(--bg-page);
}
.auction-cta__lines { flex: 1; min-width: 0; }
.auction-cta__lines p {
  font-size: var(--type-h3-card);
  line-height: 1.6;
  letter-spacing: var(--tracking-body);
  color: var(--text-heading);
}
.auction-cta__lines p:first-child { font-weight: var(--weight-semibold); }
.auction-cta__action { display: flex; flex-direction: column; align-items: center; gap: var(--gap-sm); flex-shrink: 0; }
.auction-cta__action .btn-note { font-weight: var(--weight-medium); }

/* Investor only. A claim strip across the foot of the capture card. The
   modifier carries the wrap so the other three segment pages, which have no
   third child in this form, keep the single row they were built with. */
.auction-cta--strip { flex-wrap: wrap; row-gap: var(--gap); }
.auction-cta__strip {
  flex-basis: 100%;
  margin: 0;
  padding: var(--gap-sm) var(--gap);
  background: var(--bg-brand);
  color: var(--text-on-brand);
  text-align: center;
  font-weight: var(--weight-semibold);
}

/* ==========================================================================
   Prestige
   ========================================================================== */

.prestige { background: var(--bg-prestige); color: var(--text-on-brand); position: relative; overflow: clip; }
.prestige::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("../img/prestige-grain-bg.webp") center / cover;
  pointer-events: none;
}
.prestige .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-xl);
  position: relative;
}
.prestige-visual {
  width: 546px;
  height: 401px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: clip;
}
.prestige-visual img { width: 140%; max-width: none; }
.prestige-copy { display: flex; flex-direction: column; gap: 18px; max-width: 477px; }
.prestige-copy h2 { color: var(--text-on-brand); }
.prestige-copy .qualifier { font-weight: var(--weight-regular); color: var(--text-on-brand); }
.prestige-copy .body { color: var(--text-on-brand-muted); }
.prestige-actions { display: flex; align-items: center; gap: var(--gap); padding-top: 10px; flex-wrap: wrap; }

/* ==========================================================================
   FAQ
   ========================================================================== */

.faq { background: var(--bg-surface); }
.faq .wrap { display: flex; gap: var(--gap-xl); }
.faq h2 { width: 40%; flex-shrink: 0; padding-bottom: 12px; }
.faq-list { display: flex; flex-direction: column; gap: var(--gap-sm); flex: 1; min-width: 0; }
.faq-item {
  transition: border-color 260ms ease-out;
  /* Lets block-size transition from 0 to auto for the open animation. */
  interpolate-size: allow-keywords;
  border-radius: var(--radius-card);
  background: var(--bg-page);
  border: 1px solid var(--border-muted);
  transition: border-color 140ms ease;
}
.faq-item:hover { border-color: var(--border-brand); }
.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  min-height: var(--tap-min);
  padding: 18px 20px;
  cursor: pointer;
  list-style: none;
  font-size: var(--type-body);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-button);
  line-height: 20px;
  color: var(--text-heading);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary svg { flex-shrink: 0; transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1); }
.faq-item[open] summary svg { transform: rotate(45deg); }

/* Smooth open and close. ::details-content with interpolate-size is the
   native way to animate a details element; browsers without it simply
   toggle instantly, which is the same behaviour as before. */
.faq-item::details-content {
  block-size: auto;
  opacity: 1;
  overflow: clip;
  transition: block-size 360ms cubic-bezier(0.22, 1, 0.36, 1), opacity 280ms ease-out, content-visibility 360ms allow-discrete;
}
.faq-item:not([open])::details-content {
  block-size: 0;
  opacity: 0;
}
.faq-item__a { padding: 0 20px 18px; max-width: 68ch; }

/* ==========================================================================
   Motion preferences
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* Needs to outrank .marquee.is-looping .marquee__track. The script also
     skips the loop entirely under this preference; this is the backstop. */
  .marquee__track,
  .marquee.is-looping .marquee__track { animation: none; }
  *, *::before, *::after { transition-duration: 0.01ms; }
}

/* ==========================================================================
   Responsive
   The design was drawn at 1440. Everything below follows the mobile-390
   token mode and the old template's breakpoints: 768 and 1024.
   ========================================================================== */

@media (max-width: 1023px) {
  .hero-grid { flex-direction: column; align-items: stretch; }
  .hero .lead-card { width: 100%; }
  .hero-awards { flex-direction: column; align-items: flex-start; gap: var(--gap); }
  .hero-awards__strip { width: 100%; }
  .truth-head { flex-direction: column; align-items: flex-start; }
  .truth-visual { width: 100%; max-width: 540px; height: auto; aspect-ratio: 540 / 470; }
  .truth-visual__docs { width: 100%; position: static; translate: none; }
  .truth-visual__medal { left: 3%; top: 24%; }
  .stats .wrap { flex-direction: column; align-items: flex-start; }
  .stats-visual { width: 100%; max-width: 550px; height: auto; aspect-ratio: 550 / 405; }
  .stats-visual__photo { width: 100%; height: auto; top: 4%; }
  .stats-visual__medal { left: auto; right: 6%; top: 42%; width: 84px; height: 84px; }
  .stat--properties { left: 50%; translate: -50%; }
  .prestige .wrap { flex-direction: column; align-items: flex-start; }
  .prestige-visual { width: 100%; max-width: 546px; height: auto; aspect-ratio: 546 / 401; }
  .faq .wrap { flex-direction: column; }
  .faq h2 { width: 100%; }
  .service-card__img { height: 300px; }
}

/* Tablet only. The three story cards stay side by side until 768, so each
   card column is at its narrowest here and three stats across would clip.
   Below 768 the cards go full width and the row fits again. */
@media (min-width: 768px) and (max-width: 1023px) {
  .story-stats { flex-direction: column; gap: 6px; }
  .story-stat { flex-direction: row; align-items: baseline; gap: 6px; }
  .story-stat dd { order: 0; }
  .story-stat dt { order: 1; }
}

@media (max-width: 767px) {
  .hero .wrap { padding-top: 48px; padding-bottom: 56px; }
  .story-cards { flex-direction: column; }
  .truth-card { flex-direction: column; align-items: stretch; padding: var(--space-gutter); gap: var(--gap); }
  .truth-card__media { width: 100%; height: auto; aspect-ratio: 361 / 300; }
  .truth-card:nth-child(1) { top: 72px; }
  .truth-card:nth-child(2) { top: 84px; }
  .truth-card:nth-child(3) { top: 96px; }
  .truth-card:nth-child(4) { top: 108px; }
  .truth-card:nth-child(5) { top: 120px; }
  .rating-rows { width: 100%; }
  .services-head { flex-direction: column; align-items: flex-start; }
  .service-cards { flex-direction: column; }
  .auction { padding-block: var(--band-y); }
  .auction-strip { margin-block: var(--gap-lg) 0; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .auction-strip__row { width: max-content; margin: 0; padding-inline: var(--space-gutter); }
  .auction-photo { width: 78vw; max-width: 420px; height: auto; aspect-ratio: 573 / 680; }
  .auction-moves { gap: var(--gap-lg); margin-top: var(--gap-lg); }
  .moves-row { flex-direction: column; gap: var(--gap-lg); }
  .auction-cta { flex-direction: column; align-items: flex-start; gap: var(--gap); padding: var(--space-gutter); }
  .auction-cta__action { align-items: flex-start; }
  .stat__num { font-size: 16px; }
  .stats-visual__medal { width: 64px; height: 64px; }
  .hero-vs { gap: 8px; }
  .avatar-card__img { width: 72px; height: 72px; }
}


/* ==========================================================================
   Round 2, 17 Aug 2026: form step two, scroll reveals, live shader hosts.
   ========================================================================== */

/* --- Scroll reveals --------------------------------------------------------
   States exist only when script has run (html.js) and motion is welcome.
   The auction photos take the glass entry: in from the right, blur
   settling to sharp, like a pane sliding into place. */

/* Reveals run as animations, not transitions. A transition shorthand here
   outranks each component's own transition rule and silently wipes out its
   hover easing, which is what made the story and step cards snap. Animations
   touch no transition property, so hovers keep their own timing. The stagger
   arrives as animation-delay, and fill mode "both" holds the from-state
   through the delay so nothing flashes before its turn. */
/* data-reveals="rescued" is set by the head script on a page that declares
   its reveal classes in markup, when page.js has not arrived in three
   seconds. It switches the from-state off document-wide, which is the only
   version of the rescue that also covers markup still parsing. A page whose
   classes are added at runtime never gets the attribute and never needs it,
   so this selector reads the same as before there. */
@media (prefers-reduced-motion: no-preference) {
  .js:not([data-reveals="rescued"]) .reveal,
  .js:not([data-reveals="rescued"]) .reveal-glass { opacity: 0; }
  .js .reveal.revealed { animation: reveal-up 700ms ease-out both; }
  .js .reveal-glass.revealed { animation: reveal-glass 1400ms cubic-bezier(0.22, 1, 0.36, 1) both; }
}

@keyframes reveal-up {
  from { opacity: 0; translate: 0 24px; }
  to   { opacity: 1; translate: 0 0; }
}

@keyframes reveal-glass {
  from { opacity: 0; translate: 96px 0; filter: blur(14px) saturate(1.15); }
  to   { opacity: 1; translate: 0 0; filter: none; }
}

/* --- Live shader hosts -----------------------------------------------------
   Each host is layered exactly over its exported still; the still fades
   once the canvas is up and remains the fallback until then. */

.shader-host { position: absolute; z-index: 2; }
.shader-host canvas { width: 100%; height: 100%; display: block; }

.still-behind-shader { transition: opacity 600ms ease; opacity: 0 !important; }

.prestige-visual { position: relative; }
.prestige-visual .shader-host {
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  width: 148%;
  aspect-ratio: 809 / 607;
}

.shader-host--band { position: absolute; inset: 0; z-index: 0; }

/* ==========================================================================
   Round 5, 18 Aug 2026: mobile refinements and the closer band.
   ========================================================================== */

/* --- Closer band, desktop and mobile --------------------------------------- */

.closer { position: relative; background: var(--bg-brand-hover); overflow: clip; }
.closer__wrap {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--gap);
}
.closer h2 { color: var(--text-on-brand); max-width: 560px; }
.closer__body { color: var(--text-on-brand-muted); max-width: 56ch; }
.closer__btn { min-width: 280px; }
.closer__note { font-size: var(--type-micro); color: var(--text-on-brand-muted); }
/* The ticker closes the band. Full width of the wrap, same mask and loop
   as the hero strip, with a hairline above it so it reads as a footnote
   of proof rather than another content row. */
.closer__ticker {
  width: 100%;
  margin-top: var(--gap-sm);
  padding-top: var(--space-gutter);
  border-top: 1px solid var(--border-on-brand);
}

/* --- Fixed mobile CTA -------------------------------------------------------
   Liquid glass: translucent brand ink over a strong backdrop blur. Slides
   up only when neither capture form is on screen (script toggles .is-up).
   Colours are token-derived mixes, same pattern as the pressed states. */

.mobile-cta { display: none; }

@media (max-width: 767px) {
  .js .mobile-cta {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 60;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 10px var(--space-gutter) calc(12px + env(safe-area-inset-bottom, 0px));
    background: color-mix(in oklab, var(--bg-brand-deep), transparent 42%);
    -webkit-backdrop-filter: blur(22px) saturate(1.35);
    backdrop-filter: blur(22px) saturate(1.35);
    border-top: 1px solid color-mix(in oklab, var(--text-on-brand), transparent 82%);
    /* 16px, client-directed 18 Aug 2026. Outside the two-token radius
       scale (card 8, pill 999), so it needs a radius token in Figma before
       this ships. Recorded in HANDOVER.md. */
    border-radius: 16px 16px 0 0;
    translate: 0 120%;
    transition: translate 460ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  .js .mobile-cta.is-up { translate: 0 0; }
}

/* The hint centres in the bar; the POC toggle is taken out of flow so
   deleting it leaves the live layout byte-identical. */
.mobile-cta__top {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}
.mobile-cta__hint {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--type-micro);
  line-height: 16px;
  color: var(--text-on-brand);
}
.mobile-cta__hint strong { font-weight: var(--weight-semibold); }
.mobile-cta__dot {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-pill);
  background: var(--border-success);
  animation: cta-dot 2.4s ease-in-out infinite;
}
/* Nobody signed in: the dot stops pulsing and goes muted, and the button
   asks for a callback instead of a conversation. */
.mobile-cta.is-offline .mobile-cta__dot {
  background: var(--text-on-brand-muted);
  animation: none;
}
@keyframes cta-dot {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* The shine: a conic highlight orbiting the button border. The masked
   pseudo element is the ring; only its gradient angle animates. Browsers
   without @property keep a static highlight, which reads as a lit edge. */
@property --shine {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
.mobile-cta__btn { position: relative; width: 100%; }
.mobile-cta__btn::after {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: calc(var(--radius-card) + 2px);
  padding: 2px;
  background: conic-gradient(
    from var(--shine),
    transparent 0turn,
    color-mix(in oklab, var(--text-on-brand), transparent 25%) 0.10turn,
    transparent 0.22turn,
    transparent 0.5turn,
    color-mix(in oklab, var(--action-prestige), transparent 35%) 0.60turn,
    transparent 0.72turn
  );
  -webkit-mask: linear-gradient(rgb(0,0,0) 0 0) content-box, linear-gradient(rgb(0,0,0) 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(rgb(0,0,0) 0 0) content-box, linear-gradient(rgb(0,0,0) 0 0);
  mask-composite: exclude;
  animation: cta-shine 6s linear infinite;
  pointer-events: none;
}
@keyframes cta-shine {
  to { --shine: 360deg; }
}

@media (prefers-reduced-motion: reduce) {
  .mobile-cta__btn::after, .mobile-cta__dot { animation: none; }
}

/* --- Mobile alignment pass --------------------------------------------------
   Requested 18 Aug 2026: centre the hero stack, the stats copy, the
   auction CTA card and the prestige band on small screens only. */

@media (max-width: 767px) {
  /* H1: four lines rather than three, see .br--mobile above. */
  .br--desktop { display: none; }
  .br--mobile { display: inline; }

  /* Hero: everything centres. */
  .hero-copy { text-align: center; }
  .hero-copy-top { align-items: center; }
  .hero-title-block { align-items: center; }
  .hero-vs { justify-content: center; }
  .hero-sub { display: flex; flex-direction: column; align-items: center; }
  .hero-awards { align-items: center; }
  .hero-awards__label { text-align: center; width: auto; }

  /* Truth cards pin higher so each stays readable longer before the next
     slides over it. */
  .truth-card:nth-child(1) { top: 24px; }
  .truth-card:nth-child(2) { top: 36px; }
  .truth-card:nth-child(3) { top: 48px; }
  .truth-card:nth-child(4) { top: 60px; }
  .truth-card:nth-child(5) { top: 72px; }

  /* Stats band copy and action centre. */
  .stats-copy { align-items: center; text-align: center; }
  .stats-copy__head { align-items: center; }
  .stats-points { align-items: center; }
  .stats-point { justify-content: center; }
  .stats-cta { align-items: center; }

  /* Auction CTA card centres. */
  .auction-cta { align-items: center; text-align: center; }
  .auction-cta__action { align-items: center; }

  /* Prestige: centred copy, stacked full-width actions. */
  .prestige-copy { align-items: center; text-align: center; }
  .prestige-actions { flex-direction: column; align-items: stretch; width: 100%; }
  .prestige-actions .btn { width: 100%; }

  /* Closer button stretches like the prestige actions. */
  .closer__btn { width: 100%; min-width: 0; }
}

/* ==========================================================================
   POC ONLY, 18 Aug 2026. DELETE THIS BLOCK BEFORE LAUNCH.
   Demo switch for the fixed mobile CTA, so the nobody-available state can be
   shown in a client meeting. Renders only under body[data-poc="true"], so
   dropping that attribute hides it without touching this file. It is not a
   product control: it rewrites an availability claim, which no visitor may
   do. Tracked in HANDOVER.md and README.md.
   ========================================================================== */

.poc-toggle { display: none; }

body[data-poc="true"] .poc-toggle {
  position: absolute;
  right: 0;
  top: 50%;
  translate: 0 -50%;
  /* 44 high for the tap-target rule, with a smaller visual track inside. */
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  min-height: var(--tap-min);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.poc-toggle__track {
  position: relative;
  display: block;
  width: 34px;
  height: 20px;
  border-radius: var(--radius-pill);
  background: var(--border-success);
  transition: background-color 260ms ease-out;
}
.poc-toggle__knob {
  position: absolute;
  left: 16px;
  top: 2px;
  width: 16px;
  height: 16px;
  border-radius: var(--radius-pill);
  background: var(--text-on-brand);
  transition: left 260ms cubic-bezier(0.22, 1, 0.36, 1);
}
.poc-toggle[aria-checked="false"] .poc-toggle__track { background: var(--text-on-brand-muted); }
.poc-toggle[aria-checked="false"] .poc-toggle__knob { left: 2px; }
.poc-toggle:focus-visible { outline: 2px solid var(--text-on-brand); outline-offset: 2px; border-radius: var(--radius-pill); }

@media (prefers-reduced-motion: reduce) {
  .poc-toggle__track, .poc-toggle__knob { transition: none; }
}

/* ============================= end POC ONLY ============================== */

