/* ══════════════════════ CSS3D PANELS ══════════════════════
   Every panel is authored at ~700 CSS px tall and framed so it
   occupies ~66% of viewport height. That lands ~0.76 screen px
   per element px at 1440x900, so a 22px rule here reads as ~17px.
   Keep that ratio in mind before changing any font-size.        */
.p3d{position:absolute;overflow:hidden;
  -webkit-font-smoothing:antialiased;backface-visibility:hidden;}

/* ── 1. OFFICE TEAR SHEET (1120 x 700) ──
   Four roles in a 2x2 grid. fitH is 0.72 here (vs 0.66 elsewhere) to buy
   back the screen space this density needs; at ~0.83 screen px per element
   px, an 18.5px rule below reads as ~15px. */
#sheet{width:1120px;height:700px;background:var(--paper);color:var(--ink);
  padding:30px 38px;font-family:var(--sans);
  box-shadow:inset 0 0 90px rgba(150,132,96,.15);}
#sheet .grain{position:absolute;inset:0;pointer-events:none;opacity:.5;
  background-image:radial-gradient(rgba(120,105,75,.10) 1px,transparent 1px);
  background-size:4px 4px;}
#sheet .hdr{display:flex;justify-content:space-between;align-items:flex-start;
  border-bottom:1.5px solid var(--ink);padding-bottom:12px;}
#sheet .who{display:flex;gap:18px;align-items:center;}
#sheet .ph{width:78px;height:96px;object-fit:cover;object-position:center 20%;
  border:1px solid var(--rule);}
#sheet h1{font-family:var(--serif);font-size:40px;line-height:1;font-weight:400;letter-spacing:-.012em;}
#sheet .sub{font-family:var(--mono);font-size:12.5px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--muted);margin-top:7px;}
#sheet .avail{margin-top:9px;display:inline-block;font-family:var(--mono);font-size:11.5px;
  letter-spacing:.13em;text-transform:uppercase;color:var(--gold);
  border:1px solid var(--gold);padding:4px 9px;}
#sheet .doc{text-align:right;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.18em;text-transform:uppercase;color:var(--muted);line-height:1.9;}
#sheet .doc b{display:block;color:var(--ink);font-size:16px;letter-spacing:.2em;font-weight:500;}
#sheet .body{display:grid;grid-template-columns:1fr 1fr;gap:16px 36px;margin-top:16px;
  /* Space reserved for the absolutely-positioned .foot below. The footer is
     pinned, so without this the last bullet can run underneath it on any
     machine whose font metrics render a line or two taller than mine. */
  padding-bottom:52px;}
#sheet .rt{display:flex;justify-content:space-between;align-items:baseline;
  border-bottom:1px solid var(--rule);padding-bottom:4px;}
#sheet .rt em{font-family:var(--serif);font-style:normal;font-size:23px;}
#sheet .rt span{font-family:var(--mono);font-size:10.5px;letter-spacing:.13em;color:var(--muted);}
#sheet .rr{font-family:var(--mono);font-size:10.5px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--gold);margin:6px 0 7px;}
#sheet ul{list-style:none;}
#sheet li{font-size:18.5px;line-height:1.38;color:var(--ink2);padding-left:15px;
  position:relative;margin-bottom:6px;}
#sheet li:before{content:'';position:absolute;left:0;top:10px;width:7px;height:1px;background:var(--gold);}
#sheet li b{color:var(--ink);font-weight:600;}
#sheet .foot{position:absolute;left:38px;right:38px;bottom:14px;display:flex;
  justify-content:space-between;font-family:var(--mono);font-size:10px;
  letter-spacing:.2em;text-transform:uppercase;color:var(--muted);}

/* ── 2. TRADING BOARD (1240 x 700) ── */
#board{width:1240px;height:700px;background:#07090c;color:#d8dde4;
  padding:34px 40px;font-family:var(--mono);
  box-shadow:inset 0 0 120px rgba(0,0,0,.9);}
#board .scan{position:absolute;inset:0;pointer-events:none;opacity:.32;
  background:repeating-linear-gradient(0deg,transparent 0 3px,rgba(0,0,0,.5) 3px 4px);}
#board .bh{display:flex;justify-content:space-between;align-items:baseline;
  border-bottom:1px solid rgba(201,162,39,.4);padding-bottom:11px;}
#board .bh h2{font-family:var(--serif);font-size:37px;font-weight:400;color:#f0ead9;letter-spacing:.01em;}
#board .bh .r{font-size:12.5px;letter-spacing:.24em;color:#c9a227;text-transform:uppercase;}
/* fixed columns: $ROBOADVISOR.PY is the widest ticker and was
   colliding with the description cell under auto layout */
