/* =============================================================================
   Estatepreneur — Keegan Soh
   -----------------------------------------------------------------------------
   DIRECTION. Site type: personal brand + marketing. Industry: property, so aspiration, space and
   light; but the brand name points at entrepreneurship, so this is a founder's site, not an
   agency listing portal.

   ONE AUDIENCE: property owners and buyers. The layout carries shapes that look like an
   audience split — two hero CTAs, an asymmetric bento, a four-column ledger — and they are not.
   They exist so the page never reads as a row of equal boxes. Both CTAs and every tile lead
   into the same journey: the work, the record, and the man.

   PALETTE — NOW READ OFF THE CLIENT'S OWN LOGO (house rule), 2026-08-30.
   The logo arrived: a wide geometric wordmark, pure #FFFFFF on #020304, no icon and NO COLOUR
   AT ALL. So the house rule resolves to black and white, and the ink ramp below is now the
   logo's own near-black rather than the charcoal I picked. Two honest consequences:
     - The champagne brass is OURS, not his. The brand as delivered has no accent. It is kept
       because a single restrained metallic is what the category does (Christie's), but it is a
       PROPOSAL and it is the one thing to drop if he wants pure monochrome.
     - The stone paper is also ours; a wordmark has no paper colour to give.

   PALETTE — READ OFF THE CATEGORY (哥 2026-08-28: "colour is too AI").
   The first build used clay on bone. Warm orange-brown is the tell of a generated
   palette and appears nowhere in this industry. Measured from the real brands'
   own stylesheets:

     Christie's Real Estate  #c9ac77 champagne x67, over cool greys #7e868c
                             #484f54 #384249
     Knight Frank Malaysia   theme-color #181a1d, stone neutrals #f0f0e4
                             #e2e2cb #cbc9bd
     Savills Malaysia        #252839 deep ink x82

   Every credible one is the same shape: deep desaturated charcoal, stone/greige
   paper, and ONE restrained metallic. So: cool charcoal ink, stone paper, and
   champagne brass used sparingly — seals, rules, the ledger super, one CTA.
   Still not the house institutional navy/gold: this is charcoal not navy, stone
   not white, and a grotesk not an authority serif.

   NOTE ON DIRECTION OF LIGHTNESS: clay was a DARK accent, brass is a LIGHT one.
   Anything that was "accent background + white text" had to be inverted to dark
   text on brass, or it lands at 2:1. The contrast audit in tools/verify.py checks
   light surfaces as well as dark for exactly this reason.

   THE ONE DISTINCTIVE CHOICE: the ledger. Oversized numerals and hairline rules
   are the structural motif of the whole site, because Keegan's argument is his
   record — years, states, transactions, transacted value. The numbers are the
   layout, not decoration inside it.

   PROVISIONAL: there is still no Estatepreneur logo, and the house rule is
   palette-from-logo. When it arrives the --brass ramp is the only thing that has
   to change. That is why every accent goes through one variable.
   ========================================================================== */

:root{
  /* ACCENT — champagne brass, the one metallic. Christie's #c9ac77 is the anchor.
     brass-900 is not brass at all: it is the deepest ink, used for the hero gradient
     foot, and it keeps that name so every gradient stop stays in one ramp. */
  --brass-900:#000000;
  --brass-800:#6E5A33;
  --brass-700:#A0854F;
  --brass:#C9AC77;
  --brass-400:#DBC49B;
  --brass-200:#E3D3B4;

  /* INK — cool desaturated charcoal, the category's anchor (Knight Frank #181a1d,
     Savills #252839). Never the old warm near-black. */
  --ink:#020304;        /* the logo's exact ground */
  --ink-2:#111417;
  --ink-3:#343A40;
  --slate:#595F66;   /* 5.76:1 on --bone, 5.06:1 on --sand (was 4.40 / 3.87) */

  /* PAPER — stone / greige, not cream and not white (Knight Frank #f0f0e4 family) */
  --bone:#F3F2ED;
  --sand:#E6E4DA;
  --line:#D2CFC3;
  --line-2:#E6E4DA;
  --white:#FFFFFF;

  --ok:#2F6B4F;
  --warn:#7A5F26;    /* 4.91:1 on the composited confirm pill (was 3.98) */

  /* SYSTEM */
  --wrap:1200px;
  --r:4px;            /* sharp, not rounded — a ledger has corners */
  --r-lg:6px;
  --shadow:0 1px 2px rgba(21,23,26,.05), 0 18px 44px -22px rgba(21,23,26,.28);
  --ease:cubic-bezier(.2,.7,.2,1);

  /* THE LOGO OUTRANKS THE CATEGORY RULE. motion-and-menus.md says property/luxury reads as a
     high-contrast serif, and that was right until the wordmark arrived - it is wide, geometric
     and technical, with sheared terminals, and a classical serif argues with it on every page.
     Archivo echoes its construction without imitating the mark itself. The blockiness 哥 called
     out came from 800-weight tight tracking, square cards and no motion, all of which are gone:
     this runs at 400/600 with wide tracking, over the bento, marquee and parallax. */
  --f-display:"Archivo", "Helvetica Neue", system-ui, sans-serif;
  --f-body:"Plus Jakarta Sans", system-ui, -apple-system, sans-serif;
  --f-zh:"Noto Sans SC", var(--f-body);

  /* TYPE SCALE — the only sizes on this site. Floor is 14px. */
  --fs-hero:clamp(44px,7.2vw,104px);
  --fs-h1:clamp(34px,5vw,62px);
  --fs-h2:clamp(28px,3.6vw,44px);
  --fs-h3:21px;
  --fs-lead:19px;
  --fs-base:16px;
  --fs-sm:14px;
  --fs-xs:14px;
  --fs-num:clamp(56px,9.5vw,140px);   /* the ledger numerals */
}

