/* =========================================================================
   Agency platform — front end
   -------------------------------------------------------------------------
   Every colour, face, measure and rhythm here comes from a custom property
   emitted by App\Support\Theme. Nothing is hardcoded, which is what makes one
   component library able to serve agencies that look nothing like each other.

   If you are tempted to write a literal colour in this file: don't. Add a
   token instead, so the admin panel can reach it.
   ========================================================================= */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body.site {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-ink);
  font-family: var(--f-body);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, picture, video, canvas, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }

a { color: inherit; text-underline-offset: .22em; text-decoration-thickness: 1px; }

:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

.skip-link {
  position: absolute; left: -9999px;
  background: var(--c-primary); color: var(--c-on-primary);
  padding: .8rem 1.2rem; z-index: 200; border-radius: var(--radius-sm);
}
.skip-link:focus { left: 1rem; top: 1rem; }

/* ---------- typography ---------- */
h1, h2, h3, h4, .display {
  font-family: var(--f-display);
  font-weight: var(--fw-display);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-display);
  margin: 0 0 .6em;
  text-wrap: balance;
}

h1 { font-size: var(--fs-3xl); }
h2 { font-size: var(--fs-2xl); }
h3 { font-size: var(--fs-xl); }
h4 { font-size: var(--fs-lg); }

p { margin: 0 0 1.15em; max-width: 68ch; }
p:last-child { margin-bottom: 0; }

.lede {
  font-size: var(--fs-lg);
  line-height: 1.5;
  color: var(--c-ink);
  max-width: 62ch;
}

.muted { color: var(--c-ink-muted); }

.kicker {
  font-family: var(--f-body);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--c-kicker);
  margin: 0 0 .9rem;
  display: block;
}

blockquote {
  margin: 0;
  font-family: var(--f-display);
  font-size: var(--fs-xl);
  line-height: 1.32;
  font-weight: var(--fw-display);
  text-wrap: balance;
}

figcaption {
  margin-top: .7rem;
  font-size: var(--fs-sm);
  color: var(--c-ink-muted);
  line-height: 1.45;
}

hr.rule {
  border: 0;
  border-top: var(--rule-weight) solid var(--c-rule);
  margin: var(--rhythm) 0;
}

/* ---------- layout ---------- */
.container {
  width: min(100% - (var(--gutter) * 2), var(--container));
  margin-inline: auto;
}

.container--narrow { width: min(100% - (var(--gutter) * 2), 760px); margin-inline: auto; }
.container--wide   { width: min(100% - (var(--gutter) * 2), 1480px); margin-inline: auto; }

/* Break out of the container without the old trick of closing and
   re-opening the page wrappers mid-template. */
.full-bleed {
  width: 100vw;
  margin-left: 50%;
  transform: translateX(-50%);
}

/* Less above than below: the first section on a page sat a long way down,
   because a rhythm generous enough to separate two sections is too much
   between the header and the start of the content. */
.section { padding-block: var(--rhythm-sm) var(--rhythm); }
.section--tight { padding-block: var(--rhythm-sm); }
.section--flush { padding-block: 0; }
.section + .section { padding-top: 0; }

.section--surface { background: var(--c-surface); }
.section--ink     { background: var(--c-primary); color: var(--c-on-primary); }
.section--ink .muted, .section--ink figcaption { color: color-mix(in srgb, var(--c-on-primary) 70%, transparent); }
.section--ink .kicker { color: var(--c-accent); }

/* -------------------------------------------------------------------------
   Per-section tone and width.

   Applied by the section renderer as a wrapper, so an editor can set the
   background treatment and measure of any block without a component needing
   to know about it. Width works by overriding --container on the wrapper,
   which every .container inside then inherits.
   ------------------------------------------------------------------------- */
.tone { position: relative; }

.tone--surface { background: var(--c-surface); }
.tone--surface-2 { background: var(--c-surface-2); }

.tone--ink {
  background: var(--c-primary);
  color: var(--c-on-primary);
  --c-ink: var(--c-on-primary);
  --c-ink-muted: color-mix(in srgb, var(--c-on-primary) 72%, transparent);
  --c-ink-faint: color-mix(in srgb, var(--c-on-primary) 52%, transparent);
  --c-rule: color-mix(in srgb, var(--c-on-primary) 22%, transparent);
  --c-kicker: var(--c-accent);
  --c-surface: color-mix(in srgb, var(--c-on-primary) 8%, transparent);
  /* Opaque twin of --c-surface, for anything that has to mask what a
     browser paints underneath it - autofill in particular. The
     translucent one cannot hide a solid fill. */
  --c-field-bg: color-mix(in srgb, var(--c-on-primary) 8%, var(--c-primary));
  --c-chip-bg: color-mix(in srgb, var(--c-on-primary) 12%, transparent);
  --c-chip-text: var(--c-on-primary);
  --c-chip-border: color-mix(in srgb, var(--c-on-primary) 26%, transparent);
  --c-data: var(--c-accent);
  /* A dark red is invisible on this panel. */
  --c-error: color-mix(in srgb, #ff8a7a 78%, var(--c-on-primary));
}

.tone--accent {
  background: var(--c-accent);
  color: var(--c-on-accent);
  --c-ink: var(--c-on-accent);
  --c-ink-muted: color-mix(in srgb, var(--c-on-accent) 72%, transparent);
  --c-ink-faint: color-mix(in srgb, var(--c-on-accent) 52%, transparent);
  --c-rule: color-mix(in srgb, var(--c-on-accent) 22%, transparent);
  --c-kicker: color-mix(in srgb, var(--c-on-accent) 80%, transparent);
  --c-surface: color-mix(in srgb, var(--c-on-accent) 8%, transparent);
  --c-field-bg: color-mix(in srgb, var(--c-on-accent) 8%, var(--c-accent));
  --c-data: var(--c-on-accent);
  --c-error: color-mix(in srgb, #ff8a7a 78%, var(--c-on-accent));
}

/* A toned block needs its own vertical padding; a default one is already
   spaced by the section inside it. */
.tone--surface, .tone--surface-2, .tone--ink, .tone--accent { padding-block: var(--rhythm-sm); }

.width--narrow { --container: 760px; }
.width--wide   { --container: 1480px; }
.width--full   { --container: 100%; --gutter: 0px; }

.grid { display: grid; gap: var(--gap); }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--auto { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.grid--split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; }
.grid--split.is-reversed > :first-child { order: 2; }

@media (max-width: 860px) {
  .grid--2, .grid--3, .grid--split { grid-template-columns: minmax(0, 1fr); }
  .grid--split.is-reversed > :first-child { order: 0; }
}

.stack > * + * { margin-top: var(--gap); }

/* ---------- media ---------- */
.frame {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--c-surface);
  box-shadow: var(--shadow);
}

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

.ratio-3-2  { aspect-ratio: 3 / 2; }
.ratio-4-3  { aspect-ratio: 4 / 3; }
.ratio-1-1  { aspect-ratio: 1 / 1; }
.ratio-16-9 { aspect-ratio: 16 / 9; }
.ratio-hero { aspect-ratio: 21 / 9; }

@media (max-width: 720px) {
  .ratio-hero { aspect-ratio: 4 / 3; }
}

figure { margin: 0; }

/* Subtle zoom on hover for interactive imagery only. */
.frame--zoom > img { transition: transform var(--speed) var(--ease); }
a:hover .frame--zoom > img, .frame--zoom:hover > img { transform: scale(1.035); }

/* ---------- chips & buttons ---------- */
.chips { display: flex; flex-wrap: wrap; gap: .55rem; margin: 0; padding: 0; list-style: none; }

.chip {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .38rem .85rem;
  background: var(--c-chip-bg);
  color: var(--c-chip-text);
  border: 1px solid var(--c-chip-border);
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: .02em;
  white-space: nowrap;
}

.chip--accent { background: var(--c-accent-soft); color: var(--c-ink); border-color: transparent; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  padding: .92rem 1.7rem;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--c-cta-bg);
  color: var(--c-cta-text);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: .02em;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--speed) var(--ease), transform var(--speed) var(--ease), border-color var(--speed) var(--ease);
}

.btn:hover { background: var(--c-cta-bg-hover); transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--ghost {
  background: transparent;
  color: currentColor;
  border-color: color-mix(in srgb, currentColor 32%, transparent);
}
.btn--ghost:hover { background: color-mix(in srgb, currentColor 8%, transparent); }

.btn--accent { background: var(--c-accent); color: var(--c-on-accent); }
.btn--accent:hover { background: var(--c-accent-hover); }

.link-more {
  display: inline-flex; align-items: center; gap: .45rem;
  font-size: var(--fs-sm); font-weight: 600; letter-spacing: .03em;
  text-transform: uppercase; text-decoration: none;
  color: var(--c-kicker);
  border-bottom: 1px solid currentColor;
  padding-bottom: .18rem;
  transition: gap var(--speed) var(--ease);
}
.link-more:hover { gap: .8rem; }

/* ---------- header ---------- */
/* The navbar has its own surface, as it did on the original sites, rather than
   being a tint of the page. --c-nav-bg falls back to the page background, so
   an agency that has not set one looks unchanged. */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: var(--c-nav-bg);
  color: var(--c-nav-link);
  border-bottom: 1px solid transparent;
  transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.site-header.is-stuck { border-bottom-color: var(--c-nav-rule); }

.site-header__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem;
  min-height: 78px;
}

