/* ==========================================================================
   Cappella Embassy — VISUAL SYSTEM V2 (Residence + Chancery)
   Owner Gate 2 representative prototype. HOME + TYPE D2 only.
   Authority: CAPPELLA_EMBASSY_VISUAL_SYSTEM_V2_FINAL.md, as adjusted by
   docs/decisions/OWNER_GATE_1_DECISION_RECORD.md.

   This file is linked ONLY by src/_includes/partials/head-v2.njk. No other
   route loads it. styles.css is untouched.

   Typography: Libre Caslon Display / Text and Noto Serif SC / Noto Sans SC are
   self-hosted subsets of the authoritative upstream (V2 prerequisite P6, met).
   ========================================================================== */

/* Self-hosted, subset from the authoritative upstream github.com/google/fonts.
   Libre Caslon Display · Libre Caslon Text · Noto Serif SC · Noto Sans SC are
   SIL OFL 1.1; licences and hashes are recorded in
   docs/decisions/OWNER_GATE_2_REFINEMENT_REPORT.md. No runtime request is made
   to any third-party font host. */
@font-face{font-family:"Libre Caslon Display";src:url('/assets/fonts/libre-caslon-display-400.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Libre Caslon Text";src:url('/assets/fonts/libre-caslon-text-400.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:Inter;src:url('/assets/fonts/inter-400-normal.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:Inter;src:url('/assets/fonts/inter-600-normal.woff2') format('woff2');font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:"Noto Serif SC";src:url('/assets/fonts/noto-serif-sc-400.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Noto Serif SC";src:url('/assets/fonts/noto-serif-sc-500.woff2') format('woff2');font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:"Noto Sans SC";src:url('/assets/fonts/noto-sans-sc-500.woff2') format('woff2');font-weight:500;font-style:normal;font-display:swap}
/* Numeric-run companion. Its own family name, so only --ff-num reaches it and
   no other weight-400 request in "Noto Sans SC" is redirected. */
@font-face{font-family:"Noto Sans SC Num";src:url('/assets/fonts/noto-sans-sc-400.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}

/* ---------- §7.1 tokens ---------- */
.v2{
  --paper:#F7F2E8; --paper-2:#EFE7D8; --plate:#FFFFFF;
  --ink:#0F1A15; --ink-2:#182720;
  --text:#221F1B; --text-2:#5B564E;
  --text-on-ink:#F3EDE1; --text-2-on-ink:rgba(243,237,225,.68);
  --gold:#AB8D61; --gold-2:#C99260; --gold-light:#D9C39A;
  --plum:#7D6985; --plum-text:#5F4E6A; --plum-light:#B9ACBF;
  --line:rgba(34,31,27,.14); --line-strong:rgba(34,31,27,.32); --line-on-ink:rgba(243,237,225,.16);
  --focus:#5F4E6A; --error:#8B3A3A;

  /* §10.2 rhythm */
  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px; --s-5:24px; --s-6:32px; --s-7:48px; --s-8:64px;
  --chapter-gap:clamp(4rem,8vw,7rem);
  --band-pad:clamp(2.5rem,5vw,4.5rem);
  --desk-pad:clamp(4rem,8vw,6.5rem);
  --gap:clamp(1rem,2vw,2rem);
  --bar-h:72px;

  --ease:cubic-bezier(.22,1,.36,1);
  --ff-display:"Libre Caslon Display",Georgia,'Times New Roman',serif;
  --ff-text:"Libre Caslon Text",Georgia,'Times New Roman',serif;
  --ff-ui:Inter,system-ui,-apple-system,sans-serif;
  --ff-num:Inter,system-ui,sans-serif;
}
/* §8.4 keeps figures, unit codes, currency and dates in the Latin family in
   every locale, so Inter stays first in --ff-num and the numerals keep their
   tabular lining treatment. A Chinese linguistic unit word inside the same run
   has no glyph in a Latin-only stack and used to drop to the system PingFang SC.
   Owner decision, 2026-09-08 - a figure and a Latin unit code stay Latin, a
   Chinese unit WORD renders in Noto Sans SC 400. "Noto Sans SC Num" is that 400
   cut under its own family name, so only --ff-num can reach it and the 500 ui
   role is untouched. Declarations only below - keep prose outside the block,
   because tools/validate.mjs parses these custom properties by splitting on
   the semicolon. */
.v2.cjk-page{
  --ff-display:"Noto Serif SC","Songti SC","STSong","SimSun",serif;
  --ff-text:"Noto Serif SC","Songti SC","STSong","SimSun",serif;
  --ff-ui:"Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif;
  --ff-num:Inter,"Noto Sans SC Num",system-ui,sans-serif;
}
/* §8.4 Chinese metrics */
.v2.cjk-page .wordmark-cjk,.v2.cjk-page .display-l,.v2.cjk-page .display-m{font-weight:500;line-height:1.22;letter-spacing:.02em}
.v2.cjk-page .label,.v2.cjk-page .nav-link,.v2.cjk-page .btn,.v2.cjk-page .small,
.v2.cjk-page .caption,.v2.cjk-page summary{font-weight:500}
/* §8.1/§8.4 CJK weight and family consistency.
   The authorised Chinese inventory is Noto Serif SC 400 (body) / 500 (Display
   L·M) and Noto Sans SC 400 / 500 (interface, one weight in use: 500 for labels
   and nav). It has no 700 and no 600. Five selectors were asking for a weight or
   a family the Chinese page cannot answer, so the browser left the family and
   painted the glyphs in Songti SC / PingFang SC:

     th                          UA bold  -> Noto Serif SC 700   (no such face)
     h3 with no class            UA bold  -> Noto Serif SC 700   (no such face)
     details.more > summary      600      -> Noto Sans SC  600   (no such face)
     .ledger small               inherit  -> Noto Sans SC  400   (family disagrees
     .ruled li em                inherit  -> Noto Sans SC  400    with the role)
     .flag                       600      -> Noto Sans SC  600   (no such face)

   Each is mapped to the nearest AUTHORISED Chinese role — exactly the mapping
   .label already makes, where Inter 600 on a Latin page becomes Noto Sans SC 500
   on a Chinese one. th and summary are Label roles (Noto Sans SC 500); a classed
   or bare h3, a ledger note and a ruled tag are body roles (Noto Serif SC 400,
   the same treatment h3.heading already ships with). Weight and family only:
   size, colour, tracking and case are untouched, so hierarchy is still carried
   the way the protected routes carry it. Scoped to .cjk-page, so no Latin or
   Malay page can move. */
.v2.cjk-page th{font-family:var(--ff-ui);font-weight:500}
.v2.cjk-page details.more > summary{font-weight:500}
.v2.cjk-page h3:not([class]){font-weight:400}
.v2.cjk-page .ledger small,
.v2.cjk-page .ruled li em,
.v2.cjk-page .flag{font-family:var(--ff-text);font-weight:400}
.v2.cjk-page{text-spacing-trim:space-first}
/* on ink, the register flips: gold speaks, plum recedes (§6.5) */
.v2 .on-ink{--text:var(--text-on-ink);--text-2:var(--text-2-on-ink);--line:var(--line-on-ink);--line-strong:var(--line-on-ink);--plum-text:var(--gold-light);--focus:#D9C39A}

/* ---------- base ---------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:calc(var(--bar-h) + 16px);background:#F7F2E8}
body.v2{
  margin:0;min-width:320px;background:var(--paper);color:var(--text);
  font-family:var(--ff-text);font-size:1.0625rem;line-height:1.62;
  font-variant-numeric:oldstyle-nums;font-synthesis-weight:none;
  text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;overflow-x:clip;
}
@media (min-width:900px){body.v2{font-size:1.125rem}}
body.v2.cjk-page{line-height:1.85;font-variant-numeric:normal}
.v2 img,.v2 picture,.v2 svg,.v2 video{display:block;max-width:100%}
.v2 img{height:auto}
.v2 a{color:inherit;text-decoration:none}
.v2 button,.v2 input,.v2 select,.v2 textarea{font:inherit;color:inherit}
.v2 h1,.v2 h2,.v2 h3,.v2 h4,.v2 p,.v2 figure,.v2 ul,.v2 ol,.v2 dl,.v2 dd,.v2 table{margin:0}
.v2 ul,.v2 ol{padding:0;list-style:none}
.v2 :focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.v2 ::selection{background:var(--gold-light);color:var(--ink)}
.vh{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.v2 .skip-link{position:fixed;top:-6rem;left:1rem;z-index:9999;padding:.85rem 1.25rem;background:var(--gold);color:var(--ink);font-family:var(--ff-ui);font-weight:600;font-size:.8125rem}
.v2 .skip-link:focus{top:1rem}

/* ---------- §10.1 surfaces ---------- */
.v2 .wrap{width:min(100% - clamp(1.5rem,6vw,6rem),1320px);margin-inline:auto}
.v2 .grid12{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--gap)}
.v2 .band{padding-block:var(--band-pad);position:relative}
.v2 .band + .band{border-top:1px solid var(--line)}
.v2 .band-2{background:var(--paper-2)}
.v2 .band-ink{background:var(--ink);color:var(--text-on-ink);padding-block:var(--desk-pad);border-top:0}

/* ---------- §8.2 type scale ---------- */
.v2 .wordmark{font-family:var(--ff-display);font-size:clamp(3.25rem,8.4vw,7.5rem);line-height:.92;letter-spacing:.05em;text-transform:uppercase;font-weight:400}
.v2 .display-l{font-family:var(--ff-display);font-size:clamp(2.4rem,4.6vw,4.25rem);line-height:1.02;font-weight:400;text-wrap:balance}
.v2 .display-m{font-family:var(--ff-display);font-size:clamp(1.75rem,2.6vw,2.5rem);line-height:1.12;font-weight:400;text-wrap:balance}
.v2 .numeral-xl{font-family:var(--ff-display);font-size:clamp(2.5rem,4vw,3.75rem);line-height:1;font-variant-numeric:tabular-nums lining-nums}
.v2 .heading{font-family:var(--ff-text);font-size:1.25rem;line-height:1.3;font-weight:400}
.v2 .lead{font-family:var(--ff-text);font-size:clamp(1.15rem,1.5vw,1.375rem);line-height:1.5;max-width:52ch}
.v2 .small{font-family:var(--ff-ui);font-size:.8125rem;line-height:1.5;color:var(--text-2)}
.v2 .label{font-family:var(--ff-ui);font-size:.6875rem;line-height:1.2;letter-spacing:.18em;text-transform:uppercase;font-weight:600}
.v2 .num{font-family:var(--ff-num);font-variant-numeric:tabular-nums lining-nums;white-space:nowrap}
.v2 .measure{max-width:min(66ch,720px)}
.v2 .measure p + p{margin-top:1.1em}
.v2 .position{max-width:44ch}
.v2 .chapter-title{max-width:18ch}
.v2 .answers{max-width:70ch}
.v2.cjk-page .measure,.v2.cjk-page .lead,.v2.cjk-page .position,.v2.cjk-page .answers,
.v2.cjk-page .chapter-title{max-width:38em}
.v2.cjk-page{line-break:strict;word-break:normal;hanging-punctuation:first last}
/* The mirror rule for the other two locales. English and Malay pages carry a
   few Chinese words inline — 中文 in the Contact position line, 雅益轩 in the
   naming answers, 简体中文 in the language switcher. Default line breaking
   offers a break opportunity between EVERY pair of CJK characters, so a narrow
   column split 中文 across two lines. keep-all suppresses exactly those breaks
   and leaves Latin word spacing untouched, because Latin already breaks only at
   spaces and hyphens. Chinese pages must keep breaking between characters, so
   they are excluded — their own rule is the line above. */
.v2:not(.cjk-page){word-break:keep-all}
.v2.cjk-page .label,.v2.cjk-page .nav-link{letter-spacing:.12em}
.v2.cjk-page .wordmark{letter-spacing:.08em}

/* ---------- §9 coronet ---------- */
.v2 .coronet{display:block;width:100%;height:100%;fill:currentColor}
.v2 .rule-coronet{display:flex;align-items:flex-end;gap:6px;height:20px;color:var(--gold);margin-bottom:var(--s-3)}
.v2 .rule-coronet i{display:block;width:1px;background:currentColor}
.v2 .rule-coronet i:nth-child(1),.v2 .rule-coronet i:nth-child(5){height:8px}
.v2 .rule-coronet i:nth-child(2),.v2 .rule-coronet i:nth-child(4){height:14px}
.v2 .rule-coronet i:nth-child(3){height:20px}
.v2 .rule-coronet::after{content:"";height:1px;width:40px;background:currentColor;margin-bottom:3px;opacity:.7}
.v2 .eyebrow{display:block;color:var(--plum-text);margin-bottom:var(--s-4)}
.v2 .on-ink .rule-coronet{color:var(--gold-light)}

.v2 .watermark{display:none}
@media (min-width:900px){
  .v2 .watermark{display:block;position:absolute;left:0;top:-4%;width:min(560px,40%);
    color:var(--gold);opacity:.06;pointer-events:none;z-index:0}
}
.v2 .watermark + *{position:relative;z-index:1}

/* ---------- §9 seal ---------- */
.v2 .seal{position:absolute;left:16px;bottom:16px;display:inline-flex;align-items:center;gap:8px;
  padding:6px 10px;border:1px solid currentColor;background:transparent;
  font-family:var(--ff-ui);font-size:.6875rem;letter-spacing:.18em;text-transform:uppercase;font-weight:600;line-height:1.25;z-index:2}
.v2 .seal svg{width:12px;height:12px;flex:none}
.v2 .seal span{display:block}
.v2 .seal span + span{opacity:.85}
.v2 .seal-on-photo{color:var(--paper);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);background:rgba(15,26,21,.22)}
.v2 .seal-on-plate{color:var(--plum)}
.v2 .doc-seal{width:64px;height:64px;border:1px solid var(--plum);border-radius:50%;color:var(--plum);display:grid;place-items:center}
.v2 .doc-seal svg{width:26px;height:26px}

/* ---------- §12.1 Chancery Bar ---------- */
.v2 .bar{position:sticky;top:0;z-index:900;background:var(--paper);border-bottom:1px solid var(--line)}
.v2 .bar-in{display:flex;align-items:center;justify-content:space-between;gap:clamp(.75rem,1.6vw,1.5rem);height:var(--bar-h)}
.v2 .lockup{display:inline-flex;align-items:center;gap:12px;color:var(--text);min-height:44px}
.v2 .lockup .mark{width:32px;height:32px;color:var(--gold);flex:none}
.v2 .lockup b{display:block;white-space:nowrap;font-family:var(--ff-display);font-weight:400;font-size:1.0625rem;letter-spacing:.14em;text-transform:uppercase;line-height:1.05}
.v2 .lockup .sub{display:block;white-space:nowrap;font-family:var(--ff-ui);font-size:.625rem;letter-spacing:.16em;text-transform:uppercase;color:var(--text-2);margin-top:3px}
.v2 .nav-list{display:flex;align-items:center;gap:clamp(.7rem,1.35vw,1.6rem)}
/* Owner directive 2026-09-10 — the 360 experience joins the primary navigation,
   taking the link count from seven to eight. The bar lives inside .wrap, which
   is capped at 1320 px, so the room an eighth link needs cannot be bought with
   a wider viewport: measured at the entry state the bar had 0 px free at both
   1320 and 1200, and the eighth link left it −23 px (EN) / −25 px (MS) at a
   1320 px wrap and −87 px / −89 px at a 1240 px wrap.

   The Owner's order forbids smaller navigation type, negative tracking, wrapped
   labels and overlap, so the two remaining moves are taken instead:

   1. The Brochure word folds into its glyph wherever the full navigation is
      present. This is not a new device — it is Owner Gate 2 §7's own treatment,
      previously scoped to 1200–1319 px, now scoped to the whole desktop band.
      The item is not hidden and it is not unlabelled: the word stays in the DOM
      and in the accessible name, and it returns in full below the swap, where
      the links sit in the sheet and the bar has 294–575 px free.
   2. The responsive swap moves from 1200 px to 1360 px. Below it the existing
      sheet — which already carries every primary destination, including the one
      360 entry — is the better composition, exactly as the Owner permits.

   Measured after the change: 1440 px +58 px (EN) / +56 px (MS) free, 1366 px
   +31 px / +30 px, both with zero wrap, zero overlap and no new overflow. */
@media (max-width:1359px){
  .v2 .bar .lockup .sub{display:none}
  .v2 .bar .btn{padding:0 1rem}
  .v2 .nav-main{display:none}
  .v2 .lockup .sub{display:none}
}
@media (min-width:1360px){
  .v2 .bar-doc span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
}
.v2 .nav-link{font-family:var(--ff-ui);font-size:.75rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;line-height:1;padding-block:6px;white-space:nowrap}
.v2 .nav-link[aria-current]{border-bottom:1px solid var(--plum)}
.v2 .bar-side{display:flex;align-items:center;gap:var(--s-4)}
.v2 .lang{display:flex;align-items:center;gap:6px;font-family:var(--ff-ui);font-size:.6875rem;letter-spacing:.12em;text-transform:uppercase;color:var(--text-2)}
.v2 .lang a[aria-current]{color:var(--text);border-bottom:1px solid var(--plum)}
.v2 .bar-doc{display:inline-flex;align-items:center;gap:8px;font-family:var(--ff-ui);font-size:.6875rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--plum-text);white-space:nowrap}
.v2 .bar-doc svg{width:14px;height:14px}
.v2 .burger{display:none;width:48px;height:48px;border:1px solid var(--line-strong);background:transparent;padding:0;cursor:pointer;place-items:center}
.v2 .burger i{display:block;width:18px;height:1px;background:var(--text)}
.v2 .burger i + i{margin-top:5px}
.v2 .bar-compact-only{display:none}
/* The burger reveal must stay BELOW the base .burger declaration: both are
   (0,2,0), so an earlier block loses the cascade and the compact bar would ship
   with no way into the sheet. */
@media (max-width:1359px){
  .v2 .burger{display:grid}
}
@media (max-width:1199px){
  .v2{--bar-h:64px}
}
@media (max-width:899px){
  .v2{--bar-h:60px}
  .v2 .bar-doc,.v2 .bar .btn{display:none}
  /* The language switch stays reachable from the bar at every width down to
     640 px — a mechanic taken from the navigation benchmark. Below 640 px it
     lives in the sheet only, where the rows are large enough to hit. */
  .v2 .lang{font-size:.625rem;gap:4px}
}
@media (max-width:639px){ .v2 .bar .lang{display:none} }

/* nav sheet (§14) — top-aligned, scrolls, never centred */
.v2 .sheet{position:fixed;inset:0;z-index:950;background:var(--paper);overflow-y:auto;
  display:block;padding:var(--s-5) 0 var(--s-8)}
.v2 .sheet[hidden]{display:none}
.v2 .sheet-head{display:flex;align-items:center;justify-content:space-between;min-height:60px}
.v2 .sheet-close{width:44px;height:44px;border:1px solid var(--line-strong);background:transparent;cursor:pointer;font-size:1rem;color:var(--text)}
.v2 .sheet nav a{display:flex;align-items:center;min-height:56px;font-family:var(--ff-text);font-size:1.375rem;padding:.5rem 0;border-bottom:1px solid var(--line)}
.v2 .sheet nav a[aria-current]{color:var(--plum-text)}
.v2 .sheet nav a[aria-current]::after{content:"";flex:none;width:6px;height:6px;background:var(--gold);margin-left:auto}
.v2 .sheet-doc{padding:.85rem 0;border-bottom:1px solid var(--line);font-family:var(--ff-ui);font-size:.8125rem}
.v2 .sheet-langs{display:flex;gap:.5rem;padding:1rem 0;font-family:var(--ff-ui);font-size:.875rem}
.v2 .sheet-langs a{display:inline-flex;align-items:center;min-height:48px;padding:0 .9rem;border:1px solid var(--line-strong)}
.v2 .sheet-langs a[aria-current]{border-color:var(--gold);color:var(--plum-text)}
.v2 .sheet-phone{margin-top:1rem;font-family:var(--ff-ui);font-size:.9375rem}

/* ---------- §12.5 chapter ---------- */
/* Interior chapter height is fold-aware for the same reason the arrival hero is,
   and by the same mechanism. A flat 64svh seated 576px of hero at 1440x900 and
   left only 251px under it, while the Title Band needs 269px (two display-l
   lines) and up to 277px in Chinese, where the position line stacks below the
   title. The fold then bisected the H1's second line, or the size line. Measured
   at 1440x900 before the correction, 12 of the 15 chapter routes crossed it,
   worst case 26px; the three named in the review are instances of this one
   dependency, not three faults.
   The hero therefore keeps its full 64svh wherever the viewport is tall enough
   to seat the title too, and yields only the exact shortfall on shorter screens
   — inert at 1680x1050 and above, where nothing crossed. Type scale, focal
   model, image assets and the Title Band's own rhythm are all untouched; the
   only thing that moves is the height of a container that was already
   viewport-proportional. Widths <=900px keep the mobile rule below. */
.v2 .chapter{position:relative;width:100%;overflow:hidden;background:var(--ink);
  --interior-chrome:185px;                       /* bar 72+1 + band lead-in ~100 + 12 breathing */
  --title-line:clamp(2.4rem,4.6vw,4.25rem);      /* one .display-l line, font-size */
  height:clamp(420px,min(64svh,100svh - var(--interior-chrome) - var(--title-line) * 2.75),720px)}
/* Arrival hero height is FOLD-AWARE, not merely viewport-proportional.
   The Home wordmark is a deliberate two-line lockup (.wm-2 is display:block),
   so the title band needs a fixed vertical block below the hero: header 73px +
   band lead-in ~91px + two wordmark lines + breathing room. At a plain 70svh a
   1440x900 desktop seated only 197px of first fold under a 630px hero while the
   wordmark alone needs 221px — the fold then bisected "CAPPELLA" mid-glyph.
   Nothing was clipping the text (overflow:visible, max-width:none); the cut was
   pure vertical geometry. The hero therefore keeps its full 70svh wherever the
   viewport is tall enough to seat the wordmark too, and yields only the exact
   shortfall on shorter screens. Type scale is untouched.
   Widths <=900px keep the mobile/tablet rule below and are unaffected. */
.v2 .chapter-arrival{
  --wm-line:clamp(3.25rem,8.4vw,7.5rem);        /* one .wordmark line, font-size */
  --arrival-chrome:180px;                        /* header 73 + band lead-in 91 + 16 breathing */
  height:clamp(420px,min(70svh,100svh - var(--arrival-chrome) - var(--wm-line) * 1.84),800px)}
.v2 .chapter picture,.v2 .chapter img{width:100%;height:100%;object-fit:cover;object-position:var(--focal,50% 50%)}
/* Focal model: IMAGE x PLACEMENT x VIEWPORT. object-position alone cannot help
   when the container is NARROWER than the image: cover then scales to height,
   the whole frame height stays visible, and a hero with generous ceiling reads
   as dead space on a phone. --zoom-m tightens the crop for that viewport only,
   pivoting on --zoom-origin-m (default: the mobile focal point). */
.v2 .chapter img{transform-origin:var(--focal,50% 50%)}
.v2 .chapter-plate{display:none;background:var(--ink);color:var(--gold-light);height:72px;align-items:center;
  font-family:var(--ff-ui);font-size:.8125rem;letter-spacing:.06em}
@media (min-width:900px){.v2 .chapter-plate{display:flex}}
@media (max-width:899px){
  .v2 .chapter{height:48svh;min-height:320px}
  .v2 .chapter-arrival{height:56svh;min-height:380px}
  .v2 .chapter img{object-position:var(--focal-m,var(--focal,50% 50%));
    transform-origin:var(--zoom-origin-m,var(--focal-m,var(--focal,50% 50%)))}
}

/* ---------- §12.6 title band ---------- */
/* Top padding is tighter than --band-pad so that, under a 70svh arrival
   chapter and the 72 px bar, the wordmark's first line enters the first
   viewport at 1440 x 1000 (V2 §22.4 criterion 3). */
.v2 .titleband{padding-top:clamp(1.75rem,2.6vw,2.35rem);padding-bottom:var(--band-pad)}
.v2 .titleband .rule-coronet{margin-bottom:var(--s-2)}
.v2 .titleband .eyebrow{margin-bottom:var(--s-3)}
.v2 .titleband .breadcrumb{font-family:var(--ff-ui);font-size:.75rem;color:var(--text-2);margin-bottom:var(--s-5)}
.v2 .titleband .breadcrumb a:hover{text-decoration:underline}
.v2 .tb-row{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--gap);align-items:end}
.v2 .tb-title{grid-column:1/9}
.v2 .tb-position{grid-column:9/13;padding-bottom:.6rem}
@media (max-width:1199px){.v2 .tb-title,.v2 .tb-position{grid-column:1/13}.v2 .tb-position{padding-bottom:0;margin-top:var(--s-5)}}
.v2 .wm-1{color:var(--ink)}
.v2 .wm-2{color:var(--plum);display:block}
.v2 .wordmark-cjk{font-family:var(--ff-display);font-size:clamp(2.75rem,7vw,6rem);line-height:1.05;letter-spacing:.08em;font-weight:400}
.v2 .wordmark-cjk small{display:block;font-family:Georgia,'Times New Roman',serif;font-size:.32em;letter-spacing:.22em;text-transform:uppercase;color:var(--plum);margin-top:.5em}
.v2 .tb-actions{display:flex;flex-wrap:wrap;gap:var(--s-4);margin-top:var(--s-7)}

/* ---------- §12.7 credentials ledger ---------- */
.v2 .ledger{border-top:1px solid var(--line-strong);border-bottom:1px solid var(--line-strong);
  display:grid;grid-template-columns:repeat(2,1fr);margin-top:var(--s-7)}
@media (min-width:900px){.v2 .ledger{grid-template-columns:repeat(6,1fr)}}
.v2 .ledger > div{padding:var(--s-5) var(--s-4);border-left:1px solid var(--line)}
.v2 .ledger > div:first-child{border-left:0;padding-left:0}
@media (max-width:899px){
  .v2 .ledger > div{border-top:1px solid var(--line)}
  .v2 .ledger > div:nth-child(-n+2){border-top:0}
  .v2 .ledger > div:nth-child(odd){border-left:0;padding-left:0}
}
/* Four-entry ledger. Six tracks are right only for the six-entry project ledger;
   four entries there would leave two dead tracks and wrap prose into 200px
   columns. These entries carry sentence-length values whose Malay and Chinese
   renderings are the longest, so a track is only split again once it can still
   hold a line: one column on phones, two through tablet and small laptop, four
   from 1100px. Each state restates its own borders because the base ledger's
   nth-child rules assume two or six tracks. */
@media (max-width:639px){
  .v2 .ledger-4{grid-template-columns:1fr}
  .v2 .ledger-4 > div,.v2 .ledger-4 > div:nth-child(-n+2){border-left:0;padding-left:0;border-top:1px solid var(--line)}
  .v2 .ledger-4 > div:first-child{border-top:0}
}
@media (min-width:640px) and (max-width:1099px){
  .v2 .ledger-4{grid-template-columns:repeat(2,1fr)}
  .v2 .ledger-4 > div{border-top:1px solid var(--line);border-left:1px solid var(--line)}
  .v2 .ledger-4 > div:nth-child(-n+2){border-top:0}
  .v2 .ledger-4 > div:nth-child(odd){border-left:0;padding-left:0}
}
@media (min-width:1100px){
  .v2 .ledger-4{grid-template-columns:repeat(4,1fr)}
  .v2 .ledger-4 > div,.v2 .ledger-4 > div:nth-child(-n+2){border-top:0;border-left:1px solid var(--line);padding-left:var(--s-4)}
  .v2 .ledger-4 > div:first-child{border-left:0;padding-left:0}
}
.v2 .ledger dt{color:var(--plum-text)}
/* Ledger entries sit side by side, never stacked in a numeric column, so tabular
   figures align nothing here. Libre Caslon Text's tnum also remaps comma, period,
   colon, middle dot and slash to the 1187-unit digit advance, which detaches every
   comma from its digits ("1 , 109"). Values that genuinely need tabular treatment
   carry .num, whose Inter tnum leaves punctuation alone. */
.v2 .ledger dd{font-family:var(--ff-text);font-size:1.25rem;line-height:1.3;margin-top:var(--s-2);font-variant-numeric:lining-nums}
/* .num on the cell itself asks for tabular figures deliberately; the rule above
   out-specifies .v2 .num, so that intent is restated rather than lost. */
.v2 .ledger dd.num{font-variant-numeric:tabular-nums lining-nums}
.v2 .ledger-2{grid-template-columns:1fr 1fr!important}
.v2 .ledger-2 dd.num{white-space:normal}
.v2 .ledger-2 > div{border-left:1px solid var(--line);border-top:1px solid var(--line)}
.v2 .ledger-2 > div:nth-child(-n+2){border-top:0}
.v2 .ledger-2 > div:nth-child(odd){border-left:0;padding-left:0}
/* One column below 640px: no cell keeps a left rule, and every cell but the first
   keeps its divider. These must out-specify the two-column nth-child rules above,
   which is why the single-column state restates them rather than relying on order. */
@media (max-width:639px){
  .v2 .ledger-2{grid-template-columns:1fr!important}
  .v2 .ledger-2 > div,.v2 .ledger-2 > div:nth-child(-n+2){border-left:0;padding-left:0;border-top:1px solid var(--line)}
  .v2 .ledger-2 > div:first-child{border-top:0}
}
.v2 .seal-on-plate{background:var(--plate)}
.v2 .ledger small{display:block;font-family:var(--ff-ui);font-size:.75rem;color:var(--text-2);margin-top:4px;font-variant-numeric:normal}

/* ---------- §12.19 buttons ---------- */
.v2 .btn{display:inline-flex;align-items:center;justify-content:center;gap:.6rem;min-height:48px;padding:0 1.5rem;
  border:1px solid transparent;border-radius:0;cursor:pointer;
  font-family:var(--ff-ui);font-size:.8125rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
  transition:background-color .18s var(--ease),color .18s var(--ease),border-color .18s var(--ease)}
.v2 .btn svg{width:17px;height:17px;flex:none}
.v2 .btn-primary{background:var(--gold);color:var(--ink);border-color:var(--gold)}
.v2 .btn-primary:hover{background:var(--gold-2);border-color:var(--gold-2)}
.v2 .btn-secondary{border-color:var(--line-strong);color:var(--text);background:transparent}
.v2 .btn-secondary:hover{border-color:var(--text)}
.v2 .on-ink .btn-secondary{border-color:var(--line-on-ink);color:var(--text-on-ink)}
.v2 .tertiary{font-family:var(--ff-ui);font-size:.8125rem;font-weight:600;color:var(--plum-text);text-decoration:underline;text-underline-offset:4px}
.v2 .on-ink .tertiary{color:var(--gold-light)}

/* ---------- §12.20 plates and captions ---------- */
.v2 .plate{position:relative;border:1px solid var(--line-strong);background:var(--paper-2);overflow:hidden}
.v2 .plate > picture{display:block;width:100%;height:100%}
.v2 .plate img{width:100%;height:100%;object-fit:cover;object-position:var(--focal,50% 50%)}
.v2 .plate-detail{background:var(--paper-2);padding:clamp(12px,1.4vw,20px);overflow:visible}
.v2 .plate-detail .plate-inner{background:var(--plate);border:1px solid var(--line);position:relative;display:block}
.v2 .plate-detail img{width:100%;height:auto;object-fit:contain;min-height:min(420px,62vw);background:var(--plate)}
.v2 .ratio-32{aspect-ratio:3/2}
.v2 .ratio-45{aspect-ratio:4/5}
.v2 .ratio-169{aspect-ratio:16/9}
.v2 .caption{font-family:var(--ff-ui);font-size:.8125rem;color:var(--text-2);margin-top:var(--s-3);text-align:left}
.v2 figure.fig{margin:0}
.v2 button.plate{padding:0;cursor:zoom-in;display:block;width:100%;text-align:left;font:inherit;color:inherit}

/* ---------- §10.4 splits ---------- */
.v2 .split-57{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--gap)}
.v2 .split-57 > :first-child{grid-column:1/6}
.v2 .split-57 > :last-child{grid-column:6/13}
.v2 .split-75{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--gap)}
.v2 .split-75 > :first-child{grid-column:1/8}
.v2 .split-75 > :last-child{grid-column:8/13}
.v2 .split-48{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--gap)}
.v2 .split-48 > :first-child{grid-column:1/5}
.v2 .split-48 > :last-child{grid-column:5/13}
@media (min-width:900px){.v2 .sticky-title{position:sticky;top:calc(var(--bar-h) + 24px);align-self:start}}
@media (max-width:899px){
  .v2 .split-57 > *,.v2 .split-75 > *,.v2 .split-48 > *{grid-column:1/13!important}
}

