/* ==========================================================================
   Mira Verde / Trussardi Residences — design system
   ==========================================================================

   Structure, layout rhythm and interaction model follow the reference build
   (avenewdevelopment.ae/projects/cheval-residences-dubai-islands): a 72px page
   gutter, an 80px transparent nav over a full-height hero, alternating light /
   band / dark sections, full-bleed image blocks with the copy set into the top
   left and an accent rule along the bottom edge, 50/50 gallery-and-text rows,
   a four-up amenities carousel, and a centred two-column contact form.

   Colour, typography and the accent are Percent&Co's, not the reference's:
   green #26332F, cream #F3F1EC, burgundy #5E1B1B, Cormorant Garamond and
   EB Garamond. Every token name below is unchanged from the previous system —
   lead-capture.css is layered on top of them and must keep resolving.
   ========================================================================== */

:root {
  /* Colour */
  --green:        #26332F;   /* dark sections, buttons, ink on light */
  --green-soft:   #4E5C56;   /* secondary button fill */
  --footer-green: #1A2320;
  --shell:        #12160F;   /* logo lockup panel — community */
  --shell-alt:    #171C19;   /* logo lockup panel — project */
  --grey:         #EFEFEC;   /* primary light background */
  --grey-alt:     #E9E9E6;
  --band:         #C7CDC8;   /* the mid band between light and dark */
  --band-deep:    #B7BEB9;
  --white:        #FFFFFF;
  --cream:        #F3F1EC;   /* text on dark, light buttons */
  --muted:        #5F6E6A;   /* body copy on light */
  --muted-soft:   #6C7A75;
  --label:        #8F9993;   /* eyebrows, micro-labels on light */
  --hairline:     #DFE2DE;
  --hairline-alt: #E8E8E5;
  --burgundy:     #5E1B1B;   /* the accent — rules, borders, marks */

  --on-dark-90: rgba(243, 241, 236, .9);
  --on-dark-85: rgba(243, 241, 236, .85);
  --on-dark-70: rgba(243, 241, 236, .7);
  --on-dark-62: rgba(243, 241, 236, .62);
  --on-dark-50: rgba(243, 241, 236, .5);
  --on-dark-38: rgba(243, 241, 236, .38);
  --rule-dark:  rgba(243, 241, 236, .18);
  --fill-dark:  rgba(243, 241, 236, .06);

  /* The accent rule under every full-bleed image. The reference runs a gold
     texture here; this is its Percent&Co equivalent — burgundy lit through the
     centre so the edge reads as a struck line rather than a flat border. */
  --accent-rule: linear-gradient(90deg,
      #2F3E39 0%, #5E1B1B 24%, #8E3E3E 50%, #5E1B1B 76%, #2F3E39 100%);

  /* Type */
  --display: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --body:    'EB Garamond', Georgia, 'Times New Roman', serif;

  /* ------------------------------------------------------ tracking (§6.3)
     Every letter-spacing in this file and in lead-capture.css reads one of
     these. They are tokens rather than literals for one reason:

       LETTER-SPACING IS NOT UNIVERSAL. Arabic is a CURSIVE script — tracking
       forces gaps into the letter joins, and the result does not read as
       elegant, it reads as broken text. It has to go to zero, everywhere,
       including the values set inside media queries, which a per-rule
       override would never reach.

     The values below are the design as approved; nothing about the Latin or
     Cyrillic pages changes. See the [data-script] blocks further down.
     ------------------------------------------------------------------- */
  --track-04: .04em;
  --track-06: .06em;
  --track-08: .08em;
  --track-10: .1em;
  --track-12: .12em;
  --track-14: .14em;
  --track-16: .16em;
  --track-18: .18em;
  --track-20: .2em;
  --track-22: .22em;
  --track-24: .24em;
  --track-30: .3em;

  /* The counterweight to tracking on a centred control — see .btn. Zeroed
     with the tracking it compensates for. */
  --track-indent: .2em;

  /* Geometry — the reference's 72px page gutter and 28.8px full-bleed inset */
  --container: 1600px;
  --container-narrow: 1440px;
  --pad:     clamp(20px, 5vw, 72px);
  --gutter:  var(--pad);
  --edge:    clamp(12px, 2vw, 28.8px);
  --rhythm:  clamp(72px, 9vw, 144px);
  --gap-tight: clamp(12px, 1.2vw, 20px);
  --gap-card:  clamp(14px, 1.6vw, 26px);
  /* Flat corners throughout, as the reference is. --r-btn is the lead layer's
     input radius as well as the button's, so it stays a small real value —
     pill buttons set 999px themselves rather than through this token. */
  --r-lg: 0px;
  --r-md: 0px;
  --r-sm: 0px;
  --r-btn: 4px;

  /* Motion */
  --ease: cubic-bezier(.2, .8, .2, 1);
  --header-h: 80px;
}

/* --------------------------------------------------------------- base --- */

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--header-h);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--grey);
  color: var(--green);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

/* Any component that sets `display` outranks the UA's [hidden] rule, and the
   floor-plan panel is toggled by the hidden attribute alone — without this the
   "released on request" fallback renders underneath a real drawing. */
[hidden] { display: none !important; }

img, svg, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
input, button, textarea, select { font: inherit; color: inherit; }
::placeholder { color: #93A09B; opacity: 1; }
::selection { background: var(--burgundy); color: var(--cream); }

:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }

.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;
}

.skip-link {
  position: absolute;
  inset-inline-start: 12px; top: -100px;
  z-index: 200;
  background: var(--cream);
  color: var(--green);
  padding: 12px 20px;
  transition: top .2s;
}
.skip-link:focus { top: 12px; }

/* ---------------------------------------------------------- typography --- */

h1, h2, h3, h4 { margin: 0; font-family: var(--display); font-weight: 300; }
p { margin: 0; text-wrap: pretty; }
p + p { margin-top: 1.1em; }

/* The reference's h1: uppercase, light, and opened up with a wide track. */
.h-hero {
  font-weight: 300;
  font-size: clamp(38px, 5.4vw, 88px);
  line-height: 1.02;
  letter-spacing: var(--track-10);
  text-transform: uppercase;
  text-wrap: balance;
}
.h-hero strong { font-weight: 500; }

/* The reference's h2 — one size everywhere, uppercase, tight leading. */
.h-section {
  font-weight: 300;
  font-size: clamp(24px, 2.5vw, 38px);
  line-height: 1.08;
  letter-spacing: var(--track-06);
  text-transform: uppercase;
  text-wrap: balance;
}
.h-section--lg { font-size: clamp(28px, 3.2vw, 52px); }

/* The reference's h3 — the tiny label that sits above every h2. `.eyebrow` is
   the same thing under its previous name; both are live in the markup. */
.label,
.eyebrow {
  margin: 0;
  font-family: var(--body);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: var(--track-24);
  line-height: 1;
  text-transform: uppercase;
}

.micro {
  font-size: 11px;
  letter-spacing: var(--track-20);
  text-transform: uppercase;
  line-height: 1.4;
}

.h-card {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(20px, 1.7vw, 27px);
  line-height: 1.18;
  letter-spacing: var(--track-04);
  text-transform: uppercase;
}

.figure {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(24px, 2.6vw, 42px);
  line-height: 1;
  font-variant-numeric: lining-nums tabular-nums;
}

/* The reference keeps body copy inside a narrow measure everywhere. */
.copy { font-size: 17px; line-height: 1.55; }
.copy--set { max-width: 42ch; }
.lead { font-size: clamp(17px, 1.25vw, 20px); line-height: 1.6; }

.muted { color: var(--muted); }
/* Was --on-dark-62 (5.5:1). Body copy is not a caption: on the dark bands it
   is the only thing carrying the argument, and 62% cream on #26332F reads as
   grey rather than as text. 85% measures 8.9:1 composited, and the headings
   above it are full cream, so the hierarchy survives the change. */
.muted-dark { color: var(--on-dark-85); }
.section--dark .muted-dark,
.section--dark .duo__text > p { font-size: clamp(17px, 1.15vw, 18px); line-height: 1.6; }

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

.section { padding: var(--rhythm) var(--pad); }
.section--tight { padding-block: clamp(52px, 6vw, 104px); }
.section--flush { padding: 0; }
.section--edge  { padding-inline: var(--edge); }
/* Full-bleed blocks are laid edge to edge and carry no padding of their own,
   so two in a row butt together. This is the air between them, and it takes
   the colour of the section it belongs to, which keeps the band boundaries
   reading as deliberate rather than as a seam. */
.section--flush + .section--flush { padding-top: clamp(40px, 5vw, 84px); }
/* A flush block followed by a band of a different colour has no air under it:
   the next band's own top padding is that band's colour, so the two read as one
   object butted together. This gives the block a gap underneath in its OWN
   colour, matching the air above it. Used on the developer block, which runs
   straight into the dark agency band below it. */
.section--gap-below { padding-bottom: var(--rhythm); }
/* The inverse of --gap-below: closes the gap under a section so it runs
   straight into the next one. Only safe where the block below carries the
   same ground — two bands of the SAME colour read as one continuous field,
   and the next section's own top padding is all the air the join needs.
   Against a different colour this butts two grounds together with no air and
   reads as a seam. Used on the amenities row, which runs into the lead block
   below it on the same band. */
.section--flush-below { padding-bottom: 0; }
.section--dark  { background: var(--green); color: var(--cream); }
.section--light { background: var(--grey);  color: var(--green); }
.section--band  { background: var(--band);  color: var(--green); }

.wrap { max-width: var(--container); margin-inline: auto; }
.wrap--narrow { max-width: var(--container-narrow); margin-inline: auto; }
.wrap--text { max-width: 760px; margin-inline: auto; }

.split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(24px, 3vw, 60px); }
.split--wide  { grid-template-columns: minmax(0, 1fr) minmax(0, .8fr); }
.split--lead  { grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); }
.split--layouts   { grid-template-columns: minmax(0, .6fr) minmax(0, 1.4fr); gap: clamp(16px, 1.6vw, 26px); }
.split--lead-card { grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); }
.split--flush { gap: 0; align-items: stretch; }
.split--end   { align-items: end; }
.split--start { align-items: start; }
.split--stretch { align-items: stretch; }

/* §14.6, in its grid form. Every track below is minmax(0, 1fr) and not a
   bare 1fr, because THE AUTOMATIC MINIMUM OF AN fr TRACK IS min-content: the
   column shrinks only until its longest word, and then stops. In English that
   limit is never reached. In German it is — «Aufenthaltsvisum» and
   «Diversifizierung» pushed the four-figure stat strip 36px past a 320px
   phone, measured. minmax(0, 1fr) lets the track shrink to zero and hands the
   wrapping back to the text, where overflow-wrap and hyphens deal with it. */
