/* ==========================================================================
   Raylea — Home page
   Layers on css/styles.css: tokens, reset, type roles, header, mobile nav,
   footer and the register panel all come from there. Only what the home
   page adds lives here.
   ========================================================================== */

:root {
  /* The design sets body copy and headings on orange in a light tone that
     is lighter than Sand. Sampled off the supplied PDF and corrected back
     out of the export's colour shift — CONFIRM against the brand guide
     before launch. */
  /* Sampled from the glyph cores of the supplied design artwork, where the
     background also measures as the true brand orange — so this is the
     design's own light ink, not a derivation. */
  --color-cream: #f6ebdf;

  /* Brand orange is #f47a40; this is the same hue nudged until navy copy on
     it clears WCAG AA at every size (4.53:1 against 4.30:1). The difference
     is not visible side by side. Used wherever orange carries type — the
     Enquire pill and the register CTA button. */
  --color-orange-aa: #f48240;

  /* STANDBY, not currently used. Brand orange taken down the same hue until
     light type on it clears WCAG AA (cream reaches 4.59:1 / APCA Lc 69).
     Judged too far off brand; kept here as the known-good value for when a
     deeper brand orange is supplied. Swap it into --band-colour below. */
  --color-orange-deep: #ba430b;

  /* The Vision band's field colour. The panorama's gradient starts from the
     same token, so the two surfaces can never drift apart and the band can
     be re-coloured from one line. See the contrast notes in CLAUDE.md. */
  --band-colour: var(--color-orange);

  /* One easing and one duration for every reveal on the page, so the whole
     thing reads as a single motion system rather than a pile of effects. */
  --ease-reveal: cubic-bezier(0.16, 1, 0.3, 1);
  --reveal-duration: 820ms;

  /* One page margin throughout — the header, the vision band, the panorama
     and the activities all sit on it. */
  --gutter: clamp(24px, 2.4vw, 40px);
}

/* ---------- Shared reveal grammar ----------
   Text rises 24px as it fades. Headings do the same thing a line at a time.
   Images uncover from an edge while the picture inside them settles out of
   a slight zoom. Nothing else moves. */

/* The .js prefix on every starting state is deliberate — see the inline
   script in the document head. Only the finishing states are unscoped. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity var(--reveal-duration) var(--ease-reveal) var(--reveal-delay, 0ms),
    transform var(--reveal-duration) var(--ease-reveal) var(--reveal-delay, 0ms);
}
[data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/* Lines are wrapped by home.js: .line is the mask, .line__inner is what
   moves. overflow on the mask is what makes the line appear to rise out of
   the one above rather than fade in place. */
.line {
  display: block;
  overflow: hidden;
  /* Tracked uppercase headings sit close to their box; without this the
     ascenders and the hanging comma on "ZILZIE," get clipped by the mask. */
  padding-bottom: 0.12em;
  margin-bottom: -0.12em;
}
.js .line__inner {
  display: block;
  transform: translateY(110%);
  transition: transform var(--reveal-duration) var(--ease-reveal) var(--line-delay, 0ms);
}
[data-split-lines].is-revealed .line__inner {
  transform: none;
}

/* Media reveal: the figure masks, the <img> inside moves and un-zooms.
   --media-from is set per direction below. */
[data-reveal-media] {
  overflow: hidden;
}
.js [data-reveal-media] > img,
.js [data-reveal-media] > picture,
.js [data-reveal-media] > .activities__frame,
.js [data-reveal-media] > .vision__frame {
  display: block;
  transform: translate(var(--media-from-x, 0), var(--media-from-y, 0)) scale(1.08);
  transition: transform calc(var(--reveal-duration) * 1.15) var(--ease-reveal);
}
[data-reveal-media].is-revealed > img,
[data-reveal-media].is-revealed > picture,
[data-reveal-media].is-revealed > .activities__frame,
[data-reveal-media].is-revealed > .vision__frame {
  transform: none;
}
[data-reveal-media="right"] { --media-from-x: 12%; }
[data-reveal-media="left"]  { --media-from-x: -12%; }
[data-reveal-media="up"]    { --media-from-y: 8%; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-reveal].is-revealed,
  .line__inner,
  [data-reveal-media] > img,
  [data-reveal-media] > picture,
  [data-reveal-media] > .activities__frame,
  [data-reveal-media] > .vision__frame {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* ---------- Pill buttons ----------
   styles.css's .btn--outline is the arrow link, not a pill. The home design
   uses two true pills: solid orange in the header, outlined over the video. */
.btn-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 11px 26px;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.12em;
  line-height: 1;
  border-radius: 999px;
  /* White on the brand orange, at the client's request (site-wide). This is
     2.73:1, which fails WCAG AA for 13px button text (4.5:1) — the same
     accepted position as the orange Vision band and the About hero. Navy
     was used before (4.53:1 on --color-orange-aa, the orange nudged lighter
     for it); with white type that nudge only hurts, so the pill is back on
     the true brand orange. Hover still goes to navy, where the label is
     8.34:1. */
  border: 1.5px solid var(--color-orange);
  background: var(--color-orange);
  color: #ffffff;
  white-space: nowrap;
  transition: background-color 0.3s var(--ease), color 0.3s var(--ease),
              border-color 0.3s var(--ease), transform 0.3s var(--ease);
}
.btn-pill:hover,
.btn-pill:focus-visible {
  background: var(--color-blue);
  border-color: var(--color-blue);
  color: var(--color-cream);
}

