/* ── Roy Albeck · coming-soon · light editorial lane ─────────────────────
   Brand (locked): base #FAF8F8 · ink #10131C · accent #F0341B
   Type: Space Grotesk (workhorse) · Pixelify Sans (pixel signature) · Space Mono (annotations) */

:root{
  --base:#FAF8F8; --ink:#10131C; --red:#F0341B;
  --ink-soft:rgba(16,19,28,.60); --ink-faint:rgba(16,19,28,.38);
  --line:rgba(16,19,28,.14); --grid:rgba(16,19,28,.05); --dot:rgba(16,19,28,.11);
  --sans:'Space Grotesk',ui-sans-serif,sans-serif;
  --pixel:'Pixelify Sans',monospace;
  --mono:'Space Mono',ui-monospace,monospace;
  --floor:96px;
}
*{box-sizing:border-box;margin:0;padding:0}
/* stop iOS from inflating text in landscape */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
html,body{height:100%}
/* visible, branded keyboard focus — mouse/touch stay clean via :focus-visible */
a:focus-visible,button:focus-visible,[tabindex]:focus-visible{
  outline:2px solid var(--red);outline-offset:3px;border-radius:2px}
/* base: just the page color + the top highlight; the grid lives on a separate
   layer below so it can be MASKED to fade away from the hero (focal contrast).
   The "dot" layer was dropped — three competing grid layers (lines+dots+radial)
   is what made the page noisy. Reference: Vercel/blueprint grid principle —
   "grid should be barely visible; if you notice it consciously, it's too much." */
body{
  font-family:var(--sans);color:var(--ink);background-color:var(--base);
  background-image:radial-gradient(circle at 50% -10%, rgba(255,255,255,.7), transparent 55%);
  background-position:center top;
  overflow:hidden;-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent;
  position:relative;
}
/* grid layer — readme.com principle: the grid is CONTEXTUAL, not wallpaper. It
   fades in toward the BOTTOM to become the "ground plane" the pixel twin lives
   and plays on (the illustrated/active zone), while the hero reading area up top
   stays clean negative space. Subtle node-dots sit at intersections in that band
   for blueprint character. If you notice the grid consciously up top, it's too much. */
