/* ============================================================
   HERO · DECK
   The laptop, and the page inside it. ONE view, start to finish.

   THE RULE THAT MAKES THIS FLICKER-FREE
   There is exactly one copy of this page, and it is real HTML from
   the first frame. Nothing is ever swapped, mounted, unmounted,
   cross-faded, or hidden as the zoom runs. The only thing that
   changes is a single `scale()` on .deck-frame.

   The previous version drew the page twice — once as a WebGL
   CanvasTexture on a 3D laptop and once as a DOM panel — and
   cross-faded between them. Two representations of the same thing
   can never blend cleanly: they differ in size, in sharpness, in
   how text is rasterised, and in when their content appears. That
   dissolve is what read as a flash. It is gone because the second
   representation is gone.

   HOW THE GEOMETRY WORKS
   .deck-frame is exactly the size of the viewport, and .deck-screen
   fills it. So at scale 1 the screen boundary IS the viewport
   boundary — 100% wide, 100% tall, by construction rather than by
   tuning. There is no number to get right and nothing to measure.

   The laptop around it is a real 3D model (js/hero/laptop.js), drawn
   in its own WebGL pass and positioned by js/hero/dock.js from THIS
   element's measured rectangle — the 3D follows the DOM, never the
   reverse. Because the chassis lives outside the display quad, it
   leaves the viewport by itself as the scale approaches 1.
   ============================================================ */

/* FOUR SCREENS OF RUNWAY — about 3600px at a 900px viewport. #deck sticks to
   the top for all of it and releases at the end, which is what lets the
   visitor simply keep scrolling into the rest of the site; no JS is involved
   in that release.

   js/ui/deck.js divides the length into the logo's travel, the laptop's rise,
   the dock, and then a long steady expansion. There is no hold in it: the
   screen is legible from the moment it appears, so it is read WHILE it grows
   rather than during a beat where scrolling does nothing. */
#zoom{position:relative;z-index:4;height:400svh;background:transparent}

/* THE PERSPECTIVE CONTAINER.
   js/hero/dock.js writes `perspective` and `perspective-origin` here every
   time the canvas changes size, from the SAME focal length the WebGL camera
   uses — (viewportH / 2) / tan(fov / 2). The value below is only a sane
   starting point for the frames before the first script runs.

   overflow:hidden is what clips the workstation once it is larger than the
   viewport, and it is safe on this element specifically: flattening only
   happens when overflow is set on something carrying transform-style:
   preserve-3d, and that is .deck-cam below, not this. */
#deck{
  position:sticky;top:0;height:100svh;overflow:hidden;
  /* the dark room the laptop sits in, behind the WebGL backdrop */
  background:transparent;
  perspective:1150px;
}

/* THE CAMERA.
   Carries the 3D camera's inverse world matrix. js/hero/dock.js sizes and
   offsets it to sit exactly on the WebGL canvas's rectangle rather than on
   #deck's, because #deck is sticky and slides relative to the canvas at both
   ends of the runway while the projection must stay centred on the canvas.

   Do NOT give this a transform-origin. The maths below assumes the default
   50% 50%, which is what makes the projection centre land on the box centre;
   0 0 throws the whole scene by half a viewport. */
.deck-cam{
  position:absolute;left:0;top:0;width:100%;height:100%;
  transform-style:preserve-3d;
}

.deck-frame{
  position:absolute;left:0;top:0;width:100%;height:100%;
  /* NO transform here. js/hero/dock.js writes a matrix3d derived from the
     display quad's world matrix, so this element IS the laptop screen as far
     as the browser is concerned — it keystones with it under the elevated
     three-quarter camera and resolves to exactly the viewport rectangle when
     the camera comes square-on at the end.

     Leaving it unset is also the no-JS fallback: with no transform the
     element is already the full viewport, which is the final state.

     transform-style stays FLAT. The page inside is a flat page; letting it
     preserve-3d would send every child through the perspective divide on its
     own instead of rasterising the panel once.

     NO will-change:transform either. It pins the layer's raster scale, so the
     page inside would stay rasterised at whatever size it was promoted at and
     go soft as it grows. */
}

/* The chassis is NOT drawn here any more — it is a real 3D model, built in
   js/hero/laptop.js and drawn in its own render pass over the WebGL canvas.
   js/hero/dock.js places its camera so the model's display quad lands exactly
   on .deck-screen below, which is why there is nothing to line up by hand:
   the 3D is derived from this element's measured rectangle every frame. */

