/* ============================================================
   DANIRVVXX — index
   Concept: a 2000s profile page, shot on NightShot.
   Edit freely. Sections are ordered top to bottom as they appear.
   ============================================================ */

/* ---------- 1. TOKENS ---------------------------------------
   Change these and the whole site follows.                    */
:root{
  --page:#040705;        /* outside the frame            */
  --frame:#060A07;       /* the page surface             */
  --mod:#0D1710;         /* module fill                  */
  --mod-head:#0C1610;    /* module header bar            */
  --btn:#080F0A;         /* contact buttons              */

  --text:#CDEBCF;        /* body copy                    */
  --bright:#D6F2D8;      /* headings, hover              */
  --mid:#7FB98F;         /* secondary                    */
  --dim:#5F8F72;         /* labels, captions             */

  --rule:#24402C;        /* hairlines                    */
  --rule-2:#1E3524;      /* fainter hairlines            */
  --border:#3E6B4E;      /* module borders               */
  --rust:#8C3A22;        /* the only non-green accent    */
  --amber:#B8892A;       /* date stamp only              */

  --mono:'IBM Plex Mono',ui-monospace,Menlo,Consolas,monospace;
  --display:'Archivo Narrow','Arial Narrow',Helvetica,sans-serif;

  --pad:40px;            /* frame inset, desktop         */
}

*{box-sizing:border-box}
html,body{margin:0;padding:0;background:var(--page)}
body{
  font-family:var(--mono);
  color:var(--text);
  font-size:14px;
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
  background:var(--frame);
  min-height:100vh;
  overflow-x:hidden;
}
a{color:var(--text);text-decoration:none}
a:hover{color:var(--bright)}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
:focus-visible{outline:2px solid var(--amber);outline-offset:2px}

/* ---------- 2. MOTION KEYFRAMES -----------------------------
   Tape behaviour only. transform + opacity, never filter —
   filter drops frames in the Instagram in-app browser.        */

@keyframes blink{0%,49%{opacity:1}50%,100%{opacity:0}}

/* Tracking lock — plays once on load, never again. */
@keyframes trackingLock{
  0%{transform:translate3d(-15px,3px,0)}
  18%{transform:translate3d(10px,-2px,0)}
  36%{transform:translate3d(-6px,1px,0)}
  58%{transform:translate3d(3px,0,0)}
  80%{transform:translate3d(-1.5px,0,0)}
  100%{transform:translate3d(0,0,0)}
}
@keyframes tearA{
  0%{transform:translate3d(-100%,0,0);opacity:.9}
  45%{transform:translate3d(14%,0,0);opacity:.8}
  78%{transform:translate3d(-5%,0,0);opacity:.3}
  100%{transform:translate3d(0,0,0);opacity:0}
}
@keyframes tearB{
  0%{transform:translate3d(88%,0,0);opacity:.75}
  50%{transform:translate3d(-11%,0,0);opacity:.55}
  82%{transform:translate3d(4%,0,0);opacity:.2}
  100%{transform:translate3d(0,0,0);opacity:0}
}

/* Focus hunt — lens overshoots sharp then settles. */
@keyframes focusHunt{
  0%{transform:scale(1.022)}
  62%{transform:scale(.9955)}
  100%{transform:scale(1)}
}

/* Scanline crawl — exactly one 5px pattern period, so it loops seamlessly. */
@keyframes crawl{from{transform:translate3d(0,0,0)}to{transform:translate3d(0,-5px,0)}}

/* Head-switch band — the smear at the bottom edge of a VHS frame. */
@keyframes headSwitch{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}

/* Auto-exposure breathing — ~3% drift over 8s. */
@keyframes breathe{0%,100%{opacity:0}45%{opacity:.03}}
@keyframes portraitBreathe{
  0%,100%{transform:scale(1)}
  50%{transform:scale(1.015)}
}
@keyframes portraitRollBar{
  0%{transform:translate3d(0,0,0);opacity:0}
  12%{opacity:.78}
  88%{opacity:.78}
  100%{transform:translate3d(0,100%,0);opacity:0}
}

/* Tape dropout — recurring, randomised 50-70s, one sweep down. */
@keyframes dropoutBand{
  0%{transform:translate3d(0,0,0);opacity:0}
  12%{transform:translate3d(0,12vh,0);opacity:.85}
  72%{transform:translate3d(0,72vh,0);opacity:.7}
  100%{transform:translate3d(0,102vh,0);opacity:0}
}
@keyframes dropoutDip{0%,100%{opacity:0}30%{opacity:.1}}