*,*::before,*::after{box-sizing:border-box}
/* overflow-x:clip, NOT hidden. The mobile drawer is parked at translateX(100%), and a fixed
   element sitting past the right edge widens the layout viewport on a real phone: innerWidth
   measured 647 on a 390px device, so the browser shrank the whole page to fit and every page
   rendered zoomed out. `clip` clips it without creating a scroll container, which `hidden` would,
   and a scroll container would break the sticky header. */
html{-webkit-text-size-adjust:100%; scroll-behavior:smooth; overscroll-behavior-x:none; overflow-x:clip}
body{
  margin:0; background:var(--bone); color:var(--ink);
  font:400 var(--fs-base)/1.65 var(--f-body);
  overscroll-behavior-x:none; overflow-x:clip;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%; display:block}
a{color:inherit}
h1,h2,h3,h4{font-family:var(--f-display); font-weight:600; letter-spacing:-.026em; line-height:1.03; margin:0}
p{margin:0}
button{font:inherit}

:focus-visible{outline:2.5px solid var(--brass); outline-offset:3px}
.skip{position:absolute; left:-9999px; top:0; background:var(--ink); color:var(--bone); padding:12px 18px; z-index:200}
.skip:focus{left:12px; top:12px}

.wrap{width:min(100% - 40px, var(--wrap)); margin-inline:auto}

/* ---------- Framework ribbon ------------------------------------------- */
.ribbon{background:var(--ink); color:var(--sand); font-size:var(--fs-sm)}
.ribbon__in{display:flex; gap:14px; align-items:baseline; flex-wrap:wrap; padding:9px 0}
.ribbon strong{color:var(--brass-200); font-family:var(--f-display); letter-spacing:.02em}
.ribbon span{opacity:.78; flex:1 1 260px; min-width:0}
.ribbon a{color:var(--brass-200); text-decoration:none; white-space:nowrap; border-bottom:1px solid transparent}
.ribbon a:hover{border-bottom-color:currentColor}

/* ---------- Nav: floating glass pill (house rule) ----------------------
   Translucent + blur, rounded, inset from the edges, overlaying the hero. KEPT on mobile, just
   tighter. Note the drawer is anchored ABSOLUTE to the pill, not fixed to the viewport: the pill
   has backdrop-filter, which makes it the containing block for fixed descendants, so a fixed
   drawer renders offset. That same property is why the old off-canvas drawer widened the layout
   viewport on a phone. Anchoring to the pill fixes both. */
.nav{position:sticky; top:0; z-index:100; padding:14px 0 0; background:transparent}
.nav__pill{
  position:relative;
  width:min(100% - 32px, calc(var(--wrap) + 40px)); margin-inline:auto;
  display:flex; align-items:center; gap:20px; padding:10px 14px 10px 16px;
  background:rgba(243,242,237,.72); backdrop-filter:saturate(1.5) blur(16px);
  border:1px solid rgba(21,23,26,.07); border-radius:999px;
  box-shadow:0 1px 2px rgba(21,23,26,.04), 0 22px 48px -30px rgba(21,23,26,.5);
  transition:background .3s var(--ease), box-shadow .3s var(--ease), padding .3s var(--ease);
}
.nav.is-stuck .nav__pill{
  background:rgba(243,242,237,.92); padding-block:7px;
  box-shadow:0 1px 2px rgba(21,23,26,.06), 0 18px 40px -26px rgba(21,23,26,.6);
}

.brand{display:flex; align-items:center; gap:12px; text-decoration:none; flex:0 0 auto}
/* The wordmark is 13:1, so it is sized by WIDTH and the height follows. Namespaced so a generic
   component rule can never paint over it. */
.brand__mark{display:block; color:var(--ink); line-height:0}
.brand__mark svg{width:clamp(132px,15vw,178px); height:auto; display:block}
.brand:hover .brand__mark{opacity:.75; transition:opacity .3s var(--ease)}
.brand__sub{font-size:var(--fs-sm); color:var(--slate); letter-spacing:.1em; text-transform:uppercase;
            padding-left:12px; border-left:1px solid var(--line); white-space:nowrap}
.brand__mark--foot{color:var(--bone)}
.brand__mark--foot svg{width:210px}

.nav__links{display:flex; align-items:center; gap:20px; margin-left:auto}
/* Underline that grows from the centre - the house hover pointer. */
.nav__links > a{position:relative; text-decoration:none; font-size:var(--fs-sm); font-weight:500;
                color:var(--ink-3); padding:6px 0; transition:color .25s var(--ease)}
.nav__links > a > span{position:relative; display:inline-block}
.nav__links > a > span::after{
  content:""; position:absolute; left:0; right:0; bottom:-5px; height:2px; background:var(--brass);
  transform:scaleX(0); transform-origin:50% 50%; transition:transform .3s var(--ease);
}
.nav__links > a:hover{color:var(--ink)}
.nav__links > a:hover > span::after,
.nav__links > a[aria-current="page"] > span::after{transform:scaleX(1)}
.nav__links > a[aria-current="page"]{color:var(--ink)}
.nav__cta{background:var(--ink); color:var(--bone)!important; padding:11px 20px!important;
          border-radius:999px; text-decoration:none; font-size:var(--fs-sm); font-weight:600;
          transition:transform .22s var(--ease), background .25s var(--ease)}