#board table{width:100%;border-collapse:collapse;margin-top:14px;table-layout:fixed;
  margin-bottom:44px;}
#board col.c1{width:196px;} #board col.c3{width:210px;} #board col.c4{width:106px;}
#board th{font-size:11px;letter-spacing:.22em;text-transform:uppercase;
  color:#6d7683;text-align:left;padding:0 0 10px;font-weight:400;
  border-bottom:1px solid rgba(255,255,255,.09);}
#board th.n,#board td.n{text-align:right;}
#board td{padding:19px 0;border-bottom:1px solid rgba(255,255,255,.055);vertical-align:baseline;
  transition:transform .18s ease;}
#board tr.pos-row{pointer-events:auto;cursor:pointer;}
#board tr.pos-row:hover td{background:rgba(201,162,39,.055);transform:translateX(3px);}
#board tr.pos-row:hover .sym{text-shadow:0 0 14px rgba(201,162,39,.55);}
#board .sym{font-size:20px;color:#c9a227;font-weight:500;letter-spacing:.02em;white-space:nowrap;}
#board .desc{font-family:var(--sans);font-size:17.5px;color:#c8cdd5;padding-right:18px;}
#board .stack{font-size:12px;color:#6d7683;letter-spacing:.05em;white-space:nowrap;}
#board .up{font-size:20px;color:#3ecf7a;font-weight:500;white-space:nowrap;}
#board .flat{font-size:20px;color:#c9a227;font-weight:500;}
#board .live{font-size:20px;color:#5aa9ff;font-weight:500;}
#board .bf{position:absolute;left:40px;right:40px;bottom:22px;display:flex;
  justify-content:space-between;font-size:11.5px;letter-spacing:.2em;
  text-transform:uppercase;color:#5b6470;border-top:1px solid rgba(255,255,255,.07);padding-top:12px;}
#board .bf b{color:#3ecf7a;font-weight:500;}

/* ── 3. STUDY BOOK SPREAD (1080 x 700) ── */
#spread{width:1080px;height:700px;display:flex;background:var(--paper2);color:var(--ink);
  font-family:var(--sans);}
#spread .pg{flex:1;padding:40px 42px 46px;position:relative;}
#spread .pg.l{box-shadow:inset -22px 0 28px -22px rgba(60,50,30,.42);}
#spread .pg.r{box-shadow:inset 22px 0 28px -22px rgba(60,50,30,.42);}
#spread .gutter{width:3px;background:linear-gradient(90deg,rgba(80,66,40,.30),rgba(80,66,40,.06),rgba(80,66,40,.30));}
#spread h3{font-family:var(--mono);font-size:11.5px;letter-spacing:.28em;
  text-transform:uppercase;color:var(--gold);border-bottom:1px solid var(--rule);
  padding-bottom:7px;margin-bottom:14px;}
#spread h3:not(:first-child){margin-top:26px;}
#spread .pn{position:absolute;bottom:12px;font-family:var(--mono);font-size:11px;color:var(--muted);letter-spacing:.2em;}
#spread .pg.l .pn{left:38px;} #spread .pg.r .pn{right:38px;}

/* ── 4. ROOFTOP CARD (1160 x 660) ── */
#card{width:1160px;height:660px;background:linear-gradient(158deg,#faf7f0 0%,#efe8d8 100%);
  color:var(--ink);padding:48px 56px;font-family:var(--sans);
  box-shadow:inset 0 0 0 1px rgba(154,123,46,.5), inset 0 0 0 7px transparent, inset 0 0 0 8px rgba(154,123,46,.28);}
#card .grain{position:absolute;inset:0;pointer-events:none;opacity:.45;
  background-image:radial-gradient(rgba(120,105,75,.11) 1px,transparent 1px);background-size:4px 4px;}
#card h1{font-family:var(--serif);font-size:62px;font-weight:400;line-height:.95;letter-spacing:-.012em;}
#card .role{font-family:var(--mono);font-size:14px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--muted);margin-top:11px;}
#card .rule{height:1px;background:var(--gold);margin:28px 0 0;opacity:.55;}
/* The card was top-weighted: four rows, then a void, then a stamp pinned to the
   bottom edge. The rows now sit in a centred block that uses the height, and the
   type is up a step because there is room for it. */
#card .rows{display:grid;grid-template-columns:1fr 1fr;gap:40px 40px;margin-top:34px;}
#card .row .k{font-family:var(--mono);font-size:12.5px;letter-spacing:.24em;
  text-transform:uppercase;color:var(--gold);}
#card .row .v{font-family:var(--mono);font-size:23px;color:var(--ink);margin-top:7px;letter-spacing:0;}
#card .row .v a{color:inherit;text-decoration:none;border-bottom:1px solid rgba(154,123,46,.45);
  pointer-events:auto;cursor:pointer;transition:border-color .2s ease,color .2s ease;}
