/* =========================================================================
   AMIRHOSSEIN.dev — cyberpunk portfolio styles
   Palette + layout tokens live in :root — change a colour, change the mood.
   ========================================================================= */

:root{
  /* Read back by the boot guard in index.html to prove this stylesheet actually
     loaded. If it can't see `ok`, css/ (and therefore js/ and vendor/) is not
     next to index.html, and it says so instead of showing a blank page.
     DO NOT REMOVE. */
  --sentinel:ok;

  --cyan:#00f5ff;
  --pink:#ff006e;
  --purple:#bf00ff;
  --gold:#ffbe0b;
  --mint:#00ff9d;

  --bg:#04040a;
  --bg2:#08080f;
  --glass:rgba(9,10,20,.78);
  --line:rgba(0,245,255,.28);
  --line-hot:rgba(0,245,255,.6);

  --txt:#e9ecff;
  --txt-dim:#8d93b5;

  --r:10px;
  --pad:clamp(14px,2vw,22px);
  --panel-w:clamp(320px,29vw,430px);
  --map:240px;              /* minimap size — overridden per breakpoint */

  --f-head:'Orbitron',system-ui,sans-serif;
  --f-body:'Rajdhani',system-ui,sans-serif;
}

*{margin:0;padding:0;box-sizing:border-box}
html,body{height:100%;overflow:hidden}

body{
  background:var(--bg);
  color:var(--txt);
  font-family:var(--f-body);
  cursor:none;
  -webkit-font-smoothing:antialiased;
}

button,a{font-family:inherit;color:inherit;cursor:none}
::selection{background:var(--cyan);color:#000}

/* ─────────────────────────────────────────── cursor */
.cursor,.cursor-dot{position:fixed;top:0;left:0;pointer-events:none;z-index:9999;will-change:transform}
.cursor{
  width:26px;height:26px;border:1.5px solid var(--cyan);border-radius:50%;
  box-shadow:0 0 14px rgba(0,245,255,.55),inset 0 0 10px rgba(0,245,255,.18);
  transition:width .18s,height .18s,border-color .18s,opacity .2s;
}
.cursor.big{width:44px;height:44px;border-color:var(--pink);box-shadow:0 0 22px rgba(255,0,110,.5)}
.cursor-dot{width:5px;height:5px;border-radius:50%;background:var(--pink);box-shadow:0 0 10px var(--pink)}
.cursor.hidden,.cursor-dot.hidden{opacity:0}

/* ─────────────────────────────────────────── fx overlays */
.scanlines{
  position:fixed;inset:0;pointer-events:none;z-index:60;
  background:repeating-linear-gradient(0deg,rgba(0,0,0,0) 0 2px,rgba(0,0,0,.10) 2px 4px);
}
.scanlines.slim{background:repeating-linear-gradient(0deg,rgba(0,0,0,0) 0 3px,rgba(0,0,0,.055) 3px 4px)}
.vignette{
  position:fixed;inset:0;pointer-events:none;z-index:59;
  background:radial-gradient(ellipse at 50% 48%,transparent 42%,rgba(0,0,0,.62) 100%);
}

/* ─────────────────────────────────────────── loader */
.loader{
  position:fixed;inset:0;z-index:9000;display:grid;place-items:center;
  background:radial-gradient(circle at 50% 42%,#0b0b1b 0%,#04040a 62%);
  transition:opacity .7s ease,visibility .7s ease;
}
.loader.gone{opacity:0;visibility:hidden}
.loader-inner{text-align:center;padding:0 20px}

.brand{
  font-family:var(--f-head);font-weight:900;letter-spacing:.10em;
  font-size:clamp(1.9rem,7vw,4.6rem);
  text-shadow:0 0 26px rgba(0,245,255,.55),0 0 60px rgba(0,245,255,.25);
}
.brand span{color:var(--pink);text-shadow:0 0 26px rgba(255,0,110,.6)}
.brand-sub{
  margin-top:14px;font-family:var(--f-head);color:var(--cyan);
  letter-spacing:.42em;font-size:clamp(.55rem,1.6vw,.85rem);
  animation:blink 1.5s steps(1,end) infinite;
}
@keyframes blink{0%,58%{opacity:1}59%,100%{opacity:.22}}

.load-track{
  width:min(420px,76vw);height:3px;margin:30px auto 12px;
  background:rgba(255,255,255,.08);border-radius:3px;overflow:hidden;
}
.load-track i{
  display:block;height:100%;width:0;border-radius:3px;
  background:linear-gradient(90deg,var(--cyan),var(--pink));
  box-shadow:0 0 16px var(--cyan);
  transition:width .35s ease;
}
.load-stat{font-family:var(--f-head);font-size:.62rem;letter-spacing:.3em;color:var(--txt-dim)}

/* ─────────────────────────────────────────── canvas + hud shell */
#scene{position:fixed;inset:0;width:100%;height:100%;display:block}

.hud{position:fixed;inset:0;z-index:70;pointer-events:none;opacity:1;transition:opacity .5s}
.hud.hidden{opacity:0}
.hud>*{pointer-events:auto}

/* ─────────────────────────────────────────── top bar */
.topbar{
  position:absolute;top:0;left:0;right:0;
  display:flex;align-items:center;gap:var(--pad);
  padding:14px clamp(14px,2.4vw,30px);
  background:linear-gradient(180deg,rgba(4,4,10,.92),rgba(4,4,10,0));
}
.logo h1{
  font-family:var(--f-head);font-weight:700;letter-spacing:.08em;
  font-size:clamp(.95rem,2.1vw,1.5rem);line-height:1;
}
.logo h1 span{color:var(--pink)}
.logo-sub{
  display:block;margin-top:4px;font-family:var(--f-head);
  font-size:.48rem;letter-spacing:.3em;color:var(--cyan);opacity:.85;
}

.nav{display:flex;gap:clamp(4px,1vw,14px);margin:0 auto;flex-wrap:wrap;justify-content:center}
.nav-item{
  position:relative;display:flex;align-items:center;gap:7px;
  background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.08);
  border-radius:999px;padding:7px 14px;
  font-family:var(--f-head);font-size:clamp(.52rem,1.15vw,.7rem);
  letter-spacing:.18em;color:var(--txt-dim);
  transition:.22s;
}
.nav-key{
  display:grid;place-items:center;width:15px;height:15px;border-radius:4px;
  background:rgba(0,245,255,.12);color:var(--cyan);font-size:.52rem;
}
.nav-item:hover{color:var(--cyan);border-color:var(--line-hot);box-shadow:0 0 18px rgba(0,245,255,.22)}
.nav-item.active{
  color:#04040a;background:var(--cyan);border-color:var(--cyan);
  box-shadow:0 0 24px rgba(0,245,255,.5);
}
.nav-item.active .nav-key{background:rgba(0,0,0,.25);color:#04040a}

.topright{display:flex;gap:8px}
.chip{
  background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.10);
  border-radius:999px;padding:7px 13px;
  font-family:var(--f-head);font-size:.52rem;letter-spacing:.16em;
  color:var(--cyan);transition:.2s;white-space:nowrap;
}
/* Touch targets: `touch-action:manipulation` removes the 300ms tap delay and
   stops a double-tap being read as zoom. Every tappable control needs it or a
   phone feels broken even when the handler is wired correctly.
   NOTE: the panel close button's real class is `.panel-x` (v11 first shipped
   these rules against a `.panel-close` that does not exist anywhere in the
   markup, so the close button kept its unusable 24px hit area). */
.chip,.nav-item,.panel-x,.btn,.sprint-btn,.card,.joy,.look-pad{
  touch-action:manipulation;
}
/* Apple's minimum comfortable target is 44px; these were as small as 24px. */
body.is-mobile .chip{padding:11px 15px;font-size:.6rem;min-height:42px;min-width:42px}
body.is-mobile .nav-item{padding:10px 14px;font-size:.6rem;min-height:42px}
body.is-mobile .panel-x{width:42px;height:42px;font-size:1.05rem}
/* the head is the row the close button lives in — give it room to grow */
body.is-mobile .panel-head{padding:11px var(--pad)}
.chip:hover{border-color:var(--line-hot);box-shadow:0 0 16px rgba(0,245,255,.25)}
.chip.off{color:var(--txt-dim)}

/* ─────────────────── radar / minimap
   v7: bigger (176 -> 240 desktop) and it scales down through breakpoints
   instead of vanishing on phones. --map is the single knob. */
.radar{
  position:absolute;left:clamp(10px,2vw,26px);bottom:clamp(10px,2vw,26px);
  background:var(--glass);border:1px solid var(--line);border-radius:var(--r);
  padding:10px;backdrop-filter:blur(14px);
  box-shadow:0 0 30px rgba(0,245,255,.10);
  z-index:2;
  transition:opacity .3s;
}
#minimap{
  display:block;border-radius:6px;
  width:var(--map);height:var(--map);
}
.radar-meta{
  display:flex;flex-direction:column;gap:2px;margin-top:8px;
  font-family:var(--f-head);font-size:.5rem;letter-spacing:.14em;color:var(--txt-dim);
}
.radar-meta span:nth-child(1){color:var(--gold)}
.radar-meta span:nth-child(3){color:var(--cyan)}
.radar-meta span:nth-child(4){color:var(--txt-dim);opacity:.6;font-size:.44rem}
/* dim the radar while a panel is open so it never fights for attention */
body.panel-open .radar{opacity:.5}