.btn-pill--outline {
  background: transparent;
  border-color: var(--color-cream);
  color: var(--color-cream);
  padding: 15px 34px;
  letter-spacing: 0.14em;
}
.btn-pill--outline:hover,
.btn-pill--outline:focus-visible {
  background: var(--color-cream);
  border-color: var(--color-cream);
  color: var(--color-blue);
}

/* ---------- Arrow link ----------
   Same behaviour as styles.css's .btn--outline (underline draws in, arrow
   nudges) but it inherits its colour from the section instead of being
   locked to orange — orange on sand is 1.94:1, which is unreadable. */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  position: relative;
  margin-top: 36px;
  padding-bottom: 6px;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.12em;
  color: inherit;
}
.link-arrow::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: currentColor;
  opacity: 0.45;
}
.link-arrow::after {
  content: '\2192';
  transition: transform 0.3s var(--ease);
}
.link-arrow:hover::after,
.link-arrow:focus-visible::after { transform: translateX(5px); }
.link-arrow:hover::before,
.link-arrow:focus-visible::before { opacity: 1; }

/* ---------- Header ----------
   The home header is transparent over the video, then a solid sand bar. It
   also hides on scroll down and comes back on scroll up, so the nav is never
   more than a flick away without sitting over the content the whole time. */
.site-header--home {
  transition: background-color 0.4s var(--ease), transform 0.45s var(--ease);
}
.site-header--home.is-hidden {
  transform: translateY(-100%);
}
.site-header--home .site-header__inner {
  gap: clamp(16px, 3vw, 44px);
  /* styles.css sets --section-pad-x (up to 96px) here, which left the nav
     sitting noticeably further in than everything below it. The header now
     uses the same --gutter as the page. */
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}
/* The splash page centres its nav absolutely; the home nav sits in the flow
   and pushes right, because the Enquire pill has to sit after it. */
.site-nav--home {
  position: static;
  transform: none;
  margin-left: auto;
}
.site-nav--home .site-nav__list {
  gap: clamp(14px, 1.9vw, 32px);
  font-size: 14.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--color-cream);
}
.site-header--home.is-scrolled .site-nav--home .site-nav__list { color: var(--color-blue); }

/* Current page. aria-current is already on the link for screen readers;
   this makes it visible. An underline in the link's own colour, so it is
   navy on the sand bar and sand on the mobile overlay, rather than orange:
   orange is the hover colour, and on sand it is 1.94:1 — too faint to read
   as a deliberate mark. The home page has no current link; the logo is
   its marker. */
.site-nav__list a[aria-current="page"],
.mobile-nav__list a[aria-current="page"] {
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 7px;
}
.mobile-nav__list a[aria-current="page"] {
  text-decoration-thickness: 2px;
  text-underline-offset: 10px;
}
.site-header--home .btn-pill { flex-shrink: 0; }

/* ---------- Hero ---------- */
.hero-video {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  /* stretch, not flex-end: the content column runs the full height and
     distributes itself, which is what sets the logo near the centre and the
     text group down at the base. */
  align-items: stretch;
  overflow: hidden;
  background: var(--color-blue);
  color: var(--color-cream);
}

/* Oversized and offset so the parallax shift in home.js never exposes an
   edge — the same buffering trick the splash hero uses. */
/* The poster lives here, not on the <video> alone: opacity on the video
   element hides its own poster frame too, so while the file was buffering the
   hero sat empty. As a background on the layer beneath, the still is up
   immediately and the video fades in over it. */