#card .row .v a:hover{color:var(--gold);border-bottom-color:var(--gold);}
#card .sig{position:absolute;right:56px;bottom:44px;text-align:right;}
#card .sig em{font-family:var(--serif);font-style:italic;font-size:36px;color:var(--ink);}
#card .sig span{display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.24em;
  text-transform:uppercase;color:var(--muted);margin-top:6px;}
#card .stamp{position:absolute;left:56px;bottom:46px;font-family:var(--mono);font-size:12.5px;
  letter-spacing:.2em;text-transform:uppercase;color:var(--gold);
  border:1px solid var(--gold);padding:8px 13px;}

/* ── 4b. ROOFTOP MESSAGE PAD (1000 x 700) ──
   A real form on a legal pad. Everything interactive needs pointer-events
   explicitly: CSS3DRenderer sets pointer-events:none on its own wrapper, and
   #css3d is none as well, so children must opt back in. */
#note{width:1000px;height:700px;background:#fdf6d8;color:#1b1710;
  padding:34px 40px 30px;font-family:var(--sans);
  box-shadow:inset 0 0 60px rgba(150,130,70,.20);}
#note .rule-lines{position:absolute;inset:0;pointer-events:none;opacity:.5;
  background-image:repeating-linear-gradient(180deg,transparent 0 37px,rgba(90,110,150,.30) 37px 38px);}
#note:before{content:'';position:absolute;top:0;bottom:0;left:74px;width:1px;
  background:rgba(190,90,80,.45);}
#note .nhead{display:flex;justify-content:space-between;align-items:baseline;
  border-bottom:2px solid rgba(27,23,16,.75);padding-bottom:10px;margin-bottom:16px;position:relative;}
#note .nhead span{font-family:var(--serif);font-size:34px;}
#note .nhead .to{font-family:var(--mono);font-size:14px;letter-spacing:.12em;color:#6a6350;}
#note textarea{width:100%;height:398px;background:transparent;border:none;outline:none;
  resize:none;font-family:var(--sans);font-size:23px;line-height:38px;color:#1b1710;
  position:relative;pointer-events:auto;cursor:text;padding:0;}
#note textarea::placeholder{color:rgba(27,23,16,.34);}
#note .nfoot{display:flex;gap:14px;align-items:center;margin-top:14px;position:relative;}
#note .nfoot input{flex:1;background:transparent;border:none;border-bottom:1px solid rgba(27,23,16,.35);
  outline:none;font-family:var(--mono);font-size:17px;color:#1b1710;padding:7px 2px;
  pointer-events:auto;cursor:text;}
#note .nfoot input::placeholder{color:rgba(27,23,16,.32);}
#note .nfoot button{font-family:var(--mono);font-size:15px;letter-spacing:.2em;text-transform:uppercase;
  background:#1b1710;color:#fdf6d8;border:none;padding:12px 26px;cursor:pointer;
  pointer-events:auto;transition:background .2s ease;}
#note .nfoot button:hover{background:#3c3320;}
#note .nfoot button:disabled{opacity:.5;cursor:default;}
#note .nstatus{position:absolute;left:40px;bottom:8px;font-family:var(--mono);font-size:14px;
  letter-spacing:.1em;color:#5e6b45;min-height:18px;}
#note .nstatus.err{color:#a33a2a;}

/* ── 6. FLANKING SIGN (660 x 200) ── */
#flank-sign{width:660px;height:200px;background:#0c0b0d;color:var(--gold2);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  border:2px solid rgba(201,162,39,.55);
  box-shadow:inset 0 0 46px rgba(201,162,39,.16);}
#flank-sign .line1{font-family:var(--mono);font-size:38px;letter-spacing:.14em;font-weight:600;}
/* the sign is mounted smaller now (0.80 m) so it clears the header — the
   subtitle has to carry more weight on screen than it did at 0.92 m */
#flank-sign .line2{font-family:var(--mono);font-size:24px;letter-spacing:.30em;margin-top:10px;
  color:rgba(245,241,232,.95);}

/* ══════════════════ 7. TRADING WALL SCREENS ══════════════════
   All authored at 1 CSS px = 1 mm (config.SCREEN uses scale 0.001), so the
   numbers below are literally millimetres on the wall.

   Displayed scale differs per screen because they sit at different distances
   from the reading stop — the flank stacks land at ~0.34 screen px per
   element px, the consoles at ~0.44, the crawl at ~0.40. Font sizes are
   picked against those ratios: 42px on a flank screen reads as ~14px. That
   is the whole reason these are DOM and not textures — at those sizes a
   minified bitmap has nothing left, while glyphs and SVG strokes are still
   rasterised at native device density.                                   */