.grid { display: grid; gap: var(--gap-tight); }
.grid--cards { gap: var(--gap-card); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.stack-head { margin-bottom: clamp(28px, 3.2vw, 52px); }
.center { text-align: center; }

/* ----------------------------------------------------------------- nav --- */

/* 80px, transparent over the hero, and it takes a solid ground the moment the
   hero has passed. Cream ink stranded on a light section is the one failure
   this must never have, so the solid ground is the markup default and JS only
   ever removes it. */
.site-header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 80;
  height: var(--header-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-inline: var(--pad);
  background: rgba(239, 239, 236, .96);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  color: var(--green);
  transition: background .5s ease, color .4s ease, box-shadow .5s ease;
}
.site-header::after {
  content: "";
  position: absolute; inset: auto 0 0;
  height: 1px;
  background: var(--hairline);
  opacity: 1;
  transition: opacity .5s ease;
}
.site-header.is-top {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  color: var(--cream);
}
.site-header.is-top::after { opacity: 0; }

/* Agency mark · hairline · project logotype — the lockup carries both brands
   at every width. Both marks take their colour from the bar, so the pair
   inverts with `.is-top` for free. */
.brand { display: flex; align-items: center; gap: clamp(11px, 1.1vw, 18px); flex-shrink: 0; }
.brand__stack { display: flex; flex-direction: column; gap: 3px; }
.brand__rule { width: 1px; align-self: stretch; min-height: 26px; background: currentColor; opacity: .3; }

.brand__logo {
  position: relative;
  display: block;
  height: var(--brand-logo-h, clamp(13px, 1.3vw, 19px));
  width: calc(var(--brand-logo-h, clamp(13px, 1.3vw, 19px)) * var(--brand-logo-ratio));
  background-color: currentColor;
  -webkit-mask: var(--brand-logo-src) no-repeat center / contain;
  mask: var(--brand-logo-src) no-repeat center / contain;
}
/* The ratio is the mark's own width ÷ height, straight off its viewBox
   (logo2.svg is 504.48 × 135.2). It sizes the element box; the mask is
   `contain` inside it, so a wrong ratio does not distort the mark — it pads it
   with dead space and pushes the header apart. Re-measure it when the file
   is replaced. */
/* Sized off --brand-logo-h rather than the shared default: at 3.73 this mark is
   a compact block, not a long wordmark, so the default height that suits a wide
   logotype leaves it reading as a footnote beside the agency stack. Height
   drives width through the ratio above, so this is the only number to touch. */
.brand__logo--mv {
  --brand-logo-ratio: 3.73;
  --brand-logo-h: clamp(18px, 1.85vw, 26px);
  --brand-logo-src: url(../logo/logo2.svg);
}
/* The collection lockup, carried by the Trussardi page's header in place of
   the community one. Ratio re-measured off the supplied file's viewBox
   (700 x 244 = 2.87) per the note above; it replaced a 9.05 rule that pointed
   at ../logo/trussardi-mira-verde.svg — a file this build never had, left over
   from the Mira Verde clone. Heights match --mv rather than the old --tr: at
   2.87 this is a compact stack like the community mark, not the long single-line
   wordmark 9.05 was sized for. Narrower than --mv, so the header gains room. */
.brand__logo--tr {
  --brand-logo-ratio: 2.87;
  --brand-logo-h: clamp(18px, 1.85vw, 26px);
  --brand-logo-src: url(../logo/trussardi-residences.svg);
}
.brand__logo span {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
@supports not ((-webkit-mask-image: url(#m)) or (mask-image: url(#m))) {
  .brand__logo { width: auto; height: auto; background: none; }
  .brand__logo span {
    position: static; width: auto; height: auto; margin: 0;
    clip-path: none; overflow: visible;
    font-family: var(--display);
    font-size: clamp(17px, 1.4vw, 23px);
    letter-spacing: var(--track-22);
    text-transform: uppercase;
  }
}
.brand__mark {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(15px, 1.2vw, 19px);
  letter-spacing: var(--track-20);
  line-height: 1.1;
  text-transform: uppercase;
}
.brand__sub { font-size: clamp(8px, .62vw, 10px); letter-spacing: var(--track-30); text-transform: uppercase; opacity: .65; line-height: 1.2; }

.header__right { display: flex; align-items: center; gap: clamp(14px, 1.8vw, 30px); flex-shrink: 0; }
.header__nav { display: flex; align-items: center; gap: clamp(14px, 1.6vw, 26px); }
.header__nav a {
  position: relative;
  font-size: 12px;
  letter-spacing: var(--track-18);
  text-transform: uppercase;
  padding-block: 4px;
  transition: opacity .3s;
}
/* The rule is drawn from the left rather than switched on, so the nav answers
   the pointer with movement instead of a state change. */
.header__nav a::after {
  content: "";
  position: absolute; inset: auto 0 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .4s var(--ease);
}
.header__nav a:hover::after { transform: scaleX(1); transform-origin: left; }

/* MENU, then the two rules — the reference's mark, in the agency's ink. */
.menu-btn {
  display: flex; align-items: center; gap: 14px;
  background: none; border: none; padding: 8px 0; cursor: pointer;
}
.menu-btn .micro { font-size: 12px; letter-spacing: var(--track-20); }
.menu-btn__lines { display: flex; flex-direction: column; gap: 5px; }
.menu-btn__lines i {
  display: block; width: 46px; height: 1px;
  background: currentColor;
  transition: transform .4s var(--ease), width .4s var(--ease);
}
.menu-btn:hover .menu-btn__lines i:last-child { width: 30px; }
.menu-btn[aria-expanded="true"] .menu-btn__lines i:first-child { transform: translateY(3px) rotate(5deg); }
.menu-btn[aria-expanded="true"] .menu-btn__lines i:last-child  { transform: translateY(-3px) rotate(-5deg); width: 46px; }

/* -------------------------------------------------------------- button --- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 12px 32px;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 12px;
  letter-spacing: var(--track-20);
  /* Letter-spacing appends a trailing space after the last glyph, so centred
     text sits one track-width left of true centre. Indenting by the same
     amount puts it back. Every letter-spaced, centred control needs this. */
  text-indent: var(--track-indent);
  text-transform: uppercase;
  line-height: 1.2;
  cursor: pointer;
  white-space: nowrap;
  transition: background .35s, color .35s, border-color .35s,
              transform .35s var(--ease), box-shadow .35s var(--ease);
}
/* Every button answers the pointer with the same small lift, so the page has
   one hover language rather than a different one per component. */
.btn:hover { transform: translateY(-2px); box-shadow: 0 10px 24px rgba(24, 34, 30, .16); }
.btn:active { transform: translateY(0); box-shadow: none; }
.btn--pill { border-radius: 999px; }
.btn--solid { background: var(--green); color: var(--cream); }
.btn--solid:hover { background: var(--green-soft); }
.btn--cream { background: var(--cream); color: var(--green); }
.btn--cream:hover { background: var(--white); }
.btn--soft { background: var(--green-soft); color: var(--cream); }
.btn--soft:hover { background: var(--green); }
/* The reference's outline pill — a hairline in the accent, nothing else. */
.btn--line { border-color: var(--burgundy); color: var(--green); background: none; }
.btn--line:hover { background: var(--burgundy); border-color: var(--burgundy); color: var(--cream); }
.btn--outline-dark { border-color: rgba(243, 241, 236, .4); color: var(--cream); }
.btn--outline-dark:hover { background: var(--cream); color: var(--green); }
.btn--glass {
  background: rgba(243, 241, 236, .14);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-color: rgba(243, 241, 236, .35);
  color: var(--cream);
}
.btn--glass:hover { background: var(--cream); color: var(--green); }
.btn--block { display: flex; width: 100%; }

/* A download control carries its arrow, as in the reference. */
.btn--dl::after { content: "↓"; margin-inline-start: 12px; font-size: 13px; letter-spacing: 0; }

/* Every other letter-spaced pill in the system needs the same optical
   correction as .btn — see the note there. */
.bed-pill { text-indent: .16em; }
.pill-badge,
.chips span,
.doc-links a,
.doc-links button,
.lead-submit { text-indent: .16em; }

/* The document chips are the one control that had drifted: two different
   sizes across two stylesheets, and no letter-spacing while everything around
   them is tracked. One definition, matching the small-pill treatment. */
.doc-links a,
.doc-links button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 38px;
  padding: 8px 18px;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: none;
  font-family: var(--body);
  font-size: 11px;
  letter-spacing: var(--track-16);
  text-transform: uppercase;
  line-height: 1.2;
  color: var(--muted);
  cursor: pointer;
  transition: border-color .3s, color .3s, background .3s, transform .3s var(--ease);
}
.doc-links a:hover,
.doc-links button:hover { border-color: var(--burgundy); color: var(--green); transform: translateY(-2px); }

.link-rule {
  display: inline-block;
  padding-bottom: 5px;
  border-bottom: 1px solid currentColor;
  font-size: 13px;
  letter-spacing: var(--track-14);
  text-transform: uppercase;
  transition: opacity .35s;
}
.link-rule:hover { opacity: .7; }

/* ---------------------------------------------------------- nav panel --- */

.nav-panel {
  position: fixed;
  inset: 0;
  z-index: 79;
  background: rgba(26, 35, 32, .985);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  padding: clamp(90px, 10vw, 140px) var(--pad) clamp(40px, 5vw, 80px);
  color: var(--cream);
  opacity: 0;
  visibility: hidden;
  transition: opacity .4s ease, visibility .4s;
}
.nav-panel[data-open="true"] { opacity: 1; visibility: visible; }
.nav-panel a {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(22px, 2.8vw, 42px);
  letter-spacing: var(--track-08);
  text-transform: uppercase;
  padding: 11px 0;
  border-bottom: 1px solid rgba(243, 241, 236, .12);
  transition: padding-inline-start .35s var(--ease), color .3s;
}
.nav-panel a:hover { padding-inline-start: 14px; }
.nav-panel a:last-child { border-bottom: 0; }
.nav-panel .nav-panel__back {
  font-family: var(--body);
  font-size: 11px;
  letter-spacing: var(--track-24);
  padding: 0 0 22px;
  border-bottom: 0;
  color: var(--on-dark-50);
}

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

/* The reference hero: full height, copy pinned to the bottom edge, the H1 on
   the left and a short kicker plus the scroll cue on the right. */
.hero {
  position: relative;
  height: 100vh;
  height: 100svh;
  min-height: 620px;
  overflow: hidden;
}
.hero__slides { position: absolute; inset: 0; }
.hero__slide { position: absolute; inset: 0; opacity: 0; transition: opacity 1.7s ease; }
.hero__slide.is-active { opacity: 1; }
/* The renders put the horizon a little below centre; a default crop on a
   viewport wider than the source loses the ridge line to sky. */
.hero__slide img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 62%; }
.hero__slide.is-active img { animation: mvDrift 16s ease-out forwards; }

.hero__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(26, 35, 32, .42) 0%,
    rgba(26, 35, 32, .10) 38%,
    rgba(26, 35, 32, .72) 100%);
}

.hero__inner {
  position: absolute;
  inset: auto 0 0;
  z-index: 2;
  padding: 0 var(--pad) clamp(44px, 6vw, 90px);
  color: var(--cream);
}
.hero__grid {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(24px, 3vw, 56px);
}
.hero__h1 { flex: 1 1 auto; min-width: 0; }
/* Wide enough that the CTA pair sits on one line beside the kicker — the pair
   is the page's revenue path and must not stack into a column here. */
.hero__aside { flex: 0 0 auto; width: min(580px, 48vw); display: flex; flex-direction: column; gap: clamp(16px, 1.8vw, 26px); }
.hero__kicker {
  font-size: clamp(15px, 1.15vw, 19px);
  line-height: 1.35;
  letter-spacing: var(--track-06);
  text-transform: uppercase;
  font-family: var(--display);
  font-weight: 300;
  color: var(--cream);
}
.hero .eyebrow { color: var(--on-dark-70); margin-bottom: clamp(16px, 1.8vw, 26px); }

.hero__scroll {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  align-self: flex-start;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--cream);
  font-size: 13px;
  letter-spacing: var(--track-10);
  color: var(--cream);
}
.hero__scroll i {
  display: block; width: 9px; height: 9px;
  border-inline-end: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  animation: mvNudge 2.4s var(--ease) infinite;
}