/* ─────────────────────────────────────────── panels */
.panels{position:absolute;inset:0;pointer-events:none;z-index:3}

.panel{
  position:absolute;top:clamp(78px,10vh,104px);right:clamp(12px,2vw,26px);
  width:var(--panel-w);max-height:calc(100vh - clamp(150px,20vh,190px));
  overflow-y:auto;overflow-x:hidden;
  /* Touch scrolling inside a panel: momentum scrolling on WebKit, and
     `touch-action:pan-y` so the browser knows a vertical drag here is a SCROLL
     and not a gesture for something else. Without this the panel body was
     unscrollable on a phone. */
  -webkit-overflow-scrolling:touch;
  touch-action:pan-y;
  overscroll-behavior:contain;
  background:var(--glass);border:1px solid var(--line);border-radius:var(--r);
  backdrop-filter:blur(20px);
  box-shadow:0 0 44px rgba(0,245,255,.12),inset 0 0 44px rgba(0,245,255,.03);
  opacity:0;visibility:hidden;transform:translateX(26px) scale(.985);
  transition:opacity .32s ease,transform .32s cubic-bezier(.2,.9,.25,1),visibility .32s;
  pointer-events:none;
}
.panel.open{opacity:1;visibility:visible;transform:none;pointer-events:auto}
.panel::before{
  content:'';position:sticky;top:0;display:block;height:3px;
  background:linear-gradient(90deg,var(--cyan),var(--pink),var(--purple));
  box-shadow:0 0 18px var(--cyan);z-index:2;
}

.panel-head{
  position:sticky;top:3px;z-index:2;
  display:flex;align-items:center;gap:10px;
  padding:14px var(--pad);border-bottom:1px solid var(--line);
  background:linear-gradient(180deg,rgba(9,10,20,.96),rgba(9,10,20,.84));
  backdrop-filter:blur(10px);
}
.panel-head h2{
  font-family:var(--f-head);font-size:.78rem;font-weight:500;
  letter-spacing:.22em;color:var(--cyan);
}
.panel-status{
  margin-left:auto;display:flex;align-items:center;gap:6px;
  font-family:var(--f-head);font-size:.48rem;letter-spacing:.14em;color:var(--txt-dim);
}
.dot{width:7px;height:7px;border-radius:50%;animation:pulse 2s ease-in-out infinite}
.dot.online{background:var(--mint);box-shadow:0 0 10px var(--mint)}
.dot.gaming{background:var(--pink);box-shadow:0 0 10px var(--pink)}
.dot.anime{background:var(--purple);box-shadow:0 0 10px var(--purple)}
.dot.project{background:var(--gold);box-shadow:0 0 10px var(--gold)}
.dot.contact{background:var(--cyan);box-shadow:0 0 10px var(--cyan)}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.45;transform:scale(.82)}}

.panel-x{
  background:none;border:1px solid rgba(255,255,255,.12);border-radius:6px;
  width:24px;height:24px;color:var(--txt-dim);font-size:.7rem;line-height:1;transition:.2s;
}
.panel-x:hover{color:var(--pink);border-color:var(--pink);box-shadow:0 0 14px rgba(255,0,110,.35)}

.panel-body{padding:var(--pad)}
.panel::-webkit-scrollbar{width:5px}
.panel::-webkit-scrollbar-track{background:transparent}
.panel::-webkit-scrollbar-thumb{background:var(--line-hot);border-radius:3px}

