/**
 * Davar v2 — the shell: reset, base type, header, footer, dock.
 *
 * Reads only from tokens.css. No hex value belongs in this file; if a colour is
 * needed that the tokens do not express, the token set is what should change.
 *
 * RTL is not a variant here. Every inset, margin and padding is written with a
 * logical property (inline-start rather than left), so the Kurdish and Arabic
 * layouts are the same layout rather than a mirrored afterthought that drifts.
 */

/* ── Reset, only what is actually needed ─────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
/**
 * `hidden` means hidden.
 *
 * The browser's own rule for it is display:none at the lowest possible weight,
 * so any component that sets a display of its own silently outranks it — a
 * badge declared `display: grid` stays on screen with the attribute set, which
 * is how a counter reading 0 would have shipped on every page. Said once, at
 * the top, rather than remembered per component.
 */
[hidden] { display: none !important; }
body {
  margin: 0;
  background: var(--surface);
  color: var(--ink-body);
  font: var(--t-base)/1.6 var(--font-ui);
  -webkit-font-smoothing: antialiased;
  /**
   * The dock and the compare tray are both fixed; nothing should end up
   * underneath either. shell.js measures where they actually landed and writes
   * the union here. The fallback is not zero: with no JavaScript the dock is
   * still rendered and still fixed, and reserving nothing would put it on top
   * of the last thing on the page.
   */
  padding-block-end: var(--bottom-clearance, 88px);
}
:lang(ku) body, :lang(ar) body { font-family: var(--font-ar); }
h1, h2, h3, h4, h5 { margin: 0; color: var(--ink); font-weight: 600; text-wrap: balance; }
p { margin: 0; }
img, video, svg { max-width: 100%; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-lit); text-decoration: underline; }
:focus-visible { outline: 2px solid var(--brand-gold); outline-offset: 2px; border-radius: 2px; }

.v2-wrap { max-width: var(--page-max); margin-inline: auto; padding-inline: var(--page-pad); }
.v2-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* Keyboard users must be able to reach the content past the header. */
.v2-skip {
  position: absolute; inset-inline-start: -9999px; top: 0; z-index: var(--z-modal);
  background: var(--surface-deep); color: var(--brand-mint);
  padding: var(--s-3) var(--s-5); border-radius: 0 0 var(--radius) 0;
}
.v2-skip:focus { inset-inline-start: 0; }
.num { font-variant-numeric: tabular-nums; }

/* ══ BUTTONS ═════════════════════════════════════════════════════════
   Shared by every page, so they live here and not in a page sheet. The
   homepage loads only home.css and shell.css, and these were in
   listings.css — its search submit rendered as a bare browser button.
   ═════════════════════════════════════════════════════════════════════ */
.v2-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding-inline: var(--s-5);
  border: 1px solid transparent; border-radius: var(--radius);
  font-size: var(--t-base); font-weight: 600; font-family: inherit;
  cursor: pointer; text-decoration: none;
  transition: filter var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.v2-btn.is-primary { background: var(--accent); color: var(--accent-on); }
.v2-btn.is-primary:hover { filter: brightness(1.08); text-decoration: none; }
.v2-btn.is-line { background: transparent; color: var(--ink-body); border-color: var(--rule-strong); }
.v2-btn.is-line:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.v2-btn.is-live { background: var(--live); color: var(--live-on); }
.v2-btn.is-live:hover { filter: brightness(1.06); text-decoration: none; }
.v2-btn:disabled { opacity: .5; cursor: not-allowed; }
/* Buttons carrying an icon beside their label — save, compare, ask. */
.v2-btn svg { flex: none; margin-inline-end: var(--s-2); }

/* ══ FORMS ════════════════════════════════════════════════════════════
   The enquiry form, and every other form on the site.

   These lived in property.css, because the property page was the first to
   need them — and the contact, sell and valuation pages do not load that
   sheet. The fifth time a shared component has been found trapped in a page
   sheet, and the worst of the five: .v2-hp is the honeypot, hidden by CSS.
   On a page without this rule the field is visible, a real person fills it
   in, and their message is thrown away as a bot.
   ═════════════════════════════════════════════════════════════════════ */
.v2-form { display: grid; gap: var(--s-3); }
.v2-form label { display: grid; gap: var(--s-1); }
.v2-form label > span { font-family: var(--font-mono); font-size: 10px; letter-spacing: .13em; text-transform: uppercase; color: var(--ink-dim); }
.v2-form input, .v2-form textarea {
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--surface); color: var(--ink);
  padding: var(--s-3); font-size: var(--t-sm); min-height: 44px; width: 100%;
}
.v2-form textarea { min-height: 84px; resize: vertical; }
.v2-form input:focus-visible, .v2-form textarea:focus-visible { border-color: var(--accent); }
.v2-form input[aria-invalid="true"] { border-color: var(--live); border-width: 2px; }
.v2-form-error { font-size: var(--t-sm); color: var(--live-on); background: var(--live-soft); padding: var(--s-2) var(--s-3); border-radius: var(--radius); }
/* Honeypot: off-screen rather than display:none, which some bots detect. */
.v2-hp { position: absolute !important; inset-inline-start: -9999px; width: 1px; height: 1px; opacity: 0; }
.v2-sent {
  padding: var(--s-4); border-radius: var(--radius);
  background: var(--surface-warm); border-inline-start: 2px solid var(--verified);
  font-size: var(--t-sm); color: var(--ink-body);
}