/* The word-by-word rise the reference runs on its hero, done with CSS only. */
.split-word { display: inline-block; overflow: clip; vertical-align: bottom; }
.split-word > span { display: inline-block; transform: translateY(105%); opacity: 0; }
.is-revealed .split-word > span {
  transform: none;
  opacity: 1;
  transition: transform 1s var(--ease), opacity .8s ease;
  transition-delay: calc(var(--i, 0) * 70ms);
}

/* --------------------------------------------------------------- intro --- */

/* Centred logotype, heading and one paragraph — the reference's opening beat. */
.intro { padding: clamp(90px, 10vw, 144px) var(--pad); text-align: center; }
.intro__inner { display: flex; flex-direction: column; align-items: center; }
.intro__mark { width: clamp(140px, 14vw, 200px); height: auto; margin-bottom: clamp(26px, 3vw, 44px); opacity: .9; }
.intro .h-section { max-width: 16em; }
.intro__copy { margin-top: clamp(22px, 2.6vw, 38px); max-width: 68ch; font-size: clamp(17px, 1.2vw, 19px); line-height: 1.6; color: var(--muted); }
.section--dark .intro__copy { color: var(--on-dark-70); }

/* ------------------------------------------------------ full-bleed block --- */

/* An image that runs the width of the page inside a small inset, the copy set
   into its top left, and the accent rule struck along the bottom edge. */
.imgfull { position: relative; overflow: hidden; height: clamp(420px, 56vw, 720px); }
.imgfull > img,
.imgfull > picture img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 1.6s var(--ease);
}
.imgfull:hover > img,
.imgfull:hover > picture img { transform: scale(1.04); }
/* The copy used to rely on a heavy corner scrim, which asked the render to be
   dark exactly where it is often brightest and left the text sitting in a grey
   wash across a third of the picture. The copy now carries its own plate, so
   the scrim is back to a light vignette — legibility is the plate's job, and
   the render is left looking like a render. */
.imgfull__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(22, 30, 27, .30) 0%, rgba(22, 30, 27, 0) 32%),
    linear-gradient(0deg,   rgba(22, 30, 27, .34) 0%, rgba(22, 30, 27, 0) 30%);
}
/* The plate. A frosted panel with the accent struck down its leading edge —
   the copy sits ON something rather than floating over the picture, and the
   contrast no longer depends on which render is behind it. */
.imgfull__body {
  position: absolute;
  top: clamp(20px, 3.4vw, 50px);
  inset-inline-start: clamp(14px, 4vw, 60px);
  inset-inline-end: clamp(14px, 4vw, 60px);
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: clamp(12px, 1.4vw, 20px);
  max-width: min(56ch, 640px);
  padding: clamp(20px, 2.2vw, 36px) clamp(20px, 2.4vw, 40px);
  background: rgba(19, 27, 24, .62);
  -webkit-backdrop-filter: blur(16px) saturate(1.08);
  backdrop-filter: blur(16px) saturate(1.08);
  border-inline-start: 3px solid var(--burgundy);
  box-shadow: 0 24px 60px rgba(10, 16, 13, .3);
  color: var(--cream);
}
/* No backdrop-filter (older Safari, Firefox with it disabled) means the panel
   would be a flat 62% wash over a busy render. Fall back to near-opaque. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .imgfull__body { background: rgba(19, 27, 24, .88); }
}
.imgfull__body--right { inset-inline-start: auto; inset-inline-end: clamp(14px, 4vw, 60px); text-align: end; align-items: flex-end; border-inline-start: 0; border-inline-end: 3px solid var(--burgundy); }
.imgfull__body--bottom { top: auto; bottom: clamp(30px, 4vw, 60px); }
.imgfull__body .label { color: var(--on-dark-70); }
.imgfull__body p { color: var(--on-dark-90); max-width: 48ch; }
.imgfull__body--right p { margin-inline-start: auto; }

/* ------------------------------------------------------- master plan --- */

/* Edge to edge, and NOT cropped: this is a drawing whose labels are the
   content, so `cover` would cut "Trussardi Residences Tower A" off the left
   edge on a wide viewport. Height follows the aspect ratio instead. */
.masterplan { position: relative; margin: clamp(30px, 3.4vw, 54px) 0 0; }
.masterplan__zoom {
  display: block;
  position: relative;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}
.masterplan img { width: 100%; height: auto; display: block; }
.masterplan__hint {
  position: absolute;
  inset-inline-end: clamp(14px, 2vw, 28px); bottom: clamp(18px, 2.2vw, 32px);
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid rgba(243, 241, 236, .22);
  background: rgba(18, 22, 15, .78);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: var(--cream);
  font-size: 12px;
  letter-spacing: var(--track-14);
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
  transition: background .3s;
}
.masterplan__zoom:hover .masterplan__hint { background: var(--burgundy); }
/* Bottom right is where the drawing's own labels are densest, and at phone
   width the hint lands on top of three of them. */
@media (max-width: 760px) {
  .masterplan__hint {
    top: 12px; inset-inline-end: 12px; bottom: auto;
    padding: 6px 12px;
    font-size: 11px;
  }
}

/* The rule. 4px, and it reads as struck metal rather than a flat border. */
.accent-line {
  position: absolute; inset: auto 0 0;
  height: 4px;
  background: var(--accent-rule);
  z-index: 2;
}

/* --------------------------------------------------- gallery + text row --- */

/* Half the row is a gallery, half is a centred block of copy. `--flip` swaps
   the sides on wide screens; DOM order stays reading order. */
/* minmax(0,1fr), not 1fr: a grid item's automatic minimum is its min-content,
   and a slider track full of 100%-wide slides has a large one. With plain 1fr
   the column refuses to shrink and the page gains a horizontal scrollbar on a
   phone. Same reason for .dev below. */
.duo { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; }
/* Held inside the same inset as the full-bleed blocks, so every block on the
   page shares one left and right edge. The reference lets its gallery rows run
   edge to edge while its image blocks sit inset, and the two-pixel disagreement
   between them is visible at every boundary. */
.duo, .dev { margin-inline: var(--edge); }
.duo__media { position: relative; min-width: 0; }
.duo__text {
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  gap: clamp(24px, 3vw, 50px);
  padding: clamp(40px, 5vw, 80px) clamp(24px, 4vw, 72px);
}
.duo__text > p { max-width: 46ch; color: var(--muted); }
.section--dark .duo__text > p,
.section--band .duo__text > p { color: inherit; }
.section--band .duo__text > p { color: #3B4844; }
@media (min-width: 1001px) {
  .duo--flip .duo__media { order: 2; }
}

/* --------------------------------------------------------------- slider --- */

/* One vanilla slider drives the gallery rows, the amenities row and the
   developer row. No CDN: the reference loads Splide and Swiper; this is
   ~120 lines of CSS and JS against two dependencies. */
.slider { position: relative; min-width: 0; }
/* pan-y hands horizontal gestures to the pointer handlers in main.js while
   leaving vertical page scroll to the browser. Without it a touch swipe is
   claimed by the native pan, the pointer stream is cancelled mid-drag, and the
   gallery cannot be paged by finger at all. */
.slider__viewport { overflow: hidden; touch-action: pan-y; }
/* A finger drag across a photo must not start a native image drag instead. */
.slider__slide img { -webkit-user-drag: none; user-select: none; }
.slider__track {
  display: flex;
  transition: transform .8s var(--ease);
  will-change: transform;
}
.slider__slide { flex: 0 0 100%; min-width: 0; }

.slider--gallery .slider__viewport { position: relative; }
.slider--gallery .slider__slide img {
  width: 100%;
  height: clamp(320px, 46vw, 648px);
  object-fit: cover;
  display: block;
  transition: transform 1.6s var(--ease);
}
/* The reference lifts its gallery slightly on hover. Scaling the image rather
   than the slide keeps the track transform — which is what pages the carousel
   — free of anything else. */
.slider--gallery:hover .slider__slide img { transform: scale(1.035); }
.slider--gallery .accent-line { z-index: 4; }

/* Enlarge control over the gallery. */
.slider__zoom {
  position: absolute;
  top: clamp(14px, 1.6vw, 24px); inset-inline-end: clamp(14px, 1.6vw, 24px);
  z-index: 4;
  width: 44px; height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(243, 241, 236, .5);
  background: rgba(26, 35, 32, .3);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: var(--cream);
  font-size: 16px; line-height: 1;
  cursor: zoom-in;
  opacity: 0;
  transition: opacity .35s, background .35s;
}
.slider:hover .slider__zoom, .slider__zoom:focus-visible { opacity: 1; }
.slider__zoom:hover { background: var(--burgundy); border-color: var(--burgundy); }

/* Counter — "10 / 18", as the reference announces its slide position. */
.slider__count {
  position: absolute;
  inset-inline-start: clamp(14px, 1.6vw, 24px); bottom: clamp(20px, 2.2vw, 32px);
  z-index: 4;
  font-size: 12px;
  letter-spacing: var(--track-20);
  color: var(--cream);
  text-shadow: 0 1px 12px rgba(20, 28, 24, .8);
  font-variant-numeric: lining-nums tabular-nums;
}

/* Dot rail. */
.slider__dots {
  position: absolute;
  inset: auto clamp(14px, 1.6vw, 24px) clamp(20px, 2.2vw, 32px) auto;
  z-index: 4;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
  max-width: 60%;
}
.slider__dots button {
  width: 22px; height: 2px;
  padding: 0;
  border: 0;
  background: rgba(243, 241, 236, .38);
  cursor: pointer;
  transition: background .3s;
}
.slider__dots button[aria-selected="true"] { background: var(--cream); }
.slider__dots button:hover { background: rgba(243, 241, 236, .75); }

/* Round arrows, hairlined in the accent — the reference's control. */
.slider__arrow {
  width: 46px; height: 46px;
  border-radius: 50%;
  border: 1px solid var(--burgundy);
  background: rgba(255, 255, 255, .4);
  color: var(--green);
  font-size: 17px; line-height: 1;
  cursor: pointer;
  transition: background .35s, color .35s, transform .35s var(--ease), border-color .35s;
}
.slider__arrow:hover { background: var(--burgundy); color: var(--cream); }
.slider__arrow--prev:hover { transform: translateX(-3px); }
.slider__arrow--next:hover { transform: translateX(3px); }
.slider__arrow[disabled] { opacity: .3; cursor: default; }
.slider__arrow[disabled]:hover { transform: none; background: rgba(255, 255, 255, .4); color: var(--green); }
.section--dark .slider__arrow { border-color: var(--on-dark-50); background: rgba(243, 241, 236, .06); color: var(--cream); }
.section--dark .slider__arrow:hover { background: var(--cream); color: var(--green); }
/* Both disabled-hover resets must be restated on the dark ground: the dark
   hover rule above carries the same specificity and comes later, so without
   this a paged-out arrow lights up cream. */
.section--dark .slider__arrow[disabled]:hover { background: rgba(243, 241, 236, .06); color: var(--cream); transform: none; }
.slider__arrows { display: flex; gap: 10px; justify-content: center; margin-top: clamp(24px, 2.6vw, 40px); }

/* ----------------------------------------------------------- amenities --- */

/* Four up, static. This was a paged, autoplaying rail; with exactly four cards
   it had nothing to page on a desktop and only ever moved under the reader.
   It is a grid now — four across, two on a tablet, one on a phone. */
.amenities__head { text-align: center; display: flex; flex-direction: column; align-items: center; }
.amenities__head .intro__mark { margin-bottom: clamp(20px, 2.4vw, 34px); }
.amenity-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(20px, 2.4vw, 38px) clamp(10px, 1.2vw, 18px);
  margin-top: clamp(34px, 4vw, 62px);
}
/* Six amenities here, so three across rather than four — four would leave a
   widow pair on the second row. The 1100px rule further down still takes it to
   two and 640px to one; this only sets the desktop count. */