.scr{background:#05070a;color:#cfd8e3;font-family:var(--mono);
  display:flex;flex-direction:column;
  border:3px solid #10161f;box-shadow:inset 0 0 60px rgba(90,150,220,.10);
  --up:#43d98b; --dn:#ef6d6d;}
.scr.up{--acc:var(--up);} .scr.dn{--acc:var(--dn);}

.scr-h{display:flex;justify-content:space-between;align-items:baseline;
  padding:14px 20px 8px;letter-spacing:.06em;}
.scr-h .sym{font-weight:700;color:#f0d68a;}
.scr-h .pct{font-weight:700;color:var(--acc);}
.scr-c{flex:1;position:relative;padding:0 8px;min-height:0;}
.scr-c .spk{position:absolute;inset:0;width:100%;height:100%;}
.scr-f{display:flex;justify-content:space-between;
  padding:6px 20px 12px;color:rgba(160,178,198,.55);}

/* horizon line so a flat stretch still reads as a chart, not a blank box */
.scr-c::after{content:'';position:absolute;left:8px;right:8px;bottom:34%;
  border-top:1px dashed rgba(120,150,190,.16);}

.scr.flank .scr-h{font-size:42px;} .scr.flank .scr-f{font-size:26px;}
.scr.console .scr-h{font-size:40px;} .scr.console .scr-f{font-size:25px;}

/* ── the crawl (3400 x 150) ── */
.tkr{background:#05070a;border-top:2px solid #131b25;border-bottom:2px solid #131b25;
  display:flex;align-items:center;}
.tkr-run{display:flex;white-space:nowrap;will-change:transform;
  animation:crawl 52s linear infinite;}
/* 52px in a 150px strip, not 88px: a crawl reads as a crawl because many
   symbols are moving through it. At 88px only five fitted across the 3.4m
   rail and it looked like a headline, not a tape. */
.tkr i{font-style:normal;font-family:var(--mono);font-size:52px;
  letter-spacing:.04em;padding:0 26px;display:inline-block;}
.tkr i b{color:#f0d68a;font-weight:700;margin-right:14px;}
.tkr i.up{color:#43d98b;} .tkr i.dn{color:#ef6d6d;}
/* the strip holds the symbol list twice, so -50% is exactly one full copy
   and the loop point is invisible */
@keyframes crawl{from{transform:translateX(0);}to{transform:translateX(-50%);}}
@media (prefers-reduced-motion:reduce){.tkr-run{animation:none;}}

/* ═══ 4c · THE RÉSUMÉ ═══════════════════════════════════════════
   A printed page on a desk. Set in the serif/mono pair the rest of the
   site uses rather than a LaTeX clone, so it belongs to this place — but
   laid out with a real résumé's density and hierarchy. 1080x1400. */
#resume{width:1080px;height:1400px;background:var(--paper);color:var(--ink);
  padding:56px 70px 0;box-sizing:border-box;overflow:hidden;
  box-shadow:0 40px 90px rgba(0,0,0,.55);}
#resume .grain{position:absolute;inset:0;pointer-events:none;opacity:.42;
  background-image:radial-gradient(rgba(120,100,60,.10) 1px,transparent 1px);
  background-size:3px 3px;}
#resume .rhead{border-bottom:2px solid var(--ink);padding-bottom:13px;margin-bottom:17px;}
#resume h1{font-family:var(--serif);font-size:46px;letter-spacing:.005em;line-height:1;margin:0;}
#resume .rcontact{font-family:var(--mono);font-size:15px;color:var(--muted);margin-top:11px;letter-spacing:.02em;}

#resume .rsec{font-family:var(--mono);font-size:14px;letter-spacing:.26em;text-transform:uppercase;
  color:var(--gold);border-bottom:1px solid rgba(154,123,46,.42);
  padding-bottom:5px;margin:19px 0 10px;}
#resume .rsec:first-of-type{margin-top:0;}

#resume .rrow{display:flex;justify-content:space-between;align-items:baseline;gap:24px;margin-top:11px;}
#resume .rrow > div:first-child{font-family:var(--serif);font-size:20px;line-height:1.25;}
#resume .rrow b{font-weight:600;}
#resume .rwhen{font-family:var(--mono);font-size:13.5px;color:var(--muted);
  white-space:nowrap;letter-spacing:.02em;}
#resume .rnote{font-family:var(--mono);font-size:13px;color:var(--muted);margin-top:6px;line-height:1.45;}

#resume ul{margin:7px 0 0;padding-left:19px;list-style:none;}
#resume li{position:relative;font-size:14.6px;line-height:1.44;margin-bottom:4px;color:#2c2924;}
#resume li::before{content:'';position:absolute;left:-15px;top:9px;width:5px;height:5px;
  background:var(--gold);opacity:.75;}