/* ---------- neighbours ruled list ---------- */
.v2 .ruled{margin-top:var(--s-6);border-top:1px solid var(--line)}
.v2 .ruled li{border-bottom:1px solid var(--line);padding:.75rem 0;display:flex;justify-content:space-between;gap:var(--s-4);align-items:baseline}
.v2 .ruled li em{font-style:normal;font-family:var(--ff-ui);font-size:.75rem;color:var(--plum-text);white-space:nowrap}

/* ---------- Premium Residence Selection Index (Owner Gate 2 §3) ----------
   Only what a visitor needs to choose the next residence to explore. Ruled,
   never boxed; the plan opens full size from the row itself. */
.v2 .selection{margin-top:var(--s-5);border-top:1px solid var(--line-strong)}
/* Quiet label layer (Owner Gate 2 §4) — editorial, not a table header. */
.v2 .selection-head{padding-top:var(--s-3);padding-bottom:var(--s-3);border-bottom:1px solid var(--line)}
.v2 .selection-head .label{color:var(--plum-text)}
.v2 .selection-head:hover{background:none}
.v2 .selection-onrequest{font-family:var(--ff-ui);font-size:.875rem;color:var(--text-2);
  text-align:right;white-space:nowrap;font-variant-numeric:normal}
.v2 .price-qualifier{margin-top:var(--s-5);max-width:60ch}
@media (max-width:899px){
  /* Qualified with .selection-row so it out-specifies the later
     `.v2 .selection-row{display:grid}`, which shares its specificity and won on
     source order — the decorative label layer therefore stayed on phones, where
     the row is re-composed into named areas and the head has no areas to fall
     into. It auto-placed into the implicit grid as a scattered pseudo-header
     whose last label was clipped at the device edge, and its max-content width
     widened the document. It is aria-hidden and carries no content of its own. */
  .v2 .selection-row.selection-head{display:none}
  .v2 .selection-onrequest{grid-area:from}
}
.v2 .selection-row{display:grid;align-items:center;gap:var(--gap);
  grid-template-columns:52px minmax(0,1.5fr) minmax(0,1fr) minmax(0,.7fr) minmax(0,.9fr);
  padding:var(--s-3) 0;border-bottom:1px solid var(--line)}
