/* ============================================================================
   Vaishnavi AT-One Life Phase 4 — "Canopy"
   sites/vaishnavi-at-one-life-ph4/css/styles.css

   Palette: the nine canonical brand tokens, copied verbatim from
   builders/vaishnavi-group/vaishnavi-group.json → branding.colors.
   Nothing outside :root carries a colour literal; every tint, shade and
   translucency below is a color-mix() over two of those nine (plus the two
   form-state colours, which are deliberately NOT folded into the brand hue —
   a navy "error" is not readable as an error).

   THE SYSTEM. 227 plots on 16.12 acres is a story about open ground and a
   layout, so the page rhythm carries the acreage rather than the photography
   carrying it. Three moves:

     1. A wide band, a narrow column. Every section is a full-bleed band of
        ground colour; inside it the running text sits on a 44rem measure
        while tables, galleries and plan grids break out to 64rem. The
        alternation between the two widths IS the signature.
     2. Hairline accents, never boxes. A 3px navy rule marks an h3, a
        blockquote and a stat; a 2px navy rule sits above an h2 and is what
        .gold-divider paints. There are no drop shadows anywhere — depth comes
        from tinted ground, exactly as on a site plan.
     3. Soft 18px geometry on every photographic surface, pill geometry on
        every control, and generous vertical rhythm (clamp 3–5.5rem per band)
        so the page breathes like the 47.8% open area it describes.

   TYPE. Playfair Display (self-hosted variable, 38 KB) carries display only —
   h1, h2, .section-title, .num. Inter (self-hosted variable, 48 KB) carries
   everything else. Both files are already preloaded by the generated <head>,
   so using both is what justifies those two preloads. font-display: optional
   with metric-matched local fallbacks: if a face misses its block window the
   fallback is kept for the life of the page and there is no swap, so the
   hero cannot shift under a late font.

   COLOUR ROLES, and the grounds each ink was measured against
   (WCAG 2.1 contrast, computed not assumed — see the table in the notes):

     ground   --light-secondary  (white)      .bg-white, cards, nav, FAQ
     ground   --brand-tertiary   (pale wash)  .bg-light, .page-hero, #contact
     ground   --brand-secondary  (deep navy)  .closing-cta, #footer, table heads
     body ink --dark-primary     17.93:1 / 16.02:1 on the two light grounds
     soft ink --dark-secondary    8.86:1 /  7.92:1
     accent   --brand-primary    12.03:1 / 10.74:1  links, rules, primary CTA
     deep     --brand-secondary  15.49:1 / 13.84:1  and as the dark ground

   ⚠ The three dark-band grounds invert EVERYTHING: body, links, <strong>,
   <em>, <code>, borders, .page-updated and the focus ring. `strong` is
   therefore never given a global colour in this sheet — it inherits, and is
   coloured only inside the light contexts that need it. That single decision
   removes the fleet's most repeated dark-band bug.
   ========================================================================== */


/* ══════════════════════════════════════════════════════════ 1. FONT FACES ══ */

@font-face {
  font-family: "Inter";
  src: url("../assets/fonts/inter-var.woff2") format("woff2");
  font-weight: 100 900;              /* file's real wght axis is 100–900 */
  font-style: normal;
  font-display: optional;
}

@font-face {
  font-family: "Playfair Display";
  src: url("../assets/fonts/playfair-var.woff2") format("woff2");
  font-weight: 400 900;              /* Playfair's axis starts at 400 — never ask for less */
  font-style: normal;
  font-display: optional;
}

/* Metric-matched fallbacks. With font-display: optional a slow face is never
   swapped in, so these are what most first-visit readers actually see; the
   overrides keep their line boxes the same height as the webfont's. */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial");
  ascent-override: 90.00%;
  descent-override: 22.43%;
  line-gap-override: 0.00%;
  size-adjust: 107.12%;
}
@font-face {
  font-family: "Playfair Display Fallback";
  src: local("Georgia");
  ascent-override: 97.25%;
  descent-override: 22.56%;
  line-gap-override: 0.00%;
  size-adjust: 109.94%;
}


/* ═════════════════════════════════════════════════════════════ 2. TOKENS ══ */

:root {
  /* ---- The nine canonical brand tokens. Source of truth:
         builders/vaishnavi-group/vaishnavi-group.json → branding.colors ---- */
  --brand-primary:   #012C8D;
  --brand-secondary: #011E60;
  --brand-tertiary:  #F0F2F8;
  --dark-primary:    #171717;
  --dark-secondary:  #4a4a4a;
  --dark-tertiary:   #d8d8d8;
  --light-primary:   #f8f8f8;
  --light-secondary: #ffffff;
  --light-tertiary:  #eeeeee;

  /* ---- The two permitted exceptions: form validation state. Folding these
         into the navy would make "invalid" and "submit" the same colour. ---- */
  --state-success: #166534;
  --state-error:   #991b1b;

  /* ---- Geometry and rhythm (no colour here) ---- */
  --shell:    75rem;     /* 1200px — outer frame for nav and footer          */
  --measure:  64rem;     /* 1024px — the wide band: tables, gallery, plans   */
  --column:   44rem;     /*  704px — the reading measure, ~72 characters     */
  --gutter:   clamp(1.15rem, 4.5vw, 2.75rem);
  --rhythm:   clamp(3rem, 7vw, 5.5rem);
  --navh:     68px;
  --radius:   18px;      /* photographic surfaces, cards, bands             */
  --radius-sm: 10px;     /* inputs, chips, small panels                     */
  --pill:     999px;
  --rule:     3px;       /* the hairline accent weight — the theme's motif   */

  --font-display: "Playfair Display", "Playfair Display Fallback", ui-serif, Georgia, "Times New Roman", serif;
  --font-body: "Inter", "Inter Fallback", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;


  /* Aliases for the generated related-projects block, which reads
     --font-body / --font-headline / --font-display. Without these it
     falls back to Georgia and breaks the page's type. */
  --font-headline: var(--font-display);
}


/* ══════════════════════════════════════════════════════════════ 3. RESET ══ */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--navh) + 1rem);
}

body {
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.72;
  color: var(--dark-primary);
  background: var(--light-secondary);
  -webkit-font-smoothing: antialiased;
  /* A RERA id, a survey-number string and a routing URL all appear in running
     text; without this the body scrolls sideways at 360px. */
  overflow-wrap: break-word;
}

img, picture, svg, video { display: block; max-width: 100%; }
img { height: auto; background-color: color-mix(in srgb, var(--brand-primary) 6%, var(--light-tertiary)); }

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: 0; }

ul, ol { padding-left: 1.25rem; }

table { border-collapse: collapse; }

address { font-style: normal; }