#resume .rskill{font-size:14.4px;line-height:1.45;margin-bottom:3px;}
#resume .rskill span{display:inline-block;min-width:118px;font-family:var(--mono);font-size:12.5px;
  letter-spacing:.18em;text-transform:uppercase;color:var(--gold);}

/* Pinned to the page foot. The anchor is a real <a download> — the click
   arrives via main.js's raycast dispatch, which calls .click() on it, so the
   browser's own download path runs exactly as if the user hit it directly. */
#resume .rfoot{position:absolute;left:70px;right:70px;bottom:38px;
  display:flex;align-items:center;gap:18px;
  border-top:1px solid rgba(154,123,46,.42);padding-top:22px;}
#resume .rfoot a{flex:1;text-align:center;font-family:var(--mono);font-size:17px;
  letter-spacing:.24em;text-transform:uppercase;text-decoration:none;
  background:var(--ink);color:var(--paper);padding:17px 26px;
  transition:background .2s ease;}
#resume .rfoot a.hov{background:var(--gold);}
#resume .rfoot button{font-family:var(--mono);font-size:13px;letter-spacing:.2em;
  text-transform:uppercase;background:none;border:1px solid rgba(60,55,45,.4);
  color:var(--muted);padding:17px 22px;cursor:pointer;transition:border-color .2s ease,color .2s ease;}
#resume .rfoot button.hov{border-color:var(--gold);color:var(--gold);}

/* Hover states, driven by main.js adding .hov — CSS :hover never fires on a
   CSS3D panel because the pointer never actually reaches these elements. */
#card .row .v a.hov{color:var(--gold);border-bottom-color:var(--gold);}

/* ═══ 3 · THE BOOK ══════════════════════════════════════════════
   A book of positions: what I hold on the left, where I'm committed on the
   right. Entries carry no thesis lines by design — the substance sits in
   "Currently working on", which is the only block that needs to argue. */
#spread .hold{margin-bottom:11px;}
#spread .hold em{font-family:var(--serif);font-style:italic;font-size:25px;
  display:block;line-height:1.16;color:var(--ink);}
#spread .hold span{font-family:var(--mono);font-size:12.5px;color:var(--muted);letter-spacing:.07em;}

#spread .alloc{width:100%;border-collapse:collapse;margin-bottom:4px;}
#spread .alloc td{padding:9px 0;border-bottom:1px solid rgba(120,100,60,.22);vertical-align:baseline;}
#spread .alloc tr:last-child td{border-bottom:none;}
#spread .alloc .tk{font-family:var(--mono);font-size:23px;letter-spacing:.06em;
  color:var(--ink);width:110px;}
#spread .alloc .nm{font-family:var(--mono);font-size:12.5px;color:var(--muted);letter-spacing:.04em;}
/* tabular-nums so the two rows' figures line up once they are marked live */
#spread .alloc .pf{font-family:var(--mono);font-size:16px;text-align:right;
  color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap;width:92px;}
#spread .alloc .pf.up{color:var(--green);} #spread .alloc .pf.down{color:var(--red);}

#spread .disc{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--muted);margin-top:10px;}

#spread .prob{margin-bottom:15px;}
#spread .prob em{font-family:var(--serif);font-style:italic;font-size:24px;
  display:block;line-height:1.16;color:var(--ink);}
#spread .prob span{display:block;font-size:16px;line-height:1.44;color:var(--ink2);
  margin-top:4px;padding-left:15px;position:relative;}
#spread .prob span:before{content:'';position:absolute;left:0;top:9px;
  width:6px;height:1px;background:var(--gold);}

/* Source column: repo links replaced the Return figures. Styled to the board's
   own palette — an unstyled <a> renders in browser default blue, which is the
   one colour nothing else on this site uses. */
#board td.n a{color:var(--gold2,#c9a227);text-decoration:none;
  border-bottom:1px solid rgba(201,162,39,.35);letter-spacing:.06em;
  transition:color .2s ease,border-color .2s ease;}
#board td.n a.hov{color:#f5f1e8;border-bottom-color:#f5f1e8;}

/* ═══ FOCUS ═══════════════════════════════════════════════════════
   There was not a single focus style in the project. The panels are real DOM
   inside the CSS3D layer, so keyboard users could already Tab through every
   link and field on them — they just had no way to see where they were, which
   is worse than not being able to reach them at all.

   Two rings, because the panels come in two grounds: dark ink on the paper
   panels, gold on the dark ones. Both use focus-visible so a mouse click never
   paints a ring, and both use outline+offset rather than a border so nothing
   reflows when focus moves. */
