:root{
  /* DEEP BRASS on near-black.

     The names say what the colour IS, which they previously did not: the
     tokens were called --gold and --blue-NNN while holding blue and black
     respectively, left over from an earlier theme swap. Anyone editing
     "--gold" was changing a blue. Renamed to roles, and the aliases folded
     in — 21 tokens held only 15 distinct values, with --gold and --blue-600
     being literally the same colour.

     Contrast, measured on --bg #050607, not carried over:
       --accent      #B08D57   6.6:1   AA as text; 6.3:1 under #0c0c0c fill
       --accent-lt   #D4B483  10.3:1   AA as text
       --accent-dk   #7A6038   3.4:1   NOT a text colour at body size.
                                      Fills, strokes and gradient stops only.
                                      It does appear as the last stop of the
                                      .grad display type, where 30.6px/600
                                      counts as large text and 3.3:1 clears
                                      the 3:1 bar that applies there.

     The logo and favicon do NOT follow this palette. The mark runs the brand
     blue #4fa2ee to #0a4a94, matching the printed material, which makes it
     the one cool element on a warm page — a deliberate choice, not an
     oversight. It was briefly recoloured to brass and changed back.
     Drawn as an SVG gradient in logoSvg() (site.js) and duplicated in the
     favicon data-URI (build_pages.py): change one and you must change the
     other, since neither reads these tokens. */
  --accent:#B08D57; --accent-lt:#D4B483; --accent-dk:#7A6038;
  --accent-wash:#241E14;   /* dark brass tint: icon beds, chips, pills */
  --accent-bed:#12100B;    /* near-black brass tint: quiet surfaces */

  --ink:#f4f6f9; --ink-2:#dbcfc9; --muted:#a8a198; --line:#1f2429;
  --surface:#0c0e12; --bg:#050607; --sand:#0c0e12; --sand-line:#1f2429;
  --black:#000000; --lift:#0f1216;

  /* Apple's typography, the only way it can legitimately be used here.
     SF Pro cannot be served as a webfont: its licence covers designing for
     Apple platforms and mock-ups, not shipping font files from a commercial
     site. But -apple-system resolves to San Francisco already installed on
     the device, so every iPhone, iPad and Mac renders the real thing with
     nothing downloaded. Inter carries the same look everywhere else — it was
     drawn as an SF-like UI face and is the standard open substitute.
     Display and body share the stack because Apple uses one family for both,
     switching optical size rather than typeface. */
  --f-display:-apple-system,BlinkMacSystemFont,'SF Pro Display','SF Pro Text',
    'Inter','Segoe UI',Roboto,'Helvetica Neue',sans-serif;
  --f-body:-apple-system,BlinkMacSystemFont,'SF Pro Text','SF Pro Display',
    'Inter','Segoe UI',Roboto,'Helvetica Neue',sans-serif;
  --shadow-s:0 2px 10px rgba(0,0,0,.5);
  --shadow-m:0 18px 50px rgba(0,0,0,.6);
  --shadow-l:0 36px 96px rgba(0,0,0,.7);
  --ease:cubic-bezier(.22,1,.36,1);
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;overflow-x:hidden}
body{background:var(--bg);color:var(--ink);font-family:var(--f-body);
  font-size:16px;line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased;
  font-feature-settings:"cv02","cv03","cv04","ss01"}
img{max-width:100%;display:block}
a{color:inherit}
::selection{background:var(--accent);color:#fff}
.wrap{max-width:1340px;margin:0 auto;padding:0 clamp(1.1rem,4vw,3rem)}
h1,h2,h3,h4,.num{font-family:var(--f-display);letter-spacing:normal;font-weight:600;line-height:1.14}
.num{font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}

/* Gradient emphasis text (corporate, replaces the old serif italic) */
.grad{color:var(--accent)}
@supports (-webkit-background-clip:text) or (background-clip:text){
  .grad{background:linear-gradient(100deg,var(--accent-lt) 0%,var(--accent) 55%,var(--black) 100%);
    -webkit-background-clip:text;background-clip:text;color:transparent;
    -webkit-text-fill-color:transparent}
}

/* ---------- Ambient field ---------- */
#waves{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden}
#waves svg{position:absolute;width:140%;height:100%;left:-20%;top:0}
.wave-path{fill:none;stroke:var(--accent-dk);stroke-width:1;opacity:.28}

main,header,footer,section{position:relative;z-index:1}

/* ---------- Preloader ---------- */
#preloader{position:fixed;inset:0;z-index:9999;background:var(--bg);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1.5rem}
#preloader .mark{height:92px;width:auto}
.pre-bar{width:210px;height:2px;background:var(--accent-wash);border-radius:4px;overflow:hidden}
.pre-bar i{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--accent-lt),var(--accent-lt))}
.pre-lab{display:flex;justify-content:space-between;width:210px;font-size:0.62rem;
  letter-spacing:.26em;color:var(--muted);font-weight:500;text-transform:uppercase}

/* ---------- Cursor ---------- */

/* ---------- Header ---------- */
.header{position:fixed;top:0;left:0;width:100%;z-index:540;padding:1.25rem 0;transition:padding .5s var(--ease)}
.header .wrap{display:flex;align-items:center;justify-content:space-between;gap:1.2rem;position:relative}
/* Transparent at every scroll position, by request — no plate behind the
   nav at all, matching the reference (text and logo float directly on
   whatever is behind them). .shell is kept as an empty, inert node rather
   than deleted: removing it would mean re-touching every place that toggles
   .stuck, and something may yet want a hairline back under the nav. The
   text-shadow below is what actually carries legibility now that there is no
   background to guarantee contrast against a bright frame of the hero video. */
.header .shell{position:absolute;inset:0;background:transparent;
  border-bottom:1px solid transparent;pointer-events:none;
  transition:background .4s var(--ease),backdrop-filter .4s var(--ease)}
.header.stuck{padding:.75rem 0}
/* Fully transparent only at rest, over the hero — a real bug forced this
   split. With no backing at ANY scroll depth, .stmt's oversized statement
   text (up to 4.4rem/~70px) scrolls straight through the fixed header on
   every inner section, showing fragments of giant type behind the nav
   ("year." was visible mid-word behind "BOOK A SITE VISIT" on a real
   screenshot). A blurred, low-opacity scrim only once .stuck (i.e. once the
   hero has been scrolled past into ordinary page content) keeps the header
   transparent over the hero exactly as asked, while stopping body copy from
   visually colliding with the nav everywhere else. */
.header.stuck .shell{background:rgba(12,12,12,.5);backdrop-filter:blur(16px);
  -webkit-backdrop-filter:blur(16px)}
.brand,.nav a,.nav .drop-toggle{text-shadow:0 1px 10px rgba(0,0,0,.55)}
.brand{display:flex;align-items:center;gap:.68rem;text-decoration:none;position:relative;z-index:2}
.brand svg{height:42px;width:auto;flex-shrink:0;
  transition:height .5s var(--ease)}
.header.stuck .brand svg{height:36px}
.brand-txt{display:flex;flex-direction:column;line-height:1.05;padding-top:2px}
.brand-txt b{font-family:var(--f-display);font-weight:600;font-size:1.15rem;letter-spacing:.03em;
  text-transform:uppercase;color:var(--ink);line-height:1}
.brand-txt small{font-size:0.62rem;letter-spacing:.345em;color:var(--accent);font-weight:500;
  text-transform:uppercase;margin-top:.2rem}
.nav{display:flex;gap:.15rem;align-items:center;position:relative;z-index:2}
.nav a{text-decoration:none;font-size:0.9rem;font-weight:500;color:var(--ink-2);
  padding:.62rem 1.05rem;border-radius:100px;position:relative;transition:color .3s;letter-spacing:-.012em}
.nav a span{position:relative;z-index:2}
.nav-pill{position:absolute;top:0;left:0;height:100%;border-radius:100px;background:var(--accent-bed);
  opacity:0;z-index:1;pointer-events:none}
.nav a:hover,.nav a.active{color:var(--accent)}
.head-cta{display:flex;align-items:center;gap:.6rem;position:relative;z-index:2}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.65rem;border:none;cursor:pointer;
  font-family:var(--f-body);font-weight:600;font-size:0.82rem;text-decoration:none;letter-spacing:-.005em;
  padding:.82rem 1.45rem;border-radius:100px;position:relative;overflow:hidden;white-space:nowrap;
  transition:transform .4s var(--ease),box-shadow .4s,background .35s,color .35s,border-color .35s}
.btn-primary{background:var(--accent);color:#0c0c0c;box-shadow:0 10px 26px rgba(176,141,87,.3)}
.btn-primary:hover{background:var(--accent);box-shadow:0 18px 40px rgba(176,141,87,.4)}
.btn-ghost{background:rgba(255,255,255,.7);color:var(--ink);border:1.5px solid var(--line)}
.btn-ghost:hover{border-color:var(--accent-lt);color:var(--accent);background:var(--surface)}
.btn-white{background:var(--accent);color:#0c0c0c;box-shadow:var(--shadow-m)}
.btn .arw{width:25px;height:25px;border-radius:50%;background:rgba(255,255,255,.22);
  display:grid;place-items:center;transition:transform .4s var(--ease);flex-shrink:0}
.btn:hover .arw{transform:translateX(3px)}
.btn-ghost .arw,.btn-white .arw{background:var(--accent-bed)}

/* ---------- Shiny CTA (contact form submit only — one flourish, not a
   sitewide button style) ----------
   Ported from a React/styled-jsx component: the "use client" directive and
   <style jsx> block don't mean anything outside Next.js, so this is the
   same conic-gradient technique as a plain CSS class instead. Colors swapped
   from the demo's black/blue to the site's own tokens (--lift, --accent,
   --accent-lt, --ink) so it reads as this site's premium accent, not a
   borrowed one. @property is Houdini — Firefox and older Safari just render
   the gradient static instead of animating it, which is a fine fallback,
   consistent with how the rest of this site degrades. */
@property --gradient-angle{syntax:"<angle>";initial-value:0deg;inherits:false}
@property --gradient-angle-offset{syntax:"<angle>";initial-value:0deg;inherits:false}
@property --gradient-percent{syntax:"<percentage>";initial-value:5%;inherits:false}
@property --gradient-shine{syntax:"<color>";initial-value:white;inherits:false}
.shiny-cta{
  --shiny-bg:var(--lift);--shiny-bg-subtle:var(--line);--shiny-fg:var(--ink);
  --shiny-hi:var(--accent);--shiny-hi-subtle:var(--accent-lt);
  --duration:3s;--transition:800ms cubic-bezier(.25,1,.5,1);
  isolation:isolate;position:relative;overflow:hidden;cursor:pointer;outline-offset:4px;
  display:inline-flex;align-items:center;justify-content:center;gap:.65rem;width:100%;
  padding:1rem 1.6rem;font-family:var(--f-body);font-size:.92rem;font-weight:600;
  letter-spacing:-.005em;line-height:1.2;border:1px solid transparent;border-radius:100px;
  color:var(--shiny-fg);
  background:linear-gradient(var(--shiny-bg),var(--shiny-bg)) padding-box,
    conic-gradient(from calc(var(--gradient-angle) - var(--gradient-angle-offset)),
      transparent,var(--shiny-hi) var(--gradient-percent),
      var(--gradient-shine) calc(var(--gradient-percent) * 2),
      var(--shiny-hi) calc(var(--gradient-percent) * 3),
      transparent calc(var(--gradient-percent) * 4)) border-box;
  box-shadow:inset 0 0 0 1px var(--shiny-bg-subtle);
  transition:var(--transition);
  transition-property:--gradient-angle-offset,--gradient-percent,--gradient-shine,box-shadow,transform;
}
.shiny-cta::before,.shiny-cta::after,.shiny-cta span::before{
  content:"";pointer-events:none;position:absolute;inset-inline-start:50%;inset-block-start:50%;
  translate:-50% -50%;z-index:-1}
.shiny-cta:active{translate:0 1px}
.shiny-cta::before{--size:calc(100% - 2px * 3);--position:2px;--space:calc(var(--position) * 2);
  width:var(--size);height:var(--size);
  background:radial-gradient(circle at var(--position) var(--position),var(--shiny-fg) calc(var(--position) / 4),transparent 0) padding-box;
  background-size:var(--space) var(--space);background-repeat:space;
  mask-image:conic-gradient(from calc(var(--gradient-angle) + 45deg),black,transparent 10% 90%,black);
  border-radius:inherit;opacity:.25;z-index:-1}
.shiny-cta::after{width:100%;aspect-ratio:1;
  background:linear-gradient(-50deg,transparent,var(--shiny-hi),transparent);
  mask-image:radial-gradient(circle at bottom,transparent 40%,black);opacity:.5}
.shiny-cta span{z-index:1;display:inline-flex;align-items:center;gap:.65rem}
.shiny-cta span::before{--size:calc(100% + 1rem);width:var(--size);height:var(--size);
  box-shadow:inset 0 -1ex 2rem 4px var(--shiny-hi);opacity:0;
  transition:opacity var(--transition);animation:calc(var(--duration) * 1.5) shiny-breathe linear infinite}
.shiny-cta,.shiny-cta::before,.shiny-cta::after{
  animation:gradient-angle var(--duration) linear infinite,
    gradient-angle calc(var(--duration) / .4) linear infinite reverse paused;
  animation-composition:add}
.shiny-cta:is(:hover,:focus-visible){--gradient-percent:20%;--gradient-angle-offset:95deg;
  --gradient-shine:var(--shiny-hi-subtle)}
.shiny-cta:is(:hover,:focus-visible),.shiny-cta:is(:hover,:focus-visible)::before,
.shiny-cta:is(:hover,:focus-visible)::after{animation-play-state:running}
.shiny-cta:is(:hover,:focus-visible) span::before{opacity:1}
.shiny-cta .arw{width:25px;height:25px;border-radius:50%;background:rgba(176,141,87,.18);
  display:grid;place-items:center;transition:transform .4s var(--ease);flex-shrink:0}
.shiny-cta:hover .arw{transform:translateX(3px)}
@keyframes gradient-angle{to{--gradient-angle:360deg}}
@keyframes shiny-breathe{from,to{scale:1}50%{scale:1.2}}

.burger{display:none;width:42px;height:42px;border-radius:50%;border:1.5px solid var(--line);
  background:var(--surface);cursor:pointer;flex-direction:column;align-items:center;justify-content:center;gap:4px;
  position:relative;z-index:530}
.burger i{display:block;width:15px;height:1.6px;background:var(--ink);transition:transform .4s var(--ease),opacity .3s}
.burger.open i:nth-child(1){transform:translateY(5.6px) rotate(45deg)}
.burger.open i:nth-child(2){opacity:0}
.burger.open i:nth-child(3){transform:translateY(-5.6px) rotate(-45deg)}

/* ---------- Hero ---------- */
.hero{min-height:100svh;display:flex;align-items:center;padding:9.5rem 0 4rem;position:relative}

.tagline .dot{width:6px;height:6px;border-radius:50%;background:var(--accent-lt);animation:pulse 2.4s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.3;transform:scale(1.55)}}

.line-mask{display:block;overflow:hidden;padding-bottom:.06em}
.line-mask .ch{display:inline-block;transform:translateY(105%);will-change:transform}

.mark-ring.r1{inset:-9%}
.mark-ring.r2{inset:-21%;border-style:dashed;opacity:.38}

.float-chip .ic{width:26px;height:26px;border-radius:4px;background:var(--accent-bed);
  color:var(--accent);display:grid;place-items:center;flex-shrink:0}

@keyframes hint{0%,100%{opacity:.2;transform:scaleY(.55)}50%{opacity:1;transform:scaleY(1)}}

/* ---------- Section shell ---------- */
.section{padding:clamp(4.5rem,9vw,8rem) 0}
.eyebrow{display:inline-flex;align-items:center;gap:.55rem;font-size:0.62rem;font-weight:500;
  letter-spacing:.22em;text-transform:uppercase;color:var(--accent);margin-bottom:1.05rem}
.eyebrow::before{content:'';width:20px;height:1.5px;background:var(--accent-lt)}
.s-title{font-size:clamp(1.9rem,3.9vw,3.3rem);line-height:1.1;letter-spacing:-.012em;color:var(--ink)}
.s-sub{color:var(--muted);font-size:1rem;line-height:1.8;max-width:600px;margin-top:1.05rem}
.s-head{margin-bottom:3.1rem;max-width:760px}
.s-head.center{margin-inline:auto;text-align:center}
/* One-line heading.

   "From the people who bought" was breaking after "who" and dropping "bought"
   onto a line of its own. The type was never the problem — .s-head caps every
   section header at 760px for measure, while .s-title keeps growing to 70.4px,
   so at 1440 the line wanted 954px inside a 737px column. Its .wrap has 1340px
   sitting unused.

   Lifting the cap for this one header is the fix that leaves the type scale
   alone; capping the font instead would have meant 54px here against 70px on
   every neighbouring heading. No nowrap: 1340px against 954px needed is enough
   headroom that a wider font stack than the one measured still fits, where
   nowrap would have turned any miss into a horizontal scrollbar.

   Phones still wrap it, and deliberately. At 375px the column IS the viewport
   at 315px, so one line there needs 24px type against a 35.2px floor — a third
   smaller than every other section heading on the page. */
.s-head-wide{max-width:none}
.s-head.center .eyebrow{justify-content:center}
.s-head.center .s-sub{margin-inline:auto}
.head-row{display:flex;justify-content:space-between;align-items:flex-end;gap:2rem;margin-bottom:3.1rem;flex-wrap:wrap}
.head-row .s-head{margin-bottom:0}

/* ---------- About ---------- */
.about{display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,5vw,4.5rem);align-items:center}
.about p{color:var(--muted);line-height:1.9;margin-bottom:1.05rem}
.about p strong{color:var(--ink);font-weight:600}
.pill-list{display:flex;flex-direction:column;gap:.7rem;margin:1.7rem 0 1.9rem}
.pill-list div{display:flex;align-items:center;gap:.75rem;font-size:0.9rem;font-weight:600;color:var(--ink-2)}
.tick{width:21px;height:21px;border-radius:50%;background:var(--accent);color:#fff;
  display:grid;place-items:center;flex-shrink:0;font-size:0.62rem;font-weight:600}
.about-media{position:relative;display:grid;grid-template-columns:1fr 1fr;gap:1rem}
html.js .about-media figure{overflow:hidden;border-radius:4px;box-shadow:var(--shadow-m);background:var(--accent-bed);
  aspect-ratio:3/2;clip-path:inset(0 0 100% 0)}
/* Specificity note: the base rule above is "html.js .about-media figure"
   (two classes + one element). ".about-media figure.shown" alone is only
   two classes + one element too, but counts the SAME two classes — .shown
   never actually out-specifies html.js, so the reveal could never win and
   this collage has been permanently clipped to nothing. html.js prefix
   added here purely to break the tie in .shown's favor. */