.amenity-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* The facet is centred in its column by `margin-inline:auto`, so the title and
   the sentence have to be centred against it or the card reads as two pieces
   that miss each other. One centred column, and the copy takes a measure
   rather than the full column width so the ragged edge stays symmetrical. */
.amenity-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
/* The reference cuts each amenity image into a faceted crystal inside a struck
   metal edge, and gives each one a different cut so the row reads as four
   objects rather than four thumbnails. Same device here, in the brand accent:
   the frame is the accent gradient, the image is clipped 2px inside it.

   Each facet is a closed polygon on the same five-point armature, nudged per
   card. Keep them convex — a concave point reads as a rendering fault, not a
   cut stone. */
.amenity__frame {
  --facet: polygon(38% 0%, 100% 22%, 88% 88%, 20% 100%, 0% 42%);
  position: relative;
  width: min(100%, 300px);
  aspect-ratio: 1 / 1;
  margin-inline: auto;
  padding: 3px;
  background: var(--accent-rule);
  clip-path: var(--facet);
  transition: filter .5s var(--ease), transform .5s var(--ease);
}
.amenity__frame img {
  width: 100%; height: 100%;
  object-fit: cover;
  clip-path: var(--facet);
  transition: transform 1.4s var(--ease);
}
.amenity-card:hover .amenity__frame img { transform: scale(1.06); }
.amenity-card:hover .amenity__frame { filter: brightness(1.06); transform: translateY(-5px); }
.amenity-card .amenity__title { transition: color .4s; }
.amenity-card:hover .amenity__title { color: var(--burgundy); }
.section--dark .amenity-card:hover .amenity__title { color: var(--cream); }

.amenity-card:nth-child(4n+2) .amenity__frame {
  --facet: polygon(58% 2%, 100% 42%, 74% 100%, 10% 90%, 0% 26%);
}
.amenity-card:nth-child(4n+3) .amenity__frame {
  --facet: polygon(44% 0%, 96% 14%, 100% 74%, 32% 100%, 0% 58%);
}
.amenity-card:nth-child(4n+4) .amenity__frame {
  --facet: polygon(64% 0%, 100% 50%, 64% 100%, 6% 84%, 0% 22%);
}
/* Two lines' worth of room whether the title needs it or not, so the
   paragraphs below sit on one baseline across the row. */
.amenity__title {
  margin-top: clamp(16px, 1.8vw, 24px);
  min-height: 2.4em;
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(18px, 1.5vw, 24px);
  letter-spacing: var(--track-08);
  text-transform: uppercase;
  line-height: 1.2;
}
.amenity__text { margin-top: 10px; max-width: 32ch; margin-inline: auto; font-size: 16px; line-height: 1.5; color: #3E4B47; }
.section--dark .amenity__text { color: var(--on-dark-62); }

/* ------------------------------------------------------- reason cards --- */

/* Nine equal boxes stacked three-by-three read as a specification sheet: the
   eye has no entry point and no reason to move. The same nine reasons on a
   paged rail give it one — three at a time, numbered, with the accent struck
   across the top of each so the row reads as a sequence being worked through
   rather than a wall. Same slider as the amenities row, wider slides. */
.slider--cards { margin-top: clamp(28px, 3.2vw, 48px); }
.slider--cards .slider__track { gap: var(--gap-card); align-items: stretch; }
.slider--cards .slider__slide { flex: 0 0 calc((100% - 2 * var(--gap-card)) / 3); display: flex; }

.reason {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  padding: clamp(24px, 2.4vw, 38px) clamp(20px, 2.2vw, 32px);
  background: var(--white);
  overflow: hidden;
  transition: transform .5s var(--ease), box-shadow .5s var(--ease);
}
.reason::before {
  content: "";
  position: absolute; inset: 0 0 auto;
  height: 3px;
  background: var(--accent-rule);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .6s var(--ease);
}
.reason:hover { transform: translateY(-6px); box-shadow: 0 22px 50px rgba(24, 34, 30, .13); }
.reason:hover::before { transform: scaleX(1); }
.reason__no {
  font-family: var(--display);
  font-size: clamp(30px, 2.8vw, 44px);
  line-height: 1;
  letter-spacing: var(--track-04);
  color: var(--burgundy);
  opacity: .32;
  font-variant-numeric: lining-nums tabular-nums;
  transition: opacity .5s var(--ease);
}
.reason:hover .reason__no { opacity: .7; }
.reason__t { font-family: var(--display); font-weight: 400; font-size: clamp(19px, 1.6vw, 25px); line-height: 1.2; letter-spacing: var(--track-04); text-transform: uppercase; }
.reason__p { font-size: 16px; line-height: 1.55; color: var(--muted); }

/* Three reasons fit on one row, so they are a static grid rather than the
   paged rail nine of them need. `.reason` is self-contained — it sets its own
   width, ground and padding — so it drops into a plain grid unchanged. */
.reason-grid { margin-top: clamp(28px, 3.2vw, 48px); align-items: stretch; }

/* A project card with no page behind it yet is an <article>, not an <a>. The
   hover zoom is the affordance of a link, so it is switched off here — the
   button inside the card is the thing that is actually clickable. */
.project-card--static:hover img { transform: none; }

/* ---------------------------------------------------------- developer --- */

.dev { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: stretch; }
.dev__media { position: relative; overflow: hidden; min-height: clamp(340px, 40vw, 620px); }
/* :not() matters — the logotype is a sibling <img> in the same box, and a bare
   `> img` rule stretches it to the full panel. The ground is inside a
   <picture> now, so that arm is named too: a `> img` rule alone stops
   matching the moment the image gains a webp source set. */
.dev__media > img:not(.dev__logo),
.dev__media > picture img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.dev__media .lockup__scrim { position: absolute; inset: 0; background: linear-gradient(125deg, rgba(12, 18, 14, .38), rgba(12, 18, 14, .74)); }
.dev__logo {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(280px, 52%);
  max-height: 34%;
  height: auto;
  object-fit: contain;
  z-index: 2;
}
.dev__body {
  display: flex; flex-direction: column; justify-content: center;
  padding: clamp(40px, 5vw, 86px) clamp(24px, 4vw, 72px);
  text-align: center;
  align-items: center;
}
.dev__body .slider { width: 100%; }
.dev__slide { display: flex; flex-direction: column; align-items: center; gap: clamp(18px, 2vw, 30px); padding-inline: 4px; }
.dev__slide p { max-width: 52ch; color: var(--muted); }
.section--dark .dev__slide p { color: var(--on-dark-70); }

/* The strip of partner house marks that stood here has been removed, along
   with assets/logo/brands/. Percent&Co have no licence to reproduce Bentley,
   ETRO, ELIE SAAB, Jacob & Co., Trussardi, John Richmond or Gianfranco Ferré
   trade marks, and a brochure is not a licence. The developer paragraph still
   names the houses in running text, which is nominative use and needs none. */

/* The developer row is a full-bleed block with no padding of its own, so the
   dark agency band below butted straight onto it. It gets the same air below
   it that the section above gives it. */
#developer.section--flush { padding-bottom: var(--rhythm); }

/* ------------------------------------------------------------- contact --- */

.contact { padding: clamp(80px, 9vw, 140px) var(--pad); }
.contact__head { text-align: center; display: flex; flex-direction: column; align-items: center; gap: clamp(16px, 2vw, 26px); }
.contact__head p { max-width: 52ch; color: var(--muted); }
.contact__form { margin: clamp(40px, 4.6vw, 72px) auto 0; max-width: 860px; }

/* The reference lays its contact fields out two to a row. The form itself is
   built by lead-capture.js as a flex column and is shared with the dialogs, so
   the two-column arrangement is applied here, on this one mount, rather than
   in the module — the dialogs stay single-column where width is scarce (§11).
   The three fields are the first three divs in the form; everything after them
   (honeypot, consent, submit, note) runs the full width. */
.contact__form .lead-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: clamp(20px, 3vw, 44px);
  align-items: start;
}
.contact__form .lead-form > .lead-field:nth-of-type(3),
.contact__form .lead-form > .lead-trap,
.contact__form .lead-form > .lead-consent,
.contact__form .lead-form > .lead-submit,
.contact__form .lead-form > .lead-note { grid-column: 1 / -1; }
/* The shared submit is width:100% for the dialogs; here it becomes the
   reference's centred 270px pill. */
.contact__form .lead-form > .lead-submit {
  justify-self: center;
  width: auto;
  min-width: 270px;
  border-radius: 999px;
  font-size: 12px;
  letter-spacing: var(--track-20);
  text-transform: uppercase;
}

@media (max-width: 700px) {
  .contact__form .lead-form { grid-template-columns: 1fr; }
  .contact__form .lead-form > .lead-field { grid-column: 1 / -1; }
}

/* ------------------------------------------------------------ invest --- */

/* A dark band carrying only two bordered boxes of text reads as a placeholder.
   This gives it a ground (a render held well back so the copy still measures
   past 4.5:1), a figure strip built from the numbers the approved copy already
   states, and an accent edge on each panel. */
.invest { position: relative; overflow: hidden; }
/* Lift the content off the ground image by naming the wrapper, not `> *`. A
   bare child selector matches .accent-line too and, at equal specificity,
   beats its `position: absolute` — which drops the rule into the flow and
   lands it on top of the CTA. */
.invest > .wrap--narrow { position: relative; z-index: 1; }
.invest__bg { position: absolute; inset: 0; z-index: 0; }
.invest__bg img { width: 100%; height: 100%; object-fit: cover; }
.invest__bg::after {
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(30, 41, 37, .90) 0%, rgba(30, 41, 37, .95) 55%, rgba(30, 41, 37, .99) 100%);
}
.invest .accent-line { z-index: 2; }

.panel {
  position: relative;
  padding: clamp(26px, 3vw, 46px);
  background: rgba(243, 241, 236, .05);
  border: 1px solid rgba(243, 241, 236, .13);
  transition: background .4s, border-color .4s;
}
.panel::before {
  content: "";
  position: absolute; inset: 0 0 auto;
  height: 2px;
  background: var(--accent-rule);
}
.panel:hover { background: rgba(243, 241, 236, .08); border-color: rgba(243, 241, 236, .22); }

/* ------------------------------------------------------------- cards --- */

.card {
  background: var(--white);
  padding: clamp(26px, 2.8vw, 46px);
  transition: transform .5s var(--ease), box-shadow .5s var(--ease);
}
.card:hover { transform: translateY(-5px); box-shadow: 0 20px 46px rgba(24, 34, 30, .12); }
.card--sm { padding: clamp(20px, 2.2vw, 32px); }
.card--dark { background: var(--fill-dark); border: 1px solid rgba(243, 241, 236, .12); padding: clamp(20px, 2.2vw, 34px); }
.card--line { background: none; border: 1px solid var(--hairline); }
.section--band .card--line { border-color: rgba(38, 51, 47, .18); }

.figure-card { border-top: 1px solid var(--hairline); padding-top: clamp(14px, 1.4vw, 22px); }
.section--dark .figure-card { border-top-color: var(--rule-dark); }