.hero-video__media {
  position: absolute;
  top: -6%;
  left: 0;
  right: 0;
  height: 112%;
  background: var(--color-blue) url("../assets/video/hero-poster.jpg") center / cover no-repeat;
  will-change: transform;
}
.hero-video__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The poster is painted by the <video> itself and the file fades in over
     it once it can actually play, so there is no flash of empty blue. */
  opacity: 0;
  transition: opacity 900ms var(--ease-reveal);
}
.hero-video__video.is-playing { opacity: 1; }

/* A single flat overlay across the whole frame. --hero-overlay is the one
   number to turn. 0.55 was measured off the rendered page against the surf
   frames, which are the brightest in the loop: it puts the intro copy at
   AA for body text and the H1 comfortably clear. At 0.45 the intro measured
   4.33:1, just under. Lower it and re-check against a surf frame. */
.hero-video__scrim {
  --hero-overlay: 0.55;
  position: absolute;
  inset: 0;
  background: rgba(32, 33, 54, var(--hero-overlay));
}

.hero-video__content {
  position: relative;
  z-index: 1;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: calc(var(--header-height) + 16px) var(--gutter) clamp(76px, 9vh, 118px);
  /* A soft shadow buys more legibility over moving footage than darkening
     the scrim would, and it costs none of the picture. The frame changes
     constantly, so the type cannot rely on any one background. */
  text-shadow: 0 1px 22px rgba(32, 33, 54, 0.55), 0 1px 3px rgba(32, 33, 54, 0.3);
}

/* Two auto top margins split the free height evenly, which lands the
   brandmark near the middle and the text group at the base — and it holds
   that rhythm at any viewport height without a magic number. */
/* The brandmark file is a square 283×283 artboard with its clear space built
   in, so the artwork reads at roughly three-quarters of whatever width is
   set here — hence the generous numbers. */
.hero-video__brand {
  margin-top: auto;
  width: clamp(230px, 24vw, 370px);
}
.hero-video__brand img {
  display: block;
  width: 100%;
  height: auto;
}

/* Same arrow link as "Discover More" in the band below, so the hero's call to
   action and the section links read as one component. It inherits cream here
   and navy down the page. */
.hero-video__cta {
  margin-top: 26px;
  font-size: 13px;
}