.brand { display: inline-flex; align-items: center; gap: .7rem; text-decoration: none; }
/* max-width matters once SVG logos are in play: a wordmark exported at
   600x40 would otherwise render 630px wide and push the navigation off the
   header. Raster logos benefit from the same guard. */
.brand__logo { max-height: 42px; max-width: min(220px, 42vw); width: auto; }
.brand__text { display: grid; gap: .18rem; }
.brand__name {
  font-family: var(--f-display);
  /* Set heavier than the headings - see Theme::wordmarkWeight. The fallback
     keeps it sensible if the token is ever absent. */
  font-weight: var(--fw-brand, var(--fw-display));
  font-size: var(--fs-lg);
  letter-spacing: -0.01em;
  line-height: 1;
}
/* Second line of the wordmark: "Cheshire", "Prime Property".
 *
 * It was the smallest text on the page - around 10.6px - and sat at 78%
 * opacity on top of that, which left the lower half of the lockup noticeably
 * weaker than the name above it. Widely tracked capitals are already hard
 * work at that size, and fading them as well was one compromise too many. */
.brand__strapline {
  font-family: var(--f-body);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-kicker);
  line-height: 1;
  opacity: .92;
}

.nav { display: flex; align-items: center; gap: .6rem; }

.nav__list {
  display: flex; align-items: center; gap: .3rem;
  list-style: none; margin: 0; padding: 0;
}

.nav__item { position: relative; }

.nav__link {
  position: relative;
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .55rem .85rem;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--c-nav-link);
  text-decoration: none;
  border-radius: var(--radius-sm);
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}

.nav__link::after {
  content: ""; position: absolute; left: .85rem; right: .85rem; bottom: .3rem;
  height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--speed) var(--ease);
}
.nav__link:hover::after, .nav__link[aria-current="page"]::after { transform: scaleX(1); }

.nav__caret { width: 13px; height: 13px; flex: none; opacity: .6; }

/* ---------- dropdowns ----------

   A dropdown panel is the page background, so its links cannot keep the bar's
   link colour. On Tatton & Vale that colour is a pale gold picked to sit on
   dark green, and on cream it all but disappears. --c-nav-link-on-bg is the
   bar's own background colour, contrast-guarded against the page: dark green
   on cream, and recognisably the same brand either way. */
.nav__submenu {
  position: absolute; top: 100%; left: 0; min-width: 230px;
  background: var(--c-bg);
  border: 1px solid var(--c-rule);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
  padding: .4rem;
  margin: 0; list-style: none;
  opacity: 0; visibility: hidden; transform: translateY(6px);
  transition: opacity var(--speed) var(--ease), transform var(--speed) var(--ease), visibility var(--speed);
}

.nav__item:hover > .nav__submenu,
.nav__item:focus-within > .nav__submenu { opacity: 1; visibility: visible; transform: translateY(0); }

.nav__submenu .nav__link {
  display: flex; align-items: center; justify-content: space-between; gap: .8rem;
  padding: .55rem .7rem;
  font-size: 0.92rem;
  color: var(--c-nav-link-on-bg);
}
.nav__submenu .nav__link::after { display: none; }
.nav__submenu .nav__link:hover,
.nav__submenu .nav__link[aria-current="page"] { background: var(--c-surface); }

/* The third tier and deeper fly out sideways instead of stacking downwards,
   so a deep tree stays on the screen. */
.nav__submenu .nav__submenu {
  top: -.4rem; left: 100%; margin-left: .25rem;
  transform: translateX(-6px) translateY(0);
}
.nav__submenu .nav__item:hover > .nav__submenu,
.nav__submenu .nav__item:focus-within > .nav__submenu { transform: translateX(0); }

/* Near the right-hand end of the bar a flyout would leave the viewport, so
   those open to the left instead. */
.nav__list > .nav__item:nth-last-child(-n+2) .nav__submenu .nav__submenu {
  left: auto; right: 100%; margin: 0 .25rem 0 0;
}
.nav__submenu .nav__caret { transform: rotate(-90deg); }

/* Hover opens a tier on a pointer; a touch screen has nothing to hover with,
   so parents also carry a disclosure button. It is only needed once the menu
   becomes a panel. */
.nav__expand { display: none; }

.nav-toggle { display: none; background: none; border: 0; padding: .5rem; cursor: pointer; color: inherit; }
.nav-toggle svg { width: 26px; height: 26px; }

@media (max-width: 900px) {
  .nav-toggle { display: inline-flex; }

  .nav {
    position: fixed; inset: 78px 0 auto; z-index: 99;
    flex-direction: column; align-items: stretch;
    background: var(--c-bg);
    /* The panel is the page, so everything in it - links, the disclosure
       carets, the ghost button - takes the navbar colour rather than the
       colour meant for use on the navbar. */
    color: var(--c-nav-link-on-bg);
    border-bottom: 1px solid var(--c-rule);
    padding: .5rem var(--gutter) 1.5rem;
    gap: .4rem;
    transform: translateY(-120%);
    transition: transform var(--speed) var(--ease);
    max-height: calc(100dvh - 78px); overflow-y: auto;
  }
  .nav.is-open { transform: translateY(0); }

  .nav__list { flex-direction: column; align-items: stretch; gap: 0; }

  .nav .nav__link { color: var(--c-nav-link-on-bg); }
  .nav__link { padding: .9rem 0; font-size: 1.02rem; }
  .nav__link::after { display: none; }

  .nav__list > .nav__item { border-bottom: 1px solid var(--c-rule); }
  .nav__list > .nav__item:last-child { border-bottom: 0; }

  /* The label stays a link to the parent's own page; the caret beside it is
     what opens the tier. */
  .nav__item--has-children > .nav__link { padding-right: 3.2rem; }
  .nav__caret { display: none; }

  .nav__expand {
    display: inline-flex; align-items: center; justify-content: center;
    position: absolute; top: 0; right: 0; width: 3.2rem; height: 3.2rem;
    background: none; border: 0; padding: 0; cursor: pointer;
    color: inherit;
  }
  .nav__expand svg { width: 17px; height: 17px; transition: transform var(--speed) var(--ease); }
  .nav__item.is-expanded > .nav__expand svg { transform: rotate(180deg); }

  .nav__submenu {
    position: static; opacity: 1; visibility: visible; transform: none;
    display: none;
    background: none; border: 0; box-shadow: none;
    padding: 0 0 .6rem 1rem; min-width: 0;
  }
  .nav__item.is-expanded > .nav__submenu { display: block; }
  .nav__submenu .nav__submenu { margin: 0; padding-bottom: .2rem; }
  .nav__submenu .nav__item { border-bottom: 0; }
  .nav__submenu .nav__link { font-size: 0.96rem; padding: .62rem 0; }
  .nav__submenu .nav__link:hover { background: none; }

  .nav__cta { margin-top: .9rem; text-align: center; }
}

/* ---------- hero ---------- */
.hero { position: relative; isolation: isolate; }

.hero__media { position: relative; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; }

.hero--immersive .hero__media { min-height: min(84dvh, 780px); }
.hero--immersive .hero__media img { position: absolute; inset: 0; }

.hero__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, #000 12%, transparent) 0%,
    transparent 38%,
    color-mix(in srgb, #000 58%, transparent) 100%
  );
}

.hero__body {
  position: absolute; inset-inline: 0; bottom: 0;
  padding-block: clamp(2rem, 6vw, 4.5rem);
  color: var(--c-hero-text);
}

.hero__title {
  font-size: clamp(var(--fs-2xl), 6vw, var(--fs-4xl));
  margin-bottom: .45em;
  max-width: 18ch;
  color: inherit;
}

.hero__lede { max-width: 54ch; font-size: var(--fs-lg); opacity: .94; }

.hero__actions { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.9rem; }

.hero--panel { background: var(--c-surface); }
.hero--panel .hero__body { position: static; color: var(--c-ink); padding-block: var(--rhythm); }

/* ---------- property cards ---------- */
.card {
  display: flex; flex-direction: column;
  text-decoration: none;
  color: inherit;
  height: 100%;
}

.card__media { position: relative; margin-bottom: 1.05rem; }

.card__badge {
  position: absolute; top: .85rem; left: .85rem; z-index: 2;
  background: color-mix(in srgb, var(--c-bg) 92%, transparent);
  color: var(--c-ink);
  padding: .32rem .75rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase;
}

.card__title { font-size: var(--fs-lg); margin-bottom: .3rem; }
.card__meta { font-size: var(--fs-sm); color: var(--c-ink-muted); margin-bottom: .7rem; }

.card__price {
  font-family: var(--f-display);
  font-size: var(--fs-lg);
  font-weight: var(--fw-display);
  margin-top: auto;
  padding-top: .6rem;
}

.card__facts { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: .75rem; }

/* ---------- stats ---------- */
/* Four stats, always in a grid that divides evenly.
   auto-fit produced three columns at some widths, which left the fourth stat
   alone on its own row with two empty cells showing the rule colour through
   the 1px gap. Explicit counts avoid the orphan; currency values still need
   room, so it drops to two across before it gets tight. */
