/* ==========================================================================
   upsell-promo-v1

   There is deliberately NO :root token block in this file. Tokens are injected
   per page by the build, derived from one brand hex plus a light/dark flag.
   Consequences, both enforced by `lintStylesheet`:
     - no hex literals in here, ever
     - no raw rgba(); alpha comes from token triplets, rgb(var(--x-rgb) / .18)
   That is the exact gap that made the donor page un-rebrandable (59 brand-tinted
   rgba() calls living outside its token block).
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 5rem;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img, svg, video, iframe { display: block; max-width: 100%; }
img { height: auto; }

a { color: var(--accent); text-underline-offset: 2px; }

h1, h2, h3 {
  font-family: var(--font-display);
  margin: 0;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
  font-weight: 700;
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); letter-spacing: -0.01em; }
p  { margin: 0; text-wrap: pretty; }

:where(a, button, summary, input):focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: 3px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--surface);
  color: var(--text);
  padding: 0.75rem 1rem;
  z-index: 50;
}
.skip:focus { left: 0; }

/* ---------- type scale ---------- */

:where(body) {
  --fs-base: clamp(1rem, 0.97rem + 0.16vw, 1.09rem);
  --fs-sm:   clamp(0.84rem, 0.82rem + 0.1vw, 0.9rem);
  --fs-h3:   clamp(1.1rem, 1.04rem + 0.28vw, 1.28rem);
  --fs-h2:   clamp(1.7rem, 1.4rem + 1.3vw, 2.6rem);
  --fs-h1:   clamp(2.2rem, 1.65rem + 2.7vw, 3.7rem);
}

/* ---------- layout ---------- */

.wrap {
  width: 100%;
  max-width: 86rem;
  margin-inline: auto;
  padding-inline: clamp(1.15rem, 4vw, 3rem);
}
.wrap.narrow { max-width: 52rem; }

.section { padding-block: clamp(3.5rem, 8vw, 6.5rem); }

.band {
  padding-block: clamp(3.5rem, 8vw, 6.5rem);
  background: var(--surface);
  border-block: 1px solid var(--border);
}

.band-accent {
  position: relative;
  overflow: hidden;
  /* clip-path (not overflow) is what clips a position:fixed descendant, which
     is how the backdrop stays pinned to the viewport and the section becomes a
     window that reveals it while scrolling. */
  clip-path: inset(0);
  background: rgb(var(--accent-tint-rgb) / 0.5);
  border-block-color: rgb(var(--accent-fill-rgb) / 0.16);
}

/*
 * Data-network backdrop. The asset is an alpha MASK rather than an image, so the
 * fill is the brand accent and it recolours per white label — a literal dark-navy
 * render would have clashed with both the light ground and any non-blue brand.
 * Faded out toward the bottom so the stat tiles sit on clean ground.
 */
.data-backdrop {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--accent-fill);
  opacity: 0.16;
  -webkit-mask-image: var(--mask), linear-gradient(to bottom, black 45%, transparent 92%);
  mask-image: var(--mask), linear-gradient(to bottom, black 45%, transparent 92%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center top;
  mask-position: center top;
  -webkit-mask-size: cover, 100% 100%;
  mask-size: cover, 100% 100%;
}

/* Scroll reveal: on wide screens the backdrop is pinned to the viewport and
   the section scrolls over it like a window. The mask's bottom fade becomes
   viewport-relative, which reads as the map dissolving ahead of the scroll.
   Small screens and reduced-motion keep the calm absolute version. */
@media (min-width: 56rem) and (prefers-reduced-motion: no-preference) {
  .data-backdrop { position: fixed; }
}

.band-accent > .wrap { position: relative; }

/* Deep band: a derived guaranteed-dark surface in the brand's hue (--deep),
   never a raw brand color — a brand whose alt color is light would wash it out. */
.band-dark {
  background:
    radial-gradient(60rem 26rem at 85% -20%, rgb(var(--accent-fill-rgb) / 0.3), transparent 60%),
    var(--deep);
  border-block: none;
  color: var(--on-deep);
}

.section > .wrap > h2,
.band > .wrap > h2 { margin-bottom: 0.6rem; }

.section-lead {
  color: var(--text-dim);
  max-width: 46rem;
  margin-bottom: clamp(1.9rem, 4vw, 3rem);
}
/* Heading block sits in .wrap while the marquee runs full-bleed beneath it */
.section-lead-flush { margin-bottom: clamp(1.5rem, 3vw, 2.25rem); }

.eyebrow {
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.9rem;
}

.ico { width: 1.15em; height: 1.15em; flex: none; }

/* ---------- reveal (JS adds data-js on <html>; no JS = no hidden content) ---------- */

html[data-js] .reveal {
  opacity: 0;
  translate: 0 18px;
  transition: opacity 600ms ease, translate 600ms ease;
  transition-delay: calc(var(--d, 0) * 70ms);
}
html[data-js] .reveal.in { opacity: 1; translate: 0 0; }

@media (prefers-reduced-motion: reduce) {
  html[data-js] .reveal { opacity: 1; translate: none; transition: none; }
}

/* ---------- top bar ---------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgb(var(--bg-rgb) / 0.88);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}

.topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 4rem;
}

.brand-logo { width: auto; max-height: 2.25rem; object-fit: contain; object-position: left center; }

.topbar-deadline {
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 0.4rem 0.8rem;
  border-radius: 999px;
  color: var(--accent);
  background: rgb(var(--accent-fill-rgb) / 0.1);
  border: 1px solid rgb(var(--accent-fill-rgb) / 0.28);
  white-space: nowrap;
}

/* ---------- hero ---------- */

.hero {
  padding-block: clamp(3rem, 7vw, 6rem);
  position: relative;
  overflow: hidden;
}

.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(58rem 30rem at 10% -12%, rgb(var(--accent-fill-rgb) / 0.12), transparent 62%),
    radial-gradient(42rem 26rem at 98% 15%, rgb(var(--accent-fill-alt-rgb) / 0.1), transparent 60%);
  pointer-events: none;
}

.hero-inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}

.hero-copy > * + * { margin-top: 1.35rem; }

.lead {
  font-size: clamp(1.08rem, 1rem + 0.35vw, 1.28rem);
  color: var(--text-dim);
  max-width: 30rem;
}

/* Compact one-line price under the CTA — the full treatment lives in the offer card */
.price-line {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  flex-wrap: wrap;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.price-now {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.price-was { color: var(--text-muted); font-variant-numeric: tabular-nums; }

.hero-media { position: relative; }

/* The frame is the positioning context for the investor pins, and it owns the
   aspect ratio so a taller crop fills the column without distorting the art. */
.hero-frame {
  position: relative;
  border-radius: 20px;
  overflow: hidden;
  border: 1px solid var(--border);
  box-shadow: 0 34px 74px -30px rgb(var(--text-rgb) / 0.4);
  background: var(--surface-2);
  aspect-ratio: 108 / 124;
}

.hero-frame > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---------- hero entrance ---------- */

/*
 * Runs on load from CSS alone — no JS, so it cannot leave content stranded if a
 * script fails. The global prefers-reduced-motion rule collapses the duration,
 * and `both` fill means the end state (visible) is what survives.
 *
 * The copy fades and rises; the frame moves WITHOUT an opacity ramp on purpose.
 * It holds the LCP image, and starting it transparent would delay the largest
 * paint for the sake of a flourish. Transform-only keeps it painted from frame one.
 */
@keyframes hero-rise {
  from { opacity: 0; translate: 0 16px; }
  to   { opacity: 1; translate: 0 0; }
}

@keyframes hero-frame-in {
  from { translate: 0 26px; }
  to   { translate: 0 0; }
}

.hero-copy > * {
  animation: hero-rise 640ms cubic-bezier(0.2, 0.75, 0.3, 1) both;
  animation-delay: calc(80ms + var(--seq, 0) * 90ms);
}
.hero-copy > :nth-child(1) { --seq: 0; }
.hero-copy > :nth-child(2) { --seq: 1; }
.hero-copy > :nth-child(3) { --seq: 2; }
.hero-copy > :nth-child(4) { --seq: 3; }
.hero-copy > :nth-child(5) { --seq: 4; }
.hero-copy > :nth-child(6) { --seq: 5; }

.hero-frame { animation: hero-frame-in 820ms cubic-bezier(0.2, 0.75, 0.3, 1) both; }

/* Lands after the frame has settled, so it reads as arriving onto the image */
.hero-float { animation: hero-rise 620ms cubic-bezier(0.2, 0.75, 0.3, 1) 620ms both; }

/* ---------- investor pins ---------- */

.hero-pins { position: absolute; inset: 0; pointer-events: none; }

.hero-pin {
  position: absolute;
  left: var(--x);
  top: var(--y);
  translate: -50% -50%;
  display: grid;
  place-items: center;
  width: 3.4rem;
  height: 3.4rem;
  border-radius: 999px;
  color: var(--accent);
  /* Softer than a solid chip so the rooftop still reads through it */
  background: rgb(var(--surface-rgb) / 0.68);
  backdrop-filter: blur(3px);
  box-shadow: 0 8px 22px -6px rgb(var(--scrim-rgb) / 0.5);
}
.hero-pin .ico { width: 1.7rem; height: 1.7rem; }

/* Pulse ring reads as a live signal on the map */
.hero-pin::before {
  content: "";
  position: absolute;
  inset: -0.55rem;
  border-radius: 999px;
  border: 2.5px solid rgb(var(--accent-fill-rgb) / 0.5);
}

html[data-js] .hero-pin {
  opacity: 0;
  scale: 0.4;
  transition: opacity 420ms cubic-bezier(0.2, 1.3, 0.4, 1), scale 420ms cubic-bezier(0.2, 1.3, 0.4, 1);
  transition-delay: calc(900ms + var(--i) * 150ms);
}
html[data-js] .hero-frame.in .hero-pin { opacity: 1; scale: 1; }

html[data-js] .hero-frame.in .hero-pin::before {
  animation: pin-pulse 2.6s ease-out infinite;
  animation-delay: calc(1500ms + var(--i) * 150ms);
}

@keyframes pin-pulse {
  0%   { scale: 0.7; opacity: 0.9; }
  70%  { scale: 2.15; opacity: 0; }
  100% { scale: 2.15; opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  html[data-js] .hero-pin { opacity: 1; scale: 1; transition: none; }
  html[data-js] .hero-frame.in .hero-pin::before { animation: none; }
}

/* Floating demand-proof chip anchored to the hero image */
.hero-float {
  position: absolute;
  left: clamp(-1.25rem, -2vw, -2rem);
  bottom: 1.4rem;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: var(--fs-sm);
  color: var(--text);
  background: rgb(var(--surface-rgb) / 0.92);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  padding: 0.7rem 1rem;
  box-shadow: 0 16px 40px -18px rgb(var(--text-rgb) / 0.35);
  max-width: min(88%, 20rem);
}
.hero-float .ico { color: var(--accent); }
.hero-float strong { font-variant-numeric: tabular-nums; }

.hero-expired .hero-inner { grid-template-columns: minmax(0, 1fr); }
.hero-expired::before { opacity: 0.35; }

/* ---------- video band ---------- */

.video-head { margin-bottom: clamp(1.25rem, 3vw, 2rem); }

.eyebrow-ondark { color: var(--on-deep); opacity: 0.75; }

.video-h { color: var(--on-deep); text-wrap: balance; }

.video-lead { margin-top: 0.5rem; max-width: 46rem; color: rgb(var(--on-deep-rgb) / 0.75); text-wrap: pretty; }

.video-shell {
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 40px 90px -35px rgb(var(--scrim-rgb) / 0.9);
  aspect-ratio: 16 / 9;
  background: var(--deep);
}

.video-facade {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  cursor: pointer;
  background: none;
  font-family: inherit;
}
.video-facade img { width: 100%; height: 100%; object-fit: cover; }

.video-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgb(var(--scrim-rgb) / 0.66), rgb(var(--scrim-rgb) / 0.04) 55%);
  transition: background 200ms ease;
}

.video-play {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  display: grid;
  place-items: center;
  width: clamp(5rem, 10vw, 6.75rem);
  height: clamp(5rem, 10vw, 6.75rem);
  border-radius: 999px;
  background: var(--accent-fill);
  color: var(--on-accent);
  box-shadow: 0 0 0 10px rgb(var(--accent-fill-rgb) / 0.25), 0 18px 40px -12px rgb(var(--accent-fill-rgb) / 0.7);
  transition: scale 180ms ease;
}
.video-play .ico { width: 40%; height: 40%; margin-left: 6%; }

html[data-js] .video-play {
  animation: play-pulse 2.6s cubic-bezier(0.3, 0.6, 0.4, 1) infinite;
}

.video-facade:hover .video-play { animation-play-state: paused; }

@keyframes play-pulse {
  0% { box-shadow: 0 0 0 10px rgb(var(--accent-fill-rgb) / 0.25), 0 0 0 10px rgb(var(--accent-fill-rgb) / 0.3), 0 18px 40px -12px rgb(var(--accent-fill-rgb) / 0.7); }
  65% { box-shadow: 0 0 0 10px rgb(var(--accent-fill-rgb) / 0.25), 0 0 0 46px rgb(var(--accent-fill-rgb) / 0), 0 18px 40px -12px rgb(var(--accent-fill-rgb) / 0.7); }
  100% { box-shadow: 0 0 0 10px rgb(var(--accent-fill-rgb) / 0.25), 0 0 0 46px rgb(var(--accent-fill-rgb) / 0), 0 18px 40px -12px rgb(var(--accent-fill-rgb) / 0.7); }
}

@media (prefers-reduced-motion: reduce) {
  html[data-js] .video-play { animation: none; }
}

.video-facade:hover .video-play { scale: 1.07; }
.video-facade:hover .video-scrim { background: linear-gradient(to top, rgb(var(--scrim-rgb) / 0.55), transparent 55%); }

.video-frame { width: 100%; height: 100%; border: 0; }

/* ---------- feature row (in the dark band) ---------- */

.feature-row {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13.5rem, 1fr));
  gap: clamp(1.25rem, 3vw, 2.25rem);
}

.feature h3 { margin-bottom: 0.35rem; color: var(--on-deep); }
.feature p { font-size: var(--fs-sm); color: rgb(var(--on-deep-rgb) / 0.72); }

.feature-ico {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  margin-bottom: 0.9rem;
  border-radius: 12px;
  background: rgb(var(--on-deep-rgb) / 0.1);
  border: 1px solid rgb(var(--on-deep-rgb) / 0.16);
  color: var(--on-deep);
}
.feature-ico .ico { width: 1.4rem; height: 1.4rem; }

/* ---------- lead type cards ---------- */

/* Four across, with the featured card spanning the whole row beneath them —
   a fifth card on a line of its own is what this replaces. */
.lead-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.4rem;
}

.lead-grid[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }

