/* Cora laptop shell.

   A drawn MacBook that the Cora scene sits inside. Not a photograph: every
   part of it is gradients, radii and shadows on a dozen elements plus one svg,
   so it weighs a few kilobytes, stays sharp at any size, and rotates in real
   3D rather than tilting like a picture.

   Sizes are design pixels against the same 1200 x 760 screen the scene uses,
   which is almost exactly a MacBook Air's aspect ratio, so the stage drops in
   at scale 1 with no distortion and no cropping.

   THE CAMERA MODEL, measured rather than guessed. Two facts about CSS,
   confirmed by rotating a test plane and reading where its corners landed:

     1. On a plane hinged at its top edge, rotateX(POSITIVE) swings the far edge
        TOWARD the camera. At +70 degrees that edge measured 521px wide against
        399px at the hinge; at -70 it measured 323px. A keyboard deck comes
        toward you, so the base is a positive rotation.

     2. On the assembly, rotateX(NEGATIVE) raises the camera above the machine
        and reveals the top of the deck. So elevation is written as a positive
        number and negated once, in one place.

   The hinge angle belongs to the LID, not the deck. A laptop sits on a flat
   desk and the screen leans back off it. 0 is closed, 90 is square, 107 is how
   one actually sits, past 120 is the arrow shape.

   perspective-origin sits at the middle of the screen, the eye level of a
   straight-on shot, so at elevation 0 the lid is a perfect rectangle and the
   deck is edge on. That is what a head-on photograph of a MacBook looks like.

   Focal length is part of the camera, not a constant. Apple's head-on product
   shots use a very long lens, near enough orthographic, which is the only way
   the lid can be a perfect rectangle AND the keyboard stay invisible at once.
   A short lens gives the three quarter shots their drama. */

/* A scaled element still occupies its full unscaled size in layout, so the rig
   carries the scaled footprint and the laptop is positioned inside it.
   Without this the assembly is 1226px wide in layout whatever the zoom says,
   and it shoulders its way off the left of a narrow window. */
.rig{
  --W:1226px;                 /* lid outer width */
  --LH:803px;                 /* lid outer height */
  --TOT:1320px;               /* lid plus however much deck the angle shows */
  position:relative;
  /* The footprint follows --zlayout, which a page animating the zoom sets ONCE
     to its largest value. --z then only ever touches the transform, and a
     transform composites where a width does not. Left alone it tracks --z,
     which is what a page with a fixed camera wants. */
  width:calc(var(--W) * var(--zlayout, var(--z,.58)));
  height:calc(var(--TOT) * var(--zlayout, var(--z,.58)));
  perspective:var(--lens,3400px);perspective-origin:50% 30%
}
.mb{
  --sw:1200px;                /* screen, matching the stage exactly */
  --sh:760px;
  --bz:13px;                  /* bezel, sides and bottom */
  --bzt:30px;                 /* bezel, top, thicker for the camera */
  --deck:880px;               /* how deep the base is */
  --lid-angle:107deg;         /* 0 closed, 90 square, 107 as one really sits */
  --elev:0deg;                /* camera height. 0 is level with the screen */
  --azim:0deg;                /* camera swing. positive views it from its left */
  --roll:0deg;
  --skin:#2b3240;             /* Midnight */
  --skin-dk:#141821;
  --skin-lt:#454f61;
  position:absolute;top:0;left:50%;
  width:var(--W);height:var(--LH);margin-left:calc(var(--W) / -2);
  transform-style:preserve-3d;transform-origin:50% 0;
  /* translateY is first, so it applies last, in the rig's flat space. It is a
     framing nudge for shots where the assembly swings out of view, and the lift
     for shots where it leaves the desk. */
  transform:translateY(var(--ty,0px)) scale(var(--z,.58))
    rotateX(calc(var(--elev) * -1)) rotateY(var(--azim)) rotateZ(var(--roll))
}
/* Pages that drive the camera from scroll turn this off: a transition fighting
   a value written every frame lags behind the scrollbar. */
.rig[data-ease="1"] .mb{transition:transform .9s cubic-bezier(.33,.02,.13,1)}