/* -------------------------------------------------------- project card --- */

.project-card { display: block; position: relative; overflow: hidden; color: var(--cream); }
.project-card img { width: 100%; height: clamp(340px, 46vw, 700px); object-fit: cover; transition: transform 1.4s var(--ease); }
.project-card:hover img { transform: scale(1.04); }
.project-card__scrim { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(26, 35, 32, .1) 30%, rgba(26, 35, 32, .88)); }
.project-card__body { position: absolute; inset: 0; padding: clamp(22px, 2.8vw, 48px); display: flex; flex-direction: column; justify-content: space-between; }
.project-card__foot { display: flex; flex-wrap: wrap; gap: clamp(20px, 3vw, 56px); align-items: flex-end; justify-content: space-between; }
.pill-badge {
  align-self: flex-start;
  background: rgba(243, 241, 236, .14);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(243, 241, 236, .3);
  padding: 8px 18px;
  border-radius: 999px;
  font-size: 11px;
  letter-spacing: var(--track-20);
  text-transform: uppercase;
}
.project-card__cta { background: var(--cream); color: var(--green); }
/* Two controls on the Trussardi card: the link through to the project page,
   and the form opener. They sit as one group at the end of the foot so the
   card keeps its single right-hand block. */
.project-card__actions { display: flex; flex-wrap: wrap; align-items: center; gap: clamp(10px, 1vw, 16px); }
.project-card:hover .project-card__cta { background: var(--burgundy); color: var(--cream); }

/* On a phone the copy stops being an overlay. A 340px picture cannot hold a
   heading, ten lines of body and a button, and darkening the photograph enough
   to make it try costs the photograph. So below 760px the card unstacks: the
   image is the image, at its own height, and the copy sits beneath it on solid
   green — no scrim, nothing dimmed, and the card is as tall as its text plus
   the padding at any string length. */
@media (max-width: 760px) {
  .project-card { display: grid; grid-template-columns: minmax(0, 1fr); }
  .project-card > picture { display: block; overflow: hidden; }
  .project-card img { height: clamp(200px, 56vw, 320px); }
  .project-card__scrim { display: none; }
  .project-card__body {
    position: relative; inset: auto;
    background: var(--green);
    padding: 50px 22px;
    gap: clamp(20px, 5vw, 30px);
  }
  .project-card__foot { gap: clamp(18px, 4vw, 26px); align-items: stretch; }
  .project-card__cta { width: 100%; }
  .project-card__actions { flex-direction: column; align-items: stretch; gap: 12px; }
  .project-card__actions > .btn { width: 100%; }
}

/* ------------------------------------------------------------ location --- */

/* Name over time, not name-then-time-across-a-gap. On one line the two halves
   of each pair sat a third of the row apart and the eye had to travel to pair
   them up; stacked, each destination is one object. */
.travel {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 6px; padding: clamp(14px, 1.4vw, 20px) 0;
  border-top: 1px solid var(--hairline);
  transition: border-top-color .4s, padding-inline-start .4s var(--ease);
}
.travel:hover { border-top-color: var(--burgundy); padding-inline-start: 8px; }
.section--dark .travel { border-top-color: var(--rule-dark); }
.travel span:first-child {
  font-size: 11px; letter-spacing: var(--track-20); text-transform: uppercase;
  color: var(--label);
}
.section--dark .travel span:first-child { color: var(--on-dark-50); }
.travel span:last-child { font-family: var(--display); font-size: clamp(22px, 1.9vw, 30px); line-height: 1; white-space: nowrap; transition: color .4s; }
.travel:hover span:last-child { color: var(--burgundy); }
.section--dark .travel:hover span:last-child { color: var(--cream); }

/* The map is a real Google embed, built lazily — nothing is requested from
   Google on a visit that never reaches Location. It used to run the full width
   of the page, where a raw Google tile set is simply a large ugly rectangle;
   it is a framed panel beside the travel times now, at half the row. */
.location__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(28px, 3.4vw, 60px);
  align-items: stretch;
  margin-top: clamp(30px, 3.4vw, 52px);
}
.location__times { display: flex; flex-direction: column; justify-content: center; }
.map {
  position: relative;
  margin: 0;
  min-height: clamp(300px, 34vw, 480px);
  overflow: hidden;
  border: 1px solid var(--hairline);
  background-color: #2E3C37;
  background-image: repeating-linear-gradient(135deg, rgba(243, 241, 236, .05) 0 2px, transparent 2px 12px);
}
@media (max-width: 1000px) {
  .location__row { grid-template-columns: minmax(0, 1fr); }
  .map { min-height: clamp(260px, 52vw, 380px); }
}
.map iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; opacity: 0; transition: opacity .7s var(--ease); }
.map[data-loaded="true"] iframe { opacity: 1; }
.map[data-touch="true"]:not([data-active="true"]) iframe { pointer-events: none; }
.map__hint {
  position: absolute; z-index: 3;
  left: 50%; bottom: 22px; transform: translateX(-50%);
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid rgba(243, 241, 236, .18);
  background: rgba(18, 22, 15, .82);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: var(--cream);
  font-size: 12px;
  letter-spacing: var(--track-14);
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
  transition: opacity .3s;
}
.map:not([data-touch="true"]) .map__hint,
.map[data-active="true"] .map__hint { opacity: 0; }
.map__fallback {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; text-align: center; padding: 24px;
  background: linear-gradient(rgba(24, 34, 30, .72), rgba(24, 34, 30, .72));
  color: var(--cream);
}
.map__fallback p { max-width: 40ch; font-size: 16px; color: var(--on-dark-70); }

/* ------------------------------------------------------------ lightbox --- */

.lightbox {
  position: fixed; inset: 0; z-index: 120;
  background: rgba(20, 28, 25, .96);
  display: flex; align-items: center; justify-content: center;
  padding: clamp(16px, 4vw, 64px);
  cursor: zoom-out;
  opacity: 0; visibility: hidden;
  transition: opacity .4s ease, visibility .4s;
}
.lightbox[data-open="true"] { opacity: 1; visibility: visible; }
.lightbox img { max-width: 100%; max-height: 100%; object-fit: contain; transition: opacity .35s ease; }
.lightbox[data-swapping="true"] img { opacity: 0; }
.lightbox__close {
  position: absolute; top: clamp(14px, 2vw, 28px); inset-inline-end: clamp(14px, 2vw, 28px);
  width: 48px; height: 48px; border-radius: 50%;
  border: 1px solid rgba(243, 241, 236, .4);
  background: none; color: var(--cream);
  font-size: 22px; line-height: 1; cursor: pointer;
  transition: background .3s, color .3s;
}
.lightbox__close:hover { background: var(--cream); color: var(--green); }

/* An enlarged gallery image that can only be closed and re-opened to see the
   next one is a dead end — the visitor is deepest in the imagery at exactly
   the moment the interface stops helping. The arrows page the gallery the
   image came from, and the slider is moved to match on close, so the two are
   never showing different pictures. */
.lightbox__nav {
  position: absolute; top: 50%;
  transform: translateY(-50%);
  width: 54px; height: 54px;
  border-radius: 50%;
  border: 1px solid rgba(243, 241, 236, .4);
  background: rgba(20, 28, 25, .4);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: var(--cream);
  font-size: 19px; line-height: 1;
  cursor: pointer;
  transition: background .3s, color .3s, border-color .3s, transform .3s var(--ease);
}
.lightbox__nav--prev { inset-inline-start: clamp(10px, 2vw, 30px); }
.lightbox__nav--next { inset-inline-end: clamp(10px, 2vw, 30px); }
.lightbox__nav:hover { background: var(--burgundy); border-color: var(--burgundy); }
.lightbox__nav--prev:hover { transform: translateY(-50%) translateX(-3px); }
.lightbox__nav--next:hover { transform: translateY(-50%) translateX(3px); }
.lightbox__count {
  position: absolute;
  left: 50%; bottom: clamp(14px, 2.4vw, 30px);
  transform: translateX(-50%);
  margin: 0;
  font-size: 12px;
  letter-spacing: var(--track-22);
  color: var(--on-dark-70);
  font-variant-numeric: lining-nums tabular-nums;
}
/* A single-image target keeps the plain lightbox it had. */
.lightbox[data-single="true"] .lightbox__nav,
.lightbox[data-single="true"] .lightbox__count { display: none; }

@media (max-width: 640px) {
  .lightbox { padding-inline: 12px; }
  .lightbox__nav { width: 44px; height: 44px; }
  .lightbox__nav--prev { inset-inline-start: 6px; }
  .lightbox__nav--next { inset-inline-end: 6px; }
}

/* --------------------------------------------------------- units table --- */

/* Every secondary size in this table used to be set at the smallest value in
   the system — 11px tracked labels, a 13px unit conversion, a 15px note — all
   of it in a low-alpha cream on dark green. On a 15-inch laptop that is a
   squint. The row values were already large; the supporting text now sits at a
   size and an alpha that can be read at arm's length. */
.table-scroll { border: 1px solid var(--rule-dark); overflow-x: auto; }
.units-table { min-width: 560px; }
/* Three columns, not the sibling sites' four: this project has no source
   figure for a saleable area, so there is no Area column. See the note on
   UNITS in src/data/figures.mjs. */
.units-head, .units-row { display: grid; grid-template-columns: 1.4fr 1.2fr .9fr; gap: 16px; align-items: center; }
.units-head {
  padding: 16px clamp(16px, 2vw, 32px);
  color: var(--on-dark-70);
  background: rgba(243, 241, 236, .04);
  border-bottom: 1px solid var(--rule-dark);
}
.units-head.micro { font-size: 13px; letter-spacing: var(--track-18); }
.units-row { padding: clamp(18px, 1.8vw, 26px) clamp(16px, 2vw, 32px); transition: background .35s; }
.units-row + .units-row { border-top: 1px solid var(--rule-dark); }
.units-row:hover { background: rgba(243, 241, 236, .05); }
.units-row .cell {
  font-family: var(--display);
  font-size: clamp(21px, 1.6vw, 27px);
  line-height: 1.2;
  letter-spacing: var(--track-04);
  font-variant-numeric: lining-nums tabular-nums;
}
.units-row .cell small { display: block; margin-top: 3px; font-family: var(--body); font-size: 16px; letter-spacing: var(--track-04); color: var(--on-dark-70); }
.units-row .cell-action {
  font-size: 13px;
  letter-spacing: var(--track-16);
  text-transform: uppercase;
  color: var(--on-dark-70);
  text-align: end;
  transition: color .3s;
}
.units-row:hover .cell-action { color: var(--cream); }
.table-note { margin-top: clamp(16px, 1.8vw, 26px); font-size: 17px; line-height: 1.55; color: var(--on-dark-70); }

/* ---------------------------------------------------------- unit cards --- */