html.js .about-media figure.shown{clip-path:inset(0 0 0 0);transition:clip-path 1.15s var(--ease)}
.about-media figure:first-child{grid-column:1 / -1;border-radius:0}
.about-media img{width:100%;height:100%;object-fit:cover;object-position:center;
  transition:transform 1.2s var(--ease)}

.about-media figure:hover img{transform:scale(1.07)}

/* ---------- Projects ---------- */
.filters{display:flex;gap:.45rem;flex-wrap:wrap}
/* 37px -> 44px. These three are the only control on the projects page and
   they were the smallest touch target on the site. min-height rather than more
   padding, for the same reason as .btn: a padding figure that clears 44px at
   one font size stops clearing it at another, and this chip sizes off 0.82rem.
   inline-flex because min-height on a button leaves the label sitting on the
   padding box rather than centred in the taller pill. */
.filters button{font-family:var(--f-body);font-size:0.82rem;font-weight:600;letter-spacing:-.005em;
  display:inline-flex;align-items:center;justify-content:center;min-height:44px;
  padding:.6rem 1.25rem;border-radius:100px;border:1.5px solid var(--line);background:var(--surface);
  color:var(--muted);cursor:pointer;transition:all .35s var(--ease)}
.filters button:hover{border-color:var(--accent-dk);color:var(--accent)}
.filters button.active{background:var(--accent);border-color:var(--accent);color:#0c0c0c;
  box-shadow:0 8px 22px rgba(176,141,87,.28)}

.p-card.hide{display:none}