/* Focus. Navy reads at 12.03:1 on white and 10.74:1 on the wash — far past
   the 3:1 non-text minimum. Dark bands re-declare it in white further down. */
:focus-visible {
  outline: 3px solid var(--brand-primary);
  outline-offset: 2px;
  border-radius: 2px;
}


/* ═════════════════════════════════════════════════════════ 4. TYPOGRAPHY ══ */

h1, h2, h3, h4 {
  line-height: 1.18;
  text-wrap: balance;
  color: var(--dark-primary);
}

h1, h2 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.015em;
}

h1 { font-size: clamp(2rem, 5.4vw, 3.15rem); line-height: 1.08; }
h2 { font-size: clamp(1.6rem, 3.2vw, 2.35rem); line-height: 1.16; }

/* h3 is deliberately in the OTHER family — sans, tight, with the theme's 3px
   navy hairline at its left edge. The register change is what separates a
   sub-head from a section head without adding a second display size. */
h3 {
  font-family: var(--font-body);
  font-size: clamp(1.02rem, 1.6vw, 1.15rem);
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: -0.005em;
  color: var(--brand-secondary);        /* 15.49:1 on white, 13.84:1 on the wash */
}

h4 { font-family: var(--font-body); font-size: 1rem; font-weight: 700; }

p { text-wrap: pretty; }

a {
  color: var(--brand-primary);          /* 12.03:1 on white, 10.74:1 on the wash */
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}
/* Hover moves navy → deeper navy AND thickens the rule. The colour step alone
   (12.03 → 15.49) is legible but subtle; the rule change is what makes the
   hover unmistakable without a hue the palette does not own. */
a:hover {
  color: var(--brand-secondary);
  text-decoration-thickness: 2px;
}

strong { font-weight: 650; }            /* no colour — see the header note */
em { font-style: italic; }

code {
  font-family: var(--font-mono);
  font-size: 0.86em;
  background: color-mix(in srgb, var(--brand-primary) 8%, var(--light-secondary));
  color: var(--brand-secondary);        /* 13.34:1 on that tint */
  padding: 0.1em 0.36em;
  border-radius: 4px;
  /* RERA ids are 36 unbroken characters. Without this they set the minimum
     page width on a 360px phone. */
  overflow-wrap: anywhere;
}

blockquote {
  border-left: var(--rule) solid var(--brand-primary);
  padding: 0.15rem 0 0.15rem 1.15rem;
  color: var(--dark-secondary);         /* 8.86:1 on white, 7.92:1 on the wash */
}
blockquote p + p { margin-top: 0.85rem; }

hr {
  border: 0;
  height: 1px;
  background: color-mix(in srgb, var(--brand-primary) 25%, var(--light-tertiary));
  margin-block: 2.5rem;
}

.page-updated {
  font-size: 0.82rem;
  letter-spacing: 0.01em;
  color: var(--dark-secondary);         /* 8.86:1 / 7.92:1 */
}
.page-updated time { font-weight: 650; }

.lead {
  font-size: clamp(1.02rem, 1.5vw, 1.12rem);
  line-height: 1.68;
  color: var(--dark-secondary);         /* 7.92:1 on the --brand-tertiary hero ground */
}
.lead strong { color: var(--dark-primary); }

/* Section head furniture, shared by the FAQ header and the contact block. */
.section-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--brand-primary);          /* 12.03:1 on white, 10.74:1 on the wash */
}

.section-title {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 3.2vw, 2.35rem);
  font-weight: 600;
  letter-spacing: -0.015em;
  margin-top: 0.55rem;
  color: var(--dark-primary);
}

/* The motif at its plainest: a 2px navy rule. Named "gold" by the generator;
   this theme has no gold, and navy is what the rule is everywhere else. */
.gold-divider {
  width: 64px;
  height: 2px;
  background: var(--brand-primary);
  border-radius: var(--pill);
  margin-top: 1.15rem;
}


/* ═════════════════════════════════════════════════════════════ 5. LAYOUT ══ */

/* A neutral centring wrapper. Applied by the generator alongside
   .gallery-grid and .contact-inner, so it must not add its own padding —
   .gallery-grid already sits inside a gutter-padded column. */
.container {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
}

#main-content { display: block; scroll-margin-top: calc(var(--navh) + 1rem); }

/* Every in-page anchor target clears the sticky nav. */
#hero,
#section-overview,
#section-location,
#section-master-plan,
#section-plot-options,
#section-floor-plans,
#section-gallery,
#section-amenities,
#section-pricing,
#section-reviews,
#section-builder,
#section-cta,
#faq,
#contact { scroll-margin-top: calc(var(--navh) + 1rem); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 300;
  background: var(--brand-secondary);
  color: var(--light-secondary);        /* 15.49:1 */
  padding: 0.8rem 1.35rem;
  font-weight: 650;
  text-decoration: none;
  border-radius: 0 0 var(--radius-sm) 0;
}
.skip-link:focus { left: 0; }
/* ⚠ The ring has to be INSET here. The chip is navy and it reveals itself on
   the white page, so a white ring at the global +2px offset would be painted
   on that white page at 1.00:1 — a focus indicator that does not exist. A
   negative offset puts it inside the navy chip: 15.49:1. */
.skip-link:focus-visible {
  outline-color: var(--light-secondary);
  outline-offset: -4px;
}
/* ⚠ The skip link is an <a>, so the global `a:hover` (0,1,1) outranks the bare
   `.skip-link` (0,1,0) and would repaint this label in --brand-secondary — on
   a --brand-secondary ground, i.e. 1.00:1, the control gone. Restated at
   (0,2,0) so the cascade cannot take it. */
.skip-link:hover,
.skip-link:focus:hover { color: var(--light-secondary); }   /* 15.49:1 */


/* ═══════════════════════════════════════════════════ 6. NAV + MOBILE MENU ══ */

#nav {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  height: var(--navh);
  width: 100%;
  max-width: 100%;
  padding-inline: var(--gutter);
  background: var(--light-secondary);
  border-bottom: 1px solid color-mix(in srgb, var(--brand-primary) 16%, var(--light-tertiary));
}
/* No shadow on scroll — this theme gets its depth from ground colour. The
   scrolled state deepens the hairline and tints the bar instead. */
#nav.scrolled {
  background: color-mix(in srgb, var(--brand-primary) 4%, var(--light-secondary));
  border-bottom-color: color-mix(in srgb, var(--brand-primary) 34%, var(--light-tertiary));
}

.nav-logo,
.footer-logo {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin-right: auto;
  text-decoration: none;
  color: inherit;
  min-width: 0;
}

.brand-logo-img {
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  object-fit: contain;
  background: none;                     /* the global img tint would box the mark */
}