.unit-card {
  position: relative;
  background: var(--white);
  padding: clamp(24px, 2.4vw, 38px);
  display: flex; flex-direction: column;
  overflow: hidden;
  transition: transform .5s var(--ease), box-shadow .5s var(--ease);
}
.unit-card::before {
  content: "";
  position: absolute; inset: 0 0 auto;
  height: 3px;
  background: var(--accent-rule);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .6s var(--ease);
}
.unit-card:hover { transform: translateY(-5px); box-shadow: 0 20px 46px rgba(24, 34, 30, .12); }
.unit-card:hover::before { transform: scaleX(1); }
.unit-card h4 { font-family: var(--display); font-weight: 300; font-size: clamp(21px, 1.9vw, 30px); letter-spacing: var(--track-06); text-transform: uppercase; line-height: 1.15; margin-bottom: clamp(14px, 1.6vw, 22px); }
.unit-card dl { margin: 0; display: grid; gap: 12px; }
.unit-card dt { font-size: 11px; letter-spacing: var(--track-20); text-transform: uppercase; color: var(--label); }
.unit-card dd { margin: 4px 0 0; font-family: var(--display); font-size: clamp(18px, 1.4vw, 24px); line-height: 1.25; font-variant-numeric: lining-nums tabular-nums; }
/* margin-top:auto pins the button to the card's bottom edge so a row of cards
   with unequal copy still lines its buttons up. It must not also carry
   padding-top — that pads the button itself and pushes its label off centre. */
.unit-card dl { margin-bottom: clamp(22px, 2.4vw, 32px); }
.unit-card .btn { margin-top: auto; }

/* ------------------------------------------------------------- layouts --- */

.bed-pills { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: clamp(18px, 1.8vw, 26px); }
.bed-pill {
  flex: 0 0 auto;
  min-width: 54px; height: 46px;
  padding-inline: 18px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: var(--white);
  color: var(--green);
  font-size: 12px; letter-spacing: var(--track-16); text-transform: uppercase;
  cursor: pointer;
  transition: background .3s, color .3s, border-color .3s;
}
.bed-pill[aria-selected="true"] { background: var(--green); border-color: var(--green); color: var(--cream); }

.plan-choice { position: relative; text-align: start; background: var(--white); border: 1px solid var(--hairline); padding: 14px; cursor: pointer; transition: border-color .3s; }
.plan-choice[aria-selected="true"] { border-color: var(--burgundy); }
.plan-choice__radio { position: absolute; top: 12px; inset-inline-end: 12px; width: 16px; height: 16px; border-radius: 50%; background: var(--grey); border: 1px solid #DDDFDC; }
.plan-choice[aria-selected="true"] .plan-choice__radio { background: var(--burgundy); border-color: var(--burgundy); }
.plan-choice__thumb { display: block; width: 100%; height: clamp(80px, 8vw, 110px); background: var(--cream); object-fit: contain; padding: 6px; }
.plan-choice__size { display: block; margin-top: 10px; font-family: var(--display); font-size: 18px; }

.plan-panel { background: var(--white); padding: clamp(24px, 2.6vw, 44px); text-align: center; }
/* contain, not cover: a floor plan cropped to fill its frame is a floor
   plan with rooms missing. The tray carries the letterboxing, and it is the
   SAME --cream the drawings' own paper was retinted to — a contained image on
   a near-but-not-equal ground shows its bars as an inner border. */
.plan-panel__img { margin: clamp(20px, 2.2vw, 34px) auto 0; max-width: 620px; height: clamp(240px, 28vw, 430px); background: var(--cream); object-fit: contain; padding: 12px; width: 100%; }
.chips { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: clamp(20px, 2.2vw, 34px); }
.chips span { border: 1px solid var(--hairline); border-radius: 999px; padding: 9px 18px; font-size: 14px; color: var(--muted); }
.plan-cta { background: var(--white); padding: clamp(20px, 2.2vw, 34px); display: flex; flex-wrap: wrap; gap: 16px; align-items: center; justify-content: space-between; }

/* ------------------------------------------------------- instalments --- */

/* Same correction as the units table above: the schedule is the single most
   scrutinised block on the page and it was set in 12px tracked caps and 15px
   copy at 62% cream. The "when" column now carries --on-dark-85 (8.9:1 on
   #26332F, measured composited) at 17px. */
.instalments { border: 1px solid var(--rule-dark); overflow-x: auto; }
.instalments__inner { min-width: 620px; }
.instalment { display: grid; grid-template-columns: 1.2fr .5fr 2fr; gap: 16px; align-items: center; padding: clamp(18px, 1.8vw, 26px) clamp(16px, 2vw, 32px); transition: background .35s; }
.instalment + .instalment { border-top: 1px solid var(--rule-dark); }
.instalment:hover { background: rgba(243, 241, 236, .05); }
.instalment__name { font-size: 14px; letter-spacing: var(--track-14); text-transform: uppercase; color: var(--on-dark-85); }
.instalment__pct { font-family: var(--display); font-size: clamp(24px, 2.1vw, 34px); line-height: 1; font-variant-numeric: lining-nums tabular-nums; }
.instalment__when { color: var(--on-dark-85); font-size: 17px; line-height: 1.5; }
.instalment--final { background: rgba(243, 241, 236, .07); }
.instalment--final .instalment__when { color: var(--cream); }

/* --------------------------------------------------------- checklist --- */

.checklist { list-style: none; margin: clamp(20px, 2.2vw, 32px) 0 0; padding: 0; display: grid; gap: 13px; }
.checklist li { display: flex; gap: 14px; align-items: flex-start; font-size: 16px; line-height: 1.55; }
.checklist li::before { content: ""; flex: 0 0 auto; width: 6px; height: 6px; margin-top: .6em; border-radius: 50%; background: var(--burgundy); }
.checklist--dark li::before { background: var(--cream); }

/* -------------------------------------------------------- service cards --- */

/* The four things the agency actually does. As a bulleted list inside one
   bordered box on a dark ground they ran together into a paragraph of grey;
   four cards give each one its own edge, its own numeral and its own measure. */
.svc-cards {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gap-card);
  margin-top: clamp(24px, 2.6vw, 40px);
}
.svc-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: clamp(12px, 1.2vw, 18px);
  padding: clamp(22px, 2.2vw, 34px) clamp(20px, 2vw, 30px);
  background: var(--fill-dark);
  border: 1px solid rgba(243, 241, 236, .13);
  overflow: hidden;
  transition: transform .5s var(--ease), background .4s, border-color .4s;
}
.svc-card::before {
  content: "";
  position: absolute; inset: 0 0 auto;
  height: 2px;
  background: var(--accent-rule);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .6s var(--ease);
}
.svc-card:hover { transform: translateY(-5px); background: rgba(243, 241, 236, .09); border-color: rgba(243, 241, 236, .22); }
.svc-card:hover::before { transform: scaleX(1); }
.svc-card__no {
  font-family: var(--display);
  font-size: clamp(26px, 2.2vw, 36px);
  line-height: 1;
  letter-spacing: var(--track-04);
  color: var(--cream);
  opacity: .38;
  font-variant-numeric: lining-nums tabular-nums;
  transition: opacity .5s var(--ease);
}
.svc-card:hover .svc-card__no { opacity: .8; }
.svc-card p { font-size: 16px; line-height: 1.55; color: var(--on-dark-70); }

/* The light-ground version is not used yet; it exists so the component can be
   reused without being redefined at the point of use. */
.section--light .svc-card { background: var(--white); border-color: transparent; }
.section--light .svc-card__no { color: var(--burgundy); }
.section--light .svc-card p { color: var(--muted); }

@media (max-width: 1100px) { .svc-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .svc-cards { grid-template-columns: minmax(0, 1fr); } }

/* --------------------------------------------------------- photo CTA --- */

/* A centred heading, a line of copy and two buttons on flat green is a slide,
   not a section. Same copy and the same two buttons, on a render held back far
   enough that both still measure past 4.5:1. */
.cta-photo { position: relative; overflow: hidden; }
.cta-photo__bg { position: absolute; inset: 0; z-index: 0; }
.cta-photo__bg img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 58%; }
/* Held back far enough that cream copy still measures 5.6:1 against the
   BRIGHTEST patch of the render behind it (a sunlit window at sRGB 220
   composites to 95), not against its average — but no further, or the band is
   just a slightly darker green and the render might as well not be there. */
.cta-photo__bg::after {
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(24, 33, 30, .76) 0%, rgba(24, 33, 30, .66) 45%, rgba(24, 33, 30, .84) 100%);
}
.cta-photo > .wrap--text { position: relative; z-index: 1; }
.cta-photo .accent-line { z-index: 2; }
.cta-photo { padding-block: clamp(80px, 9vw, 150px); }
/* The lead variant is the one that has to be read across a room: heading one
   step up, body copy at reading size in near-full cream rather than the muted
   grey the flat-green version used. */
.cta-photo__lead { max-width: 900px; }
.cta-photo__lead > p { font-size: clamp(18px, 1.35vw, 21px); line-height: 1.55; color: var(--on-dark-90); }
/* This band is half the height of the community page's, so a `cover` crop of a
   wide render keeps only the dark middle of it and the picture disappears into
   the overlay. Hold the sky, and lighten the overlay to match — the measured
   contrast behind the smallest control here is recorded in site/README.md. */
.cta-photo--lead .cta-photo__bg img { object-position: 50% 34%; }
.cta-photo--lead .cta-photo__bg::after {
  background:
    linear-gradient(180deg, rgba(24, 33, 30, .70) 0%, rgba(24, 33, 30, .62) 45%, rgba(24, 33, 30, .80) 100%);
}

/* ------------------------------------------------------- agency stats --- */

.agency-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--gap-tight); margin-top: clamp(28px, 3vw, 46px); }
.agency-stat {
  position: relative;
  border-top: 1px solid var(--hairline);
  padding-top: clamp(14px, 1.4vw, 22px);
}
/* The figure strip is the one place the accent can appear without competing
   with an image, so the rule above each number is redrawn on hover. */
.agency-stat::before {
  content: "";
  position: absolute; inset: -1px 0 auto;
  height: 1px;
  background: var(--burgundy);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .55s var(--ease);
}
.agency-stat:hover::before { transform: scaleX(1); }
.section--dark .agency-stat::before { background: var(--cream); }
.agency-stat .figure { transition: transform .55s var(--ease); }
.agency-stat:hover .figure { transform: translateY(-3px); }
.agency-stat .micro { margin-top: 8px; color: var(--label); }
.section--dark .agency-stat { border-top-color: var(--rule-dark); }
.section--dark .agency-stat .micro { color: var(--on-dark-50); }

/* ------------------------------------------------------------ lead card --- */

.lead-card { background: var(--white); overflow: hidden; }
.lead-card__body { padding: clamp(28px, 3.4vw, 60px); display: flex; flex-direction: column; justify-content: center; }
.lead-card__media { min-height: clamp(240px, 30vw, 480px); overflow: hidden; }
.lead-card__media img { width: 100%; height: 100%; object-fit: cover; }
.doc-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: clamp(18px, 2vw, 28px); }
.doc-links a {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 9px 18px;
  font-size: 14px;
  color: var(--muted);
  transition: border-color .3s, color .3s;
}
.doc-links a:hover { border-color: var(--burgundy); color: var(--green); }
.doc-links a::before { content: "↓"; font-size: 13px; }

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

.faq { background: var(--white); padding: 0 clamp(22px, 2.4vw, 42px); }
.faq__item + .faq__item { border-top: 1px solid var(--hairline); }
.faq__q { width: 100%; background: none; border: 0; padding: clamp(22px, 2.2vw, 30px) 0; display: flex; justify-content: space-between; align-items: center; gap: 24px; cursor: pointer; text-align: start; color: var(--green); }
.faq__q span:first-child { font-family: var(--display); font-weight: 400; font-size: clamp(19px, 1.6vw, 26px); line-height: 1.25; letter-spacing: var(--track-04); }
.faq__icon { flex-shrink: 0; font-family: var(--display); font-weight: 300; font-size: 28px; line-height: 1; transition: transform .45s var(--ease); }
.faq__q[aria-expanded="true"] .faq__icon { transform: rotate(45deg); }
.faq__a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .5s var(--ease); }
.faq__q[aria-expanded="true"] + .faq__a { grid-template-rows: 1fr; }
.faq__a > div { overflow: hidden; }
.faq__a p { padding-bottom: clamp(22px, 2.2vw, 30px); max-width: 78ch; font-size: 16px; line-height: 1.65; color: var(--muted); }

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