.stats {
  display: grid;
  /* Callers set --stat-cols when they render fewer than four tiles. */
  grid-template-columns: repeat(var(--stat-cols, 4), minmax(0, 1fr));
  gap: 1px;
  background: var(--c-rule);
  border: 1px solid var(--c-rule);
  border-radius: var(--radius);
  overflow: hidden;
}

@media (max-width: 1000px) { .stats { grid-template-columns: repeat(min(var(--stat-cols, 4), 2), minmax(0, 1fr)); } }
@media (max-width: 460px)  { .stats { grid-template-columns: minmax(0, 1fr); } }
.stat { background: var(--c-bg); padding: 1.5rem 1.35rem; min-width: 0; }
/* Sized absolutely, for the same reason the data tables are: a stat label is
   read, not skimmed past, and it should not shrink because a site chose a
   wide display scale. */
.stat__label { font-size: 0.82rem; text-transform: uppercase; letter-spacing: var(--tracking-kicker); color: var(--c-ink-muted); margin-bottom: .55rem; }
.stat__value {
  font-family: var(--f-display);
  /* Scale with the cell rather than the type ramp, so a seven-figure
     median cannot overflow its column on a narrow viewport. */
  font-size: clamp(1.35rem, 2.3vw + .55rem, 2.1rem);
  font-weight: var(--fw-display);
  line-height: 1.05;
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}
.stat__note { font-size: 0.85rem; line-height: 1.35; color: var(--c-ink-faint); margin-top: .4rem; }

/* ---------- editorial sections ---------- */
.prose > * + * { margin-top: 1.1em; }
.prose h2, .prose h3 { margin-top: 1.6em; }

/* -------------------------------------------------------------------------
   Editorial copy authored as HTML.

   The area guides are stored as simple markup — p, h3, strong, b, br — so
   rather than re-author 66 pages this styles what is already there: a lede
   opening paragraph, sub-headings that sit as section breaks, and a measure
   that stays readable.
   ------------------------------------------------------------------------- */
.editorial { max-width: 68ch; }

.editorial > p { margin: 0 0 1.15em; }

/* The opening paragraph carries the summary, so give it presence. */
.editorial > p:first-of-type {
  font-size: var(--fs-lg);
  line-height: 1.5;
  color: var(--c-ink);
}

.editorial h2,
.editorial h3 {
  font-size: var(--fs-xl);
  margin: 2.2em 0 .6em;
  padding-top: 1.4em;
  border-top: var(--rule-weight) solid var(--c-rule);
}

.editorial h3:first-child,
.editorial h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

.editorial h4 { font-size: var(--fs-lg); margin: 1.8em 0 .5em; }

.editorial strong, .editorial b { font-weight: 600; color: var(--c-ink); }

.editorial ul, .editorial ol { margin: 0 0 1.15em; padding-left: 1.3rem; }
.editorial li { margin-bottom: .45em; }

.editorial a { color: var(--c-kicker); text-decoration: underline; }
.editorial a:hover { color: var(--c-ink); }

.editorial img { border-radius: var(--radius); margin: 1.6em 0; }

.editorial blockquote {
  margin: 1.8em 0;
  padding-left: 1.4rem;
  border-left: 3px solid var(--c-accent);
  font-size: var(--fs-lg);
}

/* Facts pulled out of the guide, shown beside the copy on wide screens. */
.facts { list-style: none; margin: 0; padding: 0; }
.facts li {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .8rem;
  align-items: baseline;
  padding: .78rem 0;
  border-bottom: 1px solid var(--c-rule);
  font-size: 0.95rem;
}
.facts li:first-child { border-top: 1px solid var(--c-rule); }
.facts dt, .facts .facts__label { color: var(--c-ink-muted); }
.facts .facts__value { font-weight: 600; text-align: right; }

.aside-sticky { position: sticky; top: calc(78px + 1.5rem); }
@media (max-width: 960px) { .aside-sticky { position: static; } }

/* -------------------------------------------------------------------------
   "At a glance" fact panel.

   It stands beside a column of editorial copy, so it has to read as its own
   object rather than as a list that happens to sit to the right of the text.
   A tinted surface, a ruled heading and figures set in the display face give
   it the weight of a fact box in a magazine, which is what it is.
   ------------------------------------------------------------------------- */
/* Copy on the left, fact panel on the right, stacking on a narrow screen.
   This lives here rather than in an inline style because an inline
   grid-template-columns outranks the media query and leaves the sidebar
   holding a 330px track on a phone. */
.editorial-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 330px);
  gap: calc(var(--gap) * 1.6);
  align-items: start;
}

@media (max-width: 960px) {
  .editorial-layout { grid-template-columns: minmax(0, 1fr); gap: var(--gap); }
}

.fact-panel {
  background: var(--c-surface);
  border: 1px solid var(--c-rule);
  border-radius: var(--radius);
  padding: 1.4rem 1.35rem 1.5rem;
  box-shadow: var(--shadow);
}

.fact-panel__head {
  display: block;
  margin: 0;
  padding-bottom: .9rem;
  /* Sized here rather than inherited: .kicker is tuned for a standfirst
     above a headline, and lands at 12.5px, which is too quiet for the only
     heading this panel has. */
  font-size: 0.8rem;
  border-bottom: var(--rule-weight) solid var(--c-rule-strong);
  color: var(--c-kicker);
}

/* The panel supplies the outer rules, so the list drops its own. */
.fact-panel .facts li:first-child { border-top: 0; padding-top: 1.05rem; }
.fact-panel .facts li:last-child { border-bottom: 0; padding-bottom: .1rem; }

/* Label above, fact below.
   These are not short label/value pairs. Every fact in the guides is a
   sentence of 80 to 100 characters - "First recorded in the Domesday Book of
   1086 as Cunetesford" - so setting them beside their label squeezes the
   label to a sliver and the two collide. Stacked, both get the full measure,
   and a short value reads the same way as a long one. */
.fact-panel .facts li {
  display: block;
  padding: .95rem 0;
}

.fact-panel .facts__label {
  display: block;
  margin-bottom: .32rem;
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  line-height: 1.3;
  color: var(--c-kicker);
}

.fact-panel .facts__value {
  display: block;
  font-size: 0.96rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--c-ink);
  text-align: left;
  font-variant-numeric: tabular-nums;
  overflow-wrap: break-word;
}

.fact-panel__note {
  margin: 1.1rem 0 0;
  padding-top: .95rem;
  border-top: 1px solid var(--c-rule);
  font-size: 0.83rem;
  line-height: 1.5;
  color: var(--c-ink-faint);
}

/* Below the editorial column it is no longer a sidebar, so it spans the
   measure and the rows can breathe. */
@media (max-width: 960px) {
  .fact-panel { padding: 1.5rem 1.4rem; }
  .fact-panel .facts li { padding: .85rem 0; }
}

/* -------------------------------------------------------------------------
   Data tables that have to work on a phone.
   ------------------------------------------------------------------------- */
/* Data tables are read, not skimmed, so they are sized in absolute terms
   rather than off the display type scale. At a 1.414 scale --fs-sm lands
   near 12px and --fs-xs near 8.5px, which is too small for a table of
   addresses and prices whatever the heading sizes are doing. */
.data-table { width: 100%; border-collapse: collapse; font-size: clamp(0.95rem, 0.9rem + 0.15vw, 1.02rem); }
.data-table th {
  text-align: left;
  font-weight: 600;
  color: var(--c-ink-muted);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: .07em;
  padding: .75rem .7rem .75rem 0;
  border-bottom: var(--rule-weight) solid var(--c-rule);
  white-space: nowrap;
}
.data-table td { padding: .9rem .7rem .9rem 0; border-bottom: 1px solid var(--c-rule); vertical-align: top; line-height: 1.45; }
.data-table .num { text-align: right; padding-right: 0; font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }
.data-table th.num { text-align: right; }
.data-table .sub { display: none; color: var(--c-ink-muted); font-size: 0.85rem; margin-top: .2rem; }
.data-table tbody tr:hover { background: color-mix(in srgb, var(--c-surface) 60%, transparent); }

/* Postcode links.
   Linked everywhere a postcode appears, to give the postcode tier pages as
   many internal routes as possible — but never underlined, because they sit
   inside dense tables and beside addresses where underlines become noise. */
.pc-link {
  text-decoration: none;
  color: inherit;
  border-bottom: 1px dotted color-mix(in srgb, currentColor 38%, transparent);
  transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
  white-space: nowrap;
}
.pc-link:hover,
.pc-link:focus-visible {
  color: var(--c-kicker);
  border-bottom-color: currentColor;
}

/* Below ~700px the secondary columns fold under the address so the price
   keeps its own column and nothing wraps mid-figure. */
@media (max-width: 700px) {
  .data-table .col-fold { display: none; }
  .data-table .sub { display: block; }
  .data-table td { padding-right: .4rem; }
}

.feature__body { max-width: 60ch; }

.timeline { list-style: none; margin: 0; padding: 0 0 0 1.6rem; border-left: var(--rule-weight) solid var(--c-rule); }
.timeline__item { position: relative; padding-bottom: 1.9rem; }
.timeline__item::before {
  content: ""; position: absolute; left: calc(-1.6rem - 4px); top: .5rem;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--c-accent); box-shadow: 0 0 0 4px var(--c-bg);
}
.timeline__year { font-size: var(--fs-xs); letter-spacing: var(--tracking-kicker); text-transform: uppercase; color: var(--c-kicker); }