/* inline-flex column, not two block spans: blockified flex items keep a
   word boundary between the developer name and the project name, so the
   link's accessible name stays "Vaishnavi Group Vaishnavi AT-One Life
   Phase 4" rather than one run-on token. */
.brand-text {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1.2;
  min-width: 0;
}
.brand-developer {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--brand-primary);          /* 12.03:1 on the white bar */
}
.brand-project {
  font-size: 0.95rem;
  font-weight: 650;
  letter-spacing: -0.015em;
  color: var(--dark-primary);           /* 17.93:1 */
}

.nav-links {
  display: none;                        /* desktop nav appears at 1024 */
  list-style: none;
  padding: 0;
  margin: 0;
  gap: 1.15rem;
}
.nav-links a {
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--dark-primary);           /* 17.93:1 */
  text-decoration: none;
  padding-block: 0.35rem;
  border-bottom: 2px solid transparent;
}
/* Hover keeps the AA-safe navy and adds a rule, so the state survives a
   grayscale render as well as a colour one. */
.nav-links a:hover {
  color: var(--brand-primary);          /* 12.03:1 */
  border-bottom-color: var(--brand-primary);
}

.nav-cta {
  display: none;                        /* pairs with .nav-links at 1024 */
  align-items: center;
  background: var(--brand-primary);
  color: var(--light-secondary);        /* 12.03:1 */
  font-size: 0.86rem;
  font-weight: 650;
  text-decoration: none;
  padding: 0.55rem 1.2rem;
  border-radius: var(--pill);
  transition: background 180ms ease;
}
.nav-cta:hover { background: var(--brand-secondary); color: var(--light-secondary); } /* 15.49:1 */

.hamburger {
  display: grid;
  place-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  margin-left: auto;
  flex: 0 0 auto;
  color: var(--brand-secondary);
}
.hamburger span {
  display: block;
  width: 22px;
  height: 2px;
  background: currentColor;
  transition: transform 200ms ease, opacity 160ms ease;
}
.hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger.open span:nth-child(2) { opacity: 0; }
.hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

#mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  padding: calc(var(--navh) + 0.5rem) var(--gutter) 2rem;
  background: var(--light-secondary);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* visibility, not display: the drawer can transition, and a closed drawer
     is still removed from the tab order. */
  visibility: hidden;
  transform: translateX(100%);
  transition: transform 260ms ease, visibility 0s linear 260ms;
}
#mobile-menu.open {
  visibility: visible;
  transform: translateX(0);
  transition: transform 260ms ease, visibility 0s;
}
#mobile-menu a {
  padding-block: 0.95rem;
  font-size: 1.02rem;
  font-weight: 550;
  color: var(--dark-primary);           /* 17.93:1 */
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--brand-primary) 16%, var(--light-tertiary));
}
#mobile-menu a:hover { color: var(--brand-primary); }  /* 12.03:1 */

#mobile-menu-close,
.menu-close {
  position: absolute;
  top: 0.85rem;
  right: var(--gutter);
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  font-size: 1.9rem;
  line-height: 1;
  border-radius: var(--pill);
  color: var(--brand-secondary);        /* 15.49:1 on white */
}
.menu-close:hover {
  background: color-mix(in srgb, var(--brand-primary) 10%, var(--light-secondary));
  color: var(--brand-secondary);        /* 12.86:1 on that tint */
}

/* ⚠ The id-scoped `#mobile-menu a` (1,0,1) outranks a bare `.menu-cta`
   (0,1,0), so the drawer's link ink — near-black — would land on this
   button's navy fill at 1.50:1. The RESTING state needs the id too, not just
   the hover one. (No audit that walks the page catches this: the drawer is
   visibility:hidden until it is opened, so every contrast crawler skips it.) */
#mobile-menu .menu-cta,
.menu-cta {
  margin-top: 1.5rem;
  text-align: center;
  background: var(--brand-primary);
  color: var(--light-secondary);        /* 12.03:1 */
  font-weight: 650;
  border-radius: var(--pill);
  border-bottom: 0 !important;          /* beats the #mobile-menu a hairline */
  padding: 0.85rem 1.4rem !important;
}
/* ⚠ `#mobile-menu a:hover` is (1,1,1) and outranks a bare `.menu-cta:hover`
   at (0,2,0), so the drawer's link-hover navy would land on this button's
   navy fill at 1.29:1 — the control gone. Re-stated WITH the id, at (1,2,0),
   so the cascade cannot take it. */
#mobile-menu .menu-cta:hover,
.menu-cta:hover { background: var(--brand-secondary); color: var(--light-secondary); } /* 15.49:1 */


/* ════════════════════════════════════════════════════════════ 7. BUTTONS ══ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  padding: 0.8rem 1.55rem;
  border: 1px solid transparent;
  border-radius: var(--pill);
  font-size: 0.92rem;
  font-weight: 650;
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  transition: background 180ms ease, color 180ms ease, border-color 180ms ease;
}

/* Primary. Generator calls it "gold"; this palette's primary is navy. */
.btn-gold {
  background: var(--brand-primary);
  color: var(--light-secondary);        /* 12.03:1 */
  border-color: var(--brand-primary);
}
.btn-gold:hover,
.btn-gold:focus-visible {
  background: var(--brand-secondary);   /* 15.49:1 — both states pass */
  color: var(--light-secondary);
  border-color: var(--brand-secondary);
}

/* Secondary on a light ground: navy outline, navy label. Used in the homepage
   hero card (which is an OPAQUE light card, not the photograph) and in the
   subpage hero, so one light-ground definition serves both. */
.btn-outline {
  background: transparent;
  color: var(--brand-primary);          /* 12.03:1 white / 10.74:1 wash */
  border-color: var(--brand-primary);
}
.btn-outline:hover,
.btn-outline:focus-visible {
  background: var(--brand-primary);
  color: var(--light-secondary);        /* 12.03:1 inverted — still passes */
}

/* Tertiary, used in the in-page .cta-row pairs. The border is
   --dark-secondary at 8.86:1 on white and 7.92:1 on the wash, well past the
   3:1 minimum for a control boundary; a navy tint light enough to read as
   "quiet" could not clear 3:1 on the wash, which is why this one is neutral. */
.btn-outline-dark {
  background: transparent;
  color: var(--dark-primary);           /* 17.93:1 / 16.02:1 */
  border-color: var(--dark-secondary);
}
.btn-outline-dark:hover,
.btn-outline-dark:focus-visible {
  background: var(--dark-primary);
  color: var(--light-secondary);        /* 17.93:1 inverted */
  border-color: var(--dark-primary);
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2.25rem;
  max-width: var(--column);
}


/* ═══════════════════════════════════════════════════════ 8. HOMEPAGE HERO ══ */