.hero-video__group {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Commuters Sans, not the brand script: the wordmark directly above is
   already a script, and two of them this close read as a mismatch rather
   than a pair. The sans also lets the line-rise reveal match the H2s.

   Deliberately well below the brandmark's own cap height so the fold reads
   in four clear steps — logo, then this, then the intro, then the link. At
   its earlier 38px it was competing with the logo rather than following it. */
.hero-video__title {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: clamp(16px, 1.6vw, 22px);
  line-height: 1.4;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 0 0 16px;
  max-width: none;
}

.hero-video__intro {
  font-family: var(--font-label);
  font-variation-settings: "MONO" 0, "slnt" 0, "wght" 400;
  text-transform: none;
  font-size: clamp(15px, 1.1vw, 17px);
  line-height: 1.55;
  max-width: 46ch;
  margin: 0;
}

/* ---------- Vision (orange band) ----------
   A text-only band between the video hero and the panorama, so the colour is
   doing the work a photograph would.

   Orange is a light colour — luminance 0.335 — so no light ink can sit on
   it: cream is 2.34:1 and even pure white only reaches 2.73:1, short of the
   3:1 large-text floor. The band therefore runs navy type on orange, which
   is 4.53:1 and passes AA at every size. --color-orange-aa is the brand
   orange nudged just far enough to clear that bar; the two are not
   distinguishable side by side. */
.intro {
  /* Pure white, not cream. On this orange white is the best light ink
     available — 2.73:1 / APCA Lc 57, against cream's 2.32:1 / Lc 45. It
     still fails WCAG AA; see CLAUDE.md for why that is a known, accepted
     position here and what resolves it. */
  --intro-ink: #ffffff;
  --intro-bg: var(--band-colour);

  position: relative;
  overflow: hidden;
  background: var(--intro-bg);
  color: var(--intro-ink);
  /* Bottom padding is tuned with .panorama's top padding as a pair: together
     they set the 150px gap from the masterplan link down to the image. */
  padding: clamp(56px, 6.9vw, 99px) 0 clamp(44px, 6.25vw, 90px);
}

.intro__inner { position: relative; padding: 0 var(--gutter); }


/* opacity:1 explicitly — .eyebrow's own 0.85 would knock navy-on-orange back
   to 3.58:1, under the 4.5 this 13px label needs. It currently happens to be
   overridden by the reveal rule, but that is a coincidence of specificity and
   would not hold with scripting off. */
.intro__eyebrow {
  margin: 0 0 clamp(14px, 1.7vw, 25px);
  opacity: 1;
}

/* Full-measure rule under a section eyebrow, drawn in on reveal. Its own
   element rather than a border so it can animate independently of the text
   above. Shared by the Vision and Enquire sections. */
.section-rule {
  display: block;
  height: 1px;
  background: currentColor;
  opacity: 0.4;
  transform-origin: left;
}
.js [data-rule] {
  transform: scaleX(0);
  transition: transform 1100ms var(--ease-reveal);
}
[data-rule].is-revealed { transform: scaleX(1); }

.intro__grid {
  position: relative;
  z-index: 1;
  display: grid;
  /* Asymmetric, and the heading gets the wider track. 1.45 rather than the
     reference's ~1.2 buys the extra width that lets the heading run at 38px
     and still hold its two-line break — the column width is what caps the
     type size here, not taste. */
  grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
  gap: clamp(28px, 6.2vw, 90px);
  margin-top: clamp(28px, 4.6vw, 67px);
  align-items: start;
}

.intro .heading-lg {
  font-size: clamp(24px, 2.65vw, 38px);
  line-height: 1.22;
  letter-spacing: 0.1em;
  /* The column is the only constraint, which is what holds the two-line
     break "A NEW HILLSIDE COMMUNITY / IN ZILZIE, QUEENSLAND". */
  max-width: none;
  margin: 0;
}

/* The column already sets the measure; .body-copy's own 52ch would cut it
   narrower than the design's column and leave the track half empty. */
.intro .body-copy { max-width: none; }
.intro .link-arrow { margin-top: clamp(18px, 1.8vw, 26px); }

@media (prefers-reduced-motion: reduce) {
  .js [data-rule] { transform: none; transition: none; }
}

/* ---------- Panorama ----------
   The orange-to-sand handover. The gradient is anchored to this band's own
   top and bottom edges rather than driven by scroll position, so both seams
   stay exact no matter how tall the viewport is — same reasoning as .views
   in styles.css. The scroll-driven part is the frame's own expansion. */
.panorama {
  position: relative;
  /* Orange to sand is a short step between two warm tones, so it interpolates
     cleanly and needs no intermediate stop. (The lavender stop that was here
     existed only to keep a navy-to-sand blend out of a dead grey.) */
  background: linear-gradient(to bottom,
    var(--band-colour) 0%,
    var(--band-colour) 16%,
    var(--color-sand) 74%,
    var(--color-sand) 100%);
  padding: clamp(32px, 4.17vw, 60px) 0 clamp(48px, 6vw, 104px);
}

/* Starts inset and opens out towards the page edges as it centres on screen.
   --panorama-expand is driven 0 -> 1 by home.js. */
/* clip-path rather than an animated width: the frame keeps one fixed box and
   only its visible rect changes, so the expansion is composited instead of
   relaying out the image on every scroll frame. */
.panorama__frame {
  margin: 0;
  width: 100%;
  clip-path: inset(
    0 calc(var(--gutter) + (1 - var(--panorama-expand, 0)) * 7vw)
    round calc(10px + (1 - var(--panorama-expand, 0)) * 6px));
  will-change: clip-path;
}
.panorama__frame img {
  width: 100%;
  height: auto;
  display: block;
  aspect-ratio: 1648 / 822;
  object-fit: cover;
}
@media (prefers-reduced-motion: reduce) {
  .panorama__frame { clip-path: inset(0 var(--gutter) round 10px); }
}

/* ---------- Activities (sand band) ---------- */
.activities {
  background: var(--color-sand);
  color: var(--color-blue);
  padding: clamp(40px, 5vw, 88px) 0 clamp(80px, 10vw, 150px);
}
.activities__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(40px, 6vw, 96px);
  /* stretch, so the copy column matches the image's height and the list can
     be pushed to its foot. */
  align-items: stretch;
  padding: 0 var(--gutter);
}

/* The heading and body sit at the top, the list and its link at the bottom,
   level with the base of the image. The space between the two groups is
   whatever is left over, so it breathes on tall viewports instead of being
   a fixed gap. */
.activities__copy {
  display: flex;
  flex-direction: column;
}
/* Flex items stretch to the cross axis by default, which pulled the arrow
   link to the full column width and dragged its underline out with it. The
   heading, body and list DO want the full width; only the link shrinks to
   its own text. */
.activities__copy .link-arrow {
  align-self: flex-start;
}
/* No measure on either: the heading, the body and the list below them all
   take the full grid column, so the three line up on both edges and the
   heading stops breaking to four lines. */
.activities .heading-lg {
  font-size: clamp(24px, 2.9vw, 37px);
  letter-spacing: 0.1em;
  max-width: none;
  margin-bottom: 26px;
}
.activities .body-copy { max-width: none; }