.faq { border-top: 1px solid var(--c-rule); }
.faq__item { border-bottom: 1px solid var(--c-rule); }
.faq__q {
  width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  background: none; border: 0; cursor: pointer; text-align: left;
  padding: 1.15rem 0;
  font-family: var(--f-display); font-size: var(--fs-lg); font-weight: var(--fw-display);
}
.faq__q::after { content: "+"; font-size: 1.4em; line-height: 1; transition: transform var(--speed) var(--ease); }
.faq__item[open] .faq__q::after { transform: rotate(45deg); }
.faq__a { padding-bottom: 1.3rem; color: var(--c-ink-muted); max-width: 70ch; }

.spec-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr)); gap: 0; }
.spec-list li { padding: .85rem 0; border-bottom: 1px solid var(--c-rule); font-size: var(--fs-sm); display: flex; gap: .7rem; align-items: baseline; }
.spec-list li::before { content: ""; flex: none; width: 5px; height: 5px; border-radius: 50%; background: var(--c-accent); transform: translateY(-2px); }

.gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: .8rem; }
.gallery__item { cursor: zoom-in; border: 0; padding: 0; background: none; }

/* A gallery of 3+ gets a feature tile. */
.gallery--mosaic { grid-auto-flow: dense; }
.gallery--mosaic .gallery__item:first-child { grid-column: span 2; grid-row: span 2; }
@media (max-width: 720px) { .gallery--mosaic .gallery__item:first-child { grid-column: span 1; grid-row: span 1; } }

.compare { position: relative; overflow: hidden; border-radius: var(--radius); box-shadow: var(--shadow); touch-action: none; }
.compare img { width: 100%; display: block; }
.compare__after { position: absolute; inset: 0; width: 50%; overflow: hidden; }
.compare__after img { width: 100vw; max-width: none; height: 100%; object-fit: cover; }
.compare__handle {
  position: absolute; top: 0; bottom: 0; left: 50%; width: 2px;
  background: #fff; cursor: ew-resize; transform: translateX(-50%);
}
.compare__handle::after {
  content: "⟷"; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 42px; height: 42px; border-radius: 50%; background: #fff; color: #111;
  display: grid; place-items: center; font-size: 1rem;
  box-shadow: 0 4px 18px rgb(0 0 0 / .3);
}

/* ---------- footer ---------- */
/* Sized independently of --rhythm. Inheriting the section rhythm made the
   footer 6.5rem of padding on the Luxe preset before any content, which read
   as a large empty band rather than a footer. */
.site-footer {
  background: var(--c-footer-bg);
  color: var(--c-footer-text);
  padding-block: clamp(2rem, 4vw, 3rem) clamp(1.25rem, 2vw, 1.75rem);
  margin-top: clamp(2.5rem, var(--rhythm), 5rem);
}
.site-footer a { color: inherit; text-decoration: none; opacity: .85; }
.site-footer a:hover { opacity: 1; text-decoration: underline; }
.site-footer h4 { font-size: 0.78rem; text-transform: uppercase; letter-spacing: var(--tracking-kicker); color: inherit; opacity: .7; margin-bottom: .7rem; }
/* Explicit tracks, not repeat(auto-fit).
   `repeat(auto-fit, …)` is invalid in the same declaration as a flexible
   track, so "1.6fr repeat(auto-fit, minmax(150px,1fr))" was thrown away
   entirely and the footer fell back to a single column — four blocks stacked
   vertically, which is what made it so tall. */
.site-footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.8fr) minmax(0, 1fr);
  gap: 1.6rem 2rem;
}
.site-footer__bottom {
  margin-top: 1.6rem; padding-top: 1rem;
  border-top: 1px solid var(--c-footer-rule);
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: .6rem;
  font-size: 0.76rem; opacity: .7;
}
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .4rem; font-size: 0.88rem; }
.site-footer p { font-size: 0.88rem; line-height: 1.55; }

.site-footer__brand { display: inline-flex; align-items: center; gap: .65rem; text-decoration: none; margin-bottom: .6rem; }
.site-footer__brand img { max-height: 38px; max-width: min(220px, 60vw); width: auto; }

/* Portal and ombudsman badges. Logos vary wildly in aspect ratio, so they are
   height-constrained and desaturated to sit together as a row rather than
   competing with each other. */
.site-footer__memberships {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1.2rem;
  margin-top: 1.5rem; padding-top: 1.2rem;
  border-top: 1px solid var(--c-footer-rule);
}
.site-footer__memberships img {
  max-height: 26px; width: auto;
  filter: grayscale(1) brightness(1.6);
  opacity: .72;
  transition: opacity var(--speed) var(--ease), filter var(--speed) var(--ease);
}
.site-footer__memberships a:hover img { filter: none; opacity: 1; }
.site-footer__memberships span { font-size: .78rem; opacity: .75; }

@media (max-width: 680px) { .site-footer__grid { grid-template-columns: 1fr; gap: 1.4rem; } }

/* -------------------------------------------------------------------------
   A-Z index for a long list of area guides.
   ------------------------------------------------------------------------- */
.az-index {
  display: flex; flex-wrap: wrap; gap: .3rem;
  margin: 0 0 var(--rhythm-sm);
  padding-bottom: 1.1rem;
  border-bottom: var(--rule-weight) solid var(--c-rule);
}
.az-index a,
.az-index span {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 2.15rem; padding: .36rem .45rem;
  font-size: 0.86rem; font-weight: 600;
  border-radius: var(--radius-sm);
  text-decoration: none;
}
.az-index a {
  color: var(--c-nav-link-on-bg);
  border: 1px solid var(--c-rule);
  transition: background var(--speed) var(--ease);
}
.az-index a:hover { background: var(--c-surface); }
/* Letters with no guide stay in place so the alphabet does not reflow as
   guides are added, but they are plainly not targets. */
.az-index span { color: var(--c-ink-faint); opacity: .4; }

/* Clear the sticky header when jumped to. */
.az-group { scroll-margin-top: 96px; }
.az-group + .az-group { margin-top: var(--rhythm-sm); }
.az-group__letter {
  font-size: 0.82rem; text-transform: uppercase;
  letter-spacing: var(--tracking-kicker);
  color: var(--c-kicker);
  margin: 0 0 1rem;
  padding-bottom: .5rem;
  border-bottom: 1px solid var(--c-rule);
}

/* -------------------------------------------------------------------------
   Valuation enquiry form.

   Every colour is a token, so the same markup renders correctly whichever
   tone the section is set to - the dark "ink" tone it defaults to, or a pale
   one if an agency would rather it sat quietly.
   ------------------------------------------------------------------------- */
.vform { display: grid; gap: var(--rhythm-sm); }
.vform__intro { max-width: 62ch; }

.vform__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.15rem 1.5rem;
}
.field--full { grid-column: 1 / -1; }

@media (max-width: 720px) {
  .vform__grid { grid-template-columns: minmax(0, 1fr); }
}

.field { display: grid; gap: .42rem; min-width: 0; }

.field__label {
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--c-ink-muted);
}
.field__req { color: var(--c-kicker); margin-left: .1rem; }

.field__input {
  width: 100%;
  font: inherit;
  font-size: 1rem;
  line-height: 1.4;
  color: var(--c-ink);
  background: var(--c-field-bg, var(--c-surface));
  /* The same edge as the tick pills. A softer rule read as an unfinished
     field next to them, which made the typed half of the form look like an
     afterthought beside the half you click. */
  border: var(--rule-weight) solid var(--c-rule-strong);
  border-radius: var(--radius-sm);
  padding: .78rem .85rem;
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
.field__input::placeholder { color: var(--c-ink-faint); }

.field__input:focus {
  outline: none;
  border-color: var(--c-focus);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-focus) 34%, transparent);
}

/* Autofill paints its own background and text, which on the dark tone comes
   out white on white. Both have to be forced back to the tokens. */
.field__input:-webkit-autofill,
.field__input:-webkit-autofill:hover,
.field__input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--c-ink);
  -webkit-box-shadow: 0 0 0 100px var(--c-field-bg, var(--c-surface)) inset;
  caret-color: var(--c-ink);
}

.field__input.is-invalid { border-color: var(--c-error); }
.field__error { margin: 0; font-size: 0.84rem; line-height: 1.4; color: var(--c-error); }

.field__textarea { resize: vertical; min-height: 6.5rem; }
.field__select { cursor: pointer; }
/* The option list is drawn by the operating system, not the page, so it needs
   colours of its own or a dark tone leaves it unreadable in some browsers. */
.field__select option { color: var(--c-on-bg); background: var(--c-bg); }

.field__check {
  display: flex; align-items: flex-start; gap: .7rem;
  font-size: 0.94rem; line-height: 1.5;
  color: var(--c-ink-muted);
  cursor: pointer;
}
.field__check input {
  flex: none;
  width: 1.15rem; height: 1.15rem;
  margin-top: .12rem;
  accent-color: var(--c-accent);
  cursor: pointer;
}

/* Turnstile renders its own widget here. It sizes itself, so this only gives
   it room and keeps it on the same rhythm as the fields above. */