/* ══ HEADER ═══════════════════════════════════════════════════════════ */
.v2-hd { position: sticky; top: 0; z-index: var(--z-header); background: var(--surface); }

/* The ticker. Real signals only — it is empty rather than padded. */
.v2-ticker {
  background: var(--surface-warm);
  border-block-end: 1px solid var(--rule-faint);
  font-size: var(--t-xs);
  color: var(--ink-body);
  overflow: hidden;
}
.v2-ticker-in {
  display: flex; align-items: center; gap: var(--s-6);
  padding-block: var(--s-2);
  white-space: nowrap; overflow-x: auto;
  scrollbar-width: none;
}
.v2-ticker-in::-webkit-scrollbar { display: none; }
.v2-ticker .live {
  color: var(--live-on); background: var(--live);
  padding: 2px var(--s-2); border-radius: var(--radius-pill);
  font-size: 10px; font-weight: 700; letter-spacing: .04em; flex: none;
}
.v2-ticker b { color: var(--accent); font-weight: 600; }

.v2-hd-main {
  display: flex; align-items: center; gap: var(--s-5);
  padding-block: var(--s-3);
  border-block-end: 1px solid var(--rule);
}
/**
 * The wordmark is Latin and must stay Latin-ordered.
 *
 * DA<i>V</i>AR is three inline runs. In an RTL paragraph the bidi algorithm
 * keeps each run's own order but lays the runs out right to left, so the
 * brand rendered as "ARVDA" on the Kurdish site — which is the default
 * language, so that was the name almost every visitor would have seen.
 *
 * `direction: ltr` sets the run order; `unicode-bidi: isolate` stops the
 * surrounding Kurdish from pulling it back into the paragraph's ordering.
 * Both are needed — either alone still reorders in some contexts.
 */
.v2-logo {
  display: inline-flex; align-items: baseline; gap: 1px; flex: none;
  direction: ltr; unicode-bidi: isolate;
  font-family: var(--font-display); font-size: var(--t-xl); font-weight: 600;
  letter-spacing: .04em; color: var(--ink); text-decoration: none;
}

/**
 * Anything Latin-and-punctuated inside Kurdish prose needs the same isolation:
 * a CSS custom property renders as "brand-cream--" without it, a URL loses its
 * scheme to the end of the line, and a version number inverts.
 */
.v2-ltr, code, .num[dir="ltr"] { direction: ltr; unicode-bidi: isolate; }
.v2-logo:hover { text-decoration: none; color: var(--ink); }
.v2-logo i { color: var(--brand-gold); font-style: normal; }

.v2-nav { display: none; gap: var(--s-5); flex: 1; min-width: 0; }
@media (min-width: 900px) { .v2-nav { display: flex; } }
.v2-nav a { color: var(--ink-body); font-size: var(--t-sm); white-space: nowrap; }
.v2-nav a:hover { color: var(--accent); text-decoration: none; }
.v2-nav a[aria-current="page"] { color: var(--ink); font-weight: 600; }

.v2-hd-act { display: flex; align-items: center; gap: var(--s-2); margin-inline-start: auto; }
.v2-icon {
  position: relative;
  display: grid; place-items: center; width: 38px; height: 38px;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--surface-card); color: var(--ink-body); cursor: pointer;
  transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.v2-icon:hover { border-color: var(--accent); color: var(--accent); }

/**
 * The small number on a saved heart or beside "Compare".
 *
 * It was called .v2-count, and so is the results count on the listings page —
 * two components, two sheets, one class name. shell.css loads first and
 * listings.css overrode only the colour and size, so the live listings page
 * rendered "84 properties" as a 41×16 gold pill hanging out of the flow above
 * the sort control. Renamed here rather than there: this is the badge, that is
 * a sentence, and the badge is the one nothing had used yet.
 */
.v2-badge {
  position: absolute; inset-block-start: -6px; inset-inline-end: -6px;
  background: var(--live); color: var(--live-on);
  font-size: 9.5px; font-weight: 700; min-width: 16px; height: 16px;
  border-radius: var(--radius-pill); display: grid; place-items: center;
  padding-inline: 4px; font-variant-numeric: tabular-nums;
}
/* Beside a word rather than over an icon: in the flow, not hung off a corner. */
.v2-nav .v2-badge, .v2-menu .v2-badge { position: static; display: inline-grid; margin-inline-start: var(--s-2); }
.v2-lang {
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--surface-card); color: var(--ink-body);
  padding: 0 var(--s-2); height: 38px; font-size: var(--t-sm); cursor: pointer;
}
.v2-burger { display: grid; }
@media (min-width: 900px) { .v2-burger { display: none; } }