/* ---------- the display ---------- */
.deck-screen{position:absolute;inset:0;z-index:1;overflow:hidden;background:#fff}

/* The reflection on the glass: a broad diagonal sheen plus a soft darkening
   into the corners, the way a real panel looks under a key light. Purely
   decorative — pointer-events:none so it can never take a click, aria-hidden
   so it is not read out, and js/ui/deck.js fades it to nothing as the screen
   becomes the viewport. Kept deliberately faint: at .40 the diagonal band
   hazed the text underneath it, and the content on that display has to be
   legible before anything else. */
.deck-glare{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  opacity:var(--glare,1);
  background:
    linear-gradient(118deg,
      rgba(255,255,255,0) 24%,
      rgba(214,226,255,.17) 38%,
      rgba(255,255,255,.05) 46%,
      rgba(255,255,255,0) 58%),
    radial-gradient(120% 90% at 50% 0%,
      rgba(255,255,255,.10) 0%, rgba(255,255,255,0) 55%),
    radial-gradient(130% 120% at 50% 50%,
      rgba(0,10,61,0) 62%, rgba(0,10,61,.09) 100%);
}

/* ---------- the page itself ----------
   Laid out at final size from the very first frame. It is never
   re-laid-out during the zoom: the scale is a transform, so there is
   no reflow, no font resize, and nothing that could pop. */
.deck-page{
  position:absolute;inset:0;
  display:flex;flex-direction:column;
  background:#fff;color:var(--ink900);
}
.deck-body{
  flex:1;min-height:0;display:grid;place-items:center;
  padding:2vh clamp(24px,6vw,96px) 4vh;
}
@supports (padding:max(0px)){
  .deck-body{
    padding-left:max(clamp(24px,6vw,96px), env(safe-area-inset-left));
    padding-right:max(clamp(24px,6vw,96px), env(safe-area-inset-right));
  }
}

/* ---------- the mock site's header ----------
   Sized in vh/vw so it scales with the laptop rather than jumping between
   breakpoints as the frame grows. .deck-mark is the landing pad for the 3D
   mark in the opening — js/hero/choreography.js reads its rectangle every
   frame and flies the mark to it, so its position here IS the target. */
.deck-nav{
  flex:0 0 auto;display:flex;align-items:center;gap:1.1vw;
  padding:1.5vh clamp(24px,6vw,96px);
  border-bottom:1px solid var(--line);
}
.deck-mark{
  width:3.4vh;height:auto;color:var(--navy);flex:0 0 auto;
  display:flex;align-items:center;
}
.deck-mark svg{width:100%;height:auto;display:block}
.deck-wm{
  font-family:Archivo;font-weight:800;font-size:1.9vh;letter-spacing:-.02em;
  line-height:1;color:var(--ink900);
}
.deck-wm i{
  display:block;font-family:'Geist Mono',monospace;font-style:normal;
  font-size:.72vh;letter-spacing:.22em;font-weight:500;color:var(--ink300);
  margin-top:.35vh;
}
.deck-links{display:flex;gap:1.6vw;margin-left:auto}
.deck-links span{font-size:1.35vh;color:var(--ink500)}
.deck-cta{
  background:var(--navy);color:#fff;border-radius:100px;
  padding:.85vh 1.5vw;font-size:1.3vh;font-weight:600;white-space:nowrap;
}
/* the header is the first thing to go when there is no room for it */
@media(max-height:520px){ .deck-links,.deck-cta{display:none} }
@media(max-width:560px){ .deck-links{display:none} }

.scr-in{width:100%;max-width:1180px}

.scr-kick{
  display:flex;align-items:center;gap:12px;
  font-family:'Geist Mono',monospace;font-size:clamp(10px,1.1vw,12px);
  letter-spacing:.18em;text-transform:uppercase;color:var(--b500);
}
.scr-kick i{width:34px;height:1px;background:#C7D0EC;display:block;flex:0 0 auto}

.scr-in h2{
  font-family:Archivo;font-weight:800;
  font-size:min(clamp(28px,4.6vw,62px),9.5vh);
  letter-spacing:-.035em;line-height:1.02;color:var(--ink900);
  margin:clamp(14px,2vh,26px) 0 0;
}
.scr-lead{
  font-size:clamp(14px,1.5vw,18px);line-height:1.6;color:var(--ink500);
  max-width:56ch;margin:clamp(12px,1.8vh,22px) 0 0;
}

.scr-cols{
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:clamp(16px,2vw,30px);margin-top:clamp(22px,3.6vh,48px);
}
.scr-col{border-top:2px solid var(--navy);padding-top:clamp(12px,1.8vh,20px)}
.scr-col span{
  font-family:'Geist Mono',monospace;font-size:clamp(9px,1vw,11px);
  letter-spacing:.12em;color:var(--b500);display:block;
}
.scr-col h3{
  font-family:Archivo;font-weight:600;font-size:clamp(14px,1.45vw,20px);
  letter-spacing:-.01em;color:var(--ink900);line-height:1.22;
  margin:clamp(6px,1vh,12px) 0 clamp(6px,.9vh,11px);
}
.scr-col p{font-size:clamp(12px,1.1vw,14.5px);line-height:1.5;color:var(--ink500)}

/* ---------- responsive ----------
   The page is one viewport tall and cannot scroll inside the screen,
   so it has to fit at every size — in the LONGEST language, not the
   shortest. Malay runs about 15% longer than English. */
@media(max-width:900px){
  .scr-cols{grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(14px,2.2vh,22px) 18px}
}
@media(max-width:520px){
  .scr-lead{font-size:13.5px;line-height:1.5}
}
@media(max-width:380px){
  .scr-in h2{font-size:min(25px,7.8vh);margin-top:12px}
  .scr-lead{font-size:12.5px;line-height:1.4;margin-top:10px}
  .scr-cols{margin-top:16px;gap:14px}
  .scr-col{padding-top:11px}
  .scr-col h3{font-size:14px;margin:6px 0 0}
  .scr-col p{display:none}
}
/* landscape phone: wide and short, so the columns go back to one row */
@media(max-height:560px) and (min-width:600px){
  .scr-cols{grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin-top:14px}
  .scr-col h3{font-size:14px}
  .scr-col p{font-size:11.5px;line-height:1.4}
}
@media(max-height:420px) and (min-width:600px){ .scr-col p{display:none} }
@media(max-height:560px) and (max-width:599px){
  .scr-lead{display:none}
  .scr-in h2{font-size:min(24px,7.5vh)}
  .scr-cols{margin-top:14px}
  .scr-col p{display:none}
}

/* Reduced motion: no zoom at all. The frame simply starts at full size,
   which is the same page, just without the approach. */
/* Reduced motion: no approach. js/ui/deck.js pins cover 1 / tilt 0 / rise 1,
   which js/hero/dock.js resolves to the square-on full-screen framing — the
   same page, without the flight to it. Only the runway needs collapsing. */
@media(prefers-reduced-motion:reduce){
  #zoom{height:100svh}
}