/* Chips laid over a photograph, and the price chip that leads them. They
   were inline rgb() values, so they followed no agency's palette. */
.chip--on-media {
  background: color-mix(in srgb, var(--c-on-primary) 16%, transparent);
  color: inherit;
  border-color: color-mix(in srgb, var(--c-on-primary) 30%, transparent);
  backdrop-filter: blur(6px);
}
.chip--price {
  background: var(--c-accent);
  color: var(--c-on-accent);
  border-color: transparent;
  font-weight: 600;
}

/* What a viewing enquiry is about, stated above the form so nobody has to
   remember which listing they clicked. */
.vform__subject {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem .8rem;
  margin: 0 0 1.2rem;
  padding: .9rem 1.1rem;
  border: 1px solid var(--c-rule);
  border-radius: var(--radius-sm);
  background: var(--c-surface);
  font-size: 0.95rem;
}
.vform__subject span:first-child { color: var(--c-ink-muted); font-size: 0.85rem; }
.vform__subject strong { font-family: var(--f-display); font-size: 1.05rem; }
.vform__price { margin-left: auto; color: var(--c-kicker); font-weight: 600; }

.vform__check { margin-top: 1.5rem; min-height: 65px; }

.vform__actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem;
  margin-top: 1.6rem;
}
.vform__footnote { margin: 0; font-size: 0.84rem; color: var(--c-ink-faint); max-width: 46ch; }

.vform__alert {
  margin: 0 0 1.2rem;
  padding: .85rem 1rem;
  border: 1px solid var(--c-error);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--c-error) 12%, transparent);
  color: var(--c-ink);
  font-size: 0.92rem;
}

/* Off-screen rather than display:none, which some bots know to skip. */
.vform__trap {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.vform__done {
  display: grid; justify-items: start; gap: .6rem;
  max-width: 52ch;
  padding: 1.8rem;
  border: 1px solid var(--c-rule);
  border-radius: var(--radius);
  background: var(--c-surface);
}
.vform__done svg { width: 34px; height: 34px; color: var(--c-kicker); }
.vform__done h3 { margin: 0; }
.vform__done p { margin: 0; color: var(--c-ink-muted); }

/* =========================================================================
   Editorial devices
   Five blocks whose job is pace and hierarchy rather than more content: the
   difference between a page that reads as an article and one that reads as a
   stack of boxes.
   ========================================================================= */

/* ---------- opening spread ---------- */
.opening__title { margin: .3rem 0 0; }

.opening__standfirst {
  font-family: var(--f-display);
  font-size: clamp(1.15rem, 1rem + 0.9vw, 1.6rem);
  font-weight: 400;
  line-height: 1.45;
  color: var(--c-ink-muted);
  margin: .9rem 0 0;
  max-width: 46ch;
}

.opening__ornament { margin: 1.8rem 0; }
.opening__ornament--rule {
  height: var(--rule-weight);
  background: linear-gradient(to right, var(--c-rule-strong), transparent);
  max-width: 14rem;
}
.opening__ornament--diamond { display: flex; gap: .5rem; align-items: center; }
.opening__ornament--diamond span {
  width: 6px; height: 6px; transform: rotate(45deg);
  background: var(--c-kicker); opacity: .8;
}
.opening__ornament--diamond span:nth-child(2) { opacity: .5; }
.opening__ornament--diamond span:nth-child(3) { opacity: .3; }

/* The drop cap. ::first-letter only takes a handful of properties, which is
   why this is float rather than grid. */
.opening__body > p:first-of-type::first-letter {
  float: left;
  font-family: var(--f-display);
  font-weight: var(--fw-display);
  font-size: 4.1em;
  line-height: .78;
  padding: .04em .1em 0 0;
  color: var(--c-kicker);
}

.opening__byline {
  margin: 1.6rem 0 0;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: var(--tracking-kicker);
  color: var(--c-ink-faint);
}

/* ---------- margin notes ---------- */
.marginalia__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 15rem);
  gap: calc(var(--gap) * 1.4);
  align-items: start;
}

.marginalia__notes { display: grid; gap: 1.6rem; }

.marginalia__note {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .6rem;
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--c-ink-muted);
  border-top: var(--rule-weight) solid var(--c-rule-strong);
  padding-top: .7rem;
}
.marginalia__note p { margin: 0; }

.marginalia__marker {
  font-family: var(--f-display);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--c-kicker);
  line-height: 1.4;
}

/* No margin to sit in on a phone, so the notes fold in underneath rather
   than being dropped. */
@media (max-width: 900px) {
  .marginalia__grid { grid-template-columns: minmax(0, 1fr); gap: var(--gap); }
  .marginalia__notes {
    border-left: 2px solid var(--c-rule-strong);
    padding-left: 1.1rem;
    gap: 1.1rem;
  }
  .marginalia__note { border-top: 0; padding-top: 0; }
}

/* ---------- chapter rule ---------- */
.chapter { display: grid; gap: 1.4rem; justify-items: center; text-align: center; }
.chapter__rule { width: 100%; max-width: 20rem; height: var(--rule-weight); background: var(--c-rule-strong); }
.chapter__label { display: flex; align-items: baseline; gap: 1rem; justify-content: center; flex-wrap: wrap; }

.chapter__number {
  font-family: var(--f-display);
  font-size: clamp(2.2rem, 1.6rem + 2.4vw, 3.4rem);
  font-weight: var(--fw-display);
  line-height: 1;
  color: var(--c-kicker);
  opacity: .9;
}
.chapter__title { margin: 0; }
.chapter__sub { margin: .3rem 0 0; color: var(--c-ink-muted); font-size: var(--fs-sm); }

/* ---------- duotone photograph ---------- */
.duotone { position: relative; isolation: isolate; }
.duotone__media { position: relative; overflow: hidden; }
.duotone__media img {
  display: block; width: 100%; height: clamp(18rem, 46vh, 34rem); object-fit: cover;
  filter: grayscale(1) contrast(1.08) brightness(1.02);
}

/* Grayscale underneath, a two-colour gradient blended over the top: a proper
   duotone rather than a flat wash, and both colours come from the agency's
   own palette. */
.duotone__tint {
  position: absolute; inset: 0;
  background: linear-gradient(140deg, var(--c-primary), var(--c-accent));
  mix-blend-mode: color;
  opacity: .92;
}

/* A single pass of the scrim is not enough where the agency's accent is pale:
   the duotone leaves the foot of the picture bright and the caption sits on
   it. Two passes of the same token, so it stays the agency's own scrim colour
   rather than a hardcoded black, plus a shadow for the brightest photographs. */
.duotone__caption {
  position: absolute; inset: auto 0 0 0;
  padding: calc(var(--rhythm-sm) * 1.3) 0 1.6rem;
  background:
    linear-gradient(to top, var(--c-hero-scrim) 0%, var(--c-hero-scrim) 62%, transparent 100%),
    linear-gradient(to top, var(--c-hero-scrim) 0%, var(--c-hero-scrim) 45%, transparent 100%),
    linear-gradient(to top, var(--c-hero-scrim) 0%, transparent 100%);
  color: var(--c-hero-text);
  text-shadow: 0 1px 12px rgb(0 0 0 / 0.35);
}
/* The kicker colour is chosen to sit on the page background, not on a
   photograph, so over the picture it takes the hero text colour. */
.duotone__caption .kicker { color: var(--c-hero-text); opacity: .85; }
.duotone__caption h2 { margin: .2rem 0 .4rem; }
.duotone__caption p { margin: 0; max-width: 54ch; opacity: .92; }

/* ---------- market pulse ---------- */
.pulse__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  gap: 1px;
  background: var(--c-rule);
  border: 1px solid var(--c-rule);
  border-radius: var(--radius);
  overflow: hidden;
}

.pulse__figure { background: var(--c-bg); padding: 1.4rem 1.3rem; display: grid; gap: .3rem; }

.pulse__label {
  font-size: 0.76rem; text-transform: uppercase;
  letter-spacing: var(--tracking-kicker); color: var(--c-ink-muted);
}

.pulse__value {
  font-family: var(--f-display);
  font-weight: var(--fw-display);
  font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.1rem);
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.pulse__value.is-up   { color: var(--c-data); }
.pulse__value.is-down { color: var(--c-error); }

.pulse__note { font-size: 0.84rem; color: var(--c-ink-faint); }

.pulse__mix { margin-top: var(--rhythm-sm); display: grid; gap: .7rem; }
.pulse__bar {
  display: flex; height: 12px; border-radius: var(--radius-pill);
  overflow: hidden; background: var(--c-surface);
}
.pulse__seg { display: block; height: 100%; }
.pulse__seg--0 { background: var(--c-primary); }
.pulse__seg--1 { background: var(--c-accent); }
.pulse__seg--2 { background: color-mix(in srgb, var(--c-primary) 52%, var(--c-bg)); }
.pulse__seg--3 { background: color-mix(in srgb, var(--c-accent) 48%, var(--c-bg)); }

.pulse__key {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .4rem 1.3rem;
  font-size: 0.85rem; color: var(--c-ink-muted);
}
.pulse__key li { display: flex; align-items: center; gap: .45rem; }
.pulse__swatch { width: .7rem; height: .7rem; border-radius: 2px; display: inline-block; }

.pulse__source { margin: 1.2rem 0 0; font-size: 0.8rem; color: var(--c-ink-faint); }