/**
 * The menu behind the burger.
 *
 * Inside the sticky header, so it travels with it and needs no z-index of its
 * own. Above 900px the real navigation is back and this cannot be opened —
 * display:none rather than only hiding the button, so a panel left open while
 * the window was being resized cannot survive the change.
 */
.v2-menu { border-block-start: 1px solid var(--rule); background: var(--surface-card); }
@media (min-width: 900px) { .v2-menu { display: none; } }
.v2-menu-nav { display: grid; padding-block: var(--s-1) var(--s-3); }
.v2-menu-nav a {
  display: flex; align-items: center; gap: var(--s-2);
  padding-block: var(--s-3); color: var(--ink); font-size: var(--t-md);
  border-block-end: 1px solid var(--rule-faint);
}
.v2-menu-nav a:hover { color: var(--accent); text-decoration: none; }
.v2-menu-nav a:last-child { border-block-end: 0; }
.v2-menu-nav a[aria-current="page"] { color: var(--accent); font-weight: 600; }

/**
 * ── The compare tray ─────────────────────────────────────────────────────
 *
 * The memory that makes comparing possible: pick a listing, keep browsing, and
 * the selection is still there on the next page.
 *
 * It sits opposite the assistant. On a narrow screen the dock is full width,
 * so the tray rides above it on the height shell.js measures; on a wide one
 * they sit at the same level at opposite ends. Below the dock in the stacking
 * order deliberately — and below the filter sheet, which is fixed to the same
 * corner of a phone screen and must be able to cover this.
 */
.v2-tray {
  position: fixed; z-index: var(--z-sticky);
  inset-inline-start: var(--s-4);
  inset-block-end: var(--dock-clearance, 72px);
  margin-block-end: env(safe-area-inset-bottom, 0px);
  max-width: calc(100vw - var(--s-8));
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  background: var(--surface-deep); color: var(--brand-mint);
  border-radius: var(--radius-lg); box-shadow: var(--lift-3);
  padding-block: var(--s-2);
  padding-inline: var(--s-4) var(--s-2);
}
@media (min-width: 900px) {
  /* Beside the dock rather than above it: there is room, and a bar that moves
     up and down as the assistant opens and closes is a bar that draws the eye
     for no reason. */
  .v2-tray { inset-block-end: var(--s-6); max-width: min(420px, calc(100vw - 480px)); }
}
/**
 * The four slots.
 *
 * Dashed while empty, gold when taken. The count says how many are picked; the
 * slots say how many are left, which is what makes somebody pick a third.
 */