.p-status{position:absolute;top:1rem;left:1rem;z-index:2;font-size:0.62rem;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;padding:.4rem .85rem;border-radius:100px;backdrop-filter:blur(8px)}
.p-status.ongoing{background:var(--accent);color:#fff}
.p-status.completed{background:rgba(255,255,255,.94);color:var(--accent-lt)}
.p-count{position:absolute;top:1rem;right:1rem;z-index:2;font-size:0.62rem;font-weight:500;
  letter-spacing:.08em;padding:.4rem .8rem;border-radius:100px;background:rgba(18,14,9,.55);
  color:#fff;backdrop-filter:blur(8px);display:flex;align-items:center;gap:.35rem}
.p-loc{position:absolute;left:1.2rem;bottom:1rem;z-index:2;color:#fff;font-size:0.72rem;
  font-weight:500;letter-spacing:.14em;text-transform:uppercase;display:flex;align-items:center;gap:.4rem}

.p-type{font-size:0.62rem;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--accent)}

.p-amen{display:flex;flex-wrap:wrap;gap:.38rem;margin-bottom:1.25rem}
.p-amen i{font-style:normal;font-size:0.62rem;font-weight:500;padding:.32rem .65rem;border-radius:100px;
  background:var(--accent-bed);color:var(--accent);border:1px solid var(--accent-wash)}
.p-amen i.more{background:transparent;color:var(--muted);border-color:var(--line)}
.p-foot{margin-top:auto;display:flex;align-items:center;justify-content:space-between;gap:1rem}

.p-dev{font-size:0.62rem;color:var(--muted);font-weight:500}

/* ---------- Modal ---------- */
.modal{position:fixed;inset:0;z-index:8000;display:none;align-items:center;justify-content:center;padding:1.2rem}
.modal.open{display:flex}
.modal-bg{position:absolute;inset:0;background:rgba(18,14,9,.52);backdrop-filter:blur(9px);opacity:0}
.modal-box{position:relative;background:var(--surface);border-radius:4px;width:min(1000px,100%);
  max-height:92vh;overflow-y:auto;box-shadow:var(--shadow-l);opacity:0;transform:translateY(30px) scale(.97)}
.modal-close{position:absolute;top:1rem;right:1rem;z-index:5;width:42px;height:42px;border-radius:50%;
  border:none;background:rgba(255,255,255,.95);box-shadow:var(--shadow-m);cursor:pointer;
  display:grid;place-items:center;color:var(--ink);transition:transform .4s var(--ease),background .3s}
.modal-close:hover{transform:rotate(90deg)}
.modal-hero{position:relative;aspect-ratio:16/8;overflow:hidden;background:var(--accent-bed)}
.modal-hero img{width:100%;height:100%;object-fit:cover}
.modal-hero::after{content:'';position:absolute;inset:0;background:linear-gradient(180deg,transparent 40%,rgba(18,14,9,.82) 100%)}
.modal-hero .cap{position:absolute;left:clamp(1.2rem,3vw,2.2rem);bottom:1.4rem;z-index:2;color:#fff}
.modal-hero .cap h3{font-size:clamp(1.6rem,3.1vw,2.4rem);line-height:1.1;letter-spacing:-.012em}
.modal-hero .cap p{font-size:0.72rem;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  opacity:.92;margin-top:.5rem;display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
.modal-body{padding:clamp(1.4rem,3vw,2.3rem)}

.modal-body>p{color:var(--muted);line-height:1.9;margin-bottom:1.05rem}

/* ---------- Lightbox ---------- */
.lightbox{position:fixed;inset:0;z-index:8500;display:none;align-items:center;justify-content:center;
  background:rgba(9,7,5,.93);padding:1.4rem}
.lightbox.open{display:flex}
.lightbox img{max-width:94vw;max-height:86vh;border-radius:4px;box-shadow:0 40px 90px rgba(0,0,0,.5)}
.lb-btn{position:absolute;width:48px;height:48px;border-radius:50%;border:none;cursor:pointer;
  background:rgba(255,255,255,.15);color:#fff;display:grid;place-items:center;
  backdrop-filter:blur(8px);transition:background .3s}
.lb-btn:hover{background:rgba(255,255,255,.3)}
.lb-prev{left:1.4rem;top:50%;transform:translateY(-50%)}
.lb-next{right:1.4rem;top:50%;transform:translateY(-50%)}
.lb-close{top:1.4rem;right:1.4rem}
.lb-count{position:absolute;bottom:1.4rem;left:50%;transform:translateX(-50%);color:#fff;
  font-size:0.72rem;font-weight:500;letter-spacing:.14em;background:rgba(255,255,255,.14);
  padding:.45rem 1rem;border-radius:100px;backdrop-filter:blur(8px)}

.svc-ic{width:50px;height:50px;border-radius:4px;background:linear-gradient(135deg,var(--accent-lt),var(--accent));
  color:#fff;display:grid;place-items:center;margin-bottom:1.25rem;box-shadow:0 10px 22px rgba(176,141,87,.3);
  transition:transform .5s var(--ease)}
.svc-c:hover .svc-ic{transform:rotate(-8deg) scale(1.07)}

/* ---------- Testimonials ---------- */
.t-track{display:flex;gap:1.25rem;overflow-x:auto;padding-bottom:1.4rem;scroll-snap-type:x mandatory;
  scrollbar-width:thin;scrollbar-color:var(--accent-dk) var(--accent-bed);cursor:grab}
.t-track.dragging{cursor:grabbing;scroll-snap-type:none;user-select:none}
.t-track::-webkit-scrollbar{height:5px}
.t-track::-webkit-scrollbar-thumb{background:var(--accent-dk);border-radius:100px}
.t-track::-webkit-scrollbar-track{background:var(--accent-bed);border-radius:100px}
.t-c{flex:0 0 min(390px,82vw);scroll-snap-align:start;background:var(--surface);border:1px solid var(--line);
  border-radius:4px;padding:2rem;display:flex;flex-direction:column;transition:box-shadow .45s,border-color .4s}
.t-c:hover{box-shadow:var(--shadow-m);border-color:var(--accent-wash)}
.stars{display:flex;gap:.15rem;color:var(--accent-lt);margin-bottom:1.05rem}
.t-c p{color:var(--ink-2);font-size:0.9rem;line-height:1.85;flex:1}
.t-who{display:flex;align-items:center;gap:.8rem;margin-top:1.5rem;padding-top:1.25rem;border-top:1px solid var(--line)}
.t-av{width:42px;height:42px;border-radius:50%;background:linear-gradient(135deg,var(--accent-lt),var(--accent));
  color:#fff;display:grid;place-items:center;font-weight:600;flex-shrink:0;font-family:var(--f-display)}
.t-who b{display:block;font-size:0.9rem;font-weight:500;font-family:var(--f-body);letter-spacing:0}
.t-who span{display:block;font-size:0.82rem;color:var(--muted);margin-top:1px}

/* ---------- CTA band ---------- */
.band{border-radius:4px;overflow:hidden;position:relative;
  background:linear-gradient(120deg,var(--black),var(--accent) 58%,var(--accent-lt));
  color:#fff;display:grid;grid-template-columns:1.1fr .9fr;align-items:center}
.band-wave{position:absolute;inset:0;opacity:.26;pointer-events:none}
.band-in{padding:clamp(2.2rem,4.5vw,3.5rem);position:relative;z-index:2}
.band .eyebrow{color:#fff;opacity:.85}
.band .eyebrow::before{background:rgba(255,255,255,.6)}
.band h2{font-size:clamp(1.75rem,3.1vw,2.7rem);line-height:1.12;letter-spacing:-.012em;margin-bottom:1.05rem}
.band p{opacity:.9;line-height:1.8;margin-bottom:1.9rem;max-width:520px}
.band-pts{display:flex;flex-direction:column;gap:.65rem;margin-bottom:1.9rem}
.band-pts div{display:flex;align-items:center;gap:.7rem;font-size:0.9rem;font-weight:600}
.band-pts .tick{background:rgba(255,255,255,.24)}
.band-media{position:relative;min-height:100%;align-self:stretch;overflow:hidden}
.band-media img{width:100%;height:100%;object-fit:cover;min-height:340px}

.blog-c .tag{font-size:0.62rem;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);margin-bottom:.95rem}

/* ---------- Contact ---------- */
.contact{display:grid;grid-template-columns:1fr 1fr;gap:1.35rem}
.card-w{background:var(--surface);border:1px solid var(--line);border-radius:4px;padding:clamp(1.5rem,3vw,2.3rem)}
.field{margin-bottom:1rem}
.field label{display:block;font-size:0.62rem;font-weight:500;letter-spacing:.15em;text-transform:uppercase;
  color:var(--muted);margin-bottom:.45rem}
.field input,.field select,.field textarea{width:100%;padding:.9rem 1.05rem;border-radius:4px;
  background:var(--bg);border:1.5px solid var(--line);color:var(--ink);font-family:var(--f-body);
  font-size:0.9rem;transition:border-color .3s,background .3s,box-shadow .3s;outline:none}
.field textarea{resize:vertical;min-height:104px}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--accent-lt);background:var(--surface);
  box-shadow:0 0 0 4px rgba(176,141,87,.3)}
.field input[aria-invalid="true"]{border-color:#d64545;box-shadow:0 0 0 4px rgba(214,69,69,.12)}
.note{font-size:0.72rem;color:var(--muted);margin-top:.85rem;line-height:1.6}
.c-lines{display:flex;flex-direction:column;gap:1.35rem;margin-bottom:1.6rem}
.c-line{display:flex;gap:1rem;align-items:flex-start}
.c-line .svc-ic{width:44px;height:44px;border-radius:4px;margin:0;flex-shrink:0}
.c-line span{display:block;font-size:0.62rem;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);margin-bottom:.28rem}
.c-line a,.c-line p{font-size:0.9rem;font-weight:600;color:var(--ink);text-decoration:none;line-height:1.6}
.c-line a:hover{color:var(--accent)}
.map-wrap{border-radius:4px;overflow:hidden;border:1px solid var(--line)}
.map-wrap iframe{display:block;width:100%;height:220px;border:0}

/* ---------- Footer ---------- */
.footer{background:var(--black);color:#fff;padding:4.4rem 0 1.8rem;
  margin-top:clamp(3rem,6vw,5rem);overflow:hidden;position:relative}
.footer-wave{position:absolute;inset:0;opacity:.17;pointer-events:none}
.f-grid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1.2fr;gap:2.6rem;position:relative;z-index:2}
.footer .brand-txt b{color:#fff}
/* Both of these asked for --accent-dk as TEXT on a black footer: 3.5:1,
   under AA. Neither was reaching the page — the wordmark rule is overridden
   twice further down, and no page has a footer <h4> — so nothing on the site
   looked wrong. Corrected anyway: left as they were, they are a trap that
   pays out the moment someone deletes an override or adds an h4 down here,
   and they contradict the fills-only rule stated in :root. --accent is what
   the overrides already resolve to, at 6.79:1. */
.footer .brand-txt small{color:var(--accent)}
.footer h4{font-size:0.62rem;font-weight:500;letter-spacing:.2em;text-transform:uppercase;
  color:var(--accent);margin-bottom:1.15rem;font-family:var(--f-body)}
.footer ul{list-style:none;display:flex;flex-direction:column;gap:.68rem}
.footer a{color:rgba(255,255,255,.72);text-decoration:none;font-size:0.9rem;transition:color .3s}
.footer a:hover{color:#fff}
.footer p{color:rgba(255,255,255,.72);font-size:0.9rem;line-height:1.8}
.socials{display:flex;gap:.55rem;margin-top:1.35rem}
.socials a{width:37px;height:37px;border-radius:50%;background:rgba(255,255,255,.1);
  display:grid;place-items:center;color:#fff;transition:all .35s var(--ease)}
.socials a:hover{background:var(--accent-lt);transform:translateY(-3px)}
.f-bot{margin-top:2.9rem;padding-top:1.5rem;border-top:1px solid rgba(255,255,255,.14);
  display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap;
  font-size:0.82rem;color:rgba(255,255,255,.6);position:relative;z-index:2;
  /* Keep this row out from under the .fab call buttons, which are fixed to the
     viewport at z-600 and sit exactly in the band the last footer row occupies
     once you reach the bottom of the page. Measured at 375px: the buttons
     covered the tail of the copyright by 12px and 8px of the Careers LINK, so
     part of a real navigation target was not tappable.

     Not a phone-only problem, which is why there is no breakpoint here: .wrap's
     gutter is clamp(1.1rem,4vw,3rem), so it tops out at 48px while the fab
     column needs 50px + 1.3rem = 71px. Every width below about 1386px collided,
     1366x768 laptops included; only past that does centring inside the 1340px
     max-width open a wide enough margin.

     So the reserve is derived instead: (100vw - 100%)/2 is the gutter this row
     already has, and the padding is whatever the fab still needs beyond it. It
     resolves to 0 once the gutter is sufficient, so wide screens are untouched
     -- measured 69.2px at 375, 38.3px at 1024, 25.8px at 1366, 0px at 1440.
     The extra 1rem absorbs 100vw counting the scrollbar while 100% does not. */
  padding-right:max(0px, calc(50px + 1.3rem + 1rem - (100vw - 100%)/2))}
.f-bot a{font-size:0.82rem}

/* ---------- FAB ---------- */
.fab{position:fixed;right:1.3rem;bottom:1.3rem;z-index:600;display:flex;flex-direction:column;gap:.6rem}
.fab a{width:50px;height:50px;border-radius:50%;display:grid;place-items:center;
  box-shadow:var(--shadow-m);transition:transform .35s var(--ease);color:#fff}
.fab a:hover{transform:scale(1.09)}
.fab .wa{background:#25d366}
.fab .ph{background:var(--accent)}

/* ---------- Reveal ---------- */
/* Gated on html.js, which an inline <head> script sets before first paint.
   If JS is blocked, errors early, or never runs, the class is absent and this
   rule never applies — the content is simply visible. Reveal animation must
   never be the thing standing between a reader and the page. */
html.js .rv{opacity:0;transform:translateY(56px) scale(.97)}
html.js .rv.in{opacity:1;transform:none;transition:opacity 1.15s var(--ease),transform 1.15s var(--ease)}

/* ---------- Focus visibility ---------- */
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,
[tabindex]:focus-visible{outline:2.5px solid var(--accent);outline-offset:3px;border-radius:4px}

/* ---------- Responsive ---------- */
@media (max-width:1024px){

  
  
  .about,.contact,.band{grid-template-columns:1fr}
  .band-media{min-height:260px}
  .about-media figure:first-child{border-radius:4px}
  
  .f-grid{grid-template-columns:1fr 1fr}
}
@media (max-width:820px){
  .brand svg{height:38px}
  .header.stuck .brand svg{height:34px}
  .brand-txt b{font-size:1.15rem;letter-spacing:.03em}
  .brand-txt small{font-size:0.62rem;letter-spacing:.345em}
  .header{padding:1rem 0}
  .hero{padding:8rem 0 3rem}
  .page-hero{padding:8.5rem 0 2.4rem}
  .nav{position:fixed;top:0;right:0;height:100dvh;width:min(84vw,330px);background:var(--surface);
    flex-direction:column;align-items:flex-start;justify-content:center;gap:.3rem;
    padding:2rem;box-shadow:var(--shadow-l);transform:translateX(100%);
    transition:transform .55s var(--ease);z-index:520}
  .nav.open{transform:none}
  /* The off-canvas panel only ever covers ~84vw. Without something behind it,
     the rest of the page — including the header/logo above it — stayed fully
     visible and legible through the gap: a real, longstanding gap this had
     before today, found by actually looking at a phone-width screenshot
     rather than by code review. Sits above page content (1), below the
     panel (520), and doubles as click-outside-to-close. Note: .header itself
     had to move to z-index 540 (above .nav) for this to work right — the
     burger button lives inside .header, and a descendant's own z-index can
     never lift it above a sibling of its ancestor, so with header still at
     500 the open panel was quietly covering the burger, making the close
     tap a dead spot. */
  .nav-scrim{position:fixed;inset:0;z-index:510;background:rgba(0,0,0,.6);
    opacity:0;pointer-events:none;transition:opacity .45s var(--ease)}
  .nav-scrim.open{opacity:1;pointer-events:auto}
  .nav a{font-size:1.15rem;font-family:var(--f-display);font-weight:600;padding:.55rem 0;letter-spacing:-.012em}
  .nav-pill{display:none}
  .burger{display:flex}
  /* .head-cta (the burger's own parent) and .nav are SIBLINGS under .wrap, each
     with its own z-index (2 and 520). Because .head-cta sets a z-index of its
     own, it opens a local stacking context — the burger's z-index:530 only
     ranks it within that context and can never out-rank .nav, a sibling
     .head-cta itself is beaten by at the level above. Raising .head-cta here,
     not the burger, is what actually lets the close tap reach the button
     instead of the open panel eating the click. */
  .head-cta{z-index:521}
  .head-cta .btn{display:none}

  .f-grid{grid-template-columns:1fr}
  
  .lb-prev{left:.6rem}.lb-next{right:.6rem}
}
@media (prefers-reduced-motion:reduce){
  *{animation-duration:.01ms!important;transition-duration:.01ms!important}
  html.js .rv{opacity:1;transform:none}
  .line-mask .ch{transform:none}
  .about-media figure{clip-path:none}
}

/* =========================================================================
   Multi-page additions: dropdown navigation, page heroes, sub-pages
   ========================================================================= */

/* --- Dropdown navigation --- */
.nav .has-drop{position:relative}
.nav .drop-toggle{display:inline-flex;align-items:center;gap:.38rem;cursor:pointer;background:none;border:none;
  font-family:var(--f-body);font-size:0.9rem;font-weight:500;color:var(--ink-2);
  letter-spacing:-.012em;padding:.62rem 1.05rem;border-radius:100px;transition:color .3s}
.nav .drop-toggle .caret{transition:transform .35s var(--ease)}
.nav .has-drop.open .drop-toggle .caret{transform:rotate(180deg)}
.nav .has-drop.open .drop-toggle,.nav .drop-toggle:hover,.nav .drop-toggle.active{color:var(--accent)}
.drop{position:absolute;top:calc(100% + 24px);left:50%;transform:translateX(-50%) translateY(-8px);
  background:var(--surface);border:1px solid var(--line);border-radius:4px;box-shadow:var(--shadow-l);
  padding:.6rem;min-width:252px;opacity:0;visibility:hidden;pointer-events:none;z-index:60;
  transition:opacity .3s var(--ease),transform .3s var(--ease),visibility .3s}
.nav .has-drop.open .drop{opacity:1;visibility:visible;pointer-events:auto;transform:translateX(-50%) translateY(0)}
.drop::before{content:"";position:absolute;top:-24px;left:0;width:100%;height:24px}
.drop a{display:flex;align-items:center;gap:.75rem;padding:.6rem .75rem;border-radius:4px;
  font-size:0.82rem;font-weight:500;color:var(--ink-2);white-space:nowrap;text-decoration:none;
  transition:background .25s,color .25s}
.drop a:hover{background:var(--accent-bed);color:var(--accent)}
.drop a .thumb{width:44px;height:34px;border-radius:4px;object-fit:cover;flex-shrink:0;background:var(--accent-bed)}
.drop a .dtxt{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.drop a .dtxt b{font-weight:600;font-size:0.82rem;letter-spacing:-.015em;font-family:var(--f-display)}
.drop a .dtxt small{font-size:0.62rem;color:var(--muted);font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;margin-top:2px}
.drop a .st{margin-left:auto;font-size:0.62rem;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  padding:.2rem .5rem;border-radius:100px;flex-shrink:0}
.drop a .st.ongoing{background:var(--accent);color:#fff}
.drop a .st.completed{background:var(--accent-bed);color:var(--accent-lt);border:1px solid var(--accent-wash)}
.drop.wide{min-width:342px}
.drop .drop-head{font-size:0.62rem;font-weight:500;letter-spacing:.2em;text-transform:uppercase;
  color:var(--muted);padding:.55rem .75rem .35rem}
.drop .drop-sep{height:1px;background:var(--line);margin:.4rem .75rem}

/* --- Page hero (inner pages) --- */
.page-hero{padding:10rem 0 3.4rem;position:relative}
.page-hero .wrap{position:relative;z-index:2}
.crumb{display:flex;align-items:center;gap:.5rem;font-size:0.72rem;font-weight:600;color:var(--muted);
  margin-bottom:1.05rem;flex-wrap:wrap}
.crumb a{text-decoration:none;color:var(--accent);
  display:inline-flex;align-items:center;min-height:26px}   /* 24 landed on
  23.99 after rounding and still failed the floor */
.crumb a:hover{text-decoration:underline}
.crumb .sep{opacity:.45}
.page-hero h1{font-size:clamp(2.1rem,4.5vw,3.7rem);line-height:1.07;letter-spacing:-.012em;max-width:17ch}
.page-hero .lead{color:var(--muted);font-size:1rem;line-height:1.8;max-width:620px;margin-top:1.05rem}

/* --- Project detail page --- */
.pd-hero{position:relative;border-radius:4px;overflow:hidden;aspect-ratio:16/7;
  background:var(--accent-bed);box-shadow:var(--shadow-m)}
.pd-hero img{width:100%;height:100%;object-fit:cover}
.pd-hero::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 42%,rgba(18,14,9,.8) 100%)}
.pd-hero .cap{position:absolute;left:clamp(1.2rem,3vw,2.4rem);bottom:1.5rem;z-index:2;color:#fff}
.pd-hero .cap h2{font-size:clamp(1.5rem,3vw,2.3rem);letter-spacing:-.012em;line-height:1.1}
.pd-hero .cap p{font-size:0.72rem;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  margin-top:.5rem;display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;opacity:.93}
.pd-grid{display:grid;grid-template-columns:1.55fr .95fr;gap:2.6rem;align-items:start;margin-top:2.8rem}
.pd-body h3{font-size:1.35rem;letter-spacing:-.012em;margin:2.1rem 0 .85rem}
.pd-body h3:first-child{margin-top:0}
.pd-body p{color:var(--muted);line-height:1.9;margin-bottom:1.05rem}
.pd-side{position:sticky;top:106px;display:flex;flex-direction:column;gap:1rem}
.spec-card{background:var(--surface);border:1px solid var(--line);border-radius:4px;padding:1.6rem;box-shadow:var(--shadow-s)}
.spec-card h4{font-size:0.62rem;font-weight:500;letter-spacing:.2em;text-transform:uppercase;
  color:var(--accent);margin-bottom:1rem;font-family:var(--f-body)}
.spec-row{display:flex;justify-content:space-between;gap:1rem;padding:.68rem 0;border-bottom:1px dashed var(--line)}
.spec-row:last-of-type{border-bottom:none}
.spec-row>span{font-size:0.72rem;color:var(--muted);font-weight:600}
.spec-row>b{font-size:0.82rem;font-weight:600;text-align:right;font-family:var(--f-display);letter-spacing:-.015em}
.amen-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:.6rem}
.amen-grid div{display:flex;align-items:center;gap:.6rem;padding:.7rem .9rem;background:var(--surface);
  border:1px solid var(--line);border-radius:4px;font-size:0.82rem;font-weight:500;color:var(--ink-2)}
.amen-grid .dotm{width:6px;height:6px;border-radius:50%;background:var(--accent-lt);flex-shrink:0}
/* Was a grid of click-to-enlarge thumbnail buttons + lightbox; replaced by
   request with one box hosting a scroll-scrubbed sequence through this
   project's own gallery photos (buildScrollymation, assets/site.js — same
   function the hero uses, fed this project's gal array instead of the
   shared frame sequence, not pinned). The <img> is the plain first-photo
   fallback already in the server-rendered markup if canvas/ScrollTrigger
   isn't available; .hero-scrolly-canvas (defined once, used by the hero
   too) overlays it once frames are ready. */
.gal-grid{position:relative;overflow:hidden;border-radius:4px;aspect-ratio:16/9;background:var(--accent-bed)}
.gal-grid img{width:100%;height:100%;object-fit:cover;display:block}
.gal-count{position:absolute;right:1rem;bottom:1rem;z-index:2;color:#fff;font-size:0.78rem;
  letter-spacing:.04em;text-shadow:0 1px 6px rgba(0,0,0,.6)}
.pd-next{display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap;
  padding-top:2rem;border-top:1px solid var(--line);margin-top:3rem}

/* --- Careers --- */
.role-list{display:flex;flex-direction:column;gap:1rem}
.role{background:var(--surface);border:1px solid var(--line);border-radius:4px;padding:1.6rem 1.8rem;
  display:flex;justify-content:space-between;align-items:center;gap:1.4rem;flex-wrap:wrap;
  transition:transform .45s var(--ease),box-shadow .45s,border-color .4s}
.role:hover{transform:translateY(-4px);box-shadow:var(--shadow-m);border-color:var(--accent-wash)}
.role h3{font-size:1rem;letter-spacing:-.012em;margin-bottom:.4rem}
.role .meta{display:flex;gap:1rem;flex-wrap:wrap;font-size:0.72rem;color:var(--muted);font-weight:600}
.role .meta span{display:flex;align-items:center;gap:.35rem}

.val .svc-ic{margin-bottom:1.2rem}

/* --- noscript fallback --- */
.ns-nav{padding:6.5rem 1.5rem 1rem;text-align:center;font-size:0.9rem}
.ns-nav a{color:var(--accent);margin:0 .6rem;font-weight:600}

@media (max-width:820px){
  .nav .has-drop{width:100%}
  .nav .drop-toggle{font-size:1rem;font-family:var(--f-display);font-weight:600;
    padding:.55rem 0;letter-spacing:-.012em;width:100%;justify-content:space-between}
  .drop{position:static;transform:none;opacity:1;visibility:visible;pointer-events:auto;
    box-shadow:none;border:none;padding:.1rem 0 .5rem .8rem;min-width:0;background:transparent;display:none}
  .nav .has-drop.open .drop{display:block;transform:none}
  .drop a{padding:.5rem .4rem;font-size:0.9rem}
  .drop a .thumb{width:34px;height:26px}
  .drop .drop-head{padding:.4rem .4rem .2rem}
  .pd-grid{grid-template-columns:1fr;gap:2rem}
  .pd-side{position:static}
}

/* =========================================================================
   Project showcase hero — photography leads{padding:calc(var(--hdr-clear, 8rem)) 0 0}

.sc-slide.on{opacity:1}

.sc-slide.on img{transform:scale(1);transition:transform 7s linear}

.sc-badge .dot{width:6px;height:6px;border-radius:50%;background:var(--accent-lt)}

.sc-copy .lead{color:rgba(255,255,255,.84);font-size:clamp(.98rem,1.2vw,1.1rem);line-height:1.75;
  max-width:520px;margin-top:1.1rem}

.btn-ondark{background:rgba(255,255,255,.14);color:#fff;border:1.5px solid rgba(255,255,255,.4);
  backdrop-filter:blur(8px)}
.btn-ondark:hover{background:rgba(255,255,255,.24);border-color:#fff}

.sc-card .st{display:inline-block;font-size:0.62rem;font-weight:500;letter-spacing:.15em;
  text-transform:uppercase;padding:.28rem .7rem;border-radius:100px;margin-bottom:.7rem}
.sc-card .st.ongoing{background:var(--accent);color:#fff}
.sc-card .st.completed{background:var(--accent-bed);color:var(--accent-lt)}

.sc-card .loc{font-size:0.72rem;color:var(--muted);font-weight:600;display:flex;align-items:center;
  gap:.35rem;margin-bottom:.95rem}

.sc-card .go{display:inline-flex;align-items:center;gap:.5rem;font-size:0.72rem;font-weight:500;
  letter-spacing:.12em;text-transform:uppercase;color:var(--accent);transition:gap .35s var(--ease)}
.sc-card:hover .go{gap:.9rem}

/* =========================================================================
   Assurance strip — what an investor checks first
   ========================================================================= */
.assure{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:1.1rem}
.assure div{background:var(--surface);border:1px solid var(--line);border-radius:4px;padding:1.7rem 1.5rem;
  transition:box-shadow .45s var(--ease),border-color .4s}
.assure div:hover{box-shadow:var(--shadow-m);border-color:var(--accent-wash)}
.assure .ic{width:42px;height:42px;border-radius:4px;background:var(--accent-bed);color:var(--accent);
  display:grid;place-items:center;margin-bottom:1rem}
.assure h3{font-size:1rem;font-weight:600;letter-spacing:-.02em;margin-bottom:.45rem}
.assure p{color:var(--muted);font-size:0.82rem;line-height:1.7}

/* location highlights on cards + detail pages */
.hl-list{display:flex;flex-direction:column;gap:.55rem;margin:0 0 1.2rem}
.hl-list li{display:flex;gap:.6rem;align-items:flex-start;font-size:0.82rem;color:var(--ink-2);line-height:1.55}
.hl-list .pin{color:var(--accent-lt);flex-shrink:0;margin-top:.22rem}
.pd-hl{background:var(--surface);border:1px solid var(--line);border-radius:4px;padding:1.6rem}
.pd-hl h4{font-size:0.62rem;font-weight:500;letter-spacing:.2em;text-transform:uppercase;
  color:var(--accent);margin-bottom:1rem;font-family:var(--f-body)}

@media (max-width:1024px){
  /* minmax(0,
1fr) not 1fr: a plain fr track takes its min-content floor from the
     card's text and refuses to collapse{grid-template-columns:minmax(0,1fr);align-items:start}

}
@media (max-width:820px){

  

  
  .sc-copy .lead{font-size:0.9rem;line-height:1.62;margin-top:.8rem;
    display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
  
  .sc-actions .btn{flex:1 1 auto;justify-content:center;padding:.78rem 1rem;font-size:0.82rem}

  
              /* specs live on the project page on small screens */
  .sc-card .loc{margin-bottom:.55rem}

  
  
}
.p-approval{position:absolute;top:1rem;left:1rem;z-index:2;margin-top:2.1rem;font-size:0.62rem;
  font-weight:500;letter-spacing:.14em;text-transform:uppercase;padding:.32rem .75rem;
  border-radius:100px;background:rgba(255,255,255,.94);color:var(--accent-lt);backdrop-filter:blur(8px)}

/* =========================================================================
   Project page: configurations, map, register interest
   ========================================================================= */
.cfg-table{width:100%;border-collapse:collapse;background:var(--surface);border:1px solid var(--line);
  border-radius:4px;overflow:hidden}
.cfg-table th{text-align:left;font-size:0.62rem;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--muted);padding:.95rem 1.2rem;background:var(--sand,#faf7f2);
  border-bottom:1px solid var(--line)}
.cfg-table td{padding:1.05rem 1.2rem;border-bottom:1px solid var(--line);font-size:0.9rem}
.cfg-table tr:last-child td{border-bottom:none}
.cfg-table td:first-child{font-family:var(--f-display);font-weight:600;letter-spacing:-.02em}
.cfg-table td:nth-child(2){font-variant-numeric:tabular-nums;font-weight:600}
.cfg-table td:last-child{color:var(--muted);font-size:0.82rem}
.pd-map{border-radius:4px;overflow:hidden;border:1px solid var(--line);background:var(--accent-bed)}
.pd-map iframe{display:block;width:100%;height:340px;border:0}
.ri{background:var(--sand,#faf7f2);border:1px solid var(--line);border-radius:4px;
  padding:clamp(1.5rem,3vw,2.4rem)}
.ri-grid{display:grid;grid-template-columns:1fr 1fr;gap:1rem}
.ri h3{font-size:1.35rem;letter-spacing:-.012em;margin-bottom:.5rem}
.ri p{color:var(--muted);font-size:0.9rem;line-height:1.7;margin-bottom:1.4rem}
.ri .full{grid-column:1/-1}
@media (max-width:820px){.ri-grid{grid-template-columns:1fr}.pd-map iframe{height:250px}}

.sc-video.ready{opacity:1}

.play-film{display:inline-flex;align-items:center;gap:.75rem;background:rgba(255,255,255,.14);
  border:1.5px solid rgba(255,255,255,.42);color:#fff;border-radius:100px;
  padding:.6rem 1.3rem .6rem .6rem;cursor:pointer;font-family:var(--f-body);font-weight:600;
  font-size:0.82rem;backdrop-filter:blur(10px);transition:background .35s,border-color .35s,transform .4s var(--ease)}
.play-film:hover{background:rgba(255,255,255,.26);border-color:#fff;transform:translateY(-2px)}
.play-film .tri{width:32px;height:32px;border-radius:50%;background:var(--surface);color:var(--accent-lt);
  display:grid;place-items:center;flex-shrink:0}
.play-film small{display:block;font-size:0.62rem;letter-spacing:.14em;text-transform:uppercase;opacity:.75;font-weight:500}

.film{position:fixed;inset:0;z-index:8600;display:none;align-items:center;justify-content:center;
  background:rgba(7,6,4,.96);padding:clamp(1rem,4vw,3rem)}
.film.open{display:flex}
.film-box{width:min(1200px,100%);position:relative}
.film video{width:100%;height:auto;max-height:84vh;border-radius:4px;display:block;
  box-shadow:0 40px 100px rgba(0,0,0,.6);background:#000}
.film-close{position:absolute;top:-52px;right:0;width:44px;height:44px;border-radius:50%;border:none;
  background:rgba(255,255,255,.14);color:#fff;cursor:pointer;display:grid;place-items:center;
  backdrop-filter:blur(8px);transition:background .3s}
.film-close:hover{background:rgba(255,255,255,.3)}
@media (max-width:820px){
  .film-close{top:auto;bottom:-56px;right:50%;transform:translateX(50%)}
  .play-film{font-size:0.82rem;padding:.5rem 1.05rem .5rem .5rem}
  .play-film .tri{width:28px;height:28px}
}

/* =========================================================================
   EDITORIAL LAYER
   The palette alone did not deliver the reference look — that comes from
   layout: full-bleed imagery,
arch masks,
flat borderless cards and far more
   whitespace. This block overrides the card-based styling above.
   ========================================================================= */

/* --- 1. Hero breaks out of the centred container{padding-top:0}
.sc-wrap > .wrap{max-width:none;padding:0}

/* --- 2. Section rhythm --- */
.section{padding:clamp(3rem,6vw,6rem) 0}
.s-head{margin-bottom:clamp(1.6rem,3vw,2.6rem)}
.s-title{font-size:clamp(1.9rem,4.4vw,3.5rem);line-height:1.12;letter-spacing:-.012em}
.s-sub{font-size:1.15rem;max-width:52ch;margin-top:1.4rem}

/* --- 3. Project cards: flat and editorial{background:none;border:none;border-radius:0;box-shadow:none!important}

.p-amen,.p-approval{display:none}          /* let the photograph carry the card */
.p-status{background:none;color:#fff;backdrop-filter:none;padding:0;top:1.2rem;left:1.4rem;
  font-size:0.62rem;letter-spacing:.24em}
.p-status.completed{color:#fff}
.p-count{display:none}

/* --- 4. About collage: no shadow --- */
.about-media figure{box-shadow:none;border-radius:4px}
/* html.js prefix: same specificity trap as the clip-path rule above — the
   base "html.js .about-media figure" rule already sets aspect-ratio:3/2,
   and plain ".about-media figure:first-child" can't out-specify it. */
html.js .about-media figure:first-child{border-radius:0;aspect-ratio:3/4}
.about{gap:clamp(1.5rem,4vw,3rem)}

/* --- 5. Flatten the remaining boxed components --- */
.assure div,
.t-c,
.role{background:none;border:none;box-shadow:none!important;
  padding:0;border-radius:0}
.assure{gap:clamp(2.5rem,5vw,4rem)}
.assure div{border-top:1px solid var(--line);padding-top:1.8rem}
.assure div:hover{transform:none}

.svc-ic,.assure .ic{background:none;color:var(--ink);box-shadow:none;width:auto;height:auto;
  margin-bottom:1.1rem}
.t-c{border-top:1px solid var(--line);padding-top:1.8rem;flex:0 0 min(420px,84vw)}
.role{border-top:1px solid var(--line);padding-top:1.6rem;border-radius:0}

/* --- 6. Buttons: quieter, letterspaced ---
   Rounded to a pill everywhere (was flat-cornered, border-radius:0) per
   feedback on the header CTA — that started as a header-only exception
   matching .nav-pill, then got asked for site-wide, so the exception is
   gone and this is just the button shape now. */
/* 1.1rem of vertical padding on 11.5px text rendered a 54px pill — the
   padding was three times the height of the type it wrapped, which is what
   made it read as oversized rather than the letterspacing or the radius.
   .9rem brings it to ~46px. min-height holds the 44px tap floor explicitly
   so a later type change cannot quietly push it under. */
.btn{border-radius:100px;font-size:0.72rem;letter-spacing:.18em;text-transform:uppercase;font-weight:600;
  padding:.9rem 1.7rem;min-height:44px}
.btn-primary{background:var(--accent);color:#0c0c0c;box-shadow:none}
.btn-primary:hover{background:var(--accent-lt);color:#0c0c0c;box-shadow:none;transform:none}
.btn-ghost{background:none;border:1px solid var(--ink);border-radius:100px}
.btn-ghost:hover{background:var(--ink);color:var(--bg);transform:none}
.btn .arw{display:none}

@media (max-width:820px){

  html.js .about-media figure:first-child{border-radius:0;aspect-ratio:3/4}

}

/* =========================================================================
   EDITORIAL LAYER 2 — full-viewport hero, alternating dark sections,
   arch-topped sections, Didone type scale.
   ========================================================================= */

/* --- Didone wants size and air --- */
.s-title{font-size:clamp(2.2rem,5.2vw,4.4rem);line-height:1.1;letter-spacing:-.01em;font-weight:600}

.page-hero h1{font-size:clamp(2.3rem,5.6vw,4.8rem);line-height:1.08;letter-spacing:-.012em;font-weight:600;max-width:15ch}
.band h2{font-size:clamp(2rem,4vw,3.3rem);line-height:1.1;letter-spacing:-.012em;font-weight:600}

.eyebrow{letter-spacing:.4em;font-size:0.62rem;font-weight:500;color:var(--ink-2);
  font-family:var(--f-body);margin-bottom:1.5rem}

.sc-copy .lead{font-size:1rem;max-width:44ch}

/* --- Alternating dark sections, the reference's core rhythm --- */
.section.theme-dark{background:#2a2a2a;color:#fff}
.section.theme-dark .s-title,
.section.theme-dark h3{color:#fff}
.section.theme-dark .eyebrow{color:rgba(255,255,255,.62)}
.section.theme-dark .s-sub,
.section.theme-dark p{color:rgba(255,255,255,.74)}
.section.theme-dark .assure div,
.section.theme-dark .adv-c,
.section.theme-dark .svc-c{border-top-color:rgba(255,255,255,.2)}
.section.theme-dark .adv-n{color:rgba(255,255,255,.3)}
.section.theme-dark,.estate.alt{--muted:rgba(255,255,255,.72);
  --line:rgba(255,255,255,.18)}
.section.theme-dark .svc-ic,
.section.theme-dark .assure .ic{color:#fff}
.section.theme-dark .btn-primary{background:var(--accent);color:#0c0c0c}
.section.theme-dark .btn-ghost{border-color:rgba(255,255,255,.5);color:#fff}
.section.theme-dark .btn-ghost:hover{background:var(--surface);color:var(--ink)}

/* --- Arch-topped section: the reference's signature shape --- */
.section.arch{border-radius:0;
  margin-top:clamp(-6rem,-8vw,-4rem);position:relative;z-index:2}
/* ...except the one directly after the hero. Rising over the section above
   is the whole point of an arch, but the hero is a full screen and anything
   overlapping it means the opening is not one clean screen — it was covering
   the bottom 30px on a phone. Sits flush instead; the rise still applies to
   every other arch on the page. */
.fs + .section.arch{margin-top:0}

/* --- Quieter,
wider-tracked small type throughout --- */
.p-type,
.card-info span:first-child{letter-spacing:.2em;font-size:0.62rem;font-weight:600}

.sc-badge .dot{display:none}

@media (max-width:820px){
  
  .section.arch{border-radius:0;margin-top:-3rem}
}

/* =========================================================================
   NARRATIVE HOME — section types mirroring the reference's scroll structure
   ========================================================================= */
.theme-brand{background:var(--sand);color:var(--ink)}
.theme-brand .eyebrow{color:var(--ink-2)}

/* full-screen opening */
.fs{min-height:100svh;display:grid;align-content:end;position:relative;overflow:hidden}
.fs-media{position:absolute;inset:0;z-index:0}
.fs-media img{width:100%;height:100%;object-fit:cover}
/* Hero scrim. 0deg runs bottom -> top, so the first stop is the BOTTOM.

   The film was reported as too dark, and the scrim was most of the reason:
   at .82 alpha the bottom band was showing 18% of the video. It was that
   heavy to keep a hero CTA legible, but the CTA and the hero title were both
   removed by request -- the only text left in .fs is an .sr-only h1, which
   is never painted. So .82 and .48 were darkening the film to protect
   nothing. Measured composite at mid-frame: 69 -> 107 of 255.

   The TOP stop is deliberately unchanged at .6. The header sits there with
   no background of its own until it sticks, and over the brightest frame of
   the hero (top-strip luma 189) the brass wordmark already measures only
   2.45:1 against it. Lightening this stop would make an existing contrast
   problem worse; it needs fixing at the header, not here. */
/* DEFAULT: the protective scrim, for any hero that has text over it --
   which is every project page. 0deg runs bottom -> top.

   This was lightened to .38/.12/.6 to brighten the homepage film, and that
   was verified against the homepage, whose hero carries no visible text at
   all. What that missed is that project heroes use the same .fs-media and DO
   carry text: an h1 at 99px plus the location line. Measured behind the title
   on all five project pages, the lightened gradient left only .17 alpha there
   -- white on Country Meadows fell to 1.56:1, illegible.

   Back to values that protect the title. At the title band the alpha works
   out at .58, which holds white above 4.5:1 even over the brightest hero on
   the site (the Country Meadows aerial, image luma 247 behind the text). */
.fs-media::after{content:"";position:absolute;inset:0;
  background:linear-gradient(0deg,rgba(14,14,14,.82) 0%,rgba(14,14,14,.48) 55%,rgba(14,14,14,.6) 100%)}

/* The homepage film keeps the light treatment, because its hero really is
   text-free: .fs-in there is .fs-bare and holds only an .sr-only h1, which is
   never painted. Scoped by data-page rather than :has(.fs-bare) so it does
   not depend on :has() support, and the SAFE value is the default -- a new
   hero with text gets protection without anyone remembering to ask. */
body[data-page="home"] .fs-media::after{
  background:linear-gradient(0deg,rgba(14,14,14,.38) 0%,rgba(14,14,14,.12) 50%,rgba(14,14,14,.6) 100%)}
/* The canvas draws its own cover-fit (buildHeroScrollymation does the math
   a plain img would get for free via object-fit) and sits over the static
   f0001.jpg fallback already in the markup; opacity fades it in only once
   the first frame has actually painted, so there's never a blank flash. */
.hero-scrolly-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:1;
  display:block;opacity:0;transition:opacity .5s var(--ease)}
.hero-scrolly-canvas.ready{opacity:1}
.fs-in{position:relative;z-index:2;max-width:1340px;margin:0 auto;width:100%;
  padding:0 clamp(1.1rem,4vw,3rem) clamp(3rem,7vw,6rem)}
.fs-in h1{color:#fff;font-size:clamp(2.7rem,9vw,8rem);line-height:1.02;letter-spacing:-.012em;font-weight:600}
.fs-in .place{color:rgba(255,255,255,.82);font-size:clamp(1rem,2.4vw,1.9rem);margin-top:.6rem;
  letter-spacing:.16em;text-transform:uppercase;font-family:var(--f-body);font-weight:500;font-size:0.82rem}
.fs-foot{display:flex;justify-content:space-between;align-items:flex-end;gap:2rem;
  margin-top:clamp(2rem,5vw,3.5rem);flex-wrap:wrap}
.fs-foot p{color:rgba(255,255,255,.8);max-width:34ch;font-size:0.9rem;line-height:1.7}

/* full-width statement */
/* Was clamp(6rem,15vw,14rem): at 2000px that is 224px above AND below a
   single sentence, so the section rendered 600px tall and read as an empty
   black band rather than a pause. Halved. */
.stmt{padding:clamp(3.5rem,7vw,7rem) 0;text-align:center}
.stmt p{font-family:var(--f-display);font-weight:600;
  font-size:clamp(1.9rem,5.2vw,4.4rem);line-height:1.08;letter-spacing:-.012em;
  max-width:20ch;margin:0 auto;color:inherit}
.stmt.left{text-align:left}
.stmt.left p{margin:0;max-width:22ch}

/* three pillars */
.pillars{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:clamp(2.5rem,5vw,4.5rem)}
.pillar h3{font-size:1.35rem;font-weight:600;letter-spacing:-.005em;margin-bottom:.9rem}
.pillar p{color:var(--muted);line-height:1.8;font-size:0.9rem}
.theme-brand .pillar p,.theme-dark .pillar p{color:inherit;opacity:.78}
.pillar .n{font-family:var(--f-body);font-size:0.62rem;letter-spacing:.3em;opacity:.5;
  display:block;margin-bottom:1.4rem;font-weight:600}

/* location hierarchy */
.loc-stack{text-align:center}
.loc-stack .l1{font-size:0.72rem;letter-spacing:.42em;text-transform:uppercase;font-family:var(--f-body);
  font-weight:500;color:var(--muted);margin-bottom:.6rem}
.loc-stack .l2{font-family:var(--f-display);font-size:clamp(2.2rem,6vw,5rem);line-height:1.06;font-weight:600;
  letter-spacing:-.014em}
.loc-stack .l3{font-size:1rem;color:var(--muted);margin-top:.6rem}

/* project rows{display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,5vw,5rem);align-items:center;
  padding:clamp(3rem,7vw,6rem) 0;border-top:1px solid var(--line)}

/* credits block */
.credits{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:0;
  border-top:1px solid var(--line)}
.credits div{padding:2.2rem 1.6rem 0 0}
.credits dt{font-size:0.62rem;letter-spacing:.28em;text-transform:uppercase;color:var(--muted);
  font-weight:500;margin-bottom:.8rem}
.credits dd{margin:0;font-family:var(--f-body);font-size:1.15rem;font-weight:400;letter-spacing:normal;line-height:1.4}

@media (max-width:820px){

  
  
  /* Full screen on a phone too. 88svh left a strip of the next section
     showing under the hero, so the opening was never a clean single
     screen. svh, not vh: it measures the viewport with the browser UI
     visible, so the hero does not jump when the URL bar hides. */
  .fs{min-height:100svh}
  .stmt{padding:4.5rem 0}
}

/* Every page's hero now shares ONE height with .fs (100svh, 88svh under
   820px) — inner pages previously ran at 66svh while the home hero was full
   screen, which is the size inconsistency between pages that this removes.
   The construction (media layer, scrim, crumb, heading) is unchanged. */
.fs.fs-inner .fs-in h1{font-size:clamp(2.6rem,8vw,7rem)}
.fs .crumb{color:rgba(255,255,255,.72);margin-bottom:1.1rem}
.fs .crumb a{color:rgba(255,255,255,.92)}
.fs .crumb .sep{opacity:.5}
/* detail-page body sits on the narrative grid */
.pd-grid{margin-top:0}
.pd-side{top:120px}

/* --- status + approval badges on listing rows ---
   Anchored to the BASE of the frame: the arch top has a 300px radius{position:relative}

/* pill styling is shared; only the placement differs between contexts */
.unit-badges,.fs-badges{display:flex;flex-wrap:wrap;gap:.4rem}
.unit-badges span,.fs-badges span{font-size:0.62rem;font-weight:500;letter-spacing:.18em;
  text-transform:uppercase;padding:.4rem .85rem;border-radius:100px;backdrop-filter:blur(10px);
  white-space:nowrap}
.unit-badges .st.ongoing,.fs-badges .st.ongoing{background:var(--accent);color:#0c0c0c}
.unit-badges .st.completed,.fs-badges .st.completed{background:rgba(255,255,255,.94);color:var(--ink)}
/* A 22%-white wash over an unknown photograph is not a background — over a
   bright render it left white text on near-white. The plate is now opaque
   enough to guarantee its own contrast: over a pure-white frame the plate still
   resolves to rgb(52,52,52), where --accent-lt holds at 6.26:1. */
.unit-badges .ap,.fs-badges .ap{background:rgba(14,14,14,.84);color:var(--accent-lt);
  border:1px solid rgba(176,141,87,.55)}
/* on a card: pinned to the base of the arch frame */
.unit-badges{position:absolute;left:1.1rem;bottom:1.1rem;z-index:3;max-width:calc(100% - 2.2rem)}
/* on a full-bleed hero there is no frame corner, so they sit in the flow */
.fs-badges{margin-top:1.1rem}
@media (max-width:820px){
  .unit-badges{left:.85rem;bottom:.85rem}
  .unit-badges span,.fs-badges span{font-size:0.62rem;padding:.34rem .7rem;letter-spacing:.14em}
}

/* =========================================================================
   Momentum scrolling (Lenis) + scroll-linked motion
   ========================================================================= */
html.lenis,html.lenis body{height:auto}
.lenis.lenis-smooth{scroll-behavior:auto!important}
.lenis.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain}
.lenis.lenis-stopped{overflow:hidden}

/* media that parallaxes needs headroom to travel inside its frame */
.fs-media img{will-change:transform}
.fs-media{overflow:hidden}

/* SplitText line masking */
.split-line{overflow:hidden;display:block}
.split-line > div{will-change:transform}

/* =========================================================================
   Estate bands — full-bleed, alternating light/dark
   -------------------------------------------------------------------------
   The distinguishing move of the reference layout: every project owns a whole
   screen-width band rather than sharing one scrolling column, and the bands
   alternate ground so the eye reads them as separate addresses. The spec stack
   is deliberately LIGHT (300) under a semibold name — that weight split, not
   the colour, is what carries the look.
   ========================================================================= */
.estates{margin:0;padding:0}
.estate{width:100%;position:relative;overflow:hidden;
  padding:clamp(3.5rem,8vw,7rem) 0;background:var(--bg);color:var(--ink)}
.estate.alt{background:var(--black);color:#fff}
.estate-in{max-width:1340px;margin:0 auto;padding:0 clamp(1.1rem,4vw,3rem);
  display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(2rem,5vw,4.5rem);align-items:center}
.estate.alt .estate-media{order:2}

.estate-media{position:relative;overflow:hidden;aspect-ratio:4/3;background:var(--sand)}
.estate.alt .estate-media{background:#1a1a1a}
.estate-media img{width:100%;height:100%;object-fit:cover;
  transition:transform 1.1s var(--ease);will-change:transform}
.estate:hover .estate-media img{transform:scale(1.045)}

.estate-copy .eyebrow{margin-bottom:1.1rem}
.estate.alt .eyebrow{color:var(--accent-lt)}
.estate-copy h3{font-size:clamp(1.7rem,3.3vw,2.7rem);font-weight:600;
  letter-spacing:-.012em;line-height:1.16;margin-bottom:.7rem}
.estate-tag{font-size:0.9rem;font-weight:400;line-height:1.75;
  color:var(--muted);max-width:44ch;margin-bottom:1.9rem}
.estate.alt .estate-tag{color:rgba(255,255,255,.78)}

/* the four-line spec stack */
.estate-spec{display:grid;gap:0;margin:0 0 2rem;border-top:1px solid var(--line)}
.estate.alt .estate-spec{border-top-color:rgba(255,255,255,.22)}
.estate-spec div{display:flex;align-items:baseline;justify-content:space-between;
  gap:1.4rem;padding:.85rem 0;border-bottom:1px solid var(--line)}
.estate.alt .estate-spec div{border-bottom-color:rgba(255,255,255,.14)}
.estate-spec dt{font-size:0.62rem;font-weight:500;letter-spacing:.24em;
  text-transform:uppercase;color:var(--muted);flex-shrink:0}
.estate.alt .estate-spec dt{color:rgba(255,255,255,.58)}
.estate-spec dd{margin:0;font-size:1rem;font-weight:400;text-align:right;line-height:1.35}

.estate-badges{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:1.4rem}
.estate.alt .estate-badges .st{background:rgba(255,255,255,.12);color:#fff;
  border-color:rgba(255,255,255,.28)}
.estate.alt .estate-badges .ap{background:transparent;color:var(--accent-lt);
  border-color:var(--accent-lt)}
.estate.alt .btn-primary{background:var(--accent);color:#0c0c0c}
.estate.alt .btn-primary:hover{background:var(--accent-lt);color:var(--black)}

@media (max-width:860px){
  .estate-in{grid-template-columns:1fr;gap:1.8rem}
  .estate.alt .estate-media{order:0}
  .estate-media{aspect-ratio:16/10}
  .estate-spec dd{font-size:1rem}
}

/* Counter band — plain numerals, hairline separators, no cards */
.tally{border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}
.tally div{padding:clamp(2rem,4vw,3.2rem) 1rem;text-align:center;
  border-right:1px solid var(--line);
  /* Lets .tally b size against THIS cell instead of the viewport — see below. */
  container-type:inline-size}
.tally div:last-child{border-right:0}
/* Sized to the cell, not the window.

   A viewport clamp cannot work here. Above ~1400px the wrapper hits its
   max-width, so the cell stops growing while vw keeps going; below that,
   the wrapper padding and the cell's own padding mean cell width is not
   proportional to vw either. A single vw curve overflows at one end or
   wastes space at the other, and the old clamp(2rem,4.4vw,3.4rem)
   overflowed at every width above the 950px breakpoint.

   Measured, not guessed: the longest figure, "15,00,000", was 5.29em wide
   when this was set in Montserrat, putting the ceiling at 18.9% of the
   cell. 18cqi cleared that by only 3px, which is not a margin at all when
   the first paint uses a fallback face that may be wider than the webfont,
   so 16cqi was chosen instead. The cell's side padding came down from
   1.4rem to 1rem to buy some of that back rather than shrinking the
   numerals further.

   Since the switch to SF Pro / Inter the same string measures about
   5.00em, so that 16cqi now leaves roughly 20% spare rather than 15%.
   Re-measured at 320, 375, 960 and 1440px after the font change. The
   figure is the widest thing in the cell, so it is what to re-check if
   the typeface ever changes again.

   The vw fallback below is for browsers without container queries, and is
   deliberately tamer than the old one so it fits across the whole range
   rather than only at the top of it. */
.tally b{display:block;font-family:var(--f-display);font-weight:600;
  font-size:clamp(1.5rem,2.4vw,2.5rem);line-height:1;color:var(--accent);
  letter-spacing:-.02em;font-variant-numeric:tabular-nums}
@supports (container-type:inline-size){
  .tally b{font-size:clamp(1rem,16cqi,2.8rem)}
}
.tally span{display:block;margin-top:.7rem;font-size:0.62rem;font-weight:500;
  letter-spacing:.24em;text-transform:uppercase;color:var(--muted)}
/* At 375px the auto-fit grid lands on 2x2, not a single column, so the
   separators have to be keyed to the 2-up rhythm rather than stripped.
   Threshold raised from 640px: between roughly 640-950px the grid still
   auto-fits 4 columns (4*160px minmax fits), but the widest figures here
   ("15,00,000", 10 characters) need more room than a quarter-width column
   has at this font-size — they were overflowing into the neighbouring
   column with no visible gap. 2-up at this width gives each number about
   double the space, which is enough even for the longest one. */
@media (max-width:950px){
  .tally{grid-template-columns:1fr 1fr}
  /* Two-up on a 375px screen leaves each cell about 109px of content box,
     and 1.4rem of side padding was eating a third of it. The clamp floor
     also had to come down to 1rem: at 1.35rem it outranked the cqi term
     and forced the figure 7px wider than its cell — an overflow that only
     showed up by sweeping widths, never in a single screenshot. */
  .tally div{padding-inline:.5rem}
  .tally div{border-bottom:1px solid var(--line)}
  .tally div:nth-child(2n){border-right:0}
  .tally div:nth-last-child(-n+2){border-bottom:0}
}

/* =========================================================================
   Frames — the matted-print treatment
   -------------------------------------------------------------------------
   A hairline mat inset from the image edge plus four corner ticks. It reads as
   an architectural drawing rather than a web card, and it gives the motion
   layer something to draw on entry.

   EVERY hidden-start state below is scoped to html.has-motion, which site.js
   only sets once GSAP + ScrollTrigger are confirmed and motion is not reduced.
   Without that class the frame renders complete and static — the content can
   never be trapped behind an animation that did not load.
   ========================================================================= */
.frame{position:relative;overflow:hidden;isolation:isolate;
  --frame-line:rgba(42,42,42,.34);--frame-mat:rgba(42,42,42,.14)}
.estate.alt .frame,.theme-dark .frame{--frame-line:rgba(255,255,255,.5);
  --frame-mat:rgba(255,255,255,.2)}

.frame-img{position:absolute;inset:0;overflow:hidden}
.frame-img img{width:100%;height:100%;object-fit:cover;display:block;
  will-change:transform}

/* the mat: a hairline rectangle floating inside the image edge */
.frame::after{content:'';position:absolute;inset:clamp(10px,1.5vw,18px);
  border:1px solid var(--frame-mat);pointer-events:none;z-index:2}

/* four corner ticks, drawn as two-sided boxes */
.fm{position:absolute;width:clamp(14px,2vw,22px);height:clamp(14px,2vw,22px);
  border:1px solid var(--frame-line);pointer-events:none;z-index:3;
  transform-origin:center}
.fm-tl{top:clamp(10px,1.5vw,18px);left:clamp(10px,1.5vw,18px);border-right:0;border-bottom:0}
.fm-tr{top:clamp(10px,1.5vw,18px);right:clamp(10px,1.5vw,18px);border-left:0;border-bottom:0}
.fm-bl{bottom:clamp(10px,1.5vw,18px);left:clamp(10px,1.5vw,18px);border-right:0;border-top:0}
.fm-br{bottom:clamp(10px,1.5vw,18px);right:clamp(10px,1.5vw,18px);border-left:0;border-top:0}

/* plate: the index numeral, bottom-left inside the mat */
.frame-idx{position:absolute;left:clamp(10px,1.5vw,18px);bottom:clamp(10px,1.5vw,18px);
  z-index:3;padding:.42rem .7rem;font-family:var(--f-display);font-weight:600;
  font-size:0.72rem;letter-spacing:.16em;font-variant-numeric:tabular-nums;
  background:rgba(235,228,221,.94);color:#2a2a2a;pointer-events:none}
.estate.alt .frame-idx{background:rgba(14,14,14,.88);color:#fff}

/* --- motion-gated start states --- */
html.has-motion .frame-img img{clip-path:inset(0 0 100% 0)}
html.has-motion .fm{transform:scale(0)}
html.has-motion .frame-idx{opacity:0;transform:translateY(8px)}

/* =========================================================================
   Paper grain — a single fixed overlay
   Turbulence at 3% is invisible as texture and visible as depth; it stops the
   flat sand ground reading as a plain CSS colour on a large monitor.
   ========================================================================= */
#grain{position:fixed;inset:0;z-index:2;pointer-events:none;opacity:.038;
  mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:180px 180px}
@media (prefers-reduced-motion:reduce){#grain{display:none}}

/* =========================================================================
   Estate band — frame integration and refined detail
   ========================================================================= */
.estate-media.frame{aspect-ratio:4/3}
.estate-media.frame img{transition:none}          /* parallax owns transform now */
.estate:hover .estate-media.frame img{transform:none}

/* a hairline rule that draws across under the project name */
.estate-rule{height:1px;width:100%;background:var(--frame-line);
  margin:0 0 1.5rem;transform-origin:0 50%}
html.has-motion .estate-rule{transform:scaleX(0)}

.estate-spec div{transition:background .35s var(--ease)}
.estate-spec div:hover{background:rgba(176,141,87,.05)}
.estate.alt .estate-spec div:hover{background:rgba(255,255,255,.05)}

@media (max-width:860px){.estate-media.frame{aspect-ratio:16/10}}

/* =========================================================================
   Gallery — matted plates
   A lighter version of the frame: mat only, no corner ticks. Ticks on every
   thumbnail would read as clutter; they are reserved for the estate imagery.
   ========================================================================= */
.gal-grid{border-radius:0;background:var(--sand)}
.gal-grid img{border-radius:0}

/* Hero video sits over the still and under the scrim; the still stays as the
   poster/fallback and is never removed, so a refused autoplay costs nothing. */
.fs-media img{position:relative;z-index:0}
.fs-video{position:absolute;inset:0;z-index:1;opacity:0;
  transition:opacity 1.4s var(--ease);pointer-events:none}
.fs-video.ready{opacity:1}
/* Full-bleed, by explicit request: the video fills the hero and the first
   screen on every device, the way a hero background is conventionally treated
   (the same way a CSS background-size:cover image would be). The trade-off is
   real and was measured before making this change: on a portrait phone, cover
   shows the centre ~28% of the frame's width and crops the rest, because a
   16:9 clip cannot fill a taller-than-wide box any other way without leaving
   empty space. That crop is normal for hero video across the web and is what
   "fit the entire page" means in practice — the alternative (contain) is what
   was shipped previously and left letterboxing on mismatched screens. */
.fs-video video{width:100%;height:100%;object-fit:cover;object-position:center;
  display:block}
.fs-media::after{z-index:2}

/* =========================================================================
   The Bengaluru Index
   A survey drawing, not a map widget: hairlines, crosshairs, tabular figures.
   Lives on the dark ground so it reads as a plate rather than a page section.
   ========================================================================= */
.atlas{display:grid;grid-template-columns:1.55fr .85fr;gap:clamp(1.5rem,4vw,3.5rem);
  align-items:stretch;margin-top:clamp(1.5rem,3vw,2.5rem)}
.atlas-map{position:relative;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.02)}
.atlas-map svg{display:block;width:100%;height:auto}

.atlas-grid line{stroke:rgba(255,255,255,.055);stroke-width:1}
.atlas-spoke{stroke:rgba(176,141,87,.28);stroke-width:1;stroke-dasharray:3 5}
.atlas-core{fill:var(--accent-lt)}
.atlas-core-lab{fill:rgba(255,255,255,.62);font-family:var(--f-body);font-size:13px;
  letter-spacing:.3em;font-weight:600}

.atlas-mark{cursor:pointer;outline:none}
.atlas-halo{fill:transparent;stroke:transparent;transition:fill .35s var(--ease)}
.atlas-x{stroke:rgba(255,255,255,.55);stroke-width:1;transition:stroke .3s var(--ease)}
.atlas-dot{fill:var(--accent-lt);transition:r .35s var(--ease),fill .3s var(--ease)}
.atlas-mark.done .atlas-dot{fill:transparent;stroke:var(--accent-lt);stroke-width:2}
.atlas-lab{fill:rgba(255,255,255,.68);font-family:var(--f-display);font-size:12px;
  font-weight:600;letter-spacing:.18em}
.atlas-nm{fill:#fff;font-family:var(--f-display);font-size:19px;font-weight:600}
.atlas-mark:hover .atlas-halo,
.atlas-mark:focus-visible .atlas-halo,
.atlas-mark.on .atlas-halo{fill:rgba(176,141,87,.14)}
.atlas-mark:hover .atlas-dot,
.atlas-mark.on .atlas-dot{r:9}
.atlas-mark:hover .atlas-x,
.atlas-mark.on .atlas-x{stroke:var(--accent-lt)}
.atlas-mark:focus-visible .atlas-x{stroke:#fff;stroke-width:2}

.atlas-read{border:1px solid rgba(255,255,255,.16);padding:clamp(1.3rem,2.4vw,2rem);
  display:flex;flex-direction:column;gap:.6rem;background:rgba(255,255,255,.02)}
.atlas-eyebrow{font-size:0.62rem;font-weight:500;letter-spacing:.24em;text-transform:uppercase;
  color:var(--accent-lt)}
.atlas-read h3{font-size:1.35rem;font-weight:600;color:#fff;line-height:1.2}
.atlas-read>p{font-size:0.82rem;color:rgba(255,255,255,.72);font-weight:400}
.atlas-read dl{display:grid;gap:0;margin:.6rem 0 .4rem;border-top:1px solid rgba(255,255,255,.16)}
.atlas-read dl div{display:flex;justify-content:space-between;gap:1rem;padding:.62rem 0;
  border-bottom:1px solid rgba(255,255,255,.12)}
.atlas-read dt{font-size:0.62rem;font-weight:500;letter-spacing:.2em;text-transform:uppercase;
  color:rgba(255,255,255,.58)}
.atlas-read dd{margin:0;font-size:0.9rem;font-weight:400;color:#fff;text-align:right}
.atlas-read .btn{margin-top:auto;align-self:flex-start}
.atlas-coord{font-size:0.62rem;letter-spacing:.14em;color:rgba(255,255,255,.62);
  font-variant-numeric:tabular-nums;padding-top:.7rem}

/* the accessible / no-JS equivalent */
.atlas-list{list-style:none;margin:clamp(1.4rem,3vw,2.2rem) 0 0;padding:0;
  border-top:1px solid rgba(255,255,255,.16)}
.atlas-list li{display:grid;grid-template-columns:auto 1fr auto auto;gap:1rem;align-items:baseline;
  padding:.85rem 0;border-bottom:1px solid rgba(255,255,255,.12)}
.atlas-list .n{font-family:var(--f-display);font-size:0.72rem;font-weight:600;
  letter-spacing:.18em;color:var(--accent-lt)}
.atlas-list a{color:#fff;text-decoration:none;font-weight:600;font-size:0.9rem}
.atlas-list a:hover{color:var(--accent-lt)}
.atlas-list .loc{font-size:0.82rem;font-weight:400;color:rgba(255,255,255,.72)}
.atlas-list .yr{font-size:0.62rem;font-weight:500;letter-spacing:.2em;text-transform:uppercase;
  color:rgba(255,255,255,.58)}

@media (max-width:900px){
  .atlas{grid-template-columns:1fr}
  .atlas-nm{font-size:26px}
  .atlas-lab{font-size:17px}
  .atlas-core-lab{font-size:17px}
  .atlas-list li{grid-template-columns:auto 1fr;row-gap:.2rem}
  .atlas-list .loc,.atlas-list .yr{grid-column:2}
}

/* =========================================================================
   Delivery timeline
   ========================================================================= */
.tl{display:grid;gap:0;margin-top:clamp(1.2rem,2.5vw,2rem)}
.tl-step{display:grid;grid-template-columns:110px 24px 1fr;align-items:start;
  gap:clamp(.8rem,2vw,1.6rem);padding:clamp(1.1rem,2.2vw,1.7rem) 0;
  border-bottom:1px solid var(--line)}
.tl-step:first-child{border-top:1px solid var(--line)}
.tl-y{font-family:var(--f-display);font-size:0.72rem;font-weight:500;letter-spacing:.22em;
  text-transform:uppercase;color:var(--muted);padding-top:.28rem}
.tl-dot{position:relative;width:24px;height:24px}
.tl-dot::before{content:'';position:absolute;left:50%;top:.42rem;width:9px;height:9px;
  transform:translateX(-50%) rotate(45deg);background:var(--accent)}
.tl-dot::after{content:'';position:absolute;left:50%;top:1.5rem;bottom:-2.2rem;width:1px;
  transform:translateX(-50%);background:var(--line)}
.tl-step:last-child .tl-dot::after{display:none}
.tl-b h3{font-size:1.15rem;font-weight:600;margin-bottom:.3rem}
.tl-b p{font-size:0.9rem;font-weight:400;color:var(--muted);line-height:1.7}
@media (max-width:640px){
  .tl-step{grid-template-columns:1fr;gap:.35rem}
  .tl-dot{display:none}
}

/* =========================================================================
   Accessibility pass — WCAG 2.2 AA, against the ui-ux-pro-max checklist
   ========================================================================= */

/* 2.4.11 Focus Appearance. Dark grounds get a white ring.

   The reason this rule exists has expired: the old accent measured 2.19:1 on
   the charcoal and black plates, under the 3:1 an indicator needs, so those
   grounds had to opt out of it. Brass measures 6.25:1 on the charcoal and
   6.79:1 on black, so the global ring would now pass here unaided.

   Kept anyway rather than deleted: it is a working accessibility affordance
   and removing it is a behaviour change, not a colour change. It does mean
   focus reads white in dark sections and brass elsewhere, which is now a
   choice rather than a necessity and can be dropped if that bothers you. */
.section.theme-dark a:focus-visible,
.section.theme-dark button:focus-visible,
.section.theme-dark [tabindex]:focus-visible,
.estate.alt a:focus-visible,
.estate.alt button:focus-visible,
.footer a:focus-visible,
.footer button:focus-visible{outline-color:#fff}

/* 2.5.8 Target Size (Minimum): 24x24 CSS px. Footer and utility links were
   18-23px tall — they read fine but are under the floor on touch. */
.footer a{display:inline-flex;align-items:center;min-height:24px}
.atlas-list a{display:inline-flex;align-items:center;min-height:24px}

/* Running text below 12px. The eyebrow step is a micro-label size and is right
   for "Portfolio" or "Amenities", but the estate bands reuse it to carry a real
   location line — that is running text at 9.9px. Give it its own step. */
.estate-copy .eyebrow{font-size:0.82rem;letter-spacing:.18em}
.note{font-size:0.82rem}

/* The atlas marks set outline:none because the ring would trace the whole <g>
   bounding box; the visible focus state is drawn on the crosshair and halo
   instead (see .atlas-mark:focus-visible above). Make that explicit for the
   SVG-aware case rather than leaving a bare outline:none. */
.atlas-mark:focus-visible .atlas-dot{stroke:#fff;stroke-width:2}

/* =========================================================================
   BLACK & GOLD — overrides for rules that assumed a light ground
   ========================================================================= */

/* Plates. On a dark site the differentiation runs the other way: the "dark"
   section is now the LIFTED one, and the alternating bands separate by a step
   up rather than a swap to black. */
.section.theme-dark{background:var(--lift);color:var(--ink)}
.section.theme-dark .s-title,.section.theme-dark h3{color:var(--ink)}
.section.theme-dark .eyebrow{color:var(--accent)}
.section.theme-dark .s-sub,.section.theme-dark p{color:var(--muted)}
.section.theme-dark,.estate.alt{--muted:#a8a198;--line:#1f2429}
.theme-brand{background:var(--sand);color:var(--ink)}
.theme-brand .eyebrow{color:var(--accent)}

.estate{background:var(--bg);color:var(--ink)}
.estate.alt{background:var(--lift);color:var(--ink)}
.estate.alt .estate-media{background:#101010}
.estate-media{background:#101010}
.estate.alt .estate-tag{color:var(--muted)}
.estate.alt .eyebrow,.estate .eyebrow{color:var(--accent)}
.estate.alt .estate-spec{border-top-color:var(--line)}
.estate.alt .estate-spec div{border-bottom-color:var(--line)}
.estate.alt .estate-spec dt{color:var(--muted)}
.estate-spec div:hover,.estate.alt .estate-spec div:hover{background:rgba(176,141,87,.07)}

/* Surfaces that were white cards are now lifted panels. */
body{background:var(--bg);color:var(--ink)}
.t-c,
.role,
.assure div,
.drop,
.modal-panel,
.field input,
.field select,
.field textarea{background:var(--surface);border-color:var(--line)}
.field input,.field select,.field textarea{color:var(--ink)}
.field input::placeholder,.field textarea::placeholder{color:#6f6a62}
.field input:focus,.field select:focus,.field textarea:focus{background:var(--lift);
  border-color:var(--accent);box-shadow:0 0 0 3px rgba(176,141,87,.18)}
/* Found via 21st.dev search ("dark luxury contact form input focus") for the
   idea of a soft glow ring on focus, not the code — kept to a single low-alpha
   ring rather than the neon/pulsing versions that search turned up, since
   this is a booking enquiry form, not a product launch page. Label sharpens
   to gold in step with the field, the same idle-to-gold cue the pillars and
   roles already use on hover. */
.field:focus-within label{color:var(--accent-lt)}
.field label{transition:color .3s var(--ease)}
.field input,.field select,.field textarea{transition:border-color .3s var(--ease),
  background .3s var(--ease),box-shadow .3s var(--ease)}
.nav-pill{background:rgba(176,141,87,.14)}
.drop a:hover{background:rgba(176,141,87,.10);color:var(--accent-lt)}

/* Both dark and white text actually clear contrast on this blue (white is
   8.68:1, #0c0c0c is 7.21:1) — kept dark text anyway, so this fill doesn't
   need touching every .btn-primary/badge consumer that already assumes it. */
.btn-primary{background:var(--accent);color:#0c0c0c;box-shadow:0 10px 26px rgba(176,141,87,.22)}
.btn-primary:hover{background:var(--accent-lt);color:#0c0c0c;box-shadow:0 18px 40px rgba(176,141,87,.3)}
.section.theme-dark .btn-primary,.estate.alt .btn-primary{background:var(--accent);color:#0c0c0c}
.section.theme-dark .btn-primary:hover,.estate.alt .btn-primary:hover{background:var(--accent-lt);color:#0c0c0c}
.btn-ghost{border-color:var(--line);color:var(--ink)}
.btn-ghost:hover{background:var(--accent);color:#0c0c0c;border-color:var(--accent)}
/* Ramp runs light->mid, not mid->dark. With --accent-dk at the far end the
   initials measured 3.31:1 there — the dark text cleared AA at the top of the
   gradient and failed at the bottom of the same circle. Now 9.93:1 falling to
   6.33:1, so it passes across the whole fill. */
.t-av{background:linear-gradient(135deg,var(--accent-lt),var(--accent));color:#0c0c0c}
.svc-ic,.assure .ic{background:rgba(176,141,87,.14);color:var(--accent)}
.unit-badges .st.ongoing,.fs-badges .st.ongoing{background:var(--accent);color:#0c0c0c}
.unit-badges .st.completed,.fs-badges .st.completed{background:rgba(255,255,255,.9);color:#0c0c0c}

/* Frames: gold hairlines on a black ground. */
.frame{--frame-line:rgba(176,141,87,.62);--frame-mat:rgba(176,141,87,.24)}
.estate.alt .frame,.theme-dark .frame{--frame-line:rgba(176,141,87,.62);--frame-mat:rgba(176,141,87,.24)}
.frame-idx,.estate.alt .frame-idx{background:rgba(12,12,12,.9);color:var(--accent-lt)}
.gal-grid{background:#101010}

/* Counters and the timeline diamond pick up the accent. */
.tally b{color:var(--accent)}
.tl-dot::before{background:var(--accent)}
.grad{color:var(--accent)}
@supports (-webkit-background-clip:text) or (background-clip:text){
  .grad{background:linear-gradient(100deg,var(--accent-lt) 0%,var(--accent) 55%,var(--accent-dk) 100%);
    -webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}
}
::selection{background:var(--accent);color:#0c0c0c}

/* The atlas already lived on a dark plate — only its ground needs to settle. */
.atlas-map,.atlas-read{border-color:var(--line);background:rgba(255,255,255,.015)}

/* Grain: multiply is invisible on black. Screen at low opacity lifts instead. */
#grain{mix-blend-mode:screen;opacity:.05}

/* Project cards show the WHOLE render — shrink to fit{background:#101010;display:grid;place-items:center}

.gal-grid img{object-fit:contain;background:#101010}

/* Hero is now the film alone — no title block. The CTA sits at the base where
   the scrim is heaviest (0.82 alpha, 11.41:1 over a white frame), so it stays legible over any
   frame of the video. */
.fs-in.fs-bare{display:flex;align-items:flex-end;min-height:100%}
.fs-in.fs-bare .fs-foot{display:flex;justify-content:flex-start}

/* Wordmark without the mark: it now carries the identity alone, so it gets the
   spacing and the accent the lockup used to provide. */
.brand{gap:0}
.brand-txt b{color:var(--ink);letter-spacing:.16em}
.brand-txt small{color:var(--accent);letter-spacing:.22em}
.footer .brand-txt b{color:var(--ink)}
.footer .brand-txt small{color:var(--accent)}
#preloader{gap:1.1rem}
.pre-lab span{color:var(--muted)}
.pre-bar{background:var(--line)}
.pre-bar i{background:linear-gradient(90deg,var(--accent-dk),var(--accent-lt))}

/* Satellite base for the index. The imagery is real, so it has to be veiled
   hard enough for gold marks and white labels to survive on top of it — a
   bright rooftop would otherwise swallow both. */
.atlas-veil{fill:rgba(8,8,8,.62)}
.atlas-grid line{stroke:rgba(255,255,255,.05)}
.atlas-spoke{stroke:rgba(212,180,131,.4);stroke-width:1;stroke-dasharray:3 6}
.atlas-core{fill:var(--accent-lt)}
.atlas-core-lab,.atlas-lab,.atlas-nm{paint-order:stroke;stroke:rgba(0,0,0,.75);stroke-width:3px;
  stroke-linejoin:round}
.atlas-nm{fill:#fff}
.atlas-lab{fill:var(--accent-lt)}
.atlas-core-lab{fill:rgba(255,255,255,.78)}
.atlas-dot{fill:var(--accent-lt)}
.atlas-mark.done .atlas-dot{fill:transparent;stroke:var(--accent-lt);stroke-width:2}
.atlas-x{stroke:rgba(255,255,255,.7)}
.atlas-mark:hover .atlas-halo,.atlas-mark:focus-visible .atlas-halo,
.atlas-mark.on .atlas-halo{fill:rgba(212,180,131,.2)}
.atlas-attr{fill:rgba(255,255,255,.55);font-family:var(--f-body);font-size:11px;
  paint-order:stroke;stroke:rgba(0,0,0,.7);stroke-width:2.5px;stroke-linejoin:round}

/* Social icons: the grey discs read as muddy on black. Gold outline, gold fill
   on hover, and a real focus ring. */
.socials a,.footer .socials a{width:42px;height:42px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  background:transparent;border:1px solid var(--line);color:var(--ink);
  transition:background .3s var(--ease),color .3s var(--ease),border-color .3s var(--ease)}
.socials a:hover,.footer .socials a:hover{background:var(--accent);border-color:var(--accent);color:#0c0c0c}
.socials a:focus-visible{outline:2px solid var(--accent-lt);outline-offset:3px}
.socials svg,.footer .socials svg{width:19px;height:19px}

/* The mailto/tel links in the sales-office block measured 23.x px tall — under
   the 24 CSS px floor by a sub-pixel, which still fails. */
.credits dd a{display:inline-flex;align-items:center;min-height:26px;text-decoration:none}

/* Project listing cards: show the WHOLE render.
   The frame was 3:4 portrait while the renders run 0.71 to 1.50 — meadows-gate
   at 1.41 was losing roughly half its width. Shrink to fit inside a 4:3 frame
   on a matching ground{aspect-ratio:4/3;background:#101010}

   /* the bottom scrim assumed a full-bleed crop */

/* The entry tween scaled to 1.06 and the drift moved it 8% — both re-crop a
   contained image at the frame edge. The wipe stays; the transform goes. */
html.has-motion .estate-media.frame .frame-img img{transform:none!important}

/* =========================================================================
   Scroll-linked motion graphics
   Start states are gated on html.has-motion, so if GSAP never boots every one
   of these renders in its finished position instead of staying hidden.
   ========================================================================= */

/* SVG groups need a fill-box origin or scale() pivots off the whole canvas. */
.atlas-mark{transform-box:fill-box;transform-origin:center}

/* Timeline spine — a real element so a scrub can drive it. */
.tl{position:relative}
.tl-spine{position:absolute;left:calc(110px + clamp(.8rem,2vw,1.6rem) + 12px);
  top:1.9rem;bottom:1.9rem;width:1px;background:linear-gradient(180deg,var(--accent),var(--accent-dk));
  transform-origin:50% 0;pointer-events:none}
.tl-dot::after{display:none}          /* the spine replaces the per-step stub */
@media (max-width:640px){.tl-spine{display:none}}

/* A gold rule under every section title, drawn on reveal. Uses the existing
   .rv/.in reveal system rather than a second mechanism. */
.s-head .s-title{position:relative;padding-bottom:.85rem}
.s-head .s-title::after{content:'';position:absolute;left:0;bottom:0;
  width:56px;height:2px;background:var(--accent);transform-origin:0 50%}
.s-head.center .s-title::after{left:50%;transform:translateX(-50%)}
html.has-motion .s-head .s-title::after{transform:scaleX(0);
  transition:transform 1s var(--ease) .2s}
html.has-motion .s-head.center .s-title::after{transform:translateX(-50%) scaleX(0)}
html.has-motion .s-head.rv.in .s-title::after{transform:scaleX(1)}
html.has-motion .s-head.center.rv.in .s-title::after{transform:translateX(-50%) scaleX(1)}

/* The hero plane travels, so it needs headroom to travel into. */
.fs-media{will-change:transform}

/* =========================================================================
   Estate frames take the shape of the image inside them
   -------------------------------------------------------------------------
   A fixed frame ratio cannot serve renders running 0.71 to 1.50. cover cropped
   half the tower off; contain kept the whole render but stranded it in a black
   void with the mat floating out at the frame edge, unrelated to the picture.

   So the frame is sized BY the image instead of the other way round: the figure
   is fit-content, the image renders at its natural shape within a width and
   height cap, and the mat and corner ticks — being absolute to the figure —
   land exactly on the picture edge. Nothing crops, nothing letterboxes, and the
   mount reads as a mount because it actually touches the print.
   ========================================================================= */
.estate-media.frame{aspect-ratio:auto;width:fit-content;max-width:100%;
  margin-inline:auto;background:none;overflow:visible}
.estate-media.frame .frame-img{position:relative;inset:auto;overflow:hidden;
  background:#101010;line-height:0}
.estate-media.frame .frame-img img{display:block;object-fit:contain;
  width:auto;height:auto;max-width:100%;max-height:72vh}

@media (max-width:860px){
  .estate-media.frame .frame-img img{max-height:58vh}
}

/* With width/height attributes present the browser derives the box from the
   intrinsic ratio, so the frame reserves space before the image decodes.
   These two keep that reservation honest once CSS caps the size. */
.estate-media.frame .frame-img img{max-width:100%;max-height:72vh}
.estate-media.frame .frame-img{max-width:100%}

/* =========================================================================
   Interiors reel
   ========================================================================= */
.reel{margin-top:clamp(1.4rem,3vw,2.4rem)}
.reel-stage{position:relative;overflow:hidden;background:#0a0a0a;
  border:1px solid var(--line)}
.reel-track{display:flex;will-change:transform;
  transition:transform 1s var(--ease)}
.reel-slide{flex:0 0 100%;margin:0;position:relative;
  height:clamp(360px,62vh,660px);overflow:hidden}

/* A soft-focus wash of the same shot fills whatever the contained image does
   not. Mixed ratios (0.56 to 1.0) in one fixed stage otherwise leave a portrait
   stranded in dead ground — this keeps the plate full without cropping a single
   pixel of the actual picture. */
.reel-slide::before{content:'';position:absolute;inset:-8%;z-index:0;
  background-image:var(--shot);background-size:cover;background-position:center;
  filter:blur(34px) saturate(1.2) brightness(.4);transform:scale(1.12)}

/* Absolute + margin:auto, NOT height:100% in a grid: the slide's height is
   definite but a centred grid item leaves it indefinite for percentage
   resolution, so height:100% fell back to auto and max-width bound instead —
   a 736x736 rendered 563 tall inside a 355px slide and was clipped. */
.reel-slide img{position:absolute;inset:0;margin:auto;z-index:1;
  max-width:calc(100% - 2rem);max-height:calc(100% - 2rem);
  width:auto;height:auto;object-fit:contain;display:block;
  box-shadow:0 24px 70px rgba(0,0,0,.55)}
.reel-slide figcaption{position:absolute;left:0;right:0;bottom:0;z-index:2;
  padding:2.6rem clamp(1rem,3vw,2rem) clamp(.9rem,2vw,1.4rem);
  font-size:0.82rem;font-weight:400;color:#fff;letter-spacing:.02em;
  background:linear-gradient(0deg,rgba(0,0,0,.86) 0%,rgba(0,0,0,0) 100%)}

.reel-nav{position:absolute;top:50%;transform:translateY(-50%);z-index:3;
  width:46px;height:46px;border-radius:50%;border:1px solid rgba(212,180,131,.5);
  background:rgba(10,10,10,.72);color:var(--accent-lt);cursor:pointer;
  display:grid;place-items:center;transition:background .3s var(--ease),color .3s var(--ease)}
.reel-nav:hover{background:var(--accent);color:#0c0c0c;border-color:var(--accent)}
.reel-nav:focus-visible{outline:2px solid #fff;outline-offset:3px}
.reel-nav.prev{left:clamp(.6rem,1.6vw,1.2rem)}
.reel-nav.next{right:clamp(.6rem,1.6vw,1.2rem)}

.reel-foot{display:flex;align-items:center;justify-content:space-between;
  gap:1rem;margin-top:1rem}
.reel-dots{display:flex;gap:.5rem}
.reel-dots button{background:none;border:0;cursor:pointer;line-height:0;
  min-width:26px;min-height:26px;padding:0 5px;   /* 2px indicator + padding was
  18px tall — under the 24 CSS px pointer-target floor */
  display:grid;place-items:center}
.reel-dots i{display:block;width:26px;height:2px;background:var(--line);
  transition:background .35s var(--ease)}
.reel-dots button[aria-selected="true"] i{background:var(--accent)}
.reel-dots button:hover i{background:var(--accent-lt)}
.reel-dots button:focus-visible{outline:2px solid var(--accent-lt);outline-offset:2px}
.reel-count{font-size:0.72rem;letter-spacing:.24em;color:var(--muted);
  font-variant-numeric:tabular-nums}

@media (prefers-reduced-motion:reduce){.reel-track{transition:none}}
@media (max-width:600px){
  .reel-slide{height:clamp(300px,52vh,460px)}
  .reel-slide img{max-width:calc(100% - 1rem);max-height:calc(100% - 1rem)}
  .reel-nav{width:40px;height:40px}
}

/* =========================================================================
   Wordmark — the two lines now share an optical width
   The lockup read as odd because OPULENT ran wide at 18px while DEVELOPERS sat
   at 9.9px underneath, so the block had no shared edge and the second line
   looked like a stray caption. Matched widths, and a gold hairline between
   them to make the pairing deliberate.
   ========================================================================= */
.brand-txt{gap:.34rem;padding-top:0}
.brand-txt b{font-family:var(--f-display);font-weight:600;
  font-size:clamp(1.05rem,1.5vw,1.28rem);letter-spacing:.22em;color:var(--ink);
  line-height:1}
.brand-txt small{font-size:clamp(.54rem,.72vw,.63rem);font-weight:500;
  /* Tracking is derived rather than chosen, so the two lines genuinely share a
     width. OPULENT scales on its own clamp (16.8px -> 20.48px) while this line
     is pinned at .66rem by the small-label block at the end of this file, so a
     single fixed .53em could only match at one point in that range: measured
     17px (17%) too wide at 375/601/1024 and 5.4px too narrow at 1440. Solving
     "glyph sum + 9 tracks = OPULENT's width" for the track gives the line
     below. Verified 0.0px width gap at 375, 1024 and 1440.

     0.6733 and 7.6px were measured on THIS machine, and --f-display is a
     system stack (SF Pro on Apple, Segoe on Windows), so the two lines are set
     in different faces and the constants are metric-specific. Expect a few px
     of drift on other platforms -- far less than the 17px it replaces, but
     re-measure if the name, the tagline or the font stack changes. */
  --ls:calc(clamp(1.05rem,1.5vw,1.28rem) * 0.6733 - 7.6px);
  letter-spacing:var(--ls);
  /* letter-spacing is emitted after the LAST glyph too, so the element's box
     runs one track wider than the text you can see. The old fix was
     text-indent:.53em, which shifted the whole line right instead of trimming
     the box -- that is what left DEVELOPERS sitting 5.6px inside OPULENT's
     left edge at every width measured. A negative margin-right is no good
     either: it shrinks the MARGIN box, so as a stretched flex item the border
     box (and with it the hairline) actually got 6.2px longer. So the element
     takes its own max-content width instead of stretching, and the hairline
     moves to a ::before that subtracts exactly one track. */
  align-self:flex-start;
  width:max-content;
  color:var(--accent);line-height:1}
/* The rule is drawn here rather than as small's border-top so its length can be
   the text's length: 100% is the box (glyphs + 10 tracks), minus one track. */
.brand-txt small::before{content:"";display:block;
  width:calc(100% - var(--ls));
  border-top:1px solid rgba(176,141,87,.42);
  margin-bottom:.4rem}
.footer .brand-txt b{color:var(--ink)}
.footer .brand-txt small{color:var(--accent)}

/* Visually hidden but reachable by screen readers and search engines. Not
   display:none — that removes it from the accessibility tree too. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* The footer column headings moved h4 -> h2 for document structure; they must
   keep the size they had, since nothing about them changed visually. */
.footer .f-h{font-size:0.72rem;font-weight:500;letter-spacing:.2em;
  text-transform:uppercase;color:var(--accent);margin-bottom:1rem;line-height:1.3}

/* Same for the project listing name: h3 -> h2 is a structure change, not a
   visual one. Keep the h3 scale it already had. */
.unit-copy .unit-name{font-size:clamp(1.6rem,3.1vw,2.5rem);font-weight:600;
  letter-spacing:-.01em;line-height:1.14}

/* Structure-only promotions. These elements changed tag for document outline
   reasons, so they inherit the exact scale they rendered at before. */
.pillar .pillar-h{font-size:1.35rem;font-weight:600;letter-spacing:-.005em;
  margin-bottom:.9rem;line-height:1.14}
.pd-body .pd-h{font-size:1.5rem;font-weight:600;letter-spacing:-.005em;
  line-height:1.2;margin:2.1rem 0 .85rem}
.pd-body .pd-h:first-child{margin-top:0}
.spec-card .card-h,.pd-hl .card-h{font-size:0.82rem;font-weight:500;
  letter-spacing:.18em;text-transform:uppercase;color:var(--accent);
  margin-bottom:1rem;line-height:1.3}

/* Last-resort reveal, set by the head script when site.js never signalled that
   its reveal loop started. Must outrank the html.js hiding rules. */
html.force-reveal .rv{opacity:1!important;transform:none!important}
html.force-reveal .about-media figure{clip-path:none!important}
html.force-reveal .frame-img img{clip-path:none!important}
html.force-reveal .fm{transform:scale(1)!important}
html.force-reveal .frame-idx{opacity:1!important;transform:none!important}
html.force-reveal .estate-rule{transform:scaleX(1)!important}

/* =========================================================================
   Preloader wordmark
   Used to be a canvas particle effect painted over this text (vaporize-
   text-effect, assets/site.js); removed by request, so this is just the
   static wordmark now — no .has-canvas/#preCanvas left to style.
   ========================================================================= */
.pre-mark{position:relative;width:min(90vw,760px);height:clamp(74px,12vh,118px);
  display:grid;place-items:center}
.pre-word{font-family:var(--f-display);font-weight:500;color:var(--ink);
  font-size:clamp(1.05rem,4.2vw,2.1rem);letter-spacing:.2em;text-align:center;
  line-height:1.2;text-transform:uppercase}

/* =========================================================================
   Project ring
   The tall wrapper is the scroll track; the stage sticks inside it. 220vh, not
   the 500vh the original used — five cards need 360 degrees, not five screens
   of scrolling to get there.
   ========================================================================= */
.ring-wrap{position:relative;height:220vh;background:var(--bg)}
.ring-stage{position:sticky;top:0;height:100vh;display:grid;place-items:center;
  overflow:hidden;perspective:1800px}
.ring-spin{position:relative;width:1px;height:1px;transform-style:preserve-3d;
  will-change:transform}

.ring-card{position:absolute;left:50%;top:50%;width:clamp(200px,21vw,272px);
  margin-left:calc(clamp(200px,21vw,272px) / -2);margin-top:-210px;
  text-decoration:none;color:var(--ink);
  background:#101010;border:1px solid var(--line);
  display:flex;flex-direction:column;overflow:hidden;
  transition:border-color .35s var(--ease)}
.ring-card:hover{border-color:var(--accent)}
.ring-card:focus-visible{outline:2px solid var(--accent-lt);outline-offset:4px}

/* Contained, never cropped — these renders run 0.71 to 1.50. */
/* Absolute + margin:auto, NOT max-height on a centred grid item. The grid
   route leaves the percentage indefinite, so max-width binds alone and the
   0.71 portrait rendered 378px tall inside a 268px frame and was clipped.
   Identical trap to the interiors reel; identical fix. */
.ring-shot{height:268px;background:#0c0c0c;position:relative;overflow:hidden}
.ring-shot img{position:absolute;inset:0;margin:auto;
  max-width:calc(100% - 1.2rem);max-height:calc(100% - 1.2rem);
  width:auto;height:auto;object-fit:contain;display:block}

.ring-meta{display:block;padding:1rem 1.05rem 1.15rem}
.ring-eyebrow{display:block;font-size:0.82rem;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--accent);margin-bottom:.5rem;line-height:1.4}
.ring-name{font-size:1rem;font-weight:600;line-height:1.25;letter-spacing:-.005em;
  color:var(--ink);margin:0 0 .6rem}
.ring-badges{display:flex;flex-wrap:wrap;gap:.4rem}
.ring-badges .st,.ring-badges .ap{font-size:0.62rem;padding:.28rem .55rem}

/* --- Flat fallback: under 900px, or reduced motion. No 3D, no scroll
       coupling, no sticky. The same cards, laid out as a grid. --- */
.ring-wrap.is-flat{height:auto}
.ring-wrap.is-flat .ring-stage{position:static;height:auto;perspective:none;
  padding:clamp(2rem,6vw,3.5rem) clamp(1.1rem,4vw,3rem);overflow:visible}
.ring-wrap.is-flat .ring-spin{position:static;width:100%;height:auto;
  transform:none!important;transform-style:flat;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  gap:clamp(1rem,2.5vw,1.6rem);max-width:1340px;margin:0 auto}
.ring-wrap.is-flat .ring-card{position:static;width:auto;margin:0;
  transform:none!important;opacity:1!important}
.ring-wrap.is-flat .ring-shot{height:auto;aspect-ratio:4/3}
.ring-wrap.is-flat .ring-shot img{position:absolute;inset:0;margin:auto}

@media (prefers-reduced-motion:reduce){
  .ring-spin{transition:none}
}

/* Brand mark restored beside the wordmark, as an SVG rather than the original
   flattened PNG. It keeps the brand blue while the UI around it is brass: it
   is the identity, not a themed surface, so it does not follow the palette. */
.brand{gap:.62rem}
.brand svg{height:34px;width:auto;flex-shrink:0;display:block;
  transition:height .5s var(--ease)}
.header.stuck .brand svg{height:29px}
.footer .brand svg{height:38px}
@media (max-width:600px){.brand svg{height:29px}}

/* Spec lines on the ring cards — the two figures that used to live in the
   stacked rows. Kept to two because a 272px card cannot hold four and stay
   legible; the rest are one click away on the detail page. */
.ring-spec{display:block;margin-top:.85rem;padding-top:.7rem;
  border-top:1px solid var(--line)}
.ring-spec span{display:flex;align-items:baseline;justify-content:space-between;
  gap:.7rem;font-size:0.82rem;font-weight:400;color:var(--ink);line-height:1.7}
.ring-spec i{font-style:normal;font-size:0.62rem;font-weight:500;
  letter-spacing:.18em;text-transform:uppercase;color:var(--muted);flex-shrink:0}
.ring-card{height:auto}
.ring-wrap.is-flat .ring-card{margin-bottom:0}

/* =========================================================================
   Premium hover — pillars and roles
   -------------------------------------------------------------------------
   Found via 21st.dev search ("luxury real estate hero", dark premium hero
   references) while looking for a restrained glow/lift language to borrow —
   not code, just the idea: content blocks that respond to touch. .pillar
   (the home page's three pillars, and every Services/Insights card, since
   both render through the same class) and .role (career listings) were the
   only static blocks left on the site with no hover state at all; everything
   else — the ring, the reel, the atlas, the estate bands — already had one.

   Kept deliberately quiet for a luxury register: a small lift, the numeral
   sharpening from half-opacity to full gold, and a hairline that draws in
   under the heading — the same drawn-rule gesture already used on every
   section title, so this reads as the site's existing language rather than a
   new one. :focus-within mirrors :hover so keyboard users get the same cue.
   ========================================================================= */
.pillar{transition:transform .45s var(--ease)}
.pillar:hover,.pillar:focus-within{transform:translateY(-6px)}
.pillar .n{transition:opacity .35s var(--ease),color .35s var(--ease)}
.pillar:hover .n,.pillar:focus-within .n{opacity:1;color:var(--accent)}
.theme-brand .pillar:hover .n,.theme-dark .pillar:hover .n,
.theme-brand .pillar:focus-within .n,.theme-dark .pillar:focus-within .n{color:var(--accent-lt)}
/* .pillar-h, not just h3: the home page's own pillar heading was promoted
   from h3 to h2.pillar-h in an earlier pass to fix a heading-level skip. Both
   selectors are needed or home silently drops the underline-draw that
   services/blog (which kept a plain h3) still get — one class, two markups,
   caught by testing all three surfaces rather than just one. */
.pillar h3,.pillar .pillar-h{position:relative;display:inline-block;padding-bottom:.5rem}
.pillar h3::after,.pillar .pillar-h::after{content:'';position:absolute;left:0;bottom:0;
  width:100%;height:1px;background:var(--accent);transform:scaleX(0);transform-origin:0 50%;
  transition:transform .45s var(--ease)}
.pillar:hover h3::after,.pillar:focus-within h3::after,
.pillar:hover .pillar-h::after,.pillar:focus-within .pillar-h::after{transform:scaleX(1)}

.role{transition:border-color .4s var(--ease),padding-left .4s var(--ease)}
.role:hover,.role:focus-within{border-top-color:var(--accent)!important;padding-left:.6rem}
.role h3{transition:color .35s var(--ease)}
.role:hover h3,.role:focus-within h3{color:var(--accent-lt)}

@media (prefers-reduced-motion:reduce){
  .pillar,.pillar .n,.pillar h3::after,.pillar .pillar-h::after,.role,.role h3{transition:none}
}

/* =========================================================================
   ARCHITECTURE REEL (section 4b)
   One render per screen. Without JS this is a plain stack of full-height
   figures — real content, scrolls normally. With JS the section pins and
   each panel slides up over the one before it, so the "page" turns.

   Panels stack by DOM order — no z-index on the panels themselves. They DO
   each get isolation:isolate, which is load-bearing: the layers inside a
   panel are z-indexed against each other, and without a stacking context
   per panel those inner z-indexes would escape and let a lower panel's
   photo paint over the incoming panel's backdrop mid-slide. isolate scopes
   them without changing how the panels themselves order.
   ========================================================================= */
.projreel{position:relative;background:var(--black);overflow:hidden}
.projreel-panel{position:relative;height:100svh;min-height:480px;overflow:hidden;margin:0;
  isolation:isolate;background:var(--black) center/cover no-repeat}

/* Blurred backdrop of the panel's own photo, filling whatever the contained
   render leaves over. On a phone that is most of the screen: a 16:9 elevation
   contained in a 375x812 viewport paints at 375x211, about a quarter of the
   page, and the rest would otherwise be dead black. Same file as the <img>,
   so it costs no extra request.

   The photo comes from an inline background-image on the figure and is
   inherited down to here. It has to arrive that way: a relative url() written
   into a custom property resolves against THIS stylesheet's folder, not the
   document, so --shot:url('assets/img/x.jpg') silently requested
   /assets/assets/img/x.jpg and painted nothing. An inline style resolves
   against the page, which is what the rest of the markup already assumes. */
.projreel-panel::before{content:'';position:absolute;inset:0;z-index:0;
  background-image:inherit;background-size:cover;background-position:center;
  background-repeat:no-repeat;
  filter:blur(34px) brightness(.4) saturate(1.1);
  transform:scale(1.12);pointer-events:none}
/* contain, not cover. The six renders don't share an aspect ratio (one is
   portrait among five 16:9s), and cover-cropping a 16:9 elevation into a
   phone's portrait viewport leaves a vertical sliver of building. Containing
   on the black ground keeps every render whole — "one image, one page" — and
   is the same treatment the project galleries already use. */
.projreel-panel img{position:relative;z-index:1;
  width:100%;height:100%;object-fit:contain;object-position:center;display:block}
.projreel-shade{position:absolute;inset:0;z-index:2;background:var(--black);opacity:0;pointer-events:none}
/* Slide caption: project name over its own render, with the location under
   it. The block spans the full width and carries the scrim itself — the name
   sits over photography whose brightness is not ours to predict (a sunlit
   facade behind white type is the failure case), so legibility cannot rest on
   text-shadow alone. */
.projreel-cap{position:absolute;z-index:3;left:0;right:0;bottom:0;
  padding:clamp(1.5rem,4vw,3.4rem);
  display:flex;flex-direction:column;align-items:flex-start;gap:.55rem;
  background:linear-gradient(to top,rgba(0,0,0,.78),rgba(0,0,0,.42) 42%,transparent);
  pointer-events:none}
/* Only the link takes pointer events back, so the scrim never swallows a tap
   meant for the page behind it. */
.projreel-cap .projreel-name{pointer-events:auto}

.projreel-name{display:inline-block;color:#fff;text-decoration:none;
  font-family:var(--f-display);font-weight:500;letter-spacing:-.02em;line-height:1.05;
  font-size:clamp(2.1rem,6.2vw,4.4rem);
  text-shadow:0 2px 30px rgba(0,0,0,.55)}
a.projreel-name{transition:opacity .35s var(--ease)}
a.projreel-name:hover,a.projreel-name:focus-visible{opacity:.78}
/* The name is a link on a photograph, where an underline is the only thing
   that reads as one at a glance — but only once it is being pointed at, so
   the resting state stays as clean as the reference. */
a.projreel-name:hover{text-decoration:underline;text-underline-offset:.14em;
  text-decoration-thickness:1px}

.projreel-loc{display:inline-flex;align-items:center;gap:.5rem;
  color:rgba(255,255,255,.88);font-family:var(--f-body);font-weight:400;
  font-size:clamp(.82rem,2.4vw,1.02rem);letter-spacing:.01em;
  text-shadow:0 1px 16px rgba(0,0,0,.7)}
.projreel-pin{flex-shrink:0;opacity:.92}

/* The counter keeps the glass chip, opposite corner from the name. No
   backdrop-filter on it: it rides inside the pinned reel, where six panels
   are transformed every scroll frame, and blurring a backdrop that is itself
   moving is the one place in this layer where the cost would show. */
.projreel-num{position:absolute;z-index:3;right:clamp(1.1rem,4vw,3rem);top:clamp(1.1rem,4vw,3rem);
  color:#fff;font-size:.76rem;letter-spacing:.18em;
  background-color:rgba(255,255,255,.07);background-image:var(--glass-sheen);
  border:1px solid var(--glass-line);box-shadow:inset 0 1px 0 0 var(--glass-edge);
  border-radius:100px;padding:.4rem .85rem;
  text-shadow:0 1px 16px rgba(0,0,0,.7)}
.projreel-num span{color:rgba(255,255,255,.6)}

/* Pinned mode: the section is exactly one screen tall and the panels are
   stacked in place; ScrollTrigger's pin spacer supplies the scroll length. */
html.js .projreel.projreel-js{height:100svh}
/* min-height is for the static fallback only. Here the panel must match the
   host exactly — on a short viewport (landscape phone) a 480px floor would
   push the panel past the host and the overflow would clip the caption. */
html.js .projreel.projreel-js .projreel-panel{position:absolute;inset:0;height:100%;min-height:0;
  will-change:transform}

@media (max-width:640px){
  .projreel-panel{min-height:420px}
  .projreel-cap{font-size:.75rem}
}

/* About Us prose. Replaces the old two-column concept block, whose paragraph
   styling was inline on the <p>s and went with it. Capped at a readable
   measure rather than the full wrap width — .about used to do that job by
   being a grid column. */
.about-prose{max-width:62ch}
.about-prose p{color:var(--muted);line-height:1.9;font-size:1.05rem}
.about-prose p + p{margin-top:1.1rem}

/* =========================================================================
   LIQUID GLASS
   -------------------------------------------------------------------------
   Every panel, card and control becomes a pane of frosted glass: it tints
   and blurs whatever is behind it, carries a hairline edge, and catches a
   specular sheen along its top.

   Three rules this layer follows.

   1. It changes MATERIAL, not colour. Nothing here introduces a hue — the
      tint is plain white at low alpha over the existing dark ground, so the
      brass-on-black theme is untouched.
   2. The header only glasses once it is stuck. Over the hero it stays
      transparent, which is how it was asked for.
   3. .btn-primary stays solid. It is the call to action and needs to sit
      on top of the glass, not join it.

   Contrast, recomputed over the composited glass (white .055 over #050607
   resolves to about rgb(20,21,22)) rather than carried over from the solid
   surface it replaces: --ink 17.1:1, --ink-2 12.1:1, --muted 7.2:1. All
   pass AA, and the first two pass AAA.
   ========================================================================= */
:root{
  --glass-tint:rgba(255,255,255,.055);
  --glass-tint-hi:rgba(255,255,255,.085);
  --glass-line:rgba(255,255,255,.13);
  --glass-edge:rgba(255,255,255,.26);
  --glass-blur:20px;
  --glass-sat:165%;
  --glass-drop:0 14px 42px rgba(0,0,0,.42);
  /* The sheen: a highlight down from the top edge plus a soft bloom that
     travels across the pane. Layered as background-image so no
     pseudo-element is needed — several of these components already use
     ::before/::after.

     --glass-light is driven by scroll position (initGlassLight, site.js), so
     the light appears to move over the glass as the page moves. Scroll-linked
     rather than a looping keyframe: a keyframe would repaint every pane
     forever, including while the page sits still, which on a phone is heat
     and battery for motion nobody is watching. This costs nothing at rest
     and only repaints while the browser is already compositing a scroll. */
  --glass-light:35%;
  --glass-sheen:
    linear-gradient(180deg,rgba(255,255,255,.10) 0%,rgba(255,255,255,0) 42%),
    radial-gradient(120% 85% at calc(12% + var(--glass-light) * .76) -22%,
      rgba(255,255,255,.15),transparent 60%);
}

/* The homepage is mostly full-bleed sections and editorial text, so without
   these four it had almost no surface to be glass ON — the effect was real
   but invisible outside the header and the testimonial row. */
.pillar,.tally,.atlas-read,.credits,
.card-w,.t-c,.role,.spec-card,.pd-hl,.modal-box,.band,
.drop,.float-chip,.burger,.lb-btn,.lb-count,.gal-count,
.btn-ghost,.btn-ondark,.amen-grid div{
  background-color:var(--glass-tint);
  background-image:var(--glass-sheen);
  border:1px solid var(--glass-line);
  box-shadow:inset 0 1px 0 0 var(--glass-edge),var(--glass-drop);
}

/* Cards get a soft radius; the pills and circles keep the shape they have. */
.pillar,.tally,.atlas-read,.credits,
.card-w,.t-c,.role,.spec-card,.pd-hl,.modal-box,.band,.drop{border-radius:18px}
.amen-grid div{border-radius:12px}

/* The four newcomers arrive with layout baggage from when they were bare:
   rules that draw their own separators, or that were written against a
   transparent background. Glass replaces those, so they have to go or the
   pane reads as a bordered box with a second border inside it. */
.pillar,.tally,.atlas-read,.credits{padding:clamp(1.5rem,3vw,2.2rem)}
.tally{border-top:0;border-bottom:0}
.credits{border-top:0!important}
.atlas-read{border-color:var(--glass-line)}

.pillar:hover,.tally:hover,.atlas-read:hover,
.card-w:hover,.t-c:hover,.role:hover,.spec-card:hover,
.float-chip:hover,.btn-ghost:hover,.btn-ondark:hover{
  background-color:var(--glass-tint-hi);
  border-color:rgba(255,255,255,.22);
}

/* The header is the one pane that only forms on scroll. */
.header.stuck .shell{
  background-color:rgba(255,255,255,.05);
  background-image:var(--glass-sheen);
  border-bottom:1px solid var(--glass-line);
  box-shadow:inset 0 1px 0 0 var(--glass-edge),0 10px 34px rgba(0,0,0,.4);
}

/* Blur is the expensive half, so it is opt-in and separable: without support
   the rules above still read as a tinted, edge-lit pane rather than falling
   back to nothing. */
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .pillar,.tally,.atlas-read,.credits,
  .card-w,.t-c,.role,.spec-card,.pd-hl,.modal-box,.band,
  .drop,.float-chip,.burger,.lb-btn,.lb-count,.gal-count,
  .btn-ghost,.btn-ondark,.amen-grid div,.header.stuck .shell{
    -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-sat));
    backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-sat));
  }
}

@media (max-width:820px){
  /* The off-canvas nav is the largest pane on the page and sits over the
     whole viewport, so it gets its own heavier frost. */
  .nav{background-color:rgba(10,12,14,.72);background-image:var(--glass-sheen);
    border-left:1px solid var(--glass-line)}
  /* Half the blur radius on phones: cost scales with the blurred area, and
     these panes are near full-width here. */
  :root{--glass-blur:12px}
}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  @media (max-width:820px){
    .nav{-webkit-backdrop-filter:blur(26px) saturate(150%);
      backdrop-filter:blur(26px) saturate(150%)}
  }
}

/* Motion is opt-out: with prefers-reduced-motion the light parks in the
   middle and never moves (initGlassLight returns early and leaves the
   declared default in place). */

/* =========================================================================
   PHONE: tap targets and small type
   Found by measuring a 375x812 viewport, not by eye.

   Tap targets. 44px is the floor a finger needs. Four controls were under
   it, and the worst was the one added most recently: the project name on
   each reel slide was a 35px-tall link — its own line box, no padding — and
   it is the primary action on that whole section.

   Type. These are letterspaced uppercase labels, which read smaller than
   their size suggests. The logo's "DEVELOPERS" was 8.6px, below any usable
   floor on a phone; the rest sat at 9.9px. Raised only here — the desktop
   sizes are fine and the design language is unchanged.
   ========================================================================= */
@media (max-width:820px){
  /* 35px -> ~45px. The caption's gap absorbs the added height so the name
     and location keep the spacing they were designed with. */
  a.projreel-name{padding-block:.3rem}
  .projreel-cap{gap:.3rem}

  .drop-toggle{min-height:44px}
  .atlas-list a{padding-block:.65rem}      /* 24px -> ~45px */

  .brand-txt small{font-size:.66rem}

/* The badges and spec chips, same lift. These looked decorative enough to
   leave at 0.62rem on the first pass, which was the wrong read: "Ongoing",
   "Completed", "RERA & BMRDA Approved" and the Bedrooms / Unit size labels
   are the facts a buyer scans a project card FOR. They are pills inside
   flex-wrap rows, so the extra width wraps rather than overflowing --
   re-measured at 320, 768 and 1920 after the change. */
.unit-badges span,.fs-badges span,.p-amen i,.ring-badges .st,.ring-badges .ap,
.sc-card .st{font-size:.69rem}       /* 8.6px -> 10.6px */
  .eyebrow,.atlas-eyebrow,.atlas-coord,
  .tally span,.yr{font-size:.69rem}        /* 9.9px -> 11.0px */
}

@media (max-width:820px){
  /* Second pass on tap targets, from the same 375x812 measurement.

     The footer link lists were the largest cluster: 14 links at 24px in a
     tight column, which is both hard to hit and easy to mis-hit. Padding
     rather than margin, so the target grows instead of just the gap.

     The Sales Office phone and email matter most of the lot — they are the
     conversion on this page and were 26px. */
  .f-grid ul li a,.f-bot span a{display:inline-block;padding-block:.6rem}
  /* min-height rather than padding: these two are the conversion on this
     page, and a padding figure that clears 44px at one font size quietly
     stops clearing it at another. */
  .credits dd a{display:inline-flex;align-items:center;min-height:44px}
  /* The interiors carousel's own controls. Padding alone did not lift the
     dots — the button is line-height:0 around a 2px bar, so the box never
     grew. Size the box instead and centre the bar inside it. */
  .reel-dots button{min-width:44px;min-height:44px;display:grid;place-items:center;padding:0}
  .reel-nav{width:44px;height:44px}

  /* 42px and 43px respectively — a shade under the 44px floor. */
  .burger{width:44px;height:44px}
  .socials a{width:44px;height:44px}

  /* Missed by the first pass: .yr is overridden by a longer selector, and
     the spec-list dt was never in the list. */
  .atlas-list li .yr,.atlas-read dl dt,
  .credits dt{font-size:.69rem}        /* Address / Phone / Email labels */
  /* Project pages, missed by the first pass because it only measured the
     homepage. The form labels matter most of these — someone has to read
     them to fill the enquiry in. */
  .field label,.cfg-table thead th,.fs-badges .st{font-size:.69rem}
}

@media (max-width:820px){
  /* Second pass on tap targets, from the same 375x812 measurement.

     The footer link lists were the largest cluster: 14 links at 24px in a
     tight column, which is both hard to hit and easy to mis-hit. Padding
     rather than margin, so the target grows instead of just the gap.

     The Sales Office phone and email matter most of the lot — they are the
     conversion on this page and were 26px. */
  .f-grid ul li a,.f-bot span a{display:inline-block;padding-block:.6rem}
  /* min-height rather than padding: these two are the conversion on this
     page, and a padding figure that clears 44px at one font size quietly
     stops clearing it at another. */
  .credits dd a{display:inline-flex;align-items:center;min-height:44px}
  /* The interiors carousel's own controls. Padding alone did not lift the
     dots — the button is line-height:0 around a 2px bar, so the box never
     grew. Size the box instead and centre the bar inside it. */
  .reel-dots button{min-width:44px;min-height:44px;display:grid;place-items:center;padding:0}
  .reel-nav{width:44px;height:44px}

  /* 42px and 43px respectively — a shade under the 44px floor. */
  .burger{width:44px;height:44px}
  .socials a{width:44px;height:44px}

  /* Missed by the first pass: .yr is overridden by a longer selector, and
     the spec-list dt was never in the list. */
  .atlas-list li .yr,.atlas-read dl dt,
  .credits dt{font-size:.69rem}        /* Address / Phone / Email labels */
  /* Project pages, missed by the first pass because it only measured the
     homepage. The form labels matter most of these — someone has to read
     them to fill the enquiry in. */
  .field label,.cfg-table thead th,.fs-badges .st{font-size:.69rem}
}

/* =========================================================================
   SMALL LABELS: the same lift, at every width

   The two blocks above raise the letterspaced label family from 9.9px to
   11px, but only under 820px. Desktop kept 9.9px, which was never a
   deliberate choice -- both passes were measured on a 375px viewport, so
   they only ever fixed what that viewport showed.

   Measured at 1920 on about.html: 23 visible elements under 10.5px --
   .eyebrow x6, step numbers x6, definition-list dt x9, wordmark x2. These
   are uppercase at .24em-.4em tracking, which reads smaller than the number
   suggests, and they carry real content: section eyebrows, the Address /
   Phone / Email labels, and the form labels someone has to read to fill the
   enquiry in.

   Same selectors and same .69rem as the mobile passes, so this is one
   consistent size rather than a third variant. 0.62rem stays the token for
   the genuinely decorative labels -- badges, tags, counters -- which is why
   this is a selector list and not a change to the token itself.
   ========================================================================= */
.eyebrow,.atlas-eyebrow,.atlas-coord,.tally span,.yr,
.atlas-list li .yr,.atlas-read dl dt,.credits dt,
.field label,.cfg-table thead th,.fs-badges .st,
.pillar .n,.estate-spec dt,.footer h4{font-size:.69rem}
.brand-txt small{font-size:.66rem}

/* =========================================================================
   SALES OFFICE CAROUSEL
   The six addresses beside the contact details, running the same
   renderReel() component as the interiors reel - arrows, dots, counter,
   autoplay. It was previously a scroll-scrubbed canvas with no controls at
   all, so it only moved if you happened to be scrolling past it.
   ========================================================================= */
.sv-grid{display:grid;grid-template-columns:1.05fr .95fr;
  gap:clamp(1.6rem,4vw,3.4rem);align-items:center}
.sv-info .credits{margin-top:0}
/* Shorter than the interiors reel: this one sits beside the contact details
   rather than running full width, so a tall stage would push the phone
   number and the booking buttons too far down the column. */
.sv-reel .reel-slide{height:clamp(240px,34vw,360px)}

@media (max-width:820px){
  /* One column, SLIDER first. This was the other way round, on the argument
     that the phone number should not sit under a carousel — but measured on a
     375px screen that put the slider 909px below the heading, a full screen
     past the fold, in a section only 1336px tall. Nobody scrolling this
     section saw a slider at all. The heading and the booking buttons still
     bracket it, so the call to action is not lost; the carousel just stops
     being invisible. */
  .sv-grid{grid-template-columns:1fr;gap:1.8rem}
  .sv-reel{order:1}
  .sv-info{order:2}
}

/* Emphasis inside prose blocks. The paragraphs run at --muted, so a <strong>
   that only changed weight would barely read as emphasis against it — the
   colour lift is what makes it land. */
.about-prose strong{color:var(--ink);font-weight:600}
/* The closing line of the joint-development pitch: its own beat, not another
   body paragraph. */
.jd-close{margin-top:1.6rem}
/* The floor matters more than the cap here: at 375px the vw term is only
   ~9px, so the clamp sits on its minimum and an earlier 1.05rem floor made
   this line exactly the same size as the body copy it is meant to close. */
.jd-close strong{font-family:var(--f-display);font-weight:500;
  font-size:clamp(1.25rem,3.2vw,1.5rem);letter-spacing:-.01em;line-height:1.35}

/* Keep the gradient phrase on one line.

   SplitText splits by line into separate <div>s, and a span straddling a line
   break is CLONED into two elements — measured: "A legacy " and "to keep." as
   two .grad nodes. With background-clip:text each clone then paints its own
   full gradient ramp, so the ramp restarts halfway through the phrase. Keeping
   it unbroken is what makes it read as one gradient.

   Safe to force here: this is the only .grad on the site, and the statement it
   sits in is short. Checked at 320px, the narrowest phone worth targeting. */
.stmt .grad{white-space:nowrap}

/* =========================================================================
   AMENITY GROUPS (Tranquil)
   Three themed columns instead of the pill list. Same dot as .amen-grid so
   the two treatments read as one family rather than two components.
   ========================================================================= */
.amen-groups{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  gap:clamp(1.8rem,4vw,3.2rem)}
.amen-group h3{font-family:var(--f-display);font-weight:600;
  font-size:clamp(1rem,2.2vw,1.15rem);letter-spacing:-.01em;color:var(--ink);
  padding-bottom:.9rem;margin-bottom:1.1rem;border-bottom:1px solid var(--line)}
.amen-group ul{list-style:none;margin:0;padding:0;
  display:flex;flex-direction:column;gap:.7rem}
.amen-group li{display:flex;align-items:baseline;gap:.65rem;
  color:var(--muted);font-size:0.92rem;line-height:1.6}
/* Matching .amen-grid .dotm: same 6px, same colour. translateY nudges it onto
   the text's optical centre, since the flex baseline aligns a round dot too
   low against lowercase. */
.amen-group li::before{content:'';width:6px;height:6px;border-radius:50%;
  background:var(--accent-lt);flex-shrink:0;transform:translateY(-1px)}

/* Project gallery slider. Same component as the interiors and sales reels;
   only the stage height differs, since this one runs the full width of the
   page rather than sharing a column. */
.gal-reel .reel-slide{height:clamp(260px,46vw,560px)}

/* The dots are 44px squares so a finger can hit them, which means even seven
   of them plus the counter exceed a 375px row. Let both the footer and the
   dot row wrap instead of spilling out of the container. */
.reel-foot{flex-wrap:wrap;gap:.5rem 1rem}
.reel-dots{flex-wrap:wrap}

/* =========================================================================
   TYPE SYSTEM
   Headings   SF Pro Display, Semibold 600 (Medium 500 for large display names)
   Body       SF Pro Text, Regular 400
   Small caps SF Pro Text, Medium 500, tracking .14em and up
   Restrained by rule: no 300, no 700, no 900 anywhere in this stylesheet.

   What changed: small uppercase labels ran at 700 and body copy at 300. Bold
   labels fight the headings for attention at a size too small to need the
   weight, and 300 body text on a dark ground reads thin — the stroke breaks up
   before the colour contrast does. 500 and 400 respectively.
   ========================================================================= */

/* Supporting line under the opening statement. Body face at Regular, so it
   reads as the quieter half of the pair rather than a second headline —
   large headline, restrained support, which is the whole point of the
   statement block. */
.stmt p.stmt-sub{font-family:var(--f-body);font-weight:400;
  font-size:clamp(0.95rem,2.2vw,1.25rem);line-height:1.6;letter-spacing:normal;
  color:var(--muted);max-width:46ch;margin:clamp(1rem,2vw,1.4rem) auto 0}

/* =========================================================================
   SMALL LABELS: one size, every width
   Last block in the file, deliberately.

   Two earlier passes raised this family from 9.9px to 11px, but only under
   820px -- both were measured on a 375px viewport, so desktop kept 9.9px by
   omission rather than by choice. Measured at 1920: 23 visible elements
   under 10.5px on about.html alone.

   These are uppercase at .14em-.4em tracking, which reads smaller than the
   number suggests, and they are not decoration: section eyebrows, the
   Address / Phone / Email labels, the form labels, and on a project card the
   status, the approval and the Bedrooms / Unit size specs -- the facts
   someone scans the card for.

   It sits at the end because .ring-badges and .ring-spec i are declared
   around line 1800 and won on source order when this block was earlier in
   the file: same specificity, later wins. Raising specificity instead would
   have started an arms race with whatever gets added next.

   0.62rem remains the token for genuinely decorative labels. This is a
   selector list, not a change to the token.
   ========================================================================= */
.eyebrow,.atlas-eyebrow,.atlas-coord,.tally span,.yr,
.atlas-list li .yr,.atlas-read dl dt,.credits dt,
.field label,.cfg-table thead th,.fs-badges .st,
.pillar .n,.estate-spec dt,.footer h4,
.unit-badges span,.fs-badges span,.p-amen i,
.ring-badges .st,.ring-badges .ap,.ring-spec i,
.sc-card .st,.pre-lab{font-size:.69rem}
.brand-txt small{font-size:.66rem}

/* =========================================================================
   PROJECTS SLIDER
   The projects listing, as a side slider driven by the All / Ongoing /
   Completed filters. Same renderReel component as the galleries, so the
   arrows, dots, counter and swipe all come for free; only the slide height
   and the caption block are specific to it.
   ========================================================================= */
/* The 320px floor was measured wrong-way-round: the caption carries five
   things and is ~260px tall regardless of viewport, so at a 320px screen it
   filled 84% of the slide and left 50px of photograph. A project card that is
   almost entirely caption is not showing anyone the building. 420px floor puts
   the caption at ~62% there, which leaves the shot a real share of the frame.
   The vw term and the 560 cap are unchanged, so desktop is unaffected. */
.ps-reel .reel-slide{height:clamp(420px,52vw,560px)}
/* Arrows sit in the picture, not on the caption.

   The shared reel centres its arrows at top:50%, which is right when the
   caption is a thin bar. This caption is a panel about 260-275px tall, so at
   every width the midpoint lands inside it -- measured at 375px, the location
   line ran under BOTH arrows (line 328-346, arrows 305-349). The caption is
   roughly a fixed height while the slide grows, so a percentage cannot solve
   this; a fixed offset from the top can, and 48px puts the 44px arrow at
   48-92px, inside the picture band on every size from 420px up. */
.ps-reel .reel-nav{top:48px;transform:none}
/* Fit the whole image to the card, do not crop it.

   I had this on object-fit:cover for one consistent frame, because these five
   heroes range from 1060x1484 portrait to 1600x900 landscape. Cover crops, and
   on the portrait shots it was cutting the top and bottom off the building --
   which on a page whose whole job is showing the building is the wrong trade.
   Reverted to the shared reel's contain behaviour, so every project is shown
   whole and the letterbox is filled by .reel-slide::before, the blurred copy
   of the same shot, rather than by flat black. */

/* The caption is a panel over the lower half of the shot rather than a bar
   under it: these slides carry five things (location, name, badges, specs, a
   button) and a bar deep enough for all five would leave the photograph a
   letterbox strip. The gradient is opaque enough at the base to hold the
   button and the spec text over any frame. */
.ps-cap{position:absolute;left:0;right:0;bottom:0;z-index:2;
  display:flex;flex-direction:column;align-items:flex-start;gap:.7rem;
  padding:clamp(1.1rem,3vw,2rem);
  /* Measured against the brightest pixel behind each label on all five
     slides. At .92/.72/transparent the panel thinned out too early: over
     the Tranquil and TechVille shots the sky sits right behind the spec
     labels (image luma 116 and 112) and --muted fell to 1.83:1 and 1.94:1.
     Carrying real weight to the top of the panel fixes every label at once,
     which is better than tinting the one that happened to fail. The top stop
     stays short of opaque so the panel still reads as part of the picture. */
  background:linear-gradient(0deg,rgba(10,10,10,.95) 0%,rgba(10,10,10,.9) 55%,rgba(10,10,10,.72) 100%)}
/* Top stop .62 -> .72. .62 cleared the spec labels but left the location line,
   which sits highest in the panel, at 4.36-4.43:1 against the brightest sky
   behind it -- passing to the eye but under 4.5. .72 puts it at about 5.7:1,
   so it is not sitting on the threshold where a future image swap tips it. */
.ps-loc{display:inline-flex;align-items:center;gap:.45rem;color:var(--accent-lt);
  font-size:.69rem;font-weight:500;letter-spacing:.2em;text-transform:uppercase}
.ps-name{font-family:var(--f-display);font-weight:600;color:#fff;margin:0;
  font-size:clamp(1.5rem,3.4vw,2.3rem);letter-spacing:-.015em;line-height:1.1}
.ps-badges{display:flex;flex-wrap:wrap;gap:.4rem}
.ps-spec{display:flex;flex-wrap:wrap;gap:.35rem 1.4rem;color:var(--ink-2);font-size:.9rem}
.ps-spec i{font-style:normal;display:block;font-size:.69rem;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted);margin-bottom:.15rem}
.ps-open{margin-top:.35rem}
.ps-empty{color:var(--muted);padding:2rem 0}

@media (max-width:560px){
  /* The button is the widest item in the caption and the specs are the least
     urgent, so on a narrow phone the specs step down rather than the button
     wrapping onto two lines. */
  .ps-spec{gap:.3rem .9rem;font-size:.84rem}
  .ps-cap{gap:.55rem}
}

/* Insights cards that link somewhere.
   The unlinked card keeps its existing look exactly; only a card with a
   destination gains the affordances -- pointer, hover lift, and a "Read more"
   row. A card that looks clickable and is not is worse than a plain one. */
.blog-link{display:flex;flex-direction:column;text-decoration:none;color:inherit;
  transition:transform .35s var(--ease),border-color .35s var(--ease)}
.blog-link:hover{transform:translateY(-4px);border-color:var(--accent)}
.blog-link h3{color:var(--ink)}
.blog-more{display:inline-flex;align-items:center;gap:.5rem;margin-top:auto;
  padding-top:1rem;color:var(--accent);font-size:.69rem;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase}
.blog-link:hover .blog-more{color:var(--accent-lt)}
.blog-more .arw{display:inline-flex}

/* "Own land in Bengaluru?" on one line.

   Two separate things were breaking it. The inline max-width:16ch resolved to
   about 20px NARROWER than the phrase needs at every width -- 371px against
   382 needed at 375, 743 against 764 at 1440 -- so it wrapped even where the
   column had room to spare. Removing that alone fixes 768 and 1024, where the
   parent has 671px and 737px available.

   At 1440 and above the column itself is the limit: .s-head caps at 737px and
   the phrase wants 764px at the full 70.4px type. Rather than widen .s-head --
   which would drag the supporting paragraph out to a punishing measure -- the
   cap on THIS heading drops to 4.15rem (66.4px). That needs 720px, inside the
   737 available, and the change is small enough not to read as a different
   size from its neighbours.

   Phones still wrap it, and cannot not: at 375px the column IS the viewport at
   315px, and one line there needs about 29px type against a 35.2px floor. */
.jd-title{max-width:none;font-size:clamp(2.2rem,5.2vw,4.15rem)}

/* =========================================================================
   LOGO: brand blue mark, and a tagline that survives the hero

   The mark is back to the brand blue it is on the printed material. A brand
   mark that disagrees with your letterhead and site boards is a worse problem
   than one cool element in a warm palette, and the two families do not fight:
   measured 1.14:1 against each other, so they read as separate things.

   DEVELOPERS is the part that had to change. Over the hero the header has no
   background of its own -- it is transparent until .stuck -- so brass, a
   mid-tone, had nothing to separate it from a bright frame: measured 3.02:1
   average and 1.48:1 over the brightest part of the poster, which is invisible.
   White survives the same test at 8.64 and 4.22 because it sits at the end of
   the range rather than the middle.

   So: white while the header floats over imagery, brass once it sticks onto
   the dark bar, where brass measures 6.56:1 and is doing what it is good at.
   Scoped to .header on purpose -- the footer wordmark sits on solid black and
   keeps its brass at 6.79:1. */
.header:not(.stuck) .brand-txt small{color:#fff}
.header.stuck .brand-txt small{color:var(--accent)}