.v2 .selection-row:hover{background:var(--paper-2)}
.v2 .selection-row.is-here{background:var(--paper-2)}
.v2 .selection .miniplan{width:52px;height:46px;padding:0;border:1px solid var(--line);background:var(--plate);cursor:zoom-in;display:block}
.v2 .selection .miniplan img{width:100%;height:100%;object-fit:contain}
.v2 .selection .miniplan:hover{border-color:var(--gold)}
.v2 .selection-name{font-family:var(--ff-text);font-size:1.125rem;line-height:1.25}
.v2 .selection-name a{border-bottom:1px solid var(--line-strong)}
.v2 .selection-name a:hover{border-bottom-color:var(--gold)}
.v2 .selection-size,.v2 .selection-bed,.v2 .selection-from{font-family:var(--ff-ui);font-size:.9375rem;font-variant-numeric:tabular-nums lining-nums}
.v2 .selection-size small,.v2 .selection-bed small{font-size:.75rem;color:var(--text-2)}
.v2 .selection-from{text-align:right;white-space:nowrap}
.v2 .flag{display:inline-block;margin-left:10px;padding:2px 7px;border:1px solid var(--plum);color:var(--plum-text);
  font-family:var(--ff-ui);font-size:.5625rem;letter-spacing:.14em;text-transform:uppercase;font-weight:600;white-space:nowrap}