/* One composition at every width: the photograph fills the section and an
   OPAQUE light card sits on it. Because the card is opaque, everything inside
   it is measured against --light-secondary — no text is ever judged against a
   photograph, which is the whole reason for the card. */
#hero {
  position: relative;
  display: grid;
  align-items: center;
  overflow: hidden;
  background: var(--brand-secondary);   /* shows while the hero image decodes */
}

.hero-bg,
.hero-overlay,
.hero-content { grid-area: 1 / 1; }

.hero-bg {
  position: relative;
  align-self: stretch;
  overflow: hidden;
}
/* A stretched grid item is not a definite height for a percentage to resolve
   against, so height:100% would fall back to auto and the 1200² source would
   paint at its intrinsic square and set the row. Out of flow instead. */
.hero-bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-overlay {
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--brand-secondary) 34%, transparent) 0%,
    color-mix(in srgb, var(--brand-secondary) 10%, transparent) 42%,
    color-mix(in srgb, var(--brand-secondary) 58%, transparent) 100%
  );
}

.hero-content {
  position: relative;
  z-index: 2;
  /* The top inset is the photograph's share of the section. Phones give it
     the most, because their card is the tallest relative to the screen. */
  margin: clamp(150px, 26svh, 240px) auto clamp(1.25rem, 3svh, 3rem);
  width: min(var(--measure), calc(100% - (var(--gutter) * 2)));
  background: var(--light-secondary);
  border-radius: var(--radius);
  padding: clamp(1.4rem, 4vw, 2.5rem);
}

.hero-badge {
  display: inline-block;
  background: color-mix(in srgb, var(--brand-primary) 10%, var(--light-secondary));
  color: var(--brand-secondary);        /* 12.86:1 on that tint */
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  padding: 0.42rem 0.95rem;
  border-radius: var(--pill);
}

.hero-title {
  margin-top: 1rem;
  font-family: var(--font-display);
  font-size: clamp(1.95rem, 6vw, 3.15rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.06;
  color: var(--dark-primary);           /* 17.93:1 */
}

.hero-content .page-updated { margin-top: 0.85rem; }

.hero-sub {
  margin-top: 1.05rem;
  font-size: clamp(0.96rem, 1.4vw, 1.04rem);
  line-height: 1.68;
  color: var(--dark-secondary);         /* 8.86:1 */
  max-width: 58ch;
}
.hero-sub strong { color: var(--dark-primary); }  /* 17.93:1 — set, never inherited */

.hero-btns {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.6rem;
}

.hero-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1.6rem;
}
/* Five short facts read better as a wrapped strip of chips than as a grid —
   a 5-column grid on a 700px card gives a 7-character column. */
.hero-stat {
  background: color-mix(in srgb, var(--brand-primary) 8%, var(--light-secondary));
  color: var(--brand-secondary);        /* 13.34:1 on that tint */
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 0.45rem 0.9rem;
  border-radius: var(--pill);
}


/* ════════════════════════════════════════════════════════ 9. SUBPAGE HERO ══ */

/* Wash ground rather than a full-bleed photograph: a subpage should read as a
   subpage. The image becomes an 18px plate beside the text at 1024. */
.page-hero {
  background: var(--brand-tertiary);
  padding-block: clamp(2.25rem, 5vw, 4rem);
  border-bottom: 1px solid color-mix(in srgb, var(--brand-primary) 16%, var(--light-tertiary));
}

/* Flex, not grid: 404.html ships this block with the text child ONLY, and a
   two-column grid would leave half the band empty there. */
.page-hero-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(1.75rem, 4vw, 3rem);
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.page-hero-inner > * { flex: 1 1 20rem; min-width: 0; }

.page-hero h1 { color: var(--dark-primary); }         /* 16.02:1 on the wash */
.page-hero .page-updated { margin-top: 0.8rem; }
.page-hero .lead { margin-top: 1rem; }

.page-hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.6rem;
}

.page-hero-image { flex: 1 1 18rem; }
.page-hero-image img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius);
}

.page-hero-stats {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
  margin-top: 1.9rem;
}
/* The motif again: a 3px navy hairline carrying a number. */
.page-hero-stat {
  border-left: var(--rule) solid var(--brand-primary);
  padding-left: 0.85rem;
}
.num {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 2.2vw, 1.5rem);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.015em;
  /* Playfair ships proportional figures by default, which makes "227 of 899"
     and "13 May 2026" sit at inconsistent heights next to each other. */
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--brand-secondary);        /* 13.84:1 on the wash */
}
.lbl {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--dark-secondary);         /* 7.92:1 on the wash */
}


/* ════════════════════════════════════════════════ 10. PROSE BANDS + RHYTHM ══ */

.prose-section { padding-block: var(--rhythm); }

/* The two light grounds. Alternated by the generator down the page; the wash
   is --brand-tertiary, which is the only brand token light enough to be a
   ground and is what gives the whole page its faint navy cast. */
.bg-white { background: var(--light-secondary); }
.bg-light { background: var(--brand-tertiary); }

/* Base column. Always paired with .wide by the generator, so .wide is what
   actually sets the band width; .prose-inner sets everything else. */
