/* AIREI — /demo/ conversion page. Page-scoped only; everything shared lives in
   tokens.css / base.css / shell.css and is NOT redeclared here.

   Motion contract (DESIGN.md §5 · plan §08) is obeyed literally in this file:
   transitions only on opacity / transform / colour / border-color / background /
   outline-offset, easing only from the three tokens, and every rule that moves a
   pixel carries a prefers-reduced-motion final state. qa/motion-lint.py enforces it.

   Why this file exists at all: qa/html-lint.py bans <style> blocks, so a page that
   needs its own layout gets its own stylesheet rather than an inline one. */

/* ---- hero ------------------------------------------------------------- */
.hero{padding-top:clamp(56px,7vw,104px);border-top:0}
/* clamp + line-height brought into line with proof.css:13 on 2026-08-26. `.lede` is ONE
   component — the standfirst under the h1 — and it was carrying two different formulas:
   proof.css clamp(18px,2vw,21px)/1.55 on the eleven proof-family pages, demo.css
   clamp(18px,1.6vw,21px) with no leading (so body 1.6) on the seven demo-family ones.
   They agree at 390px and again at 1440px, and disagree in between: at a 1000px window the
   same paragraph set 18px/1.6 here and 20px/1.55 there. Nothing chose that; it is the
   arithmetic of two hand-written clamps. proof.css is the majority and the one that states
   its leading, so this side moves. margin-top:22px is demo-family layout and stays. */
.lede{max-width:var(--measure);font-size:clamp(18px,2vw,21px);line-height:1.55;
  color:var(--ink-muted);margin-top:22px}
.hero .lede{margin-top:26px}
h1.hero-h{font-size:clamp(38px,6.2vw,78px);max-width:16ch;margin-top:22px}
.hero-h em{font-style:normal;color:var(--signal-gold)}
.form-proof{margin-top:16px;padding-top:14px;font-size:14px}
.hero-proof{margin-top:28px;padding-top:0;border-top:0;font-size:14.5px}
.headcount{margin-top:30px;padding-top:18px;border-top:1px solid var(--hairline-strong);
  max-width:var(--measure);font-size:15.5px;color:var(--ink-muted)}
.headcount b{color:var(--ink);font-weight:600}

/* ---- section headings -------------------------------------------------
   THE SEVEN PAGES ON THIS STYLESHEET HAD NO SECTION-HEADING STYLE AT ALL.
   Measured on the live site 2026-08-26 at 1440px, computed font-size of every <h2>:

     index / ms                                    64px   (home.css `h2.big`)
     company syarikat mypalm ms-mypalm traceability
     kebolehkesanan careers kerjaya ai-ffb-grading
     penggredan-ai news berita                     54px   (proof.css:16 `h2`)
     products produk contact hubungi privacy
     demo kenali-jurutera                          26px   <- NOBODY SET THIS

   26px is not a smaller heading, it is the ABSENCE of a heading: it is Chrome's UA
   default `h2{font-size:1.5em}` landing on the 17px body, and it applied to all 17
   <h2> elements across those seven pages. products.html is the commercial centre of
   the site — four products, four services — and its three section headings ("Find
   your position first.", "What each one is.", "Work AIREI does that is not a
   product.") were rendering 2.1x smaller than the equivalent heading on every
   editorial page, so the page had a 78px h1 and then nothing until 15px list copy.
   That flatness is the whole reason the page reads as a specification sheet.

   Matched to `proof.css:16` DELIBERATELY rather than to home.css's larger `h2.big`
   ramp: the site should own ONE section-heading scale, and the nine editorial pages
   already carry it. This is not a new size, it is the existing one reaching the
   pages that were missing it.

   Spacing has to be declared here too. base.css sets `h1,h2,h3,h4{margin:0}`, and on
   the proof.css pages the eyebrow/h2/lede gaps come from `.sec-head`, which none of
   these seven pages use (`grep -c sec-head` -> 0 on all seven). Without the two rules
   below the new 54px heading would sit hard against `.eyebrow` above it and against
   `.trust` below it. */