/* Lid --------------------------------------------------------------------- */
.lid{
  position:absolute;inset:0;border-radius:22px 22px 5px 5px;
  transform-origin:50% 100%;
  /* translateZ lifts the lid clear of the deck so that at 0 degrees it rests
     on top of it rather than fighting it for the same plane. It is the lid's
     own thickness, so shut, the two stack instead of overlapping. */
  transform:rotateX(calc(var(--lid-angle) - 90deg)) translateZ(-23px);
  background:linear-gradient(150deg,var(--skin-lt) 0%,var(--skin) 26%,var(--skin-dk) 100%);
  padding:var(--bzt) var(--bz) var(--bz);
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,.055),        /* the machined edge */
    0 42px 80px -28px rgba(0,0,0,.85)
  /* The lid is one flat plane, on purpose. Nothing inside it is 3D (the
     screen, the notch and the glare all lie on it), so preserve-3d bought
     nothing in Chrome and broke Safari: WebKit culls backface-hidden children
     of a nested preserve-3d element from the wrong side, and the screen's
     content vanished from the front (16 Sep 2026, bisected in headed WebKit). */
}
/* The lid has thickness. Without this it reads as a picture of a screen rather
   than an object, and it is one element. A real Air's lid is a shade over 4mm
   against 304mm wide, and this is a touch more than that, because a perfectly
   scaled one reads as paper on a screen. */