@media (max-width: 74rem) { .lead-grid, .lead-grid[data-cols="3"] { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 40rem) { .lead-grid { grid-template-columns: minmax(0, 1fr); } }

/* ---------- matched-investor circles ---------- */

/*
 * Illustrates the feature literally: portraits surfacing on a property the way
 * matches would. Decorative, so the container is aria-hidden and the copy beside
 * it carries the meaning. Positioned in % of the media box.
 */
.match-pins { position: absolute; inset: 0; pointer-events: none; }

.match-pin {
  position: absolute;
  left: var(--x);
  top: var(--y);
  translate: -50% -50%;
  width: 4.6rem;
  height: 4.6rem;
  border-radius: 999px;
  overflow: hidden;
  border: 3px solid var(--surface);
  box-shadow: 0 10px 24px -5px rgb(var(--scrim-rgb) / 0.6);
}
.match-pin img { width: 100%; height: 100%; object-fit: cover; }

/* Accent halo, so they read as system-surfaced matches rather than stray photos */
.match-pin::after {
  content: "";
  position: absolute;
  inset: -0.5rem;
  border-radius: 999px;
  border: 2px solid rgb(var(--accent-fill-rgb) / 0.55);
}

/* Pops in when the block scrolls into view — .in already comes from the reveal
   observer, so this needs no extra JS. */
html[data-js] .match-pin {
  opacity: 0;
  scale: 0.35;
  transition: opacity 460ms cubic-bezier(0.2, 1.3, 0.4, 1), scale 460ms cubic-bezier(0.2, 1.3, 0.4, 1);
  transition-delay: calc(260ms + var(--i) * 150ms);
}

@media (prefers-reduced-motion: reduce) {
  html[data-js] .match-pin { opacity: 1; scale: 1; transition: none; }
}

@media (max-width: 40rem) {
  .match-pin { width: 3.7rem; height: 3.7rem; border-width: 2.5px; }
  .match-pin::after { inset: -0.38rem; }
}

.lead-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: translate 200ms ease, box-shadow 200ms ease;
}
.lead-card:hover {
  translate: 0 -4px;
  box-shadow: 0 24px 50px -24px rgb(var(--text-rgb) / 0.3);
}

.lead-media {
  position: relative;
  aspect-ratio: 11 / 7;
  overflow: hidden;
}
.lead-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: scale 400ms ease;
}
.lead-card:hover .lead-media img { scale: 1.05; }

/* Text sits ON the image, so the overlay is a fixed dark scrim independent of
   theme — white type on it is a constant, not a token pairing. */
.lead-media-overlay {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, rgb(var(--scrim-rgb) / 0.82), rgb(var(--scrim-rgb) / 0.18) 55%, rgb(var(--scrim-rgb) / 0.05)),
    linear-gradient(to right, rgb(var(--accent-fill-rgb) / 0.28), transparent 60%);
}

.lead-num {
  position: absolute;
  top: 0.9rem;
  left: 1rem;
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--on-scrim);
  background: rgb(var(--scrim-rgb) / 0.45);
  backdrop-filter: blur(6px);
  border: 1px solid rgb(var(--on-scrim-rgb) / 0.28);
  border-radius: 999px;
  padding: 0.22rem 0.65rem;
  font-variant-numeric: tabular-nums;
}

.lead-title {
  position: absolute;
  left: 1rem;
  right: 1rem;
  bottom: 0.85rem;
  color: var(--on-scrim);
  font-size: clamp(1.2rem, 1.1rem + 0.4vw, 1.45rem);
  text-shadow: 0 2px 12px rgb(var(--scrim-rgb) / 0.6);
}

.lead-body {
  padding: 1.25rem 1.4rem 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  flex: 1;
}

.lead-desc { color: var(--text-dim); font-size: var(--fs-sm); }

.ticks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.ticks li { display: flex; gap: 0.55rem; align-items: flex-start; font-size: var(--fs-sm); }
.ticks .ico { color: var(--accent); margin-top: 0.28em; }

.lead-for {
  margin-top: auto;
  padding-top: 0.9rem;
  border-top: 1px solid var(--border);
  font-size: var(--fs-sm);
  color: var(--text-dim);
}

/* ---------- data stats ---------- */

.data-head {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 1.5rem 3rem;
  flex-wrap: wrap;
  margin-bottom: clamp(1.9rem, 4vw, 3rem);
}
.data-head h2 { margin: 0; }
.data-lead { color: var(--text-dim); max-width: 30rem; font-size: var(--fs-sm); }

.stats {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 1rem;
}

.stat {
  background: var(--surface);
  border: 1px solid rgb(var(--accent-fill-rgb) / 0.18);
  border-radius: 14px;
  padding: 1.3rem 1.35rem 1.15rem;
}

.stat dd {
  margin: 0 0 0.15rem;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 1.35rem + 1vw, 2.3rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  line-height: 1.1;
}

.stat dt {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-muted);
}

/* ---------- day-to-day split ---------- */

.day-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(2rem, 6vw, 5rem);
  align-items: center;
}

/* Offset pair with a dot matrix bleeding between them. The grid is two columns
   and three rows; each shot spans two rows, staggered by one, which produces the
   overlap without any absolute positioning. */
.day-media {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(3, auto);
  gap: 0 clamp(0.75rem, 2vw, 1.35rem);
  position: relative;
}

.day-shot { position: relative; z-index: 1; margin: 0; border-radius: 18px; overflow: hidden; }

.day-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: 1px solid var(--border);
  border-radius: 18px;
  box-shadow: 0 28px 64px -30px rgb(var(--text-rgb) / 0.4);
}

.day-shot-1 { grid-column: 1; grid-row: 1 / span 2; }
.day-shot-2 { grid-column: 2; grid-row: 2 / span 2; }

/* Row 3 begins exactly at the first shot's bottom edge, so a negative offset
   slides the matrix up behind it — the top rows read as tucked under the photo
   while the rest fills the empty bottom-left quadrant. */
.day-dots {
  grid-column: 1;
  grid-row: 3;
  align-self: start;
  justify-self: start;
  z-index: 0;
  margin-top: -2.75rem;
  width: min(13.5rem, 82%);
  aspect-ratio: 6 / 5;
  background-image: radial-gradient(circle, var(--accent-fill) 30%, transparent 32%);
  background-size: 1.45rem 1.45rem;
  /* whole dots only — round flexes the tile size so the box edge never slices a column */
  background-repeat: round;
  opacity: 0.9;
}

.day-copy h2 { margin-bottom: 1.5rem; }

.benefits { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }

.benefits li {
  display: flex;
  gap: 1rem;
  align-items: flex-start;
  padding-block: 0.95rem;
}
.benefits li + li { border-top: 1px solid var(--border); }

.benefit-ico {
  display: grid;
  place-items: center;
  flex: none;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 10px;
  background: rgb(var(--accent-fill-rgb) / 0.1);
  color: var(--accent);
  margin-top: 0.1rem;
}
.benefit-ico .ico { width: 1.2rem; height: 1.2rem; }

.benefits div { display: flex; flex-direction: column; gap: 0.12rem; }
.benefits span { color: var(--text-dim); font-size: var(--fs-sm); }

/* ---------- testimonials: infinite marquee ---------- */

.marquee {
  /* Sits outside .wrap so it is full-bleed structurally — no 100vw/negative
     margin hack, which also keeps it correct wherever vw is unreliable. */
  overflow: hidden;
  /* Fade the ends so cards enter and leave instead of popping */
  mask-image: linear-gradient(to right, transparent, black 6%, black 94%, transparent);
}

.marquee-track {
  display: flex;
  width: max-content;
  /* No column-gap: each card carries its own trailing margin so the two halves
     tile identically and the -50% loop is seamless. */
  animation: marquee-scroll 68s linear infinite;
}

@keyframes marquee-scroll {
  from { translate: 0 0; }
  to   { translate: -50% 0; }
}

.marquee:hover .marquee-track,
.marquee:focus-within .marquee-track { animation-play-state: paused; }

/* Fixed rem width: the track is sized by max-content, so a viewport-relative
   width here would be both circular-ish and needless. */
.marquee .quote {
  flex: none;
  width: 23rem;
  margin: 0 1.25rem 0 0;
}

@media (max-width: 34rem) { .marquee .quote { width: 17.5rem; } }

.quote {
  position: relative;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1.6rem 1.5rem 1.35rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.quote::before {
  content: "\201C";
  position: absolute;
  top: 0.4rem;
  right: 1rem;
  font-family: var(--font-display);
  font-size: 4.5rem;
  line-height: 1;
  color: rgb(var(--accent-fill-rgb) / 0.18);
  pointer-events: none;
}

.quote p { font-size: var(--fs-sm); position: relative; }

.quote cite {
  margin-top: auto;
  font-style: normal;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text-dim);
}
.quote cite::before {
  content: "";
  display: block;
  width: 1.75rem;
  border-top: 2px solid rgb(var(--accent-fill-rgb) / 0.5);
  margin-bottom: 0.6rem;
}

/* No animation: become a normal swipeable row and drop the duplicate set. */
@media (prefers-reduced-motion: reduce) {
  .marquee { overflow-x: auto; mask-image: none; }
  .marquee-track { animation: none; }
  .marquee .quote[aria-hidden="true"] { display: none; }
}

/* ---------- offer: two-panel checkout ---------- */

/*
 * No `overflow: hidden` here, deliberately. It used to clip the rounded corners
 * and the bleeding watermark in one go — but an overflow-hidden ancestor
 * becomes the scrollport for any sticky descendant, and a scrollport that never
 * scrolls means the panel never sticks. So the clipping moves down to the two
 * children, which each own the corners they actually touch.
 */
.checkout {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 24px;
  box-shadow: 0 36px 90px -46px rgb(var(--text-rgb) / 0.4);
}

.checkout-main {
  position: relative;
  padding: clamp(1.75rem, 4vw, 3rem);
  border-radius: 24px 0 0 24px;
  overflow: hidden;   /* crops the watermark bleeding off the left edge */
}

/* The offer glyph as a watermark, bleeding off the panel's left edge — the
   crop comes from .checkout's existing overflow:hidden, so nothing here needs
   to know the box's size. Sized by height (75%) with width:auto, then pulled
   left by 58% of its OWN width, which keeps the visible slice consistent
   whatever the glyph's aspect ratio turns out to be for a future offer. */
.checkout-watermark {
  position: absolute;
  top: 50%;
  left: 0;
  translate: var(--watermark-shift, -48%) -50%;
  height: var(--watermark-height, 75%);
  width: auto;
  z-index: 0;
  color: rgb(var(--watermark-rgb) / var(--watermark-alpha));
  pointer-events: none;
}

/* Absolutely positioned siblings paint above in-flow content by default, so the
   copy has to be lifted explicitly rather than relying on document order. */
.checkout-main > :not(.checkout-watermark) { position: relative; z-index: 1; }
.checkout-main h2 { margin-bottom: 0.75rem; }
.checkout-lead { color: var(--text-dim); max-width: 34rem; margin-bottom: 1.6rem; }

/* interpolate-size inherits and is scoped here rather than to :root, so the
   auto-keyword opt-in reaches the ::details-content transition below and
   nothing else on the page. Same arrangement as the Lien Data docket. */
.includes { list-style: none; margin: 0; padding: 0; display: block; interpolate-size: allow-keywords; }

.inc-group {
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
  padding: 1.4rem 0 0.4rem;
  border-bottom: 1px solid var(--border);
}
.inc-group:first-child { padding-top: 0; }

.inc { border-bottom: 1px solid var(--border); }
.inc:last-child { border-bottom: none; }

.inc-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.85rem 0.15rem;
  cursor: default;
  list-style: none;
}
.inc-row::-webkit-details-marker { display: none; }
details .inc-row { cursor: pointer; }

.includes .ico { color: var(--accent); flex: 0 0 auto; width: 1.1em; height: 1.1em; }

.inc-name { flex: 1 1 auto; font-weight: 700; font-size: var(--fs-base); color: var(--text); }

.inc-more {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-muted);
  border: 1px solid var(--border-strong);
  border-radius: 1rem;
  padding: 0.22rem 0.6rem;
  transition: color 0.15s, border-color 0.15s;
}
.inc-more .ico {
  color: inherit;
  width: 0.95em;
  height: 0.95em;
  transition: rotate 320ms cubic-bezier(0.34, 1.35, 0.5, 1);
}
details .inc-row:hover .inc-more { color: var(--accent); border-color: var(--accent); }
details[open] .inc-more { color: var(--accent); border-color: rgb(var(--accent-rgb) / 0.45); background: rgb(var(--accent-tint-rgb) / 0.7); }
/* the info mark turns with the disclosure, so the pill reads as a control that
   moved rather than one that merely changed colour */
details[open] .inc-more .ico { rotate: 180deg; }

.inc-d summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 0.4rem; }

/*
 * "What's this?" used to snap its answer open at full height. Two layers now,
 * the same pair the Lien Data docket uses and for the same reason — neither one
 * can do the whole job on its own:
 *
 *   1. ::details-content animates the BOX from zero to auto. Needs
 *      `interpolate-size` (set on .includes) and `allow-discrete` for the
 *      content-visibility flip. Browsers without ::details-content ignore this
 *      block entirely and open instantly, exactly as before — it degrades
 *      rather than breaking.
 *   2. The paragraph fades and rises on a keyframe ANIMATION, not a transition:
 *      the element goes from display:none to displayed, and a transition has no
 *      start value to run from. This half works everywhere, so the text stops
 *      appearing abruptly even where the height still snaps.
 *
 * This is shared machinery — every offer's checkout list gets it.
 */
.inc-d::details-content {
  block-size: 0;
  overflow: clip;
  opacity: 0;
  transition:
    block-size 300ms cubic-bezier(0.3, 0.8, 0.35, 1),
    opacity 220ms ease,
    content-visibility 300ms allow-discrete;
}
.inc-d[open]::details-content {
  block-size: auto;
  opacity: 1;
}

@keyframes inc-open {
  from { opacity: 0; translate: 0 -0.4rem; }
  to   { opacity: 1; translate: 0 0; }
}

.inc-blurb {
  margin: 0 0 0.9rem 1.8em;
  font-size: var(--fs-sm);
  color: var(--text-dim);
  max-width: 34rem;
  text-wrap: pretty;
}
.inc-d[open] .inc-blurb {
  animation: inc-open 320ms cubic-bezier(0.2, 0.7, 0.3, 1) 30ms both;
}

@media (prefers-reduced-motion: reduce) {
  .inc-more .ico { transition: none; }
  .inc-d::details-content { transition: none; }
  .inc-d[open] .inc-blurb { animation: none; }
}

.checkout-panel {
  padding: clamp(1.75rem, 4vw, 2.5rem);
  background: rgb(var(--accent-tint-rgb) / 0.6);
  border-left: 1px solid rgb(var(--accent-fill-rgb) / 0.2);
  border-radius: 0 24px 24px 0;
}