h2{font-size:clamp(30px,4.4vw,54px)}
.eyebrow + h2{margin-top:14px}
h2 + .trust,h2 + .routes,h2 + .offices{margin-top:clamp(26px,3vw,40px)}

/* ---- the two lanes ---------------------------------------------------- */
.lanes{display:grid;gap:clamp(28px,4vw,56px);grid-template-columns:1fr;margin-top:44px}
@media (min-width:960px){.lanes{grid-template-columns:1fr 1fr}}
.lane{border:1px solid var(--hairline-strong);background:var(--panel-navy);
  padding:clamp(24px,3vw,38px);display:flex;flex-direction:column}
/* Both lanes stretch to the same height on purpose — they are parallel, not
   primary/secondary, and unequal cards would say otherwise. `margin-top:auto` pushes
   each lane's footnote to the card foot so the extra height reads as alignment
   rather than as a void at the bottom of the shorter one. */
/* 2.6vw -> 2.4vw, to agree with home.css:268. Same class, same two-card component, two
   different interpolations between the same 24px floor and 32px ceiling, so the two lane
   headings drifted apart only between ~923px and ~1333px and nowhere else — which is the
   signature of a value nobody chose. The gap is small (about 1.5px at its widest) and that
   is the argument for closing it rather than against: there is no design intent to lose.
   The rest of this component genuinely does differ by page and is left alone — home seams
   its two cards with a 1px rule and turns two-up at 820px, demo gives each card its own
   border and turns two-up at 960px. */
.lane h3{font-size:clamp(24px,2.4vw,32px);margin-bottom:14px}
.lane>p{color:var(--ink-muted);font-size:15.5px;max-width:52ch}
.lane-tag{font-family:var(--f-mono);font-size:12.5px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--crude-amber);margin-bottom:16px}

/* WhatsApp lane */
.wa-list{list-style:none;margin:24px 0 0;padding:0;display:grid;gap:12px}
.wa-card{border:1px solid var(--hairline);background:var(--panel-navy-2);padding:18px 20px}
.wa-card h4{font-family:var(--f-body);font-weight:700;font-size:16px;letter-spacing:0;line-height:1.3}
.wa-card p{margin:6px 0 14px;font-size:14.5px;color:var(--ink-muted)}
.wa-card .btn{width:100%;justify-content:center}
/* ABSOLUTE BAN — side-stripe borders. A `border-left` thicker than 1px used as a coloured
   accent on a card, a list item, a callout or an alert is the first item on the DON'T list
   in the impeccable skill, and it is the one AI tell this site was still carrying in six
   places. The prescribed rewrites are full borders, background tints, leading
   numbers/icons, or nothing, and each of the six below takes whichever of those the
   surrounding component already uses. */
/* Was a 2px left stripe on a callout. The tint was always doing the work; the edge moves
   to the top, where it reads as the head of a note rather than as decoration beside one. */
.honest{margin-top:auto;padding:20px 16px 14px;border-top:2px solid var(--crude-amber);
  background:var(--panel-navy-2);font-size:14px;color:var(--ink-muted)}
.honest b{color:var(--ink);font-weight:600}

/* Book-a-slot lane */
.buildstate{margin:22px 0 0;padding:13px 15px;border:1px dashed var(--crude-amber);
  background:var(--panel-navy-2);font-size:13.5px;color:var(--ink-muted)}
.buildstate b{color:var(--signal-gold);font-weight:600}
.buildstate a{color:var(--signal-gold);border-bottom:1px solid var(--hairline-strong)}
/* --airei-blue-lift is 4.00:1 on --panel-navy-2 and 4.5 is the bar. It passes on
   --panel-navy, which is why it survives elsewhere. Caught by qa/contrast.py on my
   own markup, minutes after I added it. */
