:root { color-scheme: dark; --bg:#080c20; --text:#f1f5ff; --muted:#b5c2d8; --accent:#79e5ff;
  --header-h:94px; --travel:80vh;
  --panel-w:min(880px, calc(100vw - 48px)); --panel-h:350px;
  --c0:#79e5ff; --c1:#bca4ff; --c2:#ff9bbb; --c3:#ff9a6a; --c4:#ffd184; --c5:#8af2b7; }
* { box-sizing:border-box; }
body { margin:0; background:radial-gradient(ellipse at 20% 25%,#44206b55,transparent 50%),radial-gradient(ellipse at 80% 40%,#065d7844,transparent 50%),radial-gradient(ellipse at 50% 80%,#63294333,transparent 45%),var(--bg); color:var(--text); font:15px/1.65 "Segoe UI","Microsoft YaHei",sans-serif; }
body::before { content:""; position:fixed; inset:0; pointer-events:none; opacity:.16; background-image:linear-gradient(#54738522 1px,transparent 1px),linear-gradient(90deg,#54738522 1px,transparent 1px); background-size:64px 64px; mask-image:linear-gradient(transparent,#000); }
button,a { -webkit-tap-highlight-color:transparent; }
button { font:inherit; color:inherit; cursor:pointer; }
button:focus-visible,a:focus-visible { outline:2px solid var(--accent); outline-offset:5px; }
a { color:inherit; text-decoration:none; }
.skip { position:fixed; z-index:10; top:-60px; left:20px; padding:10px 18px; background:#163246; }
.skip:focus { top:12px; }
.header { position:sticky; top:0; z-index:20; background:color-mix(in srgb,var(--bg) 80%,transparent); backdrop-filter:blur(8px); max-width:1440px; margin:auto; min-height:94px; padding:22px 5%; display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid #90cddd22; gap:20px; }
.brand { display:flex; align-items:center; gap:12px; font-size:23px; letter-spacing:4px; font-weight:650; }
.brand-icon { color:var(--accent); font-size:35px; line-height:1; }
.brand-sub { margin-left:10px; padding-left:20px; border-left:1px solid #67839155; font-size:11px; letter-spacing:2px; font-weight:400; color:var(--muted); }
nav { display:flex; gap:6px; }
nav button { border:1px solid transparent; background:transparent; padding:9px 13px; border-radius:5px; font-size:14px; color:var(--muted); }
nav button span { font:10px Consolas,monospace; margin-right:6px; opacity:.8; }
nav button:hover { background:#9ce9ee10; color:var(--text); }
nav button[aria-current] { background:color-mix(in srgb,var(--accent) 14%,transparent); border-color:var(--accent); color:var(--accent); }
/* Depth. With 3D on (html.depth-on), the stage sticks under the header while the page
   scrolls through --travel; app.js turns that scroll into a camera push from the cube
   out to the projected readout. Without 3D the readout simply sits in the page. */
.stage { position:relative; max-width:1440px; margin:auto; overflow:clip; }
.depth-on .depth-track { height:calc(100vh - var(--header-h) + var(--travel)); }
.depth-on .stage { touch-action:pan-y; position:sticky; top:var(--header-h); height:calc(100vh - var(--header-h)); max-width:none; }
.side-caption { position:absolute; z-index:6; top:32px; left:6%; display:grid; gap:8px; max-width:240px; text-shadow:0 1px 3px #000, 0 0 12px #05030f; font-size:12px; letter-spacing:2px; color:var(--muted); pointer-events:none; }
.side-caption span:first-child { font:10px Consolas,monospace; letter-spacing:2px; color:#7996a9; }
.side-caption .identity { color:var(--text); }
.cube-hint { color:var(--accent); }
.cube-hint::before { content:"↗ "; }
.stage-index { position:absolute; z-index:6; top:24px; right:6%; text-shadow:0 0 14px #05030f; font:72px/1 Consolas,monospace; color:#63879766; font-weight:300; pointer-events:none; }
.stage-index small { display:block; text-align:right; font-size:13px; color:var(--muted); margin-top:12px; }
#world, #cube-button, .depth-toggle { display:none; }
.depth-on #world { display:block; position:absolute; inset:0; z-index:1; width:100%; height:100%; pointer-events:none; }
.depth-on #cube-button { display:block; position:absolute; z-index:4; left:0; top:0; background:transparent; border:0; border-radius:50%; }
#cube-button:hover { background:radial-gradient(circle,color-mix(in srgb,var(--accent) 8%,transparent),transparent 65%); }
.cube-fallback { font-size:140px; color:var(--accent); }
.readout { position:relative; padding-top:24px; }
.depth-on .readout { position:absolute; left:0; right:0; top:0; z-index:3; padding:0; transform-origin:0 0; }
.depth-on .depth-toggle { display:block; position:absolute; z-index:6; right:6%; bottom:24px; padding:8px 16px; border:1px solid color-mix(in srgb,var(--accent) 60%,transparent); background:#0b1129cc; color:var(--accent); font:12px Consolas,"Microsoft YaHei",monospace; letter-spacing:2px; }
.depth-toggle:hover { border-color:var(--accent); }
/* Music on/off, bottom left (the depth toggle sits bottom right). */
.music-toggle { position:absolute; z-index:6; left:6%; bottom:24px; padding:8px 16px; border:1px solid color-mix(in srgb,var(--accent) 45%,transparent); background:#0b1129cc; color:var(--muted); font:12px Consolas,"Microsoft YaHei",monospace; letter-spacing:2px; }
.music-toggle span { margin-left:6px; }
.music-toggle[aria-pressed="true"] { color:var(--accent); border-color:var(--accent); box-shadow:0 0 12px color-mix(in srgb,var(--accent) 40%,transparent); }
.music-toggle:hover { border-color:var(--accent); }
/* The hand-held light: a ring with four ticks in the complementary colour (--lens, set by
   app.js), following the mouse anywhere on the stage. It is a lens: everything seen
   through it (cube, beam, city, the panel's colours) turns to the complementary hue. The
   system cursor hides; buttons keep a pointer and the panel its text cursor. */
.lens-ring { display:none; }
.depth-on .lens-ring { --lens-r:88px; display:block; position:absolute; z-index:5; left:0; top:0; width:calc(var(--lens-r) * 2); height:calc(var(--lens-r) * 2);
  margin:calc(var(--lens-r) * -1) 0 0 calc(var(--lens-r) * -1); border:1.5px solid var(--lens, #fff); border-radius:50%; pointer-events:none; opacity:0;
  -webkit-backdrop-filter:hue-rotate(180deg) saturate(1.25); backdrop-filter:hue-rotate(180deg) saturate(1.25);
  box-shadow:0 0 14px color-mix(in srgb,var(--lens, #fff) 70%,transparent), inset 0 0 18px color-mix(in srgb,var(--lens, #fff) 35%,transparent); }
.lens-ring::before { content:""; position:absolute; inset:-9px; pointer-events:none;
  background:
    linear-gradient(var(--lens, #fff),var(--lens, #fff)) center top / 1.5px 12px no-repeat,
    linear-gradient(var(--lens, #fff),var(--lens, #fff)) center bottom / 1.5px 12px no-repeat,
    linear-gradient(var(--lens, #fff),var(--lens, #fff)) left center / 12px 1.5px no-repeat,
    linear-gradient(var(--lens, #fff),var(--lens, #fff)) right center / 12px 1.5px no-repeat; }
.lens-ring::after { content:""; position:absolute; left:50%; top:50%; width:4px; height:4px; margin:-2px 0 0 -2px; border-radius:50%; background:var(--lens, #fff); }
.stage.lens-on { cursor:none; }
.stage.lens-on button { cursor:pointer; }
.stage.lens-on .readout { cursor:auto; }
.stage.lens-on .track-scene { cursor:grab; }
.depth-toggle span { margin-left:8px; }

/* The panel is a hologram: an elongated hexagon of light (the shape the projector throws),
   translucent, with scanlines and glowing type. Modules don't slide; the new one is
   scanned in over the old one in place. Its outline is an SVG (.hex-frame, hexshape.js)
   following the same corners as the clip-path. --tip and --hex-vertical are read by JS. */
.track-scene { position:relative; z-index:3; height:var(--panel-h); outline:none; touch-action:pan-y; cursor:grab; }
.track-scene.dragging { cursor:grabbing; user-select:none; }
.track { position:absolute; inset:0; }
.panel { --tip:96px; --hex-vertical:0;
  position:absolute; top:0; left:50%; width:var(--panel-w); height:var(--panel-h); margin-left:calc(var(--panel-w) / -2);
  padding:30px calc(var(--tip) + 12px) 26px; overflow-y:auto; scrollbar-width:thin; scrollbar-color:#7389b1 transparent;
  visibility:hidden;
  /* Light enters at the edge facing the projector (the top) and thins out; fine scanlines;
     a faint core; a dark, see-through body so the type stays the brightest thing. */
  background:
    linear-gradient(180deg, color-mix(in srgb,var(--accent) 20%,transparent), transparent 42%),
    repeating-linear-gradient(0deg, transparent 0 2px, color-mix(in srgb,var(--accent) 6%,transparent) 2px 3px),
    radial-gradient(ellipse 60% 70% at 50% 45%, color-mix(in srgb,var(--accent) 9%,transparent), transparent 75%),
    linear-gradient(180deg, #08112ca8, #040919c0);
  clip-path:polygon(var(--tip) 0, calc(100% - var(--tip)) 0, 100% 50%, calc(100% - var(--tip)) 100%, var(--tip) 100%, 0 50%); }
.panel.active, .panel.scan-out { visibility:visible; }
.panel > * { position:relative; }
.panel > svg.hex-frame { position:absolute; inset:0; width:100%; height:100%; pointer-events:none; }
.hex-glow { fill:none; stroke:var(--accent); stroke-width:44; opacity:.2; filter:blur(14px); }  /* clipped: only the inner half shows */
.hex-outline { fill:none; stroke:color-mix(in srgb,var(--accent) 70%,#fff); stroke-width:1.5; filter:drop-shadow(0 0 4px var(--accent)) drop-shadow(0 0 10px var(--accent)); }
/* Light running round the frame: two bright streaks chasing each other. */
.hex-flow { fill:none; stroke:#fff; stroke-width:2.2; stroke-linecap:round; stroke-dasharray:7 43; filter:drop-shadow(0 0 4px var(--accent)) drop-shadow(0 0 10px var(--accent)); animation:hex-flow 6s linear infinite; }
@keyframes hex-flow { to { stroke-dashoffset:-100; } }
.hex-inner { fill:none; stroke:var(--accent); stroke-width:1; stroke-dasharray:2 7; opacity:.55; }
.hex-bead { fill:#fff; filter:drop-shadow(0 0 3px var(--accent)) drop-shadow(0 0 8px var(--accent)); }
/* A slow band of light sweeping down the projection. */
.panel.active::before { content:""; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(transparent, color-mix(in srgb,var(--accent) 10%,transparent) 45%, color-mix(in srgb,#fff 6%,transparent) 50%, transparent) 0 -45% / 100% 28% no-repeat;
  animation:sweep 6.5s linear infinite; }
@keyframes sweep { from { background-position:0 -45%; } 60%, to { background-position:0 145%; } }
/* Type is light too: bright, with a glow; headlines with a hint of colour fringing. */
.panel h1, .panel h2 { color:#f4f9ff; text-shadow:-1px 0 #ff4f8b40, 1px 0 #4ff3ff40, 0 0 22px color-mix(in srgb,var(--accent) 45%,transparent); }
.panel h3, .panel dt { color:#eef4ff; text-shadow:0 0 12px color-mix(in srgb,var(--accent) 30%,transparent); }
.panel p, .panel dd, .panel .panel-bottom { color:color-mix(in srgb,#d3def2 85%,var(--accent)); text-shadow:0 0 10px color-mix(in srgb,var(--accent) 22%,transparent); }
.panel .record-label { color:color-mix(in srgb,var(--accent) 75%,#d3def2); }
.panel dd a { color:var(--accent); border-bottom:1px solid color-mix(in srgb,var(--accent) 50%,transparent); }
.panel dd a:hover { border-bottom-color:var(--accent); }
.panel .skill-list>div, .panel .panel-bottom { border-color:color-mix(in srgb,var(--accent) 22%,transparent); }
/* A faint flicker, as projected light does. */
.panel.active > :not(.hex-frame) { animation:flicker 7s infinite; }
@keyframes flicker { 0%, 93%, 95%, 100% { opacity:1; } 94% { opacity:.72; } 96.5% { opacity:.88; } }
.panel-top { display:flex; align-items:center; gap:14px; color:var(--accent); font:11px Consolas,monospace; letter-spacing:3px; margin:0 0 18px; padding:0 0 12px; text-shadow:0 0 8px color-mix(in srgb,var(--accent) 60%,transparent);
  background:repeating-linear-gradient(90deg, color-mix(in srgb,var(--accent) 50%,transparent) 0 1px, transparent 1px 8px) left bottom / 100% 5px no-repeat; }
.panel-top > span:first-child { flex:none; }
.panel-top::after { content:""; flex:1; height:1px; background:linear-gradient(90deg, color-mix(in srgb,var(--accent) 60%,transparent), transparent); order:1; }
.panel-symbol { order:2; font-size:22px; line-height:1; }
h1,h2 { margin:0 0 16px; font-size:36px; font-weight:600; line-height:1.35; letter-spacing:1px; text-shadow:0 0 18px color-mix(in srgb,var(--accent) 30%,transparent); }
h3 { margin:4px 0; font-size:18px; font-weight:500; }
p { margin:0; color:var(--muted); font-size:15px; line-height:1.8; }
.accent { color:var(--accent); text-shadow:0 0 18px color-mix(in srgb,var(--accent) 45%,transparent); }
.placeholder { color:var(--text); margin:0 3px; }
.panel-bottom { margin-top:22px; padding-top:14px; border-top:1px solid #8fbec322; display:flex; justify-content:space-between; align-items:center; gap:12px; font-size:13px; color:var(--muted); }
.text-button { padding:0; background:none; border:0; color:var(--accent); font-size:14px; white-space:nowrap; }
.text-button:hover { text-decoration:underline; }
.text-button span { margin-left:10px; }
.record-label { color:var(--muted); font:11px/1.5 Consolas,monospace; letter-spacing:2px; }
.project-list { display:grid; grid-template-columns:1fr 1fr; gap:26px; }
.project-list>div+div { border-left:1px solid #8fbec322; padding-left:26px; }
.skill-list { margin:0; }
.skill-list>div { display:grid; grid-template-columns:120px 1fr; border-bottom:1px solid #8fbec322; padding:12px 0; font-size:15px; }
dt { color:var(--text); } dd { margin:0; color:var(--muted); }

/* Scan switch: a line of light runs down the panel; above it the new module (scan-in),
   below it the old one still (scan-out). app.js sets --scan-ms and --scan-delay. */
@property --scan { syntax:'<percentage>'; inherits:true; initial-value:110%; }
.panel.scan-in, .panel.scan-out { animation:scan var(--scan-ms, .45s) linear var(--scan-delay, 0s) both; }
.panel.scan-in { z-index:2; mask-image:linear-gradient(#000 calc(var(--scan) - 2%), transparent calc(var(--scan) + 2%)); }
.panel.scan-out { z-index:1; mask-image:linear-gradient(transparent calc(var(--scan) - 2%), #000 calc(var(--scan) + 2%)); }
.panel.scan-in::after { content:""; position:absolute; left:0; right:0; top:calc(var(--scan) - 2%); height:2px; pointer-events:none;
  background:var(--accent); box-shadow:0 0 14px 3px var(--accent); }
.panel.scan-in > :not(.hex-frame) { animation:none; }
@keyframes scan { from { --scan:-6%; } to { --scan:106%; } }

/* Six-segment rail: one segment per module, a cursor that follows the track. */
.rail-bar { display:flex; justify-content:center; align-items:center; gap:18px; padding:22px 16px 6px; }
.rail-bar button { flex:none; background:#102332; border:1px solid #63879766; border-radius:50%; width:36px; height:36px; }
.rail-bar button:hover { color:var(--accent); border-color:var(--accent); }
.rail { position:relative; display:grid; grid-template-columns:repeat(6, 1fr); gap:6px; width:min(360px, 60vw); height:6px; }
.rail i { display:block; background:var(--seg); opacity:.28; clip-path:polygon(4px 0, 100% 0, calc(100% - 4px) 100%, 0 100%); }
.rail i:nth-child(1) { --seg:var(--c0); } .rail i:nth-child(2) { --seg:var(--c1); } .rail i:nth-child(3) { --seg:var(--c2); }
.rail i:nth-child(4) { --seg:var(--c3); } .rail i:nth-child(5) { --seg:var(--c4); } .rail i:nth-child(6) { --seg:var(--c5); }
.rail-cursor { position:absolute; top:-5px; bottom:-5px; left:calc(var(--at, 0) * (100% + 6px) / 6); width:calc((100% - 30px) / 6); border:1px solid var(--accent); box-shadow:0 0 12px var(--accent), inset 0 0 8px color-mix(in srgb,var(--accent) 50%,transparent); }
#page-status { margin:8px 0 30px; text-align:center; font:11px Consolas,"Microsoft YaHei",monospace; letter-spacing:2px; color:var(--muted); }

footer { position:relative; display:flex; justify-content:space-between; align-items:center; max-width:1296px; margin:0 auto; border-top:1px solid #90cddd22; padding:18px 3%; color:#8fa7ba; font-size:10px; letter-spacing:1px; }
.footer-star { color:var(--accent); margin-left:16px; }
.error-note { text-align:center; color:var(--accent); }
@media(max-width:760px) {
  :root { --header-h:108px; --panel-h:530px; }
  .header { flex-direction:column; padding:18px 14px 10px; gap:14px; }
  .brand { font-size:20px; } .brand-sub { font-size:10px; }
  nav { justify-content:center; width:100%; gap:2px; }
  nav button { flex:1; padding:8px 2px; font-size:13px; } nav button span { display:none; }
  .side-caption { top:14px; left:16px; } .side-caption span:not(.cube-hint) { display:none; }
  .stage-index { top:12px; right:16px; font-size:34px; } .stage-index small { font-size:10px; }
  .panel { --tip:56px; --hex-vertical:1; padding:calc(var(--tip) + 2px) 22px calc(var(--tip) - 10px);
    clip-path:polygon(50% 0, 100% var(--tip), 100% calc(100% - var(--tip)), 50% 100%, 0 calc(100% - var(--tip)), 0 var(--tip)); }
  .panel-top { margin:0 0 14px; padding:0 0 10px; }
  h1,h2 { font-size:28px; } .panel-bottom { align-items:flex-start; flex-wrap:wrap; }
  .project-list { grid-template-columns:1fr; gap:14px; } .project-list>div+div { border-left:0; border-top:1px solid #8fbec322; padding:12px 0 0; }
  .skill-list>div { grid-template-columns:96px 1fr; }
  footer { margin:0 22px; padding:17px 0; } footer>span:last-child { display:none; }
  .depth-on .depth-toggle { right:16px; top:76px; bottom:auto; padding:6px 10px; font-size:11px; }
  .music-toggle { left:16px; top:76px; bottom:auto; padding:6px 10px; font-size:11px; }
}
@media(prefers-reduced-motion:reduce) { *,*::before,*::after { transition:none !important; animation:none !important; } }