.checkout-panel-inner {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

/*
 * The includes list runs to ten lien types plus two extras, so on desktop the
 * left column is roughly twice the height of the right one and the buy button
 * scrolls away long before the reading does. The INNER block sticks rather than
 * the panel itself: the panel keeps stretching the full row height, so the
 * tinted column still reads as one solid half of the card instead of becoming a
 * short box floating on white.
 *
 * Gated on viewport HEIGHT as well as width. A panel taller than the space
 * below the top bar would stick with its own bottom — the CTA on the second
 * plan, for Investor Leads — pinned off-screen and unreachable. Under that
 * height it simply scrolls normally, which is the correct fallback.
 */
@media (min-width: 62rem) and (min-height: 44rem) {
  .checkout-panel-inner {
    position: sticky;
    top: 5.25rem;   /* 4rem top bar + breathing room */
    /* A panel taller than the space under the top bar would otherwise stick
       with its own bottom pinned off-screen and unreachable — Investor Leads'
       second plan button is exactly that case on a short laptop. Capping the
       height and letting it scroll internally means nothing is ever stranded.
       The negative margin buys back room for .plan-flag, which straddles its
       card's top edge and would be sheared off by the overflow box. */
    max-height: calc(100svh - 6.5rem);
    overflow-y: auto;
    /* NO overscroll-behavior: contain here. The panel only overflows on short
       viewports, and containing the scroll would trap the page when a visitor
       reaches the panel's end with the pointer over it. Chaining is right. */
    margin: -0.75rem;
    padding: 0.75rem;
  }
}

.panel-label {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}





/* ---------- plan options ---------- */

/*
 * Two options on one ordered dimension — residential, then the same thing
 * extended to commercial — with a clear recommendation. That is the shape the
 * research says is safe; what predicts choice overload is illegible options,
 * not how many there are.
 */
.plans { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.85rem; }

.plan {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1.1rem 1.2rem 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

.plan-pick {
  border-color: rgb(var(--accent-fill-rgb) / 0.55);
  box-shadow: 0 0 0 1px rgb(var(--accent-fill-rgb) / 0.35);
}

.plan-flag {
  position: absolute;
  top: 0;
  right: 1rem;
  translate: 0 -50%;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--on-accent);
  background: var(--accent-fill);
  border-radius: 999px;
  padding: 0.22rem 0.6rem;
  white-space: nowrap;
}

.plan-name { font-size: var(--fs-sm); font-weight: 700; }

.plan-price { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; font-family: var(--font-display); }
.plan-now {
  font-size: clamp(1.9rem, 1.6rem + 1.1vw, 2.5rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.plan-was { font-size: 1.05rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }

.plan-blurb { font-size: var(--fs-sm); color: var(--text-dim); margin-bottom: 0.35rem; }

/* Quiet button for the non-recommended plan: still obviously clickable, but it
   does not compete with the one most people should take. */
.btn-quiet {
  background: transparent;
  color: var(--accent);
  border-color: rgb(var(--accent-fill-rgb) / 0.45);
}
.btn-quiet:hover { background: rgb(var(--accent-fill-rgb) / 0.08); translate: 0 -1px; }

.inc-star .ico { color: var(--accent); }

.panel-trust { list-style: none; margin: 0.35rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.7rem; }
.panel-trust li { display: flex; gap: 0.55rem; align-items: flex-start; font-size: var(--fs-sm); color: var(--text-dim); }
.panel-trust .ico { color: var(--ok); margin-top: 0.24em; }
.panel-trust strong { color: var(--text); }

.panel-deadline {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  padding-top: 0.85rem;
  border-top: 1px solid rgb(var(--accent-fill-rgb) / 0.2);
}

@media (max-width: 56rem) {
  .checkout { grid-template-columns: minmax(0, 1fr); }
  .checkout-main { border-radius: 24px 24px 0 0; }
  .checkout-panel {
    border-left: none;
    border-top: 1px solid rgb(var(--accent-fill-rgb) / 0.2);
    border-radius: 0 0 24px 24px;
  }
  /* 75% of a narrow stacked panel is proportionally far bigger than 75% of a
     wide column, so the watermark has to shrink and crop harder or it competes
     with the copy instead of sitting behind it. */
  .checkout-watermark {
    height: 56%;
    translate: -62% -50%;
    /* Proportionally larger in a narrow column, so ease it back further */
    color: rgb(var(--watermark-rgb) / calc(var(--watermark-alpha) * 0.62));
  }
}

/* Price treatment retained for the expired state's copy */
.price { display: flex; align-items: baseline; gap: 0.7rem; flex-wrap: wrap; font-family: var(--font-display); }
.price-amount {
  font-size: clamp(2.2rem, 1.8rem + 1.8vw, 3.1rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.price-anchor { font-size: 1.2rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.price-terms { font-size: var(--fs-sm); color: var(--text-muted); font-family: var(--font-body); }
.price-saving { font-size: var(--fs-sm); color: var(--accent); font-weight: 600; }

/* ---------- buttons ---------- */

.cta-group { display: flex; flex-direction: column; gap: 0.7rem; align-items: flex-start; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: inherit;
  font-size: var(--fs-base);
  font-weight: 700;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 12px;
  /* 48px minimum target with 8px+ separation from siblings */
  min-height: 3rem;
  padding: 0.85rem 1.5rem;
  transition: background-color 160ms ease, translate 160ms ease, box-shadow 160ms ease;
}

.btn-primary {
  background: var(--accent-fill);
  color: var(--on-accent);
  box-shadow: 0 14px 30px -14px rgb(var(--accent-fill-rgb) / 0.65);
}
.btn-primary:hover { background: var(--accent-fill-hover); translate: 0 -1px; }
.btn-primary:active { translate: 0 1px; }

/* Inverted button for the accent-filled final banner */
.btn-invert { background: var(--surface); color: var(--accent); }
.btn-invert:hover { translate: 0 -1px; box-shadow: 0 14px 30px -14px rgb(var(--scrim-rgb) / 0.6); }

.btn-lg { font-size: clamp(1rem, 0.96rem + 0.2vw, 1.12rem); padding: 1.05rem 1.9rem; min-height: 3.5rem; }
.btn-block { width: 100%; }

.btn .ico { width: 1.05em; height: 1.05em; }

.cta-reassure {
  display: flex;
  align-items: flex-start;
  gap: 0.45rem;
  font-size: var(--fs-sm);
  color: var(--text-dim);
  text-wrap: pretty;
}
.cta-reassure .ico {
  color: var(--ok);
  flex: 0 0 auto;
  margin-top: 0.12em;
}

/* ---------- faq ---------- */

.faq { display: flex; flex-direction: column; }

.faq details { border-bottom: 1px solid var(--border); padding: 0.35rem 0; }

.faq summary {
  cursor: pointer;
  font-weight: 700;
  padding: 1rem 0;
  list-style: none;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  min-height: 3rem;
  align-items: center;
}
.faq summary::-webkit-details-marker { display: none; }

.faq summary::after {
  content: "+";
  font-family: var(--font-display);
  font-size: 1.4rem;
  line-height: 1;
  color: var(--accent);
  flex: none;
  transition: rotate 200ms ease;
}
.faq details[open] summary::after { rotate: 45deg; }

.faq-body { padding-bottom: 1.1rem; color: var(--text-dim); font-size: var(--fs-sm); max-width: 42rem; }

/* ---------- final banner ---------- */

.final {
  padding-block: clamp(3.75rem, 9vw, 7rem);
  background:
    radial-gradient(50rem 24rem at 15% 0%, rgb(var(--on-accent-rgb) / 0.12), transparent 55%),
    linear-gradient(135deg, var(--accent-fill), var(--accent-fill-hover));
  color: var(--on-accent);
}

.final-inner { text-align: center; display: flex; flex-direction: column; align-items: center; gap: 1rem; }
.final h2 { max-width: 24ch; }
.final-sub { font-size: var(--fs-sm); color: rgb(var(--on-accent-rgb) / 0.82); }
.final .cta-group { align-items: center; margin-top: 0.5rem; }
.final .cta-reassure { color: rgb(var(--on-accent-rgb) / 0.82); }
.final .cta-reassure .ico { color: var(--on-accent); }

/* ---------- footer ---------- */

.foot { padding-block: 2.5rem; border-top: 1px solid var(--border); }

.foot-inner {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  align-items: center;
  justify-content: space-between;
}

.foot-legal { font-size: var(--fs-sm); color: var(--text-muted); }

.foot-links { list-style: none; margin: 0; padding: 0; display: flex; gap: 1.25rem; flex-wrap: wrap; }
.foot-links a { font-size: var(--fs-sm); color: var(--text-dim); text-decoration: none; }
.foot-links a:hover { color: var(--accent); text-decoration: underline; }

/* ---------- responsive ---------- */

@media (max-width: 56rem) {
  .hero-inner { grid-template-columns: minmax(0, 1fr); }
  /* Image leads on mobile, copy beneath it. Kept to a shallow crop so the
     headline still lands close to the fold rather than being pushed off it. */
  .hero-media { order: -1; }
  .hero-frame { aspect-ratio: 16 / 10; }
  /* Coordinates were placed on the uncropped frame; a cropped one moves them off the rooftops. */
  .hero-pins { display: none; }

  /* Four stacked icon rows was a tall, repetitive list. A 2x2 of tinted tiles
     halves the height and reads as a composed block — and keeps all four
     visible, which a swipeable row would not. */
  .feature-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.85rem;
  }
  .feature {
    background: rgb(var(--on-deep-rgb) / 0.06);
    border: 1px solid rgb(var(--on-deep-rgb) / 0.14);
    border-radius: 14px;
    padding: 1.05rem 0.95rem 1.15rem;
  }
  .feature-ico {
    width: 2.3rem;
    height: 2.3rem;
    border-radius: 10px;
    margin-bottom: 0.7rem;
  }
  .feature-ico .ico { width: 1.15rem; height: 1.15rem; }
  .feature h3 { font-size: 0.97rem; line-height: 1.25; }
  .feature p { font-size: 0.8rem; line-height: 1.45; }
  .hero-float { left: 0.9rem; bottom: 0.9rem; }
  .day-split { grid-template-columns: minmax(0, 1fr); }
  .day-media { max-width: 34rem; }
}

@media (max-width: 40rem) {
  /* The poster is a branded graphic carrying the product name, and at this size
     the overlay caption lands on top of its wordmark. The play button already
     says "video", so the caption is the redundant half. */
  .video-title { display: none; }
}

@media (max-width: 34rem) {
  .btn { width: 100%; }
  .cta-group { align-self: stretch; width: 100%; }
  .topbar-deadline { font-size: 0.78rem; padding: 0.3rem 0.6rem; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

@media print {
  .topbar, .final, .video-shell { display: none; }
}

/* ---------- buyer match spotlight ----------
   The expanded Buyer Match Ai section. The demo is a tablist over pre-rendered
   panels; with JS off the first panel shows and reads fine on its own.
   Score bars are driven by --v (the score itself), so a bar can never disagree
   with the number printed beside it. */

.bmx { position: relative; }

.bmx-h { max-width: 20ch; text-wrap: balance; }

.bmx-steps {
  list-style: none;
  margin: clamp(2rem, 4vw, 3rem) 0 0;
  padding: 0;
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.bmx-steps li {
  display: grid;
  gap: 0.3rem;
  padding-top: 1rem;
  border-top: 2px solid rgb(var(--accent-rgb) / 0.28);
}

.bmx-step-n {
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--accent);
}

.bmx-step-t { font-weight: 700; }
.bmx-step-b { color: var(--text-dim); font-size: var(--fs-sm); text-wrap: balance; }

.bmx-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 1rem;
  padding: 0.4rem 0.95rem;
  border-radius: 2rem;
  border: 1px solid rgb(var(--accent-rgb) / 0.35);
  background: rgb(var(--accent-tint-rgb) / 0.9);
  color: var(--accent);
  font-size: var(--fs-sm);
  font-weight: 700;
}
.bmx-pill .ico { width: 1em; height: 1em; }

/* --- lede: copy beside the house, with the matched investors pinned on it --- */

.bmx-lede { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); align-items: center; }

@media (min-width: 56rem) {
  .bmx-lede { grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); }
}

.bmx-lede-copy > h2 { margin-bottom: 0.6rem; }
.bmx-lede-copy .section-lead { margin-bottom: 0; }

/* Matches the source crop so the pin coordinates land where they were authored;
   they are percentages of this box, not of the image. */
.bmx-media {
  position: relative;
  margin: 0;
  aspect-ratio: 880 / 560;
  border-radius: 1.1rem;
  overflow: hidden;
  border: 1px solid rgb(var(--accent-fill-rgb) / 0.35);
  box-shadow: 0 26px 64px -34px rgb(var(--accent-fill-rgb) / 0.5);
}

.bmx-media > img { width: 100%; height: 100%; object-fit: cover; }

/* Fixed dark scrim rather than a themed surface — the circles sit on a photo. */
.bmx-media-scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, rgb(var(--scrim-rgb) / 0.5), rgb(var(--scrim-rgb) / 0.06) 60%),
    linear-gradient(to right, rgb(var(--accent-fill-rgb) / 0.2), transparent 65%);
}

/* Same pop-in the compact block uses; .in comes from the reveal observer. */
html[data-js] .bmx-media.in .match-pin { opacity: 1; scale: 1; }

/* --- the demo shell --- */

.bmx-demo {
  margin-top: clamp(2rem, 5vw, 3.25rem);
  border: 1px solid var(--border);
  border-radius: 1.15rem;
  background: var(--surface-raised);
  padding: clamp(1rem, 2.5vw, 1.6rem);
  box-shadow: 0 1px 2px rgb(var(--text-rgb) / 0.05), 0 18px 40px -28px rgb(var(--text-rgb) / 0.35);
}

.bmx-demo-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.7rem;
  margin-bottom: 0.9rem;
}

.bmx-demo-label {
  margin: 0;
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
}

.bmx-demo-hint { margin: 0; font-size: var(--fs-sm); color: var(--text-muted); }

.bmx-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.1rem;
}

.bmx-tab {
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  padding: 0.5rem 0.95rem;
  border-radius: 2rem;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text-dim);
  transition: background 0.18s, color 0.18s, border-color 0.18s;
}

.bmx-tab:hover { border-color: var(--accent); color: var(--text); }

.bmx-tab[aria-selected="true"] {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--on-accent);
}

.bmx-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.bmx-panel:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 0.9rem; }

/* --- stage: property on the left, ranked results on the right --- */

.bmx-stage {
  display: grid;
  gap: 1rem;
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 56rem) {
  .bmx-stage { grid-template-columns: 17rem minmax(0, 1fr); gap: 1.4rem; }
}

.bmx-prop {
  border: 1px solid var(--border);
  border-radius: 0.9rem;
  background: var(--surface-2);
  padding: 1.1rem;
  align-self: start;
}

.bmx-prop p { margin: 0; }

.bmx-prop-kind {
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.55rem !important;
}

.bmx-prop-addr { font-weight: 700; font-size: var(--fs-h3); font-family: var(--font-display); }
.bmx-prop-city { color: var(--text-dim); font-size: var(--fs-sm); }

.bmx-prop-price {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--fs-h3);
  margin-top: 0.7rem !important;
}

.bmx-prop-spec { color: var(--text-muted); font-size: var(--fs-sm); }

.bmx-sig-head {
  margin-top: 1rem !important;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text);
}

.bmx-sig { list-style: none; margin: 0.4rem 0 0; padding: 0; display: grid; gap: 0.4rem; }

.bmx-sig li {
  display: flex;
  gap: 0.45rem;
  align-items: flex-start;
  font-size: var(--fs-sm);
  color: var(--text-dim);
}

.bmx-sig .ico { color: var(--ok); flex: 0 0 auto; margin-top: 0.15em; width: 1em; height: 1em; }

/* --- results --- */

.bmx-readout {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.7rem;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-dim);
}

.bmx-readout .ico { color: var(--accent); width: 1em; height: 1em; }

.bmx-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; counter-reset: none; }

.bmx-row {
  display: grid;
  grid-template-columns: 1.6rem minmax(0, 1fr) auto;
  gap: 0.7rem;
  align-items: center;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: 0.7rem;
  background: var(--surface);
}