body::before{
  content:"";position:fixed;inset:0;z-index:0;pointer-events:none;
  background-image:
    radial-gradient(rgba(16,19,28,.085) 1.4px, transparent 1.9px),
    linear-gradient(to right, rgba(16,19,28,.04) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(16,19,28,.04) 1px, transparent 1px);
  background-size:80px 80px;
  background-position:40px 40px, 0 0, 0 0;   /* dots centred in the cells */
  -webkit-mask-image:linear-gradient(to bottom, transparent 0%, transparent 30%, rgba(0,0,0,.45) 64%, rgba(0,0,0,.9) 100%);
          mask-image:linear-gradient(to bottom, transparent 0%, transparent 30%, rgba(0,0,0,.45) 64%, rgba(0,0,0,.9) 100%);
}
body::after{ /* paper grain */
  content:"";position:fixed;inset:0;pointer-events:none;opacity:.03;z-index:9;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* artboard crop marks — the logo's frame, blown up to page scale */
.artboard{position:fixed;inset:14px;pointer-events:none;z-index:1}
.artboard::before,.artboard::after{
  content:"";position:absolute;width:22px;height:22px;border:2px solid var(--ink-faint);
}
.artboard::before{top:0;left:0;border-right:none;border-bottom:none}
.artboard::after{bottom:0;right:0;border-left:none;border-top:none}

.wrap{position:relative;z-index:3;height:100%;display:flex;flex-direction:column;
  padding:clamp(20px,4vw,44px);max-width:1240px;margin:0 auto}

/* header */
header{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}
.mark{font-weight:500;font-size:16px;letter-spacing:.01em;white-space:nowrap;flex:none}
.mark .cursor{color:var(--red);font-weight:700;animation:blink 1.1s steps(1) infinite;margin-left:1px}
/* RA monogram glyph next to the name — ink follows the theme, bar stays brand red */
.mark .glyph{display:inline-block;width:1.15em;height:1.15em;vertical-align:-0.2em;margin-right:8px}
.mark .glyph .ink{fill:currentColor}
.mark .glyph .bar{fill:var(--red)}
nav{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end;align-items:center}
/* icon buttons (Email, LinkedIn) — sharp, borderless, shimmer blooms on hover.
   The icon shapes carry a --base-colored plate so the shimmer shows AROUND the
   glyph, not through it. */
nav .ib{position:relative;overflow:hidden;width:40px;height:40px;display:inline-flex;
  align-items:center;justify-content:center;color:var(--ink-soft);text-decoration:none;transition:color .15s}
nav .ib:hover{color:var(--ink)}
nav .ib pixel-canvas{position:absolute;inset:0;z-index:0;pointer-events:none}
nav .ib .icn{position:relative;z-index:1;width:20px;height:20px;display:block}
nav .ib .icn svg{width:100%;height:100%;display:block;stroke:currentColor;fill:none;
  stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter}
nav .ib .icn svg .plate{fill:var(--base)}
nav .ib .icn svg .dot{fill:currentColor;stroke:none}
/* pixel-art icons: filled cells (not strokes); plate still reads --base so the
   shimmer shows AROUND the glyph, not through it */
nav .ib .icn svg.pxico{fill:currentColor;stroke:none}
/* mute toggle: a real <button> styled like an icon button; speaker-on/off swap */
nav button.ib{appearance:none;-webkit-appearance:none;background:transparent;border:0;padding:0;font:inherit;cursor:pointer}
nav .ib.mute .spk-off{display:none}
nav .ib.mute.muted .spk-on{display:none}
nav .ib.mute.muted .spk-off{display:block}
/* CTA — always-black primary, sharp corners, shimmer + scramble on hover.
   --ink/--base invert per theme so it stays high-contrast in light AND dark. */
nav .cta{position:relative;overflow:hidden;display:inline-flex;align-items:center;justify-content:center;
  background:var(--ink);color:var(--base);border:1.5px solid var(--ink);border-radius:0;padding:8px 16px;
  font-size:13.5px;font-weight:500;letter-spacing:.02em;text-decoration:none;--cta-fg:var(--base)}
nav .cta pixel-canvas{position:absolute;inset:0;z-index:0;pointer-events:none}
nav .cta .cta-scramble{position:relative;z-index:1;display:inline-block}

/* hero */
main{flex:1;display:flex;flex-direction:column;justify-content:center;max-width:1100px;
  padding-bottom:clamp(150px,24vh,210px)}
.herohead{display:flex;align-items:flex-start;justify-content:space-between;gap:36px;flex-wrap:wrap}
h1{font-weight:500;font-size:clamp(2.1rem,5.2vw,4.2rem);line-height:1.06;letter-spacing:-.015em;
  flex:1 1 420px;max-width:780px}
h1 em{font-family:var(--pixel);font-style:normal;font-weight:500;color:var(--red);
  font-size:1.0em;letter-spacing:0}
.sub{font-size:clamp(1rem,1.8vw,1.18rem);line-height:1.65;color:var(--ink-soft);
  max-width:56ch;margin-top:28px}
.sub b{color:var(--ink);font-weight:500}
/* status block — top-right of the headline row */
.status{margin-top:14px;display:flex;flex-direction:column;gap:6px;
  font-family:var(--mono);font-size:11px;letter-spacing:.08em;color:var(--ink-faint);
  text-align:right;white-space:nowrap;flex-shrink:0;text-transform:uppercase}
.status .lbl{display:block;margin-bottom:2px}
.status b{color:var(--ink);font-weight:500;font-size:13px;letter-spacing:.04em;text-transform:none}

/* load choreography */
.rise{opacity:0;transform:translateY(14px);animation:rise .9s cubic-bezier(.2,.7,.2,1) forwards}
@keyframes rise{to{opacity:1;transform:none}}
.d1{animation-delay:.05s}.d2{animation-delay:.16s}.d3{animation-delay:.3s}
.d4{animation-delay:.44s}.d5{animation-delay:.58s}
@keyframes blink{50%{opacity:.15}}

/* ── floor + stage ── */
#floorline{position:fixed;left:0;right:0;bottom:var(--floor);height:1px;z-index:1;
  background:linear-gradient(90deg,transparent,var(--line) 10%,var(--line) 90%,transparent)}