.lid::after{
  content:"";position:absolute;left:6px;right:6px;bottom:-13px;height:21px;
  border-radius:0 0 8px 8px;
  background:linear-gradient(180deg,#4d5765 0%,#2d343f 44%,#151a21 100%)
}
/* A desktop behind the scene, not black. The scene's own camera push
   translates the stage, and what it uncovers would otherwise be a black wedge
   down one side. A desktop is what is genuinely behind a browser window. */
.screen{
  position:relative;width:var(--sw);height:var(--sh);
  border-radius:9px;overflow:hidden;
  background:linear-gradient(152deg,#3a2d64 0%,#6940dd 34%,#a4499f 68%,#d65499 100%);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.9)
}
/* The camera housing. Sits in the bezel, so it never covers content. */
.notch{
  position:absolute;z-index:3;left:50%;top:0;width:152px;height:var(--bzt);
  margin-left:-76px;background:#080a0e;border-radius:0 0 11px 11px
}
.notch::after{
  content:"";position:absolute;left:50%;top:11px;width:7px;height:7px;margin-left:-3.5px;
  border-radius:50%;background:radial-gradient(circle at 34% 30%,#3b4a63,#080b11 70%)
}
/* Glass. One gradient, low opacity, and it stops looking like a rectangle with
   a website in it. */
.glare{
  position:absolute;z-index:2;inset:var(--bzt) var(--bz) var(--bz);border-radius:9px;
  pointer-events:none;opacity:.5;
  background:linear-gradient(122deg,
    rgba(255,255,255,.13) 0%,rgba(255,255,255,.05) 17%,
    rgba(255,255,255,0) 38%,rgba(255,255,255,0) 100%)
}

/* Turn the machine past 90 degrees and, without this, you see the keyboard
   through the bottom of the case, mirrored. Hiding the back faces of the
   deck's detail leaves the base's aluminium showing from below. The lid is a
   flat plane (see .lid), so a backface rule on the screen would do nothing in
   Chrome and hid the screen's content from the front in Safari; seen from
   behind, the lid now shows its screen mirrored through the case, which no
   page does. */
.keys,.grille,.pad{backface-visibility:hidden}

/* Base ---------------------------------------------------------------------
   Flat on the desk, always. Hinged at the lid's bottom edge and laid forward
   toward the camera, which is the positive rotation. */
.base{
  position:absolute;left:0;top:100%;width:100%;height:var(--deck);
  transform-origin:50% 0;transform:rotateX(90deg);transform-style:preserve-3d;
  border-radius:5px 5px 16px 16px;
  /* Aluminium catches light along its length. A flat fill reads as a black
     plinth. */
  background:linear-gradient(178deg,#1a1f28 0%,#39424f 9%,#4a5566 34%,#39424f 72%,#262d38 100%);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.07)
}
/* The machine has a height. Front on and shut, this wall plus the lid's own
   edge is the entire laptop, so without it a closed MacBook seen square on is
   a nine pixel line. A real Air is 11mm tall against 304mm wide, which is the
   30 against 1226 below. Hinged at the deck's front edge and stood up. */
.lip{
  position:absolute;left:0;right:0;bottom:0;height:30px;
  transform-origin:50% 100%;transform:rotateX(-90deg);
  /* Square, not rounded. The wall meets the deck along a straight line, and a
     radius here read as the front flaring away from the machine. Its top
     starts at the deck's own colour so the two read as one piece of metal
     folded over, which is what they are. */
  border-radius:0 0 3px 3px;
  background:linear-gradient(180deg,#262d38 0%,#3c4655 22%,#242b35 68%,#12161d 100%)
}
/* The scoop you put a finger in to open it. Cut into the wall rather than laid
   flat on the deck, where edge on it was a stray dark bar across the front. */
.lip::after{
  content:"";position:absolute;left:50%;top:0;width:250px;height:11px;margin-left:-125px;
  border-radius:0 0 11px 11px;background:rgba(0,0,0,.34)
}

/* The keyboard. Every keycap on the machine is ONE path in ONE svg, built from
   a row spec of about 300 bytes. Seventy eight keys, and the same path drawn
   three times: a hard shadow underneath, a lit chamfer along the top edge, and
   the face over both, which is what makes them stand proud of the deck. */
.keys{position:absolute;left:150px;top:40px;width:926px;height:352px;display:block;overflow:visible}
/* Every key sits DOWN in a well cut out of the top case, which is the thing
   that was missing: shaded like an object standing proud, they read as flat
   stickers. So the same outline is drawn three times. Two copies inflated
   slightly and offset in opposite directions become the walls of the well: the
   near-black one above each key is the wall in shadow, the pale one below is
   the wall catching light. The keycap then covers the middle and only those
   two fringes show. */
.keys .welllight{fill:#6d798d;transform:translateY(1.7px)}
.keys .welldark {fill:#04060a;transform:translateY(-1.7px)}
.keys .caps     {fill:url(#kf)}

/* Legends. Every single character key is ONE text node: an svg text element
   takes a LIST of x positions, one per character, and each absolutely placed
   character becomes its own chunk that text-anchor then centres. The number
   row is a second such node, carrying the shifted character above the digit. */
.keys text{
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;
  fill:#eceef3;fill-opacity:.93;font-weight:500
}
.keys .glyph{font-size:18px;text-anchor:middle}
.keys .num  {font-size:14px;text-anchor:middle;fill-opacity:.88}
.keys .fkey {font-size:11px;text-anchor:middle;font-weight:400;fill-opacity:.8}
.keys .word {font-size:12px;font-weight:400;fill-opacity:.85}
/* Arrows and the power symbol are drawn, not typed. A unicode triangle or
   power glyph depends on the reader's font having it, and on Windows that is a
   coin toss between a symbol and an empty box. */
.keys .tri{fill:#eceef3;fill-opacity:.9}
.keys .pwr{fill:none;stroke:#eceef3;stroke-opacity:.9;stroke-width:1.5;stroke-linecap:round}

/* The speaker grilles. A dot pattern rather than several hundred holes. */
.grille{
  position:absolute;top:44px;width:56px;height:344px;border-radius:4px;
  background-image:radial-gradient(circle,#0d1117 1.6px,rgba(0,0,0,0) 1.7px);
  background-size:9px 9px;opacity:.85
}
.grille.l{left:64px}
.grille.r{right:64px}

.pad{
  position:absolute;left:50%;top:430px;width:560px;height:366px;margin-left:-280px;
  border-radius:12px;background:linear-gradient(180deg,#2c3441,#20262f);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.55),inset 0 2px 6px rgba(0,0,0,.45)
}

/* Contact shadow. It sits on the floor rather than on the laptop, which is
   what stops the whole thing looking like a sticker, and it fades out as the
   machine leaves the desk. */
/* No filter here on purpose. A filter forces an element out of its parent's
   3D context and flattens it, so this shadow used to be drawn as a flat
   blurred blob in the lid's plane rather than lying on the floor, which is
   what put a soft dark smear across the front of a closed machine. The
   gradient is soft enough on its own. */
.floor{
  position:absolute;left:-9%;top:100%;width:118%;height:520px;
  transform-origin:50% 0;transform:rotateX(90deg) translateZ(-2px);
  background:radial-gradient(50% 44% at 50% 46%,
    rgba(0,0,0,.55) 0%,rgba(0,0,0,.34) 38%,rgba(0,0,0,.12) 62%,rgba(0,0,0,0) 80%);
  pointer-events:none;opacity:var(--shadow,1)
}

/* The scene fills the screen exactly. */
.mb .screenbox{position:absolute;inset:0}

[data-glare="0"] .glare{display:none}
[data-keys="0"] .keys,[data-keys="0"] .grille{display:none}