.bmx-row:first-child {
  border-color: rgb(var(--accent-rgb) / 0.45);
  background: rgb(var(--accent-tint-rgb) / 0.55);
}

.bmx-rank {
  font-family: var(--font-display);
  font-weight: 800;
  color: var(--text-muted);
  text-align: center;
}

.bmx-row:first-child .bmx-rank { color: var(--accent); }

.bmx-who { display: grid; gap: 0.15rem; min-width: 0; }

.bmx-name { font-weight: 700; }

.bmx-focus { display: flex; flex-wrap: wrap; gap: 0.3rem; }

.bmx-tag {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.08rem 0.45rem;
  border-radius: 1rem;
  border: 1px solid var(--border-strong);
  color: var(--text-dim);
}

.bmx-why { font-size: var(--fs-sm); color: var(--text-muted); text-wrap: pretty; }

.bmx-score { display: grid; gap: 0.3rem; justify-items: end; width: 6.5rem; }

.bmx-score-num { display: flex; align-items: baseline; gap: 0.25rem; }

.bmx-score-num b {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 800;
  line-height: 1;
  color: var(--text);
}

.bmx-score-num i {
  font-style: normal;
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
}

.bmx-row:first-child .bmx-score-num b { color: var(--accent); }

.bmx-bar {
  display: block;
  width: 100%;
  height: 0.3rem;
  border-radius: 1rem;
  background: rgb(var(--text-rgb) / 0.1);
  overflow: hidden;
}

.bmx-bar-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  width: calc(var(--v, 0) * 1%);
  background: var(--accent-fill);
}

.bmx-row:not(:first-child) .bmx-bar-fill { background: rgb(var(--accent-rgb) / 0.55); }