.activities__list {
  /* auto pushes the list and link to the bottom of the column; the padding
     guarantees a gap when there is no free space left to absorb. */
  margin: auto 0 0;
  /* The floor, not the actual gap. It has to be small enough that the copy
     column's natural height stays under the image's, otherwise there is no
     free space for the auto margin to absorb and nothing anchors. The real
     gap is this plus whatever is left over, which grows with the viewport. */
  padding-top: clamp(28px, 3.2vw, 46px);
}
.activities__item {
  position: relative;
}
/* The rule is a pseudo-element so it can draw itself in from the left on
   reveal instead of simply appearing with the row. */
.activities__item::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: currentColor;
  opacity: 0.35;
  transform-origin: left;
  transition: transform 700ms var(--ease-reveal) var(--line-delay, 0ms),
              opacity 0.3s var(--ease);
}
.js .activities__item::after { transform: scaleX(0); }
.activities__list.is-revealed .activities__item::after { transform: scaleX(1); }

.activities__trigger {
  display: block;
  width: 100%;
  text-align: left;
  padding: 14px 0;
  font-family: var(--font-body);
  font-weight: 400;
  /* Matched to .link-arrow, so the list and the "Explore the Location" link
     under it sit at one size. Tracking stays tighter than the link's 0.12em:
     these are sentences, not a three-word label, and 0.12em over a line like
     the Great Keppel entry would stretch it well past a comfortable measure. */
  font-size: 13px;
  letter-spacing: 0.08em;
  line-height: 1.5;
  color: inherit;
  cursor: default;
  transition: transform 0.4s var(--ease), opacity 0.3s var(--ease);
}

.activities__media {
  position: relative;
  margin: 0;
  border-radius: 10px;
  /* Not stretched: the figure keeps its square height so the copy column has
     something to measure itself against, and so the sticky rule below still
     has room to move within the row. */
  align-self: start;
}
.activities__frame {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: 10px;
  overflow: hidden;
}
/* The caption sits on a photograph that changes with every row — water,
   sand, grass, a cafe table — so it cannot depend on what is behind it.
   A soft scrim across the foot of the frame gives it one consistent ground.
   Clipped by the frame's own radius, and inert to the pointer so it never
   interferes with the hover swap. */
.activities__frame::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 34%;
  background: linear-gradient(to top,
    rgba(32, 33, 54, 0.66) 0%, rgba(32, 33, 54, 0.3) 45%, rgba(32, 33, 54, 0) 100%);
  pointer-events: none;
}
.activities__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.65s var(--ease-reveal);
}
.activities__image.is-active { opacity: 1; }

.activities__caption {
  position: absolute;
  right: 18px;
  bottom: 14px;
  z-index: 1;
  font-family: var(--font-label);
  font-variation-settings: "MONO" 0, "slnt" 0, "wght" 400;
  text-transform: none;
  font-size: 13px;
  letter-spacing: 0.01em;
  color: var(--color-cream);
  text-shadow: 0 1px 6px rgba(32, 33, 54, 0.5);
}

/* Hover swap is desktop-and-pointer only. Without a fine pointer the list is
   static text and the default image stays put, so nothing depends on hover
   to be understood. */
@media (hover: hover) and (pointer: fine) and (min-width: 861px) {
  .activities__list[data-activities] .activities__trigger { cursor: pointer; }
  .activities__list[data-activities] .activities__item:not(.is-active) .activities__trigger { opacity: 0.6; }
  .activities__list[data-activities] .activities__item.is-active .activities__trigger { transform: translateX(10px); }
  .activities__list[data-activities] .activities__item.is-active::after { opacity: 0.8; }
  /* The media column tracks the list rather than scrolling past it. */
  .activities__media {
    position: sticky;
    top: calc(var(--header-height) + 32px);
  }
}

/* ---------- Enquire ----------
   A navy block floating on the band's sand-to-orange ground, opening out to
   the page margins as it arrives — the panorama's move, so the page opens
   and closes on the same gesture.

   It is a block, so it is simply navy: the earlier sand-to-navy background
   fade is gone, and with it the mid-tone moment where the type and ground
   met in the middle. One fewer thing to get wrong. */
.enquire {
  --enq-error: #ffa892;

  position: relative;
  background: var(--color-blue);
  color: var(--color-cream);
  /* clip-path rather than width, so the block's internal layout never
     reflows as it opens — only the visible rect changes. */
  clip-path: inset(
    0 calc(var(--gutter) + (1 - var(--enquire-open, 0)) * 7vw)
    round calc(10px + (1 - var(--enquire-open, 0)) * 6px));
  will-change: clip-path;
  padding: clamp(56px, 7vw, 100px) 0 clamp(56px, 6.5vw, 96px);
  scroll-margin-top: calc(var(--header-height) + 24px);
}