/* ─────────────────────────────────────────── about */
.idcard{display:flex;gap:16px;align-items:flex-start}
.avatar-wrap{position:relative;width:84px;height:84px;flex:0 0 auto}
.avatar-ring{
  position:absolute;inset:-6px;border:1.5px solid var(--cyan);border-radius:50%;
  border-top-color:transparent;border-right-color:rgba(255,0,110,.7);
  animation:spin 4.5s linear infinite;
  box-shadow:0 0 22px rgba(0,245,255,.35);
}
@keyframes spin{to{transform:rotate(360deg)}}
.avatar{
  display:grid;place-items:center;width:84px;height:84px;border-radius:50%;
  background:linear-gradient(135deg,var(--purple),var(--pink));
  font-family:var(--f-head);font-weight:900;font-size:1.5rem;color:#fff;
  text-shadow:0 0 18px rgba(255,255,255,.45);
}
.idmeta h3{font-family:var(--f-head);font-size:1.02rem;letter-spacing:.02em}
.tagline{color:var(--cyan);font-size:.8rem;margin:4px 0 12px;opacity:.9}

.statgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px 12px}
.statcell{display:flex;flex-direction:column}
.sk{font-family:var(--f-head);font-size:.42rem;letter-spacing:.22em;color:var(--txt-dim)}
.sv{font-family:var(--f-head);font-size:.86rem;font-weight:700;color:var(--gold)}

.sub{
  font-family:var(--f-head);font-size:.62rem;letter-spacing:.24em;
  color:var(--pink);margin:20px 0 9px;
}
.bio{font-size:.86rem;line-height:1.68;color:var(--txt-dim);margin-bottom:9px}

.skills{display:flex;flex-direction:column;gap:11px}
.skill-top{
  display:flex;justify-content:space-between;
  font-family:var(--f-head);font-size:.5rem;letter-spacing:.14em;
  color:var(--txt-dim);margin-bottom:5px;
}
.bar{height:4px;background:rgba(255,255,255,.07);border-radius:3px;overflow:hidden}
.bar i{
  display:block;height:100%;width:var(--w);border-radius:3px;
  background:linear-gradient(90deg,var(--cyan),var(--purple));
  box-shadow:0 0 12px rgba(0,245,255,.5);
  animation:grow 1.1s cubic-bezier(.2,.9,.25,1) both;
}
@keyframes grow{from{width:0}}

/* ─────────────────────────────────────────── cards */
.grid2{display:grid;grid-template-columns:repeat(2,1fr);gap:11px}
.card{
  display:block;background:rgba(0,245,255,.035);
  border:1px solid rgba(0,245,255,.16);border-radius:8px;
  padding:13px;text-decoration:none;transition:.22s;
}
.card:hover{
  transform:translateY(-3px);
  border-color:var(--line-hot);
  box-shadow:0 8px 26px rgba(0,245,255,.18);
  background:rgba(0,245,255,.06);
}
.gicon{display:block;font-size:1.5rem;margin-bottom:7px}
.card h4{font-family:var(--f-head);font-size:.62rem;letter-spacing:.1em;margin-bottom:3px}
.card p{font-size:.72rem;color:var(--txt-dim)}

.hintline{
  font-size:.74rem;color:var(--txt-dim);line-height:1.55;
  border-left:2px solid var(--line-hot);padding-left:10px;margin-bottom:13px;
}

/* ─────────────────── art cards (anime 2:3 · games 3:4) */
.card-grid{gap:13px}
.card.art{
  padding:9px;background:rgba(255,255,255,.02);
  border-color:color-mix(in srgb,var(--accent) 40%,transparent);
}
.card.art:hover{
  border-color:var(--accent);
  box-shadow:0 8px 30px color-mix(in srgb,var(--accent) 32%,transparent);
}
.art-slot{
  position:relative;border-radius:6px;overflow:hidden;line-height:0;
  background:#0a0a14;
}
/* keep the exact source aspect while the canvas paints */
.art-slot.ratio-23{aspect-ratio:600/900}
.art-slot.ratio-34{aspect-ratio:600/800}
.art-canvas{
  width:100%;height:100%;display:block;object-fit:cover;
  filter:saturate(1.05) contrast(1.03);
}
.art-slot.has-photo::after{
  content:'PHOTO';position:absolute;top:6px;right:6px;
  font-family:var(--f-head);font-size:.36rem;letter-spacing:.14em;
  color:var(--accent);background:rgba(4,4,10,.72);
  border:1px solid color-mix(in srgb,var(--accent) 55%,transparent);
  border-radius:3px;padding:2px 5px;
}
.card.art figcaption{padding:9px 3px 2px}
.card.art figcaption h4{color:var(--accent);font-size:.58rem;line-height:1.3}
.card.art figcaption p{font-size:.7rem;color:var(--txt);opacity:.8;margin-top:2px}
.card.art figcaption small{display:block;margin-top:4px;font-size:.64rem;color:var(--txt-dim)}

/* ─────────────────── profile slot (500x500) */
.avatar-slot{
  width:84px;height:84px;border-radius:50%;overflow:hidden;
  background:#0a0a14;position:relative;line-height:0;
  box-shadow:0 0 22px rgba(191,0,255,.28);
}
.avatar-slot .art-canvas{width:100%;height:100%;object-fit:cover}