.nav__cta:hover{background:var(--ink-2); transform:translateY(-1px)}

.nav__tools{display:flex; align-items:center; gap:10px; margin-left:auto}
.nav__links ~ .nav__tools{margin-left:0}

/* ---- Language: globe + current language + chevron, opening a list (house rule) ---- */
.lang--menu{position:relative}
.lang__btn{display:flex; align-items:center; gap:7px; background:transparent; cursor:pointer;
           border:1px solid rgba(21,23,26,.12); border-radius:999px; padding:7px 11px;
           font-size:var(--fs-sm); color:var(--ink-3); font-family:var(--f-body);
           transition:border-color .25s var(--ease), background .25s var(--ease)}
.lang__btn:hover{border-color:rgba(21,23,26,.3); background:rgba(255,255,255,.5)}
.lang__globe{width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:1.3}
.lang__chev{width:10px; height:10px; fill:none; stroke:currentColor; stroke-width:1.6;
            transition:transform .28s var(--ease)}
.lang__btn[aria-expanded="true"] .lang__chev{transform:rotate(180deg)}
.lang__now{font-weight:600; letter-spacing:.02em}
.lang__list{
  position:absolute; top:calc(100% + 10px); right:0; min-width:172px; z-index:5;
  background:var(--white); border:1px solid var(--line); border-radius:var(--r-lg);
  box-shadow:0 24px 50px -22px rgba(21,23,26,.4); padding:6px;
  opacity:0; visibility:hidden; transform:translateY(-6px); transition:.24s var(--ease);
}
.lang--menu.is-open .lang__list{opacity:1; visibility:visible; transform:none}
.lang__opt{display:flex; align-items:center; justify-content:space-between; gap:12px; width:100%;
           background:transparent; border:0; cursor:pointer; text-align:left; padding:10px 12px;
           border-radius:var(--r); font-size:var(--fs-sm); color:var(--ink-3);
           font-family:var(--f-body)}
.lang__opt:hover{background:var(--sand); color:var(--ink)}
.lang__tick{width:15px; height:15px; fill:none; stroke:var(--brass-800); stroke-width:2;
            opacity:0; transition:opacity .2s var(--ease)}
.lang__opt[aria-checked="true"]{color:var(--ink); font-weight:600}
.lang__opt[aria-checked="true"] .lang__tick{opacity:1}
.lang--drawer{display:none}

.hamb{display:none; width:40px; height:36px; background:transparent;
      border:1px solid rgba(21,23,26,.12); border-radius:999px; cursor:pointer; padding:9px 10px}
.hamb span{display:block; height:1.6px; background:var(--ink); margin:3.4px 0;
           border-radius:2px; transition:.3s var(--ease)}
.hamb[aria-expanded="true"] span:nth-child(1){transform:translateY(5px) rotate(45deg)}
.hamb[aria-expanded="true"] span:nth-child(2){opacity:0}
.hamb[aria-expanded="true"] span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}

/* ---------- Sections ---------------------------------------------------- */
.section{padding:clamp(56px,7.5vw,104px) 0}
.section--tight{padding:clamp(40px,5vw,68px) 0}
.section--sand{background:var(--sand)}
.section--ink{background:var(--ink); color:var(--sand)}
.section--ink h2,.section--ink h3{color:var(--bone)}

.eyebrow{
  display:inline-flex; align-items:center; gap:9px; margin-bottom:16px;
  font-size:var(--fs-xs); font-weight:600; letter-spacing:.15em; text-transform:uppercase;
  color:var(--brass-800);
}
.eyebrow::before{content:""; width:26px; height:2px; background:var(--brass)}
.section--ink .eyebrow{color:var(--brass-200)}