@media (prefers-reduced-motion: reduce) {
  .enquire { clip-path: inset(0 var(--gutter) round 10px); }
}

/* The gutter is the block's own edge, so the content is inset from THAT,
   not from the viewport. */
.enquire__inner { padding: 0 calc(var(--gutter) + clamp(20px, 3.5vw, 50px)); }
.enquire .eyebrow { margin: 0 0 clamp(14px, 1.7vw, 25px); opacity: 1; }

.enquire__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
  gap: clamp(36px, 6vw, 96px);
  margin-top: clamp(32px, 4.6vw, 67px);
  align-items: start;
}

.enquire .heading-lg {
  font-size: clamp(24px, 2.65vw, 38px);
  line-height: 1.22;
  letter-spacing: 0.1em;
  max-width: 16ch;
  margin: 0 0 20px;
}
.enquire .body-copy { max-width: 42ch; }

/* ---------- Contact block ---------- */
.enquire__contact { margin-top: clamp(32px, 3.4vw, 48px); }
.enquire__contact-label {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.16em;
  margin: 0 0 10px;
}
.enquire__contact-line { margin: 0; }
.enquire__contact-link {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: clamp(18px, 1.8vw, 24px);
  letter-spacing: 0.06em;
  text-decoration: underline;
  text-underline-offset: 6px;
  text-decoration-thickness: 1.5px;
  padding: 6px 0;
  transition: text-decoration-thickness 0.2s var(--ease);
}
.enquire__contact-link:hover,
.enquire__contact-link:focus-visible { text-decoration-thickness: 3px; }

/* Partner logos under the sales enquiry number: developer, then selling
   agent. The label above each uses the contact label's treatment so the
   column reads as one set of details.

   The two logos are NOT one shared height. Resi Ventures is a long wordmark
   whose capitals fill its box; Projx is compact lowercase with p and j
   descenders, so its letters fill only about half of its box. At one height
   Projx reads far smaller. They are sized to match x-heights instead —
   Projx's box ~1.4x Resi's — and top-aligned under same-height labels,
   which lands both their x-height tops and their baselines within about a
   pixel of each other. Re-balance if either file is swapped for a different
   lock-up. */
.enquire__partners {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 24px clamp(32px, 4vw, 56px);
  margin-top: clamp(32px, 3.4vw, 48px);
}
.enquire__partner { margin: 0; }
.enquire__partner-label {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.16em;
  margin: 0 0 14px;
  text-transform: uppercase;
}
.enquire__partner-logo {
  display: block;
  width: auto;
}
.enquire__partner-logo--resi  { height: clamp(19px, 1.6vw, 23px); }
.enquire__partner-logo--projx { height: clamp(26px, 2.22vw, 32px); }

/* The right column: the form, then the legal links beneath it. */
.enquire__main { min-width: 0; }

/* ---------- Fields ---------- */
.enquire__fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(20px, 2vw, 28px) clamp(20px, 2.2vw, 32px);
}

.field {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin: 0;
  min-width: 0;
  border: 0;
  padding: 0;
}
.field--full { grid-column: 1 / -1; }

.field__label {
  font-family: var(--font-label);
  font-variation-settings: "MONO" 1, "slnt" 0, "wght" 500;
  text-transform: uppercase;
  font-size: 12px;
  letter-spacing: 0.12em;
  /* Inherit, never a fixed navy: the ink travels with the background. */
  color: inherit;
  padding: 0;
}
.field__req { color: var(--enq-error); }
.field__optional {
  font-variation-settings: "MONO" 1, "slnt" 0, "wght" 400;
  text-transform: none;
  letter-spacing: 0.04em;
  opacity: 0.7;
}

.field__input {
  width: 100%;
  background: transparent;
  border: 0;
  /* currentColor at 0.6, so the 3:1 that WCAG 1.4.11 wants for a control
     boundary holds at both ends of the fade. A fixed rgba() would invert
     into the navy. */
  border-bottom: 1.5px solid color-mix(in srgb, currentColor 60%, transparent);
  border-radius: 0;
  padding: 9px 0;
  font-family: var(--font-label);
  font-variation-settings: "MONO" 0, "slnt" 0, "wght" 400;
  font-size: 16px; /* 16px stops iOS zooming the page on focus */
  line-height: 1.4;
  color: inherit;
  text-transform: none;
  transition: border-color 0.2s var(--ease);
}
.field__input:hover { border-bottom-color: color-mix(in srgb, currentColor 85%, transparent); }
.field__input:focus { outline: none; border-bottom-color: currentColor; }
.field__input:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
.field__input::placeholder { color: inherit; opacity: 0.55; }