.v2-tray-slots { display: flex; gap: var(--s-1); flex: none; }
.v2-tray-slot {
  width: 26px; height: 26px; border-radius: 3px;
  border: 1px dashed color-mix(in srgb, var(--brand-mint) 34%, transparent);
  background: color-mix(in srgb, var(--brand-mint) 8%, transparent);
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.v2-tray-slot[data-on="true"] {
  background: var(--live); border-style: solid; border-color: transparent;
}
.v2-tray-n { font-size: var(--t-sm); display: flex; align-items: baseline; gap: var(--s-2); }
.v2-tray-n b { font-family: var(--font-display); font-size: var(--t-lg); color: var(--live); }
.v2-tray .v2-tray-go { padding: var(--s-2) var(--s-4); font-size: var(--t-sm); }
.v2-tray-clear {
  flex: none; width: 30px; height: 30px; border-radius: var(--radius-pill);
  border: 1px solid rgba(247, 255, 254, .22); background: transparent;
  color: var(--brand-mint); display: grid; place-items: center; cursor: pointer;
}
.v2-tray-clear:hover { border-color: var(--brand-mint); }
/**
 * The one sentence the script writes: the cap, explained at the moment it is
 * reached. It wraps the tray to a second line rather than opening anything, so
 * hitting the limit never costs the visitor a dismissal.
 */
.v2-tray-note { flex-basis: 100%; font-size: var(--t-xs); color: var(--live-soft); display: none; }
.v2-tray[data-note="true"] .v2-tray-note { display: block; }

/* ══ FOOTER ═══════════════════════════════════════════════════════════ */
.v2-ft {
  background: var(--surface-deep); color: var(--ink-faint);
  margin-block-start: var(--s-20); padding-block: var(--s-12) var(--s-8);
}
.v2-ft a { color: var(--ink-faint); font-size: var(--t-sm); }
.v2-ft a:hover { color: var(--brand-mint); text-decoration: none; }
.v2-ft-cols { display: grid; gap: var(--s-8); }
@media (min-width: 700px) { .v2-ft-cols { grid-template-columns: 1.4fr repeat(3, 1fr); } }
.v2-ft h4 {
  color: var(--brand-mint); font-size: 10.5px; font-family: var(--font-mono);
  letter-spacing: .16em; text-transform: uppercase; margin-block-end: var(--s-3); font-weight: 500;
}
.v2-ft-links { display: grid; gap: var(--s-2); }
.v2-ft-brand .v2-logo { color: var(--brand-mint); font-size: var(--t-2xl); }
.v2-ft-brand p { font-size: var(--t-sm); margin-block-start: var(--s-3); max-width: 34ch; line-height: 1.65; }
.v2-ft-chan { display: flex; gap: var(--s-2); margin-block-start: var(--s-4); flex-wrap: wrap; }
.v2-ft-chan span {
  font-size: var(--t-xs); padding: var(--s-1) var(--s-3);
  border: 1px solid rgba(247, 255, 254, .16); border-radius: var(--radius-pill);
}
.v2-ft-office { margin-block-start: var(--s-4); display: grid; gap: var(--s-1); font-size: var(--t-sm); }
.v2-ft-office a { color: var(--brand-mint); }
.v2-ft-legal {
  display: flex; gap: var(--s-4); flex-wrap: wrap;
  margin-block-start: var(--s-8); padding-block-start: var(--s-4);
  border-block-start: 1px solid rgba(247, 255, 254, .12);
}
.v2-ft-legal a { font-size: var(--t-xs); }
.v2-ft-base {
  margin-block-start: var(--s-10); padding-block-start: var(--s-5);
  border-block-start: 1px solid rgba(247, 255, 254, .1);
  display: flex; gap: var(--s-4); flex-wrap: wrap; justify-content: space-between;
  font-size: var(--t-xs);
}

/* ══ DOCK ═════════════════════════════════════════════════════════════
   Phase 0 ships this inert. It is styled, positioned, measured and reserved
   for — but it makes no request, and every control carries disabled so a
   keyboard or screen reader user is told the truth rather than finding a
   control that does nothing.
   ═════════════════════════════════════════════════════════════════════ */
.v2-dock {
  position: fixed; z-index: var(--z-dock);
  inset-inline: var(--s-4); inset-block-end: var(--s-4);
  /* Phones with a home indicator: sit above it, not under it. */
  margin-block-end: env(safe-area-inset-bottom, 0px);
}
@media (min-width: 900px) {
  .v2-dock { inset-inline: auto var(--s-6); width: min(420px, calc(100vw - var(--s-12))); }
}
.v2-dock-in {
  background: var(--surface-card);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--lift-3);
  padding: var(--s-3);
  display: grid; gap: var(--s-3);
}
.v2-dock-head { display: flex; align-items: center; gap: var(--s-2); }
.v2-dock-mark {
  width: 26px; height: 26px; border-radius: var(--radius-pill); flex: none;
  background: var(--accent); color: var(--accent-on);
  display: grid; place-items: center; font-size: 13px; font-weight: 700;
}
.v2-dock-title { font-size: var(--t-sm); font-weight: 600; color: var(--ink); flex: 1; }
.v2-dock-state {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-dim);
  border: 1px solid var(--rule); border-radius: var(--radius-pill); padding: 2px var(--s-2);
}
.v2-dock-field {
  display: flex; align-items: center; gap: var(--s-2);
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--surface); padding: var(--s-2) var(--s-3);
}
.v2-dock-field input {
  flex: 1; min-width: 0; border: 0; background: transparent; font-size: var(--t-sm);
  color: var(--ink);
}
.v2-dock-field input::placeholder { color: var(--ink-faint); }
.v2-dock-field input:disabled { cursor: not-allowed; }
.v2-dock-send {
  flex: none; width: 30px; height: 30px; border-radius: var(--radius-pill);
  border: 0; background: var(--live); color: var(--live-on);
  display: grid; place-items: center; font-size: 15px; font-weight: 700; cursor: pointer;
}
.v2-dock-send:disabled { background: var(--rule-strong); color: var(--surface-card); cursor: not-allowed; }
.v2-dock-chips { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.v2-dock-chips button {
  font-size: var(--t-xs); padding: var(--s-1) var(--s-3);
  border: 1px dashed var(--rule-strong); border-radius: var(--radius-pill);
  background: transparent; color: var(--ink-body); cursor: pointer;
}
.v2-dock-chips button:disabled { color: var(--ink-faint); cursor: not-allowed; }
.v2-dock-note {
  font-size: 10.5px; color: var(--ink-dim); line-height: 1.5;
  border-block-start: 1px solid var(--rule-faint); padding-block-start: var(--s-2);
}

/* Collapsed: a single tap target, so the dock never eats a phone screen. */
.v2-dock[data-collapsed="true"] .v2-dock-in { display: none; }
.v2-dock-tab {
  display: none; align-items: center; gap: var(--s-2);
  background: var(--surface-deep); color: var(--brand-mint);
  border: 0; border-radius: var(--radius-pill);
  padding: var(--s-3) var(--s-5); box-shadow: var(--lift-3);
  font-size: var(--t-sm); font-weight: 600; cursor: pointer;
  margin-inline-start: auto;
}
.v2-dock[data-collapsed="true"] .v2-dock-tab { display: inline-flex; }
.v2-dock-tab i { color: var(--brand-gold); font-style: normal; }

.v2-fail { padding: var(--s-20) var(--page-pad); text-align: center; color: var(--ink-dim); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
.v2-listings-head { padding-block: var(--s-8) var(--s-5); }
.v2-listings-head h1 { font-family: var(--font-display); font-size: var(--t-3xl); line-height: 1.05; }
.v2-lede { margin-block-start: var(--s-3); max-width: 62ch; font-size: var(--t-md); }
.v2-empty { padding-block: var(--s-16); text-align: center; display: grid; gap: var(--s-3); justify-items: center; }
.v2-empty h2 { font-family: var(--font-display); font-size: var(--t-xl); }
.v2-empty p { max-width: 44ch; font-size: var(--t-sm); color: var(--ink-dim); }
.v2-empty-note { padding-block: var(--s-12); text-align: center; color: var(--ink-dim); }
.v2-sort select, .v2-f-group select, .v2-f-pair input {
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--surface-card); color: var(--ink);
  min-height: 40px; padding-inline: var(--s-3); font-size: var(--t-sm);
  width: 100%;
}
.v2-sort select { width: auto; }
.v2-sort select:focus-visible, .v2-f-group select:focus-visible, .v2-f-pair input:focus-visible {
  border-color: var(--accent);
}
.v2-crumbs { display: flex; gap: var(--s-2); align-items: center; font-size: var(--t-sm); color: var(--ink-dim); flex-wrap: wrap; }
.v2-pd-top { padding-block: var(--s-5) 0; }
.v2-prose { max-width: 70ch; font-size: var(--t-md); line-height: 1.75; color: var(--ink-body); }
.v2-prose p { margin-block-end: var(--s-3); }
.v2-prose ul, .v2-prose ol { margin: 0 0 var(--s-3); padding-inline-start: var(--s-5); }
.v2-prose li { margin-block-end: var(--s-1); }
.v2-prose h3, .v2-prose h4 { font-size: var(--t-lg); margin-block: var(--s-4) var(--s-2); }
.v2-prose strong, .v2-prose b { color: var(--ink); }
.v2-pd-desc, .v2-pd-similar { padding-block: var(--s-10) 0; }
.v2-pd-desc h2, .v2-pd-similar h2 { font-family: var(--font-display); font-size: var(--t-2xl); margin-block-end: var(--s-4); }
.v2-pd-missing { padding-block: var(--s-20); text-align: center; display: grid; gap: var(--s-4); justify-items: center; }
.v2-pd-missing h1 { font-family: var(--font-display); font-size: var(--t-2xl); }
/**
 * The assistant's band.
 *
 * Teal, and inside whatever card it belongs to. It was a near-black slab
 * sitting under the search rather than in it, which read as an advertisement
 * beside the market instead of a part of it.
 */
.v2-ai-band {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--radius);
  background: var(--ai-band); color: var(--brand-mint);
}
.v2-ai-mark {
  width: 28px; height: 28px; flex: none; border-radius: var(--radius-pill);
  display: grid; place-items: center; font-size: 14px;
  background: var(--live); color: var(--live-on);
}
.v2-ai-copy { flex: 1 1 200px; display: grid; gap: 1px; min-width: 0; }
.v2-ai-copy b { font-size: var(--t-sm); color: var(--brand-mint); }
.v2-ai-copy span { font-size: var(--t-xs); color: color-mix(in srgb, var(--brand-mint) 78%, transparent); }
.v2-ai-go { flex: none; min-height: 36px; font-size: var(--t-sm); padding-inline: var(--s-4); }