.prose-inner {
  width: 100%;
  max-width: var(--column);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wide { max-width: var(--measure); }

/* THE RHYTHM. Running text stays on the 44rem measure and is centred inside
   the 64rem band; tables, galleries, plan grids and figures break out to the
   full band. The alternation is the theme. */
.prose-inner > p,
.prose-inner > h2,
.prose-inner > h3,
.prose-inner > h4,
.prose-inner > ul,
.prose-inner > ol,
.prose-inner > blockquote,
.prose-inner > hr,
.prose-inner > .cta-row {
  max-width: var(--column);
  margin-inline: auto;
}

.prose-inner > * + * { margin-top: 1.15rem; }
.prose-inner > h2 { margin-top: 2.9rem; }
.prose-inner > h2:first-child { margin-top: 0; }
.prose-inner > h3 { margin-top: 2.4rem; }
.prose-inner > .table-wrap,
.prose-inner > .gallery-grid,
.prose-inner > .plan-grid,
.prose-inner > .content-figure { margin-top: 1.9rem; }

/* The section-head ornament: a 2px navy rule above every in-prose h2. Applied
   through ::before so it needs no wrapper the generator does not emit. */
.prose-inner > h2::before {
  content: "";
  display: block;
  width: 64px;
  height: 2px;
  background: var(--brand-primary);
  border-radius: var(--pill);
  margin-bottom: 1.15rem;
}

/* h3 wears the 3px hairline instead — same motif, smaller scale. */
.prose-inner > h3 {
  border-left: var(--rule) solid var(--brand-primary);
  padding-left: 0.9rem;
}

.prose-inner li + li { margin-top: 0.5rem; }
.prose-inner li > ul,
.prose-inner li > ol { margin-top: 0.5rem; }
.prose-inner ::marker { color: var(--brand-primary); }

/* Prose sits only ever on --light-secondary or --brand-tertiary, so a dark
   ink here is safe. Stated explicitly rather than globally on `strong`. */
.prose-inner strong { color: var(--dark-primary); }   /* 17.93:1 / 16.02:1 */

/* On the wash band, white-backed inserts need to stay white. */
.bg-light .table-wrap,
.bg-light .plan-figure,
.bg-light .content-figure { background: var(--light-secondary); }


/* ═════════════════════════════════════════════════════════════ 11. TABLES ══ */

.table-wrap {
  width: 100%;
  overflow-x: auto;                     /* the table scrolls, never the page */
  -webkit-overflow-scrolling: touch;
  background: var(--light-secondary);
  border: 1px solid color-mix(in srgb, var(--brand-primary) 20%, var(--light-tertiary));
  border-radius: var(--radius);
}

.data-table {
  width: 100%;
  min-width: 38rem;                     /* forces the scroller rather than a crushed 360px table */
  font-size: 0.92rem;
  line-height: 1.6;
}

.data-table th,
.data-table td {
  padding: 0.75rem 1rem;
  text-align: left;
  vertical-align: top;
}

/* Navy head band, white label — 15.49:1, and it repeats the closing-CTA and
   footer ground so the page reads as one system. */
.data-table thead th {
  background: var(--brand-secondary);
  color: var(--light-secondary);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  white-space: nowrap;
}

.data-table td {
  color: var(--dark-primary);           /* 17.93:1 on white, 16.70:1 on the zebra tint */
  border-top: 1px solid color-mix(in srgb, var(--brand-primary) 14%, var(--light-tertiary));
}
.data-table tbody tr:nth-child(even) td {
  background: color-mix(in srgb, var(--brand-primary) 4%, var(--light-secondary));
}
.data-table td strong { color: var(--brand-secondary); }   /* 15.49:1 / 14.43:1 */
.data-table td code { font-size: 0.82em; }


/* ══════════════════════════════════════════════════ 12. FIGURES AND PLANS ══ */

.plan-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.25rem;
  justify-items: center;
}

/* Both figures carry an inline max-width:720px from the generator, which is
   exactly the source width — so a 720px plan is never upscaled into a 1024px
   band and never goes soft. */
.plan-figure,
.content-figure {
  width: 100%;
  margin-inline: auto;
  background: var(--light-secondary);
  border: 1px solid color-mix(in srgb, var(--brand-primary) 20%, var(--light-tertiary));
  border-radius: var(--radius);
  overflow: hidden;
}
.plan-figure img,
.content-figure img { width: 100%; }

.plan-figure figcaption,
.content-figure figcaption {
  padding: 0.9rem 1.1rem 1.15rem;
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--dark-secondary);         /* 8.86:1 on the white figure ground */
  border-top: 1px solid color-mix(in srgb, var(--brand-primary) 14%, var(--light-tertiary));
}


/* ════════════════════════════════════════════════════════════ 13. GALLERY ══ */

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

/* The tiles are keyboard-operable (script.js sets role=button + tabindex),
   so they need a real focus ring and a focus-visible overlay, not just hover. */
.gallery-item {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
  background: color-mix(in srgb, var(--brand-primary) 6%, var(--light-tertiary));
}
.gallery-item img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  transition: transform 420ms ease;
}
.gallery-item:hover img,
.gallery-item:focus-visible img { transform: scale(1.04); }
.gallery-item:focus-visible { outline-offset: 3px; }

.gallery-item-overlay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--brand-secondary) 62%, transparent);
  opacity: 0;
  transition: opacity 220ms ease;
}
.gallery-item:hover .gallery-item-overlay,
.gallery-item:focus-visible .gallery-item-overlay { opacity: 1; }

/* The icon carries its own opaque white disc, so the magnifier reads at
   12.03:1 regardless of how bright the photograph under the scrim is. */
.gallery-zoom-icon {
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
  border-radius: var(--pill);
  background: var(--light-secondary);
  color: var(--brand-primary);
}
/* The generator's inline SVGs carry no fill/stroke attributes — without this
   every circle and line paints as a solid black blob. */
.gallery-zoom-icon svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}


/* ═════════════════════════════════════════════════════════════════ 14. FAQ ══ */

#faq {
  background: var(--light-secondary);
  padding-block: var(--rhythm);
}

.faq-inner {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.faq-header { text-align: center; margin-bottom: 2.25rem; }
.faq-header .gold-divider { margin-inline: auto; }
#faq-heading { color: var(--dark-primary); }          /* 17.93:1 */

.faq-list {
  display: grid;
  gap: 0.55rem;
  max-width: var(--column);
  margin-inline: auto;
}

.faq-item {
  border: 1px solid color-mix(in srgb, var(--brand-primary) 28%, var(--light-tertiary));
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--light-secondary);
}
.faq-item.open {
  border-color: var(--brand-primary);
  background: color-mix(in srgb, var(--brand-primary) 4%, var(--light-secondary));
}

.faq-q {
  width: 100%;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.15rem;
  text-align: left;
  font-size: 0.98rem;
  font-weight: 650;
  line-height: 1.5;
  color: var(--dark-primary);           /* 17.93:1 / 16.70:1 when open */
}
.faq-q:hover { color: var(--brand-primary); }         /* 12.03:1 — hover verified */
.faq-q:focus-visible { outline-offset: -3px; }        /* inset, so the ring is not clipped */
.faq-q svg {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin-top: 0.25rem;
  fill: none;                           /* the bare <polyline> would fill black */
  stroke: var(--brand-primary);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 220ms ease;
}
.faq-item.open .faq-q svg { transform: rotate(180deg); }

/* max-height, not grid-template-rows 0fr→1fr: the latter has left panels
   pinned shut in this fleet while the class and aria-expanded both flipped.
   Padding lives on the paragraphs, not on the animated box, so nothing has to
   transition a padding value. */
.faq-a {
  max-height: 0;
  overflow: hidden;
  transition: max-height 280ms ease;
}
.faq-item.open .faq-a { max-height: 240rem; }
.faq-a p {
  padding-inline: 1.15rem;
  font-size: 0.94rem;
  color: var(--dark-secondary);         /* 8.86:1 / 8.26:1 on the open tint */
}
.faq-a p + p { margin-top: 0.85rem; }
.faq-a p:last-child { padding-bottom: 1.15rem; }
.faq-a strong { color: var(--dark-primary); }         /* set explicitly, never inherited */