.bmx-note {
  margin: 1rem 0 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

/* --- next steps --- */

.bmx-next { margin-top: clamp(1.75rem, 4vw, 2.5rem); }

.bmx-next-h { font-size: var(--fs-h3); margin-bottom: 0.75rem; }

.bmx-next ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

.bmx-next li {
  display: flex;
  gap: 0.7rem;
  align-items: flex-start;
  font-size: var(--fs-sm);
  color: var(--text-dim);
}

.bmx-next li > span:last-child { text-wrap: balance; }

.bmx-next-ico {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 0.6rem;
  background: rgb(var(--accent-tint-rgb) / 0.9);
  border: 1px solid rgb(var(--accent-rgb) / 0.25);
}

.bmx-next-ico .ico { color: var(--accent); width: 1.05rem; height: 1.05rem; }
.bmx-next strong { color: var(--text); display: block; }

/* --- the run animation ---
   Switching property replays the ranking: rows stagger in and each bar grows
   from zero to its score. Purely additive — the panel is already correct
   before it runs, so a failed script or reduced motion loses nothing. */

[data-js] .bmx-panel[data-run] .bmx-row {
  animation: bmx-in 0.42s cubic-bezier(0.2, 0.7, 0.3, 1) both;
  animation-delay: calc(var(--d) * 60ms);
}

[data-js] .bmx-panel[data-run] .bmx-bar-fill {
  animation: bmx-fill 0.55s cubic-bezier(0.2, 0.7, 0.3, 1) both;
  animation-delay: calc(var(--d) * 60ms);
}

@keyframes bmx-in {
  from { opacity: 0; transform: translateY(0.5rem); }
  to { opacity: 1; transform: none; }
}

@keyframes bmx-fill {
  from { width: 0; }
  to { width: calc(var(--v, 0) * 1%); }
}

.bmx-demo[data-scoring] .bmx-results { position: relative; }

.bmx-demo[data-scoring] .bmx-results::after {
  content: attr(data-scoring-label);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: 0.7rem;
  background: rgb(var(--surface-rgb) / 0.72);
  color: var(--accent);
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

@media (prefers-reduced-motion: reduce) {
  [data-js] .bmx-panel[data-run] .bmx-row,
  [data-js] .bmx-panel[data-run] .bmx-bar-fill { animation: none; }
  .bmx-demo[data-scoring] .bmx-results::after { content: none; }
}

@media (max-width: 34rem) {
  .bmx-row { grid-template-columns: 1.4rem minmax(0, 1fr); }
  .bmx-score { grid-column: 2; justify-items: start; width: 100%; }
  .bmx-score-num { order: -1; }
}

/* ---------- ticker hero (heroVariant: "ticker") ----------
   Centered statement standing on a drifting shelf of scored property cards —
   the product's output is the hero image. Same seamless-marquee arithmetic as
   the testimonials: fixed card width + own margin, track holds the set twice,
   -50% lands exactly on the duplicate. */

.hero-ticker-variant { padding-bottom: 0; }

.hero-center {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: clamp(1rem, 2.5vw, 2rem);
}

.hero-center .cta-group { margin-top: clamp(1.1rem, 2.5vw, 1.6rem); }

.hero-center h1 { max-width: 17ch; text-wrap: balance; }
.hero-center .lead { max-width: 44rem; }
.hero-center .cta-group { align-items: center; }
.hero-center .price-line { justify-content: center; }

.hero-ticker {
  margin-top: clamp(2rem, 5vw, 3.25rem);
  overflow: hidden;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  padding: 0.9rem 0;
  /* fade the drift in and out at the edges */
  -webkit-mask-image: linear-gradient(to right, transparent, rgb(var(--text-rgb)) 8%, rgb(var(--text-rgb)) 92%, transparent);
  mask-image: linear-gradient(to right, transparent, rgb(var(--text-rgb)) 8%, rgb(var(--text-rgb)) 92%, transparent);
}

.ticker-track {
  display: flex;
  width: max-content;
  animation: ticker-drift 46s linear infinite;
}


@keyframes ticker-drift {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

.tick-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: 0.65rem;
  align-items: center;
  width: 15rem;
  flex: 0 0 auto;
  margin-right: 0.9rem;
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: 0.7rem;
  background: var(--surface);
}

.tick-score {
  grid-row: 1 / span 2;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.3rem;
  line-height: 1;
  padding: 0.45rem 0.5rem;
  border-radius: 0.5rem;
  background: rgb(var(--text-rgb) / 0.06);
  color: var(--text-dim);
}

.tick-score-hot {
  background: rgb(var(--accent-tint-rgb) / 0.9);
  color: var(--accent);
  box-shadow:
    0 0 0 1px rgb(var(--accent-rgb) / 0.35),
    0 0 14px rgb(var(--accent-rgb) / 0.45),
    0 0 30px rgb(var(--accent-rgb) / 0.2);
  animation: tick-glow 2.6s ease-in-out infinite;
}

@keyframes tick-glow {
  0%, 100% { box-shadow: 0 0 0 1px rgb(var(--accent-rgb) / 0.35), 0 0 14px rgb(var(--accent-rgb) / 0.45), 0 0 30px rgb(var(--accent-rgb) / 0.2); }
  50% { box-shadow: 0 0 0 1px rgb(var(--accent-rgb) / 0.5), 0 0 22px rgb(var(--accent-rgb) / 0.65), 0 0 44px rgb(var(--accent-rgb) / 0.3); }
}

@media (prefers-reduced-motion: reduce) {
  .tick-score-hot { animation: none; }
}

.tick-addr { font-size: 0.82rem; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tick-meta { font-size: 0.72rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.ticker-note {
  margin: 0.5rem auto 1.1rem;
  text-align: center;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

@media (prefers-reduced-motion: reduce) {
  .ticker-track { animation: none; }
}

/* ---------- what's inside grid ---------- */

.inside-grid {
  list-style: none;
  margin: clamp(1.5rem, 3vw, 2.25rem) 0 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}

.inside-grid li {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  padding: 0.8rem 1rem;
  border: 1px solid var(--border);
  border-radius: 0.7rem;
  background: var(--surface);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
}

.inside-grid .ico { color: var(--ok); flex: 0 0 auto; margin-top: 0.15em; width: 1em; height: 1em; }

/* ---------- tool tour: vertical rail of tabs beside the product shot ---------- */

.tour-shell {
  margin-top: clamp(1.75rem, 4vw, 2.75rem);
  display: grid;
  gap: 1.25rem;
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 56rem) {
  .tour-shell { grid-template-columns: 18rem minmax(0, 1fr); align-items: start; }
}

.tour-rail { display: grid; gap: 0.6rem; align-content: start; }

@media (max-width: 55.99rem) {
  .tour-rail {
    grid-auto-flow: column;
    grid-auto-columns: minmax(12.5rem, 70vw);
    overflow-x: auto;
    /* full-bleed: escape the wrap's padding, then restore it as scroll gutter
       so cards align with the text at rest but slide under the screen edges */
    margin-inline: calc(-1 * clamp(1.15rem, 4vw, 3rem));
    padding-inline: clamp(1.15rem, 4vw, 3rem);
    padding-bottom: 0.5rem;
    scroll-snap-type: x proximity;
    scroll-padding-inline: clamp(1.15rem, 4vw, 3rem);
    scrollbar-width: none;
  }
  .tour-rail::-webkit-scrollbar { display: none; }
  .tour-tab { scroll-snap-align: start; }
  /* the last visible card fades at the screen edge so it's legible that the
     rail continues — the fade sits on the container, not the cards, so it
     tracks whatever is at the edge while scrolling */
  .tour-rail {
    -webkit-mask-image: linear-gradient(to right, rgb(var(--text-rgb)) 82%, transparent 99%);
    mask-image: linear-gradient(to right, rgb(var(--text-rgb)) 82%, transparent 99%);
  }
}

.tour-tab {
  font: inherit;
  text-align: left;
  cursor: pointer;
  display: grid;
  gap: 0.15rem;
  padding: 0.85rem 1rem 1.05rem;
  border: 1px solid var(--border-strong);
  border-radius: 0.8rem;
  background: var(--surface);
  transition: border-color 0.18s, background 0.18s;
}

.tour-tab:hover { border-color: var(--accent); }
.tour-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.tour-tab[aria-selected="true"] {
  border-color: rgb(var(--accent-rgb) / 0.55);
  background: rgb(var(--accent-tint-rgb) / 0.55);
}

.tour-tab-name { font-weight: 700; color: var(--text); }
.tour-tab-tag { font-size: var(--fs-sm); color: var(--text-dim); text-wrap: balance; }
.tour-tab[aria-selected="true"] .tour-tab-name { color: var(--accent); }

.tour-panel {
  display: grid;
  gap: 1.1rem;
  border: 1px solid var(--border);
  border-radius: 1rem;
  background: var(--surface-raised);
  padding: clamp(1rem, 2.5vw, 1.5rem);
}

/* display:grid would otherwise override the hidden attribute — every panel
   rendered at once, auto-placed across the shell's columns. */
.tour-panel[hidden] { display: none; }

@media (min-width: 56rem) {
  .tour-rail { grid-column: 1; grid-row: 1; }
  .tour-panel { grid-column: 2; grid-row: 1; }
}

@media (min-width: 72rem) {
  .tour-panel { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); align-items: center; }
}

.tour-panel:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.tour-shot {
  margin: 0;
  border-radius: 0.7rem;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface-2);
  aspect-ratio: 900 / 847;
}
.tour-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

.tour-desc { margin: 0 0 0.9rem; color: var(--text-dim); }

/* ---------- score explorer additions on the bmx demo ---------- */

.bmx-tabs-cards { display: grid; gap: 0.6rem; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); }

.bmx-tab-card {
  border-radius: 0.8rem;
  text-align: left;
  display: grid;
  gap: 0.1rem;
  padding: 0.75rem 1rem;
}

.bmx-tab-card .bmx-tab-name { font-weight: 700; }
.bmx-tab-card .bmx-tab-desc { font-size: 0.78rem; font-weight: 500; opacity: 0.85; text-wrap: balance; }

.bmx-tab-card[aria-selected="true"] .bmx-tab-desc { color: var(--on-accent); }

.bmx-thresh {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 0.9rem;
  margin: 0 0 1rem;
  padding: 0.7rem 0.9rem;
  border: 1px dashed var(--border-strong);
  border-radius: 0.7rem;
}

.bmx-thresh-label { font-size: var(--fs-sm); font-weight: 600; color: var(--text-dim); }

.bmx-range { flex: 1 1 10rem; accent-color: var(--accent); min-height: 1.4rem; }

.bmx-thresh-out {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--accent);
  min-width: 8.5rem;
  text-align: right;
}

.bmx-row-cut { opacity: 0.3; filter: grayscale(0.8); }
.bmx-row-cut .bmx-bar-fill { background: rgb(var(--text-rgb) / 0.25); }

/* ---------- plain lead cards (no image): the flush-heading fix ---------- */

.lead-card-plain { padding: 1.15rem 1.25rem 0.35rem; }

.lead-title-plain {
  font-size: var(--fs-h3);
  margin: 0 0 0.15rem;
  padding-bottom: 0.55rem;
  border-bottom: 2px solid rgb(var(--accent-rgb) / 0.25);
}

.lead-card-plain .lead-body { padding: 0.6rem 0 1rem; }

/* ---------- ring gauges (tool tour) ----------
   The arc and the printed number share one source: --p is value/max, the
   dashoffset is derived from it, so the ring physically cannot disagree with
   the figure inside it. Without JS the rings render already filled (the
   ring-run start state only exists under [data-js]); reduced motion skips the
   sweep the same way. */

.tour-shot-rings { position: relative; }

.ring-row {
  position: absolute;
  inset-inline: 0;
  bottom: clamp(0.6rem, 2vw, 1.1rem);
  display: flex;
  justify-content: center;
  gap: clamp(0.5rem, 2vw, 1.25rem);
  padding: 0 0.6rem;
}

.ring-gauge {
  --ring-c: 326.73;
  display: grid;
  justify-items: center;
  align-content: start;
  width: clamp(5.2rem, 10vw, 6.5rem);
  padding: 0.55rem 0.3rem 0.5rem;
  border-radius: 0.9rem;
  background: rgb(var(--scrim-rgb) / 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgb(var(--on-scrim-rgb) / 0.16);
}

.ring-gauge svg {
  width: clamp(3rem, 6vw, 3.9rem);
  height: auto;
  transform: rotate(-90deg);
  grid-area: 1 / 1;
}

.ring-bg, .ring-fg { fill: none; stroke-width: 11; }
.ring-bg { stroke: rgb(var(--on-scrim-rgb) / 0.18); }

.ring-fg {
  stroke: var(--accent-soft);
  stroke-linecap: round;
  stroke-dasharray: var(--ring-c);
  stroke-dashoffset: calc(var(--ring-c) * (1 - var(--p, 0)));
}

/* JS present: start empty, sweep to the value when .ring-run lands. */
html[data-js] .ring-gauge:not(.ring-run) .ring-fg { stroke-dashoffset: var(--ring-c); }
html[data-js] .ring-gauge.ring-run .ring-fg {
  stroke-dashoffset: calc(var(--ring-c) * (1 - var(--p, 0)));
  transition: stroke-dashoffset 1.1s cubic-bezier(0.25, 0.8, 0.3, 1);
}

.ring-num {
  grid-area: 1 / 1;
  align-self: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(0.85rem, 1.6vw, 1.05rem);
  color: var(--on-scrim);
  line-height: 1;
}

.ring-label {
  margin-top: 0.35rem;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
  color: rgb(var(--on-scrim-rgb) / 0.85);
  line-height: 1.25;
}

.ring-note {
  position: absolute;
  top: 0.6rem;
  right: 0.75rem;
  font-size: 0.66rem;
  color: rgb(var(--on-scrim-rgb) / 0.7);
}

@media (prefers-reduced-motion: reduce) {
  html[data-js] .ring-gauge:not(.ring-run) .ring-fg { stroke-dashoffset: calc(var(--ring-c) * (1 - var(--p, 0))); }
  html[data-js] .ring-gauge.ring-run .ring-fg { transition: none; }
}

/* ---------- tool tour panel entrance ----------
   Re-triggered per selection via the .tour-in class (site.js): the shot eases
   in with a slight settle, the copy follows a beat later. Nothing here runs
   without JS or under reduced motion — panels simply appear. */

html[data-js] .tour-panel.tour-in .tour-shot {
  animation: tour-shot-in 0.45s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

html[data-js] .tour-panel.tour-in .tour-copy {
  animation: tour-copy-in 0.45s cubic-bezier(0.2, 0.7, 0.3, 1) 0.08s both;
}

@keyframes tour-shot-in {
  from { opacity: 0; transform: scale(0.965) translateY(0.35rem); }
  to { opacity: 1; transform: none; }
}

@keyframes tour-copy-in {
  from { opacity: 0; transform: translateY(0.5rem); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  html[data-js] .tour-panel.tour-in .tour-shot,
  html[data-js] .tour-panel.tour-in .tour-copy { animation: none; }
}

/* ---------- tool tour autoplay ----------
   The selected tab's bar fills over the dwell time; its animationend advances
   the carousel (see site.js), so the bar is the timer rather than a picture
   of one. Runs only while the section is in view (.tour-auto), pauses on
   hover/focus, never runs without JS or under reduced motion — in both of
   those cases the bars stay hidden and the tour is a plain manual tablist. */

.tour-tab { position: relative; overflow: hidden; }

.tour-tab-bar {
  position: absolute;
  inset-inline: 1rem;   /* matches .tour-tab's horizontal padding, so the bar
                           tracks the text edge instead of the rounded corner */
  bottom: 0.5rem;
  height: 3px;
  border-radius: 3px;
  overflow: hidden;
  background: rgb(var(--accent-rgb) / 0.14);
  opacity: 0;
}

.tour-tab-fill {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
}

html[data-js] .tour-auto .tour-tab[aria-selected="true"] .tour-tab-bar { opacity: 1; }

html[data-js] .tour-auto .tour-tab[aria-selected="true"] .tour-tab-fill {
  animation: tour-progress 6s linear forwards;
}

.tour-shell:hover .tour-tab-fill,
.tour-shell:focus-within .tour-tab-fill {
  animation-play-state: paused;
}

@keyframes tour-progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
  html[data-js] .tour-auto .tour-tab[aria-selected="true"] .tour-tab-fill { animation: none; }
  html[data-js] .tour-auto .tour-tab[aria-selected="true"] .tour-tab-bar { opacity: 0; }
}

/* ---------- conversion pass: persistent + mid-page routes to the offer ---------- */

.topbar-side { display: flex; align-items: center; gap: 0.7rem; }

.topbar-cta {
  padding: 0.5rem 1.05rem;
  font-size: var(--fs-sm);
  border-radius: 2rem;
}


.inside-link { display: flex; align-items: flex-start; gap: 0.55rem; color: inherit; text-decoration: none; }
.inside-link:hover span { text-decoration: underline; text-underline-offset: 3px; }

.tour-jump { margin: 1.1rem 0 0; text-align: right; }
.tour-jump a {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--fs-sm);
  font-weight: 700;
  text-decoration: none;
}
.tour-jump a:hover { text-decoration: underline; text-underline-offset: 3px; }
.tour-jump .ico { width: 1em; height: 1em; }

.bmx-cta { margin-top: clamp(1.75rem, 4vw, 2.5rem); display: flex; justify-content: center; }
.bmx-cta .cta-group { align-items: center; }

.plan-save {
  margin: -0.35rem 0 0.35rem;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--ok);
}

/* Mobile buy bar: JS-revealed, never rendered without it, gone once the
   checkout itself is on screen. Desktop keeps the topbar CTA instead. */
.buybar {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 40;
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  padding: 0.6rem clamp(0.9rem, 4vw, 1.25rem) calc(0.6rem + env(safe-area-inset-bottom, 0px));
  background: rgb(var(--surface-rgb) / 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-top: 1px solid var(--border);
  transform: translateY(105%);
  transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.3, 1);
}

.buybar-show { transform: translateY(0); }

.buybar-price {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.15rem;
  color: var(--text);
}
.buybar-price { flex: 0 0 auto; }
.buybar-price i { font-style: normal; font-family: var(--font-body); font-weight: 500; font-size: 0.72rem; color: var(--text-dim); white-space: nowrap; }

/* width beats the global mobile .btn{width:100%} by source order: the button
   takes two-thirds of the bar, the price column keeps the rest. */
.buybar-btn { flex: 0 0 auto; width: 66%; padding: 0.75rem 1rem; font-size: var(--fs-sm); }

@media (max-width: 47.99rem) {
  html[data-js] .buybar { display: flex; }
  /* the fixed bar must never cover the last content on small screens */
  html[data-js] .foot { padding-bottom: 5rem; }
  .topbar-cta { display: none; }  /* the buy bar carries mobile; avoid doubling */
}

@media (prefers-reduced-motion: reduce) {
  .buybar { transition: none; }
}

/* ==========================================================================
   The Lien Data layout family
   --------------------------------------------------------------------------
   Offer #3 needed to stop looking like offers #1 and #2. Everything below is
   scoped to classes only the Lien Data data file produces, so Investor Leads
   and the Ai Suite render byte-identically (verified by diffing their built
   HTML before and after).

   What changes: a full-bleed DARK hero carrying a video loop and a field of
   animated money beacons; an offense/defense split panel; the lead types as an
   accordion docket; a provenance chain on the deep band; a fanned stack of
   direct-mail pieces.

   Token discipline is the same as everywhere else in this file — no hex, no
   raw rgba(), alpha only ever from an -rgb triplet.
   ========================================================================== */

/* ---------- hero: the beacons variant ---------- */

.hero-beacons {
  padding-block: clamp(4.5rem, 11vw, 8rem);
  min-height: clamp(32rem, 80svh, 47rem);
  display: grid;
  align-items: center;
  background: var(--deep);
  color: var(--on-deep);
  isolation: isolate;
}

/* The shared hero wash is tuned for a light page ground; on the deep band it
   would be invisible for any brand whose accent is already dark. */
.hero-beacons::before {
  z-index: 1;
  background:
    radial-gradient(70rem 34rem at 16% -14%, rgb(var(--accent-fill-rgb) / 0.4), transparent 62%),
    radial-gradient(46rem 30rem at 94% 112%, rgb(var(--accent-fill-alt-rgb) / 0.3), transparent 60%);
}

.hero-bg { position: absolute; inset: 0; z-index: 0; overflow: hidden; }

/*
 * Street-grid texture. An alpha MASK rather than a picture, for the same reason
 * the data band's backdrop is one: a literal render carries its own colour and
 * would fight half the fleet. Filled with --on-deep rather than the accent,
 * because on a deep ground a dark brand accent simply disappears — this layer
 * has to read the same for all sixty brands, and the accent already speaks in
 * the wash above it and the button below it.
 */
.hero-texture {
  position: absolute;
  inset: 0;
  background: var(--on-deep);
  opacity: 0.08;
  -webkit-mask-image: var(--mask);
  mask-image: var(--mask);
  -webkit-mask-size: cover;
  mask-size: cover;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* Sits ABOVE the beacon field, which is what stops a marker ever landing on a
   paragraph: the same gradient that protects the copy fades the markers behind
   it. Weighted left, where the copy is, and open on the right so the markers
   still read around the video panel. */
.hero-veil {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background:
    linear-gradient(to right,
      rgb(var(--deep-rgb) / 0.86) 0%,
      rgb(var(--deep-rgb) / 0.66) 44%,
      rgb(var(--deep-rgb) / 0.22) 78%,
      rgb(var(--deep-rgb) / 0.4) 100%),
    linear-gradient(to top, rgb(var(--deep-rgb) / 0.72), transparent 46%);
}

/* ---------- hero: copy beside the explainer ---------- */

.hero-lien-grid {
  position: relative;
  z-index: 4;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.88fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
/* No video in the hero (the pre/expired states, or another offer): one column,
   and the copy keeps its own measure rather than stretching to the full wrap. */
.hero-lien-grid.hero-lien-solo { grid-template-columns: minmax(0, 1fr); }
.hero-lien-solo .hero-lien > * { max-width: 47rem; }

.hero-lien-media { position: relative; }

.video-shell-hero {
  margin-bottom: 0;
  border-radius: 14px;
  box-shadow: 0 2rem 4rem -1.5rem rgb(var(--scrim-rgb) / 0.75);
  border: 1px solid rgb(var(--on-deep-rgb) / 0.14);
}
.video-shell-hero .video-play { width: clamp(3.6rem, 7vw, 4.9rem); height: clamp(3.6rem, 7vw, 4.9rem); }

/* The band's scrim is a bottom-up wash, built for a dark video still. This
   poster is a light title card, where that wash reads as a smudge — so the hero
   variant darkens only behind the play button instead. */
.video-shell-hero .video-scrim {
  background: radial-gradient(closest-side at 50% 50%, rgb(var(--scrim-rgb) / 0.3), rgb(var(--scrim-rgb) / 0.05) 72%, transparent);
}
.video-shell-hero .video-facade:hover .video-scrim {
  background: radial-gradient(closest-side at 50% 50%, rgb(var(--scrim-rgb) / 0.2), transparent 72%);
}

.hero-video-cap {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  margin-top: 0.85rem;
  font-size: var(--fs-sm);
  color: rgb(var(--on-deep-rgb) / 0.72);
}
.hero-video-cap .ico { width: 0.85rem; height: 0.85rem; }

@media (max-width: 61.99rem) {
  .hero-lien-grid { grid-template-columns: minmax(0, 1fr); }
  .hero-lien-media { order: -1; }
}

.hero-lien { position: relative; }
.hero-lien > * + * { margin-top: 1.25rem; }

.hero-wordmark {
  height: 2.15rem;
  width: auto;
  opacity: 0.94;
}

.hero-beacons h1 { color: var(--on-deep); text-shadow: 0 2px 26px rgb(var(--scrim-rgb) / 0.45); }
.hero-beacons .lead { color: rgb(var(--on-deep-rgb) / 0.84); max-width: 35rem; }
.hero-beacons .price-now { color: var(--on-deep); }
.hero-beacons .price-was { color: rgb(var(--on-deep-rgb) / 0.55); }
.hero-beacons .price-line { color: rgb(var(--on-deep-rgb) / 0.68); }
.hero-beacons .cta-reassure { color: rgb(var(--on-deep-rgb) / 0.74); }
.hero-beacons .cta-reassure .ico { color: rgb(var(--on-deep-rgb) / 0.9); }

/*
 * The facts row was authored for the dark Lien Data hero and took its colours
 * from --on-deep, so on the light mosaic hero it rendered near-invisible on a
 * near-white ground. The base rule is ground-neutral now; the dark hero keeps
 * exactly the colours it had, scoped to itself.
 */
.hero-facts {
  list-style: none;
  margin-top: 1.7rem;
  padding: 0.95rem 0 0;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.5rem;
  font-size: var(--fs-sm);
  color: var(--text-dim);
}
.hero-facts li { display: flex; align-items: center; gap: 0.5rem; }
.hero-facts li::before {
  content: "";
  width: 0.34rem;
  height: 0.34rem;
  border-radius: 50%;
  background: var(--accent);
}

.hero-beacons .hero-facts {
  border-top-color: rgb(var(--on-deep-rgb) / 0.16);
  color: rgb(var(--on-deep-rgb) / 0.74);
}
.hero-beacons .hero-facts li::before { background: rgb(var(--on-deep-rgb) / 0.5); }

/* ---------- the money beacons ---------- */

/*
 * Drawn, not shipped as the parent page's flat blue PNG — a fixed baby-blue
 * overlay cannot follow sixty brands. Struck in --on-deep with one accent-lit
 * lead marker, so they read identically on every partner's hero.
 *
 * Each .beacon is an absolutely positioned column of a definite height
 * (--drop, a percentage of the field), which is what lets the connecting line
 * be flex:1 and stretch between the disc and the tip.
 */
.beacon-field {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  --beacon-scale: 1;
}

.beacon {
  position: absolute;
  left: var(--x);
  top: var(--y);
  height: var(--drop);
  translate: -50% 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.beacon-dot {
  position: relative;
  flex: none;
  font-style: normal;
  width: calc(var(--s) * 1px * var(--beacon-scale));
  height: calc(var(--s) * 1px * var(--beacon-scale));
  border: 2px solid rgb(var(--on-deep-rgb) / 0.42);
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgb(var(--on-deep-rgb) / 0.05);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: calc(var(--s) * 0.42px * var(--beacon-scale));
  line-height: 1;
  color: rgb(var(--on-deep-rgb) / 0.6);
}

.beacon-line {
  flex: 1 1 auto;
  width: 2px;
  transform-origin: top center;
  background: linear-gradient(to bottom, rgb(var(--on-deep-rgb) / 0.32), rgb(var(--on-deep-rgb) / 0.1));
}

.beacon-tip {
  flex: none;
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  border: 2px solid rgb(var(--on-deep-rgb) / 0.34);
}

/* The lead marker is the one the eye should land on. Brighter rather than
   accent-filled: a dark brand accent on a deep ground reads as a hole. The
   accent shows up as the glow around it, where its exact lightness can't
   break anything. */
.beacon-lead .beacon-dot {
  border-color: rgb(var(--on-deep-rgb) / 0.85);
  color: rgb(var(--on-deep-rgb) / 0.92);
  box-shadow: 0 0 0 1px rgb(var(--accent-fill-rgb) / 0.45), 0 0 3.5rem rgb(var(--accent-fill-rgb) / 0.5);
}
.beacon-lead .beacon-line { background: linear-gradient(to bottom, rgb(var(--on-deep-rgb) / 0.7), rgb(var(--on-deep-rgb) / 0.2)); }
.beacon-lead .beacon-tip { border-color: rgb(var(--on-deep-rgb) / 0.7); }

.beacon-lead .beacon-dot::after {
  content: "";
  position: absolute;
  inset: -0.55rem;
  border-radius: 50%;
  border: 1px solid rgb(var(--on-deep-rgb) / 0.45);
}

@keyframes beacon-in {
  from { opacity: 0; translate: -50% -1.7rem; }
  to   { opacity: 1; translate: -50% 0; }
}
@keyframes beacon-draw {
  from { scale: 1 0; }
  to   { scale: 1 1; }
}
@keyframes beacon-pop {
  from { opacity: 0; scale: 0.2; }
  to   { opacity: 1; scale: 1; }
}
@keyframes beacon-float {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -0.5rem; }
}
@keyframes beacon-pulse {
  0%        { opacity: 0.55; scale: 0.85; }
  70%, 100% { opacity: 0; scale: 1.55; }
}

.beacon      { animation: beacon-in 900ms cubic-bezier(0.2, 0.75, 0.3, 1) both; animation-delay: calc(var(--i) * 120ms + 250ms); }
.beacon-line { animation: beacon-draw 700ms cubic-bezier(0.3, 0.8, 0.3, 1) both; animation-delay: calc(var(--i) * 120ms + 640ms); }
.beacon-tip  { animation: beacon-pop 420ms cubic-bezier(0.2, 1.5, 0.5, 1) both; animation-delay: calc(var(--i) * 120ms + 1280ms); }
.beacon-dot  { animation: beacon-float 7.5s ease-in-out infinite; animation-delay: calc(2s + var(--i) * 430ms); }
.beacon-lead .beacon-dot::after { animation: beacon-pulse 3.4s ease-out infinite; animation-delay: 2.2s; }

/* Narrow screens: the copy takes the full width, so the field steps back and
   the two smallest markers — the ones that would land inside a paragraph —
   drop out entirely. */
@media (max-width: 55.99rem) {
  /* The copy takes the full width here, so the markers stop being a scene on
     the right and become texture behind the type. Two of them drop out — the
     ones that would land mid-paragraph — and the veil goes flat instead of
     left-to-right, since there is no longer a left and a right to speak of.
     The veil stays heavy enough to hold the headline's contrast and light
     enough that the markers are still visible through it. */
  .beacon-field { --beacon-scale: 0.55; opacity: 0.9; }
  .beacon:nth-child(2), .beacon:nth-child(6) { display: none; }
  .hero-veil {
    background: linear-gradient(to top, rgb(var(--deep-rgb) / 0.88), rgb(var(--deep-rgb) / 0.62));
  }
}

@media (prefers-reduced-motion: reduce) {
  .beacon, .beacon-line, .beacon-tip, .beacon-dot, .beacon-lead .beacon-dot::after { animation: none; }
  .beacon { opacity: 1; translate: -50% 0; }
  .beacon-line, .beacon-tip { opacity: 1; scale: 1; }
  .beacon-lead .beacon-dot::after { opacity: 0.35; }
}

/* ---------- offense / defense ---------- */

/*
 * The parent page makes these two arguments in two separate full-width
 * sections. They belong side by side: the second one is the answer to the
 * objection the first one raises — "a property with a lien on it sounds like a
 * property I don't want".
 */
.duo-head { max-width: 46rem; }

.duo-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: clamp(2rem, 5vw, 3.5rem);
  align-items: center;
}
/* Fallback shape for an offer that declares sides but ships no imagery. */
.duo-grid-flat { grid-template-columns: minmax(0, 1fr); }

/*
 * Two photographs, staggered rather than stacked: a landscape shot pinned
 * top-left and a portrait one dropped into the bottom-right so they overlap
 * across the diagonal. The container's aspect ratio is what holds the overlap
 * constant — the figures are positioned in percentages of it, so the
 * composition survives every column width instead of collapsing at one.
 */
.duo-media {
  position: relative;
  aspect-ratio: 1 / 1.1;
}

.duo-shot {
  position: absolute;
  margin: 0;
  border-radius: 1rem;
  overflow: hidden;
  isolation: isolate;   /* keeps the tint's blend inside the figure */
  background: var(--surface-2);
}
.duo-shot img { width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) brightness(1.06) contrast(1.08); }

/* Duotone by blend rather than by baked-in colour: `color` takes hue and
   saturation from the accent and luminance from the photograph, so one pair of
   neutral images reads as every brand's own instead of dragging a fixed palette
   across sixty of them. */
.duo-shot-tint {
  position: absolute;
  inset: 0;
  background: var(--accent-fill);
  mix-blend-mode: color;
  opacity: 0.4;
}

.duo-shot-1 { top: 0; left: 0; width: 78%; aspect-ratio: 4 / 3; }
.duo-shot-2 {
  bottom: 0;
  right: 0;
  width: 52%;
  aspect-ratio: 4 / 5;
  /* the ring in the page ground is what separates it from the shot behind */
  box-shadow: 0 0 0 0.55rem var(--bg), 0 1.4rem 3rem -0.8rem rgb(var(--scrim-rgb) / 0.28);
}

.duo-shot-cap {
  position: absolute;
  left: 0.7rem;
  bottom: 0.7rem;
  z-index: 2;
  max-width: calc(100% - 1.4rem);
  padding: 0.3rem 0.65rem;
  border-radius: 999px;
  background: rgb(var(--surface-rgb) / 0.92);
  color: var(--text);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
}

.duo-stack {
  display: flex;
  flex-direction: column;
  gap: clamp(1rem, 2vw, 1.4rem);
}

.duo-side {
  padding: clamp(1.4rem, 3vw, 2.1rem);
  border: 1px solid var(--border);
  border-radius: 1.15rem;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}
.duo-side-offense { background: rgb(var(--accent-tint-rgb) / 0.7); border-color: rgb(var(--accent-fill-rgb) / 0.22); }

.duo-kicker {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.32rem 0.75rem;
  border-radius: 999px;
  border: 1px solid rgb(var(--accent-fill-rgb) / 0.28);
  background: rgb(var(--surface-rgb) / 0.75);
  color: var(--accent);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
}
.duo-kicker .ico { width: 0.95rem; height: 0.95rem; }

.duo-title { font-size: clamp(1.22rem, 1.02rem + 0.6vw, 1.6rem); }
.duo-body { color: var(--text-dim); }
.duo-side .ticks { margin-top: 0.35rem; padding-top: 0.95rem; border-top: 1px solid var(--border); }

@media (max-width: 61.99rem) {
  .duo-grid { grid-template-columns: minmax(0, 1fr); }
  /* The stagger needs width to read. Below this the two shots go side by side
     at equal height, which keeps the pairing without the cramped overlap.
     `position` stays relative, not static — the captions are absolutely
     positioned inside each figure, and dropping to static hands them the whole
     media block as a containing block, where they pile on top of each other. */
  .duo-media { aspect-ratio: 5 / 3; display: flex; gap: 0.7rem; }
  .duo-shot { position: relative; flex: 1 1 0; aspect-ratio: auto; width: auto; }
  .duo-shot-2 { box-shadow: none; }
  .duo-shot-cap {
    left: 0.5rem;
    right: 0.5rem;
    bottom: 0.5rem;
    max-width: none;
    text-align: center;
    font-size: 0.62rem;
    letter-spacing: 0.02em;
  }
}

/* ---------- the docket ---------- */

/*
 * Ten lien types as an accordion register rather than photo cards (Investor
 * Leads) or a tab rail (the Ai Suite). Ten of anything is too many for cards,
 * and the content is genuinely reference material — a definition plus what the
 * filing implies about the owner — so an index that stays scannable closed and
 * explains on demand is the honest shape. Native <details>: no JavaScript, and
 * every definition is in the HTML for search and for print.
 */
.docket-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* interpolate-size inherits, so it is set here rather than on :root — it only
     needs to reach the ::details-content height transition below, and a global
     auto-keyword opt-in would quietly change every other auto transition on the
     page. */
  interpolate-size: allow-keywords;
}