/* Moved here from the page sheets: every page uses them, and a primitive
   left in one page sheet is invisible to the others. */

/* ══ DOCK THREAD ══════════════════════════════════════════════════════════
   Live from phase 5. The reply is reels because the bot returns cards and the
   stock is 9:16 video with a burned-in caption — three tappable stills answer
   "what have you got" better than a paragraph describing them.
   ═════════════════════════════════════════════════════════════════════════ */
.v2-thread {
  display: grid; gap: var(--s-2); align-content: start;
  max-height: min(46vh, 340px); overflow-y: auto;
  padding: var(--s-1) 2px;
  overscroll-behavior: contain;
}
.v2-bub {
  max-width: 88%; padding: var(--s-2) var(--s-3);
  border-radius: var(--radius-lg); font-size: var(--t-sm); line-height: 1.55;
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.v2-bub.is-ai { background: var(--surface-warm); border: 1px solid var(--rule); color: var(--ink); justify-self: start; }
.v2-bub.is-me { background: var(--accent); color: var(--accent-on); justify-self: end; }
.v2-bub.is-error { border-color: var(--live); background: var(--live-soft); color: var(--live-on); }
.v2-bub.is-typing { display: flex; gap: 4px; align-items: center; }
.v2-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ink-faint); animation: v2blink 1.2s infinite; }
.v2-dot:nth-child(2) { animation-delay: .18s; }
.v2-dot:nth-child(3) { animation-delay: .36s; }
@keyframes v2blink { 0%, 60%, 100% { opacity: .3 } 30% { opacity: 1 } }