/* =========================================================================
   Immersive editorial
   Three blocks that behave rather than sit still. Each one degrades to
   something perfectly readable without JavaScript: the effect is the
   enhancement, never the content.
   ========================================================================= */

/* ---------- price story ---------- */
.pricestory__frame {
  position: relative;
  margin-top: var(--rhythm-sm);
  padding: 1.2rem 1rem 0;
  border: 1px solid var(--c-rule);
  border-radius: var(--radius);
  background: var(--c-surface);
}

.pricestory__svg { display: block; width: 100%; height: clamp(13rem, 30vh, 20rem); overflow: visible; }

/* Drawn on entry. The dash length is measured in JS because only the browser
   knows how long the path is. */
.pricestory__line { stroke-dasharray: var(--len, 0); stroke-dashoffset: var(--len, 0); }
.pricestory__area { opacity: 0; transition: opacity 1.1s var(--ease) .35s; }

.pricestory.is-drawn .pricestory__line {
  transition: stroke-dashoffset 1.7s cubic-bezier(.22,.61,.36,1);
  stroke-dashoffset: 0;
}
.pricestory.is-drawn .pricestory__area { opacity: 1; }

.pricestory__dot { fill: var(--c-bg); stroke: var(--c-data); stroke-width: 2.5; }

.pricestory__labels { position: absolute; inset: 1.2rem 1rem 0; pointer-events: none; }

.pricestory__marker {
  position: absolute;
  transform: translate(-50%, -100%);
  padding-bottom: 1.1rem;
  text-align: center;
  width: max-content;
  max-width: 11rem;
  opacity: 0;
  transition: opacity .5s var(--ease);
}
.pricestory.is-drawn .pricestory__marker { opacity: 1; transition-delay: 1.2s; }

.pricestory__year {
  display: block;
  font-family: var(--f-display);
  font-weight: var(--fw-display);
  font-size: 0.92rem;
  color: var(--c-data);
}
.pricestory__note { display: block; font-size: 0.76rem; line-height: 1.35; color: var(--c-ink-muted); }

.pricestory__ends {
  display: flex; flex-wrap: wrap; gap: 1rem 2.4rem;
  margin-top: 1.2rem; align-items: baseline;
}
.pricestory__ends strong {
  display: block;
  font-family: var(--f-display);
  font-weight: var(--fw-display);
  font-size: clamp(1.3rem, 1rem + 1.1vw, 1.8rem);
  font-variant-numeric: tabular-nums;
}
.pricestory__growth strong { color: var(--c-data); }
.pricestory__end { margin-left: auto; text-align: right; }
.pricestory__label {
  font-size: 0.74rem; text-transform: uppercase;
  letter-spacing: var(--tracking-kicker); color: var(--c-ink-muted);
}

@media (max-width: 640px) {
  .pricestory__marker { display: none; }
  .pricestory__end { margin-left: 0; text-align: left; }
}

/* ---------- pinned narrative ---------- */
.pinned__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: calc(var(--gap) * 1.6);
  align-items: start;
}

.pinned__beat { padding-block: clamp(3rem, 16vh, 9rem); max-width: 46ch; }
.pinned__beat:first-child { padding-top: 0; }
.pinned__beat-title { margin: .3rem 0 .6rem; }
.pinned__beat p { margin: 0; color: var(--c-ink-muted); }

.pinned__stage { position: relative; height: 100%; }
.pinned__sticky {
  position: sticky;
  top: calc(78px + 3vh);
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--c-surface);
}

.pinned__frame {
  position: absolute; inset: 0; margin: 0;
  opacity: 0;
  transform: scale(1.04);
  transition: opacity .7s var(--ease), transform 1.2s var(--ease);
}
.pinned__frame.is-active { opacity: 1; transform: scale(1); }
.pinned__frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pinned__frame figcaption {
  position: absolute; inset: auto 0 0 0;
  padding: 2.4rem 1.1rem .9rem;
  background: linear-gradient(to top, var(--c-hero-scrim), transparent);
  color: var(--c-hero-text);
  font-size: 0.82rem;
}

/* The in-flow image is the fallback. JS hides it and lights the stage. */
.pinned__inline { margin-top: 1.2rem; }
.pinned__stage { display: none; }

.pinned.is-live .pinned__inline { display: none; }
.pinned.is-live .pinned__stage { display: block; }

@media (max-width: 900px) {
  .pinned__grid { grid-template-columns: minmax(0, 1fr); }
  .pinned__beat { padding-block: 0 var(--rhythm-sm); }
  /* No room to pin anything next to the copy, so the fallback stands. */
  .pinned.is-live .pinned__inline { display: block; }
  .pinned.is-live .pinned__stage { display: none; }
}

/* ---------- image hotspots ---------- */
.hotspots__frame { position: relative; border-radius: var(--radius); overflow: hidden; }
.hotspots__frame img { display: block; width: 100%; height: auto; }

.hotspots__pin { position: absolute; transform: translate(-50%, -50%); }

.hotspots__dot {
  width: 2.1rem; height: 2.1rem;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 2px solid var(--c-on-accent);
  background: var(--c-accent);
  color: var(--c-on-accent);
  font: 600 0.85rem/1 var(--f-body);
  cursor: pointer;
  box-shadow: 0 2px 10px rgb(0 0 0 / .3);
  transition: transform var(--speed) var(--ease);
}
.hotspots__dot:hover { transform: scale(1.12); }
.hotspots__dot:focus-visible { outline: 3px solid var(--c-focus); outline-offset: 3px; }

/* A quiet pulse so the pins are noticed at all, stopped for anyone who has
   asked for less movement. */
.hotspots__dot::after {
  content: ""; position: absolute; inset: -6px;
  border-radius: 50%; border: 2px solid var(--c-accent);
  opacity: 0; animation: hotspotPing 2.6s var(--ease) infinite;
}
@keyframes hotspotPing {
  0%   { opacity: .55; transform: scale(.85); }
  70%  { opacity: 0;   transform: scale(1.35); }
  100% { opacity: 0;   transform: scale(1.35); }
}
@media (prefers-reduced-motion: reduce) {
  .hotspots__dot::after { animation: none; }
  .pricestory.is-drawn .pricestory__line { transition: none; }
  .pinned__frame { transition: none; }
}

.hotspots__card {
  position: absolute; left: 50%; bottom: calc(100% + .8rem);
  transform: translate(-50%, 6px);
  width: max-content; max-width: 16rem;
  padding: .8rem .95rem;
  background: var(--c-bg);
  color: var(--c-ink);
  border: 1px solid var(--c-rule);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lift);
  font-size: 0.86rem; line-height: 1.5;
  opacity: 0; visibility: hidden;
  transition: opacity var(--speed) var(--ease), transform var(--speed) var(--ease), visibility var(--speed);
  z-index: 3;
}
.hotspots__card strong { display: block; margin-bottom: .2rem; }
.hotspots__card p { margin: 0; color: var(--c-ink-muted); }

.hotspots__pin.is-open .hotspots__card,
.hotspots__pin:hover .hotspots__card {
  opacity: 1; visibility: visible; transform: translate(-50%, 0);
}

/* Pins near an edge would push their card off the picture. */
.hotspots__pin[style*="left: 8"] .hotspots__card,
.hotspots__pin[style*="left: 9"] .hotspots__card { left: auto; right: 0; transform: translate(0, 6px); }
.hotspots__pin[style*="left: 8"].is-open .hotspots__card,
.hotspots__pin[style*="left: 9"].is-open .hotspots__card { transform: translate(0, 0); }

.hotspots__key {
  margin: var(--rhythm-sm) 0 0; padding-left: 1.3rem;
  display: grid; gap: .6rem;
  font-size: 0.9rem; color: var(--c-ink-muted);
  max-width: 62ch;
}
.hotspots__key strong { color: var(--c-ink); }

/* Shown to screen readers, and to anyone who cannot use the pins. */
@media (hover: none) {
  .hotspots__key { display: grid; }
}

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

   A title card rather than a photograph: grey, quiet, and carrying only the
   mark, the name and one action. The grey is mixed from the agency's own
   primary rather than fixed, so it stays a neutral that belongs to the brand
   instead of a generic slab. */
/* The brand hero is a deep field of the agency's own primary, because that is
   how a mark is usually drawn: the logo Vantage supplied is gold on green, and
   putting it on a pale plate instead would be showing the artwork in a setting
   it was not made for. Type uses the hero text colour, which exists precisely
   for text over a dark hero. */