.v2 .basis{margin-top:var(--s-4);max-width:70ch}
@media (max-width:899px){
  .v2 .selection-row{grid-template-columns:60px 1fr auto;grid-template-areas:"plan name name" "plan size from" "plan bed bed";
    row-gap:2px;column-gap:var(--s-4);align-items:start;padding:var(--s-5) 0}
  .v2 .selection .miniplan{grid-area:plan;width:60px;height:52px}
  .v2 .selection-name{grid-area:name}
  .v2 .selection-size{grid-area:size}
  .v2 .selection-bed{grid-area:bed;color:var(--text-2)}
  .v2 .selection-from{grid-area:from;text-align:right}
  .v2 .flag{display:block;margin:5px 0 0;width:fit-content}
}
/* The "on request" wording is a sentence, not a figure: it carries no tabular
   alignment obligation and must be allowed to break. Held on one line it asks
   for 161px inside the ~95px final track of the five-column index, which the
   Malay string ("Harga atas permintaan") exceeds by enough to widen the whole
   document between 900 and 1024px. Released below the widest desk field, where
   the track is comfortable again; the figure states (.num) keep their nowrap.
   Placed after §.selection-from so it wins on source order, not specificity. */
@media (max-width:1099px){
  .v2 .selection-onrequest{white-space:normal}
}

