/* Marketing landing page — shared by templates/marketing/landing.html (ka)
   and landing_en.html (en). Both pages carried their own identical copy of
   this stylesheet until it was extracted here; the only rule that ever
   differed is .display-h1's font-size, which landing.html still overrides
   inline because the italic Georgian hero word needs a lower cap.

   Paths are relative to /static/css/, not resolved through url_for(). */

/* ── Landing page logo controls ─────────────────────────────────────
   Tweak these to size and position the header logo without touching
   any other CSS. All values in px except --lp-logo-ratio. Negative
   offsets move the logo left / up.
   ─────────────────────────────────────────────────────────────── */
:root {
  /* logo.png is 3000x1000 (3:1) but the artwork inside it is 4.2:1 — there is
     ~16% transparent padding top and bottom. So the mask box is always taller
     than the mark you see: at 118px wide the box is 39px tall and the visible
     logo is 28px. Size by the width below and read the real height as
     width / 4.2. */
  --lp-logo-width: 118px;
  /* logo width (box height scales via --lp-logo-ratio) */
  --lp-logo-ratio: 3 / 1;
  /* file aspect ratio of logo.png — not the artwork's */
  --lp-logo-offset-x: 0px;
  /* nudge horizontally (+ = right) */
  --lp-logo-offset-y: 0px;
  /* nudge vertically  (+ = down)  */
  --lp-topbar-pad-y: 13px;
  /* topbar top/bottom padding (affects header height) */
  --lp-em-flute: 1;
  /* the reeded-glass ribbing inside the accent words. 0 is off. */
  /* Each listing's house gets its own tile rather than one shared paper.
     Muted mid-tones: the drawings carry white and cream fills, so the tile
     has to be dark enough for them to read as the figure, and desaturated
     enough that six of them in a table don't fight the page. */
  --lp-house-tile-1: #3E4C45;
  --lp-house-tile-2: #9A8A8D;
  --lp-house-tile-3: #5A4530;
  --lp-house-tile-4: #7A5A7C;
  --lp-house-tile-5: #7C7A75;
  --lp-house-edge: #C2A368;
  /* Root-absolute, not ../: these are also read from inline style attributes,
     where a relative url() inside a custom property resolves against the
     document rather than this file. */
  --lp-house-img-1: url("/static/img/houses/house-1.webp");
  --lp-house-img-2: url("/static/img/houses/house-2.webp");
  --lp-house-img-3: url("/static/img/houses/house-3.webp");
  --lp-house-img-4: url("/static/img/houses/house-4.webp");
  --lp-house-img-5: url("/static/img/houses/house-5.webp");
  /* the thin warm line around each tile */
  --lp-em-sweep: 1;
  /* strength of the highlight that travels across the accent words. 0 is off. */
  --lp-em-sweep-time: 11s;
  /* how often it comes round */
  --lp-em-glow: .25;
  /* halo around the accent words in the headings. Scales the three blur
     radii AND their alphas together, so turning it down dims as well as
     tightens — scaling radius alone just left a hot ring. 0 kills it, 1 is
     bright enough to light the facade behind the hero line. */
  --lp-measure: clamp(1200px, 1200px + (100vw - 1500px) * 0.34, 1560px);
  --lp-bleed: clamp(0px, (100vw - 1600px) * 0.3, 280px);
  /* --lp-measure is the page's content measure: the hero inner, the hero
     foot and the hero's sizing derive from it. --lp-bleed is how far a .section.is-wide escapes it on EACH
     side, and stays 0 below a 1600px viewport where there is no spare margin
     to spend. Both fluid rather than stepped — an earlier version jumped at
     1600/2000/2400 and a laptop sitting near one of those edges snapped
     between two layouts instead of scaling. A large min on the measure is
     safe on a small screen: max-width only caps a block, it cannot force one
     wider than its parent. */
  --lp-font-prose: 'FiraGO', 'Noto Sans Georgian', var(--ds-font-body);
  /* Reading copy on the landing page ONLY — the hero standfirst, the section
     leads and the pillar/role paragraphs. landing.css is loaded by the two
     marketing templates and nothing else, so this cannot reach the CRM. */
  --lp-topbar-h: calc(var(--lp-logo-width) / 3 + var(--lp-topbar-pad-y) * 2 + 1px);
  /* derived: logo box + padding + border. Used by the facade hero to sit
     underneath the bar, so do not set it by hand. */
}

/* Narrow viewport overrides — shrink logo on mobile if you want */
@media (max-width: 620px) {
  :root {
    --lp-logo-width: 96px;
    --lp-topbar-pad-y: 10px;
  }
}

/* ── FiraGO ─────────────────────────────────────────────────────────────
   Carrois Corporate / bBox Type; the Georgian drawn by Akaki Razmadze.
   SIL Open Font License, so there is nothing to clear before shipping.

   Chosen over Noto Sans Georgian and GL Mkafio because its Georgian and its
   Latin were designed as one system. The hero standfirst is a single line of
   mixed script — "myhome.ge, ss.ge, გუნდი" — and with Inter+Noto, or with
   Mkafio, the two halves have visibly different weight and rhythm. FiraGO
   reads as one voice. It is also a Fira Sans derivative, i.e. a face built
   for interface and body text rather than a display family.

   Self-hosted: not on Google Fonts. Subsetted to Latin, Georgian,
   punctuation and currency — by BLOCK rather than by the exact characters
   currently on the page, because the copy gets translated and rewritten and
   an exact subset would break on the next edit. 786 KB TTF -> 45 KB woff2.

   Note: FiraGO has no Mtavruli (U+1C90+), the Georgian uppercase block. Do
   not text-transform Georgian in this face; the stack falls through to Noto
   Sans Georgian, which does carry it. */

@font-face {
  font-family: 'FiraGO';
  src: url('../fonts/firago-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'FiraGO';
  src: url('../fonts/firago-medium.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* ──────────────────────────────────────────────────────────────── */

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  /* Same ground as the facade hero, which is #000 — --ds-surf-2 is #0A0B0D
     and the seam between the two sections was visible. */
  background: #000;
  color: var(--ds-ink);
  font-family: var(--ds-font-body);
  -webkit-font-smoothing: antialiased;
  /* Safety net — prevent any rogue element from creating horizontal
     scroll on narrow phones. "clip" (vs "hidden") keeps
     position:sticky on .topbar working. */
  overflow-x: clip;
}

@supports not (overflow: clip) {

  html,
  body {
    overflow-x: hidden;
  }
}

a {
  color: inherit;
  text-decoration: none;
}

img {
  max-width: 100%;
  display: block;
}

/* ---------- Layout ---------- */
.lp {
  max-width: var(--lp-measure);
  margin: 0 auto;
  padding: 0 28px;
}

.section {
  padding: 110px 0;
  border-top: 1px solid var(--ds-line-2);
}

/* Sections whose content is a grid or a mock rather than prose. They step out
   of the measure by --lp-bleed on each side so the page does not visibly pinch
   between the full-bleed hero and the full-bleed closing. Copy inside them is
   unaffected: .lead and the rest keep their own max-width, so
   the reading line stays the same length while the grid gets wider. */
.section.is-wide {
  margin-inline: calc(-1 * var(--lp-bleed));
}

.section:first-of-type,
.ch-deal,
.ch-agency,
.ch-ai {
  border-top: 0;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--ds-font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ds-ink-4);
}

.eyebrow::before {
  content: "";
  display: inline-block;
  width: 24px;
  height: 1px;
  background: var(--ds-blue);
}

.display-h1 {
  font-family: var(--ds-font-display);
  font-size: clamp(42px, 6vw, 76px);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1.02;
  margin: 20px 0 28px;
}

.display-h1 em {
  font-style: italic;
  font-weight: 400;
  color: var(--ds-blue);
}

.display-h2 {
  font-family: var(--ds-font-display);
  font-size: clamp(32px, 4.5vw, 52px);
  font-weight: 500;
  letter-spacing: -0.025em;
  line-height: 1.05;
  margin: 14px 0 24px;
}

.display-h2 em {
  font-style: italic;
  font-weight: 400;
  color: var(--ds-blue);
}

.lead {
  font-size: 17px;
  line-height: 1.6;
  color: var(--ds-ink-2);
  max-width: 680px;
  font-family: var(--lp-font-prose);
}

/* ---------- Top bar ---------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  /* Filled by default, and landing-hero.js clears it with .is-top while the
     page is at the top so the facade runs edge to edge under the bar. This
     way round because without JS a solid bar is right and a permanently
     transparent one is not. */
  background: rgba(11, 13, 16, 0.72);
  backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid var(--ds-line-2);
  transition: background .25s ease, border-color .25s ease, backdrop-filter .25s ease;
}

.topbar.is-top {
  background: transparent;
  backdrop-filter: none;
  border-bottom-color: transparent;
}

/* Full-bleed: the bar ignores the content measure that every section below
   it keeps, and runs out to the screen edges. Scoped under .topbar so it
   outranks the .lp class this element also carries — .lp sets both the
   max-width and, on narrow screens, a padding shorthand that would
   otherwise flatten the bar's vertical padding and leave the row taking
   its height from the CTA. */
.topbar .topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  max-width: none;
  padding: var(--lp-topbar-pad-y) clamp(16px, 2.2vw, 40px);
}

.topbar .brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--ds-font-display);
  font-weight: 600;
  font-size: 17px;
  letter-spacing: -0.02em;
  transform: translate(var(--lp-logo-offset-x), var(--lp-logo-offset-y));
}

.topbar .brand-mark {
  display: block;
  width: var(--lp-logo-width);
  aspect-ratio: var(--lp-logo-ratio);
  background-color: var(--ds-ink);
  -webkit-mask: url("../img/logo-mask.webp") no-repeat center / contain;
  mask: url("../img/logo-mask.webp") no-repeat center / contain;
}

.topbar .links {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 13px;
  color: var(--ds-ink-3);
}

.topbar .links a.nav {
  padding: 7px 11px;
  border-radius: 7px;
  transition: color .15s ease, background .15s ease;
}

/* Header controls are smaller and flatter than the page's buttons, and they
   do not lift on hover — a bar that twitches under the cursor reads cheap. */
.topbar .btn {
  padding: 8px 15px;
  font-size: 13.5px;
  font-weight: 500;
  border-radius: 8px;
}

.topbar .btn:hover {
  transform: none
}

.topbar .lang-toggle {
  font-size: 10.5px;
  padding: 1px;
  margin: 0 2px
}

.topbar .lang-toggle a {
  padding: 4px 7px;
  border-radius: 5px
}

.topbar .links a.nav:hover {
  color: var(--ds-ink);
  background: var(--ds-surf-3);
}

.lang-toggle {
  display: inline-flex;
  gap: 2px;
  align-items: center;
  font-family: var(--ds-font-mono);
  font-size: 11px;
  border: 1px solid var(--ds-line);
  border-radius: 8px;
  padding: 2px;
}

.lang-toggle span,
.lang-toggle a {
  padding: 4px 8px;
  border-radius: 6px;
  color: var(--ds-ink-4);
  cursor: pointer;
  text-decoration: none;
}

.lang-toggle span.on,
.lang-toggle a.on {
  background: var(--ds-ink);
  color: var(--ds-surf);
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--ds-font-body);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: -0.005em;
  padding: 12px 20px;
  border-radius: 10px;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform .12s ease, background .18s ease, border-color .18s ease;
}

.btn:hover {
  transform: translateY(-1px);
}

.btn.primary {
  background: var(--ds-ink);
  color: var(--ds-surf);
}

.btn.primary:hover {
  background: var(--ds-ink-2);
}

.btn.ghost {
  background: transparent;
  color: var(--ds-ink);
  border-color: var(--ds-line);
}