/* ══════════════════════════════════════════════════════ 15. CLOSING CTA ⚠ ══ */

/* DARK BAND. Everything inside is re-inked. */
.closing-cta {
  background: var(--brand-secondary);
  padding-block: var(--rhythm);
  color: color-mix(in srgb, var(--light-secondary) 82%, var(--brand-secondary)); /* 10.64:1 */
}

.closing-cta-inner {
  width: 100%;
  max-width: var(--column);
  margin-inline: auto;
  padding-inline: var(--gutter);
  text-align: center;
}

.closing-cta h2 { color: var(--light-secondary); }    /* 15.49:1 */
.closing-cta h2::before {
  content: "";
  display: block;
  width: 64px;
  height: 2px;
  background: var(--light-secondary);   /* the navy rule would vanish here */
  border-radius: var(--pill);
  margin: 0 auto 1.15rem;
}
.closing-cta p { margin-top: 0.9rem; }
.closing-cta strong,
.closing-cta em { color: var(--light-secondary); }
.closing-cta a { color: var(--light-secondary); }
.closing-cta .page-updated { color: color-mix(in srgb, var(--light-secondary) 80%, var(--brand-secondary)); }
.closing-cta :focus-visible { outline-color: var(--light-secondary); }  /* navy-on-navy would be invisible */

/* ⚠ The primary button is navy. On a navy band that is navy-on-navy at
   1.00:1 — the whole control disappears. It inverts here: light fill, navy
   label, 15.49:1, and the hover keeps 13.84:1. */
.closing-cta .btn-gold,
.closing-cta .btn {
  background: var(--light-secondary);
  color: var(--brand-secondary);
  border-color: var(--light-secondary);
  margin-top: 1.75rem;
}
.closing-cta .btn-gold:hover,
.closing-cta .btn-gold:focus-visible,
.closing-cta .btn:hover,
.closing-cta .btn:focus-visible {
  background: var(--brand-tertiary);
  color: var(--brand-secondary);        /* 13.84:1 */
  border-color: var(--brand-tertiary);
}


/* ═══════════════════════════════════════════════════════ 16. CONTACT FORM ══ */

#contact {
  background: var(--brand-tertiary);
  padding-block: var(--rhythm);
}

.contact-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2.5rem;
  padding-inline: var(--gutter);
}

.contact-info > p,
.contact-info > ul { margin-top: 1.1rem; }
.contact-info strong { color: var(--dark-primary); }  /* 16.02:1 on the wash */
.contact-info li + li { margin-top: 0.5rem; }

.contact-detail {
  display: grid;
  gap: 1rem;
  margin-top: 1.9rem;
}
.contact-detail-item {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
}
.contact-detail-icon {
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--brand-primary) 10%, var(--light-secondary));
  color: var(--brand-primary);
}
.contact-detail-icon svg {
  width: 20px;
  height: 20px;
  fill: none;                           /* bare <path>/<rect>/<circle>, same trap as the gallery icon */
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.contact-detail-text {
  font-size: 0.92rem;
  line-height: 1.62;
  color: var(--dark-secondary);         /* 7.92:1 on the wash */
}
.contact-detail-text strong {
  display: block;                       /* the label sits on its own line, then a run of <br> text */
  margin-bottom: 0.15rem;
  color: var(--dark-primary);           /* 16.02:1 */
}

.form-card {
  background: var(--light-secondary);
  border: 1px solid color-mix(in srgb, var(--brand-primary) 20%, var(--light-tertiary));
  border-radius: var(--radius);
  padding: clamp(1.35rem, 3.5vw, 2.1rem);
  align-self: start;
}

.form-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0 0.9rem;
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-bottom: 0.9rem;
}
.form-group label {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--dark-primary);           /* 17.93:1 on the white card */
}
.form-group input,
.form-group textarea {
  width: 100%;
  padding: 0.75rem 0.9rem;
  font-size: 0.95rem;
  color: var(--dark-primary);
  background: var(--light-secondary);
  /* 3.75:1 on the white card — a field boundary is the only thing marking the
     control, so it has to clear the 3:1 non-text minimum on its own. */
  border: 1px solid color-mix(in srgb, var(--brand-secondary) 45%, var(--dark-tertiary));
  border-radius: var(--radius-sm);
}
.form-group textarea { min-height: 6.5rem; resize: vertical; }
.form-group input::placeholder,
.form-group textarea::placeholder {
  color: var(--dark-secondary);         /* 8.86:1 — placeholders are text too */
  opacity: 1;
}
.form-group input:focus-visible,
.form-group textarea:focus-visible {
  outline: 3px solid var(--brand-primary);
  outline-offset: 0;
  border-color: var(--brand-primary);
}
.form-group input.error,
.form-group textarea.error { border-color: var(--state-error); border-width: 2px; }

/* Ships in the markup with its message already written; it must stay hidden
   until contact-form.js adds .show. */
.field-error {
  display: none;
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--state-error);            /* 8.31:1 on the white card */
}
.field-error.show { display: block; }

.form-submit {
  width: 100%;
  padding: 0.85rem 1.5rem;
  border-radius: var(--pill);
  font-size: 0.95rem;
  font-weight: 700;
  background: var(--brand-primary);
  color: var(--light-secondary);        /* 12.03:1 */
  transition: background 180ms ease, color 180ms ease;
}
.form-submit:hover:not(:disabled) {
  background: var(--brand-secondary);
  color: var(--light-secondary);        /* 15.49:1 — hover verified */
}
/* contact-form.js disables the button until name + phone validate, so this is
   the state a visitor sees on load. Dimming with opacity would drop the label
   under 4.5:1, so it re-inks instead: pale navy fill, near-black label at
   8.23:1, unmistakably "not yet" but entirely legible. */
.form-submit:disabled {
  background: color-mix(in srgb, var(--brand-secondary) 30%, var(--light-tertiary));
  color: var(--dark-primary);
  cursor: not-allowed;
}

.form-status {
  margin-top: 1rem;
  padding: 0.8rem 1rem;
  font-size: 0.9rem;
  line-height: 1.5;
  border: 1px solid color-mix(in srgb, var(--brand-primary) 20%, var(--light-tertiary));
  border-radius: var(--radius-sm);
}
/* The node ships with the hidden attribute; a display declaration above would
   override it, so the guard is explicit. */
.form-status[hidden] { display: none; }
.form-status.success {
  color: var(--state-success);          /* 6.33:1 on its own 8% tint */
  border-color: var(--state-success);
  background: color-mix(in srgb, var(--state-success) 8%, var(--light-secondary));
}
.form-status.error {
  color: var(--state-error);            /* 7.24:1 on its own 8% tint */
  border-color: var(--state-error);
  background: color-mix(in srgb, var(--state-error) 8%, var(--light-secondary));
}