/* ---------- room strip · editorial room sequence · filmstrip ---------- */
.v2 .roomstrip{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--gap);margin-top:var(--s-8)}

/* Variable scale and asymmetric placement on a twelve-column field. The
   sequence is composed, not tiled: no two consecutive frames share a width. */
.v2 .rooms{margin-top:var(--s-7);display:grid;grid-template-columns:repeat(12,1fr);
  column-gap:var(--gap);row-gap:var(--chapter-gap)}
.v2 .room{margin:0;min-width:0}
.v2 .room-label{font-family:var(--ff-ui);font-size:.8125rem;color:var(--text-2)}
@media (min-width:900px){
  .v2 .room-full{grid-column:1 / -1}
  .v2 .room-left7{grid-column:1 / 8}
  .v2 .room-right5-drop{grid-column:9 / -1;align-self:end;margin-top:calc(-1 * var(--s-8))}
  .v2 .room-right9{grid-column:4 / -1}
  .v2 .room-left5{grid-column:1 / 6}
  .v2 .room-right6-drop{grid-column:7 / -1;align-self:start;margin-top:var(--s-8)}
  .v2 .rooms{row-gap:clamp(2.5rem,5vw,4.5rem)}
}
@media (max-width:899px){
  .v2 .room{grid-column:1 / -1}
  .v2 .rooms{row-gap:var(--s-8)}
  .v2 .room-left5,.v2 .room-right5-drop{max-width:78%}
  .v2 .room-right5-drop,.v2 .room-right6-drop{margin-left:auto}
  .v2 .roomstrip{display:none}
}
.v2 .filmstrip,.v2 .filmcount{display:none}
@media (max-width:899px){
  .v2 .filmstrip{display:flex;gap:12px;overflow-x:auto;scroll-snap-type:x mandatory;
    margin-inline:calc(50% - 50vw);padding-inline:8vw;scrollbar-width:none}
  .v2 .filmstrip::-webkit-scrollbar{display:none}
  .v2 .filmstrip > *{flex:0 0 84vw;scroll-snap-align:center}
  .v2 .filmcount{display:block;margin-top:var(--s-3)}
}

/* ---------- §12.11 facilities index ---------- */
/* Plate grid — the one shape the migrated Gallery and Facilities pages need
   that no existing V2 movement provides: an even field of sealed plates. */
.v2 .plates{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--gap);margin-top:var(--s-7)}
.v2 .plates > *{margin:0;min-width:0}
.v2 .plates-2{grid-template-columns:repeat(2,1fr)}
@media (max-width:1099px){.v2 .plates{grid-template-columns:repeat(2,1fr)}}
@media (max-width:639px){.v2 .plates,.v2 .plates-2{grid-template-columns:1fr}}
/* Gallery chapter navigation (V2 §20). */
.v2 .chapters{display:flex;flex-wrap:wrap;gap:var(--s-4) var(--s-6);border-bottom:1px solid var(--line);padding-bottom:var(--s-4)}
.v2 .chapters button{background:none;border:0;padding:.25rem 0;cursor:pointer;font-family:var(--ff-ui);
  font-size:.8125rem;letter-spacing:.08em;text-transform:uppercase;color:var(--text-2);border-bottom:1px solid transparent}
.v2 .chapters button[aria-pressed="true"]{color:var(--plum-text);border-bottom-color:var(--plum)}
.v2 .chapters button:hover{color:var(--ink)}
/* Content-aware inventory geometry (Owner directive 2026-09-10). Three equal
   columns put a 27-item group beside two 6-item groups and left a large empty
   field on the desktop. The wide group now takes the full measure and lays its
   zones out side by side; the compact groups sit four-up beneath it. Facts are
   untouched — only the geometry follows the content. */
.v2 .facindex{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--gap)}
.v2 .facindex > .fx-wide{grid-column:1/-1}
.v2 .facindex .fx-zones{display:grid;grid-template-columns:repeat(5,1fr);gap:var(--gap)}
.v2 .facindex .fx-zone-name{margin-top:var(--s-4)}
.v2 .facindex .fx-wide > .fx-zones > div > .fx-zone-name{margin-top:0}
@media (max-width:1199px){
  .v2 .facindex{grid-template-columns:repeat(2,1fr)}
  .v2 .facindex .fx-zones{grid-template-columns:repeat(2,1fr)}
}
.v2 .grounds-plate{margin-top:var(--s-7)}
.v2 .address-pair{margin-top:var(--gap);width:72%;margin-left:auto}
@media (max-width:899px){.v2 .address-pair{width:100%}}
.v2 .facindex > div{border-top:1px solid var(--line-strong);padding-top:var(--s-4)}
.v2 .facindex h3{color:var(--plum-text);margin-bottom:var(--s-4)}
.v2 .facindex ul{margin-top:var(--s-4);border-top:1px solid var(--line)}
.v2 .facindex li{border-bottom:1px solid var(--line);padding:.55rem 0;font-size:.9375rem}
@media (max-width:639px){.v2 .facindex,.v2 .facindex .fx-zones{grid-template-columns:1fr}}

/* ---------- §12.12 document desk ---------- */
.v2 .desk{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap)}
@media (max-width:899px){.v2 .desk{grid-template-columns:1fr}}
.v2 .doc-card{background:var(--paper-2);border:1px solid var(--line-strong);padding:var(--s-6)}
.v2 .doc-card .heading{margin-top:var(--s-5)}
.v2 .commitments{margin-top:var(--s-5);border-top:1px solid var(--line)}
.v2 .commitments li{border-bottom:1px solid var(--line);padding:var(--s-5) 0;max-width:70ch}
.v2 .commitments .rule-coronet{margin-bottom:var(--s-3)}
.v2 .doc-pending{margin-top:var(--s-6);border:1px solid var(--line-strong);background:var(--paper-2);padding:var(--s-6)}

/* ---------- §12.14 question list ---------- */
.v2 .qa{border-top:1px solid var(--line)}
.v2 .qa > div{border-bottom:1px solid var(--line);padding:var(--s-6) 0}
.v2 .qa h3{margin-bottom:var(--s-3)}

/* ---------- §12.15 tour facade ---------- */
.v2 .facade{position:relative;border:1px solid var(--line-strong);background:var(--ink)}
.v2 .facade > picture img{width:100%;aspect-ratio:16/9;object-fit:cover}
.v2 .facade-ui{position:absolute;inset:0;display:grid;place-content:center;justify-items:center;gap:var(--s-4);text-align:center;padding:var(--s-5)}
.v2 .facade-ui .small{color:var(--paper);text-shadow:0 1px 8px rgba(15,26,21,.7)}
.v2 .facade-ui .tertiary{color:var(--gold-light)}
.v2 .facade.is-live > picture,.v2 .facade.is-live .facade-ui{display:none}
.v2 .facade-frame{display:none}
.v2 .facade.is-live .facade-frame{display:block;position:relative}
.v2 .facade-frame iframe{width:100%;height:min(72vh,640px);border:0;display:block}
.v2 .tour-close[hidden],.v2 .tour-expand[hidden]{display:none}
.v2 .tour-close,.v2 .tour-expand{position:absolute;top:8px;z-index:5;background:var(--paper);color:var(--ink);border-color:var(--ink)}
.v2 .tour-close{left:8px}
.v2 .tour-expand{right:8px}
.v2 .tour-close:hover,.v2 .tour-expand:hover{background:var(--gold);border-color:var(--gold)}