.btn.ghost {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

/* Hover: a rainbow ring. A big conic gradient sits behind the button and a
   dark fill covers all of it but a thin edge; spinning the gradient is a
   transform, so the ring turns without repainting anything. */
.btn.ghost::before,
.btn.ghost::after {
  content: "";
  position: absolute;
  opacity: 0;
  transition: opacity .25s ease;
  pointer-events: none;
}

.btn.ghost::before {
  left: 50%;
  top: 50%;
  width: max(300%, 240px);
  aspect-ratio: 1;
  translate: -50% -50%;
  background: conic-gradient(#ff6b6b, #ffb84d, #ffe66d, #6bdb8f, #4dc3ff, #7f9dff, #c77dff, #ff6b6b);
  z-index: -2;
}

.btn.ghost::after {
  inset: 1.5px;
  border-radius: inherit;
  background: var(--ds-surf);
  z-index: -1;
}

.btn.ghost:hover {
  border-color: transparent;
  box-shadow: 0 0 22px -4px rgba(127, 157, 255, .45);
}

.btn.ghost:hover::before,
.btn.ghost:hover::after {
  opacity: 1;
}

.btn.lg {
  padding: 14px 24px;
  font-size: 15px;
  border-radius: 12px;
}

.btn-row {
  display: inline-flex;
  gap: 12px;
  flex-wrap: wrap;
}

/* ---------- Chapter 1 — The deal ---------- */
/* One home carried from a Scout alert to a commission, as a pipeline: four
   columns always on screen, the home's card on the track above them, the
   picked step lit. Markup: templates/marketing/_deal.html; behaviour:
   js/landing-hero.js.

   Every scene animation runs FROM an initial state INTO the element's
   natural style (animation-fill-mode: backwards), so the natural style is
   the finished frame. Nothing autoplays: the page loads on every finished
   frame and a click replays one scene. Lite mode and reduced motion never
   add .go, so a click there only moves the lit step. */
/* Edge to edge, like the hero and the closing either side of it. Inside
   the page measure each column was ~260px, Georgian claims ran to three
   lines and the section came out taller than the screen. */
.ch-deal {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  padding: 28px clamp(12px, 1.6vw, 28px);
  scroll-margin-top: var(--lp-topbar-h);
}

/* The section is the card: it fills the screen below the topbar. */
.deal {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* A chapter is a screen on a laptop; on a big monitor it stops growing
     (same width cap and height cap in all three chapters) so text and UI
     keep laptop proportions instead of floating in empty boxes. */
  max-width: 1760px;
  margin-inline: auto;
  min-height: min(calc(100svh - var(--lp-topbar-h) - 56px), 1000px);
  padding: clamp(22px, 2vw, 34px);
  border-radius: 22px;
  border: 1px solid rgba(255, 255, 255, .07);
  background: #030304;
}

/* The building, behind everything: one canvas, drawn once by
   landing-hero.js. It was ~840 window elements with ~190 blurred glows,
   and every repaint above them — a column's hover border, a scene
   replaying — re-rastered them; at 2x pixel density that was most of what
   the section cost. A bitmap has nothing to restyle or re-raster. */
.dz-facade {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  opacity: .3;
}

.deal::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(0, 0, 0, .55), rgba(0, 0, 0, .82) 45%, rgba(0, 0, 0, .9));
}

.deal>*:not(.dz-facade) {
  position: relative;
  z-index: 2;
}

.deal-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px 32px;
}

.deal-head .display-h2 {
  margin: 0;
}

/* ── the track ── */
.deal-rail {
  height: 52px;
  margin: clamp(10px, 1.3vw, 18px) 0 clamp(6px, .8vw, 10px);
}

.deal-rail::before,
.rail-fill {
  content: "";
  position: absolute;
  top: 50%;
  left: 12.5%;
  height: 1px;
}

.deal-rail::before {
  right: 12.5%;
  background: rgba(255, 255, 255, .12);
}

/* The line and the card move by transform only, never width or left, so
   a step change is compositor work, not a layout per frame. --k is how far
   along the track the step is; --railw is the track's width in px (kept
   by landing-hero.js). */
.rail-fill {
  width: 75%;
  background: var(--ds-blue);
  transform-origin: left;
  transform: scaleX(calc(var(--k, 0) / .75));
  transition: transform .9s cubic-bezier(.65, 0, .35, 1);
}

.deal[data-step="2"] { --k: .25; }
.deal[data-step="3"] { --k: .5; }
.deal[data-step="4"] { --k: .75; }

.deal-rail>i {
  position: absolute;
  top: 50%;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 50%;
  background: #0B0C10;
  border: 1px solid rgba(255, 255, 255, .25);
  transition: background .5s ease, border-color .5s ease;
}

.deal-rail .d1 { left: 12.5%; }
.deal-rail .d2 { left: 37.5%; }
.deal-rail .d3 { left: 62.5%; }
.deal-rail .d4 { left: 87.5%; }

.deal .deal-rail .d1,
.deal[data-step="2"] .deal-rail .d2,
.deal[data-step="3"] .deal-rail .d2,
.deal[data-step="3"] .deal-rail .d3,
.deal[data-step="4"] .deal-rail i {
  background: var(--ds-blue);
  border-color: var(--ds-blue);
}

/* The home itself, riding the track. */
.dz-token {
  position: absolute;
  top: 50%;
  left: 12.5%;
  translate: -50% -50%;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 12px 6px 6px;
  border-radius: 12px;
  border: 1px solid color-mix(in srgb, var(--ds-blue) 45%, transparent);
  background: rgba(14, 15, 20, .94);
  box-shadow: 0 12px 30px rgba(0, 0, 0, .6), 0 0 0 4px color-mix(in srgb, var(--ds-blue) 10%, transparent);
  transform: translateX(calc(var(--railw, 0px) * var(--k, 0)));
  transition: transform .9s cubic-bezier(.65, 0, .35, 1);
  white-space: nowrap;
}

.col-pick:focus-visible {
  outline: 2px solid var(--ds-blue);
  outline-offset: 2px;
}

/* The play-through: one unbroken glide from the first dot to the last
   (--ride is 75% of the track in px, set by the script with --ride-time),
   the line growing behind it. Transform only, on the compositor. While
   riding, the card's own left stays at the first dot ([data-step] so this
   outranks the per-step positions); .is-held pauses it while the section
   is off screen. */
.deal.is-riding[data-step] .dz-token {
  transition: none;
  animation: dz-ride var(--ride-time, 6.6s) linear forwards;
}

.deal.is-riding[data-step] .rail-fill {
  transition: none;
  animation: dz-ride-fill var(--ride-time, 6.6s) linear forwards;
}

.deal.is-held[data-step] .dz-token,
.deal.is-held[data-step] .rail-fill {
  animation-play-state: paused;
}

@keyframes dz-ride {
  from { transform: none; }
  to { transform: translateX(var(--ride)); }
}

@keyframes dz-ride-fill {
  from { transform: scaleX(0); }
  to { transform: none; }
}


.dz-token .dz-ph.sm {
  width: 40px;
  font-size: 10px;
}

.dz-token>span:last-child {
  display: grid;
  gap: 2px;
  font-family: var(--ds-font-mono);
}

.dz-token b {
  font-size: 12px;
  font-weight: 600;
}

.dz-token em {
  font-size: 11px;
  font-style: normal;
  color: var(--ds-ink-3);
}

/* ── the columns ── */
/* Three shared rows — claim, scene, proof — so every column's scene starts
   and ends on the same lines whatever its claim's length, and the scene row
   takes whatever height the card has left. */
.deal-cols {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-rows: auto minmax(clamp(270px, 34svh, 380px), 1fr) auto;
  gap: clamp(12px, 1.3vw, 22px);
}

.deal-col {
  position: relative;
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
  min-width: 0;
  padding: 16px;
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, .06);
  background: rgba(10, 11, 14, .55);
  cursor: pointer;
  /* No transition on the border or fill: a fade repaints the whole column
     every frame of it, one swap repaints it once. */
}

.deal-col.is-active {
  border-color: color-mix(in srgb, var(--ds-blue) 40%, transparent);
  background: rgba(12, 13, 17, .86);
}

@media (hover: hover) {
  .deal-col:not(.is-active):hover {
    border-color: rgba(255, 255, 255, .16);
  }
}

.col-pick {
  position: absolute;
  inset: 0;
  z-index: 3;
  padding: 0;
  border: 0;
  border-radius: inherit;
  background: none;
  cursor: pointer;
}

.col-head {
  display: grid;
  gap: 8px;
}

.col-num {
  font: 500 11px/1 var(--ds-font-mono);
  letter-spacing: .06em;
  color: var(--ds-ink-4);
}

.deal-col.is-active .col-num {
  color: var(--ds-blue);
}

.col-head h3 {
  margin: 0;
  font-family: var(--ds-font-display);
  font-size: clamp(16px, 1.25vw, 20px);
  font-weight: 500;
  line-height: 1.28;
  letter-spacing: -0.01em;
  color: var(--ds-ink);
}

.col-proof {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
  font-family: var(--lp-font-prose);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ds-ink-3);
}

.col-proof li {
  position: relative;
  padding-left: 14px;
}

.col-proof li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .7em;
  width: 6px;
  height: 1px;
  background: var(--ds-ink-5);
}

/* ── the scene: a small window of the real app ── */
/* The scene's height is its grid row, never its content: content-sized
   scenes let the tallest one stretch the row and the section ran past the
   bottom of the window. Each scene is designed to fit the row's minimum. */
.col-scene {
  position: relative;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  container-type: size;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, .08);
  background: rgba(13, 14, 17, .95);
  color: var(--ds-ink);
  font-family: var(--ds-font-body);
}

/* The scene scales its contents to fit its own box, by width or by height,
   whichever is tighter. Set on the children: container units resolve against
   an ancestor container, so on .col-scene itself they would skip it. */
.col-scene>* {
  font-size: clamp(8.5px, min(3.6cqw, 3.9cqh), 13px);
}

/* The client scene is the tallest in ems (its client line wraps to keep
   the budget whole), so it scales a little smaller by height. */
.col-scene>.sc-3 {
  font-size: clamp(8.5px, min(3.6cqw, 3.6cqh), 13px);
}

.dz-bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: .9em;
  height: 2.6em;
  padding: 0 1em;
  border-bottom: 1px solid rgba(255, 255, 255, .06);
}

.dz-dots i {
  display: inline-block;
  width: .55em;
  height: .55em;
  margin-right: .35em;
  border-radius: 50%;
  background: rgba(255, 255, 255, .14);
}

.dz-crumb {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font: 500 .82em/1 var(--ds-font-mono);
  color: var(--ds-ink-5);
}

.dz-crumb b {
  font-weight: 500;
  color: var(--ds-ink-2);
}

.sc {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .8em;
  padding: 1.1em;
}

/* ── shared pieces ── */
.dz-ph {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: .6em;
  background: var(--tile) var(--img) center / 80% no-repeat;
}

.dz-ph.sm {
  flex: none;
  width: 3em;
  aspect-ratio: 1;
  border-radius: .5em;
  background-size: 90%;
}

.dz-meta {
  font: 500 .8em/1.35 var(--ds-font-mono);
  letter-spacing: .02em;
  color: var(--ds-ink-4);
}

.dz-price {
  font: 600 1em/1.2 var(--ds-font-mono);
}

.dz-card,
.dz-agent,
.dz-client,
.dz-meet,
.dz-sold,
.dz-comm,
.dz-toast {
  border: 1px solid rgba(255, 255, 255, .07);
  border-radius: .8em;
  background: rgba(255, 255, 255, .03);
}

/* A label that changes: .x-a is what it was, .x-b what it becomes. Both sit
   in one grid cell; the finished frame shows .x-b. */
.dz-chip,
.dz-btn {
  display: inline-grid;
  flex: none;
}

.dz-chip>i,
.dz-btn>i {
  grid-area: 1 / 1;
  padding: .32em .7em;
  border-radius: 99px;
  font: 600 .78em/1.25 var(--ds-font-mono);
  font-style: normal;
  white-space: nowrap;
  text-align: center;
  background: rgba(255, 255, 255, .07);
  color: var(--ds-ink-2);
}

.dz-chip .ok,
.dz-btn .x-b {
  background: color-mix(in srgb, var(--ds-good) 18%, transparent);
  color: var(--ds-good);
}

.dz-chip .sold {
  background: color-mix(in srgb, var(--ds-blue) 20%, transparent);
  color: var(--ds-blue);
}

.x-a {
  opacity: 0;
}

.dz-btn .x-a {
  background: var(--ds-blue);
  color: #0B0C10;
}

/* ── 1. Scout ── */
.dz-toast {
  display: flex;
  gap: .75em;
  padding: .8em .9em;
  border-color: rgba(255, 255, 255, .12);
  background: rgba(26, 27, 33, .96);
  box-shadow: 0 14px 34px rgba(0, 0, 0, .55);
}

.dz-toast div,
.dz-cb,
.dz-agent div,
.dz-client div,
.dz-meet div {
  display: grid;
  gap: .2em;
  min-width: 0;
}

.dz-toast b {
  font-size: .95em;
}

.dz-toast span:not(.material-symbols-outlined) {
  font-size: .82em;
  color: var(--ds-ink-3);
}

.dz-toast .dz-watch {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-family: var(--ds-font-mono);
  color: var(--ds-blue) !important;
}

.dz-toast .material-symbols-outlined,
.dz-meet .material-symbols-outlined {
  flex: none;
  display: grid;
  place-items: center;
  width: 2em;
  height: 2em;
  border-radius: 50%;
  font-size: 1.1em;
  background: color-mix(in srgb, var(--ds-blue) 18%, transparent);
  color: var(--ds-blue);
}

.dz-card {
  display: grid;
  grid-template-columns: 34% minmax(0, 1fr);
  align-items: center;
  gap: .2em .7em;
  padding: .5em;
  border-color: color-mix(in srgb, var(--ds-blue) 50%, transparent);
}

.dz-card .dz-ph {
  aspect-ratio: 4 / 3.4;
}

.dz-cb {
  padding: .1em 0;
}

.dz-cb b,
.dz-agent b,
.dz-client b,
.dz-meet b {
  font-size: .95em;
  font-weight: 600;
}

.dz-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .4em .6em;
  margin-top: .3em;
}

/* The listing Scout dropped: an advertiser with seven live listings. Same
   fixed scrim colours as the real Scout badge. */