/* ---------- 3. FULL-SCREEN EFFECT LAYERS -------------------- */
.fx{position:fixed;inset:0;pointer-events:none;z-index:60}
.scanlines{
  top:-10px;bottom:-10px;
  animation:crawl 7s linear infinite;
  background:repeating-linear-gradient(to bottom,
    rgba(0,0,0,0) 0px,rgba(0,0,0,0) 3px,
    rgba(0,0,0,.20) 4px,rgba(0,0,0,.20) 5px);
}
.grain{
  opacity:.20;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='4'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* Vignette built from px-bounded edge gradients, so darkness is a constant
   frame no matter how long the page gets. */
.vignette{
  background:
    linear-gradient(to bottom,rgba(2,4,3,.30) 0,rgba(0,0,0,0) 120px),
    linear-gradient(to top,rgba(2,4,3,.45) 0,rgba(0,0,0,0) 200px),
    linear-gradient(to right,rgba(2,4,3,.58) 0,rgba(0,0,0,0) 64px),
    linear-gradient(to left,rgba(2,4,3,.58) 0,rgba(0,0,0,0) 64px);
}
.exposure{background:#000;opacity:0;animation:breathe 8s ease-in-out infinite;z-index:62}
.headswitch{top:auto;height:4px;bottom:0;overflow:hidden;z-index:63}
.headswitch i{
  position:absolute;inset:0;width:200%;
  animation:headSwitch 1.1s linear infinite;
  background:repeating-linear-gradient(90deg,
    rgba(205,235,207,.20) 0 6px,rgba(0,0,0,.4) 6px 11px,
    rgba(205,235,207,.09) 11px 19px,rgba(0,0,0,.3) 19px 26px);
}
.tear{z-index:64;overflow:hidden}
.tear i{position:absolute;left:0;right:0;display:block;background:rgba(205,235,207,.16)}
.tear i.a{top:22%;height:10px;animation:tearA 420ms ease-out 1 both}
.tear i.b{top:57%;height:6px;animation:tearB 420ms ease-out 1 both}
.tear.done{display:none}
.dropout{z-index:65;overflow:hidden;display:none}
.dropout.on{display:block}
.dropout .dip{position:absolute;inset:0;background:#000;opacity:0;animation:dropoutDip 300ms linear 1 both}
.dropout .band{
  position:absolute;left:0;right:0;top:-8vh;height:26px;opacity:0;
  animation:dropoutBand 300ms linear 1 both;
  background:repeating-linear-gradient(90deg,
    rgba(205,235,207,.22) 0 5px,rgba(0,0,0,.5) 5px 9px,
    rgba(205,235,207,.12) 9px 17px,rgba(0,0,0,.35) 17px 23px);
}

/* ---------- 4. CAMERA CHROME -------------------------------- */
.chrome{position:fixed;inset:0;pointer-events:none;z-index:70}
.bracket{position:absolute;width:26px;height:26px}
.bracket.tl{top:14px;left:14px;border-top:1px solid var(--border);border-left:1px solid var(--border)}
.bracket.tr{top:14px;right:14px;border-top:1px solid var(--border);border-right:1px solid var(--border)}
.bracket.bl{bottom:14px;left:14px;border-bottom:1px solid var(--border);border-left:1px solid var(--border)}
.bracket.br{bottom:14px;right:14px;border-bottom:1px solid var(--border);border-right:1px solid var(--border)}
.osd{
  position:absolute;font-size:10px;letter-spacing:.16em;
  color:var(--mid);font-variant-numeric:tabular-nums;
}
.osd-tl{top:18px;left:52px;display:flex;align-items:center;gap:7px}
.osd-tr{top:18px;right:52px}
.osd-bl{bottom:18px;left:52px}
.osd-br{bottom:18px;right:52px;color:var(--dim)}
.stamp{color:var(--amber)}
.rec-dot{width:6px;height:6px;background:var(--rust);border-radius:50%;animation:blink 1.6s steps(1,end) infinite}

/* ---------- 5. SHELL + LAYOUT ------------------------------- */
.shell{
  position:relative;z-index:1;
  /* Anchored left, not centered: on an ultrawide the nav (absolute, inside
     the shell) would otherwise float mid-viewport with a large empty gap to
     its left. max-width still caps the row, so the columns never stretch. */
  max-width:1180px;margin:0 auto 0 0;
  padding:56px var(--pad) 72px;
  animation:trackingLock 420ms ease-out 1 both;
}
.cols{display:flex;gap:44px;align-items:flex-start}
.col-l{width:330px;flex:0 0 330px}
.col-r{flex:1;min-width:0}

/* ---------- 6. NAV ------------------------------------------ */
.nav{position:absolute;left:var(--pad);top:56px;width:120px}
.nav-group{margin-bottom:22px}
.nav-label{
  font-size:10px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--rust);padding-bottom:6px;margin-bottom:6px;
  border-bottom:1px solid var(--rule-2);
}
.nav-link{
  display:block;font-size:12px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--dim);padding:4px 0;
}
.nav-link:hover{color:var(--bright)}
.nav-link.current{color:var(--bright)}
.nav-link .mark{color:var(--rust);margin-right:5px}

/* Nav is absolute on desktop; clear it and lay the columns on a grid. Two
   columns up to 1499px — left | right — with THE RIG (a .cols child, not nested
   in .col-r) dropping into the right column's track beneath it (row 2). row-gap
   is 0 so RIG keeps the modules' own 26px rhythm, matching the old flow exactly. */
@media (min-width:961px){
  .cols{
    display:grid;
    grid-template-columns:330px minmax(0,1fr);
    column-gap:44px;row-gap:0;
    align-items:start;
    margin-left:168px;
  }
  .col-l{grid-column:1;grid-row:1}
  .col-r{grid-column:2;grid-row:1}
  #rig{grid-column:2;grid-row:2}
}

/* Wide desktop: don't centre the shell (removed on purpose) — let it grow and
   give THE RIG its own third column, so the empty right-hand space gets used:
   nav | left | right | rig. Below 1500px this rule is inert and the two-column
   layout above stands. */
@media (min-width:1500px){
  .shell{max-width:1600px}
  .cols{grid-template-columns:330px minmax(0,1fr) 360px}
  #rig{grid-column:3;grid-row:1}
}

/* ---------- 7. LEFT COLUMN ---------------------------------- */
.photo{margin:0 0 22px}
.photo-slot{
  aspect-ratio:4/5;max-height:300px;width:100%;
  border:1px solid var(--rule);background:#080F0A;
  position:relative;overflow:hidden;
  animation:focusHunt 900ms ease-out 1 both;
}
.photo-slot img{
  width:100%;height:100%;object-fit:cover;display:block;
  transform-origin:center;
  animation:portraitBreathe 12s ease-in-out 900ms infinite;
}
.photo-reticle{
  position:absolute;inset:0;z-index:1;pointer-events:none;
  opacity:0;transition:opacity .2s ease-out;
}
.photo-slot:hover .photo-reticle{opacity:1}
.photo-reticle i{
  position:absolute;width:18px;height:18px;
  border:1px solid var(--mid);opacity:.9;
  transition:transform .2s ease-out,opacity .2s ease-out;
}
.photo-reticle .tl{top:12px;left:12px;border-right:0;border-bottom:0}
.photo-reticle .tr{top:12px;right:12px;border-left:0;border-bottom:0}
.photo-reticle .bl{bottom:12px;left:12px;border-right:0;border-top:0}
.photo-reticle .br{bottom:12px;right:12px;border-left:0;border-top:0}
.photo-slot:hover .photo-reticle .tl{transform:translate(3px,3px)}
.photo-slot:hover .photo-reticle .tr{transform:translate(-3px,3px)}
.photo-slot:hover .photo-reticle .bl{transform:translate(3px,-3px)}
.photo-slot:hover .photo-reticle .br{transform:translate(-3px,-3px)}
.photo-rollbar{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  opacity:0;
  background:linear-gradient(to bottom,
    rgba(0,0,0,.78) 0 3px,transparent 3px 100%);
}
.photo-rollbar.is-on{animation:portraitRollBar 380ms linear 1 both}
.photo figcaption{
  display:flex;justify-content:space-between;
  font-size:10px;letter-spacing:.14em;color:var(--dim);
  padding-top:7px;
}
.name{
  font-family:var(--display);font-weight:700;
  font-size:46px;line-height:.95;letter-spacing:.01em;
  text-transform:uppercase;color:var(--bright);
  margin:0 0 10px;
  /* chromatic fringe — magenta left, cyan right, the same pair the cursor
     echo separates into (§16), so headline and cursor share the language */
  text-shadow:-1.5px 0 0 rgba(255,60,185,.5), 1.5px 0 0 rgba(60,205,225,.45);
}
.handle{font-size:12px;color:var(--dim);display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.handle .sep{color:var(--rule)}
.online{display:flex;align-items:center;gap:6px;color:var(--text)}
.online i{width:6px;height:6px;background:var(--mid);border-radius:50%;display:block}
/* Live-play broadcast — the game name on its own line under the handle at a
   larger size, with the chrome's blinking rust REC dot beside it, so live play
   is unmissable. Shown only while in a game (camera.js §7b). */
.onair{display:flex;align-items:center;gap:9px;margin:12px 0 2px}
.onair[hidden]{display:none}   /* class display beats the hidden attr, so restore it */
.onair .rec-dot{flex:none}     /* reuse the chrome REC dot: rust, blinking, motion-gated */
.onair-game{
  font-family:var(--mono);font-weight:600;
  font-size:19px;line-height:1.2;color:var(--bright);
}
.readout{
  font-size:10px;letter-spacing:.14em;color:var(--dim);
  margin:10px 0 26px;
}

/* ---------- 8. MODULES -------------------------------------- */
.mod{margin-bottom:26px}
.mod-head{
  display:flex;align-items:center;gap:9px;
  font-size:10px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--dim);
  border-bottom:1px solid var(--rule);
  padding-bottom:7px;margin-bottom:12px;
}
.mod-head .num{color:var(--rust)}
.toggle{
  margin-left:auto;font-size:10px;letter-spacing:.16em;
  color:var(--bright);font-family:var(--mono);
}
.toggle:hover{color:var(--rust)}
.mod-body[hidden]{display:none}

/* 04 LINK ME — the button stays at its native 88x31 size; the snippet is
   bordered and scrollable rather than allowed to widen the page. */
.link-me-row{
  display:flex;align-items:flex-start;gap:14px;
}
.link-me-button{
  display:block;flex:0 0 88px;
  width:88px;height:31px;
  image-rendering:pixelated;
}
.link-snippet{
  min-width:0;flex:1;margin:0;
  border:1px solid var(--border);background:var(--btn);
  padding:9px 10px;color:var(--mid);
  font:11px/1.45 var(--mono);
  white-space:pre-wrap;overflow:auto;
}
.link-me-likes{
  display:flex;align-items:center;gap:8px;
  min-height:31px;margin-top:14px;
  padding-top:12px;border-top:1px solid var(--rule-2);
}
.link-me-likes img{display:block;width:88px;height:31px;image-rendering:pixelated}

/* 05 GUESTBOOK — hosted SmartGB link; free accounts use a link rather than
   the iframe embed. */
.guestbook-module{
  border:1px solid var(--border);
  background:var(--mod);
  padding:14px 16px;
}
.guestbook-module .mod-head{margin:0;border-bottom:0;padding-bottom:0}
.guestbook-frame{
  display:block;width:100%;height:220px;
  margin-top:12px;
  border:0;background:var(--page);
}
.guestbook-link{
  display:inline-block;
  margin-top:12px;
  border:1px solid var(--border);
  background:var(--btn);
  padding:8px 10px;
  color:var(--bright);
  font-size:11px;
  letter-spacing:.12em;
}
.guestbook-link:hover{border-color:var(--mid);background:#0D1710}

/* contact buttons — 2x2, thumb-sized */
.links{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.links a{
  border:1px solid var(--border);background:var(--btn);
  padding:11px 12px;font-size:12px;letter-spacing:.06em;
  min-height:44px;display:flex;align-items:center;
}
.links a:hover{background:#0D1710;border-color:var(--mid)}

/* label / value tables */
.kv{width:100%;border-collapse:collapse;font-size:12px}
.kv th{
  text-align:left;font-weight:400;vertical-align:top;
  color:var(--dim);width:96px;padding:6px 12px 6px 0;
  white-space:nowrap;
}
.kv td{vertical-align:top;padding:6px 0;color:var(--text)}
.kv tr+tr th,.kv tr+tr td{border-top:1px solid var(--rule-2)}

/* astrological signs — SVG glyphs, never text characters, because the
   zodiac codepoints render as colour emoji on iOS and Android. */
.signs{display:flex;flex-direction:column;gap:5px}
.sign{display:flex;align-items:center;gap:6px;color:var(--dim)}
.sign b{font-weight:400;color:var(--text);margin-left:3px}

/* ---------- 9. RIGHT COLUMN --------------------------------- */
.network{
  color:var(--bright);font-size:12px;
  border-bottom:1px solid var(--rule);
  padding-bottom:12px;margin:0 0 16px;
}
.intro{margin:0 0 26px;max-width:62ch;color:var(--text)}
.intro a{border-bottom:1px solid var(--border)}

/* ---------- 10. MOBILE -------------------------------------- */
@media (max-width:960px){
  :root{--pad:18px}
  .shell{padding:72px var(--pad) 72px}   /* clear the fixed chrome, top and bottom */
  .osd-tl,.osd-bl{left:44px}
  .osd-tr,.osd-br{right:44px}

  .nav{position:static;width:auto;margin-bottom:30px}
  .nav-group{margin-bottom:14px}

  /* one column, left column first */
  .cols{display:block}
  .col-l,.col-r{width:auto;flex:none}
  .col-r{margin-top:30px}

  .photo-slot{max-height:260px}
  .name{font-size:40px}
  .kv th{width:84px;padding-right:10px}
}

@media (max-width:420px){
  .name{font-size:34px}
  .links{grid-template-columns:1fr 1fr;gap:7px}
  .kv{font-size:11.5px}
  .kv th{width:74px}
}

/* ---------- 11. MOTION OFF ----------------------------------
   With motion disabled the page must read exactly the same,
   just still. Nothing may be hidden that carries meaning.     */
@media (prefers-reduced-motion:reduce){
  [data-motion]{animation:none !important;transform:none !important}
  .photo-slot img{animation:none !important;transform:none !important}
  .photo-slot .photo-reticle{opacity:0 !important;transition:none !important}
  .photo-slot .photo-reticle i{transform:none !important;transition:none !important}
  .photo-slot .photo-rollbar{animation:none !important;opacity:0 !important;transform:none !important}
  .tear,.dropout{display:none !important}
  .exposure{opacity:0 !important}
  .rec-dot{animation:none}
  .scanlines{animation:none}
  .headswitch i{animation:none}
  .live i{animation:none}
}

/* ---------- 12. PRINT --------------------------------------- */
@media print{
  .fx,.chrome{display:none}
  body{background:#fff;color:#000}
}

/* ---------- 13. TEXT PAGE (about.html) ----------------------
   A long-form reading page, not a data page. The camcorder chrome
   and nav stay; the bordered modules don't. The prose is broken
   into labelled sections; each section is a small image in the left
   margin plus a prose column held to a ~65-character measure.
   Only about.html uses these classes — index.html is untouched. */

/* Page title */
.prose h1{
  font-family:var(--display);font-weight:700;
  font-size:38px;line-height:1;letter-spacing:.01em;
  text-transform:uppercase;color:var(--bright);
  margin:0 0 34px;
}

/* Expand / collapse all (mobile only; hidden on desktop). */
.expand-all{
  display:inline-block;margin:0 0 30px;
  font-size:10px;letter-spacing:.16em;
  color:var(--bright);font-family:var(--mono);
}
.expand-all:hover{color:var(--rust)}

/* Section = image margin (left) + prose column (right). The image
   column stays reserved even when a section has no image, so every
   section's prose lines up. The prose track can shrink below 65ch
   on a narrow desktop rather than overflow. */
.about-sec{
  display:grid;
  grid-template-columns:200px minmax(0,65ch);
  grid-template-areas:"media head" "media copy";
  column-gap:32px;align-items:start;
  margin:0 0 46px;
}
.sec-media{grid-area:media;margin:0}
.sec-copy{grid-area:copy;min-width:0}
.sec-copy p{margin:0 0 20px;line-height:1.85}
.sec-copy p:last-child{margin-bottom:0}

/* Section header — the index module header (§8) minus the box:
   small, letter-spaced, uppercase, rust number, no border/rule. */
.sec-head{
  grid-area:head;
  display:flex;align-items:baseline;gap:9px;
  font-size:10px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--dim);margin:0 0 14px;
}
.sec-head .num{color:var(--rust)}

/* Image slot: a flat green placeholder block. The <img> carries an
   already-treated (monochrome-green) file on top — NEVER filter or
   tint it here. A missing file hides the <img> via onerror and the
   green block shows through, so nothing renders as broken. */
.sec-img{
  aspect-ratio:4/3;
  border:1px solid var(--rule);
  background:#0A130C;overflow:hidden;
}
.sec-img img{width:100%;height:100%;object-fit:cover;display:block}
.sec-cap{
  margin-top:7px;
  font-size:10px;letter-spacing:.06em;line-height:1.4;
  color:var(--dim);
}

/* "Last updated" — the camcorder date stamp echoed at the foot of
   the page: amber, mono, letter-spaced, tabular figures. */
.updated{
  margin:40px 0 0;
  font-size:10px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--amber);font-variant-numeric:tabular-nums;
}

/* Desktop: clear the absolute nav (§6), and align the title and the
   stamp with the prose column (200px image margin + 32px gap). */
@media (min-width:961px){
  .prose{margin-left:168px}
  .prose h1{margin-left:232px}
  .updated{margin-left:232px}
  .sec-head .toggle,.expand-all{display:none}
  .sec-media[hidden],.sec-copy[hidden]{display:block}
}

/* Mobile: nav is static (§10). Sections stack to one column and
   collapse behind a tappable header; the image and copy sit below
   the header when open. */
@media (max-width:960px){
  .prose h1{font-size:32px}
  .about-sec{display:block;margin-bottom:34px}
  .sec-head{cursor:pointer;margin-bottom:0}
  .sec-media{margin:14px 0}
}

/* ---------- 14. UNDER CONSTRUCTION (gallery.html) ------------
   Placeholder page: just a page title and one OSD-style line —
   no modules, no borders. Same shell, nav, and chrome as the rest;
   the container clears the absolute nav on desktop like §5. */
.stub h1{
  font-family:var(--display);font-weight:700;
  font-size:38px;line-height:1;letter-spacing:.01em;
  text-transform:uppercase;color:var(--bright);
  margin:0 0 16px;
}
.stub-osd{
  font-size:10px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim);font-variant-numeric:tabular-nums;
}
@media (min-width:961px){ .stub{margin-left:168px} }  /* clear the absolute nav (§6) */
@media (max-width:960px){ .stub h1{font-size:32px} }

/* ---------- MUSIC PAGE (music.html) --------------------------
   A compact reading column that clears the absolute nav on desktop.
   The player is kept inside the site's bordered module language instead of
   reading like a pasted third-party widget. */
.music-page{
  width:100%;max-width:none;
}
.music-page h1{
  font-family:var(--display);font-weight:700;
  font-size:38px;line-height:1;letter-spacing:.01em;
  text-transform:uppercase;color:var(--bright);
  margin:0 0 30px;
}
.music-mod{margin-bottom:26px}
.music-list{
  list-style:none;margin:0;padding:0;
}
.music-list li{
  padding:10px 0;border-top:1px solid var(--rule-2);color:var(--text);
}
.music-list li:first-child{border-top:0;padding-top:0}
.music-list li:last-child{padding-bottom:0}
.making-note{
  margin:14px 0 0;
  color:var(--dim);
  font-size:11px;
  line-height:1.5;
}
.spotify-module{
  border:1px solid var(--border);
  background:var(--mod);
  padding:14px 16px 16px;
}
.spotify-module .mod-head{
  margin:0 0 14px;
}
.spotify-module iframe{
  display:block;width:100%;height:152px;
  border:0;background:var(--btn);
}
.music-now-playing{
  border:1px solid var(--border);
  background:var(--mod);
  padding:14px 16px;overflow:hidden;
}
.music-now-playing .mod-head{
  margin:0;
  border-bottom:0;
  padding-bottom:0;
}
.music-now-playing iframe{
  display:block;width:100%;height:150px;
  border:0;background:transparent;
  overflow:hidden;
}
@media (min-width:961px){ .music-page{margin-left:168px} }
@media (max-width:960px){
  .music-page h1{font-size:32px}
  .spotify-module{padding:12px}
  .spotify-module iframe{height:152px}
  .link-me-row{flex-wrap:wrap}
  .link-snippet{flex-basis:100%}
}

/* ---------- 15. GAMES PAGE (games.html) ---------------------
   A data page built from the index's module grammar (§8: .mod /
   .mod-head / .num). Only games.html uses the classes below.
   The page is a single reading column that clears the absolute
   nav on desktop (like §5/§13) and is held to a comfortable
   measure so the rows and ladders don't stretch on ultrawides. */
.games h1{
  font-family:var(--display);font-weight:700;
  font-size:38px;line-height:1;letter-spacing:.01em;
  text-transform:uppercase;color:var(--bright);
  margin:0 0 30px;
}
@media (min-width:961px){ .games{margin-left:168px;max-width:720px} }

/* CURRENTLY PLAYING + THE NUMBERS share a hairline-separated row
   list. Both are shaped so a future render can fill them from JSON
   (see the contract comments in games.html) — one <li> per record,
   fields in fixed child spans. */
.play,.stats,.top8-index,.ranks{list-style:none;margin:0;padding:0}

/* 01 CURRENTLY PLAYING — name + a status tag (not an hour count). */
.play-row{
  display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;
  padding:9px 0;border-top:1px solid var(--rule-2);
}
.play-row:first-child{border-top:0}
.play-name{color:var(--text)}
.play-status{
  margin-left:auto;
  border:1px solid var(--rule);color:var(--mid);
  padding:3px 9px;font-size:11px;letter-spacing:.04em;white-space:nowrap;
}
/* "Updated …" stamp under the block — dim, small, tabular figures, in the
   camcorder date format (camera.js §7b fills it), so the block reads as live.
   Hidden until real Steam data replaces the rows. */
.play-updated{
  margin:12px 0 0;
  font-size:10px;letter-spacing:.14em;color:var(--dim);
  font-variant-numeric:tabular-nums;
}

/* 02 TOP 8 — a numbered index, two columns, no image tiles. Echoes
   the index Top 8 (§9) minus the pictures. Rust ordinals. */
.top8-index{display:grid;grid-template-columns:1fr 1fr;gap:8px 24px}
.top8-index li{display:flex;align-items:baseline;gap:8px;color:var(--text)}
.top8-index .n{font-size:10px;color:var(--rust);letter-spacing:.1em}

/* 03 PROJECT MOON — the standout. A bordered callout with a rust
   spine and a big display-font hour count: heavier than every
   other block on the page. */
.moon-box{
  border:1px solid var(--border);border-left:3px solid var(--rust);
  background:var(--mod);padding:18px 20px;
}
.moon-hrs{
  font-family:var(--display);font-weight:700;
  font-size:44px;line-height:1;letter-spacing:.01em;
  color:var(--bright);margin:0 0 10px;
}
.moon-hrs span{
  font-family:var(--mono);font-weight:400;font-size:13px;
  color:var(--dim);letter-spacing:.14em;text-transform:uppercase;margin-left:4px;
}
.moon-line{margin:0 0 12px;color:var(--text);max-width:52ch}
.moon-done{
  margin:0;color:var(--bright);
  font-size:11px;letter-spacing:.18em;text-transform:uppercase;
  display:flex;align-items:center;gap:9px;
}
.moon-done::before{content:"";width:16px;height:1px;background:var(--rust)}

/* 04 THE NUMBERS — name (+ optional note) on the left, the figure
   right-aligned in tabular figures. */
.stat-row{
  display:flex;align-items:baseline;gap:12px;
  padding:9px 0;border-top:1px solid var(--rule-2);
}
.stat-row:first-child{border-top:0}
.stat-main{display:flex;flex-direction:column;min-width:0}
.stat-name{color:var(--text)}
.stat-note{color:var(--dim);font-size:11px;letter-spacing:.02em;margin-top:2px}
.stat-val{
  margin-left:auto;color:var(--bright);font-size:13px;
  font-variant-numeric:tabular-nums;white-space:nowrap;text-align:right;
}

/* 05 COMPETITIVE — an eight-tier ladder per role. Discrete named
   tiers, not a value bar: eight bordered cells, filled up to the
   current tier from data-tier (1-8), leading cell brightest, with
   the tier name + a division badge as the readout. */
.comp-cap{
  margin:0 0 6px;color:var(--dim);
  font-size:10px;letter-spacing:.16em;text-transform:uppercase;
}
/* Season / endorsement / W–L line above the ladders. camera.js fills the
   #compSeason / #compEndorse / #compRecord spans from live data. */
.comp-meta{
  margin:0 0 16px;color:var(--mid);
  font-size:11px;letter-spacing:.04em;font-variant-numeric:tabular-nums;
}
/* LIVE tag in the section header — hidden until camera.js (§7) confirms real
   data loaded, so it doubles as an at-a-glance "is the fetch working?" light. */
.live{
  margin-left:auto;display:inline-flex;align-items:center;gap:6px;
  font-size:9px;letter-spacing:.18em;color:var(--mid);
}
.live[hidden]{display:none}
.live i{
  width:6px;height:6px;border-radius:50%;background:var(--mid);
  animation:blink 1.6s steps(1,end) infinite;
}
.rank{
  display:grid;align-items:center;gap:6px 14px;
  grid-template-columns:84px 1fr auto;
  grid-template-areas:"role ladder read";
  padding:10px 0;border-top:1px solid var(--rule-2);
}
.rank:first-child{border-top:0}
.rank-role{grid-area:role;color:var(--text)}
.ladder{grid-area:ladder;display:inline-flex;gap:4px;flex-wrap:wrap}
.seg{
  width:15px;height:11px;flex:0 0 auto;
  border:1px solid var(--rule);background:transparent;
}
/* fill every segment up to the current tier */
.rank[data-tier="1"] .seg:nth-child(-n+1),
.rank[data-tier="2"] .seg:nth-child(-n+2),
.rank[data-tier="3"] .seg:nth-child(-n+3),
.rank[data-tier="4"] .seg:nth-child(-n+4),
.rank[data-tier="5"] .seg:nth-child(-n+5),
.rank[data-tier="6"] .seg:nth-child(-n+6),
.rank[data-tier="7"] .seg:nth-child(-n+7),
.rank[data-tier="8"] .seg:nth-child(-n+8),
.rank[data-tier="9"] .seg:nth-child(-n+9){background:var(--mid);border-color:var(--mid)}
/* the leading (current-tier) cell reads brightest */
.rank[data-tier="1"] .seg:nth-child(1),
.rank[data-tier="2"] .seg:nth-child(2),
.rank[data-tier="3"] .seg:nth-child(3),
.rank[data-tier="4"] .seg:nth-child(4),
.rank[data-tier="5"] .seg:nth-child(5),
.rank[data-tier="6"] .seg:nth-child(6),
.rank[data-tier="7"] .seg:nth-child(7),
.rank[data-tier="8"] .seg:nth-child(8),
.rank[data-tier="9"] .seg:nth-child(9){background:var(--bright);border-color:var(--bright)}
.rank-read{grid-area:read;display:flex;align-items:center;gap:9px;justify-self:end}
.rank-name{color:var(--bright)}
.rank-div{
  min-width:22px;text-align:center;
  border:1px solid var(--border);color:var(--mid);
  padding:1px 6px;font-size:11px;font-variant-numeric:tabular-nums;
}
/* A role with no rank yet — placements pending (set by camera.js §7 when the
   fetch succeeds but the role is missing): empty ladder, no division badge. */
.rank.unplaced .rank-div{display:none}
.rank.unplaced .rank-name{color:var(--mid)}

@media (max-width:960px){
  .games h1{font-size:32px}
  .top8-index{grid-template-columns:1fr}
  /* role + readout on the top line, ladder spanning below */
  .rank{
    grid-template-columns:1fr auto;
    grid-template-areas:"role read" "ladder ladder";
  }
  .ladder{margin-top:2px}
}

/* ---------- 16. CURSOR RETICLE -----------------------------
   NightShot focus reticle (behaviour in camera.js §8): four L-shaped corner
   brackets around the pointer — the same 1px weight and green as the
   viewfinder brackets in the page corners (§4). Drawn as FOUR stacked copies,
   each translated to its own lagged position by JS: they overlap into one
   reticle at rest and smear into a trail in motion. Colour runs magenta
   (leading) -> green core (brightest, so the resting reticle reads green) ->
   cyan (trailing), the same pair the headline uses (§7). Over links the
   brackets pull inward (focus snapping); on press they pull in further. The
   real cursor stays visible beneath — only transform/opacity move. Injected by
   JS on a fine pointer with motion allowed; the media queries are a 2nd gate. */
.cursor{
  position:fixed;top:0;left:0;z-index:80;
  width:0;height:0;pointer-events:none;
  opacity:0;transition:opacity .2s ease-out;
}
.cursor.is-on{opacity:.9}
.cursor.is-link{opacity:1}          /* brighten over links/buttons */
.echo{position:absolute;top:0;left:0;width:26px;height:26px;will-change:transform}
/* one corner bracket — each keeps just two borders, an L opening toward the
   centre, exactly like .bracket in §4. */
.rb{
  position:absolute;width:7px;height:7px;
  border:1px solid currentColor;
  transition:transform .16s ease-out;
}
.rb.tl{top:0;left:0;border-right:0;border-bottom:0}
.rb.tr{top:0;right:0;border-left:0;border-bottom:0}
.rb.bl{bottom:0;left:0;border-right:0;border-top:0}
.rb.br{bottom:0;right:0;border-left:0;border-top:0}
/* leading -> trailing: magenta ghost, green core (brightest), cyan ghost —
   the split only reads once the copies pull apart in motion. */
.echo:nth-child(1){color:rgba(255,60,185,.55)}
.echo:nth-child(2){color:rgba(150,235,175,.95)}
.echo:nth-child(3){color:rgba(130,225,185,.55)}
.echo:nth-child(4){color:rgba(60,205,225,.42)}
/* Over links: brackets snap inward. On press: further in (contract). */
.cursor.is-link .rb.tl{transform:translate(2px,2px)}
.cursor.is-link .rb.tr{transform:translate(-2px,2px)}
.cursor.is-link .rb.bl{transform:translate(2px,-2px)}
.cursor.is-link .rb.br{transform:translate(-2px,-2px)}
.cursor.is-down .rb.tl{transform:translate(4px,4px)}
.cursor.is-down .rb.tr{transform:translate(-4px,4px)}
.cursor.is-down .rb.bl{transform:translate(4px,-4px)}
.cursor.is-down .rb.br{transform:translate(-4px,-4px)}

@media (prefers-reduced-motion:reduce){ .cursor{display:none} }
@media (pointer:coarse){ .cursor{display:none} }

/* ---------- 17. SOUND TOGGLE -------------------------------
   Speaker button injected by camera.js §9, sitting in the chrome
   beside SP (bottom-right). It's kept OUT of the aria-hidden chrome so
   it stays a real, focusable control, and positioned to line up with
   the OSD. Off = dim with a mute slash; on = brighter with sound waves.
   Click sounds are OFF by default; this opts in for the session. */
.snd{
  position:fixed;z-index:71;
  right:76px;bottom:14px;
  width:18px;height:18px;padding:0;
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--dim);background:none;border:0;line-height:0;
  pointer-events:auto;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.snd:hover{color:var(--mid)}
.snd.is-on{color:var(--mid)}
.snd.is-on:hover{color:var(--bright)}
/* off shows the mute slash; on shows the sound waves */
.snd .wave{display:none}
.snd.is-on .wave{display:inline}
.snd.is-on .slash{display:none}
@media (max-width:960px){
  .snd{right:68px}   /* track the OSD shift (§10: osd-br right:44px) */
}

/* ---------- 18. SIGNAL GLITCH ------------------------------
   Random readable-block corruption (driven by camera.js §11). For ~1s the
   paragraph or text block splits into magenta/cyan channel offsets — the same
   pair as the cursor reticle (§16) and the handle headline (§7) — and jitters
   a few px sideways, then the class is removed and the text is exactly as it
   was. transform + text-shadow only, so nothing around it reflows. */
@keyframes sig-glitch{
  0%  {transform:translateX(-3px);text-shadow:-3px 0 rgba(255,60,185,.9),  3px 0 rgba(60,205,225,.85)}
  12% {transform:translateX(2px); text-shadow: 3px 0 rgba(255,60,185,.85),-3px 0 rgba(60,205,225,.8)}
  24% {transform:translateX(-2px);text-shadow:-2px 0 rgba(255,60,185,.9),  2px 0 rgba(60,205,225,.8)}
  36% {transform:translateX(3px); text-shadow: 2px 0 rgba(255,60,185,.8), -3px 0 rgba(60,205,225,.85)}
  48% {transform:translateX(-1px);text-shadow:-3px 0 rgba(255,60,185,.85), 3px 0 rgba(60,205,225,.8)}
  60% {transform:translateX(2px); text-shadow: 2px 0 rgba(255,60,185,.7), -2px 0 rgba(60,205,225,.7)}
  72% {transform:translateX(-1px);text-shadow:-1px 0 rgba(255,60,185,.6),  1px 0 rgba(60,205,225,.6)}
  84% {transform:translateX(1px); text-shadow: 1px 0 rgba(255,60,185,.45),-1px 0 rgba(60,205,225,.45)}
  100%{transform:none;text-shadow:none}
}
.sig-glitch{animation:sig-glitch 1s steps(1,end) both}
@media (prefers-reduced-motion:reduce){ .sig-glitch{animation:none} }