/* ---------- expanded tour view (Owner directive 2026-09-10) ----------------
   Desktop and tablet only; below 640px the tour already opens in the frozen
   full-viewport dialog and this state is never entered.

   The enlargement is applied to the wrapper that ALREADY holds the running
   iframe, so the frame node is never moved and never reloaded. That constrains
   the technique: no ancestor of .facade may establish a containing block (no
   transform, filter or will-change), which is why .reveal's transform is
   cleared here explicitly — a translated ancestor would silently trap the
   fixed positioning and the "expanded" view would appear at page scale.

   The element is its own backdrop: it covers the viewport in ink at 98.5% and
   insets the tour area, so the enlarged player reads as a layer over the page
   rather than as a new page. The first-party control sits in the strip ABOVE
   the frame, outside the cross-origin document, and is the only control there —
   Close tour (which unloads) returns with the inline state. */
.v2 .facade.is-expanded{position:fixed;inset:0;z-index:970;margin:0!important;
  border:0;background:rgba(15,26,21,.985);opacity:1;transform:none;
  display:grid;grid-template-rows:auto 1fr;gap:var(--s-3);
  padding:clamp(10px,1.6vw,20px) clamp(10px,2vw,32px) clamp(14px,2.4vw,36px)}
.v2 .facade.is-expanded .tour-close{display:none}
.v2 .facade.is-expanded .tour-expand{position:static;justify-self:end;align-self:center}
.v2 .facade.is-expanded .facade-frame{display:block;min-height:0}
.v2 .facade.is-expanded .facade-frame iframe{height:100%;background:var(--ink)}
html.tour-zoom,html.tour-zoom body.v2{overflow:hidden}
html.tour-zoom .v2 .actionbar,html.tour-zoom .v2 .wa-pill{display:none}
.v2 dialog.tour-dialog{border:0;padding:0;width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;background:var(--ink)}
.v2 dialog.tour-dialog::backdrop{background:rgba(15,26,21,.92)}
.v2 dialog.tour-dialog iframe{width:100%;height:100dvh;border:0}

/* ---------- §12.13 enquiry desk ---------- */
.v2 .desk-enquiry{display:grid;grid-template-columns:repeat(12,1fr);gap:clamp(2rem,4vw,4rem)}
.v2 .desk-enquiry > :first-child{grid-column:1/6}
.v2 .desk-enquiry > :last-child{grid-column:6/13}
/* Below the two-column threshold the desk stacks. The twelve-track field has to
   collapse with it: a 12-track grid keeps its eleven column gaps (11 x 2rem =
   352px of irreducible width) even when every child spans the full row, so the
   desk widened the layout viewport on any device narrower than 375px and pushed
   the form, the right-column copy and the fixed action bar off-screen. One track
   plus a full-row span removes the column gaps; the row gap keeps the rhythm. */
@media (max-width:899px){
  .v2 .desk-enquiry{grid-template-columns:1fr}
  .v2 .desk-enquiry > *{grid-column:1/-1!important}
}
.v2 .wa-line{display:inline-flex;align-items:center;gap:10px;margin-top:var(--s-6);
  font-family:var(--ff-ui);font-size:.9375rem;font-weight:600;color:var(--gold-light)}
.v2 .wa-line svg{width:18px;height:18px}
.v2 .phone-line{display:block;margin-top:var(--s-5);font-family:var(--ff-text);font-size:1.25rem;color:var(--gold-light)}
.v2 form.ce-v2{background:var(--ink-2);padding:clamp(1.5rem,3vw,2.5rem)}
.v2 .f-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-4)}
@media (max-width:639px){.v2 .f-grid{grid-template-columns:1fr}}
.v2 .field{display:block;margin-top:var(--s-4)}
.v2 .field:first-child{margin-top:0}
.v2 .f-grid > .field{margin-top:0}
.v2 .field > label{display:block;font-family:var(--ff-ui);font-size:.6875rem;letter-spacing:.18em;
  text-transform:uppercase;font-weight:600;color:var(--text-2-on-ink);margin-bottom:var(--s-2)}
.v2 .field input,.v2 .field select,.v2 .field textarea{width:100%;min-height:48px;padding:0 var(--s-4);
  background:var(--ink);border:1px solid var(--line-on-ink);border-radius:0;color:var(--text-on-ink);
  font-family:var(--ff-ui);font-size:.9375rem}
.v2 .field textarea{padding:var(--s-3) var(--s-4);min-height:96px;line-height:1.5}
.v2 .field input:focus-visible,.v2 .field select:focus-visible,.v2 .field textarea:focus-visible{outline:2px solid var(--gold-light);outline-offset:1px}
.v2 .field.is-error input,.v2 .field.is-error select{border-color:var(--error)}
.v2 .err{display:block;font-family:var(--ff-ui);font-size:.75rem;color:#E4A0A0;margin-top:6px}
/* Method-aware contact hint. Empty until a method is chosen, and collapsed
   while empty so the field keeps its rhythm; it is referenced by the input's
   aria-describedby, so the guidance is announced with the field. */
.v2 .hint{display:block;font-family:var(--ff-ui);font-size:.75rem;color:var(--text-2-on-ink);margin-top:6px}
.v2 .hint:empty,.v2 form.ce-brochure .err:empty,.v2 form.ce-mm2h .err:empty{display:none}
/* The detail field is followed by a second .f-grid rather than another .field,
   and .f-grid carries no top margin of its own, so the row below it sat tight.
   Scoped to the two desks that carry the Owner contact-identity fieldset; the
   enquiry form has no such sequence. */
.v2 form.ce-brochure .field + .f-grid,.v2 form.ce-mm2h .field + .f-grid{margin-top:var(--s-4)}
.v2 .consent{display:flex;gap:var(--s-3);align-items:flex-start;margin-top:var(--s-5);
  font-family:var(--ff-ui);font-size:.8125rem;color:var(--text-2-on-ink);line-height:1.5}
.v2 .consent input{width:20px;height:20px;flex:none;margin-top:2px;accent-color:var(--gold)}
.v2 .consent a{text-decoration:underline;color:var(--gold-light)}
.v2 form.ce-v2 .btn{width:100%;margin-top:var(--s-6)}
.v2 .assurance{margin-top:var(--s-4);color:var(--text-2-on-ink)}
.v2 .hp{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}
.v2 .form-subject{font-family:var(--ff-text);font-size:1.0625rem;color:var(--text-on-ink);
  padding-bottom:var(--s-5);margin-bottom:var(--s-5);border-bottom:1px solid var(--line-on-ink)}
.v2 .form-subject strong{font-weight:400}
.v2 .form-subject .num{white-space:nowrap}
.v2 details.more{margin-top:var(--s-5);border-top:1px solid var(--line-on-ink);padding-top:var(--s-4)}
.v2 details.more > summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:8px;
  font-family:var(--ff-ui);font-size:.75rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold-light);min-height:32px}
.v2 details.more > summary::-webkit-details-marker{display:none}
.v2 details.more > summary::before{content:"+";font-size:1rem;line-height:1}
.v2 details.more[open] > summary::before{content:"–"}
.v2 details.more .more-body{margin-top:var(--s-4)}
/* explicit, so the optional fields are out of the tab order while collapsed */
.v2 details.more:not([open]) .more-body{display:none}
.v2 .form-msg{border:1px solid var(--line-on-ink);padding:var(--s-5);margin-bottom:var(--s-5)}
.v2 .form-msg[hidden]{display:none}
.v2 .form-done{display:grid;gap:var(--s-5);justify-items:start}

/* ---------- Brochure desk (Owner supersession 2026-09-09) ----------
   The form and its delivery block occupy the same column: exactly one of them
   is visible at a time, so the page does not jump on success. The delivery
   block is rendered by the build and hidden with the `hidden` attribute — it
   is revealed, never constructed, which is why a stylesheet is all that is
   needed to present it. */
.v2 .brochure-desk{display:grid}
.v2 .brochure-desk > *{grid-area:1/1}
.v2 .brochure-desk > [hidden]{display:none}
.v2 form.ce-brochure .purpose,.v2 form.ce-mm2h .purpose{margin-top:var(--s-6);color:var(--text-2-on-ink)}
.v2 form.ce-brochure .purpose a,.v2 form.ce-mm2h .purpose a{color:inherit}
.v2 .brochure-done{background:var(--ink-2);padding:clamp(1.5rem,3vw,2.5rem);display:grid;justify-items:start;gap:var(--s-4);align-content:start}
.v2 .brochure-done .heading{margin-top:var(--s-4)}
.v2 .brochure-done .btn{width:100%}
.v2 .dl-list{display:grid;gap:var(--s-5);width:100%;margin-top:var(--s-3)}
.v2 .dl-list li{display:grid;gap:var(--s-2);justify-items:start}
.v2 .dl-list .small{color:var(--text-2-on-ink)}
.v2 .dl-next{margin-top:var(--s-5);color:var(--text-2-on-ink)}