#stage{position:fixed;inset:0;z-index:4;pointer-events:none;overflow:hidden}
/* PAN-CAM (mobile follow-cam): #world holds the whole scene and translates with the camera */
#world{position:absolute;top:0;left:0;height:100%;will-change:transform}
#follow-pill{position:absolute;left:50%;bottom:calc(var(--floor) + 12px);transform:translateX(-50%) translateY(8px);
  z-index:6;font-family:var(--mono);font-size:12px;letter-spacing:.04em;color:var(--base);background:var(--ink);
  padding:7px 14px;border:0;display:flex;align-items:center;gap:7px;opacity:0;pointer-events:none;transition:.18s;cursor:pointer;white-space:nowrap}
#follow-pill.show{opacity:1;pointer-events:auto;transform:translateX(-50%) translateY(0)}
#follow-pill .dot{width:7px;height:7px;border-radius:50%;background:var(--red);animation:pulse 1.2s infinite}
@keyframes pulse{50%{opacity:.3}}
.world-edge{position:absolute;top:0;bottom:0;width:42px;z-index:3;pointer-events:none;display:none}
.world-edge.l{left:0;background:linear-gradient(90deg,rgba(250,248,248,.85),transparent)}
.world-edge.r{right:0;background:linear-gradient(270deg,rgba(250,248,248,.85),transparent)}

/* fixtures (injected by main.js) */
.fixture{position:absolute;bottom:var(--floor);pointer-events:auto;
  image-rendering:pixelated;-webkit-user-drag:none;user-select:none}
.fixture img{display:block;height:100%;width:auto;image-rendering:pixelated;-webkit-user-drag:none}
.fixlabel{position:absolute;top:100%;left:50%;transform:translateX(-50%);margin-top:14px;
  font-family:var(--mono);font-size:10px;letter-spacing:.08em;color:var(--ink-faint);
  white-space:nowrap;text-align:center}
.fixture.zone-hot .fixlabel{color:var(--red)}
.fixture.zone-hot img{filter:drop-shadow(0 0 0.5px var(--red)) drop-shadow(0 0 6px rgba(240,52,27,.35))}
/* hammock twists when the cursor tips him out of his nap */
#fix-hammock.tip{animation:hammocktip .52s ease}
@keyframes hammocktip{0%,100%{transform:none}35%{transform:rotate(-6deg)}70%{transform:rotate(4deg)}}
#fix-camera{cursor:pointer}
#fix-camera .lens{position:absolute;border-radius:50%;background:var(--red);opacity:0;
  width:15%;height:15%;left:40%;top:33%;transition:opacity .2s}
#fix-camera.live .lens{opacity:1;box-shadow:0 0 7px 1px rgba(240,52,27,.7)}
#fix-camera:hover .fixlabel,#fix-camera:focus-visible .fixlabel{color:var(--ink)}

/* twin (injected by main.js) */
#twin{position:absolute;left:0;top:0;pointer-events:auto;cursor:grab;touch-action:none;
  user-select:none;-webkit-user-select:none;will-change:transform;z-index:5}
#twin.grabbed{cursor:grabbing}
#twin.grabbed .bob{transform-origin:50% 4%}   /* he hangs from where you hold him */
#twin[data-pose="fall"] .bob{transform-origin:50% 50%}   /* the tumble strip is center-registered — spin around the canvas/body center */
#twin .flip{width:100%;height:100%;transform-origin:50% 100%}
#twin .bob{width:100%;height:100%;transform-origin:50% 100%;position:relative;
  transform:scaleX(var(--sx,1)) scaleY(var(--sy,1)) rotate(var(--rot,0deg))}
/* NO transition on transform: the rAF loop writes --rot/--sx/--sy every frame,
   so a CSS transition double-interpolates and causes the warp-on-drop + swing smear. */