@media (min-width: 62rem) {
  .docket-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(2rem, 4vw, 3.5rem);
    align-items: start;
  }
}

.docket-row { border-bottom: 1px solid var(--border); }

.docket-row summary {
  list-style: none;
  cursor: pointer;
  display: grid;
  grid-template-columns: 1.9rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 0 0.5rem;
  padding: 1rem 0.15rem;
}
.docket-row summary::-webkit-details-marker { display: none; }
.docket-row summary:hover .docket-name { color: var(--accent); }

.docket-n {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.05em;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  opacity: 0.7;
}

.docket-head { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.docket-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-h3);
  line-height: 1.25;
  transition: color 160ms ease;
}
.docket-signal { font-size: var(--fs-sm); color: var(--text-muted); }

/*
 * The plus/minus. Opening spins the whole mark a quarter turn while the vertical
 * stroke retracts into the horizontal one, so the two strokes become one
 * movement instead of one of them blinking out. Slightly overshooting easing on
 * the rotation is what makes it feel mechanical rather than linear.
 */
.docket-mark {
  position: relative;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  transition:
    border-color 260ms ease,
    background-color 260ms ease,
    rotate 340ms cubic-bezier(0.34, 1.4, 0.5, 1),
    scale 260ms cubic-bezier(0.34, 1.4, 0.5, 1);
}
.docket-mark::before,
.docket-mark::after {
  content: "";
  position: absolute;
  left: 24%;
  right: 24%;
  top: 50%;
  height: 1.5px;
  border-radius: 2px;
  background: var(--accent);
  translate: 0 -50%;
}
/* The stroke that survives is the one that is VERTICAL at rest — after the
   mark's quarter turn it lands horizontal, which is the minus. Collapsing the
   other way round leaves a vertical bar where a minus belongs. */
.docket-mark::before {
  transition: scale 300ms cubic-bezier(0.34, 1.3, 0.5, 1), opacity 200ms ease;
}
.docket-mark::after { rotate: 90deg; }

.docket-row summary:hover .docket-mark { border-color: var(--accent); scale: 1.08; }

.docket-row details[open] .docket-mark {
  rotate: 90deg;
  border-color: var(--accent);
  background: rgb(var(--accent-fill-rgb) / 0.12);
}
/* the other stroke collapses to nothing along its own length */
.docket-row details[open] .docket-mark::before { scale: 0 1; opacity: 0; }

/*
 * The body used to appear at full height the instant <details> opened. Two
 * layers of animation now, deliberately independent:
 *
 *   1. ::details-content animates the BOX from zero to auto. It needs
 *      `interpolate-size` (set on .docket-list) and `transition-behavior:
 *      allow-discrete` for the content-visibility flip. Browsers without
 *      ::details-content simply ignore this block and open instantly, exactly
 *      as before — so it degrades to the old behaviour rather than breaking.
 *   2. The paragraph itself fades and rises on a keyframe animation. An
 *      animation, not a transition, because the element goes from display:none
 *      to displayed and a transition has no start value to run from. This half
 *      works in every browser, which is why the text no longer just appears
 *      even where the height still snaps.
 */
.docket-row details::details-content {
  block-size: 0;
  overflow: clip;
  opacity: 0;
  transition:
    block-size 320ms cubic-bezier(0.3, 0.8, 0.35, 1),
    opacity 220ms ease,
    content-visibility 320ms allow-discrete;
}
.docket-row details[open]::details-content {
  block-size: auto;
  opacity: 1;
}

@keyframes docket-open {
  from { opacity: 0; translate: 0 -0.45rem; }
  to   { opacity: 1; translate: 0 0; }
}

.docket-desc {
  padding: 0 2.6rem 1.15rem 2.4rem;
  font-size: var(--fs-sm);
  color: var(--text-dim);
}
.docket-row details[open] .docket-desc {
  animation: docket-open 340ms cubic-bezier(0.2, 0.7, 0.3, 1) 40ms both;
}

.docket-note {
  display: flex;
  gap: 0.55rem;
  align-items: flex-start;
  margin-top: clamp(1.3rem, 3vw, 1.9rem);
  max-width: 52rem;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.docket-note .ico { color: var(--text-muted); margin-top: 0.25em; }

@media (prefers-reduced-motion: reduce) {
  .docket-mark, .docket-mark::before, .docket-name { transition: none; }
  .docket-row details::details-content { transition: none; }
  .docket-row details[open] .docket-desc { animation: none; }
}

/* ---------- provenance chain ---------- */

/*
 * This stands where both other offers put the accent stat band. The persuasive
 * fact here isn't a count — it's that nothing is modelled: every record started
 * as a document somebody filed with a public body. Four steps say that better
 * than a number nobody outside the company can check.
 */
.chain-head {
  display: flex;
  align-items: end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: clamp(1.2rem, 4vw, 3rem);
  margin-bottom: clamp(2.2rem, 5vw, 3.4rem);
}
.chain-head h2 { color: var(--on-deep); }
.chain-lead { max-width: 30rem; font-size: var(--fs-sm); color: rgb(var(--on-deep-rgb) / 0.74); }

/*
 * The stage. One frame per step, cross-faded, with a slow push-in that runs for
 * the length of the dwell — enough motion to say "this is playing" without
 * anything actually moving on the page.
 */
.chain-stage {
  position: relative;
  aspect-ratio: 2.4 / 1;
  margin-bottom: clamp(1.8rem, 4vw, 2.8rem);
  border-radius: 1rem;
  overflow: hidden;
  background: rgb(var(--on-deep-rgb) / 0.06);
  box-shadow: 0 1.6rem 3.4rem -1.2rem rgb(var(--scrim-rgb) / 0.7);
}

.chain-shot {
  position: absolute;
  inset: 0;
  margin: 0;
  isolation: isolate;
}
/* Fade IN only. initTablist parks the outgoing panel with the `hidden`
   attribute, and overriding its display to keep it around for a cross-fade
   would also keep it in the accessibility tree — a hidden tabpanel that screen
   readers can still reach is a worse bug than a 400ms fade that isn't a
   cross-fade. Both frames are dark duotones over a dark stage, so the seam
   doesn't read. */
.chain-shot:not([hidden]) { animation: chain-fade 420ms ease both; }

@keyframes chain-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.chain-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) brightness(1.04) contrast(1.05);
}
.chain-shot:not([hidden]) img { animation: chain-push var(--dwell, 3600ms) linear both; }

/* Same duotone-by-blend as the offense/defense pair: hue and saturation from the
   accent, luminance from the photograph, so four neutral frames read as every
   brand's own. */
.chain-shot-tint {
  position: absolute;
  inset: 0;
  background: var(--accent-fill);
  mix-blend-mode: color;
  opacity: 0.42;
}

@keyframes chain-push {
  from { scale: 1.005; }
  to   { scale: 1.055; }
}

/* ---------- the track ---------- */

.chain-track {
  position: relative;
  height: 2px;
  margin-bottom: 1.15rem;
  border-radius: 2px;
  background: rgb(var(--on-deep-rgb) / 0.16);
  overflow: hidden;
}
.chain-fill {
  display: block;
  width: 0;
  height: 100%;
  border-radius: 2px;
  background: linear-gradient(to right, rgb(var(--on-deep-rgb) / 0.45), var(--on-deep));
}

.chain {
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1.2rem, 2.5vw, 2rem);
}

/* Steps are buttons so they are clickable and keyboard-reachable, but they must
   not look like buttons — reset the UA chrome rather than fight it. */
.chain-step {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  display: grid;
  gap: 0.35rem;
  align-content: start;
}

.chain-n {
  width: 2.3rem;
  height: 2.3rem;
  margin-bottom: 0.5rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: 1px solid rgb(var(--on-deep-rgb) / 0.34);
  background: transparent;
  color: rgb(var(--on-deep-rgb) / 0.6);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  transition: background-color 340ms ease, color 340ms ease, border-color 340ms ease, scale 340ms cubic-bezier(0.34, 1.35, 0.5, 1);
}

/* The disc the fill has reached. It takes the accent as a solid fill, so the
   bar arriving and the number lighting up are one event. */
.chain-step[aria-selected="true"] .chain-n {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--on-accent);
  scale: 1.08;
}
.chain-step:hover .chain-n { border-color: rgb(var(--on-deep-rgb) / 0.7); }