#sheet a:focus-visible,
#spread a:focus-visible,
#card .row .v a:focus-visible,
#resume .rfoot a:focus-visible,
#resume .rfoot button:focus-visible,
#note textarea:focus-visible,
#note input:focus-visible,
#note button:focus-visible{
  outline:2px solid var(--gold);
  outline-offset:3px;
  border-radius:1px;
}
#board td.n a:focus-visible,
#board tr.pos-row:focus-visible{
  outline:2px solid var(--gold2,#c9a227);
  outline-offset:3px;
}
/* The .hov class is applied by the raycast for the mouse; mirroring it on
   focus keeps the two input methods showing the same state. */
#card .row .v a:focus-visible{color:var(--gold);border-bottom-color:var(--gold);}
#board td.n a:focus-visible{color:#f5f1e8;border-bottom-color:#f5f1e8;}

/* ═══ PORTRAIT ═══════════════════════════════════════════════════
   Phones and portrait tablets. The panels themselves become portrait —
   dimensions here must match CARRIER_PORTRAIT in config.js exactly, or the
   HTML and its hole-punch stop agreeing — and every multi-column block
   collapses to one column.

   Type is scaled roughly 1.9x. That is not decoration: on a 390x844 phone the
   old landscape panels put 15px copy on screen at about 4 real pixels, and the
   portrait geometry alone only recovers it to ~7. The remaining factor has to
   come from the type, because the element is a fixed-size box that gets
   projected — a media query on the viewport still applies to it, but the
   result is scaled by however much of the screen the panel occupies. */

  /* 1 · tear sheet — 860 wide.
     Eight bullets of real résumé copy is the densest thing on the site, and
     the line length is what decides whether it fits: at 760px and 27px type
     each bullet wrapped to three lines, and the four roles together ran 637px
     past the box. Wider box, slightly smaller type, tighter leading — same
     words, two lines a bullet. */
body.portrait #sheet{padding:32px 38px;}
body.portrait #sheet .hdr{flex-direction:column;gap:14px;padding-bottom:10px;}
body.portrait #sheet h1{font-size:54px;}
body.portrait #sheet .sub{font-size:18px;margin-top:5px;}
body.portrait #sheet .avail{font-size:16px;padding:7px 12px;margin-top:7px;}
body.portrait #sheet .ph{width:92px;height:114px;}
body.portrait #sheet .doc{text-align:left;font-size:15px;line-height:1.7;}
body.portrait #sheet .doc b{font-size:22px;}
body.portrait #sheet .body{grid-template-columns:1fr;gap:15px;margin-top:16px;padding-bottom:44px;}
body.portrait #sheet .rt{padding-bottom:3px;}
body.portrait #sheet .rt em{font-size:27px;}
body.portrait #sheet .rt span,body.portrait #sheet .rr{font-size:15px;}
body.portrait #sheet .rr{margin:4px 0 5px;}
body.portrait #sheet li{font-size:25px;line-height:1.31;margin-bottom:6px;padding-left:19px;}
body.portrait #sheet li:before{top:14px;width:10px;}
body.portrait #sheet .foot{font-size:15px;left:38px;right:38px;bottom:14px;}

  /* 2 · projects board — 940 wide */
body.portrait #board{padding:30px 34px;}
body.portrait #board h2{font-size:46px;}
body.portrait #board .r{font-size:16px;}
body.portrait #board .bh{padding-bottom:8px;}
  /* the table becomes stacked records: a 4-column grid cannot survive here */
body.portrait #board thead{display:none;}
body.portrait #board table,body.portrait #board tbody,body.portrait #board tr,body.portrait #board td{display:block;width:100%;}
  /* Ticker and source share the top line of each record rather than costing a
     row apiece — six projects x one saved line is most of what made this panel
     707px too tall. The source is absolutely positioned because <td> as a
     flex/float child of a display:block <tr> is not reliable across engines. */
body.portrait #board tr.pos-row{position:relative;padding:15px 0;border-bottom:1px solid rgba(255,255,255,.08);}
body.portrait #board td{padding:0;border:none;}
body.portrait #board td.sym{font-size:31px;padding-right:150px;}
body.portrait #board td.desc{font-size:27px;line-height:1.33;margin-top:8px;}
body.portrait #board td.stack{font-size:18px;margin-top:6px;}
body.portrait #board td.n{position:absolute;right:0;top:15px;width:auto;
  text-align:right;font-size:22px;margin:0;}
body.portrait #board .bf{font-size:15px;flex-wrap:wrap;gap:6px 16px;}

  /* 3 · the book — two facing pages become one column, 860 wide */