/* ---------- Price Schedule ----------
   The layouts table was an unstyled <table>: at 390px it laid out 579px inside
   a 366px column under overflow-x:clip, so the Towers, Car parks and From
   columns — the price itself — were cut off and unreachable. This is the same
   information as a Chancery ledger: three columns on desktop, one record per
   layout below 1000px, so the figure is always the last, emphasised line and
   nothing ever scrolls sideways. */
.v2 .schedule{border-top:1px solid var(--line-strong)}
.v2 .sch-head,.v2 .sch-row{display:grid;grid-template-columns:minmax(15ch,1.05fr) minmax(0,1.5fr) minmax(13ch,.62fr);
  gap:var(--gap);align-items:baseline;padding-block:var(--s-5);border-bottom:1px solid var(--line)}
.v2 .sch-head{padding-block:var(--s-3);border-bottom:1px solid var(--line-strong)}
.v2 .sch-row{transition:background .18s var(--ease)}
.v2 .sch-row:hover{background:var(--paper-2)}
.v2 .sch-layout{display:grid;gap:2px}
.v2 .sch-layout a{font-family:var(--ff-text);font-size:1.0625rem}
.v2 .sch-size{color:var(--text-2);white-space:nowrap}
.v2 .sch-config{color:var(--text-2);line-height:1.7}
.v2 .sch-from{text-align:right;display:grid;gap:4px;justify-items:end}
.v2 .sch-price{font-size:1.0625rem;white-space:nowrap}
.v2 .sch-onreq{color:var(--text-2)}
.v2 .sch-ask{font-size:.8125rem;white-space:nowrap}
.v2 .sch-basis{margin-top:var(--s-5)}
/* Chinese runs longer per line and must not be capped in ch units. */
.v2.cjk-page .sch-config{line-height:1.75}
@media (max-width:999px){
  .v2 .sch-head{display:none}
  .v2 .sch-row{grid-template-columns:1fr;gap:var(--s-2);padding-block:var(--s-6)}
  .v2 .sch-from{text-align:left;justify-items:start;grid-auto-flow:column;justify-content:start;
    gap:var(--s-4);align-items:baseline;margin-top:var(--s-2)}
  .v2 .sch-price{font-size:1.25rem}
  .v2 .sch-size{white-space:normal}
}

/* ---------- §12.17 action bar / desktop pill ---------- */
.v2 .actionbar{position:fixed;left:0;right:0;bottom:0;z-index:880;display:grid;grid-template-columns:1fr 1fr 1.2fr;
  align-items:stretch;min-height:54px;padding-bottom:env(safe-area-inset-bottom);
  background:var(--paper);border-top:1px solid var(--line-strong);box-shadow:0 -6px 18px rgba(15,26,21,.07);
  transform:translateY(0);transition:transform .24s var(--ease)}
.v2 .actionbar a{display:flex;align-items:center;justify-content:center;gap:8px;min-height:54px;
  font-family:var(--ff-ui);font-size:.75rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  border-left:1px solid var(--line)}
.v2 .actionbar a:first-child{border-left:0}
.v2 .actionbar a.is-primary{background:var(--gold);color:var(--ink)}
.v2 .actionbar svg{width:17px;height:17px}
.v2 .wa-pill{position:fixed;right:24px;bottom:24px;z-index:880;display:none;align-items:center;gap:10px;
  min-height:48px;padding:0 1.1rem;background:var(--ink);color:var(--text-on-ink);border:1px solid var(--gold);
  font-family:var(--ff-ui);font-size:.75rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
  transition:transform .24s var(--ease),opacity .24s var(--ease)}
.v2 .wa-pill svg{width:18px;height:18px;color:var(--gold-light)}
@media (min-width:900px){
  .v2 .actionbar{display:none}
  .v2 .wa-pill{display:inline-flex}
}
/* hidden until the title band actions leave the viewport; without JS both are present from load */
.v2.js .actionbar,.v2.js .wa-pill{transform:translateY(140%);opacity:0;pointer-events:none}
.v2.js.show-persistent .actionbar,.v2.js.show-pill .wa-pill{transform:none;opacity:1;pointer-events:auto}
.v2.js.hide-persistent .actionbar,.v2.js.hide-persistent .wa-pill{transform:translateY(140%);opacity:0;pointer-events:none}
@media (max-width:899px){body.v2{padding-bottom:54px}}

/* ---------- §12.18 footer ---------- */
.v2 .foot{background:var(--ink);color:var(--text-on-ink);padding-block:var(--desk-pad)}
.v2 .foot-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1.1fr;gap:var(--gap)}
@media (max-width:899px){.v2 .foot-grid{grid-template-columns:1fr 1fr}}
@media (max-width:559px){.v2 .foot-grid{grid-template-columns:1fr}}
.v2 .foot .lockup{color:var(--text-on-ink)}
.v2 .foot .lockup .mark{width:40px;height:40px;color:var(--gold-light)}
.v2 .foot h2{color:var(--gold-light);margin-bottom:var(--s-4)}
.v2 .foot-list li{padding:.35rem 0;font-family:var(--ff-ui);font-size:.875rem}
.v2 .foot-list a:hover{text-decoration:underline}
.v2 .foot-contact a{display:inline-flex;align-items:center;gap:8px;padding:.3rem 0;font-family:var(--ff-ui);font-size:.9375rem;color:var(--gold-light)}
.v2 .foot-contact a{width:100%}
.v2 .foot-contact svg{width:16px;height:16px;flex:none}
.v2 .linkbtn{background:none;border:0;padding:0;cursor:pointer;text-align:left;font-family:var(--ff-ui);font-size:.875rem;text-decoration:underline}
.v2 .foot-legal{margin-top:var(--s-8);padding-top:var(--s-6);border-top:1px solid var(--line-on-ink)}
.v2 .foot-legal p{color:var(--text-2-on-ink);font-family:var(--ff-ui);font-size:.8125rem;max-width:88ch}
.v2 .foot-legal p + p{margin-top:.75rem}
.v2 .foot-bottom{margin-top:var(--s-7);padding-top:var(--s-5);border-top:1px solid var(--line-on-ink);
  display:flex;flex-wrap:wrap;gap:var(--s-5);justify-content:space-between;
  font-family:var(--ff-ui);font-size:.8125rem;color:var(--text-2-on-ink)}
.v2 .foot-bottom nav{display:flex;gap:var(--s-5)}

/* ---------- §13 motion ---------- */
.v2 .reveal{opacity:0;transform:translateY(12px);transition:opacity .6s var(--ease),transform .6s var(--ease)}
.v2 .reveal.is-in{opacity:1;transform:none}
.v2:not(.js) .reveal{opacity:1;transform:none}
/* The crop zoom is a STATIC transform on the image, and the settle animation
   runs on the <picture> wrapper. Carrying the zoom inside the keyframes made the
   rendered crop depend on the animation's fill state, which is fragile: any
   agent that does not hold the filled value paints an untransformed — or
   unpainted — layer. Separating them keeps the accepted settle motion exactly as
   it was (scale(1.015) -> none) and makes the crop unconditional. */