/* The reference footer is one dark bar: legal left, the mark and the phone in
   the centre, contact right. */
.site-footer { background: var(--footer-green); color: var(--cream); }
.footer-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(20px, 3vw, 48px);
  padding: clamp(26px, 3vw, 34px) var(--pad);
}
.footer-bar__side { flex: 1 1 0; display: flex; flex-wrap: wrap; gap: 8px 22px; align-items: center; }
.footer-bar__side--end { justify-content: flex-end; }
.footer-bar__mid { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 12px; }
.footer-bar__mid img { width: clamp(140px, 15vw, 200px); }
.footer-bar a, .footer-bar address {
  font-size: 12px;
  letter-spacing: var(--track-18);
  text-transform: uppercase;
  font-style: normal;
  color: var(--on-dark-62);
  transition: color .3s;
}
.footer-bar a:hover { color: var(--cream); }
.footer-bar__phone { font-family: var(--display); font-size: clamp(19px, 1.6vw, 26px) !important; letter-spacing: var(--track-16) !important; color: var(--cream) !important; text-transform: none !important; }

.footer-cols {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) repeat(3, minmax(0, 1fr));
  gap: clamp(24px, 3vw, 56px);
  padding: clamp(50px, 6vw, 88px) var(--pad) clamp(30px, 3.4vw, 48px);
  border-bottom: 1px solid rgba(243, 241, 236, .1);
}
.footer-col { display: flex; flex-direction: column; gap: 11px; }
.footer-col > span:first-child { font-size: 11px; letter-spacing: var(--track-22); text-transform: uppercase; color: var(--on-dark-38); margin-bottom: 4px; }
.footer-col a, .footer-col address { font-size: 15px; font-style: normal; color: var(--on-dark-70); transition: color .3s, transform .35s var(--ease); }
.footer-col a { display: inline-block; }
.footer-col a:hover { color: var(--cream); transform: translateX(5px); }
.footer-col p { font-size: 15px; color: var(--on-dark-50); }
.footer-legal { padding: 0 var(--pad) clamp(24px, 3vw, 36px); }
.footer-legal p { font-size: 13px; line-height: 1.6; color: var(--on-dark-38); max-width: 92ch; }

/* ------------------------------------------------------------ reveals --- */

@keyframes mvFadeUp { from { opacity: 0; transform: translateY(28px); } to { opacity: 1; transform: none; } }
@keyframes mvDrift  { from { transform: scale(1); } to { transform: scale(1.08); } }
@keyframes mvNudge {
  0%, 100% { transform: rotate(45deg) translate(-1px, -1px); }
  50%      { transform: rotate(45deg) translate(2px, 2px); }
}

/* Content is visible by default; JS only adds .is-hidden once it is running. */
.reveal { transition: opacity 1s var(--ease), transform 1s var(--ease); }
.reveal.is-hidden { opacity: 0; transform: translateY(26px); }

/* A full-bleed block is far too large for a 26px nudge to read as movement, so
   it settles out of a slight overscale instead — the block arrives, rather
   than the block sliding. Its own 1.6s image transition carries it. */
.imgfull.reveal.is-hidden > img,
.imgfull.reveal.is-hidden > picture img,
.project-card.reveal.is-hidden img { transform: scale(1.07); }

/* The gallery rows and the developer panel enter from the side they sit on, so
   the two halves of a 50/50 row do not arrive as one slab. */
.reveal--left.is-hidden  { transform: translateX(-32px); }
.reveal--right.is-hidden { transform: translateX(32px); }
@media (max-width: 1000px) {
  .reveal--left.is-hidden,
  .reveal--right.is-hidden { transform: translateY(26px); }
}

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

@media (max-width: 1200px) { .header__nav { display: none; } }

@media (max-width: 1100px) {
  .amenity-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .slider--cards .slider__slide { flex: 0 0 calc((100% - var(--gap-card)) / 2); }
}

@media (max-width: 1000px) {
  :root { --header-h: 70px; }
  .desktop-only { display: none !important; }
  .split, .split--wide, .split--lead, .split--layouts, .split--lead-card { grid-template-columns: 1fr; }
  .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .duo, .dev { grid-template-columns: minmax(0, 1fr); }
  .duo__text { padding-inline: var(--pad); }
  .dev__media { min-height: clamp(300px, 60vw, 460px); }
  .footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .footer-bar { flex-direction: column; text-align: center; }
  .footer-bar__side, .footer-bar__side--end { justify-content: center; }
  .imgfull { height: clamp(400px, 78vw, 560px); }
  .imgfull__body { max-width: none; }
  .hero__grid { flex-direction: column; align-items: flex-start; gap: clamp(22px, 4vw, 40px); }
  .hero__aside { width: 100%; }
}

@media (max-width: 760px) {
  .slider--cards .slider__slide { flex: 0 0 100%; }
}

@media (min-width: 1001px) { .mobile-only { display: none !important; } }

@media (max-width: 640px) {
  .site-header { padding-inline: 16px; gap: 10px; }
  .header__right { gap: 10px; }
  .brand { flex-direction: column; align-items: flex-start; gap: 5px; }
  .brand__rule { display: none; }
  .brand__sub { display: none; }
  .brand__mark { font-size: 14px; letter-spacing: var(--track-14); }
  .brand__logo--mv { --brand-logo-h: 15px; }
  .brand__logo--tr { --brand-logo-h: 15px; }
  .menu-btn__lines i { width: 28px; }
  .menu-btn[aria-expanded="true"] .menu-btn__lines i:last-child { width: 28px; }
  /* The header CTA stays at every width (playbook §9). It is shortened to
     "Request Details" and tightened by lead-capture.css, which loads after this
     file — the stacked brand lockup above is what makes room for it. */

  .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .amenity-grid { grid-template-columns: 1fr; }
  .footer-cols { grid-template-columns: 1fr; }
  .instalment { grid-template-columns: 1fr .5fr 1.4fr; }
  .agency-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .slider__dots { max-width: 50%; }
  .slider__dots button { width: 14px; }
}

/* The setting asks us to remove *travel*, not to remove the interface. Killing
   every transition also kills hover feedback and cross-fades, which reads as a
   broken page rather than a calm one. So: no movement, no loops, no parallax —
   but opacity and colour still transition, and the reveals still fade in. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-property: opacity, background-color, color, border-color, box-shadow, fill, stroke, filter !important;
    transition-duration: .35s !important;
  }
  .reveal.is-hidden,
  .reveal--left.is-hidden,
  .reveal--right.is-hidden { transform: none; }
  .split-word > span { transform: none; }
  [data-parallax] { transform: none !important; }
  .hero__slide.is-active img { animation: none; }
  .imgfull.reveal.is-hidden > img,
  .imgfull.reveal.is-hidden > picture img,
  .project-card.reveal.is-hidden img,
  .imgfull:hover > img,
  .imgfull:hover > picture img,
  .project-card:hover img,
  .amenity-card:hover .amenity__frame,
  .amenity-card:hover .amenity__frame img,
  .slider--gallery:hover .slider__slide img { transform: none; }
  /* No lift on anything — the whole hover language above is movement, and the
     colour half of each rule still fires through the allowed properties. */
  .btn:hover,
  .card:hover,
  .unit-card:hover,
  .reason:hover,
  .travel:hover,
  .doc-links a:hover,
  .doc-links button:hover,
  .footer-col a:hover,
  .agency-stat:hover .figure,
  .svc-card:hover,
  .slider__arrow--prev:hover,
  .slider__arrow--next:hover { transform: none; box-shadow: none; }
  /* The struck rules stay — they are a state change, not travel, and without a
     transition they simply appear on hover. */
}