/* ─────────────────────────────────────────── projects */
.proj{
  border:1px solid color-mix(in srgb,var(--accent) 42%,transparent);
  border-radius:8px;padding:15px;background:rgba(0,245,255,.04);
}
.proj-head{display:flex;align-items:center;gap:9px;margin-bottom:9px}
.proj-head h4{font-family:var(--f-head);font-size:.92rem;color:var(--accent);letter-spacing:.06em}
.ver{
  font-family:var(--f-head);font-size:.46rem;letter-spacing:.1em;
  background:var(--pink);color:#fff;border-radius:4px;padding:3px 7px;
}
.proj p{font-size:.82rem;line-height:1.62;color:var(--txt-dim);margin-bottom:11px}
.plats{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:13px}
.plat{
  font-family:var(--f-head);font-size:.46rem;letter-spacing:.1em;
  border:1px solid var(--line-hot);color:var(--cyan);
  border-radius:4px;padding:3px 8px;background:rgba(0,245,255,.08);
}
.btn{
  display:inline-block;font-family:var(--f-head);font-size:.58rem;letter-spacing:.14em;
  border:1px solid var(--pink);color:var(--pink);border-radius:6px;
  padding:9px 15px;text-decoration:none;transition:.22s;
}
.btn:hover{background:var(--pink);color:#fff;box-shadow:0 0 22px rgba(255,0,110,.45)}

.ghline{
  display:flex;align-items:center;gap:9px;margin-top:15px;padding-top:14px;
  border-top:1px solid var(--line);text-decoration:none;
  font-family:var(--f-head);font-size:.6rem;letter-spacing:.1em;color:var(--txt-dim);
  transition:.2s;
}
.ghline:hover{color:var(--cyan)}
.ghi{font-size:1.1rem;color:var(--cyan)}

/* ─────────────────────────────────────────── hints + lock note */
.hint{
  position:absolute;bottom:clamp(12px,2vw,24px);left:50%;transform:translateX(-50%);
  display:flex;gap:clamp(10px,2vw,26px);align-items:center;flex-wrap:wrap;
  justify-content:center;max-width:min(760px,92vw);
  padding:9px 16px;border-radius:999px;
  background:rgba(4,4,10,.62);border:1px solid rgba(255,255,255,.07);
  backdrop-filter:blur(10px);
  transition:opacity .4s;
  z-index:1;                 /* stays under the panels */
}
.hint.dim{opacity:.28}
/* while reading a panel, fade the key hints right down */
body.panel-open .hint{opacity:.12}
.keys{display:flex;align-items:center;gap:5px}
.k{
  display:grid;place-items:center;min-width:26px;height:26px;padding:0 7px;
  border:1px solid var(--line-hot);border-radius:5px;
  font-family:var(--f-head);font-size:.55rem;font-weight:700;color:var(--cyan);
  background:rgba(0,245,255,.07);
  box-shadow:0 0 12px rgba(0,245,255,.18),inset 0 0 10px rgba(0,245,255,.07);
}
.k.wide{min-width:48px}
.kl{font-family:var(--f-head);font-size:.44rem;letter-spacing:.16em;color:var(--txt-dim);margin-left:3px}

/* ─────────────────── lock note
   BUGFIX (v7): this used to sit dead-centre at top:50% with a z-index above
   the panels. When you pressed Esc to scroll the ABOUT text, it reappeared
   right on top of the paragraphs. It now lives BELOW the panel in z-order,
   sits above the hint bar rather than centre-screen, and hides itself
   entirely while any panel is open. */
.locknote{
  position:absolute;left:50%;
  bottom:calc(clamp(12px,2vw,24px) + 56px);
  transform:translateX(-50%);
  display:flex;align-items:center;gap:11px;
  padding:11px 20px;border-radius:999px;
  background:rgba(4,4,10,.82);border:1px solid var(--line-hot);
  backdrop-filter:blur(12px);
  font-family:var(--f-head);font-size:.58rem;letter-spacing:.2em;color:var(--cyan);
  box-shadow:0 0 30px rgba(0,245,255,.22);
  animation:floatUp 2.6s ease-in-out infinite;
  transition:opacity .3s,visibility .3s;
  z-index:1;                 /* panels are z-index 3 — never covered again */
  white-space:nowrap;max-width:92vw;
}
.locknote.hidden{opacity:0;visibility:hidden}
/* any open panel suppresses it outright */
body.panel-open .locknote{opacity:0;visibility:hidden}
@keyframes floatUp{
  0%,100%{transform:translateX(-50%) translateY(0)}
  50%{transform:translateX(-50%) translateY(-6px)}
}
.pulse{
  width:9px;height:9px;border-radius:50%;background:var(--pink);
  box-shadow:0 0 12px var(--pink);animation:pulse 1.4s ease-in-out infinite;
}

/* ─────────────────────────────────────────── help overlay */
.help{
  position:fixed;inset:0;z-index:9500;display:grid;place-items:center;
  background:rgba(2,2,6,.72);backdrop-filter:blur(6px);
  opacity:0;visibility:hidden;transition:.28s;
}
.help.open{opacity:1;visibility:visible}
.help-card{
  width:min(430px,90vw);max-height:86vh;overflow-y:auto;
  background:var(--glass);border:1px solid var(--line-hot);border-radius:var(--r);
  padding:24px;box-shadow:0 0 54px rgba(0,245,255,.2);
}
.help-card h3{
  font-family:var(--f-head);font-size:.66rem;letter-spacing:.24em;
  color:var(--cyan);margin:16px 0 9px;
}
.help-card h3:first-child{margin-top:0}
.help-card table{width:100%;border-collapse:collapse}
.help-card td{padding:5px 0;font-size:.82rem;color:var(--txt-dim)}
.help-card td b{color:var(--txt);font-family:var(--f-head);font-size:.6rem;letter-spacing:.08em}
.help-card p{font-size:.8rem;line-height:1.6;color:var(--txt-dim)}
.help-card .btn{margin-top:18px;width:100%;text-align:center}

.noscript{
  position:fixed;inset:0;z-index:99999;display:grid;place-items:center;
  background:var(--bg);color:var(--cyan);font-family:var(--f-head);
  letter-spacing:.14em;text-align:center;padding:30px;
}

/* ─────────────────── boot error card
   Shown instead of a black screen when the scene cannot start. */
.boot-error{
  position:fixed;inset:0;z-index:99998;display:none;place-items:center;
  background:rgba(2,2,6,.94);backdrop-filter:blur(8px);padding:22px;
}
.boot-error.show{display:grid}
.boot-error-card{
  width:min(520px,94vw);
  background:var(--glass);border:1px solid rgba(255,92,122,.5);
  border-radius:var(--r);padding:26px;
  box-shadow:0 0 54px rgba(255,92,122,.22);
}
.boot-error-card h3{
  font-family:var(--f-head);font-size:.74rem;letter-spacing:.22em;
  color:#ff5c7a;margin-bottom:14px;
}
.boot-error-card pre{
  white-space:pre-wrap;word-break:break-word;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:.72rem;line-height:1.6;color:var(--txt-dim);
  background:rgba(0,0,0,.35);border:1px solid rgba(255,255,255,.07);
  border-radius:6px;padding:12px;margin-bottom:16px;
}
.be-hint{font-size:.82rem;line-height:1.6;color:var(--txt-dim);margin-bottom:14px}
.be-hint.small{font-size:.74rem;margin:14px 0 0;opacity:.75}
.boot-error-card .btn{display:block;text-align:center;width:100%;margin-bottom:9px}
.boot-error-card .btn.alt{
  background:transparent;color:var(--cyan);border:1px solid var(--line-hot);
}

/* ═══════════════════════════ MOBILE CONTROLS ═══════════════════════════
   BUGFIX (v13): the real cause of "top buttons and exit button don't work by
   touch, and I can't scroll the tabs".

   v11 gave .look-pad z-index:40 (below .hud's 70) and thought that was enough.
   It wasn't: #mobileBar was a CHILD of #hud, and because #hud carries
   `z-index:70` it forms a STACKING CONTEXT. A descendant's z-index is only ever
   compared with its siblings inside that context — never with the ancestor. So
   the full-screen .look-pad (40) was ranked against .topbar (auto) and .panels
   (3) and won, staying on top of the entire interface and eating every tap and
   every scroll gesture.

   The markup fix is in index.html: #mobileBar now lives OUTSIDE #hud, as a
   top-level element. Only then does 40 < 70 actually mean "below the HUD".
   `position:fixed` because it is no longer inside a full-screen ancestor. */
.mobile-bar{position:fixed;inset:0;pointer-events:none}
.mobile-bar.hidden{display:none}

/* drag-to-look surface: full screen, but UNDER every interactive HUD element */
.look-pad{
  position:absolute;inset:0;pointer-events:auto;
  touch-action:none;-webkit-user-select:none;user-select:none;
  background:transparent;
  z-index:40;                /* .hud is 70 — buttons win, empty space loses */
}

.joy-wrap{
  position:absolute;left:max(16px,env(safe-area-inset-left));
  bottom:max(20px,env(safe-area-inset-bottom));
  display:flex;flex-direction:column;align-items:center;gap:8px;
  pointer-events:auto;
  z-index:90;                /* above the HUD so a panel can't bury the stick */
}
.joy{
  position:relative;width:126px;height:126px;border-radius:50%;
  background:radial-gradient(circle,rgba(0,245,255,.10),rgba(4,4,10,.62) 70%);
  border:1.5px solid var(--line-hot);
  box-shadow:0 0 26px rgba(0,245,255,.22),inset 0 0 26px rgba(0,245,255,.10);
  touch-action:none;-webkit-user-select:none;user-select:none;
  transition:border-color .18s,box-shadow .18s;
}
.joy.active{
  border-color:var(--pink);
  box-shadow:0 0 34px rgba(255,0,110,.35),inset 0 0 30px rgba(255,0,110,.14);
}
.joy-ring{
  position:absolute;inset:16px;border-radius:50%;
  border:1px dashed rgba(0,245,255,.30);
}
.joy-cross::before,.joy-cross::after{
  content:'';position:absolute;background:rgba(0,245,255,.22);
}
.joy-cross::before{left:50%;top:14%;width:1px;height:72%;transform:translateX(-50%)}
.joy-cross::after{top:50%;left:14%;height:1px;width:72%;transform:translateY(-50%)}
.joy-knob{
  position:absolute;left:50%;top:50%;width:52px;height:52px;border-radius:50%;
  transform:translate(-50%,-50%);
  background:radial-gradient(circle at 35% 30%,#8ffcff,var(--cyan) 55%,#0a6d78);
  box-shadow:0 0 22px rgba(0,245,255,.65),inset 0 -4px 10px rgba(0,0,0,.35);
  transition:transform .06s linear;
}
.joy.active .joy-knob{
  background:radial-gradient(circle at 35% 30%,#ffd0e2,var(--pink) 55%,#7a0034);
  box-shadow:0 0 26px rgba(255,0,110,.7),inset 0 -4px 10px rgba(0,0,0,.35);
}
.joy-label{
  font-family:var(--f-head);font-size:.42rem;letter-spacing:.24em;color:var(--txt-dim);
}
.sprint-btn{
  font-family:var(--f-head);font-size:.5rem;letter-spacing:.16em;
  color:var(--cyan);background:rgba(4,4,10,.66);
  border:1px solid var(--line-hot);border-radius:999px;padding:9px 18px;
  box-shadow:0 0 16px rgba(0,245,255,.2);transition:.18s;
}
.sprint-btn.on{
  color:#04040a;background:var(--gold);border-color:var(--gold);
  box-shadow:0 0 24px rgba(255,190,11,.55);
}

/* ═══════════════ mobile layout ═══════════════
   The joystick owns the bottom-left corner, so the radar moves to the right
   side just under the top bar. It stays VISIBLE on phones now (v6 hid it) —
   just smaller. */
body.is-mobile .hint{display:none}
body.is-mobile .cursor,body.is-mobile .cursor-dot{display:none}
body.is-mobile{cursor:auto}
body.is-mobile .topbar{padding:10px 12px}
body.is-mobile .radar{
  left:auto;right:8px;bottom:auto;
  top:calc(env(safe-area-inset-top) + 92px);
  padding:7px;
}
body.is-mobile .radar-meta{font-size:.44rem}

/* ░░ SAFE-AREA PADDING ONLY ░░
   The full-screen geometry for a phone panel lives in the `@media
   (max-width:720px)` block below, NOT here. That is deliberate, and it is the
   fix for "the shelf is still small":

   `body.is-mobile` is added by JS after IS_MOBILE detection (user agent or
   `pointer:coarse`). If that detection says desktop — a narrow desktop window, a
   Windows touchscreen laptop, DevTools device mode without touch emulation, or
   simply JS not having reached that line yet — the class never arrives and every
   rule hanging off it is dead. The old layout then fell through to the plain
   `.panel` rule in the 720px block, which still said `max-height:62vh`, and the
   shelf came back cramped. Screen SIZE is a fact of the viewport; "is this a
   phone" is a guess. Size decides the layout; the guess only hides the walk
   controls, which is the one thing it is actually needed for. */
body.is-mobile .panel{
  /* keep the last card clear of the home-swipe bar */
  padding-bottom:max(10px,env(safe-area-inset-bottom));
}
/* A phone panel is modal in practice: the radar and the walk controls would
   only sit on top of what you are reading. The joystick is z-index 90 —
   ABOVE the HUD — so it must be hidden explicitly or it covers the cards.
   `body.panel-open` is set by UI.openPanel() for every device, so this works
   even when the is-mobile guess fails. */
body.is-mobile.panel-open .radar{opacity:0;pointer-events:none}
body.is-mobile.panel-open .joy-wrap{opacity:0;pointer-events:none}
body.is-mobile.panel-open .look-pad{pointer-events:none}

@media (max-width:900px) and (orientation:landscape){
  .joy{width:104px;height:104px}
  .joy-knob{width:44px;height:44px}
  .sprint-btn{padding:7px 14px}
  body.is-mobile .radar{top:calc(env(safe-area-inset-top) + 58px)}
}

/* ═══════════════ breakpoints — the map scales, never disappears ═══════════ */
@media (max-width:1180px){
  :root{--panel-w:clamp(300px,34vw,380px);--map:200px}
}

@media (max-width:900px){
  :root{--map:170px}
  .topbar{flex-wrap:wrap;gap:9px;padding:11px 14px}
  .nav{order:3;width:100%;margin:0}
  .nav-item{padding:6px 11px}
}

@media (max-width:720px){
  :root{--map:140px}
  body{cursor:auto}
  .cursor,.cursor-dot{display:none}

  /* ░░ PANELS FILL THE PHONE ░░
     This was a 62vh strip pinned to the bottom. On a 6" screen that left the
     anime cards ~90px tall — you had to scroll a shelf inside a box smaller
     than its own contents. A panel on a phone is the ONLY thing you are
     looking at, so it takes the whole screen.

     Top edge clears the HUD bar — which WRAPS to two rows under 900px, hence
     104px rather than the desktop 78px — plus the notch. Bottom edge goes to
     the very bottom; the joystick is hidden while a panel is open (see the
     body.is-mobile.panel-open rules above) so nothing is buried.

     This lives in a SIZE query, not on body.is-mobile, because the class comes
     from a JS user-agent/pointer guess that is wrong on narrow desktop windows,
     touchscreen laptops and DevTools device mode without touch emulation. The
     viewport width is a fact. */
  .panel{
    top:calc(env(safe-area-inset-top) + 104px);
    left:0;right:0;bottom:0;width:100%;
    max-height:none;
    border-radius:16px 16px 0 0;
  }
  .panel.open{transform:none}
  .hint{display:none}
  .grid2{grid-template-columns:1fr 1fr}
  .locknote{font-size:.52rem;padding:10px 15px;text-align:center}

  /* With the full height available the shelf can show real cards. */
  .card-grid{gap:11px}
  .card.art{padding:7px}
  .card.art figcaption h4{font-size:.62rem}
  .card.art figcaption p{font-size:.74rem}
}

/* Landscape phone: height is scarce, width is not. A full-screen panel here
   would hide the scene for no benefit, so it takes the right-hand side and the
   city stays visible behind it. Size-based for the same reason as above. */
@media (max-width:900px) and (max-height:520px) and (orientation:landscape){
  .panel{
    top:calc(env(safe-area-inset-top) + 56px);bottom:0;
    left:auto;right:0;
    width:min(460px,58vw);max-height:none;
    border-radius:14px 0 0 0;
  }
  .grid2{grid-template-columns:1fr 1fr}
}

@media (max-width:430px){
  :root{--map:118px}
  .idcard{flex-direction:column;align-items:center;text-align:center}
  .statgrid{width:100%}
  .grid2{grid-template-columns:1fr}
  .radar{padding:6px}
  .radar-meta{font-size:.4rem;letter-spacing:.1em}
}

/* very short screens: the radar is the first thing to go */
@media (max-height:420px){
  .radar{display:none}
}

/* ═══════════════════════════ CARD LIGHTBOX ═══════════════════════════
   Tap a card (in a panel) or a stand (in the gallery ring) and it flies to the
   centre of the screen, full size, with a glitch + neon transition. Closing
   flies it back to exactly where it came from.

   HOW THE FLIGHT WORKS: JS measures the card's real on-screen rectangle, then
   sets --fx/--fy/--fs (translate + scale) so the lightbox STARTS as an exact
   overlay of that thumbnail, and animates to identity. On close it animates back
   to the same numbers. That is a FLIP animation — it only ever animates
   transform and opacity, both of which are GPU-composited, so it stays smooth on
   a phone where animating width/height/top/left would not.

   The whole thing sits at z-index:9500 — above the HUD (70) and the joystick
   (90) but below the loader (9000)… deliberately above it too, since the loader
   is gone by then, and below the boot-error card (99999) which must always win.
   Not a child of #hud: that element has a z-index and so traps descendants. */
.lightbox{
  position:fixed;inset:0;z-index:9500;
  display:flex;align-items:center;justify-content:center;
  padding:max(14px,env(safe-area-inset-top)) 14px max(14px,env(safe-area-inset-bottom));
  visibility:hidden;pointer-events:none;
  /* the flight start/end transform, written by JS */
  --fx:0px;--fy:0px;--fs:1;--fr:0deg;
}
.lightbox.open{visibility:visible;pointer-events:auto}

.lb-scrim{
  position:absolute;inset:0;
  background:radial-gradient(ellipse at 50% 50%,rgba(8,2,20,.82),rgba(2,2,6,.95));
  backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);
  opacity:0;transition:opacity .34s ease;
}
.lightbox.open .lb-scrim{opacity:1}

/* While the lightbox is up, the 3D look-pad must not receive touches — a drag
   on the zoomed picture would otherwise spin the camera behind it. */
body.lightbox-open .look-pad{pointer-events:none}
body.lightbox-open .joy-wrap{opacity:.25;pointer-events:none;transition:opacity .25s}

.lb-card{
  position:relative;z-index:2;
  display:flex;flex-direction:column;
  width:min(92vw,520px);max-height:min(88vh,900px);
  background:linear-gradient(180deg,rgba(10,10,22,.96),rgba(6,6,14,.98));
  border:1px solid var(--accent,#00f5ff);border-radius:12px;
  box-shadow:
    0 0 0 1px rgba(255,255,255,.05),
    0 0 34px color-mix(in srgb,var(--accent,#00f5ff) 42%,transparent),
    0 24px 70px rgba(0,0,0,.7);
  overflow:hidden;
  /* START of the flight: sitting exactly on top of the thumbnail. JS writes
     --fx/--fy/--fs from the measured rectangle, and --fr as a small random
     tilt so no two pops are identical (a perfectly axis-aligned zoom is what
     made the old one feel mechanical). */
  transform:translate(var(--fx),var(--fy)) scale(var(--fs)) rotate(var(--fr));
  opacity:0;
  transform-origin:top left;
  /* Slight overshoot on arrival: the card passes its final size by a hair and
     settles. Ends at ~.36s, which is when the 260ms glitch has finished — the
     two used to end at different times, which is what read as a stutter. */
  transition:transform .36s cubic-bezier(.22,1.28,.36,1),opacity .18s ease;
  will-change:transform,opacity;
}
/* .flying is added one frame AFTER .open — that ordering is what makes the
   browser interpolate instead of jumping straight to the end state. */
.lightbox.flying .lb-card{
  transform:translate(0,0) scale(1) rotate(0deg);
  opacity:1;
}
/* closing: back down the same path, quicker and without the overshoot, so it
   feels like a dismissal rather than a bounce */
.lightbox.closing .lb-card{
  transform:translate(var(--fx),var(--fy)) scale(var(--fs)) rotate(var(--fr));
  opacity:0;
  transition:transform .26s cubic-bezier(.45,0,.75,.45),opacity .24s ease .02s;
}

/* the artwork: the real card canvas is moved in here */
.lb-art{
  position:relative;flex:0 0 auto;line-height:0;overflow:hidden;
  background:#07070f;
}
.lb-art canvas{
  display:block;width:100%;height:auto;
  max-height:min(62vh,660px);object-fit:contain;
}

/* neon scanline sheet over the art — always on, part of the look */
.lb-scan{
  position:absolute;inset:0;pointer-events:none;
  background:repeating-linear-gradient(0deg,
    rgba(0,0,0,0) 0 2px,rgba(0,0,0,.16) 2px 3px);
  mix-blend-mode:multiply;
}

/* ── THE GLITCH BURST ──────────────────────────────────────────────────────
   Rewritten. The first version fought itself: three `steps()` animations ran
   at once on the card, the art and the overlay, all for 420ms, and the biggest
   one chopped the picture in half with clip-path. The result read as "the card
   is broken", not "the card is materialising".

   The rebuild is layered instead of stacked, and much shorter — 260ms, which is
   about the longest a glitch can run before it stops reading as an artifact and
   starts reading as a bug:

     .lb-rgb    chromatic ghost. Two neon gradients on mix-blend-mode:screen,
                offset in opposite directions. Screen blending ADDS light, so it
                looks like mis-registered colour channels rather than a coloured
                sheet laid on top (which is what the old `opacity:.9` wash was).
     .lb-tear   two thin horizontal bands that rip across the art. This replaces
                the clip-path chop: a band sweeping over the picture reads as a
                signal tear, while cutting the picture in half just looks broken.
     card       one very short brightness/hue kick plus a hard white edge flash,
                so the pop has a "power-on" snap at the front.

   Everything animates only transform / opacity / filter / clip-path, all of
   which the compositor handles, so this stays cheap on a phone. NOTHING here
   animates the card's own transform — that belongs to the FLIP transition and
   two animations on one property would cancel each other out. */
.lb-rgb{
  position:absolute;inset:-2px;pointer-events:none;opacity:0;
  background:
    linear-gradient(97deg,rgba(255,0,110,.55) 0%,rgba(255,0,110,0) 42%),
    linear-gradient(263deg,rgba(0,245,255,.55) 0%,rgba(0,245,255,0) 42%),
    linear-gradient(180deg,rgba(191,0,255,.22),rgba(191,0,255,0) 30%);
  mix-blend-mode:screen;
  will-change:transform,opacity;
}
/* the tear bands live on pseudo-elements so they cost no extra markup */
.lb-art::before,
.lb-art::after{
  content:'';position:absolute;left:-6%;width:112%;pointer-events:none;
  opacity:0;z-index:3;
  background:linear-gradient(90deg,
    rgba(0,245,255,0),rgba(0,245,255,.5) 18%,
    rgba(255,255,255,.85) 50%,
    rgba(255,0,110,.5) 82%,rgba(255,0,110,0));
  mix-blend-mode:screen;
  will-change:transform,opacity;
}
.lb-art::before{height:9%;top:16%}
.lb-art::after{height:5%;top:63%}

.lightbox.glitch .lb-rgb    {animation:lbRgb .26s linear 1}
.lightbox.glitch .lb-art::before{animation:lbTearA .26s ease-out 1}
.lightbox.glitch .lb-art::after {animation:lbTearB .26s ease-out 1}
.lightbox.glitch .lb-card   {animation:lbKick .26s ease-out 1}
.lightbox.glitch .lb-art    {animation:lbShear .26s ease-out 1}

/* Chromatic split: fast, irregular, gone. The uneven timing is deliberate —
   evenly spaced keyframes read as a pulse, uneven ones read as interference. */
@keyframes lbRgb{
  0%  {opacity:.95;transform:translate3d(-9px,-2px,0) scaleX(1.02)}
  18% {opacity:.55;transform:translate3d(7px,3px,0)   scaleX(.99)}
  34% {opacity:.8; transform:translate3d(-5px,1px,0)  scaleX(1.01)}
  52% {opacity:.35;transform:translate3d(4px,-2px,0)  scaleX(1)}
  70% {opacity:.5; transform:translate3d(-2px,0,0)}
  100%{opacity:0;  transform:none}
}
/* The bands sweep in opposite directions and leave. */
@keyframes lbTearA{
  0%  {opacity:0;  transform:translate3d(-14%,0,0) scaleY(2.2)}
  14% {opacity:.95;transform:translate3d(6%,0,0)   scaleY(1)}
  30% {opacity:.5; transform:translate3d(-4%,340%,0)}
  55% {opacity:.75;transform:translate3d(3%,-180%,0) scaleY(1.6)}
  100%{opacity:0;  transform:translate3d(0,60%,0)}
}
@keyframes lbTearB{
  0%  {opacity:0;  transform:translate3d(12%,0,0)}
  20% {opacity:.85;transform:translate3d(-5%,0,0) scaleY(1.8)}
  44% {opacity:.4; transform:translate3d(4%,-260%,0)}
  68% {opacity:.7; transform:translate3d(-3%,200%,0)}
  100%{opacity:0;  transform:translate3d(0,-40%,0)}
}
/* A shear on the ART only — never on the card, whose transform is the FLIP.
   Sub-degree skew plus a couple of pixels is enough to sell a tape snap; the
   old ±9px clip-path chop was an order of magnitude too violent. */
@keyframes lbShear{
  0%  {transform:skewX(-1.6deg) translate3d(-4px,0,0);filter:brightness(1.5) saturate(1.4)}
  22% {transform:skewX(1.1deg)  translate3d(3px,0,0); filter:brightness(.82) saturate(1.7)}
  46% {transform:skewX(-.5deg)  translate3d(-2px,0,0);filter:brightness(1.18) saturate(1.2)}
  72% {transform:skewX(.2deg)   translate3d(1px,0,0); filter:none}
  100%{transform:none;filter:none}
}
/* Power-on snap: a white rim that decays into the card's normal neon glow. */
@keyframes lbKick{
  0%{
    box-shadow:0 0 0 2px rgba(255,255,255,.95),
      0 0 70px color-mix(in srgb,var(--accent,#00f5ff) 95%,transparent),
      0 24px 70px rgba(0,0,0,.7);
    filter:brightness(1.22);
  }
  30%{
    box-shadow:0 0 0 1px color-mix(in srgb,var(--accent,#00f5ff) 80%,transparent),
      0 0 48px color-mix(in srgb,var(--accent,#00f5ff) 70%,transparent),
      0 24px 70px rgba(0,0,0,.7);
    filter:brightness(.96);
  }
  100%{
    box-shadow:0 0 0 1px rgba(255,255,255,.05),
      0 0 34px color-mix(in srgb,var(--accent,#00f5ff) 42%,transparent),
      0 24px 70px rgba(0,0,0,.7);
    filter:none;
  }
}

/* A single bright line that wipes down the art as the card lands. Runs on the
   FLIP, not the glitch, so it reads as the picture being drawn in. */
.lb-wipe{
  position:absolute;left:0;right:0;top:0;height:34%;pointer-events:none;
  opacity:0;z-index:4;
  background:linear-gradient(180deg,
    rgba(0,245,255,0),rgba(0,245,255,.16) 62%,
    rgba(255,255,255,.9) 92%,rgba(255,255,255,0));
  mix-blend-mode:screen;
}
.lightbox.flying .lb-wipe{animation:lbWipe .44s cubic-bezier(.3,.7,.3,1) 1}
@keyframes lbWipe{
  0%  {opacity:0;  transform:translateY(-120%)}
  12% {opacity:1}
  88% {opacity:.7}
  100%{opacity:0;  transform:translateY(340%)}
}

/* text block under the art — scrolls if a note is long */
.lb-meta{
  padding:14px 16px 16px;overflow-y:auto;
  -webkit-overflow-scrolling:touch;touch-action:pan-y;overscroll-behavior:contain;
  border-top:1px solid color-mix(in srgb,var(--accent,#00f5ff) 30%,transparent);
}
.lb-meta h3{
  font-family:var(--f-head);font-size:.92rem;letter-spacing:.1em;
  color:var(--accent,#00f5ff);
  text-shadow:0 0 18px color-mix(in srgb,var(--accent,#00f5ff) 60%,transparent);
  line-height:1.35;
}
.lb-sub{margin-top:5px;font-size:.82rem;color:var(--txt);opacity:.85}
.lb-note{margin-top:7px;font-size:.78rem;color:var(--txt-dim);line-height:1.6}
.lb-tag{
  display:inline-block;margin-top:10px;
  font-family:var(--f-head);font-size:.42rem;letter-spacing:.16em;
  color:var(--accent,#00f5ff);background:rgba(4,4,10,.7);
  border:1px solid color-mix(in srgb,var(--accent,#00f5ff) 55%,transparent);
  border-radius:3px;padding:4px 7px;
}

/* close button: 44px minimum, which is the whole reason the old ✕ was unusable
   by touch. Sits inside .lb-card so it travels with the flight. */
.lb-x{
  position:absolute;top:8px;right:8px;
  width:44px;height:44px;border-radius:50%;
  display:grid;place-items:center;
  font-size:1.15rem;line-height:1;
  color:var(--accent,#00f5ff);
  background:rgba(4,4,10,.78);
  border:1px solid color-mix(in srgb,var(--accent,#00f5ff) 60%,transparent);
  box-shadow:0 0 16px color-mix(in srgb,var(--accent,#00f5ff) 30%,transparent);
  touch-action:manipulation;transition:.18s;
}
.lb-x:hover,.lb-x:focus-visible{
  background:var(--accent,#00f5ff);color:#04040a;outline:none;
  box-shadow:0 0 26px color-mix(in srgb,var(--accent,#00f5ff) 70%,transparent);
}

/* a card you can tap should look tappable — but the OS arrow must NOT
   replace the custom neon cursor over the shelf (that was the bug where
   the Windows cursor popped up over anime/game cards) */
.card.art{cursor:none}
body.is-mobile .card.art{cursor:auto}
.card.art:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ░░ PHONE: THE LIGHTBOX TAKES THE WHOLE SCREEN ░░
   A 460px-wide card centred on a 6" phone left ~8% margins all round and capped
   the art at 52vh, so the picture you tapped to see BIGGER arrived barely larger
   than the thumbnail. Edge to edge instead: the art gets all the room the 2:3
   ratio can use, and the text sits under it and scrolls.

   The FLIP still works untouched — JS measures the card's FINAL rect at open
   time, whatever these rules made it, so the flight is correct at any size. */
@media (max-width:720px){
  .lightbox{padding:0}
  .lb-card{
    width:100vw;max-width:none;
    height:100%;max-height:none;
    border-radius:0;border-left:none;border-right:none;
  }
  /* flex:1 + min-height:0 lets the art take the leftover space and shrink
     properly — without min-height:0 a flex child refuses to go below its
     content size and pushes the caption off the bottom of the screen. */
  .lb-art{
    flex:1 1 auto;min-height:0;
    display:flex;align-items:center;justify-content:center;
  }
  .lb-art canvas{
    width:auto;height:auto;
    max-width:100%;max-height:100%;
    object-fit:contain;
  }
  .lb-meta{
    flex:0 0 auto;max-height:34%;
    padding:12px 16px max(14px,env(safe-area-inset-bottom));
  }
  .lb-meta h3{font-size:.86rem}
  .lb-sub{font-size:.8rem}
  .lb-note{font-size:.76rem}
  /* clear of the notch, and big enough to hit without aiming */
  .lb-x{
    top:max(10px,env(safe-area-inset-top));right:10px;
    width:48px;height:48px;font-size:1.25rem;
  }
}
/* landscape phone: side by side, or the art leaves no room for anything */
@media (max-height:520px) and (orientation:landscape){
  .lightbox{padding:0}
  .lb-card{
    flex-direction:row;
    width:100vw;max-width:none;height:100%;max-height:none;
    border-radius:0;
  }
  .lb-art{flex:1 1 58%;min-width:0;min-height:0;max-width:58%}
  .lb-art canvas{max-height:100%;max-width:100%;width:auto;height:auto}
  .lb-meta{
    flex:1 1 42%;max-height:none;border-top:none;
    padding-left:18px;
    border-left:1px solid color-mix(in srgb,var(--accent,#00f5ff) 30%,transparent);
  }
}
/* Someone who asked for less motion gets the zoom without the seizure fuel. */
@media (prefers-reduced-motion:reduce){
  .lightbox.glitch .lb-rgb,
  .lightbox.glitch .lb-art,
  .lightbox.glitch .lb-card{animation:none}
}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
}