.v2-reels { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-2); justify-self: start; width: 100%; }
.v2-reel {
  position: relative; display: block; aspect-ratio: 9 / 16;
  border-radius: var(--radius); overflow: hidden;
  background: var(--surface-deep); border: 1px solid var(--rule);
}
.v2-reel img { width: 100%; height: 100%; object-fit: cover; }
.v2-reel-nomedia { position: absolute; inset: 0; background: var(--surface-warm); }
/* The caption sits below the watermark band, never across it. */
.v2-reel-cap {
  position: absolute; inset-inline: 0; inset-block-end: 0;
  display: grid; gap: 1px; padding: var(--s-2) 6px 6px;
  background: linear-gradient(transparent, rgba(14, 31, 30, .88) 42%);
  color: var(--brand-mint); font-size: 10px; line-height: 1.3;
}
.v2-reel-cap b { font-size: 11.5px; font-variant-numeric: tabular-nums; }
.v2-reel-where { opacity: .8; }
.v2-reel:hover { border-color: var(--brand-gold); }
.v2-bub strong { font-weight: 700; color: inherit; }

/**
 * ── Sharing ───────────────────────────────────────────────────────────────
 *
 * On the listing page this sits inside .v2-pd-collect, which is a two-column
 * grid — so it spans the row and lays its own buttons out rather than being
 * squeezed into one cell beside Save.
 */
.v2-share { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.v2-share .v2-btn { min-block-size: 40px; font-size: var(--t-sm); padding-inline: var(--s-3); }
.v2-share-wa svg { color: var(--live); }

/**
 * ── The dock's way out ────────────────────────────────────────────────────
 *
 * Where the note used to be. It is a link rather than a second primary button:
 * leaving for WhatsApp is a real choice and should look like an option, not
 * like the thing the panel wants you to press.
 */
.v2-dock-wa {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-xs); font-weight: 600; color: var(--ink-body);
  border-block-start: 1px solid var(--rule-faint); padding-block-start: var(--s-2);
  margin-block-start: var(--s-1);
}
.v2-dock-wa svg { color: var(--live); flex: none; }
.v2-dock-wa:hover { color: var(--accent); text-decoration: none; }
.v2-dock-wa:hover svg { color: var(--accent); }

/* ── Tabs ─────────────────────────────────────────────────────────────────
   The homepage's catalogue shelf and the project page's unit types. Moved
   here when the second page needed them, which is the rule this codebase
   already enforces for the card, the grid and the button: a component two
   pages use lives in a sheet both of them load. */
/**
 * Tabs.
 *
 * They are anchors, because ?shelf=fast is a real address the server renders —
 * so they work with no script and a chosen shelf can be sent to somebody. The
 * script upgrades them to swap panels without a reload.
 */
.v2-tabs { display: flex; gap: var(--s-1); flex-wrap: wrap; }
.v2-tab {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); font-weight: 600; color: var(--ink-body);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--rule); border-radius: var(--radius-pill);
  background: var(--surface-card);
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.v2-tab:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.v2-tab .num { font-size: var(--t-xs); color: var(--ink-faint); font-weight: 500; }
.v2-tab[aria-selected="true"] {
  background: var(--accent); border-color: var(--accent); color: var(--accent-on);
}
.v2-tab[aria-selected="true"] .num { color: color-mix(in srgb, var(--accent-on) 72%, transparent); }

/* ══ SLIDESHOW ═══════════════════════════════════════════════════════════
   One photograph at a time on a strip that snaps. Six pictures used to be six
   boxes stacked down the page, which on a phone is a column nobody reaches the
   end of. In shell.css because two pages show photographs and a component two
   pages use lives in a sheet both of them load.
   ════════════════════════════════════════════════════════════════════════ */
.v2-gal { position: relative; }
.v2-gal-rail {
  display: grid; grid-auto-flow: column; grid-auto-columns: 100%;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scrollbar-width: none;
  border-radius: var(--radius-lg);
}
.v2-gal-rail::-webkit-scrollbar { display: none; }
/**
 * Every slide the same size, so the strip does not change height as it moves
 * and the picture always sits where the last one was. The band grows with the
 * viewport and then stops: a gallery that fills the screen is a gallery you
 * have to scroll past to reach the plan underneath it.
 */
/* Flex rather than grid: a percentage height inside a grid area that is not
   stretched resolves to auto in every browser tried, which is how a 1200x675
   photograph came out 660px tall in a 378px band. A flex container has a
   definite cross size and max-block-size holds against it. */