#twin .pose{position:absolute;inset:auto 0 0 0;height:100%;display:flex;align-items:flex-end;
  justify-content:center;opacity:0;transition:opacity .12s linear;pointer-events:none}
#twin .pose.on{opacity:1}
#twin .pose img{height:100%;width:auto;image-rendering:pixelated;-webkit-user-drag:none}
#twin .pose img.blink{position:absolute;left:50%;bottom:0;transform:translateX(-50%);opacity:0}
#twin .pose img.expr{position:absolute;left:50%;bottom:0;transform:translateX(-50%);opacity:0;transition:opacity .12s linear}
/* dazed birds — tiny pixel seagulls circling the head, shown only when he's dizzy */
.dazed{position:absolute;left:50%;top:9%;width:0;height:0;z-index:3;pointer-events:none;
  opacity:0;transform:translateX(-50%);transition:opacity .15s}
.dazed.on{opacity:1}
.dazed .db{position:absolute;left:0;top:0;animation:circle 1.5s linear infinite}
.dazed .db:nth-child(2){animation-delay:-.5s}
.dazed .db:nth-child(3){animation-delay:-1s}
.dazed .db i{display:block;width:2px;height:2px;background:rgba(16,19,28,.82);
  box-shadow:2px -2px rgba(16,19,28,.82),4px 0 rgba(16,19,28,.82),-2px -2px rgba(16,19,28,.82),-4px 0 rgba(16,19,28,.82);
  animation:flap .36s steps(2,end) infinite}
@keyframes circle{0%{transform:translate(14px,0)}25%{transform:translate(0,-10px)}
  50%{transform:translate(-14px,0)}75%{transform:translate(0,10px)}100%{transform:translate(14px,0)}}
@keyframes flap{0%,100%{transform:scaleY(1)}50%{transform:scaleY(.4)}}
#twin .pose.anim{width:100%;background-repeat:no-repeat;background-position-y:bottom;image-rendering:pixelated}
/* laptop steam while vibe-coding */
/* laptop steam — centered on the laptop (translateX keeps left% exact at any twin size) */
.smoke{position:absolute;left:56%;bottom:44%;width:30px;height:52px;transform:translateX(-50%);
  opacity:0;transition:opacity .25s;pointer-events:none}
.smoke.on{opacity:.9}
.smoke span{position:absolute;bottom:0;left:50%;width:9px;height:9px;border-radius:50%;
  background:rgba(16,19,28,.42);animation:puff 1.9s ease-out infinite}
.smoke span:nth-child(2){animation-delay:.63s;left:30%}
.smoke span:nth-child(3){animation-delay:1.26s;left:66%}
@keyframes puff{0%{transform:translateY(0) scale(.5);opacity:0}
  22%{opacity:.66}100%{transform:translateY(-44px) scale(1.85);opacity:0}}
#shadow{position:absolute;width:52px;height:13px;border-radius:50%;z-index:4;
  background:radial-gradient(closest-side,rgba(16,19,28,.28),transparent);will-change:transform,opacity}

/* ── TLV window — day/night follows Roy's real clock; light shaft is baked
   into the artwork (day=warm sun, night=cool moon) ── */
#window{position:absolute;z-index:1;pointer-events:none;overflow:hidden;image-rendering:pixelated}
/* the src is a 26-cell strip; #window is sized to ONE cell (in layout) and clips it, while
   CSS pans one cell at a time. It's a seamless ping-pong (built by win2strip.py) so it ends
   exactly where it started — no loop pop, even though the day clouds/plane drift one way. */
#window img{display:block;height:100%;width:auto;image-rendering:pixelated;
  will-change:transform;animation:winpan 5s steps(14) infinite}
@keyframes winpan{to{transform:translateX(-100%)}}
/* the sky life (clouds/plane by day, moon/stars by night) is baked INTO the window
   artwork and now gently animates — the old .winsky CSS overlay was removed (Roy). */