body.portrait #spread{flex-direction:column;}
body.portrait #spread .gutter{display:none;}
body.portrait #spread .pg{padding:32px 38px 14px;}
body.portrait #spread .pg.r{padding-top:6px;}
body.portrait #spread .pg.l,body.portrait #spread .pg.r{box-shadow:none;}
body.portrait #spread h3{font-size:18px;margin:18px 0 10px;}
body.portrait #spread h3:first-child{margin-top:0;}
body.portrait #spread .hold{margin-bottom:9px;}
body.portrait #spread .hold em{font-size:31px;}
body.portrait #spread .hold span{font-size:18px;}
body.portrait #spread .alloc td{padding:7px 0;}
body.portrait #spread .alloc .tk{font-size:29px;width:165px;}
body.portrait #spread .alloc .nm{font-size:16px;}
body.portrait #spread .alloc .pf{font-size:22px;width:135px;}
body.portrait #spread .disc{font-size:14px;}
body.portrait #spread .prob{margin-bottom:11px;}
body.portrait #spread .prob em{font-size:28px;}
body.portrait #spread .prob span{font-size:26px;line-height:1.36;}
  /* Stacked, each page's folio is pinned to the bottom of ITS OWN page, so the
     left one landed in the MIDDLE of the panel — it was printing
     "The Book · 03 / 04" across the allocation table.
     Dropping .pg's positioning context lets the section marker resolve against
     the whole panel instead, which puts it where every other panel keeps its
     footer: pinned bottom-left. The right page's "Waterloo, ON" goes, because
     the section number is the one that has to survive — it is the only thing
     telling you this is 03 of 04. */
body.portrait #spread .pg{position:static;}
body.portrait #spread .pg.r{padding-bottom:54px;}
body.portrait #spread .pg.r .pn{display:none;}
body.portrait #spread .pg.l .pn{left:38px;bottom:16px;font-size:15px;}
  /* the dash sits on the first line's x-height; it has to move with the type */
body.portrait #spread .prob span:before{top:15px;width:10px;}

  /* 4 · contact card */
body.portrait #card{padding:52px 50px;}
body.portrait #card h1{font-size:70px;}
body.portrait #card .role{font-size:20px;}
body.portrait #card .rows{grid-template-columns:1fr;gap:30px;margin-top:40px;}
body.portrait #card .row .k{font-size:18px;}
body.portrait #card .row .v{font-size:31px;}
body.portrait #card .sig em{font-size:48px;}
body.portrait #card .sig span,body.portrait #card .stamp{font-size:17px;}
body.portrait #card .stamp{left:50px;bottom:44px;padding:11px 17px;}
body.portrait #card .sig{right:50px;bottom:42px;}

  /* 4b · message pad */
body.portrait #note{padding:40px 42px;}
body.portrait #note .nhead{font-size:22px;}
body.portrait #note textarea{font-size:26px;}
body.portrait #note input{font-size:22px;}
body.portrait #note button{font-size:20px;padding:16px 26px;}
body.portrait #note .nstatus{font-size:17px;}

  /* 4c · the résumé page keeps its own portrait shape; only the type grows */
body.portrait #resume{padding:52px 56px 0;}
body.portrait #resume h1{font-size:62px;}
body.portrait #resume .rcontact{font-size:20px;}
body.portrait #resume .rsec{font-size:19px;}
body.portrait #resume .rrow > div:first-child{font-size:27px;}
body.portrait #resume .rwhen{font-size:18px;}
body.portrait #resume li{font-size:20px;line-height:1.42;}
body.portrait #resume .rskill{font-size:19px;}
body.portrait #resume .rskill span{min-width:150px;font-size:16px;}
body.portrait #resume .rfoot a{font-size:22px;padding:22px 26px;}
body.portrait #resume .rfoot button{font-size:17px;padding:22px 20px;}


/* ═══ FLAT PANELS (portrait) ══════════════════════════════════════
   The card sits over the scene instead of inside it. Fixed to the viewport,
   scrollable if the content is taller than the screen, and sized in real
   viewport units — so none of the projection maths that was misplacing these
   on real phones is involved at all.

   dvh, not vh: on iOS the URL bar collapses as you scroll and vh keeps
   reporting the tall value, which is what pushes fixed elements off screen. */
body.portrait .p3d.flat{
  position:fixed;
  left:12px; right:12px;
  top:calc(env(safe-area-inset-top, 0px) + 62px);
  bottom:calc(env(safe-area-inset-bottom, 0px) + 54px);
  width:auto !important; height:auto !important;
  max-height:none;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  /* Above the canvas, below the nav. The WebGL canvas is OPAQUE — it clears to
     #08070a at alpha 1, and in flat mode nothing punches a hole in it — so a
     card that fails to sit above z-index 2 is not dimmed, it is invisible. */
  z-index:6;
  /* display, not opacity+visibility. The previous version toggled three
     properties with a transition and a delay between them, and on the device it
     reported the right box, the right class and the right computed values while
     painting nothing. There is no state to get stuck in here. */
  pointer-events:auto;
  box-shadow:0 26px 70px rgba(0,0,0,.66);
}
/* The display toggle has to out-specify the panels' own ID rules. #spread
   declares display:flex for its two facing pages, and an ID (1,0,0) beats three
   classes (0,3,1) — so the flat `display:none` never applied to it, The Book
   stayed permanently displayed on top of every other panel, and the card that
   measured correct underneath it was simply covered. */