.v2-gal-shot {
  position: relative; display: flex; align-items: center; justify-content: center;
  scroll-snap-align: start;
  background: var(--surface-warm);
  border: 1px solid var(--rule-faint); border-radius: var(--radius-lg);
  overflow: hidden;
  /* Tall enough that a wide photograph uses most of the strip rather than
     sitting in a sea of ground, short enough that the plan under it is still
     on screen: 374px on a phone, 432px on a 900px desktop. */
  block-size: clamp(200px, 48vh, 520px);
}
.v2-gal-shot:hover { text-decoration: none; }
/**
 * Whole, never cropped.
 *
 * Half of these are floor plans — an 894×1274 drawing in a cropping box loses
 * two rooms — and the same rule the property card keeps for a burned-in caption
 * applies to a plan: what carries the information is never cut.
 */
/**
 * Inside the slide, whole. Half of these are floor plans — an 894×1274 drawing
 * cropped loses two rooms — so the picture is fitted to the band rather than
 * filling it, and the ground around it is the page's own warm one.
 */
/**
 * The element fills the slide and the picture is fitted inside it.
 *
 * Sizing the element to the picture instead left a 1200x675 photograph 660px
 * tall in a 378px band — max-block-size: 100% did not hold against an auto
 * height in a grid cell — and overflow: hidden then cropped exactly what a
 * floor plan cannot afford to lose. object-fit does the fitting; the box is
 * simply the band.
 */
.v2-gal-shot img {
  display: block; inline-size: auto; block-size: auto;
  max-inline-size: 100%; max-block-size: 100%; object-fit: contain;
}
.v2-gal-cap {
  position: absolute; inset-inline: 0; inset-block-end: 0;
  padding: var(--s-2) var(--s-3); font-size: var(--t-xs); color: var(--brand-mint);
  background: linear-gradient(to top, color-mix(in srgb, var(--surface-deep) 86%, transparent), transparent);
}

/* The two buttons, for a pointer that cannot swipe. */
.v2-gal-nav {
  position: absolute; inset-block-start: 50%; transform: translateY(-50%);
  inline-size: 40px; block-size: 40px; padding: 0; z-index: 2;
  border: 1px solid var(--rule); border-radius: var(--radius-pill);
  background: var(--surface-card); color: var(--ink);
  box-shadow: var(--lift-2); cursor: pointer; display: none; place-items: center;
  transition: opacity var(--fast) var(--ease), border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.v2-gal-nav.is-prev { inset-inline-start: var(--s-2); }
.v2-gal-nav.is-next { inset-inline-end: var(--s-2); }
.v2-gal-nav:hover { border-color: var(--accent); color: var(--accent); }
.v2-gal-nav[disabled] { opacity: 0; pointer-events: none; }
[dir="rtl"] .v2-gal-nav svg { transform: scaleX(-1); }
@media (hover: hover) and (pointer: fine) {
  .v2-gal[data-v2-gal-ready] .v2-gal-nav { display: grid; }
}

.v2-gal-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3); margin-block-start: var(--s-2);
}
.v2-gal-dots { display: flex; gap: var(--s-1); flex-wrap: wrap; }
.v2-gal-dot {
  inline-size: 20px; block-size: 20px; padding: 0; border: 0; background: transparent;
  cursor: pointer; display: grid; place-items: center; border-radius: var(--radius);
}
.v2-gal-dot::after {
  content: ''; inline-size: 6px; block-size: 6px; border-radius: var(--radius-pill);
  background: var(--rule-strong);
  transition: background var(--fast) var(--ease), inline-size var(--fast) var(--ease);
}
.v2-gal-dot:hover::after { background: var(--accent); }
.v2-gal-dot[aria-current="true"]::after { background: var(--accent); inline-size: 16px; }
.v2-gal-dot:focus-visible { outline: 2px solid var(--brand-gold); outline-offset: 2px; }
.v2-gal-count { font-size: var(--t-xs); color: var(--ink-dim); }

/* ══ THE VIEWER ══════════════════════════════════════════════════════════
   Clicking a photograph used to leave the site for a raw file on a blank tab,
   with the back button as the only way home.
   ════════════════════════════════════════════════════════════════════════ */
.v2-lb {
  position: fixed; inset: 0; z-index: var(--z-modal);
  display: grid; place-items: center; padding: var(--s-4);
  background: color-mix(in srgb, var(--surface-deep) 92%, transparent);
  backdrop-filter: blur(4px);
}
.v2-lb[hidden] { display: none; }
.v2-lb-stage { inline-size: 100%; display: grid; gap: var(--s-2); justify-items: center; min-width: 0; }
.v2-lb-stage img {
  max-inline-size: 100%; max-block-size: 76vh;
  object-fit: contain; border-radius: var(--radius);
  background: var(--surface-warm);
}
.v2-lb-stage figcaption {
  display: flex; gap: var(--s-3); align-items: center; justify-content: center;
  font-size: var(--t-sm); color: color-mix(in srgb, var(--brand-mint) 78%, transparent);
}
/**
 * Every control is out of the flow, so none of them can take a pixel from the
 * picture. On a phone the two side buttons move to the foot, where a thumb
 * reaches them and they are not sitting on the middle of the photograph.
 */