#contact-form { display: block; }
#name, #phone, #email, #message { min-width: 0; }
#name-err, #phone-err, #email-err { margin-top: 0.1rem; }
#form-message { margin-top: 1rem; }


/* ═══════════════════════════════════════════════════════════ 17. FOOTER ⚠ ══ */

/* DARK BAND. Soft ink is 82% white over the navy = 10.64:1; every full-
   strength item below is stated, not inherited. */
#footer {
  background: var(--brand-secondary);
  color: color-mix(in srgb, var(--light-secondary) 82%, var(--brand-secondary));
  padding-block: clamp(3rem, 6vw, 4.5rem) 1.75rem;
}

.footer-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2.5rem;
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.footer-brand { min-width: 0; }
.footer-logo { margin-right: 0; }
#footer .brand-project { color: var(--light-secondary); }   /* 15.49:1 */
#footer .brand-developer {
  color: color-mix(in srgb, var(--light-secondary) 80%, var(--brand-secondary)); /* 10.22:1 at 0.66rem */
}

.footer-tagline {
  margin-top: 1rem;
  font-size: 0.94rem;
  max-width: 46ch;
}
.footer-about-text {
  margin-top: 0.7rem;
  font-size: 0.86rem;
  line-height: 1.65;
  max-width: 52ch;
  overflow-wrap: anywhere;              /* the RERA id is one 36-character token */
}

.footer-col { min-width: 0; }
.footer-col-title {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--light-secondary);        /* 15.49:1 */
}
.footer-col ul {
  list-style: none;
  padding: 0;
  margin-top: 0.95rem;
  display: grid;
  gap: 0.5rem;
}
.footer-col address {
  margin-top: 0.95rem;
  font-size: 0.9rem;
  line-height: 1.7;
}

#footer a {
  color: inherit;                       /* the 10.64:1 soft ink */
  text-decoration: none;
  font-size: 0.9rem;
}
#footer a:hover {
  color: var(--light-secondary);        /* 15.49:1 — hover verified on the dark band */
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
#footer strong,
#footer em { color: var(--light-secondary); }   /* the fleet's most repeated dark-band bug */
#footer .page-updated { color: color-mix(in srgb, var(--light-secondary) 80%, var(--brand-secondary)); }
#footer :focus-visible { outline-color: var(--light-secondary); }

.footer-disclaimer {
  width: 100%;
  max-width: var(--measure);
  margin: 2.5rem auto 0;
  padding: 1.5rem var(--gutter) 0;
  border-top: 1px solid color-mix(in srgb, var(--light-secondary) 18%, transparent);
}
.footer-disclaimer p {
  font-size: 0.79rem;
  line-height: 1.65;
}
.footer-disclaimer p + p { margin-top: 0.7rem; }
.footer-disclaimer a { font-size: inherit; text-decoration: underline; color: var(--light-secondary); }

.footer-copy {
  width: 100%;
  max-width: var(--measure);
  margin: 1.6rem auto 0;
  padding-inline: var(--gutter);
  font-size: 0.81rem;
}


/* ══════════════════════════════════════ 18. LIGHTBOX (injected by script.js) ══ */

/* Not present in any page's markup — script.js appends it on every page that
   has .gallery-item[data-full]. It still needs a full set of rules. */
#lightbox {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: none;
  place-items: center;
  padding: clamp(1rem, 4vw, 3rem);
  background: color-mix(in srgb, var(--dark-primary) 92%, transparent);
}
#lightbox.open { display: grid; }

.lightbox-img {
  max-width: min(100%, 1100px);
  max-height: 76vh;
  width: auto;
  object-fit: contain;
  border-radius: var(--radius);
  background: none;
}

.lightbox-caption {
  max-width: 62ch;
  margin: 1rem auto 0;
  text-align: center;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--light-secondary);        /* 14.35:1 over the composited scrim */
}

.lightbox-close,
.lightbox-prev,
.lightbox-next {
  position: absolute;
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  font-size: 1.6rem;
  line-height: 1;
  border-radius: var(--pill);
  /* 14% white over the 92% scrim composites to a mid-charcoal; the glyph on
     it measures 9.15:1. */
  background: color-mix(in srgb, var(--light-secondary) 14%, transparent);
  color: var(--light-secondary);
  transition: background 180ms ease, color 180ms ease;
}
.lightbox-close:hover,
.lightbox-prev:hover,
.lightbox-next:hover {
  background: var(--light-secondary);
  color: var(--dark-primary);           /* 17.93:1 — hover verified */
}
.lightbox-close:focus-visible,
.lightbox-prev:focus-visible,
.lightbox-next:focus-visible { outline-color: var(--light-secondary); }

.lightbox-close { top: 1rem; right: 1rem; }
.lightbox-prev  { left: 0.75rem; top: 50%; transform: translateY(-50%); }
.lightbox-next  { right: 0.75rem; top: 50%; transform: translateY(-50%); }


/* ═══════════════════════════════ 19. CONTACT MODAL (injected by script.js) ══ */

.cm-modal {
  /* Re-assert the UA's dialog centring: the `* { margin: 0 }` reset above
     strips it and pins the dialog to the top-left corner. */
  margin: auto;
  width: min(100%, 28rem);
  padding: 1rem;
  border: 0;
  background: transparent;
  overscroll-behavior: contain;
}
/* Browsers without <dialog> ignore [open] and would render the form inline. */
.cm-modal:not([open]) { display: none; }

/* ::backdrop only inherits custom properties from its originating element in
   recent engines; where it does not, the declaration is dropped and the blur
   alone still separates the dialog from the page. A literal here is not an
   option — no colour may exist outside :root. */
.cm-modal::backdrop {
  background: color-mix(in srgb, var(--dark-primary) 62%, transparent);
  backdrop-filter: blur(2px);
}

.cm-modal__panel {
  position: relative;
  padding: clamp(1.25rem, 4vw, 1.75rem);
  border: 1px solid color-mix(in srgb, var(--brand-primary) 20%, var(--light-tertiary));
  border-radius: var(--radius);
  background: var(--light-secondary);
  color: var(--dark-primary);           /* 17.93:1 */
  font-family: var(--font-body);
}

.cm-modal__title {
  margin: 0 0 1.15rem;
  padding-right: 2.5rem;
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--dark-primary);
}

.cm-modal__close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 2.4rem;
  height: 2.4rem;
  display: grid;
  place-items: center;
  font-size: 1.5rem;
  line-height: 1;
  border-radius: var(--pill);
  color: var(--dark-secondary);         /* 8.86:1 */
}
.cm-modal__close:hover {
  background: var(--light-tertiary);
  color: var(--dark-primary);           /* 15.45:1 on that grey — hover verified */
}