.v2 .chapter picture{animation:v2settle .7s var(--ease) both}
.v2 .chapter img{transform:scale(var(--zoom,1))}
@media (max-width:639px){.v2 .chapter img{--zoom:var(--zoom-m,1)}}
@keyframes v2settle{from{transform:scale(1.015)}to{transform:none}}
@media (prefers-reduced-motion:reduce){
  .v2 *,.v2 *::before,.v2 *::after{animation-duration:.01ms!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
  .v2 .reveal{opacity:1;transform:none}
  .v2 .filmstrip{scroll-snap-type:none}
}

/* ---------- §12.24 Residences Index ----------------------------------------
   Added for the P2 Residences Index. Uses the existing band, ledger, plate and
   selection vocabulary; introduces only what the route genuinely needs. */
.v2 .band-paper{background:var(--plate)}
.v2 .display-s{font-family:var(--ff-display);font-size:clamp(1.375rem,2vw,1.75rem);line-height:1.15;font-weight:400}

/* Layout by layout — plan beside the facts, stacked on a phone. */
.v2 .layout-stack{margin-top:var(--s-8);display:grid;gap:var(--s-8)}
.v2 .layout-card{display:grid;gap:var(--s-6);align-items:start;
  padding-top:var(--s-8);border-top:1px solid var(--line)}
.v2 .layout-card:first-child{padding-top:0;border-top:0}
.v2 .layout-actions{margin-top:var(--s-7);display:flex;flex-wrap:wrap;gap:var(--s-4)}
@media (min-width:900px){
  .v2 .layout-stack{gap:var(--s-8)}
  .v2 .layout-card{grid-template-columns:minmax(0,46%) minmax(0,1fr);gap:var(--s-8)}
}

/* The availability action replaces the price column on the overview only. */
.v2 .selection-act{display:flex;justify-content:flex-end;align-items:center}
.v2 .selection-act .tertiary{white-space:nowrap}
/* On phones the action shares the row's third, auto-sized track. Held on one
   line it sizes that track to the full sentence ("Semak ketersediaan semasa",
   "Check current availability") and widens the document, so the phone state
   releases it along with the row's own re-composition. */
@media (max-width:899px){
  .v2 .selection-act{justify-content:flex-start}
  .v2 .selection-act .tertiary{white-space:normal}
}

/* Four-plan premium viewer. One primary viewport; plans are never cropped. */
.v2 .planviewer{display:grid;gap:var(--s-5)}
/* The approved plan masters are 1,280-1,661 px. A full-bleed 1,318 px stage would
   ask for 2,636 device px at DPR 2 and upscale every plan. Cap the reading size so
   the on-page plate stays close to its source, and delegate true inspection to the
   lightbox, which loads the original. */
.v2 .pv-stage{background:var(--paper);border:1px solid var(--line);max-width:1000px;margin-inline:auto}
.v2 .pv-slide{margin:0}
.v2 .pv-plate{position:relative;display:block;width:100%;padding:0;border:0;background:var(--paper);cursor:zoom-in}
.v2 .pv-plate img{display:block;width:100%;height:auto;object-fit:contain}
.v2 .pv-cap{display:block;padding:var(--s-4) var(--s-5);font-family:var(--ff-ui);font-size:.8125rem;
  letter-spacing:.04em;color:var(--text);border-top:1px solid var(--line)}
.v2 .pv-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--s-4)}
.v2 .pv-count{margin:0;font-variant-numeric:tabular-nums;white-space:nowrap}
.v2 .pv-rail{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s-4)}
.v2 .pv-thumb{display:block;width:100%;padding:0;border:1px solid var(--line);background:var(--paper);cursor:pointer;
  transition:border-color .2s var(--ease)}
.v2 .pv-thumb img{display:block;width:100%;height:auto}
.v2 .pv-thumb-cap{display:block;padding:var(--s-3);font-family:var(--ff-ui);font-size:.6875rem;
  letter-spacing:.04em;color:var(--text);text-align:left}
.v2 .pv-thumb.is-active{border-color:var(--ink)}
.v2 .pv-thumb:hover{border-color:var(--gold)}
@media (max-width:639px){
  .v2 .pv-rail{grid-template-columns:repeat(2,minmax(0,1fr))}
  .v2 .pv-thumb-cap{font-size:.625rem}
  /* Previous · counter · Next does not fit one phone row once the labels are
     long (BM "Sebelumnya" / "Seterusnya"), and an unwrapped flex row has no way
     to give: it widened the document and cut the Next button, the plan seal and
     the thumbnail rail at the right edge. Wrapping is the natural adaptation —
     document order is kept, every control keeps its full label, and the row
     centres so a wrapped line reads as composed rather than stranded. */
  .v2 .pv-bar{flex-wrap:wrap;justify-content:center;row-gap:var(--s-3)}
}

/* ---------- lightbox · full-screen plan and image enlargement ---------- */
.v2 .lb{position:fixed;inset:0;z-index:990;background:var(--paper);display:none;
  grid-template-rows:auto 1fr;padding:clamp(.75rem,2vw,1.5rem)}
.v2 .lb[open],.v2 .lb.is-open{display:grid}
.v2 .lb-close{justify-self:end;background:var(--paper);position:relative;z-index:2}
.v2 .lb-figure{margin:0;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s-4);overflow:hidden}
.v2 .lb img{max-width:100%;max-height:calc(100dvh - 10rem);width:auto;height:auto;object-fit:contain;
  background:var(--plate);border:1px solid var(--line-strong);flex:0 1 auto;min-height:0}
.v2 .lb figcaption{text-align:center;display:flex;flex-direction:column;align-items:center;gap:var(--s-2);padding-bottom:var(--s-3)}
.v2 .lb-class{color:var(--plum-text)}
.v2 .lb-class[hidden]{display:none}
.v2 .lb-cap{font-family:var(--ff-ui);font-size:.8125rem;color:var(--text-2)}
@media (max-width:639px){
  .v2 .lb{padding:.5rem}
  .v2 .lb-close{width:100%;justify-self:stretch}
}

/* ---------- §16.2 print ---------- */
@media print{
  .v2 .bar,.v2 .sheet,.v2 .actionbar,.v2 .wa-pill,.v2 .facade,.v2 .film,.v2 form.ce-v2,
  .v2 .foot-list,.v2 .foot-contact,.v2 .foot-bottom nav,.v2 .lb,.v2 .skip-link{display:none!important}
  .v2,body.v2{background:#fff!important;color:#000!important;font-size:11pt}
  .v2 .band-ink,.v2 .foot{background:#fff!important;color:#000!important}
  .v2 .on-ink{--text:#000;--text-2:#333;--line:rgba(0,0,0,.2);--line-strong:rgba(0,0,0,.5)}
  .v2 .chapter{height:auto;max-height:80mm;page-break-inside:avoid}
  .v2 .plate-detail img{min-height:0;width:100%}
  .v2 .seal{position:static;border:0;padding:0;background:none!important;color:#000!important;
    backdrop-filter:none!important;-webkit-backdrop-filter:none!important;margin-top:4pt}
  .v2 .seal svg{display:none}
  .v2 .layouts,.v2 .ledger,.v2 .rooms{page-break-inside:auto}
  .v2 .room{page-break-inside:avoid}
  .v2 .print-only{display:block!important}
}
.v2 .print-only{display:none}

/* ---------- consent dialog ---------- */
.v2-dialog{border:1px solid var(--line-strong);border-radius:0;padding:0;background:var(--paper);color:var(--text);
  max-width:min(560px,92vw);box-shadow:0 24px 64px rgba(15,26,21,.22)}
.v2-dialog::backdrop{background:rgba(15,26,21,.55)}
.v2-dialog .dialog-card{padding:clamp(1.5rem,4vw,2.5rem)}
.v2-dialog .dialog-actions{display:flex;flex-wrap:wrap;gap:var(--s-4);margin-top:var(--s-6)}

/* ---------- key-plan ring overlay (V2 D2 · prerequisite P14) ---------- */
.v2 .plate-inner{position:relative}
.v2 .keyplan-ring{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
/* CSS `columns` is right for ONE long list flowing into two tracks, which is
   what unit-page-v2 uses it for. It is wrong for TWO blocks that each own a
   heading: the flow put the second heading at the foot of the left column while
   its five items floated in the right, in all three locales. Blocks get a grid,
   so both headings sit on one baseline. */
.v2 .pair-2col{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap) var(--gap);align-items:start}
@media (max-width:899px){.v2 .pair-2col{grid-template-columns:1fr;gap:var(--s-8)}}
.v2 .includes-2col{columns:2;column-gap:var(--gap)}
.v2 .includes-2col li{break-inside:avoid}
@media (max-width:639px){.v2 .includes-2col{columns:1}}
.v2 .nowrap{white-space:nowrap}
/* Title grouping for a Chinese heading. The words never change; this only says
   WHERE the line may break. Below 360px the longest group is wider than the
   measure, so the grouping is released and the heading wraps normally rather
   than overflowing — responsive wrapping is the correct behaviour there. */
@media (min-width:360px){.v2 h1 .h1-group{white-space:nowrap}}
/* Route-local ZH section-heading composition. Same device as .h1-group, one
   level down: each semantic half of the heading holds together, and the joint
   between the two spans is the only break the line can take. Released below
   360px so the narrowest column falls back to normal CJK wrapping. */
@media (min-width:360px){.v2 .display-m .h2-group{white-space:nowrap}}
/* A bound Chinese term held on one line. Two or three characters only, so the
   group is always narrower than the measure — it can never cause overflow. */
.v2 .kw{white-space:nowrap}
.v2 .sizes{white-space:normal}
.v2 .sizes .num{white-space:nowrap}

/* ---------- human check (Owner directive 2026-09-10) ----------------------
   The question is rendered into the LABEL, so it needs no box of its own — it
   only needs to read as the value it is. Tabular figures keep "3 + 4 = ?" from
   shifting as the operands change on each page load. */
/* The gap belongs to CSS, not to a literal space: a Latin colon needs one,
   and the ZH fullwidth colon already carries its own. */
.v2 .hv-q{font-variant-numeric:tabular-nums lining-nums;white-space:nowrap;margin-inline-start:.45em}
.v2.cjk-page .hv-q{margin-inline-start:0}
.v2 .hv-input{max-width:9rem}
.v2 .form-nojs{margin-top:var(--s-4);color:var(--text-2-on-ink)}
.v2 .form-nojs a{color:inherit;text-decoration:underline}