.v2-lb-btn {
  position: absolute; inline-size: 44px; block-size: 44px; padding: 0; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--brand-mint) 28%, transparent);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--surface-deep) 60%, transparent);
  color: var(--brand-mint); display: grid; place-items: center;
}
.v2-lb-btn:hover { border-color: var(--brand-mint); }
.v2-lb-btn[hidden] { display: none; }
.v2-lb-btn.is-close { inset-block-start: var(--s-4); inset-inline-end: var(--s-4); }
.v2-lb-btn.is-prev { inset-inline-start: var(--s-3); inset-block-start: 50%; transform: translateY(-50%); }
.v2-lb-btn.is-next { inset-inline-end: var(--s-3); inset-block-start: 50%; transform: translateY(-50%); }
[dir="rtl"] .v2-lb-btn.is-prev svg, [dir="rtl"] .v2-lb-btn.is-next svg { transform: scaleX(-1); }
@media (max-width: 599px) {
  .v2-lb-btn.is-prev, .v2-lb-btn.is-next {
    inset-block-start: auto; inset-block-end: var(--s-5); transform: none;
  }
}

/* ══ PICKING ONE OF A FEW THINGS ═════════════════════════════════════════
   The site's one answer to "choose one of these": the homepage's ways in, the
   catalogue's four shelves, a project's unit types, the search page's ways to
   start. Icon, name, and the figure that makes the choice — big enough for a
   thumb, and it wraps rather than scrolling out of sight.
   ════════════════════════════════════════════════════════════════════════ */
.v2-pick {
  display: grid; gap: var(--s-2);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (min-width: 480px) { .v2-pick { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 700px) { .v2-pick { gap: var(--s-3); } }
/* Rows of eight and of six, for the two places that hold that many. */
.v2-pick.is-wide { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (min-width: 700px) { .v2-pick.is-wide { grid-template-columns: repeat(8, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .v2-pick:not(.is-wide) { grid-template-columns: repeat(6, minmax(0, 1fr)); } }

.v2-pick-tile {
  position: relative;
  display: grid; justify-items: center; align-content: start; gap: 2px;
  padding: var(--s-3) var(--s-2);
  /* A thumb is about 44px across; nothing here is smaller than twice that. */
  min-block-size: 84px;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--surface-card); color: var(--ink-body); text-align: center;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease),
              color var(--fast) var(--ease);
}
.v2-pick-tile:hover { border-color: var(--accent); background: var(--surface-warm); text-decoration: none; }
.v2-pick-tile[aria-selected="true"], .v2-pick-tile[aria-current="true"] {
  border-color: var(--accent); background: var(--accent); color: var(--accent-on);
}
.v2-pick-tile[aria-selected="true"] .v2-pick-icon, .v2-pick-tile[aria-current="true"] .v2-pick-icon,
.v2-pick-tile[aria-selected="true"] .v2-pick-fig, .v2-pick-tile[aria-current="true"] .v2-pick-fig {
  color: var(--accent-on);
}
.v2-pick-tile:focus-visible { outline: 2px solid var(--brand-gold); outline-offset: 2px; }
/* Sold out, or otherwise still true but no longer available. */
.v2-pick-tile.is-muted { opacity: .62; }

.v2-pick-icon { color: var(--accent); line-height: 0; }
.v2-pick-name {
  font-size: var(--t-xs); font-weight: 600; line-height: 1.25;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  line-clamp: 2; overflow: hidden;
}
.v2-pick-fig { font-size: var(--t-xs); color: var(--ink-dim); }
/* A count that belongs to the tile rather than to the choice: how many
   photographs are behind it, how many listings are in it. */
.v2-pick-badge {
  position: absolute; inset-block-start: var(--s-1); inset-inline-end: var(--s-1);
  min-inline-size: 16px; padding: 0 4px; border-radius: var(--radius-pill);
  background: var(--live); color: var(--live-on);
  font-size: 9.5px; line-height: 16px; text-align: center;
}

/* ══ MEDIA IS FOR LOOKING AT ═════════════════════════════════════════════
   The long press that offers "save image" on a phone, the drag onto a desktop,
   the accidental text selection around a picture. A deterrent, not a lock:
   anything drawn on a screen can be photographed, and the watermark burned into
   owner media is the part that survives that.
   ════════════════════════════════════════════════════════════════════════ */
img, video {
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
  user-select: none; -webkit-user-select: none;
}
/* The card's own controls stay draggable-free too, without losing the pointer. */
.v2-card-media img, .v2-gal-shot img, .v2-lb-stage img, .v2-thumb img, .v2-stage img {
  pointer-events: none;
}