.dz-agent {
  display: flex;
  align-items: center;
  gap: .7em;
  padding: .5em;
  opacity: .4;
  filter: grayscale(.7);
}

.dz-flag {
  justify-self: start;
  max-width: 100%;
  overflow: hidden;
  padding: .25em .55em;
  border-radius: 99px;
  border: 1px solid rgba(232, 178, 74, .45);
  background: rgba(20, 15, 5, .78);
  color: #E8B24A;
  font: 700 .7em/1.2 var(--ds-font-mono);
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* ── 2. Listing ── */
.dz-bh {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .8em;
}

.dz-bh b {
  font-size: .95em;
}

.dz-count {
  white-space: nowrap;
  font: 500 .8em/1 var(--ds-font-mono);
  color: var(--ds-ink-3);
}

/* The counter is a CSS counter fed by an animatable integer. Where
   @property is unsupported the animation does nothing and the finished
   value, 3, is what shows. */
@property --dz-n {
  syntax: '<integer>';
  inherits: false;
  initial-value: 3;
}

.dz-count .n {
  --dz-n: 3;
  counter-reset: dzn var(--dz-n);
  color: var(--ds-ink);
}

.dz-count .n::after {
  content: counter(dzn);
}

.dz-site {
  display: grid;
  grid-template-columns: 2em minmax(0, 1fr) auto;
  align-items: center;
  column-gap: .7em;
  row-gap: .45em;
  padding: .75em .8em;
  border-radius: .8em;
  border: 1px solid rgba(255, 255, 255, .07);
  background: rgba(255, 255, 255, .03);
}

.dz-logo {
  grid-row: span 2;
  display: grid;
  place-items: center;
  width: 2em;
  height: 2em;
  border-radius: .5em;
  font: 700 .9em/1 var(--ds-font-body);
  color: #fff;
}

.l-myhome { background: #D9672F; }
.l-ss { background: #2B8FC9; }
.l-korter { background: #6B55E0; }

.dz-sn {
  font-size: .9em;
  font-weight: 500;
}

.dz-state {
  display: inline-grid;
  justify-items: end;
}

.dz-state i {
  grid-area: 1 / 1;
  font: 600 .74em/1.2 var(--ds-font-mono);
  font-style: normal;
  white-space: nowrap;
}

.dz-state .q { opacity: 0; color: var(--ds-ink-5); }
.dz-state .f { opacity: 0; color: var(--ds-blue); }
.dz-state .d { color: var(--ds-good); }

.dz-track {
  grid-column: 2 / -1;
  height: 2px;
  border-radius: 2px;
  overflow: hidden;
  background: rgba(255, 255, 255, .06);
}

.dz-track i {
  display: block;
  height: 100%;
  background: var(--ds-good);
  transform-origin: left;
}

/* ── 3. Client ── */
.sc-3 {
  justify-content: flex-start;
  gap: .6em;
  padding-bottom: 0;
}

.dz-client {
  display: flex;
  align-items: center;
  gap: .7em;
  padding: .65em;
}

.dz-av {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.4em;
  height: 2.4em;
  border-radius: 50%;
  background: color-mix(in srgb, var(--ds-blue) 22%, #15161B);
  color: var(--ds-blue);
  font-weight: 600;
  font-size: .85em;
}

/* Wraps between its parts, never inside one: the budget is what the
   match is made on, so it must not be cut or split. */
.dz-client .dz-meta>span {
  white-space: nowrap;
}

/* Sits on the seam between the client and their phone, overlapping both:
   it is the link between them, and a row of its own cost the scene the
   height that keeps the client's buttons above the crop. */
.dz-ai {
  position: relative;
  z-index: 2;
  align-self: center;
  margin: -1.15em 0 -1.25em;
  display: inline-flex;
  align-items: center;
  gap: .35em;
  padding: .3em .8em .3em .6em;
  border-radius: 99px;
  border: 1px solid color-mix(in srgb, var(--ds-blue) 60%, transparent);
  background: #15161D;
  box-shadow: 0 0 22px color-mix(in srgb, var(--ds-blue) 35%, transparent);
  color: var(--ds-blue);
  font: 600 .78em/1.2 var(--ds-font-mono);
}

.dz-ai .material-symbols-outlined {
  font-size: 1.2em;
}

/* The client's page on their phone, running off the bottom of the scene. */
.dz-phone {
  flex: 1;
  align-self: center;
  width: 82%;
  padding-top: 1.1em;
  margin-bottom: -2em;
  display: flex;
  flex-direction: column;
  gap: .45em;
  padding: .7em .7em 0;
  border-radius: 1.5em 1.5em 0 0;
  border: .35em solid #1C1D22;
  border-bottom: 0;
  background: #0B0C0F;
}

.dz-ph-head {
  padding: .2em .2em 0;
  font: 600 .74em/1.2 var(--ds-font-mono);
  color: var(--ds-ink-4);
}

.dz-ph.big {
  flex: none;
  aspect-ratio: 2.5 / 1;
  border-radius: .8em;
  background-size: auto 92%;
}

.dz-phone>b {
  font-size: .92em;
}

.dz-phone .dz-meta {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.dz-choice {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .4em;
  margin-top: .3em;
}

.dz-choice span {
  position: relative;
  padding: .55em .2em;
  border-radius: .6em;
  text-align: center;
  font-size: .74em;
  font-weight: 600;
  border: 1px solid rgba(255, 255, 255, .1);
  color: var(--ds-ink-3);
}

/* Chosen. The ring is the tap, played once over the button. */
.dz-choice .yes {
  border-color: transparent;
  background: var(--ds-good);
  color: #06140B;
}

.dz-choice .yes::after {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  width: 2.4em;
  height: 2.4em;
  margin: -1.2em 0 0 -1.2em;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .8);
  opacity: 0;
}

/* ── 4. Meeting ── */
.dz-meet {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5em .7em;
  padding: .7em;
}

.dz-meet>div {
  margin-right: auto;
}

.dz-sold {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6em;
  padding: .6em .7em;
  font-size: .9em;
}

.dz-comm {
  display: grid;
  gap: .5em;
  padding: .9em;
}

.dz-big {
  font: 600 2.1em/1 var(--ds-font-mono);
  letter-spacing: -0.02em;
}

.dz-split {
  display: flex;
  height: .42em;
  gap: 3px;
}

.dz-split i {
  flex: 1;
  border-radius: 99px;
  background: var(--ds-blue);
  transform-origin: left;
}

.dz-split i+i {
  background: var(--ds-good);
}

.dz-shares {
  display: grid;
  gap: .25em;
  font-size: .82em;
  color: var(--ds-ink-3);
}

.dz-shares span {
  display: flex;
  justify-content: space-between;
  gap: .6em;
}

.dz-shares b {
  font-family: var(--ds-font-mono);
  color: var(--ds-ink);
}

.dz-maths {
  font: 500 .8em/1.3 var(--ds-font-mono);
  color: var(--ds-ink-4);
}

/* ── timelines ──
   Each scene starts when .go lands on it. --t is when a piece happens,
   measured from the step's start. */
@keyframes dz-enter {
  from { opacity: 0; transform: translateY(.8em); }
}

@keyframes dz-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

@keyframes dz-in {
  from { opacity: 0; transform: translateY(.3em) scale(.96); }
}

@keyframes dz-blip {
  0% { opacity: 0; }
  12%, 88% { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes dz-grow {
  from { transform: scaleX(0); }
}

@keyframes dz-dim {
  from { opacity: 1; filter: none; }
}

@keyframes dz-ring {
  from { opacity: 0; transform: scale(.5); }
  30% { opacity: 1; }
  to { opacity: 0; transform: scale(1.9); }
}

@keyframes dz-unpick {
  from { background: transparent; color: var(--ds-ink-3); border-color: rgba(255, 255, 255, .1); }
}

@keyframes dz-rise {
  from { transform: translateY(40%); opacity: 0; }
}

@keyframes dz-n {
  0%, 36% { --dz-n: 0; }
  37%, 66% { --dz-n: 1; }
  67%, 96% { --dz-n: 2; }
  97%, 100% { --dz-n: 3; }
}

.go .x-a { animation: dz-out .25s ease var(--t, 1s) backwards; }
.go .x-b { animation: dz-in .3s ease var(--t, 1s) backwards; }

/* 1: the alert drops in, the agent's listing is struck off, the owner's is
   imported. */
.sc-1.go .dz-toast { animation: dz-enter .45s cubic-bezier(.2, .7, .2, 1) .1s backwards; }
.sc-1.go .dz-card { animation: dz-enter .45s cubic-bezier(.2, .7, .2, 1) .35s backwards; }
.sc-1.go .dz-agent { animation: dz-enter .4s ease .5s backwards, dz-dim .45s ease .95s backwards; }
.sc-1 .dz-btn { --t: 1.4s; }

/* 2: three portals fill and publish one after another. */
.sc-2.go .dz-site { animation: dz-enter .35s ease backwards; }
.sc-2.go .dz-site.p2 { animation-delay: .05s; }
.sc-2.go .dz-site.p3 { animation-delay: .1s; }
.dz-site.p1 { --a: .4s; --b: .75s; }
.dz-site.p2 { --a: .8s; --b: 1.15s; }
.dz-site.p3 { --a: 1.2s; --b: 1.55s; }
.sc-2.go .dz-state .q { animation: dz-out .15s ease var(--a) backwards; }
.sc-2.go .dz-state .f { animation: dz-blip .35s linear var(--a) backwards; }
.sc-2.go .dz-state .d { animation: dz-in .25s ease var(--b) backwards; }
.sc-2.go .dz-track i { animation: dz-grow .35s ease var(--a) backwards; }
.sc-2.go .dz-count .n { animation: dz-n 1.5s linear .25s backwards; }

/* 3: AI pairs the client with the home; the client taps Interested. */
.sc-3.go .dz-client { animation: dz-enter .4s ease backwards; }
.sc-3.go .dz-ai { animation: dz-in .3s ease .4s backwards; }
.sc-3.go .dz-phone { animation: dz-rise .55s cubic-bezier(.2, .7, .2, 1) .65s backwards; }
.sc-3.go .dz-choice .yes { animation: dz-unpick .25s ease 1.5s backwards; }
.sc-3.go .dz-choice .yes::after { animation: dz-ring .6s ease-out 1.45s; }

/* 4: the meeting goes well, the home is sold, the commission splits. */
.sc-4.go .dz-meet { animation: dz-enter .4s ease backwards; }
.sc-4 .dz-meet .dz-chip { --t: .6s; }
.sc-4.go .dz-sold { animation: dz-enter .4s ease .3s backwards; }
.sc-4 .dz-sold .dz-chip { --t: 1s; }
.sc-4.go .dz-comm { animation: dz-enter .45s ease 1.15s backwards; }
.sc-4.go .dz-split i { animation: dz-grow .45s ease 1.45s backwards; }
.sc-4.go .dz-split i+i { animation-delay: 1.6s; }

/* ── narrower screens ── */
@media (max-width: 1180px) {

  /* Two by two. The track only makes sense over four columns in a row, so
     it goes; the lit column still shows the picked step. */
  .deal-cols {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(2, auto auto auto);
    align-content: start;
  }

  /* Two columns are wide and the photos keep their aspect, so the scene's
     height follows its width rather than the window's height. An aspect-
     ratio box does not stretch to its grid track, hence the width. */
  .col-scene {
    width: 100%;
    aspect-ratio: 5 / 4;
    min-height: 340px;
  }

  .deal-rail {
    display: none;
  }

  .deal-head {
    margin-bottom: 24px;
  }
}

@media (max-width: 700px) {
  .ch-deal {
    padding: 16px 0;
  }

  .deal {
    min-height: 0;
    padding: 22px 16px;
    border-radius: 16px;
  }

  /* Stacked: one column under another; tapping one replays its scene. */
  .deal-cols {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
  }

  .deal-col {
    display: flex;
    flex-direction: column;
    grid-row: auto;
  }

  .deal-col .col-num {
    color: var(--ds-blue);
  }

  .col-scene {
    height: auto;
    aspect-ratio: 1 / 1.08;
  }
}

/* Its outline while it waits (.sk, after .sc in the markup). Static: a
   shimmer would be an animation running in three columns at once. */
.sk {
  display: none;
  position: absolute;
  inset: 2.6em 0 0;
  padding: 1.1em;
  flex-direction: column;
  justify-content: center;
  gap: .8em;
}

.sc.wait~.sk {
  display: flex;
}

.sk.sk-top {
  justify-content: flex-start;
  padding-bottom: 0;
}

.sk i {
  display: block;
  flex: none;
  border-radius: .7em;
  border: 1px solid rgba(255, 255, 255, .05);
  background: rgba(255, 255, 255, .03);
}

/* Holds the batch header's place: the scene centres header and rows
   together, so the outline has to as well. */
.sk .gap { height: 1.4em; visibility: hidden; }
.sk .r { height: 3.3em; }
.sk .t { height: 2.4em; }
.sk .b { height: 7.5em; }

.sk .ph {
  flex: 1;
  margin: 0 11%;
  border-bottom: 0;
  border-radius: 1.4em 1.4em 0 0;
}

/* A scene the play-through has not reached yet: on its first frame. With
   .go applied and every animation paused at zero, each element sits in
   its keyframe 'from' state (fill-mode backwards). */
.sc.go.wait,
.sc.go.wait *,
.sc.go.wait *::after {
  animation-play-state: paused;
}

/* Belt and braces: the script never starts a scene in either mode, but a
   scene left mid-play by a mode change still snaps to its finished frame. */
:root[data-lite] .deal .go,
:root[data-lite] .deal .go *,
:root[data-lite] .deal .go *::after {
  animation: none !important;
}

@media (prefers-reduced-motion: reduce) {

  .deal .go,
  .deal .go *,
  .deal .go *::after {
    animation: none !important;
  }

  .dz-token,
  .rail-fill {
    transition: none;
  }
}

/* ---------- Chapter 2 — The agency ---------- */
/* The agency is a building: floors are teams, each window an agent drawn
   as a silhouette whose pose is their status. The team claim on its left,
   the money claim on its right, the month's money on the street under it.
   Markup: templates/marketing/_agency.html; behaviour: js/landing-agency.js. */
.ch-agency {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  padding: 28px clamp(12px, 1.6vw, 28px);
  scroll-margin-top: var(--lp-topbar-h);
}

.ag {
  /* A window by width, and by height so the whole building and its street
     fit the screen: three floors of windows (.64 wide as tall) share what
     the head, roof, slabs and street leave over. */
  --win: clamp(44px, min(calc(min(100vw, 1760px) * .044), calc((min(100svh, 1120px) - 690px) * .213)), 84px);
  --wgap: clamp(9px, .8vw, 14px);
  --tower: calc(var(--win) * 6 + var(--wgap) * 5 + 40px);
  position: relative;
  display: flex;
  flex-direction: column;
  max-width: 1760px;
  margin-inline: auto;
  min-height: min(calc(100svh - var(--lp-topbar-h) - 56px), 1000px);
  padding: clamp(22px, 2vw, 34px);
  border-radius: 22px;
  border: 1px solid rgba(255, 255, 255, .07);
  background:
    radial-gradient(ellipse 60% 55% at 50% 100%, rgba(255, 170, 80, .07), transparent 70%),
    #030304;
}

/* The ground our building stands on, drawn once into a canvas by
   landing-agency.js. */
.ag-city {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  pointer-events: none;
}

.ag>*:not(.ag-city) {
  position: relative;
}

.ag-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px 32px;
  margin-bottom: clamp(18px, 2.4vw, 36px);
}

.ag-head .display-h2 { margin: 0; }

.ag-body {
  flex: 1;
  display: grid;
  grid-template-columns: var(--tower) minmax(0, 1fr);
  grid-template-areas: "bld panels";
  column-gap: clamp(20px, 2.6vw, 44px);
}

.ag-bld {
  grid-area: bld;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

/* ── the panels: each claim with the product UI that proves it ── */
.ag-panels {
  grid-area: panels;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-rows: auto auto;
  align-content: stretch;
  gap: 14px;
}

.ag-panel h3 {
  margin: 0 0 12px;
  font-family: var(--ds-font-display);
  font-size: clamp(18px, 1.3vw, 22px);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--ds-ink);
}

.ag-proof {
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
  display: grid;
  gap: 6px;
  font-family: var(--lp-font-prose);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ds-ink-3);
}

.ag-proof li {
  position: relative;
  padding-left: 14px;
}

.ag-proof li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .75em;
  width: 6px;
  height: 1px;
  background: var(--ds-ink-5);
}

/* ── the building ── */
.ag-tower {
  position: relative;
  padding: 0 20px;
  border: 1px solid rgba(255, 255, 255, .06);
  border-bottom: 0;
  border-radius: 10px 10px 0 0;
  background: linear-gradient(180deg, #0e0f13, #0c0d10);
}

.ag-roof {
  display: flex;
  justify-content: center;
  padding: 16px 0 14px;
}

/* Presence colour belongs here, on product UI, not on the building. */
.ag-live {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .08);
  background: rgba(255, 255, 255, .03);
  font: 500 12px/1 var(--ds-font-mono);
  color: var(--ds-ink-3);
}

.ag-live b { color: var(--ds-ink); font-weight: 600; }

.ag-live i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ds-good);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ds-good) 22%, transparent);
}