.cm-form { display: grid; gap: 0.9rem; }

.cm-form__field {
  display: flex;
  flex-direction: column;
  gap: 0.32rem;
  text-align: left;
}
.cm-form__field label {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--dark-primary);
}

/* Form controls do not inherit the page font — `font: inherit` closes it. */
.cm-form input,
.cm-form textarea {
  width: 100%;
  min-width: 0;
  font: inherit;
  font-size: 0.95rem;
  padding: 0.7rem 0.85rem;
  color: var(--dark-primary);
  background: var(--light-secondary);
  border: 1px solid color-mix(in srgb, var(--brand-secondary) 45%, var(--dark-tertiary)); /* 3.75:1 */
  border-radius: var(--radius-sm);
}
.cm-form input::placeholder,
.cm-form textarea::placeholder { color: var(--dark-secondary); opacity: 1; }
.cm-form input:focus-visible,
.cm-form textarea:focus-visible {
  outline: 3px solid var(--brand-primary);
  outline-offset: 0;
  border-color: var(--brand-primary);
}
.cm-form textarea { min-height: 5.5rem; resize: vertical; }

.cm-form__submit {
  padding: 0.8rem 1.5rem;
  border-radius: var(--pill);
  font-weight: 700;
  background: var(--brand-primary);
  color: var(--light-secondary);        /* 12.03:1 */
  transition: background 180ms ease;
}
/* A hover that only drops opacity would take the label below 4.5:1; swap the
   fill instead. */
.cm-form__submit:hover {
  background: var(--brand-secondary);
  color: var(--light-secondary);        /* 15.49:1 */
}

/* script.js writes this node's colours inline on submit; the base rule only
   has to reserve its type. */
.cm-form__status { font-size: 0.9rem; line-height: 1.45; }

.cm-form__hint {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.55;
  color: var(--dark-secondary);         /* 8.86:1 */
}
.cm-form__hint a { color: var(--brand-primary); text-decoration: underline; }  /* 12.03:1 */
.cm-form__hint a:hover { color: var(--brand-secondary); }                      /* 15.49:1 */

/* Class-based, so it cannot fight an inline body.style.overflow. */
body.has-modal-open { overflow: hidden; }


/* ════════════════════════════════════════════════════════ 20. BREAKPOINTS ══ */

/* ---- 520: the first two-up ---- */
@media (min-width: 520px) {
  .gallery-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hero-btns .btn,
  .page-hero-ctas .btn { flex: 0 1 auto; }
}

/* ---- 600: paired form fields and the stat row ---- */
@media (min-width: 600px) {
  .form-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .page-hero-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .hero-content { padding: clamp(1.75rem, 3.5vw, 2.5rem); }
  #hero { min-height: min(calc(100svh - var(--navh)), 40rem); }
  .hero-content { margin-block: clamp(1.25rem, 3svh, 3rem); }
}

/* ---- 768: plans side by side, footer splits ---- */
@media (min-width: 768px) {
  .plan-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .footer-inner { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .prose-inner > .cta-row { margin-top: 2.5rem; }
}

/* ---- 900: the contact split and the third gallery column ---- */
@media (min-width: 900px) {
  .contact-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 26rem); gap: 3rem; }
  .gallery-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .footer-inner { grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr)); gap: 3rem; }
}

/* ---- 1024: the nav uncollapses (fleet standard) ---- */
@media (min-width: 1024px) {
  .nav-links { display: flex; }
  .nav-cta { display: inline-flex; }
  .hamburger { display: none; }
  /* Hard off: a transformed-but-visible drawer would still cover the page. */
  #mobile-menu { display: none; }

  /* The homepage card moves off the centre line to the shell's left edge, so
     the aerial reads down the right of the screen — the layout IS the subject
     of this project, and it should not sit entirely behind a white card. */
  #hero { min-height: calc(100svh - var(--navh)); }
  .hero-content {
    /* A vertical inset, not margin:0 — the photograph has to read ABOVE and
       BELOW the card as well as beside it, or the left half of the hero stops
       being a photograph at all. The type below tightens just enough to buy
       that inset back and still clear a 900px window. */
    margin-block: clamp(1rem, 2.5svh, 2.25rem);
    margin-inline-end: 0;
    width: min(44rem, calc(100% - (var(--gutter) * 2)));
    padding: clamp(1.75rem, 2.3vw, 2.25rem);
    justify-self: start;
    margin-inline-start: max(var(--gutter), calc((100% - var(--shell)) / 2 + var(--gutter)));
  }
  /* The card is what sets the hero's height now, so its internal rhythm is
     what decides whether the section clears the fold. Nothing is hidden or
     moved out of the hero; every step is just tightened one notch. */
  .hero-title { font-size: clamp(2rem, 3.1vw, 2.7rem); margin-top: 0.85rem; }
  .hero-sub { font-size: 0.95rem; line-height: 1.62; margin-top: 0.9rem; max-width: none; }
  .hero-content .page-updated { margin-top: 0.7rem; }
  .hero-btns { margin-top: 1.35rem; }
  .hero-stats { margin-top: 1.35rem; }
  .hero-overlay {
    /* Sideways once the card covers the left: keep the right edge readable
       and stop the plate washing out behind the white block. */
    background: linear-gradient(
      90deg,
      color-mix(in srgb, var(--brand-secondary) 50%, transparent) 0%,
      color-mix(in srgb, var(--brand-secondary) 18%, transparent) 58%,
      color-mix(in srgb, var(--brand-secondary) 46%, transparent) 100%
    );
  }

  .page-hero-inner { flex-wrap: nowrap; }
  .page-hero-inner > *:first-child { flex: 1 1 58%; }
  .page-hero-image { flex: 0 1 42%; }
  .page-hero-image img { aspect-ratio: 1 / 1; }
}

/* ---- Phone: the card is the tallest thing on the smallest screen ---- */
@media (max-width: 519px) {
  .hero-btns .btn,
  .page-hero-ctas .btn,
  .cta-row .btn { flex: 1 1 100%; }
  /* At 360 the project name wraps and strands the "4" on its own line beside
     a 40px mark and a 44px target. One notch down and it sets on one line. */
  .brand-project { font-size: 0.86rem; }
  .brand-developer { font-size: 0.62rem; letter-spacing: 0.14em; }
  .data-table th,
  .data-table td { padding: 0.65rem 0.8rem; }
  .lightbox-prev, .lightbox-next { width: 42px; height: 42px; }
}


/* ══════════════════════════════════════════════════════ 21. REDUCED MOTION ══ */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .gallery-item:hover img,
  .gallery-item:focus-visible img { transform: none; }
}