.chain-t {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-h3);
  line-height: 1.25;
  color: rgb(var(--on-deep-rgb) / 0.62);
  transition: color 340ms ease;
}
.chain-b {
  font-size: var(--fs-sm);
  color: rgb(var(--on-deep-rgb) / 0.5);
  transition: color 340ms ease;
}
.chain-step[aria-selected="true"] .chain-t { color: var(--on-deep); }
.chain-step[aria-selected="true"] .chain-b { color: rgb(var(--on-deep-rgb) / 0.78); }

@media (max-width: 55.99rem) {
  .chain { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 1.8rem; }
  /* Once the steps stop being four columns, a full-width bar sitting above them
     no longer points at anything — the disc it "reaches" could be anywhere. So
     it stops being a track and becomes a playback bar welded to the bottom of
     the stage, which is a claim it can still keep. */
  .chain-stage { aspect-ratio: 16 / 10; margin-bottom: 0; border-radius: 1rem 1rem 0 0; }
  .chain-track { margin-bottom: clamp(1.6rem, 4vw, 2.2rem); border-radius: 0 0 2px 2px; }
}
@media (max-width: 34rem) {
  .chain { grid-template-columns: minmax(0, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
  .chain-n, .chain-t, .chain-b { transition: none; }
  .chain-shot:not([hidden]),
  .chain-shot:not([hidden]) img { animation: none; }
}

/* ---------- the direct-mail fan ---------- */

/*
 * A stack of overlapping letters says "one template per lien type" in a way a
 * bulleted list cannot, and it's the one place on this page where the product's
 * output is a physical object. CSS only; the tilt flattens on narrow screens
 * and under reduced motion, where a fanned stack is just harder to read.
 */
/*
 * A wall of envelopes behind the section. On wide screens the backdrop is
 * pinned to the VIEWPORT and the section becomes a window that travels across
 * it — more of the wall appears as you scroll, and the image itself never
 * moves. clip-path (not overflow) is what clips a fixed descendant; this is the
 * same mechanic as .data-backdrop on the Investor Leads data band.
 *
 * Greyscale, not its own warm cream: a fixed colour cast behind a white-label
 * section fights whichever accent the partner brought. Masked to nothing at the
 * top and bottom edges so it dissolves into the sections either side instead of
 * stopping at a hard line.
 */
.mailfan {
  position: relative;
  overflow: hidden;
  clip-path: inset(0);
}
.mailfan > .wrap { position: relative; z-index: 1; }

.mailfan-backdrop {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: var(--bg);
  background-size: cover;
  background-position: center;
  filter: grayscale(1) contrast(1.04);
  opacity: 0.09;
  -webkit-mask-image: linear-gradient(to bottom, transparent, black 16%, black 84%, transparent);
  mask-image: linear-gradient(to bottom, transparent, black 16%, black 84%, transparent);
}

/* Small screens and reduced-motion keep the calm version that scrolls with the
   section — the same call the data band makes. */
@media (min-width: 56rem) and (prefers-reduced-motion: no-preference) {
  .mailfan-backdrop { position: fixed; }
}

.mailfan-head { max-width: 46rem; margin-inline: auto; text-align: center; }
.mailfan-head .section-lead { margin-inline: auto; }

.mailfan-stack {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-block: clamp(0.5rem, 2vw, 1.5rem) clamp(1rem, 3vw, 2rem);
}

.mailpiece {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  max-width: 23rem;
  padding: clamp(1.1rem, 2.2vw, 1.6rem);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: 0.9rem;
  box-shadow: 0 1.1rem 2.4rem rgb(var(--scrim-rgb) / 0.09);
  transition: rotate 260ms ease, translate 260ms ease, box-shadow 260ms ease;
}
.mailpiece:nth-child(1) { rotate: -3.2deg; translate: 0 1.1rem; z-index: 1; margin-right: -1.5rem; }
.mailpiece:nth-child(2) { rotate: 0deg;    translate: 0 -0.5rem; z-index: 3; }
.mailpiece:nth-child(3) { rotate: 3.2deg;  translate: 0 1.1rem; z-index: 2; margin-left: -1.5rem; }
.mailpiece:hover {
  rotate: 0deg;
  translate: 0 -0.5rem;
  z-index: 4;
  box-shadow: 0 1.6rem 3rem rgb(var(--scrim-rgb) / 0.14);
}

.mailpiece-type {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  /* The eyebrow is a label on the piece, not the first line of it — the gap
     under it is what separates the two. */
  margin-bottom: 1.15rem;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
}
.mailpiece-type .ico { width: 0.95rem; height: 0.95rem; }

.mailpiece-head {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-h3);
  line-height: 1.25;
  margin-bottom: 0.5rem;
  padding-right: 2.2rem;
}
.mailpiece-body { font-size: var(--fs-sm); color: var(--text-dim); }

/* The badge IS the stamp — no box around it, the perforated edge lives in the
   icon's own path.
   NO rotation of its own. It used to carry -7deg, which stacked on top of
   whichever angle its card was at and made all three stamps read as tilted the
   same way while the cards clearly were not. Square to the card means each
   stamp inherits exactly its card's angle, which is what a stamp stuck to a
   piece of paper actually does. */
.mailpiece-stamp {
  position: absolute;
  top: 0.8rem;
  right: 0.85rem;
  display: grid;
  place-items: center;
  color: var(--accent);
  opacity: 0.6;
  transition: opacity 260ms ease;
}
.mailpiece-stamp .ico { width: 2rem; height: 2rem; }
.mailpiece:hover .mailpiece-stamp { opacity: 0.85; }

@media (prefers-reduced-motion: reduce) {
  .mailpiece-stamp { transition: none; }
}

.mailfan-points {
  list-style: none;
  margin: clamp(1.4rem, 3.5vw, 2.2rem) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.7rem, 2vw, 1.8rem);
}
.mailfan-points li { display: flex; gap: 0.55rem; align-items: flex-start; font-size: var(--fs-sm); color: var(--text-dim); }
.mailfan-points .ico { color: var(--accent); margin-top: 0.28em; }

.mailfan-note {
  margin-top: 1.3rem;
  text-align: center;
  font-size: 0.8rem;
  color: var(--text-muted);
}

/*
 * Narrow screens: still a pile of mail, not a column of feature cards.
 *
 * Full-width stretched boxes were the problem — a portrait card with a heading
 * and a paragraph is a feature card no matter what is written on it. What makes
 * something read as a mail piece is the LANDSCAPE proportion, so the pieces keep
 * one here (16/10, against the 1.73–1.94 the desktop cards measure) and the type
 * steps down to fit inside it rather than the box growing to fit the type.
 *
 * They alternate left and right and tuck under each other by 1.4rem, and the
 * bottom padding RESERVES that much space plus a margin. Relying on the ratio
 * to leave slack was the first attempt and it clipped the last line of copy —
 * at this width the copy fills the ratio exactly, so there was no slack to
 * borrow. Reserving the strip means the overlap lands on blank paper whatever
 * the copy does, which also lets the ratio settle where the text wants it
 * (~1.5, a 6x4 postcard) instead of fighting it.
 */
@media (max-width: 55.99rem) {
  .mailfan-stack {
    display: block;
    padding-block: 0.5rem 1.25rem;
  }

  .mailpiece {
    width: 92%;
    max-width: 26rem;
    aspect-ratio: 16 / 10;
    /* Safety net: if the copy ever outgrows the ratio — longer headline, a
       larger text setting, a 320px phone — the box grows instead of spilling
       its text over the edge. The ratio is the intent, not a guillotine. */
    min-height: max-content;
    /* The bottom padding is NOT what the next piece tucks under — the box is
       ratio-bound, so padding only squeezes the content area and never moves
       the box. It is the reserve for the other case: if the copy ever outgrows
       the ratio, min-height:max-content grows the box and this becomes the
       strip. Proven by measurement — raising it from 2.4 to 3.3rem changed the
       rendered height by nothing at all. */
    padding: 0.9rem 1rem 2.6rem;
    box-shadow: 0 0.9rem 1.8rem -0.4rem rgb(var(--scrim-rgb) / 0.16);
  }

  /* Rotation inflates each box's rendered footprint well beyond its layout box
     — about 12px at this width — so the tuck needs NO negative margin at all.
     Zero here already overlaps the pieces by roughly 0.75rem on screen, which
     is as deep as the copy allows before the second piece starts covering the
     first one's last line. */
  .mailpiece:nth-child(1) { rotate: -2.2deg; margin: 0 auto 0 0; }
  .mailpiece:nth-child(2) { rotate:  1.8deg; margin: 0 0 0 auto; }

  /* Two pieces on mobile, not three (Danny 2026-09-01). Three stacked at this
     width was a scroll of near-identical letters, and the point — one template
     per lien type — lands in two. Written as nth-child(n+3) rather than
     :nth-child(3) so an offer that ships five pieces still shows two here.
     display:none, so it leaves the accessibility tree too rather than being
     read out to a screen reader that cannot see it. */
  .mailpiece:nth-child(n + 3) { display: none; }
  .mailpiece:hover { translate: 0 0; }

  .mailpiece-type { font-size: 0.66rem; margin-bottom: 0.95rem; }
  .mailpiece-type .ico { width: 0.85rem; height: 0.85rem; }
  .mailpiece-head { font-size: 1.02rem; margin-bottom: 0.35rem; padding-right: 2.4rem; }
  .mailpiece-body { font-size: 0.8rem; line-height: 1.45; }
  .mailpiece-stamp { top: 0.7rem; right: 0.75rem; }
  .mailpiece-stamp .ico { width: 1.7rem; height: 1.7rem; }

  .mailfan-points { grid-template-columns: minmax(0, 1fr); }
}

/* Below ~368px the copy wraps far enough to eat the reserved strip and the tuck
   starts clipping the last line — measured at 320px, 30px into the body. The
   tilt and the shadows carry the layered read on their own down here, so the
   pieces get a little daylight instead of an overlap. */
@media (max-width: 23rem) {
  .mailpiece:nth-child(2) { margin-top: 1.7rem; }
}

@media (prefers-reduced-motion: reduce) {
  /* Only the transition goes. The tilt is static layout, not motion — flattening
     it here (which this used to do) took the fan away from the people who asked
     for less movement, not less design, and left them with three plain cards. */
  .mailpiece { transition: none; }
}

/* ==========================================================================
   The Commercial Leads layout family
   --------------------------------------------------------------------------
   Offer #4. Scoped to classes only its data produces, so the other three
   render byte-identically (verified by diffing built HTML).

   What is new: a light hero with a three-photo architectural mosaic; the
   explainer beside the intro copy rather than in a band; the eight property
   categories as a hairline matrix carried by drawn building icons; and a
   still, typographic three-step band.

   Same token discipline as the rest of the file — no hex, no raw rgba().
   ========================================================================== */

/* ---------- hero: the mosaic variant ---------- */

.hero-mosaic .hero-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 0.95fr); }

/*
 * Three properties, three sizes, deliberately unaligned. The container owns an
 * aspect ratio and the figures are placed in percentages of it, so the
 * composition holds at every column width instead of only at the one it was
 * eyeballed in — the same lesson the Lien Data offense/defense pair taught.
 */
.mosaic {
  position: relative;
  aspect-ratio: 1 / 0.9;
}

.mosaic figure {
  position: absolute;
  margin: 0;
  border-radius: 0.9rem;
  overflow: hidden;
  background: var(--surface-2);
  box-shadow: 0 1.2rem 2.6rem -0.9rem rgb(var(--scrim-rgb) / 0.28);
}
.mosaic img { width: 100%; height: 100%; object-fit: cover; }

.mosaic-a { top: 0; left: 0; width: 58%; aspect-ratio: 4 / 5; z-index: 1; }
.mosaic-b { top: 14%; right: 0; width: 38%; aspect-ratio: 1 / 1; z-index: 2; }
.mosaic-c {
  bottom: 0;
  right: 4%;
  width: 56%;
  aspect-ratio: 4 / 3;
  z-index: 3;
  /* a ring in the page ground separates it from the tall shot behind */
  box-shadow: 0 0 0 0.5rem var(--bg), 0 1.2rem 2.6rem -0.9rem rgb(var(--scrim-rgb) / 0.3);
}

/* The dotted grid is lifted from the parent page's own collage — the one piece
   of its decoration worth keeping, and it costs nothing to draw. */
.mosaic-dots {
  position: absolute;
  top: -1.1rem;
  left: -1.1rem;
  width: 42%;
  height: 30%;
  z-index: 0;
  pointer-events: none;
  background-image: radial-gradient(rgb(var(--accent-fill-rgb) / 0.42) 1.4px, transparent 1.5px);
  background-size: 0.85rem 0.85rem;
  -webkit-mask-image: linear-gradient(135deg, black, transparent 70%);
  mask-image: linear-gradient(135deg, black, transparent 70%);
}

/* ---------- hero entrance ---------- */

/*
 * The three photographs settle into place from the direction each one sits, and
 * the copy rises under them. Plain CSS animations rather than the shared
 * .reveal hook: the hero is in view at load, so there is nothing to observe,
 * and .reveal is gated on `html[data-js]` — a hero that needs JS to become
 * visible is a hero that is blank if the script fails.
 *
 * THE LCP RULE. Opacity 0 stops an element counting as painted, so the two
 * elements that are the largest contentful paint animate transform ONLY and
 * stay fully opaque the whole way: the tall photograph on desktop, and the h1
 * on a phone, where the mosaic has dropped below the fold. Everything else
 * fades. That is the whole reason there are two keyframe sets that otherwise
 * look identical — do not collapse them.
 *
 * Nothing sets opacity or translate in a base rule, only inside keyframes, so
 * `animation: none` under reduced motion lands on the finished state instead of
 * a hidden one.
 */