.hero--brand {
  background:
    radial-gradient(120% 95% at 50% 0%, color-mix(in srgb, #FFFFFF 10%, transparent), transparent 65%),
    linear-gradient(170deg,
      color-mix(in srgb, var(--c-primary) 92%, #FFFFFF),
      var(--c-primary));
  color: var(--c-hero-text);
  border-bottom: 1px solid color-mix(in srgb, var(--c-accent) 26%, transparent);
}

.hero--brand .hero__name,
.hero--brand .hero__strapline,
.hero--brand .hero__claim { color: var(--c-hero-text); }

.hero--brand .hero__strapline { color: color-mix(in srgb, var(--c-accent) 78%, var(--c-hero-text)); }
.hero--brand .hero__claim { color: color-mix(in srgb, var(--c-hero-text) 82%, transparent); }

.hero__plate {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 0;
  padding-block: clamp(3.5rem, 11vh, 7rem);
}

.hero__mark {
  width: clamp(4.5rem, 9vw, 7rem);
  height: auto;
  margin-bottom: 1.6rem;
}

.hero__name {
  margin: 0;
  font-family: var(--f-display);
  font-weight: var(--fw-brand, var(--fw-display));
  font-size: clamp(2.6rem, 1.6rem + 4.4vw, 5rem);
  line-height: 1;
  letter-spacing: -0.015em;
  color: var(--c-primary);
}

.hero__strapline {
  margin: 1rem 0 0;
  font-size: 0.82rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-kicker);
  color: color-mix(in srgb, var(--c-primary) 66%, transparent);
}

.hero__claim {
  margin: 2rem 0 0;
  max-width: 34ch;
  font-family: var(--f-display);
  font-size: clamp(1.1rem, 0.95rem + 0.8vw, 1.5rem);
  line-height: 1.45;
  color: color-mix(in srgb, var(--c-primary) 78%, transparent);
}

.hero--brand .hero__actions { margin-top: 2.4rem; }

/* ---------- register: numeric fields ---------- */
.field__wrap { display: block; position: relative; }

.field__wrap--prefixed .field__input { padding-left: 2.1rem; }

.field__prefix {
  position: absolute;
  left: .85rem;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.95rem;
  color: var(--c-ink-faint);
  pointer-events: none;
}

.field__hint {
  margin: .35rem 0 0;
  font-size: 0.78rem;
  color: var(--c-ink-faint);
}

.field__error {
  margin: .35rem 0 0;
  font-size: 0.82rem;
  color: var(--c-error);
}

.field__input.is-invalid {
  border-color: var(--c-error);
}

/* ---------- register: tick-box criteria ---------- */
.checkset {
  border: 0;
  padding: 0;
  margin: 1.6rem 0 0;
  min-width: 0;                 /* a fieldset will not shrink in a grid without this */
}

.checkset__legend {
  padding: 0;
  font-size: 0.76rem;
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--c-ink-muted);
}

.checkset__hint {
  margin: .35rem 0 0;
  font-size: 0.84rem;
  color: var(--c-ink-faint);
}

.checkset__options {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: .75rem;
}

/* The control stays a real checkbox so it keeps its keyboard behaviour and
   reaches the form data; the pill is the label it drives. */
.check { display: inline-flex; }

.check input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.check__label {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .5rem .95rem;
  border: var(--rule-weight) solid var(--c-rule-strong);
  border-radius: var(--radius-pill);
  font-size: 0.9rem;
  line-height: 1.2;
  cursor: pointer;
  user-select: none;
  transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease), color var(--speed) var(--ease);
}

.check__label::before {
  content: "";
  width: .7rem; height: .7rem;
  border: var(--rule-weight) solid currentColor;
  border-radius: 2px;
  opacity: .5;
  transition: opacity var(--speed) var(--ease), background var(--speed) var(--ease);
}

.check__label:hover { border-color: var(--c-accent); }

/* Selected is drawn from currentColor rather than from a named colour. A pale
   accent makes a ticked pill vanish on one agency, and the primary is the panel
   background on an ink-toned section, so neither works everywhere; the text
   colour of whatever the form is sitting on always does. */
.check input:checked + .check__label {
  background: color-mix(in srgb, currentColor 13%, transparent);
  border-color: currentColor;
  box-shadow: inset 0 0 0 1px currentColor;
}
.check input:checked + .check__label::before {
  opacity: 1;
  background: currentColor;
}

.check input:focus-visible + .check__label {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}

.vform__grid--extras { margin-top: 1.6rem; }

.register__pending {
  margin: 1rem 0 0;
  padding: .85rem 1rem;
  border: var(--rule-weight) solid var(--c-rule-strong);
  border-radius: var(--radius);
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--c-ink-muted);
}

/* ---------- area coverage ---------- */
.areas__grid {
  display: grid;
  /* Capped at three so six areas read as two tidy rows rather than 4 + 2. */
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--c-rule);
  border: 1px solid var(--c-rule);
  border-radius: var(--radius);
  overflow: hidden;
  margin-top: var(--rhythm-sm);
}

.area-card {
  background: var(--c-bg);
  padding: 1.5rem 1.4rem 1.3rem;
  display: flex;
  flex-direction: column;
  gap: .9rem;
  color: inherit;
  text-decoration: none;
  transition: background var(--speed) var(--ease);
}
a.area-card:hover,
a.area-card:focus-visible { background: var(--c-surface); }

.area-card__motif {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  height: 7.5rem;
  padding: 1rem 1rem .5rem;
  color: var(--c-kicker);
  background: color-mix(in srgb, var(--c-kicker) 6%, transparent);
  border-radius: calc(var(--radius) + 2px);
  opacity: .9;
  transition: opacity var(--speed) var(--ease), background var(--speed) var(--ease);
}
a.area-card:hover .area-card__motif {
  opacity: 1;
  background: color-mix(in srgb, var(--c-kicker) 11%, transparent);
}

/* Sized from its height so every motif occupies the same band, whatever the
   card width. Strokes do not scale, so the line weight stays the same here,
   on a wide card and in print. */
.motif { display: block; height: 100%; width: auto; max-width: 100%; }
.motif :is(path, circle, polyline) { vector-effect: non-scaling-stroke; }
.motif .motif__fill { fill: color-mix(in srgb, currentColor 16%, transparent); }

.area-card__body { display: block; flex: 1; }

.area-card__name {
  display: block;
  font-family: var(--f-display);
  font-weight: var(--fw-display);
  font-size: clamp(1.15rem, 1rem + 0.5vw, 1.4rem);
  line-height: 1.15;
}

.area-card__blurb {
  display: block;
  margin-top: .45rem;
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--c-ink-muted);
}

.area-card__foot {
  display: grid;
  gap: .5rem;
  padding-top: .85rem;
  border-top: 1px solid var(--c-rule);
}

.area-card__covers {
  display: block;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--c-ink-faint);
}

.area-card__more {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  font-size: 0.85rem;
  letter-spacing: .02em;
  color: var(--c-kicker);
}
.area-card__more .arrow {
  width: 1em;
  height: 1em;
  transition: transform var(--speed) var(--ease);
}
a.area-card:hover .area-card__more .arrow { transform: translateX(3px); }

.areas__note {
  margin: 1.2rem 0 0;
  font-size: 0.84rem;
  color: var(--c-ink-faint);
}

@media (max-width: 960px) {
  .areas__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
  .areas__grid { grid-template-columns: 1fr; }
}

/* ---------- rental pulse ---------- */
.pulse-let__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: var(--c-rule);
  border-block: 1px solid var(--c-rule);
  margin-top: var(--rhythm-sm);
}

.pulse-let__stat {
  background: var(--c-bg);
  padding: 1.4rem 1.2rem;
  display: flex;
  flex-direction: column;
  gap: .3rem;
}

.pulse-let__label {
  margin: 0;
  font-size: 0.74rem;
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--c-ink-faint);
}

.pulse-let__value {
  margin: 0;
  font-family: var(--f-display);
  font-weight: var(--fw-display);
  font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.3rem);
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}

.pulse-let__note {
  margin: 0;
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--c-ink-muted);
}

.pulse-let__stat--chart { justify-content: space-between; }
.pulse-let__spark {
  display: block;
  width: 100%;
  height: 3.2rem;
  margin: .5rem 0 .4rem;
  color: var(--c-data);
}

.pulse-let__foot {
  margin: 1rem 0 0;
  font-size: 0.8rem;
  color: var(--c-ink-faint);
}

@media (max-width: 960px) { .pulse-let__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .pulse-let__grid { grid-template-columns: 1fr; } }

/* ---------- area contact ---------- */
.contact-card__inner {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: start;
  padding: clamp(1.6rem, 3vw, 2.6rem);
  background: var(--c-surface);
  border: 1px solid var(--c-rule);
  border-radius: var(--radius);
}

.contact-card__name {
  margin: .3rem 0 0;
  font-size: clamp(1.4rem, 1.1rem + 1vw, 2rem);
  line-height: 1.1;
}

.contact-card__role {
  margin: .25rem 0 .9rem;
  font-size: 0.88rem;
  letter-spacing: .02em;
  color: var(--c-ink-faint);
}

.contact-card__who p:not(.kicker):not(.contact-card__role) {
  margin: 0 0 .7rem;
  color: var(--c-ink-muted);
  line-height: 1.65;
}

.contact-card__reach { display: grid; gap: .55rem; }

.contact-card__phone {
  font-family: var(--f-display);
  font-size: clamp(1.25rem, 1rem + 0.9vw, 1.7rem);
  font-variant-numeric: tabular-nums;
  color: var(--c-ink);
  text-decoration: none;
}
.contact-card__phone:hover { text-decoration: underline; text-underline-offset: 4px; }

.contact-card__email {
  font-size: 0.9rem;
  color: var(--c-ink-muted);
  word-break: break-word;
}

.contact-card__cta { margin-top: .6rem; justify-self: start; }

.contact-card__note {
  margin: .3rem 0 0;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--c-ink-faint);
}