/* gentle page dusk at TLV night */
body{transition:background-color 1.4s ease}
body.night{background-color:#F1EEF1;--grid:rgba(16,19,28,.065);--dot:rgba(16,19,28,.13)}

/* ── pixel viewfinder (camera on): hand skeleton + gesture tips ── */
#vf{position:absolute;width:168px;z-index:6;pointer-events:none;
  background:rgba(255,255,255,.78);border:2px solid var(--ink)}
#vf canvas{display:block;width:168px;height:126px}
#vf .c{position:absolute;width:10px;height:10px;border:3px solid var(--red)}
#vf .c.tl{left:-5px;top:-5px;border-right:none;border-bottom:none}
#vf .c.tr{right:-5px;top:-5px;border-left:none;border-bottom:none}
#vf .c.bl{left:-5px;bottom:-5px;border-right:none;border-top:none}
#vf .c.br{right:-5px;bottom:-5px;border-left:none;border-top:none}
#vf .vfled{position:absolute;right:6px;top:6px;width:7px;height:7px;background:var(--red);
  animation:blink 1.1s steps(1) infinite}
#vf .vftip{font-family:var(--pixel);font-size:15px;line-height:1;text-align:center;
  color:var(--ink);padding:5px 0 4px;border-top:2px solid var(--ink);background:#fff}
#vf.searching canvas{opacity:.3}
#vf.searching::after{content:"show your hand";position:absolute;left:0;right:0;top:54px;
  text-align:center;font-family:var(--pixel);font-size:15px;color:var(--ink-soft)}

/* click-knock feedback ring (also fires from camera air-tap) */
.knockring{position:absolute;width:8px;height:8px;border-radius:50%;border:2px solid var(--red);
  pointer-events:none;transform:translate(-50%,-50%);animation:knockpulse .6s ease-out forwards;z-index:7}
@keyframes knockpulse{
  0%{opacity:1;transform:translate(-50%,-50%) scale(.4)}
  100%{opacity:0;transform:translate(-50%,-50%) scale(5)}
}

/* hand cursor (gesture mode) */
.handdot{position:absolute;left:0;top:0;width:24px;height:24px;z-index:7;pointer-events:none;
  border:2.5px solid var(--red);border-radius:50%;background:rgba(240,52,27,.12);
  transition:background .12s ease,opacity .25s ease;will-change:transform}
.handdot.pinch{background:var(--red)}
.handdot.lost{opacity:.25}
.handdot.tap{background:var(--red);transform-origin:center;animation:tapflash .2s ease}
@keyframes tapflash{0%{box-shadow:0 0 0 0 rgba(240,52,27,.5)}100%{box-shadow:0 0 0 16px rgba(240,52,27,0)}}

/* speech bubble */
.bubble{position:absolute;left:50%;bottom:103%;transform:translateX(-50%);
  font-family:var(--pixel);font-size:17px;line-height:1;white-space:nowrap;
  background:#fff;color:var(--ink);border:2px solid var(--ink);border-radius:7px;
  padding:6px 10px 4px;opacity:0;transition:opacity .16s ease;pointer-events:none;z-index:6}
.bubble::after{content:"";position:absolute;left:50%;top:100%;transform:translateX(-50%);
  border:5px solid transparent;border-top-color:var(--ink)}
.bubble.show{opacity:1}
/* grumpier bubble when he's been tossed a few times (still friendly, just a red edge) */
.bubble.cranky{border-color:var(--red);color:var(--red)}
.bubble.cranky::after{border-top-color:var(--red)}
#twin[data-pose="nap"] .bubble{bottom:52%}
.bubble.snore{animation:snore 2s ease-in-out infinite;background:transparent;border-color:transparent;
  color:var(--ink-soft);font-size:18px}
.bubble.snore::after{display:none}
@keyframes snore{0%,100%{transform:translateX(-50%) translateY(2px) scale(.88);opacity:.4}
  50%{transform:translateX(-50%) translateY(-3px) scale(1.1);opacity:1}}

/* misc chrome */
.privacy{position:fixed;right:clamp(18px,4vw,44px);bottom:calc(var(--floor) + 14px);z-index:3;
  font-size:10.5px;letter-spacing:.06em;color:var(--ink-faint)}