.ag-wins {
  display: grid;
  grid-template-columns: repeat(6, var(--win));
  gap: var(--wgap);
}

.ag-win {
  display: block;
  width: var(--win);
  aspect-ratio: .64;
  padding: 0;
  border: 0;
  border-radius: 1px;
  background: #05070d;
  cursor: pointer;
  transition: box-shadow .7s ease;
}

/* Lamplight spilling out of a lit room; static, so drawn once. */
.ag-win:not(.is-off):not(.is-dark) {
  box-shadow: 0 0 18px -2px color-mix(in srgb, var(--glow, #ffc56e) 55%, transparent);
}

.ag-win svg {
  display: block;
  width: 100%;
  height: 100%;
  transition: opacity .7s ease;
}

.ag-win.is-dark svg { opacity: 0; }

.ag-win:focus-visible {
  outline: 2px solid var(--ds-blue);
  outline-offset: 3px;
}

/* A sale: the room flares and settles. */
.ag-win.is-flare svg { animation: ag-flare 1.6s ease-out; }

@keyframes ag-flare {
  12% { filter: brightness(1.5) saturate(1.1); }
}

.ag-win .fig-deal { display: none; }
.ag-win.is-sold .fig-deal { display: inline; }
.ag-win.is-sold .fig-work { display: none; }

/* The floor's slab: the office and its month. */
.ag-slab {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 12px -20px 0;
  padding: 7px 20px;
  border-top: 1px solid rgba(255, 255, 255, .06);
  border-bottom: 1px solid rgba(0, 0, 0, .6);
  background: #121318;
  font: 500 12px/1.2 var(--ds-font-mono);
  color: var(--ds-ink-4);
}

.ag-slab b { color: var(--ds-ink-2); font-weight: 600; }
.ag-floor + .ag-floor { padding-top: 0; }
.ag-floor .ag-wins { padding-top: 16px; }

/* ── the agent card ── */
.ag-card {
  position: absolute;
  z-index: 5;
  width: 228px;
  padding: 12px 13px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, .1);
  background: rgba(16, 17, 22, .97);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .6);
  font: 13px/1.4 var(--ds-font-body);
  color: var(--ds-ink-2);
  pointer-events: none;
  animation: ag-card-in .22s ease-out;
}

.ag-card[hidden] { display: none; }

@keyframes ag-card-in {
  from { opacity: 0; transform: translateY(4px); }
}

.ag-card .who {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}

.ag-card .who b { color: var(--ds-ink); font-weight: 600; }

.ag-card .st {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: 500 11px/1 var(--ds-font-mono);
  color: var(--ds-good);
}

.ag-card .st::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.ag-card .st.off { color: var(--ds-ink-4); }

.ag-card .row {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 5px 0;
  border-top: 1px solid rgba(255, 255, 255, .05);
  font-size: 12px;
}

.ag-card .row span { color: var(--ds-ink-4); }
.ag-card .row b { color: var(--ds-ink); font-weight: 500; text-align: right; }

.ag-card .bar {
  display: block;
  height: 3px;
  margin: 2px 0 8px;
  border-radius: 2px;
  background: rgba(255, 255, 255, .07);
  overflow: hidden;
}

.ag-card .bar i {
  display: block;
  height: 100%;
  background: var(--ds-blue);
  transform-origin: left;
}

.ag-card .open {
  display: block;
  padding-top: 6px;
  font: 500 11.5px/1 var(--ds-font-mono);
  color: var(--ds-blue);
}

/* ── the sale, and its commission dropping to the street ── */
.ag-sale,
.ag-coin {
  position: absolute;
  z-index: 4;
  left: 0;
  top: 0;
  padding: 4px 9px;
  border-radius: 999px;
  font: 600 11px/1 var(--ds-font-mono);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
}

.ag-sale {
  background: color-mix(in srgb, var(--ds-blue) 22%, #101116);
  border: 1px solid color-mix(in srgb, var(--ds-blue) 50%, transparent);
  color: #dfe3ff;
}

.ag-coin {
  background: color-mix(in srgb, var(--ds-good) 22%, #101116);
  border: 1px solid color-mix(in srgb, var(--ds-good) 50%, transparent);
  color: #d6f5e1;
}

.ag-sale.is-on { animation: ag-sale 3.4s ease both; }
.ag-coin.is-on { animation: ag-coin 1.1s cubic-bezier(.5, 0, .75, 0) both; }

@keyframes ag-sale {
  0% { opacity: 0; transform: translate(-50%, 6px); }
  10%, 80% { opacity: 1; transform: translate(-50%, 0); }
  100% { opacity: 0; transform: translate(-50%, -4px); }
}

@keyframes ag-coin {
  0% { opacity: 0; transform: translate(-50%, 0); }
  15% { opacity: 1; }
  85% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, var(--drop, 200px)); }
}

/* ── the street: the month's money ── */
.ag-street {
  display: grid;
  gap: 12px;
  padding: 16px 20px 18px;
  border: 1px solid rgba(255, 255, 255, .06);
  border-top: 2px solid rgba(255, 255, 255, .1);
  border-radius: 0 0 10px 10px;
  background: #07080a;
}

.ag-month {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font: 500 12px/1 var(--ds-font-mono);
  color: var(--ds-ink-4);
}

.ag-month b {
  font-family: var(--ds-font-display);
  font-size: clamp(24px, 2vw, 32px);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--ds-ink);
}

.ag-street.is-bump .ag-month b { animation: ag-bump .5s ease; }

@keyframes ag-bump {
  40% { color: #bff0cf; transform: translateY(-2px); }
}

.ag-goal-top {
  display: flex;
  justify-content: space-between;
  font: 500 11.5px/1 var(--ds-font-mono);
  color: var(--ds-ink-4);
  margin-bottom: 7px;
}

.ag-bar {
  display: block;
  height: 5px;
  border-radius: 3px;
  background: rgba(255, 255, 255, .07);
  overflow: hidden;
}

.ag-bar i {
  display: block;
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, var(--ds-blue), var(--ds-good));
  transform-origin: left;
  transform: scaleX(calc(var(--to) / 100));
  transition: transform 1s cubic-bezier(.2, .7, .2, 1);
}

.ag-shares {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: 12px;
  color: var(--ds-ink-4);
}

.ag-shares span { display: grid; gap: 3px; }
.ag-shares span + span { text-align: right; }
.ag-shares b { font-family: var(--ds-font-mono); color: var(--ds-ink-2); font-weight: 600; }

/* ── panels under the claims: real product UI that proves them ── */
.ag-panel {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 18px 20px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, .08);
  background: rgba(13, 14, 18, .88);
  font: 13px/1.4 var(--ds-font-body);
  color: var(--ds-ink-2);
}

.ag-panel-h {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  font: 500 11px/1 var(--ds-font-mono);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ds-ink-4);
}

.ag-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ds-good);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ds-good) 22%, transparent);
}

.ag-feed {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 2px;
}

.ag-feed li {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  border-top: 1px solid rgba(255, 255, 255, .05);
  font-size: 12.5px;
}

.ag-feed li:first-child { border-top: 0; }
.ag-feed li.is-new { animation: ag-row .45s ease-out; }

@keyframes ag-row {
  from { opacity: 0; transform: translateY(-6px); }
}

.ag-feed .av {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--ds-blue) 20%, #15161b);
  color: var(--ds-blue);
  font-size: 11px;
  font-weight: 600;
}

.ag-feed .tx { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ds-ink-3); }
.ag-feed .tx b { color: var(--ds-ink); font-weight: 500; }
.ag-feed .tm { font: 500 11px/1 var(--ds-font-mono); color: var(--ds-ink-4); }
.ag-feed .is-sale .av { background: color-mix(in srgb, var(--ds-good) 22%, #15161b); color: var(--ds-good); }
.ag-feed .is-sale .tx { color: #c6f0d4; }

.ag-offices { display: grid; gap: 10px; }

.ag-office {
  display: grid;
  grid-template-columns: 82px minmax(0, 1fr) 66px;
  align-items: center;
  gap: 10px;
  font-size: 12.5px;
  color: var(--ds-ink-3);
}

.ag-office i {
  display: block;
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--ds-blue) 60%, transparent), var(--ds-blue));
  transform-origin: left;
  transform: scaleX(calc(var(--w) / 100));
  transition: transform 1s cubic-bezier(.2, .7, .2, 1);
}

.ag-office b { font: 600 12px/1 var(--ds-font-mono); color: var(--ds-ink-2); text-align: right; }

.ag-lb {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: lb;
  display: grid;
  gap: 6px;
}

.ag-lb li {
  counter-increment: lb;
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) minmax(40px, 30%) 64px;
  align-items: center;
  gap: 10px;
  padding: 5px 0;
  border-top: 1px solid rgba(255, 255, 255, .05);
  font-size: 12.5px;
  color: var(--ds-ink-2);
}