.lead{font-size:var(--fs-lead); color:var(--slate); line-height:1.62; max-width:62ch}
.section--ink .lead{color:#BCC1C6}

.head{max-width:70ch; margin-bottom:clamp(28px,3.6vw,48px)}
.head h2{font-size:var(--fs-h2); margin-bottom:14px}

/* ---------- Buttons ----------------------------------------------------- */
.btn{display:inline-flex; align-items:center; gap:10px; text-decoration:none; cursor:pointer;
     font-family:var(--f-body); font-weight:600; font-size:var(--fs-sm);
     padding:14px 22px; border-radius:var(--r); border:1px solid transparent;
     transition:transform .18s var(--ease), background .2s var(--ease)}
.btn:hover{transform:translateY(-1px)}
.btn-primary{background:var(--brass); color:var(--ink)}
.btn-primary:hover{background:var(--brass-400)}
.btn-ink{background:var(--ink); color:var(--bone)}
.btn-ink:hover{background:var(--ink-2)}
.btn-ghost{border-color:var(--line); color:var(--ink); background:transparent}
.btn-ghost:hover{border-color:var(--ink); background:var(--white)}
.section--ink .btn-ghost{border-color:#3D444B; color:var(--sand)}
/* A ghost button on a dark backdrop inherits --ink and lands at 1.13:1 against the hero scrim,
   which is invisible rather than subtle. The .section--ink override does not reach the hero, so
   the hero needs its own. Measured, not eyeballed. */
.hero .btn-ghost{border-color:rgba(243,242,237,.45); color:var(--bone)}
.hero .btn-ghost:hover{border-color:var(--bone); background:rgba(243,242,237,.12); color:var(--bone)}
.band .btn-ghost{border-color:rgba(243,242,237,.45); color:var(--bone)}
.btns{display:flex; gap:12px; flex-wrap:wrap; margin-top:26px}

/* ---------- Hero -------------------------------------------------------- */
.hero{position:relative; color:var(--bone); overflow:hidden; isolation:isolate}
.hero__bg{position:absolute; inset:0; z-index:-2}
.hero__bg img{width:100%; height:100%; object-fit:cover; filter:saturate(.78) contrast(1.06)}
.hero::after{
  content:""; position:absolute; inset:0; z-index:-1;
  background:linear-gradient(180deg, rgba(21,23,26,.86) 0%, rgba(21,23,26,.60) 42%, rgba(21,23,26,.90) 100%);
}
.hero__in{padding:clamp(72px,11vw,152px) 0 clamp(56px,8vw,104px)}
.hero h1{font-size:var(--fs-hero); max-width:17ch}
.hero--page h1{font-size:var(--fs-h1); max-width:22ch}
.hero__sub{margin-top:22px; font-size:var(--fs-lead); color:#CDD1D5; max-width:56ch; line-height:1.6}
.hero .eyebrow{color:var(--brass-200)}

/* ---------- The ledger: the site's structural motif --------------------- */
.ledger{border-top:1px solid var(--line); margin-top:clamp(40px,5vw,64px)}
.hero .ledger{border-top-color:rgba(243,242,237,.26)}
.ledger__row{display:grid; grid-template-columns:repeat(4,1fr); gap:0}
.ledger__cell{padding:26px 22px 24px 0; border-right:1px solid var(--line)}
.hero .ledger__cell{border-right-color:rgba(243,242,237,.20)}
.ledger__cell:last-child{border-right:0}
.ledger__n{
  font-family:var(--f-display); font-weight:600; font-size:var(--fs-num);
  line-height:.86; letter-spacing:-.05em; color:var(--ink); display:block;
  /* "RM 0" broke onto two lines and wrecked the cell. It wraps rather than overflows, so the
     per-element overflow probe passed it and a person spotted it in one glance. */
  white-space:nowrap;
}
/* A value carrying a currency prefix is two or three times wider than a bare numeral, so it
   cannot use the same step. */
.ledger__n--wide{font-size:clamp(34px,5.4vw,80px); letter-spacing:-.035em}
.hero .ledger__n{color:var(--bone)}
.ledger__n em{font-style:normal; color:var(--brass-400); font-size:.5em; vertical-align:super}
.ledger__k{display:block; margin-top:12px; font-size:var(--fs-sm); color:var(--slate); letter-spacing:.02em}
.hero .ledger__k{color:#B3B8BE}
.ledger__mark{display:block; margin-top:14px}
.ledger__mark .lm{height:32px}
.ledger__cell.is-empty .ledger__n{color:var(--line); position:relative}
.hero .ledger__cell.is-empty .ledger__n{color:rgba(243,242,237,.28)}

/* ---------- Editorial split -------------------------------------------- */
.split{display:grid; grid-template-columns:1.05fr .95fr; gap:clamp(28px,4.4vw,68px); align-items:center}
.split--rev .split__media{order:2}
.split__media img{width:100%; height:100%; object-fit:cover; border-radius:var(--r-lg); filter:saturate(.82) contrast(1.04)}
.split__body h2{font-size:var(--fs-h2); margin-bottom:16px}
.split__body p + p{margin-top:14px}

/* ---------- Full-bleed band -------------------------------------------- */
.band{position:relative; color:var(--bone); isolation:isolate; overflow:hidden}
.band__bg{position:absolute; inset:0; z-index:-2}
.band__bg img{width:100%; height:100%; object-fit:cover; filter:saturate(.7) contrast(1.05) brightness(.62)}
.band::after{content:""; position:absolute; inset:0; z-index:-1;
             background:linear-gradient(90deg, rgba(21,23,26,.86), rgba(30,34,38,.62))}
.band__in{padding:clamp(64px,9vw,124px) 0}
.band__quote{font-family:var(--f-display); font-weight:400; font-size:var(--fs-h2);
             line-height:1.22; max-width:24ch; letter-spacing:-.02em}
.band__by{margin-top:20px; font-size:var(--fs-sm); color:var(--brass-200); letter-spacing:.06em; text-transform:uppercase}

/* ---------- Cards ------------------------------------------------------- */
.grid{display:grid; gap:20px}
.g-2{grid-template-columns:repeat(2,1fr)}
.g-3{grid-template-columns:repeat(3,1fr)}
.card{background:var(--white); border:1px solid var(--line); border-radius:var(--r-lg); padding:26px}
.card h3{font-size:var(--fs-h3); margin-bottom:10px}
.card p{color:var(--slate); font-size:var(--fs-base)}
.card__n{font-family:var(--f-display); font-weight:400; font-size:var(--fs-h2);
         color:var(--brass-200); line-height:1; display:block; margin-bottom:14px}
.section--ink .card{background:var(--ink-2); border-color:#3D444B}
.section--ink .card p{color:#AEB3B9}

/* ---------- Stepper ----------------------------------------------------- */
.steps{display:grid; grid-template-columns:repeat(4,1fr); gap:0; border-top:2px solid var(--ink)}
.step{padding:24px 20px 0 0; border-right:1px solid var(--line)}
.step:last-child{border-right:0}
.step__n{font-family:var(--f-display); font-weight:400; font-size:var(--fs-h2); color:var(--brass-800); line-height:1}
.step h3{font-size:var(--fs-h3); margin:12px 0 8px}
.step p{color:var(--slate); font-size:var(--fs-sm)}

/* ---------- Content slot (the point of this build) ---------------------- */
.slot{
  position:relative; border:2px dashed var(--brass-200); border-radius:var(--r-lg);
  background:
    repeating-linear-gradient(135deg, rgba(201,172,119,.07) 0 12px, transparent 12px 24px),
    var(--white);
  padding:26px 24px; display:flex; flex-direction:column; justify-content:center;
}
.slot--photo{min-height:280px}
.slot--portrait{min-height:440px}
.slot--wide{min-height:220px}
.slot--text{min-height:auto; padding:22px 24px}
.slot__tag{
  align-self:flex-start; font-size:var(--fs-xs); font-weight:700; letter-spacing:.12em;
  text-transform:uppercase; color:var(--brass-800); background:rgba(201,172,119,.16);
  padding:5px 10px; border-radius:var(--r); margin-bottom:14px;
}
.slot__label{font-family:var(--f-display); font-weight:400; font-size:var(--fs-h3); letter-spacing:-.015em}
.slot__spec{margin-top:9px; color:var(--slate); font-size:var(--fs-sm); max-width:46ch; line-height:1.55}
.section--ink .slot{background:rgba(243,242,237,.05); border-color:#4A5158}
.section--ink .slot__label{color:var(--bone)}
.section--ink .slot__spec{color:#A7ACB2}
.section--ink .slot__tag{background:rgba(227,211,180,.14); color:var(--brass-200)}

/* ---------- Brief page -------------------------------------------------- */
.brief__group{border-top:2px solid var(--ink); padding-top:22px; margin-top:44px}
.brief__group h2{font-size:var(--fs-h2); margin-bottom:18px}
.brief__item{display:grid; grid-template-columns:minmax(0,1fr) auto; gap:16px 24px;
             padding:18px 0; border-bottom:1px solid var(--line); align-items:start}
.brief__item h3{font-size:var(--fs-h3); margin-bottom:6px}
.brief__item p{color:var(--slate); font-size:var(--fs-sm); max-width:70ch}
.pill{font-size:var(--fs-xs); font-weight:700; letter-spacing:.08em; text-transform:uppercase;
      padding:6px 11px; border-radius:var(--r); white-space:nowrap}
.pill--blocking{background:rgba(201,172,119,.20); color:var(--brass-800)}
.pill--needed{background:rgba(21,23,26,.07); color:var(--ink-3)}
.pill--confirm{background:rgba(138,109,46,.16); color:var(--warn)}
.pill--optional{background:transparent; color:var(--slate); border:1px solid var(--line)}

/* ---------- Form -------------------------------------------------------- */
.form{display:grid; gap:16px; max-width:560px}
.field label{display:block; font-size:var(--fs-sm); font-weight:600; margin-bottom:7px}
.field input,.field textarea,.field select{
  width:100%; padding:13px 14px; font:400 var(--fs-base)/1.5 var(--f-body);
  border:1px solid var(--line); border-radius:var(--r); background:var(--white); color:var(--ink);
}
.field textarea{min-height:130px; resize:vertical}
.note{font-size:var(--fs-sm); color:var(--slate)}

/* ---------- Footer ------------------------------------------------------ */
.footer{background:var(--ink); color:#A7ACB2; padding:clamp(48px,6vw,80px) 0 0}
.footer__grid{display:grid; grid-template-columns:1.4fr 1fr 1fr 1fr; gap:36px}
.footer h3{font-size:var(--fs-sm); text-transform:uppercase; letter-spacing:.13em;
           color:var(--brass-200); margin-bottom:14px; font-weight:600}
.footer__col{display:flex; flex-direction:column; gap:9px}
.footer__col a{text-decoration:none; font-size:var(--fs-sm); color:#A7ACB2}
.footer__col a:hover{color:var(--bone)}
.footer__brand{display:flex; flex-direction:column; gap:20px; align-items:flex-start}
.footer__line{font-family:var(--f-display); font-weight:400; font-size:var(--fs-h3);
              color:var(--bone); line-height:1.3; max-width:24ch; letter-spacing:-.015em}
.footer__pending{font-size:var(--fs-sm); color:#8F949A; font-style:italic}  /* 5.88:1 on --ink (was 3.92) */
.footer__bottom{
  margin-top:44px; border-top:1px solid #2B3036; padding:18px 0 22px;
  display:flex; justify-content:space-between; gap:14px; flex-wrap:wrap;
  /* The one sanctioned sub-14px exception in the house system: legal fine print. */
  font-size:10px; letter-spacing:.06em; text-transform:uppercase; color:#6B7178;
}

/* ---------- Reveal ------------------------------------------------------ */
.reveal{opacity:0; transform:translateY(18px)}
.reveal.is-in{opacity:1; transform:none; transition:opacity .7s var(--ease), transform .7s var(--ease)}

/* ---------- Responsive -------------------------------------------------- */
@media (max-width:1100px){
  .nav__links{gap:15px}
  .nav__links > a{font-size:var(--fs-sm)}
}

@media (max-width:1000px){
  .ledger__row{grid-template-columns:repeat(2,1fr)}
  .ledger__cell:nth-child(2){border-right:0}
  .ledger__cell:nth-child(1),.ledger__cell:nth-child(2){border-bottom:1px solid var(--line)}
  .hero .ledger__cell:nth-child(1),.hero .ledger__cell:nth-child(2){border-bottom-color:rgba(243,242,237,.20)}
  .steps{grid-template-columns:repeat(2,1fr)}
  .step:nth-child(2){border-right:0}
  .g-3{grid-template-columns:repeat(2,1fr)}
  .footer__grid{grid-template-columns:1fr 1fr}
  .bento{grid-template-columns:1fr}
  .bento__tall{grid-row:auto}
}

/* ---- Mobile: logo LEFT, hamburger far RIGHT, nothing floating between them (house rule).
   The language switch moves INTO the drawer; the desktop CTA is hidden. ---- */
@media (max-width:900px){
  .hamb{display:block}
  .lang--menu{display:none}
  .lang--drawer{display:block; width:100%; margin-top:8px; border-top:1px solid var(--line);
                padding-top:14px}
  .lang__heading{display:block; font-size:var(--fs-xs); letter-spacing:.14em; text-transform:uppercase;
                 color:var(--slate); margin-bottom:6px}
  .nav__tools{margin-left:auto}

  /* Anchored to the PILL, not the viewport. The pill has backdrop-filter, so it is already the
     containing block for fixed descendants - a fixed drawer renders offset, and parking one
     off-canvas widens the layout viewport on a real phone. */
  .nav__links{
    position:absolute; top:calc(100% + 10px); left:0; right:0; margin-left:0;
    flex-direction:column; align-items:stretch; gap:2px;
    background:var(--white); border:1px solid var(--line); border-radius:var(--r-lg);
    box-shadow:0 30px 60px -28px rgba(21,23,26,.5); padding:14px;
    opacity:0; visibility:hidden; transform:translateY(-8px);
    transition:opacity .28s var(--ease), transform .28s var(--ease), visibility .28s;
    max-height:calc(100dvh - 140px); overflow-y:auto;
  }
  .nav__links.is-open{opacity:1; visibility:visible; transform:none}
  .nav__links > a{font-size:var(--fs-h3); font-family:var(--f-display); padding:9px 4px}
  .nav__links > a > span::after{display:none}
  .nav__cta{display:none}
  .split{grid-template-columns:1fr}
  .split--rev .split__media{order:0}
}

@media (max-width:640px){
  .wrap{width:min(100% - 32px, var(--wrap))}
  .nav__pill{gap:10px; padding:8px 10px 8px 12px}
  .brand__sub{display:none}
  .brand__mark svg{width:124px}
  .ledger__row{grid-template-columns:1fr}
  .ledger__cell{border-right:0; border-bottom:1px solid var(--line); padding:20px 0 18px}
  .hero .ledger__cell{border-bottom-color:rgba(243,242,237,.20)}
  .ledger__cell:last-child{border-bottom:0}
  .steps{grid-template-columns:1fr}
  .step{border-right:0; border-bottom:1px solid var(--line); padding:20px 0 18px}
  .step:last-child{border-bottom:0}
  .g-2,.g-3{grid-template-columns:1fr}
  .footer__grid{grid-template-columns:1fr}
  .brief__item{grid-template-columns:1fr}
  .ribbon__in{gap:6px}
  .marquee{display:none}   /* decorative; it competes with content on a phone */
}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:.001ms!important; transition-duration:.001ms!important}
  html{scroll-behavior:auto}
  .reveal{opacity:1!important; transform:none!important}
  .parallax{transform:none!important}
  .marquee__track{animation:none!important}
  @view-transition{navigation:none}
}

/* ---------- i18n --------------------------------------------------------
   Scope the HIDE under the active-language html class with !important so a
   component display rule cannot out-specify it; never force a display VALUE on
   the language being shown. */
html.en .t-zh{display:none!important}
html.zh .t-en{display:none!important}
html.zh body{font-family:var(--f-zh)}
html.zh h1,html.zh h2,html.zh h3,html.zh h4,
html.zh .ledger__n,html.zh .band__quote,html.zh .footer__line{font-family:var(--f-zh)}

/* =============================================================================
   MOTION — the house vocabulary from references/motion-and-menus.md
   Hand-built, no dependency: our CSP is script-src 'self', so a CDN slider is
   not an option, and a CSS marquee is lighter than pulling Swiper anyway.
   Everything here is switched off under prefers-reduced-motion above.
   ========================================================================== */

/* ---- The nav overlays the hero instead of sitting above it ---------------- */
:root{--nav-h:92px}
.nav{margin-bottom:calc(-1 * var(--nav-h))}
.hero__in{padding-top:calc(var(--nav-h) + clamp(48px,8vw,120px))}
@media (max-width:640px){:root{--nav-h:78px}}

/* ---- Reveals: fadeInUp is the workhorse, with variants and stagger -------- */
.reveal{opacity:0; transform:translateY(26px);
        transition:opacity .8s var(--ease), transform .8s var(--ease)}
.reveal.is-in{opacity:1; transform:none}
.reveal--zoom{transform:scale(.94)}          .reveal--zoom.is-in{transform:none}
.reveal--left{transform:translateX(-30px)}   .reveal--left.is-in{transform:none}
.reveal--right{transform:translateX(30px)}   .reveal--right.is-in{transform:none}
.reveal.d1{transition-delay:.08s} .reveal.d2{transition-delay:.16s}
.reveal.d3{transition-delay:.24s} .reveal.d4{transition-delay:.32s}

/* Stagger children of a marked container without hand-numbering every one. */
.stagger > *{opacity:0; transform:translateY(22px);
             transition:opacity .7s var(--ease), transform .7s var(--ease)}
.stagger.is-in > *{opacity:1; transform:none}
.stagger.is-in > *:nth-child(1){transition-delay:.05s}
.stagger.is-in > *:nth-child(2){transition-delay:.13s}
.stagger.is-in > *:nth-child(3){transition-delay:.21s}
.stagger.is-in > *:nth-child(4){transition-delay:.29s}
.stagger.is-in > *:nth-child(5){transition-delay:.37s}

/* ---- Scroll-driven parallax. Native timeline where supported; the JS in
        site.js drives the same custom property everywhere else. ------------- */
.hero__bg img,.band__bg img{will-change:transform; transform:translate3d(0,var(--par,0px),0) scale(1.12)}
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion:no-preference){
    .hero__bg img,.band__bg img{
      animation:parallax linear both; animation-timeline:view(); animation-range:cover;
    }
    @keyframes parallax{from{transform:translate3d(0,-6%,0) scale(1.12)}
                        to{transform:translate3d(0,6%,0) scale(1.12)}}
    /* A quiet lift on editorial imagery as it comes into view. */
    .split__media img{animation:riseIn linear both; animation-timeline:view();
                      animation-range:entry 10% cover 42%}
    @keyframes riseIn{from{transform:translateY(34px) scale(1.02); opacity:.55}
                      to{transform:none; opacity:1}}
  }
}

/* ---- Page turn: a real cross-document transition where the browser has it -- */
@view-transition{navigation:auto}
@media (prefers-reduced-motion:no-preference){
  ::view-transition-old(root){animation:turnOut .42s var(--ease) both}
  ::view-transition-new(root){animation:turnIn .52s var(--ease) both}
  @keyframes turnOut{to{opacity:0; transform:translateY(-14px) scale(.985)}}
  @keyframes turnIn{from{opacity:0; transform:translateY(22px) scale(.99)}}
  /* The brand mark and the hero headline carry across pages instead of blinking. */
  .brand__mark{view-transition-name:brandmark}
  .nav__pill{view-transition-name:navpill}
  ::view-transition-group(navpill){animation-duration:.42s}
}

/* ---- Hover micro-interactions -------------------------------------------- */
.btn{position:relative; overflow:hidden}
.btn svg{width:15px; height:15px; fill:none; stroke:currentColor; stroke-width:1.8;
         transition:transform .28s var(--ease)}
.btn:hover svg{transform:translateX(4px)}
.btn-primary:hover{box-shadow:0 14px 30px -14px rgba(201,172,119,.9)}
.btn-ink:hover{box-shadow:0 14px 30px -16px rgba(21,23,26,.8)}

.card{transition:transform .35s var(--ease), box-shadow .35s var(--ease),
                 border-color .35s var(--ease)}
.card:hover{transform:translateY(-4px); border-color:var(--brass);
            box-shadow:0 26px 50px -28px rgba(21,23,26,.45)}

/* Image zoom inside a clipped frame - the house card/media hover. */
.split__media,.bento__tile{overflow:hidden; border-radius:var(--r-lg)}
.split__media img,.bento__tile img{transition:transform .8s var(--ease)}
.split__media:hover img,.bento__tile:hover img{transform:scale(1.06)}
.bento__tile{transition:box-shadow .4s var(--ease)}
.bento__tile:hover{box-shadow:0 30px 60px -30px rgba(21,23,26,.6)}

/* ---- Ledger numerals count up (JS sets --n; this only styles the result) --- */
.ledger__n{transition:color .4s var(--ease)}
.ledger__cell{transition:background .4s var(--ease)}
.ledger__cell:hover{background:rgba(201,172,119,.07)}
.hero .ledger__cell:hover{background:rgba(243,242,237,.06)}

/* ---- Marquee ribbon: a light, moving band of the things he actually owns --- */
.marquee{overflow:hidden; border-block:1px solid var(--line); background:var(--bone);
         padding:16px 0; -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
         mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent)}
.section--ink + .marquee,.marquee.marquee--ink{background:var(--ink); border-color:#2B3036}
.marquee__track{display:flex; gap:0; width:max-content; animation:slide 38s linear infinite}
.marquee:hover .marquee__track{animation-play-state:paused}
.marquee__item{display:inline-flex; align-items:center; gap:22px; padding-inline:22px;
               font-family:var(--f-display); font-size:var(--fs-h2); color:var(--ink);
               white-space:nowrap; letter-spacing:-.01em}
.marquee--ink .marquee__item{color:var(--bone)}
.marquee__item::after{content:""; width:7px; height:7px; border-radius:50%;
                      background:var(--brass); flex:0 0 7px}
@keyframes slide{from{transform:translateX(0)} to{transform:translateX(-50%)}}

/* ---- Bento / asymmetric grid — breaks the uniform card rhythm ------------- */
.bento{display:grid; grid-template-columns:1.35fr 1fr; gap:18px}
.bento__tile{position:relative; min-height:260px; display:flex; align-items:flex-end;
             color:var(--bone); isolation:isolate;
             /* These tiles are <a> elements: without this every heading and paragraph inside
                them renders underlined. */
             text-decoration:none}
.bento__tile *{text-decoration:none}
.bento__tall{grid-row:span 2; min-height:420px}
.bento__tile img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; z-index:-2}
.bento__tile::after{content:""; position:absolute; inset:0; z-index:-1;
  background:linear-gradient(180deg, rgba(21,23,26,.05) 0%, rgba(21,23,26,.42) 52%, rgba(21,23,26,.90) 100%)}
.bento__body{padding:26px}
.bento__body h3{font-size:var(--fs-h2); color:var(--bone); margin-bottom:8px}
.bento__body p{color:#CDD1D5; font-size:var(--fs-sm); max-width:42ch}

/* ---- Scroll cue ---------------------------------------------------------- */
.cue{display:inline-flex; align-items:center; gap:10px; margin-top:30px;
     font-size:var(--fs-xs); letter-spacing:.16em; text-transform:uppercase; color:#B3B8BE}
.cue__line{display:block; width:1px; height:38px; background:linear-gradient(var(--brass),transparent);
           animation:cueDrop 2.2s var(--ease) infinite}
@keyframes cueDrop{0%{transform:scaleY(.3); transform-origin:top} 55%{transform:scaleY(1); transform-origin:top}
                   56%{transform-origin:bottom} 100%{transform:scaleY(0); transform-origin:bottom}}
@media (max-width:640px){.cue{display:none}}   /* decorative chip, dropped on mobile */

/* ---------- Record list: awards and listings ------------------------------
   A ledger row rather than another card grid - these pages are about a record,
   and a record reads as lines, not boxes. The missing-year state is styled to
   be visible on purpose: it is the ask, not an oversight. */
.record{border-top:2px solid var(--ink)}
.record__row{display:grid; grid-template-columns:auto minmax(0,1fr) auto; gap:18px 24px;
             align-items:baseline; padding:22px 0; border-bottom:1px solid var(--line);
             transition:background .3s var(--ease), padding-left .3s var(--ease)}
.record__row:hover{background:rgba(201,172,119,.06); padding-left:10px}
.record__i{font-family:var(--f-display); font-weight:600; font-size:var(--fs-h3);
           color:var(--brass-700); letter-spacing:-.02em}
.record__main h3{font-size:var(--fs-h3); margin-bottom:5px}
.record__by{font-size:var(--fs-sm); color:var(--slate)}
.record__year{font-family:var(--f-display); font-weight:600; font-size:var(--fs-h3);
              color:var(--ink); white-space:nowrap; letter-spacing:-.02em}
/* "RM 2,000,000" is far wider than a bare year and wrapped at every mobile width. nowrap alone
   would then push the row, so the step comes down too. */
@media (max-width:640px){ .record__year{font-size:var(--fs-base)} }
.record__year.is-missing{font-family:var(--f-body); font-weight:600; font-size:var(--fs-xs);
                         letter-spacing:.12em; text-transform:uppercase; color:var(--brass-800);
                         background:rgba(201,172,119,.20); padding:6px 11px; border-radius:var(--r)}
@media (max-width:640px){
  .record__row{grid-template-columns:auto minmax(0,1fr); gap:10px 16px}
  .record__year{grid-column:2; justify-self:start}
}

/* ---------- Real marks -----------------------------------------------------
   Use the awarding bodies' and the newsrooms' own logos, not their names set in our typeface.
   A name we typed is our claim; a masthead is theirs, and it is recognised before it is read.

   Every file out of tools/build_logos.py is 240px tall, flat --ink, and optically weight-matched,
   so ONE height rule governs the whole set and adding a ninth logo cannot unbalance it. The dark
   sections invert rather than loading a second set of files. */
.lm{display:block; height:40px; width:auto; opacity:.88;
    transition:opacity .3s var(--ease)}
.section--ink .lm,.band .lm,.marquee--ink .lm{filter:invert(1); opacity:.82}
@media (max-width:640px){ .lm{height:34px} }

/* The wall. Hairline cells, because the ledger rule is this site's structural motif and a wall
   of floating logos would be the one place it stopped. */
.logowall{display:grid; grid-template-columns:repeat(3,1fr);
          border-top:1px solid var(--line); border-left:1px solid var(--line)}
.logowall__cell{display:flex; flex-direction:column; align-items:center; justify-content:center;
                gap:16px; min-height:196px; padding:30px 20px; text-align:center;
                border-right:1px solid var(--line); border-bottom:1px solid var(--line);
                text-decoration:none; color:inherit; min-width:0;
                transition:background .3s var(--ease)}
a.logowall__cell:hover{background:rgba(201,172,119,.08)}
a.logowall__cell:hover .lm{opacity:1}
.logowall .lm{height:56px}
.logowall__meta{font-size:var(--fs-xs); color:var(--slate); line-height:1.5; letter-spacing:.01em}
.logowall__meta em{font-style:normal; color:var(--ink)}
/* The ask, styled like the record's missing year: visible on purpose, because a masthead with no
   clipping behind it is exactly the thing this framework exists to collect. */
.logowall__need{display:inline-flex; align-items:center; gap:8px; font-size:var(--fs-xs);
                font-weight:600; letter-spacing:.1em; text-transform:uppercase;
                color:var(--brass-800);
                /* two words on two lines left the dot orphaned against the cell edge at 390px */
                white-space:nowrap}
.logowall__need::before{content:""; width:5px; height:5px; border-radius:50%;
                        background:var(--brass); flex:0 0 5px}
@media (max-width:420px){ .logowall__need{letter-spacing:.04em; gap:6px} }
/* At 320 the nowrap marker is wider than half the viewport and pushed the whole grid past the
   edge (tools/verify.py, per-element rect). Below that the dot goes and the words may wrap: with
   no dot there is nothing left to orphan. */
@media (max-width:360px){
  .logowall__need{white-space:normal; letter-spacing:.02em}
  .logowall__need::before{display:none}
  .logowall .lm{height:46px}
}
@media (max-width:900px){ .logowall{grid-template-columns:repeat(2,1fr)} }
@media (max-width:420px){ .logowall__cell{min-height:140px; padding:20px 12px} }

/* An inline strip, for prose that used to list the same four names in a sentence. */
.logorow{display:flex; flex-wrap:wrap; align-items:center; gap:26px 34px; margin-top:22px}
.logorow .lm{height:38px}

/* In the record list the awarding body sits where its name used to. Baseline alignment is set on
   .record__row, and an inline-block image on a baseline sits ON it, which dropped the mark below
   the rule; the mark gets its own block line instead. */
.record__mark{margin-top:9px}
.record__mark .lm{height:34px}