.mono{font-family:var(--mono)}
.foot{position:fixed;bottom:calc(clamp(16px,3vw,28px) + env(safe-area-inset-bottom));z-index:3;font-size:11px;
  letter-spacing:.08em;color:var(--ink-soft)}
.foot.l{left:calc(clamp(20px,4vw,44px) + env(safe-area-inset-left))}
.foot.r{right:calc(clamp(20px,4vw,44px) + env(safe-area-inset-right))}
.noscript{position:fixed;left:50%;bottom:120px;transform:translateX(-50%);text-align:center;
  font-family:var(--mono);font-size:12px;color:var(--ink-soft)}

@media (max-width:860px){ .fixlabel{display:none} }
@media (max-width:640px){
  :root{--floor:78px}
  nav{gap:8px;flex-wrap:nowrap} nav .ib{width:36px;height:36px} nav .cta{padding:7px 13px;font-size:12.5px}
  .foot{display:none} .privacy{bottom:calc(var(--floor) + 8px)}
  /* stack the hero at the TOP (not vertically centred) so it never drifts down into the
     fixed floor scene; reserve room at the bottom for the twin + fixtures */
  main{padding-top:6px;padding-bottom:230px;justify-content:flex-start}
  .herohead{flex-direction:column;gap:16px}
  /* h1's desktop flex-basis (420px) becomes 420px of empty HEIGHT in this column layout —
     that's what pushed the status + copy onto the hammock/grill. Size it to its text. */
  h1{flex:0 0 auto;max-width:100%}
  .status{text-align:left;align-self:flex-start;margin-top:6px}
  .sub{margin-top:18px}
  #window{display:none}   /* collides with hero copy on small screens; dusk tint still tells the time */
}
/* very narrow phones (≤430px): drop the whole nav to its own row under the name so
   nothing orphan-wraps, and shave the hero footprint so the copy clears the scene */
@media (max-width:430px){
  header{flex-wrap:wrap}
  nav{flex-basis:100%;justify-content:flex-start;margin-top:2px}
  h1{font-size:clamp(1.95rem,8vw,2.4rem)}
  .herohead{gap:12px}
  .sub{font-size:.95rem;line-height:1.5;margin-top:14px}
}
/* SHORT / LANDSCAPE viewports (≤600px tall): 50/50 vertical split — hero owns the
   top half, the scene (capped by the engine) owns the bottom half. Drives landscape
   phones AND short desktop windows, regardless of width. */
@media (max-height:600px){
  :root{--floor:56px}   /* tighter floor gap so the capped twin isn't tiny on landscape */
  .wrap{padding-top:12px;padding-bottom:12px}
  main{justify-content:flex-start;padding-top:2px;padding-bottom:0}
  .herohead{flex-direction:column;gap:6px}
  h1{flex:0 0 auto;max-width:100%;font-size:clamp(1.4rem,4vw,2rem);line-height:1.08}
  /* keep the status block on landscape (Roy), but compact it to one row so the
     headline + status + sub still fit the top half */
  .status{flex-direction:row;flex-wrap:wrap;gap:2px 16px;text-align:left;align-self:flex-start;margin-top:4px}
  .status .lbl{display:none}
  .sub{margin-top:8px;font-size:.9rem;line-height:1.4;max-width:62ch}
  .foot{display:none}
  #window{display:none}
}
@media (prefers-reduced-motion:reduce){
  .rise{animation-duration:.01s}
  .chip i,.mark .cursor{animation:none}
  #twin .bob{transition:none}
  #vf .vfled{animation:none}
  #window img{animation:none}
  .dazed{display:none}
  body{transition:none}
  .pixel-burst{display:none}
}

/* pixel-burst — brand pixel-explosion on every click (js/click-burst.js).
   Per-square direction/distance arrive as --tx/--ty inline; size + color set in JS. */
.pixel-burst{
  position:fixed;pointer-events:none;z-index:9999;
  will-change:transform,opacity;
  animation:pixelBurst 380ms ease-out forwards;
}
@keyframes pixelBurst{
  from{transform:translate(0,0);opacity:1}
  to{transform:translate(var(--tx,0),var(--ty,0));opacity:0}
}