.ag-lb li:first-child { border-top: 0; }

/* each agent's progress to their own goal */
.ag-lb li i {
  display: block;
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--ds-blue) calc(var(--g) * 1%), rgba(255, 255, 255, .07) 0);
}

.ag-lb b { text-align: right; }

.ag-lb li::before { content: counter(lb); font: 600 11px/1.6 var(--ds-font-mono); color: var(--ds-ink-4); }
.ag-lb li:first-child::before { color: #ffd9a3; }
.ag-lb b { font: 600 12px/1.5 var(--ds-font-mono); color: var(--ds-ink); }
.ag-lb li.is-new { animation: ag-row .45s ease-out; }

/* Six months of sales, this month's bar growing with the sale. */
.ag-trend {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  align-items: end;
  gap: 10px;
  flex: 1;
  min-height: 90px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid rgba(255, 255, 255, .05);
}

.ag-trend span {
  display: grid;
  grid-template-rows: 1fr auto;
  height: 100%;
  gap: 6px;
  font: 500 10.5px/1 var(--ds-font-mono);
  color: var(--ds-ink-4);
  text-align: center;
}

.ag-trend i {
  align-self: end;
  justify-self: center;
  width: min(100%, 26px);
  height: calc(var(--h) * 1%);
  border-radius: 3px 3px 1px 1px;
  background: rgba(255, 255, 255, .1);
  /* grows by transform, not height: no layout per frame */
  transform-origin: bottom;
  transform: scaleY(var(--grow, 1));
  transition: transform 1s cubic-bezier(.2, .7, .2, 1);
}

.ag-trend span:last-child i { background: linear-gradient(180deg, var(--ds-good), var(--ds-blue)); }
.ag-trend span:last-child { color: var(--ds-ink-2); }

/* The deal's commission: the sale on the building, worked out. */
.ag-p-deal .ag-panel-h { justify-content: space-between; }

.ag-chip {
  padding: 3px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ds-good) 18%, transparent);
  color: var(--ds-good);
  letter-spacing: .02em;
  text-transform: none;
}

.ag-deal-sum {
  font-family: var(--ds-font-display);
  font-size: clamp(28px, 2.4vw, 40px);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--ds-ink);
}

.ag-deal-rate {
  margin-top: 4px;
  font: 500 11.5px/1.4 var(--ds-font-mono);
  color: var(--ds-ink-4);
}

.ag-split {
  display: flex;
  gap: 4px;
  margin: auto 0 10px;
  padding-top: 16px;
}

.ag-split i {
  flex: 1;
  height: 6px;
  border-radius: 3px;
  background: var(--ds-blue);
  transform-origin: left;
}

.ag-split i + i { background: var(--ds-good); }

.ag-deal-shares {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: 12px;
  color: var(--ds-ink-4);
}

.ag-deal-shares span { display: grid; gap: 3px; }
.ag-deal-shares span + span { text-align: right; }
.ag-deal-shares b { font: 600 13px/1 var(--ds-font-mono); color: var(--ds-ink); }

/* Before the sale: waiting, nothing to split yet. */
.ag-p-deal.is-pending .ag-chip,
.ag-p-deal.is-pending .ag-deal-shares b { visibility: hidden; }
.ag-p-deal.is-pending .ag-split i { transform: scaleX(0); }
.ag-p-deal.is-pending .ag-deal-sum { color: var(--ds-ink-5); }
.ag-p-deal .ag-split i { transition: transform .8s cubic-bezier(.2, .7, .2, 1); }
.ag-p-deal .ag-split i + i { transition-delay: .25s; }

/* ── narrower screens ── */
@media (max-width: 1180px) {
  .ag-body {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "bld" "panels";
    row-gap: 28px;
  }

  .ag-bld { align-items: center; }
  .ag-tower, .ag-street { width: var(--tower); }
}

@media (max-width: 700px) {
  .ch-agency { padding: 16px 0; }

  .ag {
    --win: clamp(34px, calc((100vw - 120px) / 6), 60px);
    --wgap: 6px;
    min-height: 0;
    padding: 22px 16px;
    border-radius: 16px;
  }

  .ag-tower, .ag-street { padding-left: 12px; padding-right: 12px; }
  .ag-slab { margin-left: -12px; margin-right: -12px; padding-left: 12px; padding-right: 12px; }
  .ag-panels { grid-template-columns: minmax(0, 1fr); }

  /* Under the building rather than beside a window: there is no room. */
  .ag-card {
    position: static;
    width: auto;
    margin: 14px 0 4px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ag-win svg,
  .ag-bar i { transition: none; }
  .ag-card { animation: none; }
}

/* ---------- Chapter 3 — AI ---------- */
/* Ask your agency: the claim and its questions beside a chat that answers
   with Chapter 2's numbers. Then the services Doorstep sets up, a small
   scene in each tile. The markup is the finished state.
   Markup: templates/marketing/_ai.html; behaviour: js/landing-ai.js. */
.ch-ai {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  padding: 28px clamp(12px, 1.6vw, 28px);
  scroll-margin-top: var(--lp-topbar-h);
}

.ai {
  position: relative;
  max-width: 1760px;
  margin-inline: auto;
  padding: clamp(22px, 2vw, 34px);
  border-radius: 22px;
  border: 1px solid rgba(255, 255, 255, .07);
  background:
    radial-gradient(ellipse 55% 50% at 70% 0%, rgba(110, 140, 255, .08), transparent 70%),
    #030304;
  overflow: hidden;
}

.ai-stars {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.ai>*:not(.ai-stars) { position: relative; }


.ai-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px 32px;
  margin-bottom: clamp(18px, 2.4vw, 36px);
}

.ai-head .display-h2 { margin: 0; }

.ai h3 {
  margin: 0 0 12px;
  font-family: var(--ds-font-display);
  font-size: clamp(18px, 1.3vw, 22px);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--ds-ink);
}

/* ── ask your agency ── */
.ai-ask {
  display: grid;
  grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr);
  gap: clamp(20px, 2.6vw, 44px);
}

.ai-claim { display: flex; flex-direction: column; padding-top: 6px; }
.ai-bot {
  flex: 1;
  display: grid;
  place-items: center;
  min-height: clamp(260px, 36svh, 360px);
  padding: 10px 0 18px;
  background: radial-gradient(closest-side, rgba(110, 140, 255, .16), transparent 75%);
}

/* The robot: plain SVG. It floats while on screen (the whole drawing, on
   the compositor); its parts move only on a state change. */
.ai-bot svg {
  height: clamp(200px, 30svh, 300px);
  width: auto;
  overflow: visible;
}

.ai-bot.is-on svg { animation: ai-float 3.2s ease-in-out infinite alternate; }

/* The 3D robot (landing-ai-bot.js) takes over once its first frame is
   drawn; the SVG stays for lite mode, reduced motion and no WebGL. */
.ai-bot { position: relative; }

.ai-bot-3d {
  position: absolute;
  top: 0;
  left: 50%;
  height: 100%;
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity .6s ease;
  -webkit-mask-image: linear-gradient(#000 72%, transparent);
  mask-image: linear-gradient(#000 72%, transparent);
}

.ai-bot.is-3d .ai-bot-3d { opacity: 1; }
.ai-bot.is-3d svg { visibility: hidden; animation: none; }

@keyframes ai-float {
  to { transform: translateY(-7px); }
}

.ai-bot .head,
.ai-bot .eyes,
.ai-bot .eye,
.ai-bot .ant-glow,
.ai-bot .open,
.ai-bot .happy { transform-box: view-box; }

.ai-bot .head { transform-origin: 120px 160px; transition: transform .5s cubic-bezier(.3, .7, .3, 1); }
.ai-bot .eyes { transition: transform .35s cubic-bezier(.3, .7, .3, 1); }
.ai-bot .eye { transform-box: fill-box; transform-origin: center; transition: transform .09s; }
.ai-bot.is-blink .eye { transform: scaleY(.12); }
.ai-bot .happy { opacity: 0; }
.ai-bot .open, .ai-bot .happy { transition: opacity .15s; }
.ai-bot .ant { transition: fill .3s; }
.ai-bot .ant-glow { opacity: 0; transition: opacity .3s; }

/* Thinking: it turns to the chat and the antenna lights. */
.ai-bot[data-state="think"] .head { transform: rotate(5deg); }
.ai-bot[data-state="think"] .eyes { transform: translate(9px, -2px); }
.ai-bot[data-state="think"] .ant,
.ai-bot[data-state="happy"] .ant { fill: #a9bdff; }
.ai-bot[data-state="think"] .ant-glow { opacity: 1; animation: ai-pulse .55s ease-in-out infinite alternate; }

@keyframes ai-pulse {
  to { opacity: .35; }
}

/* Answered: a smile. */
.ai-bot[data-state="happy"] .open { opacity: 0; }
.ai-bot[data-state="happy"] .happy { opacity: 1; }



.ai-try {
  margin-bottom: 10px;
  font: 500 11px/1 var(--ds-font-mono);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ds-ink-4);
}

.ai-qs { display: flex; flex-wrap: wrap; gap: 8px; }

.ai-qchip {
  display: flex;
  gap: 8px;
  align-items: baseline;
  min-height: 40px;
  padding: 9px 15px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .1);
  background: #0a0b0f;
  font: 13.5px/1.4 var(--ds-font-body);
  color: var(--ds-ink-2);
  text-align: left;
  cursor: pointer;
  transition: border-color .2s, background-color .2s;
}

.ai-qchip::before { content: "›"; color: var(--ds-blue); }
.ai-qchip:hover { border-color: rgba(255, 255, 255, .18); }
.ai-qchip:focus-visible { outline: 2px solid var(--ds-blue); outline-offset: 2px; }

.ai-qchip.is-on {
  border-color: color-mix(in srgb, var(--ds-blue) 55%, transparent);
  background: color-mix(in srgb, var(--ds-blue) 10%, transparent);
  color: var(--ds-ink);
}

/* The chat: Doorstep's own dark theme, not an imitation of Claude's or
   ChatGPT's window. Newest at the bottom; older answers scroll up under
   a fade. */
.ai-chat {
  display: flex;
  flex-direction: column;
  height: clamp(400px, 52svh, 480px);
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, .1);
  background: rgba(11, 12, 16, .9);
  box-shadow: 0 30px 80px -30px rgba(90, 120, 255, .25);
  overflow: hidden;
}

.ai-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-bottom: 1px solid rgba(255, 255, 255, .06);
  font: 500 11px/1 var(--ds-font-mono);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ds-ink-4);
}

.ai-dots { display: flex; gap: 5px; }
.ai-dots i { width: 8px; height: 8px; border-radius: 50%; background: rgba(255, 255, 255, .12); }

/* What the last question read, and how fast. */
.ai-scan { margin-left: auto; letter-spacing: 0; text-transform: none; color: color-mix(in srgb, var(--ds-blue) 80%, #fff); }
.ai-scan.is-new { animation: ag-row .4s ease-out; }

.ai-log {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 12px;
  padding: 14px 16px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(transparent, #000 48px);
  mask-image: linear-gradient(transparent, #000 48px);
  font: 13px/1.45 var(--ds-font-body);
  color: var(--ds-ink-2);
}

.ai-log>* { flex: none; }

/* The connection, at the top of the conversation until answers push it
   out of view. */
.ai-sys {
  align-self: center;
  display: grid;
  justify-items: center;
  gap: 8px;
  margin-bottom: auto;
  padding: 14px 0 8px;
  font: 500 11px/1 var(--ds-font-mono);
  letter-spacing: .04em;
  color: var(--ds-ink-4);
}

.ai-link { display: flex; align-items: center; gap: 0; }

.ai-link b {
  padding: 6px 11px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .12);
  background: #0d0e12;
  font-weight: 500;
  color: var(--ds-ink-2);
}

.ai-link b:last-child { border-color: color-mix(in srgb, var(--ds-blue) 50%, transparent); color: var(--ds-ink); }

.ai-link i {
  position: relative;
  width: 46px;
  height: 1px;
  background: linear-gradient(90deg, rgba(255, 255, 255, .15), var(--ds-blue));
}

.ai-link i::after {
  content: "";
  position: absolute;
  left: 50%;
  top: -3px;
  width: 7px;
  height: 7px;
  margin-left: -3.5px;
  border-radius: 50%;
  background: var(--ds-good);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ds-good) 22%, transparent);
}

/* The field's light arriving. */
.ai-chat.is-hit { animation: ai-hit .7s ease-out; }

@keyframes ai-hit {
  30% { border-color: color-mix(in srgb, var(--ds-blue) 70%, transparent); box-shadow: 0 0 0 1px color-mix(in srgb, var(--ds-blue) 35%, transparent), 0 30px 80px -30px rgba(90, 120, 255, .5); }
}

.ai-q {
  align-self: flex-end;
  max-width: 80%;
  padding: 8px 12px;
  border-radius: 14px 14px 4px 14px;
  background: color-mix(in srgb, var(--ds-blue) 22%, #15161b);
  color: var(--ds-ink);
}

.ai-a { align-self: stretch; }
.ai-a p { margin: 0 0 8px; color: var(--ds-ink); }
.ai-a p::before { content: "✦ "; color: var(--ds-blue); }

.ai-q.is-new,
.ai-a.is-new { animation: ag-row .45s ease-out; }

.ai-rows { display: grid; gap: 3px; }

.ai-row {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  border-radius: 8px;
  background: rgba(255, 255, 255, .035);
  font-size: 12.5px;
}

.ai-row .av {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--ds-blue) 20%, #15161b);
  color: var(--ds-blue);
  font-size: 11px;
  font-weight: 600;
}

.ai-row .nm { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ds-ink); }
.ai-row .nm small { display: block; font: 500 10.5px/1.3 var(--ds-font-mono); color: var(--ds-ink-4); }
.ai-row b { font: 600 12px/1 var(--ds-font-mono); color: var(--ds-ink); }