.form-grid{display:grid;gap:18px;margin-top:24px}
.field{display:grid;gap:7px}
.field label{font-family:var(--f-mono);font-size:13px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-muted)}
.field .req{color:var(--signal-gold)}
.field input{font-family:var(--f-body);font-size:16px;color:var(--ink);background:var(--deep-water);
  border:1px solid var(--hairline-strong);border-radius:2px;padding:13px 14px;width:100%;
  transition:border-color var(--d2) var(--e-quad),outline-offset var(--d1) var(--e-quad)}
.field input::placeholder{color:var(--ink-subtle);opacity:1}
.field input:focus{border-color:var(--signal-gold)}
.field input[aria-invalid="true"]{border-color:#FF9E7A}
.field .hint{font-size:13px;color:var(--ink-muted)}
.field .fielderr{font-size:13px;color:#FFB49A;min-height:0}
@media (prefers-reduced-motion:reduce){.field input{transition:none}}

.formerr{margin-top:4px;font-size:14.5px;color:var(--ink-muted)}
.formerr[data-state="error"]{color:#FFB49A}
.formerr[data-state="ok"]{color:var(--signal-gold)}
.formerr:empty{margin-top:0}
.formstate{margin-top:18px;padding:16px 18px;border:1px dashed var(--hairline-strong);
  background:var(--panel-navy-2);font-size:14.5px;color:var(--ink-muted)}
.formstate b{color:var(--signal-gold);font-weight:600;display:block;margin-bottom:6px;
  font-family:var(--f-mono);font-size:13px;letter-spacing:.16em;text-transform:uppercase}
.formstate[hidden]{display:none}
/* Same 4.00:1 pair as .buildstate: --airei-blue-lift passes on --panel-navy and fails
   on --panel-navy-2. Caught by qa/contrast.py on markup I had just written, for the
   second time in one session — which is the check earning its keep, not a nuisance. */
.formstate a:not(.btn){color:var(--signal-gold);border-bottom:1px solid var(--hairline-strong)}
/* `.formstate a` (0,1,1) out-specifies `.btn-primary` (0,1,0), so the WhatsApp handover
   rendered gold-on-gold: measured #FFD658 on #FFD658 = 1.00:1, i.e. invisible. Third time
   this exact shape has bitten on this site (nav.main a, .news a). Scope the link rule so a
   button placed inside a state block keeps its own colour. */
.formstate a.btn{border-bottom:0}
.formstate a.btn-primary,.formstate a.btn-primary:hover,.formstate a.btn-primary:focus{color:#14202C}
.privacy{margin-top:auto;padding-top:20px;font-size:13.5px;color:var(--ink-subtle);max-width:52ch}
.privacy a{border-bottom:1px solid var(--hairline-strong)}
noscript .formstate{border-style:solid}

/* ---- who picks up ----------------------------------------------------- */
.pickup{display:grid;gap:clamp(24px,4vw,52px);grid-template-columns:1fr;align-items:start}
@media (min-width:860px){.pickup{grid-template-columns:minmax(0,1fr) minmax(0,.82fr)}}
.pickup figure{margin:0}
/* The founder photograph carried width="800" height="450" and NO css, so the column squeezed
   its width to 478 while the height attribute held 450 — measured 478x450 against a true
   1.7778 file, i.e. 40.3% horizontally squashed, on a photograph of a named person. Same
   shape as the header-logo bug: an intrinsic ratio overridden on one axis only. */
.pickup figure img{display:block;width:100%;height:auto}
/* Sentence case per .src-line in proof.css: this caption names a person and a
   qualification ("Surendran Kuranadan, founder · DOSH Grade 1 Steam Engineer since 2012"),
   and caps flattened both. */
.pickup figcaption{margin-top:12px;font-family:var(--f-mono);font-size:12.5px;letter-spacing:.02em;
  line-height:1.7;max-width:34ch;color:var(--ink-subtle)}
.routes{list-style:none;margin:26px 0 0;padding:0;display:grid;gap:0}
.routes li{display:grid;gap:4px;padding:15px 0;border-top:1px solid var(--hairline)}
.routes .who{font-family:var(--f-mono);font-size:13px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--crude-amber)}
.routes .to{font-size:15.5px;color:var(--ink)}
.routes .to span{color:var(--ink-subtle)}

/* ---- trust furniture -------------------------------------------------- */
.trust{display:grid;gap:clamp(22px,3vw,40px);grid-template-columns:repeat(auto-fit,minmax(238px,1fr));
  margin-top:38px}
.trust-group{padding-top:16px;border-top:1px solid var(--hairline-strong)}
.trust-group h3{font-family:var(--f-mono);font-size:13px;font-weight:600;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink-subtle);margin-bottom:14px;line-height:1.4}
.trust-group ul{list-style:none;margin:0;padding:0;display:grid;gap:9px}
.trust-group li{font-size:15px;color:var(--ink);line-height:1.45}
.trust-group li span{color:var(--ink-muted)}
.trust-group a{border-bottom:1px solid var(--hairline-strong)}

@media (width <= 600px){.lane>p{color:#F4F6F8}}
/* ---- close ------------------------------------------------------------ */
.beside-cta{margin:34px 0 0;padding-top:18px;border-top:1px solid var(--hairline-strong);
  max-width:var(--measure);font-size:15px;color:var(--ink-muted)}
.beside-cta b{color:var(--ink);font-weight:600}
/* .06em -> .02em, matching the `.src` source-note role everywhere else on the site
   (base.css, news.css, home.css). Same normalisation as `.answer .src` in smart-mill.css. */
.beside-cta .src{display:block;margin-top:8px;font-family:var(--f-mono);font-size:13px;
  letter-spacing:.02em;color:var(--ink-subtle)}
.beside-cta .src a{color:var(--ink-muted);border-bottom:1px solid var(--hairline-strong)}
.close-row{display:flex;flex-wrap:wrap;gap:14px;margin-top:30px}
.close-note{margin-top:24px;font-size:14px;color:var(--ink-subtle);max-width:var(--measure)}

/* ── STICKY BOOKING BAR ──────────────────────────────────────────────────────
   Created by assets/js/stickycta.js, so it does not exist without scripting.
   It RESERVES its own height as body padding when shown rather than floating over
   the last paragraph — a fixed bar that covers the content it sits above is the
   reason operators ask for these to be removed again. */
.stickycta{position:fixed;left:0;right:0;bottom:0;z-index:90;
  display:flex;align-items:center;gap:12px;justify-content:center;
  padding:12px 16px;background:rgba(11,21,32,.97);
  border-top:1px solid var(--hairline-strong);
  box-shadow:0 -8px 30px rgba(0,0,0,.4)}
.stickycta[hidden]{display:none}
.stickycta .btn{margin:0}
.stickycta-x{background:none;border:1px solid var(--hairline-strong);color:var(--ink-muted);
  width:34px;height:34px;flex:none;border-radius:2px;cursor:pointer;
  font:400 18px/1 var(--f-mono)}
.stickycta-x:hover{color:var(--ink);border-color:var(--ink-muted)}
.stickycta-x:focus-visible{outline:2px solid var(--signal-gold);outline-offset:2px}
@media (max-width:520px){
  .stickycta{padding:10px 12px;gap:8px}
  .stickycta .btn{flex:1;text-align:center;padding-left:10px;padding-right:10px}
}

/* TAP TARGET ON TOUCH. The phone number in the trust list is the fastest route off this
   page for a reader who would rather call than fill the form, and it measured 119x20 at
   390 in a real mobile context. Inline, so padding-block — it must stay able to wrap. */
@media (pointer:coarse){ .trust-group li a{padding-block:12px} }