@media print {
  .site-header, .nav-panel, .lightbox, .hero__scroll, .slider__arrows, .slider__zoom { display: none !important; }
  body { background: #fff; color: #000; }
}


/* ==========================================================================
   THE MULTILINGUAL LAYER
   ==========================================================================

   Everything above this line is direction-neutral: the stylesheet was
   converted to LOGICAL PROPERTIES (padding-inline-start, inset-inline-end,
   text-align: start) so that mirroring is the browser's job. That is
   deliberate, and it is why there is no [dir="rtl"] sheet restating every
   rule backwards — a parallel sheet drifts the first time somebody edits one
   and not the other (SOP §6.2).

   What follows is only what genuinely remains after that conversion: the
   per-writing-system typography, the handful of glyphs and widgets that
   encode a direction of their own, and the language switcher.
   ========================================================================== */

/* ---------------------------------------------- fonts per writing system --
   §6.1. Cormorant Garamond and EB Garamond carry Latin AND Cyrillic, so the
   Russian pages need no substitute — this is the Garamond case the SOP notes.

   NEITHER HAS ARABIC. Left alone, an Arabic page falls back to a system font,
   silently, and looks like a different website. Amiri is a naskh revival with
   the same restrained, high-contrast, bookish intent as Cormorant; Noto Naskh
   Arabic holds up at running-text sizes the way EB Garamond does. Both are
   loaded only on the Arabic pages (see src/templates/head.mjs), and every face
   is declared with unicode-range by the Google stylesheet itself, so the
   English pages download exactly what they downloaded before. */
[data-script="arabic"] {
  --display: 'Amiri', 'Noto Naskh Arabic', 'Geeza Pro', 'Times New Roman', serif;
  --body:    'Noto Naskh Arabic', 'Amiri', 'Geeza Pro', 'Segoe UI', Tahoma, sans-serif;

  /* ---------------------------------------------------------------- §6.3
     ARABIC MUST NOT BE LETTER-SPACED. It is cursive: tracking forces gaps
     into the letter joins and the word stops being a word. The labels earn
     their hierarchy from size, weight and colour instead — which is what
     Arabic typography uses in place of the capitalisation it does not have.

     Zeroed at the token, so this reaches the values set inside media queries
     too. That is the whole reason the tokens exist. */
  --track-04: 0;
  --track-06: 0;
  --track-08: 0;
  --track-10: 0;
  --track-12: 0;
  --track-14: 0;
  --track-16: 0;
  --track-18: 0;
  --track-20: 0;
  --track-22: 0;
  --track-24: 0;
  --track-30: 0;
  --track-indent: 0;
}

/* §6.4 — scripts with deep descenders and marks below the baseline need more
   leading than a Latin face at the same size, and naskh headings set in a
   display face sit low in their line box. */
[data-script="arabic"] body { line-height: 1.85; }
[data-script="arabic"] h1,
[data-script="arabic"] h2,
[data-script="arabic"] h3,
[data-script="arabic"] h4,
[data-script="arabic"] .h-hero,
[data-script="arabic"] .h-section,
[data-script="arabic"] .h-card { line-height: 1.45; }
[data-script="arabic"] .travel span:last-child,
[data-script="arabic"] .figure { line-height: 1.35; }

/* ------------------------------------------------------------------- §6.1
   SIMPLIFIED CHINESE — THE ONE SCRIPT HERE THAT LOADS NO WEBFONT.

   That is a decision, not an omission. A Han face is hundreds of kilobytes per
   weight, and the `unicode-range` split that makes the Arabic pair cheap buys
   nothing: Chinese prose draws on characters from across the whole range, so a
   browser would fetch every subset of it. Against that, every device this page
   is read on already ships both faces the design needs, and Chinese has its own
   serif/sans distinction — the Song (宋体) style is its serif, with the stroke
   contrast and triangular terminals that carry Cormorant's role, and the Hei
   (黑体) style is its humanist sans. So these are the reader's own faces:
   Apple first, then Windows, then the Noto and Source Han names for Android,
   Linux and anyone who installed them.

   Latin runs inside the copy — the brand names, and the bare price amounts now
   that the currency is named in Chinese (`550,000 阿联酋迪拉姆`) — are drawn by
   these faces' own Latin glyphs, exactly as they are on the Arabic pages.
   `src/templates/head.mjs` still serves the Latin pair to a Chinese page for
   the same reason it serves it to an Arabic one. */
[data-script="han"] {
  --display: 'Songti SC', 'Noto Serif SC', 'Source Han Serif SC', 'SimSun', Georgia, serif;
  --body: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans SC',
    'Source Han Sans SC', 'Segoe UI', sans-serif;

  /* ---------------------------------------------------------------- §6.3
     HAN MUST NOT BE LETTER-SPACED EITHER, and its case is stronger than
     Arabic's. Arabic cannot be tracked because its letters JOIN. Han cannot be
     tracked because its words do not SEPARATE: Chinese is written with no
     spaces between words, so tracking has nothing to fall between except the
     inside of a word, and a tracked four-character phrase reads as four
     unrelated characters rather than as a spaced label.

     Han is also unicameral, so `text-transform: uppercase` is a no-op on it —
     the case contrast those tracked labels were reinforcing does not exist
     here at all.

     Zeroed at the token, so this reaches the values set inside media queries
     too. */
  --track-04: 0;
  --track-06: 0;
  --track-08: 0;
  --track-10: 0;
  --track-12: 0;
  --track-14: 0;
  --track-16: 0;
  --track-18: 0;
  --track-20: 0;
  --track-22: 0;
  --track-24: 0;
  --track-30: 0;
  --track-indent: 0;
}

/* §6.4 — Han needs more leading than Latin for a different reason than naskh's
   descenders: every character fills its em box to the same height, with no
   ascender-and-descender rhythm to give a line internal air, so a paragraph set
   at the Latin measure closes into a grey mass. Headings need less of the extra
   than the body does, because at display sizes the characters separate
   themselves. */
[data-script="han"] body { line-height: 1.9; }
[data-script="han"] h1,
[data-script="han"] h2,
[data-script="han"] h3,
[data-script="han"] h4,
[data-script="han"] .h-hero,
[data-script="han"] .h-section,
[data-script="han"] .h-card { line-height: 1.4; }
[data-script="han"] .travel span:last-child,
[data-script="han"] .figure { line-height: 1.35; }

/* ------------------------------------------------------------------- §6.4
   WHERE A CHINESE LINE MAY BREAK.

   Chinese may break between ANY two characters. That is right for prose and
   wrong for a measurement, because the units and the currency are WORDS here —
   平方米, 阿联酋迪拉姆 — and the default breaking strands the last character of
   one on a line of its own: `550,000 阿联酋迪拉` + `姆`. A reader sees a typo,
   not a wrap.

   `keep-all` forbids a break inside a run of Han, leaving the spaces and the
   slash as the only opportunities. The other half of the guard is in the copy:
   with `keep-all` in force the space around a figure becomes the ONLY break
   opportunity in the cell, so that is where the line then breaks unless a
   no-break space binds the figure to its counter word. Neither half works
   alone.

   Scoped to the cells that carry a figure. PROSE IS DELIBERATELY LEFT ALONE: a
   Chinese paragraph should break between characters, and forbidding it there
   overflows the column instead of wrapping. */
[data-script="han"] .figure,
[data-script="han"] .instalment__pct,
[data-script="han"] .instalment__when,
[data-script="han"] .plan-choice__size,
[data-script="han"] .units-row span,
[data-script="han"] .travel span:last-child {
  word-break: keep-all;
}

/* text-transform: uppercase is a no-op on Arabic itself, but it would shout
   the Latin brand names embedded in Arabic labels. Left in place deliberately:
   the market publishes those names in Latin capitals anyway, and suppressing
   it here would make one label in an otherwise consistent row look unstyled. */

/* --------------------------------------------------------------- bidi (§6.2)
   Latin runs inside RTL prose — brand names, currency codes, prices — are
   wrapped in <bdi> by the templates. This is the type-level half: the runs
   keep the Latin face rather than inheriting a naskh fallback that has no
   Latin of its own. */
[data-script="arabic"] bdi,
[data-script="arabic"] code {
  font-family: 'EB Garamond', Georgia, 'Times New Roman', serif;
  letter-spacing: normal;
}

/* -------------------------------------------------------------- §6.2 RTL --
   PHONE INPUTS STAY LEFT-TO-RIGHT. A phone number typed right-to-left is
   unreadable, and every bank and airline in the region does this. The input
   also carries dir="ltr" from lead-capture.js — belt and braces, because the
   attribute is what the caret follows and this rule is what the text follows
   if the attribute is ever dropped. */
[dir="rtl"] input[type="tel"],
[dir="rtl"] .iti__tel-input,
[dir="rtl"] .lead-input[type="tel"] {
  direction: ltr;
  text-align: start;
}

/* The intl-tel-input widget builds a left-to-right UI of its own: the flag
   dropdown is absolutely positioned against the input's left edge and the
   input is padded to clear it. Under RTL the flag must move to the right edge
   and the padding with it, or the two overlap (§6.2, third-party widgets). */
[dir="rtl"] .iti { direction: ltr; }

/* ------------------------------------------------------------------ §14.2 --
   A LONG WORD BREAKING A FIXED GRID OUT OF THE VIEWPORT.

   The cell shrinks correctly; shrinking a box does nothing for a word wider
   than the box. German compounds and long Russian inflected forms overflow
   the cell, and the cell overflows the page — the symptom is horizontal
   overflow on some languages only, with the header CTA off the right edge.

   `hyphens` uses the page's lang attribute, which is already set correctly on
   every page. Applied to the fixed-column layouts that carry sentences: the
   payment schedule, the travel-time rows, the spec tables and the stat
   strips. */
.travel span,
.units-row .cell,
.unit-card dt,
.unit-card dd,
.instalment__name,
.agency-stat .micro,
.amenity__title,
.footer-col a,
.footer-col address,
.lead-split__list li,
.header__nav a,
.nav-panel a,
/* HEADINGS TOO, and this is not belt-and-braces — it is the case that
   actually shipped. The privacy page's h1 is capped at 16ch, and in Russian
   the single word «Политика конфиденциальности» measures 331px inside a
   183px box: 46px of horizontal scroll on a 320px phone, from one word, on a
   page with no images and no tables. English never trips it, which is exactly
   why it went unnoticed.

   Inert where the word already fits: `hyphens: auto` only breaks a word that
   cannot fit, so the approved English headings render unchanged. It uses the
   page's `lang`, which is set correctly on every page — that is what makes it
   hyphenate «конфиденци-альности» rather than chop it anywhere. */
.h-hero,
.h-section,
.h-card,
.reason__t,
.unit-card h4,
.faq__q span:first-child,
.legal h1,
.legal h2 {
  overflow-wrap: break-word;
  hyphens: auto;
}

/* The travel row's value column is nowrap by design — a time must not break
   across lines. The NAME column is the one that has to yield. */
.travel span:first-child { min-width: 0; }

/* ------------------------------------------------------------------ §14.6 --
   FLEX ITEMS THAT CANNOT SHRINK. `flex: 1` does not let an item shrink past
   its min-content width; the automatic minimum IS min-content. Two buttons of
   translated text in a fixed-width bar is exactly the shape that eventually
   overflows, so min-width: 0 is set pre-emptively rather than after the bug
   report. */
.footer-bar__side,
.header__right,
.project-card__foot > div,
.duo__text,
.split > * { min-width: 0; }

/* ==========================================================================
   THE LANGUAGE SWITCHER — §5.3
   ==========================================================================

   Built on <details> so it works with JavaScript switched off. On a fixed
   header, a script-dependent menu is the one control that breaks before
   hydration — and this one is how a visitor reaches their own language.
   ========================================================================== */
.lang { position: relative; flex-shrink: 0; }

/* §14.1 — A CONTROL THAT INHERITS A COLOUR PAST ITS CONTAINER.
   The nav beside this declares its colour explicitly, which is why the nav
   looks fine over the hero and a `color: inherit` control would not. This
   takes the same contract, including the transition to the scrolled state, so
   the two can never diverge. */
.lang__btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 2px;
  cursor: pointer;
  list-style: none;
  color: currentColor;
  font-size: 12px;
  letter-spacing: var(--track-18);
  text-transform: uppercase;
  transition: opacity .3s;
}
.lang__btn::-webkit-details-marker { display: none; }
.lang__btn:hover { opacity: .7; }
.lang__cur { line-height: 1; }

/* A stroked SVG path taking currentColor, not a "▾" character: at this size
   the glyph renders as an indistinct dot on some platforms, and most of its
   box is padding, so it cannot simply be sized up (§14.1). */
.lang__chev { flex-shrink: 0; transition: transform .3s var(--ease); }
.lang[open] .lang__chev { transform: rotate(180deg); }

.lang__list {
  position: absolute;
  inset-block-start: calc(100% + 8px);
  inset-inline-end: 0;
  z-index: 120;
  min-width: 168px;
  margin: 0;
  padding: 6px 0;
  list-style: none;
  background: var(--cream);
  color: var(--green);
  border: 1px solid var(--hairline);
  box-shadow: 0 18px 40px rgba(18, 22, 15, .16);

  /* §5.3 — THE MENU NEEDS A STACK THAT CAN DRAW ALL THE ENDONYMS.
     The page's own stack is the ONE script guaranteed not to cover the
     others: an English page loads no Arabic face, so «العربية» would render as
     tofu in the very control a visitor uses to escape a language they cannot
     read. The system faces below are the ones that ship with each platform. */
  font-family: 'EB Garamond', 'Noto Naskh Arabic', 'Geeza Pro', 'Segoe UI',
               Tahoma, Georgia, serif;
  letter-spacing: normal;
  text-transform: none;
  font-size: 15px;
}
.lang__list a {
  display: block;
  padding: 8px 16px;
  color: var(--green);
  white-space: nowrap;
  transition: background .2s, color .2s;
}
.lang__list a:hover { background: var(--grey-alt); }

/* The current language STAYS IN THE LIST. A switcher that hides where you are
   makes the reader hunt for confirmation that they are in the right place. */
.lang__list a[aria-current="true"] {
  color: var(--burgundy);
  background: var(--grey-alt);
}

/* On a phone the switcher must not sit against the header CTA (§12.3). It
   keeps its place in the bar — it is not dropped, because a visitor on the
   wrong language has no other way out — but it gives up its padding first. */
@media (max-width: 900px) {
  .lang__btn { padding-inline: 0; }
  .lang__list { min-width: 150px; }
}
@media (max-width: 420px) {
  .lang__cur { font-size: 11px; }
  .lang__list {
    /* Anchored to the viewport edge rather than the button, so a menu opened
       from a control near the edge cannot open off-screen — which in RTL is
       the opposite edge, and is why this is an inline offset and not a right. */
    inset-inline-end: calc(var(--pad) * -1 + 8px);
  }
}