.ai-goal { grid-template-columns: minmax(0, 7em) minmax(0, 1fr) 3em; }

.ai-goal i {
  position: relative;
  height: 5px;
  border-radius: 3px;
  background: rgba(255, 255, 255, .07);
  overflow: hidden;
}

.ai-goal i::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #e0a03c;
  transform-origin: left;
  transform: scaleX(calc(var(--g) / 100));
}

.ai-a.is-new .ai-goal i::after { animation: ai-grow .9s .25s cubic-bezier(.2, .7, .2, 1) both; }

@keyframes ai-grow {
  from { transform: scaleX(0); }
}

.ai-goal b { text-align: right; color: #f0c27a; }

.ai-stale { grid-template-columns: auto minmax(0, 1fr) auto; }
.ai-stale .id { font: 500 11px/1 var(--ds-font-mono); color: var(--ds-blue); }
.ai-stale b { padding: 3px 7px; border-radius: 999px; background: rgba(224, 160, 60, .14); color: #f0c27a; font-size: 11px; }

/* "Thinking": three dots, only for the moment before an answer. */
.ai-think { align-self: flex-start; display: flex; gap: 4px; padding: 8px 2px; }
.ai-think i { width: 6px; height: 6px; border-radius: 50%; background: var(--ds-ink-4); animation: ai-think .8s ease-in-out infinite alternate; }
.ai-think i:nth-child(2) { animation-delay: .15s; }
.ai-think i:nth-child(3) { animation-delay: .3s; }

@keyframes ai-think {
  to { opacity: .25; transform: translateY(-3px); }
}

.ai-input {
  display: flex;
  align-items: center;
  margin: 0 12px 12px;
  padding: 8px 8px 8px 14px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, .1);
  background: rgba(255, 255, 255, .03);
  font: 13px/1.4 var(--ds-font-body);
  color: var(--ds-ink-4);
}

.ai-input [data-ai-type] { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.ai-input.is-typing { color: var(--ds-ink); border-color: color-mix(in srgb, var(--ds-blue) 45%, transparent); }
.ai-input .caret { display: none; width: 1.5px; height: 1.1em; margin-left: 1px; background: var(--ds-ink); }
.ai-input.is-typing .caret { display: block; }

.ai-input .send {
  flex: none;
  position: relative;
  width: 28px;
  height: 28px;
  margin-left: auto;
  border-radius: 8px;
  background: var(--ds-blue);
}

.ai-input .send::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 8px;
  height: 8px;
  border-top: 2px solid #fff;
  border-left: 2px solid #fff;
  transform: translate(-50%, -25%) rotate(45deg);
}

/* ── the services ── */
.ai-services { margin-top: clamp(28px, 3.4vw, 52px); }

.ai-services-h {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
  margin-bottom: 16px;
}

.ai-services-h h3 { margin: 0; }

.ai-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}

.ai-tile {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 18px 20px 20px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, .08);
  background: rgba(11, 12, 16, .78);
}

.ai-ic {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  margin-bottom: 14px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--ds-blue) 16%, transparent);
  color: color-mix(in srgb, var(--ds-blue) 85%, #fff);
}

.ai-ic svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linejoin: round; stroke-linecap: round; }

.ai-tile h4 {
  margin: 0 0 6px;
  font: 500 15px/1.3 var(--ds-font-display);
  color: var(--ds-ink);
}

.ai-tile p {
  margin: 0 0 16px;
  font: 13px/1.5 var(--lp-font-prose);
  color: var(--ds-ink-3);
}

.ai-sc {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 150px;
  padding: 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, .06);
  background: #08090c;
  font: 12.5px/1.4 var(--ds-font-body);
  color: var(--ds-ink-2);
}

/* call analysis */
.ai-callh { display: flex; align-items: center; gap: 12px; font: 500 11px/1 var(--ds-font-mono); color: var(--ds-ink-4); }
.ai-callh .wave { flex: 1; display: flex; align-items: center; justify-content: space-between; gap: 2px; height: 34px; }
/* bars mirrored around the centre line; the played part is bright */
.ai-callh .wave i { flex: 1; min-width: 1px; max-width: 3px; height: calc(var(--h) * 1%); border-radius: 2px; background: rgba(160, 180, 255, .22); }
.ai-callh .wave i.p { background: color-mix(in srgb, var(--ds-blue) 85%, #fff); }

.ai-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.ai-tags span { padding: 4px 9px; border-radius: 999px; background: rgba(255, 255, 255, .06); font-size: 12px; color: var(--ds-ink); }
.ai-tags .k1 { background: color-mix(in srgb, var(--ds-good) 20%, transparent); color: #c6f0d4; }
.ai-next { margin-top: auto; padding-top: 8px; border-top: 1px solid rgba(255, 255, 255, .06); color: var(--ds-ink); }
.ai-next b { font-weight: 500; color: var(--ds-blue); }

/* messages */
.ai-msg { max-width: 88%; padding: 7px 10px; border-radius: 12px; }
.ai-msg.in { align-self: flex-start; border-bottom-left-radius: 4px; background: rgba(255, 255, 255, .07); }
.ai-msg.out { align-self: flex-end; border-bottom-right-radius: 4px; background: color-mix(in srgb, var(--ds-blue) 22%, #15161b); color: var(--ds-ink); }

.ai-home {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 7px;
  padding: 6px;
  border-radius: 8px;
  background: rgba(0, 0, 0, .3);
}

.ai-home b, .ai-home em { display: block; }
.ai-home b { font: 600 11.5px/1.3 var(--ds-font-mono); }
.ai-home em { font-style: normal; font-size: 11.5px; color: var(--ds-ink-3); }

.ai-handoff {
  align-self: flex-start;
  margin-top: auto;
  padding: 4px 9px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ds-good) 18%, transparent);
  color: var(--ds-good);
  font: 500 11.5px/1.3 var(--ds-font-mono);
}

/* Each scene plays once from its first frame into the markup's finished
   one; .wait holds it on the first frame until the chapter is seen. */
.ai-tiles.go .ai-sc>*,
.ai-tiles.go .ai-tags span,
.ai-tiles.go .wave i { animation: ag-row .5s ease-out both; animation-delay: calc(var(--d, 0s) + var(--at, 0s)); }
.ai-tiles.go .wave i { animation-name: ai-wave; animation-duration: .45s; --at: calc(var(--i) * 12ms); }
.ai-tiles.go.wait * { animation-play-state: paused !important; }

@keyframes ai-wave {
  from { transform: scaleY(.15); opacity: 0; }
}

.ai-tiles .t-call .ai-callh { --at: .1s; }
.ai-tiles .t-call .ai-tags { animation: none !important; }
.ai-tiles .t-call .ai-tags .k1 { --at: 1.1s; }
.ai-tiles .t-call .ai-tags .k2 { --at: 1.35s; }
.ai-tiles .t-call .ai-tags .k3 { --at: 1.6s; }
.ai-tiles .t-call .ai-next { --at: 2.1s; }
.ai-tiles .t-client { --d: .5s; }
.ai-tiles .t-owner { --d: 1s; }
.ai-tiles .m1 { --at: .2s; }
.ai-tiles .m2 { --at: 1.4s; }
.ai-tiles .m3 { --at: 2.3s; }

/* ── narrower screens ── */
@media (max-width: 1080px) {
  .ai-ask { grid-template-columns: minmax(0, 1fr); }
  .ai-tiles { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .ai-tile.t-owner { grid-column: 1 / -1; }
}

@media (max-width: 700px) {
  .ch-ai { padding: 16px 0; }
  .ai { padding: 22px 16px; border-radius: 16px; }
  .ai-tiles { grid-template-columns: minmax(0, 1fr); }
  .ai-chat { height: 380px; }
  .ai-bot { min-height: 240px; }
  .ai-bot svg { height: 170px; }
}

@media (prefers-reduced-motion: reduce) {
  .ai-think i,
  .ai-bot svg { animation: none !important; }
}

/* ---------- Section 9 — Closing ---------- */
.closing {
  position: relative;
  text-align: center;
  padding: 140px 0;
  border-top: 1px solid var(--ds-line-2);
  /* Opaque: here the building is the whole backdrop. */
  background: #000;
  overflow: hidden;
  isolation: isolate;
}

/* The facade itself. Bled slightly past the edges so no half window is
   clipped flush against the section border. */
.cl-facade {
  position: absolute;
  /* -4% horizontally matches .fh-facade, so with the same column count and
     both sections full-bleed the cell WIDTH comes out identical. */
  inset: -3% -4%;
  z-index: 0;
  display: grid;
  column-gap: clamp(7px, .62vw, 13px);
  row-gap: clamp(13px, 1.5vw, 26px);
}

/* Darkens the middle so the copy keeps its contrast, and carries the page
   accent the old radial background used to. Vertical fades at top and
   bottom stop the facade ending on a hard line against the sections above
   and below. */
.cl-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(ellipse at 50% 50%, rgba(130, 144, 240, .09), transparent 60%),
    radial-gradient(ellipse 55% 60% at 50% 50%, rgba(0, 0, 0, .88) 0%, rgba(0, 0, 0, .72) 45%, rgba(0, 0, 0, .3) 78%, transparent 100%),
    linear-gradient(to bottom, #000 0%, transparent 14%, transparent 86%, #000 100%);
}

.closing > .lp {
  position: relative;
  z-index: 2;
}

.closing h2 {
  font-family: var(--ds-font-display);
  font-size: clamp(36px, 5vw, 60px);
  font-weight: 500;
  letter-spacing: -0.025em;
  line-height: 1.08;
  max-width: 920px;
  margin: 0 auto 28px;
}

.closing h2 em {
  font-style: italic;
  color: var(--ds-blue);
  font-weight: 400;
}

.closing p {
  max-width: 620px;
  margin: 0 auto 36px;
}

/* ---------- Footer ---------- */
.lp-footer {
  border-top: 1px solid var(--ds-line-2);
  padding: 40px 0;
  font-size: 12.5px;
  color: var(--ds-ink-4);
}

.lp-footer .row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}

.lp-footer .row+.row {
  margin-top: 14px;
}

.lp-footer a:hover {
  color: var(--ds-ink-2);
}

.lp-footer .sep {
  color: var(--ds-ink-5);
  margin: 0 8px;
}

/* ---------- Responsive ---------- */
@media (max-width: 980px) {

  .section {
    padding: 70px 0;
  }
}

/* ---------- Animations & micro-interactions ---------- */
@media (prefers-reduced-motion: no-preference) {

  /* Scroll-triggered fade-up */
  [data-reveal] {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity .75s cubic-bezier(.2, .7, .2, 1),
      transform .75s cubic-bezier(.2, .7, .2, 1);
    will-change: opacity, transform;
  }

  [data-reveal].in {
    opacity: 1;
    transform: none;
  }

  [data-reveal-stagger]>* {
    opacity: 0;
    transform: translateY(22px);
    transition: opacity .65s cubic-bezier(.2, .7, .2, 1),
      transform .65s cubic-bezier(.2, .7, .2, 1);
  }

  [data-reveal-stagger].in>* {
    opacity: 1;
    transform: none;
  }

  [data-reveal-stagger].in>*:nth-child(1) {
    transition-delay: 0s;
  }

  [data-reveal-stagger].in>*:nth-child(2) {
    transition-delay: .08s;
  }

  [data-reveal-stagger].in>*:nth-child(3) {
    transition-delay: .16s;
  }

  [data-reveal-stagger].in>*:nth-child(4) {
    transition-delay: .24s;
  }

  [data-reveal-stagger].in>*:nth-child(5) {
    transition-delay: .32s;
  }

  [data-reveal-stagger].in>*:nth-child(6) {
    transition-delay: .40s;
  }

  [data-reveal-stagger].in>*:nth-child(7) {
    transition-delay: .48s;
  }

  [data-reveal-stagger].in>*:nth-child(8) {
    transition-delay: .56s;
  }

  [data-reveal-stagger].in>*:nth-child(9) {
    transition-delay: .64s;
  }

  /* Ambient hero motion — preserves the 3D tilt */
  /* Button shimmer on hover */
  .btn.primary {
    position: relative;
    overflow: hidden;
    isolation: isolate;
  }

  .btn.primary::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(120deg, transparent 30%, rgba(255, 255, 255, .15) 50%, transparent 70%);
    transform: translateX(-110%);
    transition: transform .6s cubic-bezier(.2, .7, .2, 1);
    z-index: -1;
  }

  .btn.primary:hover::after {
    transform: translateX(110%);
  }

  .btn.ghost:hover::before {
    animation: btn-ring 2.4s linear infinite;
  }

  @keyframes btn-ring {
    to { rotate: 360deg; }
  }
}

@media (max-width: 620px) {
  .lp {
    padding: 0 16px;
  }

  /* Topbar — hide secondary nav links + login button, keep only
     the lang toggle and primary CTA so the row actually fits on a
     phone. Users can still scroll to anchored sections. */
  .topbar-inner {
    gap: 10px;
  }

  .topbar .links {
    gap: 6px;
  }

  .topbar .links a.nav,
  .topbar .links .btn.ghost {
    display: none;
  }

  /* Buttons shrink slightly so the CTA row fits */
  .btn {
    padding: 10px 14px;
    font-size: 13px;
  }

  .btn.lg {
    padding: 12px 18px;
    font-size: 14px;
  }

  /* Section spacing tightens — 70px from 980px tier is still too
     much on a phone */
  .section {
    padding: 56px 0;
  }

  .closing {
    padding: 80px 0;
  }

  .display-h1 {
    margin: 14px 0 20px;
  }

  .display-h2 {
    margin: 10px 0 18px;
  }
}

@media (max-width: 420px) {

  .hero .btn-row {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }

  .hero .btn-row .btn {
    justify-content: center;
    width: 100%;
  }

  /* Font clamp floors were 42px / 32px — still a bit heavy
     on 360-px viewports with Georgian letter widths */
  .display-h1 {
    font-size: 36px;
  }

  .display-h2 {
    font-size: 26px;
  }

  /* Closing CTA button full-width */
  .closing .btn-row {
    display: flex;
    flex-direction: column;
    align-items: stretch;
  }

  .closing .btn-row .btn {
    justify-content: center;
  }
}

/* ===================================================================
   Facade hero — the full-bleed section above the informational hero.
   A grid of windows on a near-black ground: each lit window is a
   listing, dark ones are units not in the portfolio yet. Windows are
   generated by static/js/landing-hero.js so each can carry its own
   colour temperature and so chips can anchor to one.
   Prefixed .fh- to stay clear of the .lp / .lp-footer container classes.
   =================================================================== */
.fh {
  position: relative;
  /* The topbar is sticky, so it takes flow space above. Pull back by exactly
     its height and pad the content by the same amount: the facade then runs
     underneath the transparent bar instead of starting below it, and the
     section still fills one viewport with the stat strip inside the fold. */
  margin-top: calc(-1 * var(--lp-topbar-h));
  padding-top: var(--lp-topbar-h);
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  background: #000;
  display: flex;
  align-items: center;
  isolation: isolate;
}

.fh-facade {
  position: absolute;
  inset: -6% -4%;
  z-index: 0;
  display: grid;
  column-gap: clamp(7px, .62vw, 13px);
  row-gap: clamp(13px, 1.5vw, 26px);
}

/* Painted facades (landing-hero.js paintedFacade): the resting building
   is one canvas; a window mid-fade is a real .fh-win in .fh-live on top. */
.fh-canvas,
.fh-live {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
}

.fh-live {
  right: 0;
  bottom: 0;
}

.fh-live>.fh-win {
  position: absolute;
}

/* Its own layer for the few windows mid-fade, so the glow fades on the
   compositor instead of being re-blurred every frame. */
.fh-live>.fh-win::after {
  will-change: opacity;
}

.fh-win {
  position: relative;
  border-radius: 1px;
  background: #030304;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .022);
  opacity: 0;
  /* box-shadow is deliberately NOT transitioned: a blurred shadow repaints on
     the main thread, and animating it on hundreds of elements dropped roughly
     every other frame. The glow lives on ::after and fades with opacity. */
  transition: opacity 1.1s ease, background 1.5s ease;
}

/* The glow. Separate element so it can fade with opacity alone, which the
   compositor handles without repainting the shadow each frame. */
.fh-win::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 1px;
  pointer-events: none;
  opacity: 0;
  /* Every frame of this fade re-blurs two shadows, so the duration is a
     direct multiplier on raster work: the ambient loop keeps a cohort of
     windows mid-fade at all times, and at 1.5s that cohort was ~2.5x
     larger than it needed to be. The window's own colour still crosses
     over the full 1.5s, so a room still warms up slowly — only the halo
     settles quickly. */
  transition: opacity .6s ease;
  box-shadow:
    0 0 26px -5px var(--g, rgba(255, 186, 94, .55)),
    0 0 72px -18px var(--g, rgba(255, 186, 94, .45));
}

.fh-win.lit::after {
  opacity: 1
}

.fh-win.on {
  opacity: 1
}

/* Mullions drawn with gradients rather than child nodes — one element
   per window instead of three, which matters at ~450 of them. */
.fh-win::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 1px;
  background:
    linear-gradient(to right, transparent calc(50% - .6px), rgba(0, 0, 0, .75) calc(50% - .6px), rgba(0, 0, 0, .75) calc(50% + .6px), transparent calc(50% + .6px)),
    linear-gradient(to bottom, transparent calc(34% - .6px), rgba(0, 0, 0, .6) calc(34% - .6px), rgba(0, 0, 0, .6) calc(34% + .6px), transparent calc(34% + .6px));
}