@keyframes mosaic-settle {
  from { opacity: 0; transform: translate(var(--fx, 0), var(--fy, 1rem)) scale(0.955); }
  to   { opacity: 1; transform: none; }
}
/* the LCP-safe twin: same movement, never transparent */
@keyframes mosaic-settle-opaque {
  from { transform: translate(var(--fx, 0), var(--fy, 1rem)) scale(0.965); }
  to   { transform: none; }
}
@keyframes hero-rise {
  from { opacity: 0; translate: 0 0.9rem; }
  to   { opacity: 1; translate: 0 0; }
}
@keyframes hero-rise-opaque {
  from { translate: 0 1.15rem; }
  to   { translate: 0 0; }
}
@keyframes dots-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.mosaic figure {
  animation: mosaic-settle 760ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
/*
 * `.mosaic figure` above is (0,1,1) and sets animation-name and -delay through
 * the shorthand, so a bare `.mosaic-a` at (0,1,0) loses both — the first pass
 * had every photograph fading in at zero delay, which is neither the stagger
 * nor the LCP exemption. The class has to be qualified to outrank it.
 */
.mosaic figure.mosaic-a {
  --fx: -1.4rem;
  --fy: 1.1rem;
  /* the LCP element on desktop — transform only, never transparent */
  animation-name: mosaic-settle-opaque;
  animation-duration: 720ms;
}
.mosaic figure.mosaic-b { --fx: 1.6rem; --fy: -1.1rem; animation-delay: 150ms; }
.mosaic figure.mosaic-c { --fx: 1.1rem; --fy: 1.7rem; animation-delay: 290ms; }

.mosaic-dots { animation: dots-in 700ms ease 430ms both; }

.hero-mosaic .hero-copy > * {
  animation: hero-rise 620ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
.hero-mosaic .hero-copy > *:nth-child(1) { animation-delay: 0ms; }
.hero-mosaic .hero-copy > *:nth-child(2) { animation-delay: 70ms; }
.hero-mosaic .hero-copy > *:nth-child(3) { animation-delay: 150ms; }
.hero-mosaic .hero-copy > *:nth-child(4) { animation-delay: 230ms; }
.hero-mosaic .hero-copy > *:nth-child(5) { animation-delay: 310ms; }
.hero-mosaic .hero-copy > *:nth-child(6) { animation-delay: 390ms; }
/* the LCP element on a phone, where the mosaic is below the fold */
.hero-mosaic .hero-copy > h1 { animation-name: hero-rise-opaque; }

@media (prefers-reduced-motion: reduce) {
  .mosaic figure,
  .mosaic-dots,
  .hero-mosaic .hero-copy > * { animation: none; }
}

@media (max-width: 55.99rem) {
  .hero-mosaic .hero-inner { grid-template-columns: minmax(0, 1fr); }
  /*
   * Keep the desktop ratio. Squashing it to 0.78 saved vertical space and cost
   * the composition: at that height the wide warehouse shot covered most of the
   * office shot and a third of the apartment, so the set stopped reading as
   * three properties. The percentages are shared with desktop, so the ratio is
   * the only thing that has to hold.
   */
  .mosaic { aspect-ratio: 1 / 0.95; max-width: 27rem; }
  .mosaic-dots { display: none; }
}

/* ---------- intro copy beside the explainer ---------- */

.intro-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
.intro-copy > * + * { margin-top: 1rem; }
.intro-copy h2 { margin-bottom: 0.2rem; }
.intro-p { color: var(--text-dim); max-width: 34rem; }
.intro-ticks { margin-top: 1.4rem; padding-top: 1.2rem; border-top: 1px solid var(--border); }
.intro-ticks li { font-size: var(--fs-base); }

.intro-shot {
  margin: 0;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid var(--border);
  box-shadow: 0 1.6rem 3.4rem -1.4rem rgb(var(--scrim-rgb) / 0.4);
  background: var(--surface-2);
}
.intro-shot img { display: block; width: 100%; height: auto; }

/*
 * The property-type carousel. Three visible slots — two small tiles side by
 * side on the left, one large on the right — and a queue behind them.
 *
 * Every cell is absolutely positioned and every slot is a set of percentages
 * of the same box, so one transition on `inset` + `opacity` moves a cell
 * between any two slots. Growing the small tile into the large one and sliding
 * the old large one out are therefore the SAME animation, which is what makes
 * them arrive together instead of drifting apart. The slot geometry is data:
 * change the numbers here and nothing in the JS needs to know.
 *
 * With JS off the cells stack in source order at the first three slots and the
 * section still shows three property types — the driver only re-labels which
 * cell is in which slot.
 */
.cargo {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: 14px;
}

.cargo-track { list-style: none; margin: 0; padding: 0; position: absolute; inset: 0; }

.cargo-cell {
  position: absolute;
  overflow: hidden;
  border-radius: 10px;
  background: var(--surface-2);
  box-shadow: 0 0.9rem 2rem -0.8rem rgb(var(--scrim-rgb) / 0.35);
  /* the queue position, and where a cell sits before the driver labels it */
  left: -26%;
  top: 30%;
  width: 22%;
  height: 40%;
  opacity: 0;
  z-index: 0;
}
.cargo-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }

.cargo-cell[data-slot="a"] { left: 0; top: 30%; width: 22%; height: 40%; opacity: 1; z-index: 1; }
.cargo-cell[data-slot="b"] { left: 24%; top: 30%; width: 22%; height: 40%; opacity: 1; z-index: 2; }
.cargo-cell[data-slot="big"] { left: 48%; top: 0; width: 52%; height: 100%; opacity: 1; z-index: 3; }
/* out to the right, and out of the way underneath */
.cargo-cell[data-slot="out"] { left: 104%; top: 0; width: 52%; height: 100%; opacity: 0; z-index: 1; }

/* No JS: the first three cells hold the three visible slots. */
html:not([data-js]) .cargo-cell:nth-child(1) { left: 0; top: 30%; width: 22%; height: 40%; opacity: 1; }
html:not([data-js]) .cargo-cell:nth-child(2) { left: 24%; top: 30%; width: 22%; height: 40%; opacity: 1; }
html:not([data-js]) .cargo-cell:nth-child(3) { left: 48%; top: 0; width: 52%; height: 100%; opacity: 1; }

html[data-js] .cargo-cell {
  /* the slight overshoot is the "snap into place" — it lands, then settles */
  transition:
    left 780ms cubic-bezier(0.22, 1.16, 0.36, 1),
    top 780ms cubic-bezier(0.22, 1.16, 0.36, 1),
    width 780ms cubic-bezier(0.22, 1.16, 0.36, 1),
    height 780ms cubic-bezier(0.22, 1.16, 0.36, 1),
    opacity 420ms ease;
}
/* leaving is a plain slide: overshooting something on its way off screen
   reads as a glitch rather than as arrival */
html[data-js] .cargo-cell[data-slot="out"] { transition-timing-function: ease-in; }

.cargo-tag {
  position: absolute;
  left: 0.8rem;
  bottom: 0.8rem;
  padding: 0.22rem 0.6rem 0.28rem;
  border-radius: 999px;
  border: 1px solid rgb(var(--on-scrim-rgb) / 0.28);
  background: rgb(var(--scrim-rgb) / 0.5);
  backdrop-filter: blur(7px) saturate(1.1);
  -webkit-backdrop-filter: blur(7px) saturate(1.1);
  color: var(--on-scrim);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.01em;
  opacity: 0;
  transition: opacity 300ms ease 240ms;
  pointer-events: none;
}
/* the label belongs to whichever photograph is currently the subject */
.cargo-cell[data-slot="big"] .cargo-tag { opacity: 1; }
html:not([data-js]) .cargo-cell:nth-child(3) .cargo-tag { opacity: 1; }

@media (max-width: 55.99rem) { .cargo { aspect-ratio: 4 / 3.2; } }

/*
 * On a phone the two small tiles land around 86px wide, which is too small to
 * read as a property and too small to label. Slot 'a' retires to the queue and
 * the rotation runs one-small-plus-large instead: same driver, same photographs,
 * one fewer thing on screen. The driver never learns about this — it only names
 * slots, and the slots are geometry.
 */
@media (max-width: 34rem) {
  .cargo { aspect-ratio: 4 / 3.4; }
  .cargo-cell[data-slot="a"] { left: -34%; opacity: 0; width: 30%; top: 26%; height: 48%; }
  .cargo-cell[data-slot="b"] { left: 0; width: 30%; top: 26%; height: 48%; }
  .cargo-cell[data-slot="big"] { left: 34%; width: 66%; top: 0; height: 100%; }
  .cargo-cell[data-slot="out"] { left: 104%; width: 66%; }
  html:not([data-js]) .cargo-cell:nth-child(1) { opacity: 0; }
  html:not([data-js]) .cargo-cell:nth-child(2) { left: 0; width: 30%; top: 26%; height: 48%; }
  html:not([data-js]) .cargo-cell:nth-child(3) { left: 34%; width: 66%; top: 0; height: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .cargo-cell, .cargo-tag { transition: none; }
}

.video-shell-intro {
  margin-bottom: 0;
  border-radius: 14px;
  border: 1px solid var(--border);
  box-shadow: 0 1.6rem 3.4rem -1.4rem rgb(var(--scrim-rgb) / 0.4);
}
.video-shell-intro .video-play { width: clamp(3.6rem, 7vw, 5rem); height: clamp(3.6rem, 7vw, 5rem); }
/* The band's scrim is built for a dark still; this poster is a light title card,
   so the hero-variant treatment applies here too — darken only behind the play. */
.video-shell-intro .video-scrim {
  background: radial-gradient(closest-side at 50% 50%, rgb(var(--scrim-rgb) / 0.28), rgb(var(--scrim-rgb) / 0.04) 72%, transparent);
}
.video-shell-intro .video-facade:hover .video-scrim {
  background: radial-gradient(closest-side at 50% 50%, rgb(var(--scrim-rgb) / 0.18), transparent 72%);
}

@media (max-width: 55.99rem) {
  .intro-grid { grid-template-columns: minmax(0, 1fr); }
  /* Source order stands. Hoisting the video above its own eyebrow put an
     unlabelled play button directly under the hero, with nothing saying what
     it was about to play. */
}

/* ---------- the eight property categories ---------- */

/*
 * A hairline matrix, not cards. Eight tiles sharing borders read as a directory
 * of what the database covers, which is what this section is; eight separate
 * rounded cards would have been the fourth grid of cards in this template.
 * The drawn icons are the thing carrying it.
 */
/*
 * The hairlines are the grid's own background showing through 1px gaps, not
 * per-tile borders. The first pass drew them with nth-child box-shadows and got
 * them wrong: `:nth-child(-n + 4)` is written for the four-column layout, and
 * because a media query adds no specificity it kept beating the plain `.ptype`
 * rule inside the narrow breakpoints — tile four lost its divider at every
 * width below four columns. This way the dividers are correct at 4, 2 and 1
 * columns with no arithmetic to get wrong.
 */
.ptype-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  border: 1px solid var(--border);
  border-radius: 1rem;
  overflow: hidden;
  background: var(--border);
}

.ptype {
  position: relative;
  padding: clamp(1.15rem, 2.4vw, 1.6rem);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  background: var(--surface);
  transition: background-image 220ms ease;
}
/* an opaque tile over a border-coloured grid, so the hover tint has to be a
   layer on top of the surface rather than a translucent background */
.ptype:hover {
  background-image: linear-gradient(rgb(var(--accent-tint-rgb) / 0.6), rgb(var(--accent-tint-rgb) / 0.6));
}

.ptype-ico { color: var(--accent); }
.ptype-ico .ico { width: 2.4rem; height: 2.4rem; stroke-width: 1.5; }

.ptype-name { font-size: var(--fs-h3); line-height: 1.2; }
.ptype-sub { font-size: var(--fs-sm); color: var(--text-dim); text-wrap: pretty; }

.ptype-note {
  display: flex;
  gap: 0.55rem;
  align-items: flex-start;
  margin-top: clamp(1.2rem, 3vw, 1.8rem);
  max-width: 52rem;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.ptype-note .ico { color: var(--text-muted); margin-top: 0.25em; flex: none; }

@media (max-width: 61.99rem) { .ptype-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 34rem) { .ptype-grid { grid-template-columns: minmax(0, 1fr); } }

@media (prefers-reduced-motion: reduce) { .ptype { transition: none; } }

/* ---------- search, analyze, market ---------- */

.hiw-head { max-width: 46rem; margin-bottom: clamp(2rem, 5vw, 3.2rem); }
.hiw-head h2 { color: var(--on-deep); }
.hiw-lead { margin-top: 0.7rem; color: rgb(var(--on-deep-rgb) / 0.76); }

.hiw-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--steps, 3), minmax(0, 1fr));
  --hiw-gap: clamp(1.1rem, 2.6vw, 2rem);
  gap: var(--hiw-gap);
}

.hiw-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: 14px;
  border: 1px solid rgb(var(--on-deep-rgb) / 0.14);
  background: rgb(var(--on-deep-rgb) / 0.05);
  /* NOT overflow:hidden — the connector segment lives at left:100% and would
     be clipped away. The photograph clips itself instead. */
}

.hiw-shot {
  position: relative;
  margin: 0;
  aspect-ratio: 1 / 1;
  border-radius: 13px 13px 0 0;
  overflow: hidden;
  background: rgb(var(--on-deep-rgb) / 0.08);
}
.hiw-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

.hiw-body {
  padding: clamp(1.05rem, 2.2vw, 1.5rem);
  display: grid;
  gap: 0.4rem;
  align-content: start;
}

/*
 * The step number is solid on a scrim in the photograph's corner, not an
 * outlined glyph. It was `-webkit-text-stroke` on the display face, which
 * outlines whatever the font's own contour happens to be — in Roboto the 3's
 * middle terminal is a short flat spur, and outlining it produced a wedge
 * hanging in the counter. Every weight and stroke width still showed it,
 * because it is the glyph, not the stroke; and the display face changes per
 * brand, so the artifact would change shape across sixty partners. A solid
 * fill has no contour to trace.
 */
.hiw-n {
  position: absolute;
  left: 0.85rem;
  bottom: 0.85rem;
  min-width: 2.5rem;
  padding: 0.18rem 0.5rem 0.24rem;
  border-radius: 8px;
  border: 1px solid rgb(var(--on-scrim-rgb) / 0.3);
  background: rgb(var(--scrim-rgb) / 0.5);
  backdrop-filter: blur(7px) saturate(1.1);
  -webkit-backdrop-filter: blur(7px) saturate(1.1);
  color: var(--on-scrim);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  line-height: 1.15;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
/* the no-photograph fallback keeps the old placement */
.hiw-n-bare {
  position: static;
  margin: clamp(1.05rem, 2.2vw, 1.5rem) 0 0 clamp(1.05rem, 2.2vw, 1.5rem);
  align-self: start;
  background: rgb(var(--on-deep-rgb) / 0.08);
  border-color: rgb(var(--on-deep-rgb) / 0.22);
  color: var(--on-deep);
  backdrop-filter: none;
}
.hiw-t { color: var(--on-deep); font-size: var(--fs-h3); }
.hiw-b { font-size: var(--fs-sm); color: rgb(var(--on-deep-rgb) / 0.74); }

/*
 * Chevrons sit in the gutters rather than inside a card, so they read as
 * "and then" instead of as part of either step, and they are centred on the
 * cards' own height. The drawn connector line behind them is gone at Danny's
 * call — with photographs in the cards the line was a third horizontal thing
 * competing with the image edges, and the chevrons carry the direction on
 * their own. They still animate in after their card, so the sequence is still
 * something the reader watches assemble rather than finds already assembled.
 */
.hiw-arrow {
  position: absolute;
  top: 50%;
  right: calc(var(--hiw-gap) / -2);
  translate: 50% -50%;
  display: grid;
  place-items: center;
  color: rgb(var(--on-deep-rgb) / 0.62);
}
.hiw-arrow .ico { width: 1.15rem; height: 1.15rem; stroke-width: 2.2; }

html[data-js] .hiw-arrow {
  opacity: 0;
  scale: 0.55;
  transition: opacity 300ms ease, scale 420ms cubic-bezier(0.22, 1.2, 0.36, 1);
  transition-delay: calc(var(--d, 0) * 70ms + 340ms);
}
html[data-js] .hiw-card.in .hiw-arrow { opacity: 1; scale: 1; }

@media (max-width: 55.99rem) {
  .hiw-steps { grid-template-columns: minmax(0, 1fr); --hiw-gap: 2.1rem; }
  /* stacked, the process runs downward, so the chevron turns with it */
  .hiw-arrow {
    top: auto;
    right: auto;
    bottom: calc(var(--hiw-gap) / -2);
    left: 50%;
    translate: -50% 50%;
    rotate: 90deg;
  }
}

@media (prefers-reduced-motion: reduce) {
  html[data-js] .hiw-arrow { transition: none; opacity: 1; scale: 1; }
}