@media (max-width: 860px) {
  .contact-card__inner { grid-template-columns: 1fr; }
}

/* ---------- lightbox ---------- */
.lightbox {
  position: fixed; inset: 0; z-index: 300;
  background: rgb(0 0 0 / .92);
  display: grid; place-items: center;
  opacity: 0; visibility: hidden;
  transition: opacity var(--speed) var(--ease), visibility var(--speed);
}
.lightbox.is-open { opacity: 1; visibility: visible; }
.lightbox img { max-width: 92vw; max-height: 86dvh; object-fit: contain; }
.lightbox__btn {
  position: absolute; background: none; border: 0; color: #fff;
  font-size: 2.4rem; line-height: 1; cursor: pointer; padding: 1rem; opacity: .75;
}
.lightbox__btn:hover { opacity: 1; }
.lightbox__close { top: .5rem; right: 1rem; }
.lightbox__prev  { left: .5rem; top: 50%; transform: translateY(-50%); }
.lightbox__next  { right: .5rem; top: 50%; transform: translateY(-50%); }

/* ---------- scroll reveal ---------- */
@media (prefers-reduced-motion: no-preference) {
  .reveal { opacity: 0; transform: translateY(18px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
  .reveal.is-visible { opacity: 1; transform: none; }
}

/* ---------- dev bar ---------- */
.dev-bar {
  position: fixed; bottom: 1rem; left: 50%; transform: translateX(-50%);
  z-index: 400;
  display: flex; align-items: center; gap: .6rem;
  background: #10141a; color: #e7edf5;
  padding: .5rem .5rem .5rem 1rem;
  border-radius: 999px;
  font: 500 13px/1 ui-sans-serif, system-ui, sans-serif;
  box-shadow: 0 12px 40px rgb(0 0 0 / .4);
}
.dev-bar__label { opacity: .6; letter-spacing: .06em; text-transform: uppercase; font-size: 11px; }
.dev-bar a {
  color: inherit; text-decoration: none;
  padding: .4rem .8rem; border-radius: 999px;
  background: rgb(255 255 255 / .08);
}
.dev-bar a.is-active { background: #e7edf5; color: #10141a; }
.dev-bar a:hover { background: rgb(255 255 255 / .18); }
.dev-bar a.is-active:hover { background: #fff; }

/* ---------- pagination ---------- */
.pagination { display: flex; flex-wrap: wrap; gap: .4rem; list-style: none; padding: 0; margin: var(--rhythm-sm) 0 0; justify-content: center; }
.pagination a, .pagination span {
  display: inline-grid; place-items: center; min-width: 42px; height: 42px; padding: 0 .7rem;
  border: 1px solid var(--c-rule); border-radius: var(--radius-sm);
  text-decoration: none; font-size: var(--fs-sm);
}
.pagination .is-current { background: var(--c-primary); color: var(--c-on-primary); border-color: transparent; }
.pagination .is-disabled { opacity: .4; }

/* ---------- utilities ---------- */
.text-center { text-align: center; }
.mx-auto { margin-inline: auto; }
.mt-0 { margin-top: 0; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Data pages — sold prices, postcode and sector tiers.

   These are reference pages, not editorial ones. A display scale tuned for a
   hero headline makes "House prices in SK8 1HP" enormous and pushes the
   figures below the fold, so headings here are capped and the stat values
   are brought into a sensible range. */
.page-data h1 {
  font-size: clamp(1.75rem, 1.3rem + 1.6vw, 2.6rem);
  margin-bottom: .4rem;
}
.page-data h2 {
  font-size: clamp(1.25rem, 1.05rem + 0.7vw, 1.6rem);
  margin-bottom: .7rem;
}
.page-data h3 { font-size: clamp(1.05rem, 0.95rem + 0.4vw, 1.3rem); }
.page-data .lede { font-size: clamp(1rem, 0.95rem + 0.25vw, 1.12rem); }
.page-data .stat__value { font-size: clamp(1.3rem, 1.05rem + 1vw, 1.85rem); }
.page-data .section { padding-block: clamp(2rem, var(--rhythm), 4rem); }

.section-head { margin-bottom: var(--rhythm-sm); }
.section-head--split { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 1.2rem; }
.section-head h2 { margin-bottom: 0; }

@media print {
  .site-header, .site-footer, .dev-bar, .hero__actions { display: none; }
}

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

   A front page that sets no hero title is saying the photograph is the
   statement. So the image runs clean -- no scrim over it and nothing sitting
   on top -- and the actions drop into a band underneath, where dark text on
   the page background reads better than white text on a sky anyway. */

.hero--bare .hero__body {
    position: static;
    padding: 0;
    background: none;
}

.hero--bare .hero__media {
    min-height: clamp(20rem, 46vh, 34rem);
}

.hero--bare .hero__media img {
    filter: none;
}

.hero--bare .hero__actions {
    justify-content: center;
    padding: clamp(1.25rem, 3vw, 2rem) 0 0;
    margin: 0;
}

/* ------------------------------------------------------------------ launch

   A launch announcement with one offer on it.

   Full-bleed in the agency's primary colour so it reads as a statement rather
   than a promotional strip, with the rate on a plate beside the prose: the
   figure and the condition that qualifies it are the same object, at the same
   size relationship, so neither can be read without the other. */

.launch {
    background: var(--c-primary);
    color: var(--c-on-primary, #fff);
    padding: clamp(2.5rem, 6vw, 4.5rem) 0 clamp(2rem, 4vw, 3rem);
    position: relative;
    overflow: hidden;
}

/* A hairline at the top edge, in the kicker colour, to separate the band from
   whatever it follows without a hard border. */
.launch::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 2px;
    background: linear-gradient(90deg,
        transparent,
        color-mix(in srgb, var(--c-kicker) 70%, transparent) 20%,
        color-mix(in srgb, var(--c-kicker) 70%, transparent) 80%,
        transparent);
}

.launch__inner {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
    gap: clamp(2rem, 5vw, 4rem);
    align-items: center;
}

/* ------------------------------------------------------------------- lede */

.launch__eyebrow {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin: 0 0 1rem;
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--c-kicker);
}

.launch__eyebrow-rule {
    width: 2.25rem;
    height: 1px;
    background: currentColor;
    opacity: .8;
    flex: none;
}

.launch__heading {
    margin: 0 0 .9rem;
    font-size: clamp(1.75rem, 1.1rem + 2.1vw, 2.9rem);
    line-height: 1.12;
    letter-spacing: -.015em;
    text-wrap: balance;
}

.launch__body {
    margin: 0;
    max-width: 48ch;
    font-size: 1.03rem;
    line-height: 1.65;
    opacity: .88;
}

.launch__cta {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    margin-top: 1.6rem;
    padding: .8rem 1.5rem;
    border-radius: var(--radius, 2px);
    background: var(--c-cta-bg);
    color: var(--c-cta-text);
    font-weight: 600;
    letter-spacing: .01em;
    text-decoration: none;
    transition: transform .18s var(--ease, ease), background .18s var(--ease, ease);
}

.launch__cta svg { width: 1.05em; height: 1.05em; transition: transform .18s var(--ease, ease); }
.launch__cta:hover { background: var(--c-cta-bg-hover, var(--c-cta-bg)); }
.launch__cta:hover svg { transform: translateX(3px); }
.launch__cta:focus-visible { outline: 2px solid var(--c-kicker); outline-offset: 3px; }

/* ------------------------------------------------------------------ plate */

.launch__plate {
    position: relative;
    padding: clamp(1.5rem, 3vw, 2.25rem);
    border: 1px solid color-mix(in srgb, var(--c-kicker) 45%, transparent);
    border-radius: var(--radius, 2px);
    background: color-mix(in srgb, #000 12%, transparent);
    text-align: center;
}

.launch__figure {
    margin: 0;
    font-size: clamp(3.2rem, 2rem + 4.6vw, 5.4rem);
    font-weight: 600;
    line-height: .95;
    letter-spacing: -.03em;
    color: var(--c-kicker);
    font-variant-numeric: tabular-nums;
}

.launch__figure-label {
    margin: .45rem 0 0;
    font-size: .8rem;
    font-weight: 600;
    letter-spacing: .16em;
    text-transform: uppercase;
    opacity: .82;
}

/* The qualifier, kept with the figure rather than relegated to the footnote. */
.launch__condition {
    margin: 1.1rem auto 0;
    padding-top: 1.1rem;
    border-top: 1px solid color-mix(in srgb, currentColor 22%, transparent);
    max-width: 22ch;
    font-size: .95rem;
    line-height: 1.5;
}

.launch__scarcity {
    margin: .9rem 0 0;
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--c-kicker);
}

/* ------------------------------------------------------------------ terms */

.launch__terms {
    margin: clamp(1.75rem, 4vw, 2.5rem) 0 0;
    padding-top: 1.1rem;
    border-top: 1px solid color-mix(in srgb, currentColor 16%, transparent);
    font-size: .82rem;
    line-height: 1.6;
    opacity: .72;
    max-width: 78ch;
}

/* ----------------------------------------------------------------- narrow */

@media (max-width: 860px) {
    .launch__inner {
        grid-template-columns: 1fr;
        gap: 2rem;
    }

    /* The offer leads on a phone: the number is the reason to keep reading. */
    .launch__plate { order: -1; }
}

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