.fh-win.lit {
  background: var(--w, rgba(255, 186, 94, .7));
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .07);
}

/* Under the cursor. Brighter than any resting window and quicker to arrive
   than to leave, so a swept mouse draws a trail that lingers. */
.fh-win.hot {
  background: var(--hot, rgba(255, 214, 156, .95));
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .16);
  /* Near-instant on the way in; the slow fade out is .fh-win's 1.5s
     transition taking over when .hot is removed. */
  transition: background .03s linear;
}

.fh-win.hot::after {
  opacity: 1;
  box-shadow:
    0 0 32px -3px var(--hot, rgba(255, 214, 156, .8)),
    0 0 90px -16px var(--hot, rgba(255, 214, 156, .6));
  transition: opacity .03s linear;
}

/* Atmosphere, in layers: warm bloom over the lit mass, cool haze up top,
   the page accent washing in from below. */
.fh-haze {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(60% 45% at 72% 44%, rgba(255, 176, 74, .13), transparent 70%),
    radial-gradient(80% 50% at 30% 8%, rgba(140, 160, 210, .05), transparent 70%),
    radial-gradient(120% 80% at 50% 112%, rgba(130, 144, 240, .10), transparent 62%);
}

/* Keeps the large black areas from banding, and reads as film. */
.fh-grain {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  opacity: .5;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/><feColorMatrix type='saturate' values='0'/></filter><rect width='140' height='140' filter='url(%23n)' opacity='.055'/></svg>");
  /* the grain sits above the bottom fade; let it go with it, so the hero
     ends in the same black the chapters start on */
  -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 95%);
  mask-image: linear-gradient(to bottom, #000 70%, transparent 95%);
}

.fh-vig {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(90% 70% at 50% 45%, transparent 35%, rgba(0, 0, 0, .72) 78%, #000 100%);
}

/* The copy sits on this; without it the Georgian headline is unreadable
   over lit windows. */
.fh-scrim {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(90deg, rgba(0, 0, 0, .88) 0%, rgba(0, 0, 0, .66) 36%, rgba(0, 0, 0, .18) 62%, transparent 78%);
}

.fh-fade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 22%;
  z-index: 4;
  pointer-events: none;
  background: linear-gradient(to bottom, transparent, #000 92%);
}

.fh-inner {
  position: relative;
  z-index: 6;
  width: 100%;
  max-width: var(--lp-measure);
  margin: 0 auto;
  padding: 0 28px;
  /* This box is the full measure wide but only the copy is in it, so its
     empty right half was lying over the listing cards and eating their
     hover — and only for the cards whose band fell inside it, which is why
     some grew and some didn't. The pointer passes through the container;
     the copy itself takes it back, so the headline stays selectable and
     the CTA stays clickable. */
  pointer-events: none;
}

.fh-copy {
  max-width: 600px;
  pointer-events: auto;
}

.fh-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font: 500 11px/1 var(--ds-font-mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ds-ink-4);
  padding: 7px 12px;
  border: 1px solid var(--ds-line);
  border-radius: 999px;
  background: rgba(255, 255, 255, .02);
  backdrop-filter: blur(6px);
}

.fh-eyebrow i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #FFBA5E;
  box-shadow: 0 0 10px 1px rgba(255, 186, 94, .8);
}

.fh-h1 {
  font-family: var(--ds-font-display);
  font-weight: 500;
  letter-spacing: -.035em;
  line-height: 1.0;
  font-size: clamp(40px, 5.6vw, 78px);
  margin: 10px 0 0;
  text-wrap: balance;
}

/* The typed word: every word stacked in one cell, only the typed one
   visible, so the slot is as wide as the longest and nothing reflows. */
.fh-type {
  display: inline-grid;
  justify-items: start;
}

.fh-type > span { grid-area: 1 / 1; }
.fh-type-s { visibility: hidden; }

.fh-type-w::after {
  content: '';
  display: inline-block;
  width: .06em;
  height: .82em;
  margin-left: .07em;
  vertical-align: -.04em;
  background: var(--ds-blue);
  opacity: 0;
}

.fh-type.is-typing .fh-type-w::after { opacity: 1; }
.fh-type.is-blink .fh-type-w::after { animation: fh-caret 1s steps(1) infinite; }
@keyframes fh-caret { 0% { opacity: 1; } 50% { opacity: 0; } }

/* Georgian letters sit about a third of an em below the top of their line
   box, so the same margin reads as a much bigger gap than in English. */
:lang(ka) .fh-h1 { margin-top: calc(18px - .33em); }

.fh-h1 em {
  font-style: italic;
  font-weight: 400;
  color: var(--ds-blue)
}

.fh-cta {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 34px
}

/* Chips tethered to the facade — the listing data the windows stand for. */
.fh-chip {
  position: absolute;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, .10);
  background: rgba(14, 15, 18, .72);
  backdrop-filter: blur(14px) saturate(1.2);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .55);
  font: 500 11.5px/1.35 var(--ds-font-mono);
  color: var(--ds-ink);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(6px);
  scale: 1;
  transition: opacity .7s ease, transform .7s ease, scale .22s ease;
}

/* Hover: the card comes forward.

   `scale` as its own property rather than folded into `transform`, because
   transform is already carrying two states — the entrance lift and the
   swap lift — and composing all three in one declaration would mean every
   state had to restate the others. As a separate property it just
   multiplies in, and .fh-chip.in / .swap stay untouched.

   z-index 6 and not higher: that lifts the hovered card above its siblings
   (5) while .fh-inner, also 6 but later in the DOM, keeps the headline on
   top — an enlarged card should never cover the copy. */
.fh-chip:hover {
  scale: 1.07;
  /* Above .fh-inner, not level with it. Holding at 6 meant the headline —
     same layer, later in the DOM — painted across an enlarged card wherever
     the two met, which at narrower desktop widths they do. A card being
     pointed at should be whole; the copy is static and can be behind it. */
  z-index: 7;
  /* Nearly opaque while raised. At .72 a card the enlarged one covered
     showed through it, blurred by the backdrop-filter, and read as a
     smear rather than as two cards. */
  background: rgba(14, 15, 18, .95);
  border-color: rgba(255, 255, 255, .18);
  box-shadow: 0 24px 54px rgba(0, 0, 0, .62);
}

.fh-chip.in {
  opacity: 1;
  transform: none
}

/* Mid-swap: the card lifts and clears while its replacement is written in.
   Only opacity and transform, so the swap composites rather than repaints.
   Faster than the .fh-chip entrance on purpose — the script rewrites the
   text 320ms in, and on the 0.7s entrance timing the card was still half
   visible when its content changed. */
.fh-chip.swap {
  opacity: 0;
  transform: translateY(-5px);
  transition: opacity .28s ease, transform .28s ease, scale .22s ease;
}

.fh-chip .sw {
  /* 22px when this was a colour blob; a house drawing needs a little more
     to be anything but a smudge. */
  width: 32px;
  height: 32px;
  border-radius: 6px;
  border: 1px solid var(--lp-house-edge);
  flex: 0 0 auto;
  transition: background .3s ease;
}

.fh-chip .ttl {
  display: block
}

.fh-chip .sub {
  display: block;
  color: var(--ds-ink-4);
  font-size: 10px;
  letter-spacing: .04em;
  margin-top: 2px
}

.fh-chip.c1 {
  top: 22%;
  right: 11%
}

.fh-chip.c2 {
  top: 49%;
  right: 25%
}

.fh-chip.c3 {
  top: 70%;
  right: 8%
}

@media (max-width: 900px) {
  .fh {
    min-height: 86vh
  }

  .fh-scrim {
    background: linear-gradient(180deg, rgba(0, 0, 0, .55), rgba(0, 0, 0, .9) 55%)
  }

  .fh-chip {
    display: none
  }
}