/* The native select arrow goes with appearance:none. A masked pseudo-element
   on the field, rather than a background-image, because a data-URI SVG
   cannot pick up currentColor as the ink changes. */
.field__input--select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 28px;
  cursor: pointer;
}
.field--select::after {
  content: '';
  position: absolute;
  right: 2px;
  bottom: 18px;
  width: 12px;
  height: 8px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat center / contain;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat center / contain;
  pointer-events: none;
}
/* The option list is drawn by the OS on its own ground, so it needs a real
   colour rather than the inherited one. */
.field__input--select option { color: #1b1c2e; background: #fff; }

.field__input--area {
  resize: vertical;
  min-height: 104px;
  border: 1.5px solid color-mix(in srgb, currentColor 60%, transparent);
  border-radius: 6px;
  padding: 12px 14px;
}

/* ---------- Yes / No ---------- */
.field--choice { grid-column: auto; }
.field__choices {
  display: flex;
  gap: 28px;
  padding-top: 10px;
}
.choice { display: inline-flex; align-items: center; gap: 9px; }
.choice input {
  appearance: none;
  -webkit-appearance: none;
  /* Form controls do not inherit colour from their container, so without
     this currentColor resolves to the UA default black and the ring never
     follows the fade — 1.54:1 on navy. */
  color: inherit;
  width: 18px;
  height: 18px;
  margin: 0;
  border: 1.5px solid color-mix(in srgb, currentColor 60%, transparent);
  border-radius: 50%;
  cursor: pointer;
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
.choice input:checked {
  border-color: currentColor;
  /* The inner ring uses the block.s own navy so the dot reads as a hole
     punched through it. */
  box-shadow: inset 0 0 0 4px var(--color-blue), inset 0 0 0 9px currentColor;
}
.choice input:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
.choice label {
  font-family: var(--font-label);
  font-variation-settings: "MONO" 0, "slnt" 0, "wght" 400;
  text-transform: none;
  font-size: 15px;
  cursor: pointer;
}

/* ---------- Errors ---------- */
.field__error {
  font-family: var(--font-label);
  font-variation-settings: "MONO" 0, "slnt" 0, "wght" 400;
  text-transform: none;
  font-size: 13px;
  line-height: 1.3;
  color: var(--enq-error);
}
.field.is-invalid .field__input { border-bottom-color: var(--enq-error); }
.field.is-invalid .field__input--area { border-color: var(--enq-error); }

/* ---------- Actions ---------- */
.enquire__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(16px, 2vw, 28px);
  margin-top: clamp(28px, 3vw, 40px);
}
.enquire__submit { padding: 15px 34px; }
/* .btn-pill's default hover goes navy-on-navy here. Invert it instead. */
.enquire__submit:hover,
.enquire__submit:focus-visible {
  background: var(--color-cream);
  border-color: var(--color-cream);
  color: var(--color-blue);
}
.enquire__legal {
  flex: 1 1 280px;
  font-family: var(--font-label);
  font-variation-settings: "MONO" 0, "slnt" 0, "wght" 400;
  text-transform: none;
  font-size: 13px;
  line-height: 1.5;
  margin: 0;
  max-width: 42ch;
}
.enquire__legal a { text-decoration: underline; text-underline-offset: 3px; }

.enquire__status {
  margin: clamp(20px, 2.4vw, 32px) 0 0;
  padding: 14px 18px;
  border-radius: 8px;
  font-family: var(--font-label);
  font-variation-settings: "MONO" 0, "slnt" 0, "wght" 400;
  text-transform: none;
  font-size: 15px;
  line-height: 1.45;
}
.enquire__status--ok { background: color-mix(in srgb, currentColor 12%, transparent); }
.enquire__status--error {
  background: color-mix(in srgb, var(--enq-error) 16%, transparent);
  color: var(--enq-error);
}

@media (max-width: 860px) {
  .enquire__grid { grid-template-columns: minmax(0, 1fr); gap: clamp(32px, 6vw, 48px); }
  .enquire .heading-lg { max-width: none; }
}
@media (max-width: 560px) {
  /* Paired fields stop being readable once each column is under ~140px. */
  .enquire__fields { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- Breakpoints ----------
   Matching the 860 / 640 steps already used in styles.css. */
@media (max-width: 1100px) {
  .site-nav--home .site-nav__list { font-size: 13px; letter-spacing: 0.06em; }
}

@media (max-width: 860px) {
  .site-nav--home { display: none; }
  /* The burger comes from styles.css's own 860px rule; the pill would crowd
     it, and the mobile overlay carries its own Enquire. */
  .site-header--home > .site-header__inner > .btn-pill { display: none; }

  /* The two tracks stack, so the heading no longer needs the wider one and
     the big column gap becomes ordinary vertical spacing. */
  .intro { padding: clamp(48px, 7vw, 72px) 0; }
  .intro__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(22px, 4vw, 34px);
  }

  .activities__inner { grid-template-columns: minmax(0, 1fr); }
  .activities__media { order: -1; margin-bottom: 8px; }
}

@media (max-width: 640px) {
  .hero-video__content { padding-bottom: clamp(64px, 9vh, 96px); }
  /* The heading wraps to several lines on a phone, so the block covers much
     more of the frame and more of it lands on bright water. The overlay is
     flat, so this is simply a heavier setting of the same one number. */
  .hero-video__scrim { --hero-overlay: 0.62; }
  /* The expansion has no room to read on a phone; it stays at its inset. */
  .panorama__frame { clip-path: inset(0 var(--gutter) round 10px); }
  /* The <picture> swaps in the 4:5 crop here, so the box has to take that
     ratio too — left at 2:1 it would crop the taller file straight back to
     a letterbox and the separate mobile export would buy nothing. */
  .panorama__frame img { aspect-ratio: 4 / 5; }
}

/* ---------- Enquire band ----------
   The ground behind the form block: sand at the top, resolving to orange at
   the foot so it runs straight into the footer with no seam. The block
   itself is navy and opens out like the panorama. */
.enquire-band {
  background: linear-gradient(to bottom,
    var(--color-sand) 0%,
    var(--color-sand) 8%,
    var(--color-orange) 90%,
    var(--color-orange) 100%);
  padding: clamp(40px, 5.5vw, 80px) 0 clamp(40px, 5vw, 72px);
}

/* ---------- Footer ----------
   Shorter than the splash page's full-bleed slab, with the wordmark centred
   rather than bled off both edges. Proportions taken from the supplied
   layout: 0.2365 of the viewport width tall, wordmark 44% of it.

   logo-footer.svg has no clear space — its ink fills the box exactly — so
   the box width IS the wordmark width, and the descender running off the
   bottom edge is the crop the layout shows. */
/* The wordmark SVG carries clear space — its ink is 77.4% of the box width —
   so the BOX is sized to 56.8vw to land the visible wordmark at the 44vw the
   layout shows. Left, right and top stay clear; only the descender tip is
   cut, by the footer being a little shorter than the box. */
.site-footer {
  height: 23.65vw;
  /* A pure vw height collapses on a phone; the floor keeps the wordmark
     legible, and is low enough that the bottom crop still happens. */
  min-height: 140px;
}
.site-footer__logo {
  top: 2vw;
  width: 56.8vw;
  max-width: none;
}

@media (max-width: 860px) {
  /* The wordmark takes a bigger share of a narrow screen to stay readable,
     and the footer grows with it to hold the same proportional crop. */
  .site-footer { height: 30.5vw; }
  .site-footer__logo { top: 3vw; width: 72vw; }
}
@media (max-width: 640px) {
  .site-footer { height: 36.8vw; }
  .site-footer__logo { top: 4vw; width: 86vw; }
}

/* ---------- Legal, now in the form block ---------- */
/* Under the form, in the right column, separated from the form's own
   actions by a hairline — the same 60% mix as the form control borders, so
   it reads as part of the block. Decorative here (not a control), so it is
   not held to the 3:1 the controls are. */
.enquire__legal-nav {
  margin-top: clamp(32px, 3.4vw, 48px);
  padding-top: clamp(20px, 2vw, 28px);
  border-top: 1px solid color-mix(in srgb, currentColor 25%, transparent);
}
.enquire__legal-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 0;
  font-family: var(--font-label);
  font-variation-settings: "MONO" 0, "slnt" 0, "wght" 400;
  text-transform: none;
  font-size: 13px;
  line-height: 1.5;
}
.enquire__legal-nav li:not(:last-child)::after {
  content: '|';
  margin: 0 10px;
  opacity: 0.45;
}
.enquire__legal-nav a {
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* On a phone the list wraps, and a pipe separator left at the end of the
   first line reads as a stray mark. Spaced with a gap instead there. */
@media (max-width: 600px) {
  .enquire__legal-nav ul { gap: 8px 20px; }
  .enquire__legal-nav li:not(:last-child)::after { display: none; }
}