body.portrait #sheet.flat, body.portrait #board.flat, body.portrait #spread.flat,
body.portrait #card.flat, body.portrait #note.flat{ display:none; }
body.portrait #sheet.flat.show, body.portrait #board.flat.show,
body.portrait #spread.flat.show, body.portrait #card.flat.show,
body.portrait #note.flat.show{ display:block; }
/* Type is set for a real screen now rather than a projected surface, so the
   portrait sizes above (tuned for a 760px element seen at ~60% of a phone)
   are far too large here. Back to something a phone actually reads at. */
body.portrait .p3d.flat{font-size:16px;}
body.portrait #sheet.flat{padding:22px 20px;}
body.portrait #sheet.flat h1{font-size:30px;}
body.portrait #sheet.flat .sub{font-size:12px;}
body.portrait #sheet.flat .avail{font-size:10.5px;padding:6px 9px;}
body.portrait #sheet.flat .ph{width:58px;height:72px;}
body.portrait #sheet.flat .doc{font-size:10px;}
body.portrait #sheet.flat .doc b{font-size:14px;}
body.portrait #sheet.flat .body{padding-bottom:8px;gap:16px;}
body.portrait #sheet.flat .rt em{font-size:20px;}
body.portrait #sheet.flat .rt span,body.portrait #sheet.flat .rr{font-size:10px;}
body.portrait #sheet.flat li{font-size:15.5px;line-height:1.5;padding-left:14px;}
body.portrait #sheet.flat li:before{top:9px;width:7px;}
body.portrait #sheet.flat .foot{position:static;margin-top:14px;font-size:10px;}

body.portrait #board.flat{padding:22px 20px;}
body.portrait #board.flat h2{font-size:30px;}
body.portrait #board.flat .r{font-size:10px;}
body.portrait #board.flat td.sym{font-size:19px;}
body.portrait #board.flat td.desc{font-size:15.5px;}
body.portrait #board.flat td.stack{font-size:10.5px;}
body.portrait #board.flat td.n{font-size:14px;}
body.portrait #board.flat .bf{position:static;margin-top:14px;font-size:10px;}

body.portrait #spread.flat{padding:0;}
body.portrait #spread.flat .pg{padding:22px 20px 6px;}
body.portrait #spread.flat h3{font-size:11px;margin:16px 0 8px;}
body.portrait #spread.flat .hold em{font-size:21px;}
body.portrait #spread.flat .hold span{font-size:11px;}
body.portrait #spread.flat .alloc .tk{font-size:18px;width:84px;}
body.portrait #spread.flat .alloc .nm{font-size:10.5px;}
body.portrait #spread.flat .alloc .pf{font-size:14px;width:76px;}
body.portrait #spread.flat .disc{font-size:9.5px;}
body.portrait #spread.flat .prob em{font-size:18px;}
body.portrait #spread.flat .prob span{font-size:15px;}
body.portrait #spread.flat .pn{position:static;font-size:10px;padding:0 20px 14px;}

body.portrait #card.flat{padding:26px 22px;}
body.portrait #card.flat h1{font-size:34px;}
body.portrait #card.flat .role{font-size:11px;}
body.portrait #card.flat .rows{gap:18px;margin-top:20px;}
body.portrait #card.flat .row .k{font-size:10.5px;}
body.portrait #card.flat .row .v{font-size:19px;}
body.portrait #card.flat .stamp,body.portrait #card.flat .sig{position:static;}
body.portrait #card.flat .sig{text-align:left;margin-top:18px;}
body.portrait #card.flat .sig em{font-size:26px;}
body.portrait #card.flat .sig span,body.portrait #card.flat .stamp{font-size:10px;}
body.portrait #card.flat .stamp{display:inline-block;margin-top:18px;padding:7px 11px;}

body.portrait #note.flat{padding:22px 20px;}
body.portrait #note.flat .nhead{font-size:12px;}
body.portrait #note.flat textarea{font-size:15px;min-height:120px;}
body.portrait #note.flat input{font-size:14px;}
body.portrait #note.flat button{font-size:12px;padding:13px 18px;}
body.portrait #note.flat .nstatus{font-size:11px;}