/* Match .lp's narrow gutter so the hero copy stays in line with the logo
   and with every section below it. */
@media (max-width: 620px) {

  .fh-inner {
    padding: 0 16px
  }
}

@media (prefers-reduced-motion: reduce) {

  .fh-win,
  .fh-chip,
  .fh-chip .sw {
    transition: none
  }

  .fh-chip {
    transition: none
  }

  .fh-chip:hover {
    scale: 1
  }

  .fh-chip.swap {
    opacity: 1;
    transform: none
  }
}

/* A coarse pointer has no hover, and the ambient loop is the only motion
   worth keeping there. */
@media (hover: none) {
  .fh-win.hot {
    background: var(--w)
  }

  .fh-win.hot::after {
    opacity: 0
  }
}

/* ===================================================================
   EXPERIMENT — texture on the accent words
   Switch with an attribute on <html>:

       document.documentElement.dataset.em = 'slab'   // or 'flat'

   No attribute = rim (the default below). 'flat' is today's solid colour.
   Once one is chosen the other goes.

   What was tried and dropped: a gradient fill clipped to the glyphs, the
   same plus a travelling sheen, and the same plus fractal noise — none of
   them read at any strength. The accent is 400-weight italic Mkhedruli,
   about 5px of stroke at 52px, and texture inside a letterform needs area
   to live in; a white-to-dark-to-white gradient far harder than anything
   shippable still came out as faintly uneven colour. Then a uniform
   hairline around the glyph, which read as exactly what it was — a white
   stroke. So both treatments below light the word directionally or put it
   on a surface, rather than texturing the inside of it.
   =================================================================== */

/* One list, because the accent <em> is styled in five places further up
   and two of them (.mock-title, .fh-h1) are declared after the display
   headings. Listing them here, late, is what makes this win. */
@supports (background-clip: text) and (color: color-mix(in oklab, red 50%, blue)) {

  /* --- bevel (default): the word as a lit solid ----------------------
     Directional light, not an outline. A uniform stroke around the glyph
     just reads as a stroke; what makes a material read is one edge
     catching light and the opposite edge in shadow, with the whole thing
     lifted slightly off the page. So: a hairline highlight up and left, a
     hard dark edge down and right, and a soft drop under both.

     The gradient is mixed off --ds-blue so the accent stays the brand
     colour rather than washing toward white — an earlier pass lit it hard
     enough to go lavender. Light and shade are literal rgba: they are
     lighting, not brand.

     Everything is in em, so it scales with the heading instead of
     swamping the 26px mobile size and the small mock-dashboard title. */
  :root:not([data-em="flat"]):not([data-em="slab"]) .display-h1 em,
:root:not([data-em="flat"]):not([data-em="slab"]) .display-h2 em,
:root:not([data-em="flat"]):not([data-em="slab"]) .closing h2 em,
:root:not([data-em="flat"]):not([data-em="slab"]) .fh-h1 em {
    /* lit top facet and shaded bottom, named so the gradient below stays
       readable once each one is also made translucent */
    --em-hi: color-mix(in oklab, var(--ds-blue) 72%, #fff);
    --em-lo: color-mix(in oklab, var(--ds-blue) 76%, #120D33);
    /* The fill is held a little short of opaque — enough for the ground to
       come through and read as a pane, not so much that the accent goes
       quiet next to the white it sits beside. */
    background-image:
      /* Reeded glass. Fine ribs at the italic's own angle, so they run
         with the letterforms rather than across them. Pitch is in em and
         lands around 2-3px at heading sizes: at the actual viewing size
         this reads as a shimmer along the strokes rather than as visible
         flutes, which is as much as 400-weight Mkhedruli has room for.
         Ribbing, not noise, because a repeating structure survives being
         clipped to a 5px stroke where random grain just muddies it. */
      repeating-linear-gradient(104deg,
        rgba(255, 255, 255, calc(.3 * var(--lp-em-flute))) 0 .03em,
        rgba(10, 8, 30, calc(.26 * var(--lp-em-flute))) .03em .075em,
        rgba(255, 255, 255, calc(.1 * var(--lp-em-flute))) .075em .13em),
      /* the light that travels over it, under the ribs so the ribbing
         modulates the highlight the way a real surface would */
      linear-gradient(100deg,
        transparent 40%,
        rgba(255, 255, 255, calc(.55 * var(--lp-em-sweep))) 50%,
        transparent 60%),
      linear-gradient(176deg,
        color-mix(in srgb, var(--em-hi) 80%, transparent) 0%,
        color-mix(in srgb, var(--ds-blue) 74%, transparent) 32%,
        color-mix(in srgb, var(--em-lo) 70%, transparent) 100%);
    background-size: auto, 300% 100%, 100% 100%;
    background-repeat: repeat, no-repeat, no-repeat;
    background-position: 0 0, 140% 0, 0 0;
    /* The ribs stay put and the highlight moves, which is what reeded glass
       actually does — and it makes the loop seamless for free, since the
       band parks off-stage instead of having to land back on a rib
       boundary. Mostly dead time: it crosses in the first third of
       --lp-em-sweep-time and rests for the rest, so it reads as catching
       the light now and then rather than as something running on a loop.
       Only the crossing is an animation (.is-sweeping, below); the rest is
       a timer in landing-hero.js, because a running animation restyles
       every frame even while nothing moves. */
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    text-shadow:
      -.010em -.013em 0 rgba(198, 208, 255, .38),
      .010em .015em 0 rgba(16, 12, 38, .92),
      .029em .046em .058em rgba(10, 8, 26, .5);
    /* The halo a lit pane throws onto what is behind it. drop-shadow and
       not text-shadow: it is cast from the composited glyph including its
       transparency, so it stays consistent with the fill above.

       Three layers, not one. A single blur reads as a smudge; what makes
       something look like it is emitting is a tight bright core falling
       off into a wide soft wash. The filters chain, so each one blurs the
       result of the last — that compounding is what gives the falloff.
       Radii scale with --lp-em-glow, and in em so they track the heading. */
    filter:
      drop-shadow(0 0 calc(.14em * var(--lp-em-glow)) color-mix(in srgb, var(--em-hi) calc(80% * var(--lp-em-glow)), transparent))
      drop-shadow(0 0 calc(.45em * var(--lp-em-glow)) color-mix(in srgb, var(--ds-blue) calc(60% * var(--lp-em-glow)), transparent))
      drop-shadow(0 0 calc(1.3em * var(--lp-em-glow)) color-mix(in srgb, var(--ds-blue) calc(45% * var(--lp-em-glow)), transparent));
  }

  /* --- slab: the word on a pane of glass ------------------------------
     Lit top edge, a shadow under it, and the fill carrying the accent.
     box-decoration-break keeps the pane whole when a heading wraps. */
  :root[data-em="slab"] .display-h1 em,
:root[data-em="slab"] .display-h2 em,
:root[data-em="slab"] .closing h2 em,
:root[data-em="slab"] .fh-h1 em {
    background-image: linear-gradient(170deg,
        color-mix(in srgb, var(--ds-blue) 26%, transparent),
        color-mix(in srgb, var(--ds-blue) 10%, transparent));
    color: color-mix(in oklab, var(--ds-blue) 42%, #fff);
    border-radius: 10px;
    padding: .04em .22em .1em;
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, .3),
      inset 0 -1px 0 color-mix(in srgb, var(--ds-blue) 30%, transparent),
      0 8px 30px -14px color-mix(in srgb, var(--ds-blue) 70%, transparent);
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
  }

  :root:not([data-em="flat"]):not([data-em="slab"]) em.is-sweeping {
    animation: lp-em-glass calc(var(--lp-em-sweep-time) * .34) ease-in-out;
  }

  @keyframes lp-em-glass {
    from {
      background-position: 0 0, 140% 0, 0 0
    }

    to {
      background-position: 0 0, -45% 0, 0 0
    }
  }

  @media (prefers-reduced-motion: reduce) {

    :root:not([data-em="flat"]):not([data-em="slab"]) .display-h1 em,
:root:not([data-em="flat"]):not([data-em="slab"]) .display-h2 em,
:root:not([data-em="flat"]):not([data-em="slab"]) .closing h2 em,
:root:not([data-em="flat"]):not([data-em="slab"]) .fh-h1 em {
      animation: none;
      background-position: 0 0, -45% 0, 0 0;
    }
  }
}

/* The page's sections stack above the body. Side rails (the facade
   continued in the margins) used to sit between them; they were removed
   for cost — a full-page-height masked layer for a strip beside the
   full-bleed chapters. .topbar is deliberately NOT in this list: it is
   position: sticky with z-index 40 of its own, and resetting it to
   relative here is what un-stuck the header. */
body {
  position: relative
}

.fh,
main.lp,
.closing,
.lp-footer {
  position: relative;
  z-index: 1
}

/* On wide monitors the chapters stop growing (1760px); the margins beside
   them show the building, faintly: one static tile of windows
   (img/facade-tile.svg), no canvas, no animation, fading out before it
   reaches the cards. */
@media (min-width: 1880px) {
  main.lp::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 100vw;
    margin-left: -50vw;
    z-index: -1;
    pointer-events: none;
    background: url("../img/facade-tile.svg") center top / 560px auto repeat;
    opacity: .12;
    /* out towards the cards, and in and out gently at the top and bottom
       so it meets the hero and the closing without a seam */
    -webkit-mask-image:
      linear-gradient(90deg, #000 0, #000 calc(50% - 1000px), transparent calc(50% - 890px), transparent calc(50% + 890px), #000 calc(50% + 1000px)),
      linear-gradient(to bottom, transparent 0, #000 480px, #000 calc(100% - 480px), transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
      linear-gradient(90deg, #000 0, #000 calc(50% - 1000px), transparent calc(50% - 890px), transparent calc(50% + 890px), #000 calc(50% + 1000px)),
      linear-gradient(to bottom, transparent 0, #000 480px, #000 calc(100% - 480px), transparent 100%);
    mask-composite: intersect;
  }
}

/* ===================================================================
   Idle cost
   -------------------------------------------------------------------
   Two things here. First, the accent sheen stops when it scrolls out of
   view. Second, lite mode: the same quiet page prefers-reduced-motion
   already produces, entered on hardware evidence instead of a
   preference (landing-hero.js sets data-lite on <html>).

   Measured, sitting still at the top of the page doing nothing:
   ~4,000 raster tasks per six seconds as shipped, 0 under reduced
   motion. The facade's ambient loops are the cause — each tick starts
   a 1.1-1.5s transition on windows whose ::after carries two large
   blurred shadows, and a blur re-rasters every frame of that fade.
   =================================================================== */

/* pulse animates box-shadow on the main thread and was the only animation
   still running under reduced motion — the coverage is spread over five
   media blocks and this one was missed. */
/* Lite: stop the facade moving. This is the rule that does the work —
   the windows' own transitions are what the ambient loops were paying
   for, and the JS stops ticking in lite mode anyway. */
[data-lite] .fh-win,
[data-lite] .fh-win::after,
[data-lite] .fh-chip,
[data-lite] .fh-chip .sw {
  transition: none;
}

:root[data-lite]:not([data-em="flat"]):not([data-em="slab"]) .display-h1 em,
:root[data-lite]:not([data-em="flat"]):not([data-em="slab"]) .display-h2 em,
:root[data-lite]:not([data-em="flat"]):not([data-em="slab"]) .closing h2 em,
:root[data-lite]:not([data-em="flat"]):not([data-em="slab"]) .fh-h1 em {
  animation: none;
  background-position: 0 0, -45% 0, 0 0;
}

/* Lite: one glow layer instead of two. The wide 72px halo is the
   expensive half — blur cost scales with the area covered, and at that
   radius the halos of ~360 lit windows overlap heavily. Dropping it
   takes 31% off idle raster time where dropping the glow entirely takes
   53%, so this is most of the win for a change you have to look for.

   A radial-gradient was tried here instead and measured slightly better
   again (36%), but it cannot reproduce the negative spread: the tight
   inner falloff became soft blobs that swallowed the windows. */
[data-lite] .fh-win::after {
  box-shadow: 0 0 26px -5px var(--g, rgba(255, 186, 94, .55));
}

[data-lite] .fh-win.hot::after {
  box-shadow: 0 0 32px -3px var(--hot, rgba(255, 214, 156, .8));
}

/* Lite: backdrop-filter makes the GPU re-read and re-blur what is behind
   the element. On the sticky bar that is every scrolled frame, across the
   full width of the page. Integrated graphics pay for this in a way an
   M-series Mac does not. The solid fills below are the same colours at
   full opacity, so nothing moves — it just stops being see-through. */
[data-lite] .topbar {
  backdrop-filter: none;
  background: rgba(11, 13, 16, .96);
  transition: background .25s ease, border-color .25s ease;
}

[data-lite] .fh-chip {
  backdrop-filter: none;
  background: rgba(14, 15, 18, .95);
}

[data-lite] .fh-eyebrow {
  backdrop-filter: none;
}

[data-lite] .btn.ghost:hover::before {
  animation: none;
}
