/* ============================================================
   style.css — arden warr
   single-page XMB (PSP crossbar) layout
   ------------------------------------------------------------
     1. fonts          6. content panel      11. guestbook
     2. tokens         7. chaos zone         12. contact
     3. cursors        8. music sections     13. forms
     4. reset + chrome  9. me sections       14. no-js fallback
     5. the cross     10. updates
   ============================================================ */

/* ============================================================
   1. fonts — self-hosted. woff2 first, ttf fallback.
   ============================================================ */
/* Assign — scratchy ballpoint handwriting. NOT applied anywhere yet.
   WARNING: this font has NO digits and NO punctuation. Those glyphs
   exist in the cmap but are empty outlines, so "who u callin pinhead?"
   renders without the question mark and "2026" renders as four gaps.
   Letters only, short phrases only. See --font-hand below. */
@font-face {
  font-family: 'Assign';
  src: url('assign.woff2') format('woff2'),
       url('assign.ttf') format('truetype');
  font-display: swap;
}
@font-face {
  font-family: 'Final Lap';
  src: url('FinalLap-p3M1.ttf') format('truetype');
  font-display: swap;
}
.dsp-logo {
  width: 1.8rem;
  height: 1.8rem;
  display: inline-flex;
  object-fit: contain;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--static);
  border-radius: 0.35rem;
  padding: 0.25rem;
  color: var(--glow);
  font-family: var(--font-label);
  flex: 0 0 auto;
}
@font-face {
  font-family: 'Undecapped Vinyl';
  src: url('UndecappedVinyl-r3dy.ttf') format('truetype');
  font-display: swap;
}
@font-face {
  font-family: 'Laundromat 1967';
  src: url('Laundromat1967-vA4y.ttf') format('truetype');
  font-display: swap;
}
@font-face {
  font-family: 'Astonished';
  src: url('Astonished-KMrD.ttf') format('truetype');
  font-display: swap;
}

/* ============================================================
   FLAT FILE LAYOUT — READ BEFORE ADDING ANYTHING
   ------------------------------------------------------------
   Every asset on this site lives at the ROOT. No assets/ folder,
   no fonts/ folder, no subdirectories at all. Fonts, images, gifs,
   scripts and stylesheets all sit beside index.html.

   Why: Neocities does not create folders from a loose multi-file
   upload. Drag twenty files into the dashboard and you get twenty
   files at the root — a path like assets/fonts/x.woff2 then 404s,
   silently, and the font just falls back. Flat paths cannot break
   that way.

   So when you add a file: upload it to the root and reference it by
   bare filename. url('thing.woff2'), not url('assets/thing.woff2').

   Also: Neocities is CASE-SENSITIVE and your computer is not.
   Thing.PNG and thing.png are different files on the server and the
   same file locally. Keep every filename lowercase and this can
   never bite you.
   ============================================================ */

/* ============================================================
   2. tokens
   ============================================================ */
:root {
  --crt-intensity: 0.5;
  /* Default = oxblood. Every palette below redefines this same set,
     so nothing else in the stylesheet ever names a colour directly. */
  --ink:        #0d0708;
  --ink-soft:   #1a0e11;
  --ink-deep:   #070405;
  --paper:      #f2e6e6;
  --paper-dim:  #b09a9c;
  --bruise:     #6b2230;
  --rust:       #a83a45;
  --static:     #43262b;
  --glow:       #e0616b;
  --surface-hover: #2a1318;
  --accent-wash: rgba(224, 97, 107, 0.12);
  --scrim:      rgba(13, 7, 8, 0.24);
  --panel:      rgba(13, 7, 8, 0.42);
  --wave-ink:   #0d0708;
  --wave-bruise: #6b2230;
  --wave-rust:  #a83a45;
  --wave-glow:  #e0616b;

  /* scream  = the one huge word. unreadable small — don't.
     display = distressed serif, real upper+lowercase.
     swash   = curly decorative caps. short headings only.
     label   = spiky thin sans. xmb labels, timestamps, ui.
     body    = verdana, the honest 2000s body font. */
  --font-scream:  'Final Lap', Impact, fantasy;
  --font-display: 'Undecapped Vinyl', Georgia, serif;
  --font-swash:   'Laundromat 1967', Georgia, serif;
  --font-label:   'Astonished', Tahoma, sans-serif;
  --font-subsection: 'Trebuchet MS', 'Helvetica Neue', Arial, sans-serif;
  --font-body:    Verdana, Geneva, Tahoma, sans-serif;
  /* hand = ballpoint scrawl. letters only — no digits, no punctuation.
     NOTE ON THE FALLBACKS: the generic `cursive` keyword resolves to
     Comic Sans MS on Windows. So if you ever see Comic Sans on the
     site, it is not a style choice — it means Assign failed to load
     and the stack fell all the way through. Check the network tab. */
  --font-hand:    'Assign', 'Segoe Script', 'Bradley Hand', 'Apple Chancery', cursive;

  /* the cross: where the selection cursor is pinned.
     everything slides to meet these two numbers. */
  --cursor-x: 15%;
  --cursor-y: 48%;
  /* Same number as --cursor-y, in viewport units. The icon row is
     absolutely positioned inside a column whose height is content-
     driven, so a percentage there would resolve against the wrong
     box. Change both together or the icons drift off the cursor. */
  --cursor-vy: 48vh;

  --col-w: 200px;
  --row-h: 72px;

  /* the icon row. --icon-h must cover artwork + gap + label or the
     labels will collide with the row list underneath. */
  --icon-size:  76px;   /* the artwork itself */
  --icon-h:     158px;
  --icon-gap:   22px;   /* breathing room above the cursor row */
}
html.palette-oxblood { --ink: #0d0708; --ink-soft: #1a0e11; --ink-deep: #070405; --paper: #f2e6e6; --paper-dim: #b09a9c; --bruise: #6b2230; --rust: #a83a45; --static: #43262b; --glow: #e0616b; --surface-hover: #2a1318; --accent-wash: rgba(224, 97, 107, 0.12); --scrim: rgba(13, 7, 8, 0.24); --panel: rgba(13, 7, 8, 0.42); }
html.palette-purple { --ink: #0e0813; --ink-soft: #1c1128; --ink-deep: #080510; --paper: #ece0f7; --paper-dim: #a893bd; --bruise: #58306e; --rust: #9a5fb8; --static: #46304f; --glow: #cf9ff0; --surface-hover: #29193a; --accent-wash: rgba(207, 159, 240, 0.12); --scrim: rgba(14, 8, 19, 0.24); --panel: rgba(14, 8, 19, 0.42); }
html.palette-silver { --ink: #131416; --ink-soft: #202225; --ink-deep: #0c0d0e; --paper: #eceff0; --paper-dim: #9ba0a3; --bruise: #5d6265; --rust: #a4abae; --static: #3d4144; --glow: #dfe6e8; --surface-hover: #2b2e31; --accent-wash: rgba(223, 230, 232, 0.12); --scrim: rgba(19, 20, 22, 0.24); --panel: rgba(19, 20, 22, 0.42); }
html.palette-faded { --ink: #14100b; --ink-soft: #221b13; --ink-deep: #0b0907; --paper: #f4e8d4; --paper-dim: #b8a68a; --bruise: #6d5533; --rust: #b08d54; --static: #4a3c29; --glow: #e8c98b; --surface-hover: #302719; --accent-wash: rgba(232, 201, 139, 0.12); --scrim: rgba(20, 16, 11, 0.24); --panel: rgba(20, 16, 11, 0.42); }
html.palette-moss { --ink: #0d110b; --ink-soft: #181d14; --ink-deep: #070906; --paper: #e4ecdc; --paper-dim: #9aa891; --bruise: #4a5c3a; --rust: #8a9e62; --static: #3a452f; --glow: #bcd18f; --surface-hover: #202b19; --accent-wash: rgba(188, 209, 143, 0.12); --scrim: rgba(13, 17, 11, 0.24); --panel: rgba(13, 17, 11, 0.42); }
html.palette-black { --ink: #040404; --ink-soft: #101010; --ink-deep: #000000; --paper: #ffffff; --paper-dim: #b4b4b4; --bruise: #4a4a4a; --rust: #d8d8d8; --static: #4a4a4a; --glow: #ffffff; --surface-hover: #202020; --accent-wash: rgba(255, 255, 255, 0.12); --scrim: rgba(0, 0, 0, 0.30); --panel: rgba(0, 0, 0, 0.46); }

/* background.js reads these four separately so the shader can be
   pushed further than the interface colours safely allow */
html.palette-oxblood { --wave-ink: #0d0708; --wave-bruise: #6b2230; --wave-rust: #a83a45; --wave-glow: #e0616b; }
html.palette-purple { --wave-ink: #0e0813; --wave-bruise: #58306e; --wave-rust: #9a5fb8; --wave-glow: #cf9ff0; }
html.palette-silver { --wave-ink: #131416; --wave-bruise: #5d6265; --wave-rust: #a4abae; --wave-glow: #dfe6e8; }
html.palette-faded { --wave-ink: #14100b; --wave-bruise: #6d5533; --wave-rust: #b08d54; --wave-glow: #e8c98b; }
html.palette-moss { --wave-ink: #0d110b; --wave-bruise: #4a5c3a; --wave-rust: #8a9e62; --wave-glow: #bcd18f; }
html.palette-black { --wave-ink: #040404; --wave-bruise: #4a4a4a; --wave-rust: #d8d8d8; --wave-glow: #ffffff; }

/* ============================================================
   3. cursors — wii hands, mapped to what the hand is doing
   ------------------------------------------------------------
     open hand    idle, nothing under you
     pointing     something clickable
     closed fist  you're holding the mouse down

   These are the tilted ("ccw") variants from the set — the plain
   wii-pointer.cur ships with a loading disc stuck to it, and the
   author's readme says he prefers the tilted ones anyway.

   The two numbers after each url are the hotspot (the pixel that
   actually points), read out of the original .cur headers — get
   it wrong and clicks land a few pixels off. Every rule ends in
   a normal keyword so it degrades if the png fails to load.
   Text fields deliberately keep the native I-beam; a hand there
   tells you nothing about where you're typing.
   ============================================================ */
html, body {
  cursor: auto;
}
a, button, summary, label, [role="button"],
.xmb-item, .xmb-icon, .dsp, .social-lead {
  cursor: pointer;
}
input, textarea { cursor: text; }

/* mousedown — must come last to win over the rules above */
body:active,
a:active, button:active,
.xmb-item:active, .xmb-icon:active, .dsp:active, .social-lead:active {
  cursor: grabbing;
}

/* ============================================================
   4. reset
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
body, h1, h2, h3, p, ul, figure { margin: 0; }
img { max-width: 100%; display: block; }
ul { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }

body {
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.7;
  min-height: 100vh;
  overflow-x: hidden;
}

/* grain over everything. set opacity: 0 to kill it. */
.grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 60;
  background-image: radial-gradient(circle, rgba(255,255,255,0.05) 1px, transparent 1px);
  background-size: 3px 3px;
  mix-blend-mode: overlay;
}

/* ---- CRT ----------------------------------------------------
   Deliberately STATIC. Flicker, roll bars and jitter are what make
   a CRT effect unbearable after twenty seconds; the scanlines and
   vignette alone read as "old screen" without ever asking for your
   attention. Three things keep it calm:
     1. no animation of any kind
     2. it backs off to a third of its strength once a panel is
        open, because that's when you're actually reading
     3. the line pitch doubles on hi-dpi screens so it can't moire
   Toggle in settings > general.
   -------------------------------------------------------------- */
.crt { display: none; }
html.crt-on .crt {
  display: block;
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 62;
  opacity: min(1, calc(var(--crt-intensity) * 1.1));
  transition: opacity 0.35s ease;
  background-image:
    repeating-linear-gradient(
      to bottom,
      rgba(0, 0, 0, 0.30) 0 1px,
      transparent 1px 3px);
}
/* the glass: darkened corners, slight bloom in the middle */
html.crt-on .crt::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 50%,
    transparent 42%,
    rgba(7, 4, 5, 0.55) 100%);
  background:
    radial-gradient(ellipse at 50% 50%,
      transparent 42%,
      color-mix(in srgb, var(--ink-deep) 55%, transparent) 100%);
}
/* stand down while reading */
html.crt-on body.panel-open .crt { opacity: min(1, calc(var(--crt-intensity) * 0.4)); }

@media (min-resolution: 2dppx) {
  html.crt-on .crt {
    background-image:
      repeating-linear-gradient(
        to bottom,
        rgba(0, 0, 0, 0.26) 0 2px,
        transparent 2px 5px);
  }
}

/* the animated noise field, painted by background.js */
#wave {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
}
html:not(.background-on) #wave { display: none; }
html.low-performance .grain { display: none; }
html.low-performance .crt { opacity: 0.08; }
html.low-performance .idle-mascot { display: none; }
html.gifs-off .boot-spin,
html.gifs-off .gif-aside img,
html.gifs-off .idle-mascot { display: none; }
html.gifs-off .gif-mat {
  background-image: none;
  background-color: var(--ink-soft);
}
html.gifs-off .gif-mat::before { display: none; }

/* sits between the shader and the interface. the background is
   pretty but the crossbar has to stay readable over it — turn
   these alphas down if you want more of the noise showing. */
.scrim {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 72% 62% at 50% 44%,
      var(--scrim) 0%,
      transparent 62%,
      var(--scrim) 100%);
}

a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--glow);
  outline-offset: 3px;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.skip-link {
  position: fixed;
  left: -999px; top: 0;
  background: var(--bruise);
  color: var(--paper);
  padding: 0.5rem 1rem;
  z-index: 100;
}
.skip-link:focus { left: 1rem; top: 1rem; }

/* ============================================================
   4b. chrome — top bar + footer credit
   ============================================================ */
.topbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.5rem;
  pointer-events: none;
}
.topbar > * { pointer-events: auto; }
.brand {
  font-family: var(--font-display);
  font-size: 1.3rem;
  color: var(--paper);
  letter-spacing: 0.02em;
}
.brand:hover { color: var(--glow); }

.chrome-right { display: flex; align-items: center; gap: 1rem; }
.clock {
  font-family: var(--font-label);
  font-size: 0.95rem;
  color: var(--paper-dim);
}
.sound-toggle {
  font-family: var(--font-label);
  font-size: 0.95rem;
  background: none;
  border: 1px solid var(--static);
  color: var(--paper-dim);
  padding: 0.25rem 0.7rem;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.sound-toggle:hover { color: var(--paper); border-color: var(--bruise); }
.sound-toggle[aria-pressed="true"] { color: var(--rust); border-color: var(--rust); }

.hint {
  position: fixed;
  left: 50%; bottom: 1.1rem;
  transform: translateX(-50%);
  z-index: 25;
  font-family: var(--font-label);
  font-size: 0.7rem;
  color: var(--paper-dim);
  letter-spacing: 0.04em;
  text-align: center;
  text-shadow: 0 1px 3px var(--ink-deep), 0 0 8px var(--ink);
  white-space: nowrap;
}
/* sits above the panel's z-index, so get it out of the way */
.hint { transition: opacity 0.35s ease; }
.js body.panel-open .hint { opacity: 0; pointer-events: none; }
.mobile-hint { display: none; }

/* ============================================================
   5. the cross
   ------------------------------------------------------------
   the cursor element never moves. .xmb-track slides sideways so
   the active column lands on it; each column slides vertically
   so its selected row lands on it. that's the whole mechanic.
   ============================================================ */
.js .xmb {
  position: fixed;
  inset: 0;
  z-index: 10;
  overflow: hidden;
  transition: opacity 0.3s ease;
  /* the lens. xmb.js sets this to `none` unless fisheye mode is on. */
  perspective: var(--lens-perspective, none);
  perspective-origin: var(--cursor-x) var(--cursor-y);
}
.js body.panel-open .xmb { opacity: 0.18; pointer-events: none; }

.js .xmb-cursor {
  position: absolute;
  left: var(--cursor-x);
  top: var(--cursor-y);
  width: var(--col-w);
  height: var(--row-h);
  pointer-events: none;
}
.js .xmb-track {
  position: absolute;
  top: 0; left: 0;
  display: flex;
  align-items: flex-start;
  will-change: transform;
  transition: transform 0.48s cubic-bezier(0.16, 1, 0.3, 1);
  /* without this the columns are flattened into the track and the
     perspective above has nothing to act on */
  transform-style: preserve-3d;
}
.js .xmb-col {
  flex: 0 0 var(--col-w);
  width: var(--col-w);
  position: relative;
  display: flex;
  flex-direction: column;
  will-change: transform;
  /* order matters: fall, then recede, then rotate, then shrink.
     Rotating before translating would swing the column along its own
     tilted axis and the arch would spiral instead of bow. */
  transform: translateY(var(--col-y, 0px))
             translateZ(var(--col-z, 0px))
             rotateY(var(--col-rot, 0deg))
             scale(var(--col-scale, 1));
  opacity: var(--col-opacity, 1);
  /* The blur deliberately does NOT live here. A filter on an element
     forces its subtree flat, which would kill the translateZ that
     makes the selected icon lift toward the camera. The blur is
     pushed down onto the leaves instead — see .xmb-icon svg below. */
  transform-style: preserve-3d;
  transition: transform 0.48s cubic-bezier(0.16, 1, 0.3, 1),
              filter 0.42s ease,
              opacity 0.38s ease;
}
.js .xmb-col.is-dim .xmb-items { opacity: 0; }

.xmb-icon {
  height: var(--icon-h);
  position: absolute;
  top: calc(var(--cursor-vy) - var(--icon-h) - var(--icon-gap));
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 0.6rem;
  color: var(--paper-dim);
  /* translateZ lifts the selected icon out toward the viewer. It only
     does anything while the fisheye's perspective is active, and is
     harmlessly inert otherwise. */
  transform: translateZ(var(--icon-z, 0px)) scale(var(--icon-scale, 1));
  transform-style: preserve-3d;
  transition: color 0.24s ease, transform 0.42s cubic-bezier(0.16, 1, 0.3, 1);
}
/* the xmb icon set is solid-filled artwork, not stroked outlines */
.xmb-icon svg {
  box-sizing: border-box;
  width: var(--icon-size);
  height: var(--icon-size);
  fill: currentColor;
  stroke: none;
  transition: background 0.2s ease, border-color 0.2s ease, filter 0.28s ease;
}
/* depth-of-field for the icon row, applied to the leaves rather than
   the column so the 3D space above survives */
.xmb-icon svg, .xmb-icon .label { filter: blur(var(--col-blur, 0px)); }
.xmb-icon .settings-toolbox {
  filter: brightness(0) invert(1);
}
.xmb-icon .label {
  font-family: var(--font-label);
  font-size: 1.2rem;
  letter-spacing: 0.1em;
  text-align: center;
  line-height: 1.1;
  transition: color 0.2s ease, letter-spacing 0.2s ease;
}
/* the background is a moving field — sooner or later a bright blob
   drifts under every label, so give the type its own dark backing. */
.xmb-icon .label, .xmb-item, .brand {
  text-shadow: 0 1px 3px rgba(5, 9, 20, 0.95), 0 0 14px rgba(5, 9, 20, 0.75);
}
.xmb-icon.is-sel {
  color: var(--paper);
  --icon-scale: 1;
  --icon-z: 0px;
}
.xmb-icon.is-sel .label { letter-spacing: 0.15em; }
.xmb-icon.is-sel svg { filter: drop-shadow(0 0 8px rgba(201,166,217,0.5)); }
.xmb-icon.is-sel .settings-toolbox {
  filter: brightness(0) invert(1) drop-shadow(0 0 8px rgba(142,216,255,0.5));
}

.js .xmb-items {
  transition: transform 0.46s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.38s ease;
}
.xmb-item {
  height: var(--row-h);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  line-height: 1.25;
  padding: 0 0.5rem;
  font-family: var(--font-subsection);
  font-size: 0.98rem;
  color: var(--paper-dim);
  opacity: var(--row-opacity, 1);
  /* push first, then scale: the bulge is a layout offset, the scale
     is the magnification. Reversing them scales the offset too and
     the spread runs away from you. */
  transform: translate(var(--row-arc, 0px), var(--row-push, 0px))
             scale(var(--row-scale, 1));
  transform-origin: center center;
  /* both axes compound: a far row in a far column is the softest
     thing on screen, which is the entire point of depth of field */
  filter: blur(calc(var(--row-blur, 0px) + var(--col-blur, 0px)));
  transition: color 0.2s ease, transform 0.46s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.38s ease, filter 0.42s ease;
}
.xmb-item.is-sel {
  color: var(--paper);
  text-shadow: 0 1px 3px rgba(5, 9, 20, 0.95),
               0 0 10px rgba(142, 216, 255, 0.55);
}
.xmb-item:hover, .xmb-icon:hover { color: var(--paper); }
.js .xmb-col.is-dim .xmb-items { opacity: 0.35; }
/* The old fixed blurs lived here. They're now graded per element by
   --row-blur / --col-blur in xmb.js, so hardcoding them would just
   override the falloff. */

@media (max-width: 720px) {
  :root {
    --col-w: 142px; --row-h: 64px;
    --cursor-x: 8%; --cursor-y: 48%; --cursor-vy: 48vh;
    --icon-size: 54px;
    --icon-h: 116px; --icon-gap: 16px;
  }
  .xmb-icon .label { font-size: 0.95rem; letter-spacing: 0.1em; }
  .hint { display: none; }
  .audio-track { align-items: flex-start; flex-direction: column; }
  .audio-track audio { width: 100%; }
  .polaroid { width: min(100%, 220px); }
}

/* ============================================================
   6. content panel
   ============================================================ */
.js .panel {
  position: fixed;
  inset: 0;
  z-index: 20;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--panel);
  transform: translateX(100%);
  opacity: 0;
  visibility: hidden;
  transition: transform 0.34s cubic-bezier(0.22, 0.61, 0.36, 1),
              opacity 0.25s ease,
              visibility 0.34s;
}
.js body.panel-open .panel {
  transform: translateX(0);
  opacity: 1;
  visibility: visible;
}

.panel-inner {
  max-width: 660px;
  margin: 0 auto;
  padding: 5rem clamp(0.9rem, 5vw, 1.5rem) 5rem;
}

.panel-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 2rem;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--static);
}
.back-btn {
  font-family: var(--font-label);
  font-size: 1rem;
  background: none;
  border: 1px solid var(--static);
  color: var(--paper-dim);
  padding: 0.35rem 0.85rem;
  cursor: pointer;
  flex-shrink: 0;
}
.back-btn:hover { color: var(--paper); border-color: var(--rust); }
.crumb {
  font-family: var(--font-label);
  font-size: 0.95rem;
  color: var(--static);
}
.crumb b { color: var(--paper-dim); font-weight: 400; }
.panel-signal {
  margin-left: auto;
  color: var(--rust);
  font-family: var(--font-label);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.85;
}

/* only the chosen section is on screen (js mode) */
.js .leaf { display: none; }
.js .leaf.is-live { display: block; }

.leaf h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.9rem, 5vw, 2.7rem);
  line-height: 1.2;
  margin-bottom: 1.25rem;
}
.leaf h2 {
  font-family: var(--font-subsection);
  font-weight: 400;
  font-size: 1.5rem;
  letter-spacing: 0.03em;
  margin: 2rem 0 0.8rem;
}
.leaf h3 {
  font-family: var(--font-subsection);
  font-weight: 400;
  font-size: 1.1rem;
  margin-bottom: 0.3rem;
}
.leaf p { max-width: 62ch; }
.leaf p + p { margin-top: 0.9rem; }
.dim { color: var(--paper-dim); font-size: 0.93rem; }

.leaf p a, .dim a, .link-list a {
  color: var(--glow);
  border-bottom: 1px dotted var(--static);
}
.leaf p a:hover, .dim a:hover, .link-list a:hover {
  color: var(--rust);
  border-bottom-color: var(--rust);
}

.rule {
  height: 0;
  margin: 2.25rem 0;
  border-top: 1px solid var(--static);
}

.sigil-mark {
  display: block;
  width: 26px; height: 26px;
  color: var(--bruise);
  margin-bottom: 0.75rem;
  stroke: currentColor; fill: none; stroke-width: 1.25;
}

.panel-box {
  background: var(--ink-soft);
  border: 1px solid var(--static);
  padding: 1.5rem;
}

code {
  font-family: ui-monospace, 'Courier New', monospace;
  font-size: 0.88em;
  background: var(--ink-soft);
  border: 1px solid var(--static);
  padding: 0.1em 0.35em;
}

/* ============================================================
   7. chaos zone
   ============================================================ */
.chaos-zone {
  background: var(--paper);
  color: var(--ink);
  border: 4px dashed var(--rust);
  padding: 1.5rem;
  margin-top: 1.5rem;
}
.chaos-zone h2 {
  font-family: 'Comic Sans MS', 'Comic Neue', cursive;
  color: var(--ink);
  letter-spacing: 0;
  margin-top: 0;
}
.chaos-zone p { color: var(--ink); }
.chaos-zone a { color: var(--bruise); text-decoration: underline; }

.chaos-grid { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1rem; }
.chaos-grid img {
  width: 80px; height: 80px;
  object-fit: cover;
  background: #fff;
  border: 2px solid var(--ink);
}
.chaos-grid img:nth-child(odd) { transform: rotate(-3deg); }
.chaos-grid img:nth-child(even) { transform: rotate(3deg); }

.marquee {
  overflow: hidden;
  white-space: nowrap;
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--ink);
  margin-top: 1.25rem;
  padding: 0.4rem 0;
  font-family: 'Comic Sans MS', 'Comic Neue', cursive;
  font-size: 0.85rem;
}
.marquee span {
  display: inline-block;
  padding-left: 100%;
  animation: marquee 14s linear infinite;
}
@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-100%); }
}

/* ============================================================
   8. music
   ============================================================ */
.embed-frame {
  border: 1px solid var(--static);
  background: var(--ink-soft);
  padding: 0.5rem;
}
.embed-frame iframe { display: block; border: none; width: 100%; }
.media-subheading {
  margin-top: 1.5rem;
  font-family: var(--font-subsection);
  font-size: 1.25rem;
  font-weight: normal;
  color: var(--paper);
}
.video-note { margin-top: 0.35rem; }
.video-grid {
  display: grid;
  gap: 1.25rem;
  margin-top: 1rem;
}
.video-slot {
  min-width: 0;
  width: min(100%, 560px);
  margin: 0 auto;
  padding: 0.75rem;
  border: 1px solid var(--static);
  border-left: 3px solid var(--bruise);
  background: color-mix(in srgb, var(--ink-soft) 72%, transparent);
}
.leaf h3.video-label {
  display: block;
  margin-bottom: 0.6rem;
  font-family: var(--font-hand);
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--paper);
  letter-spacing: 0.05em;
}
.video-embed {
  aspect-ratio: 16 / 9;
  border: 1px solid var(--bruise);
  background: var(--ink-soft);
  padding: 0;
  overflow: hidden;
}
.video-embed iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: var(--ink);
}
.player { width: 100%; filter: invert(0.9) hue-rotate(180deg) contrast(0.9); }
.dsp-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(195px, 1fr));
  gap: 0.6rem;
  margin-top: 1rem;
}
.dsp {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  background: var(--ink-soft);
  border: 1px solid var(--static);
  border-left: 3px solid var(--bruise);
  border-radius: 0;
  padding: 0.7rem 0.85rem;
  transition: border-left-color 0.15s ease, background 0.15s ease;
}
.dsp:hover { background: var(--surface-hover); border-left-color: var(--rust); }
.dsp .name { font-family: var(--font-display); font-size: 1.05rem; color: var(--paper); }
.dsp .kind { font-family: var(--font-label); font-size: 0.85rem; color: var(--paper-dim); flex-shrink: 0; }
.dsp:hover .kind { color: var(--rust); }
.audio-library { display: grid; gap: 0.7rem; margin-top: 1rem; }
.audio-track {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 0.85rem;
  background: var(--ink-soft);
  border: 1px solid var(--static);
}
.audio-track span { color: var(--paper); }
.audio-track audio { width: min(100%, 300px); }

.discography li {
  padding: 0.9rem 0;
  border-bottom: 1px dashed var(--static);
}
.discography li:first-child { padding-top: 0.25rem; }
.discography li:last-child { border-bottom: none; }
.release-dropdown { width: 100%; }
.release-dropdown > summary {
  display: flex;
  gap: 1rem;
  align-items: center;
  cursor: pointer;
  list-style: none;
  padding: 0.25rem 0.35rem;
  border-left: 2px solid transparent;
  transition: background 0.25s ease, border-color 0.25s ease;
}
.release-dropdown > summary:hover,
.release-dropdown[open] > summary { background: var(--accent-wash); border-left-color: var(--rust); }
.release-dropdown > summary::-webkit-details-marker { display: none; }
.release-dropdown > summary::after {
  content: '+';
  margin-left: auto;
  color: var(--paper-dim);
  font-family: var(--font-label);
  font-size: 1.2rem;
}
.release-dropdown[open] > summary::after { content: '−'; color: var(--rust); }
.release-dropdown > summary:hover .title { color: var(--rust); }
.release-dropdown > summary:focus-visible { outline: 1px solid var(--rust); outline-offset: 4px; }
.release-dropdown .release-lyrics {
  margin: 1rem 0 0 4.5rem;
}
.discography .cover {
  width: 56px; height: 56px;
  background: var(--ink-soft);
  border: 1px solid var(--static);
  flex-shrink: 0;
  object-fit: cover;
}
.discography .title { display: block; font-family: var(--font-hand); font-size: 1.05rem; font-weight: 700; }
.discography .year { display: block; font-family: var(--font-label); color: var(--paper-dim); font-size: 0.9rem; }
.archive-intro {
  max-width: 54ch;
  margin: -0.55rem 0 1.2rem;
  padding-left: 0.8rem;
  border-left: 2px solid var(--bruise);
}

/* ============================================================
   9. me — currently watching tv + last.fm
   ============================================================ */
.tv {
  background: linear-gradient(180deg, var(--surface-hover) 0%, var(--ink-soft) 100%);
  border: 1px solid var(--static);
  border-radius: 14px;
  padding: 1rem 1rem 0.65rem;
  max-width: 420px;
}
.tv-list { display: grid; gap: 1.25rem; }
.tv-screen {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--ink-deep);
  border-radius: 6px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 1rem;
}
.tv-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.62;
  filter: saturate(0.8) contrast(1.08) sepia(0.12);
  mix-blend-mode: screen;
}
.tv-screen::before {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(ellipse at 50% 35%, color-mix(in srgb, var(--glow) 32%, transparent), transparent 65%);
  animation: tv-flicker 5s ease-in-out infinite;
  z-index: 1;
}
.tv-screen::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(180deg,
    rgba(0,0,0,0.32) 0px, rgba(0,0,0,0.32) 1px,
    transparent 1px, transparent 3px);
}
@keyframes tv-flicker {
  0%, 100% { opacity: 1; }
  47% { opacity: 0.85; }
  50% { opacity: 1; }
  93% { opacity: 0.9; }
}
.tv-show {
  position: relative; z-index: 1;
  font-family: var(--font-display);
  font-size: 1.5rem; line-height: 1.25;
  color: var(--paper);
  background: color-mix(in srgb, var(--ink-deep) 82%, transparent);
  border-left: 3px solid var(--rust);
  border-radius: 3px;
  padding: 0.2rem 0.45rem;
  text-shadow: 0 2px 4px #000, 0 0 10px #000;
}
.tv-ep {
  position: relative; z-index: 1;
  font-family: var(--font-label);
  font-size: 0.95rem;
  color: var(--glow);
  background: color-mix(in srgb, var(--ink-deep) 82%, transparent);
  display: inline-block;
  padding: 0.1rem 0.4rem;
  border-radius: 3px;
  text-shadow: 0 1px 3px #000;
  margin-top: 0.35rem;
}
.tv-note {
  position: relative; z-index: 1;
  font-size: 0.85rem;
  color: var(--paper-dim);
  background: color-mix(in srgb, var(--ink-deep) 82%, transparent);
  padding: 0.2rem 0.4rem;
  border-radius: 3px;
  text-shadow: 0 1px 3px #000;
  margin-top: 0.6rem;
}
.tv-base {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding-top: 0.6rem;
}
.tv-brand { font-family: var(--font-label); font-size: 0.85rem; color: var(--paper-dim); }
.tv-led {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--rust);
  box-shadow: 0 0 6px var(--rust);
  flex-shrink: 0;
}

.lastfm-status {
  display: flex; align-items: center; gap: 0.45rem;
  font-family: var(--font-label);
  font-size: 0.95rem;
  color: var(--rust);
  margin-bottom: 0.9rem;
}
.lastfm-status .dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--rust); flex-shrink: 0;
}
.lastfm-status .dot.is-playing { animation: pulse 1.4s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

.scrobbles li {
  display: flex; gap: 0.85rem; align-items: center;
  padding: 0.6rem 0;
  border-bottom: 1px dashed var(--static);
}
.scrobbles li:last-child { border-bottom: none; }
.scrobbles .art {
  width: 46px; height: 46px;
  background: var(--ink);
  border: 1px solid var(--static);
  flex-shrink: 0; object-fit: cover;
}
.scrobbles .info { min-width: 0; flex: 1 1 auto; }
.scrobbles .track {
  color: var(--paper);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.scrobbles .track a:hover { color: var(--rust); }
.scrobbles .artist {
  color: var(--paper-dim); font-size: 0.88rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.scrobbles .when {
  font-family: var(--font-label); font-size: 0.85rem;
  color: var(--static); flex-shrink: 0; text-align: right;
}
.scrobbles li.is-now .when { color: var(--rust); }


.facts li {
  display: flex; gap: 1rem;
  padding: 0.5rem 0;
  border-bottom: 1px dashed var(--static);
}
.facts li:last-child { border-bottom: none; }
.facts .key {
  font-family: var(--font-label);
  color: var(--paper-dim);
  width: 8rem; flex-shrink: 0;
}

.polaroid {
  background: var(--ink-soft);
  border: 1px solid var(--static);
  padding: 0.75rem 0.75rem 1.25rem;
  width: 220px;
  transform: rotate(-2.5deg);
  position: relative;
  flex-shrink: 0;
}
.polaroid::before {
  content: "";
  position: absolute; top: -10px; left: 50%;
  transform: translateX(-50%) rotate(2deg);
  width: 70px; height: 22px;
  background: color-mix(in srgb, var(--paper) 15%, transparent);
}
.polaroid img { aspect-ratio: 1; object-fit: cover; }
.bio-visual {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  flex: 0 0 220px;
}
.polaroid figcaption {
  font-family: var(--font-label);
  font-size: 0.95rem;
  color: var(--paper-dim);
  text-align: center;
  margin-top: 0.6rem;
}
.about-row { display: flex; gap: 2rem; flex-wrap: wrap; align-items: flex-start; }
.about-row > div:not(.bio-visual) { flex: 1 1 260px; }
/* ============================================================
   10. updates
   ============================================================ */
.updates-list li {
  padding: 0.75rem 0;
  border-bottom: 1px dashed var(--static);
  display: flex; gap: 1rem; align-items: baseline;
}
.updates-list li:last-child { border-bottom: none; }
.updates-list time {
  font-family: var(--font-label);
  font-size: 0.95rem;
  color: var(--paper-dim);
  flex-shrink: 0; width: 6rem;
}

/* ============================================================
   11. guestbook + live chat
   ------------------------------------------------------------
   GIF MAT
   The guestbook and cbox are third-party iframes and they paint an
   opaque background of their own — nothing you put "behind" them is
   ever visible. So the gif goes in the MAT around the frame instead,
   which reads as a sticker-covered border and, unlike a real
   background, can never sit under text and hurt legibility.

   Formatting that matters:
     - image-rendering: pixelated keeps the original pixel grid crisp
       instead of letting the browser smooth a small gif up to size
     - a tint layer over the tile stops it fighting whichever palette
       is active
     - background-size is in px, NOT %, so the tile stays the same
       physical size on every screen
   Swap the url() for any of your other gifs.
   ============================================================ */
.gif-mat {
  padding: 1.75rem;
  border-radius: 6px;
  background-image: url('nichijouneko.gif');
  background-repeat: repeat;
  background-size: 96px auto;
  image-rendering: pixelated;
  position: relative;
  isolation: isolate;
}
.gif-mat::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 6px;
  background: color-mix(in srgb, var(--ink-deep) 55%, transparent);
  pointer-events: none;
  z-index: 0;
}
.gif-mat > * { position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  /* an animated tile is motion whether or not it's "the interface" */
  .gif-mat { background-image: none; background-color: var(--ink-soft); }
}

.guestbook-embed {
  position: relative;
  isolation: isolate;
  border: 1px solid var(--static);
  background: color-mix(in srgb, var(--ink) 20%, transparent);
  min-height: 220px;
  display: flex; align-items: center; justify-content: center;
  color: var(--paper-dim);
  font-size: 0.9rem; text-align: center;
  padding: 1rem;
  border-radius: 4px;
  box-shadow: inset 0 0 0 4px var(--accent-wash);
}
.guestbook-embed::before {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ink) 20%, transparent);
  pointer-events: none;
  z-index: 0;
}
.guestbook-embed iframe { position: relative; z-index: 1; display: block; width: min(100%, 420px); min-height: 420px; border: 3px solid color-mix(in srgb, var(--glow) 62%, transparent); background: transparent; }

.chat-embed {
  border: 3px solid color-mix(in srgb, var(--glow) 62%, transparent);
  background: color-mix(in srgb, var(--ink) 20%, transparent);
  padding: 0.45rem;
  min-height: 420px;
}
.chat-embed iframe {
  display: block;
  width: 100%;
  min-height: 390px;
  border: 1px solid color-mix(in srgb, var(--glow) 50%, transparent);
  background: transparent;
  border-color: rgba(142, 216, 255, 0.5);
}

.instagram-media {
  max-width: 540px !important;
  min-width: 0 !important;
  width: 100% !important;
  margin: 1rem 0 !important;
  background: var(--ink-soft) !important;
  border-color: var(--static) !important;
}

.link-list li { padding: 0.6rem 0; border-bottom: 1px dashed var(--static); }
.link-list li:last-child { border-bottom: none; }
.link-list .handle { color: var(--paper-dim); font-size: 0.88rem; }

.settings-list { margin-top: 1.25rem; }
.setting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 0;
  border-bottom: 1px dashed var(--static);
}
.setting-row strong,
.setting-row .dim { display: block; }
.setting-button {
  font-family: var(--font-label);
  font-size: 0.95rem;
  color: var(--paper-dim);
  background: var(--ink-soft);
  border: 1px solid var(--static);
  padding: 0.4rem 0.75rem;
  white-space: nowrap;
  cursor: pointer;
}
.setting-select {
  font-family: var(--font-label);
  font-size: 0.95rem;
  color: var(--paper-dim);
  background: var(--ink-soft);
  border: 1px solid var(--static);
  padding: 0.4rem 0.55rem;
}
.setting-range {
  width: min(100%, 180px);
  height: 1.5rem;
  margin: 0;
  padding: 0;
  appearance: none;
  background: transparent;
  cursor: pointer;
}
.setting-range::-webkit-slider-runnable-track {
  width: 100%;
  height: 0.3rem;
  border: 1px solid var(--static);
  background: var(--ink-soft);
}
.setting-range::-moz-range-track {
  width: 100%;
  height: 0.3rem;
  border: 1px solid var(--static);
  background: var(--ink-soft);
}
.setting-range::-webkit-slider-thumb {
  width: 0.9rem;
  height: 1.1rem;
  margin-top: -0.5rem;
  appearance: none;
  border: 1px solid var(--rust);
  border-radius: 0;
  background: var(--glow);
}
.setting-range::-moz-range-thumb {
  width: 0.9rem;
  height: 1.1rem;
  border: 1px solid var(--rust);
  border-radius: 0;
  background: var(--glow);
}
.setting-range:focus-visible {
  outline: 2px solid var(--glow);
  outline-offset: 3px;
}
.setting-select:focus { color: var(--paper); border-color: var(--rust); }
.setting-button:hover { color: var(--paper); border-color: var(--rust); }

/* ============================================================
   12. contact / socials
   ============================================================ */
.social-lead {
  display: block;
  background: var(--ink-soft);
  border: 1px solid var(--bruise);
  padding: 1.35rem 1.5rem;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.social-lead:hover { border-color: var(--rust); background: var(--surface-hover); }
.social-lead .where {
  font-family: var(--font-swash);
  font-size: 1.7rem;
  letter-spacing: 0.03em;
  color: var(--paper);
  display: block;
}
.social-lead .handle {
  font-family: var(--font-label);
  font-size: 1.05rem;
  color: var(--glow);
  display: block;
  margin-top: 0.2rem;
}
.social-lead .why {
  color: var(--paper-dim);
  font-size: 0.9rem;
  margin-top: 0.6rem;
  max-width: 48ch;
}

.socials { margin-top: 1rem; }
.socials li {
  display: flex; align-items: baseline; gap: 0.85rem;
  padding: 0.7rem 0;
  border-bottom: 1px dashed var(--static);
}
.socials li:last-child { border-bottom: none; }
.socials .where {
  font-family: var(--font-display);
  font-size: 1.05rem;
  width: 8.5rem; flex-shrink: 0;
}
.socials a:hover, .socials a:hover .where { color: var(--rust); }
.socials .handle { color: var(--paper-dim); font-size: 0.9rem; }

/* ============================================================
   13. forms
   ============================================================ */
label {
  display: block;
  font-family: var(--font-label);
  font-size: 0.95rem;
  color: var(--paper-dim);
  margin-bottom: 0.35rem;
}
input, textarea {
  width: 100%;
  background: var(--ink-soft);
  border: 1px solid var(--static);
  color: var(--paper);
  padding: 0.65rem 0.75rem;
  font-family: var(--font-body);
  font-size: 0.95rem;
  margin-bottom: 1.1rem;
}
textarea { resize: vertical; min-height: 120px; }

/* ============================================================
   14. no-js fallback — everything becomes a plain scrolling page
   ============================================================ */
.no-js-note {
  border: 1px dashed var(--rust);
  padding: 1rem;
  margin-bottom: 2rem;
  color: var(--paper-dim);
  font-size: 0.9rem;
}
.js .no-js-note { display: none; }

html:not(.js) .xmb { max-width: 660px; margin: 5rem auto 0; padding: 0 1.5rem; }
html:not(.js) .xmb-cursor { display: none; }
html:not(.js) .xmb-track { display: block; }
html:not(.js) .xmb-col { margin-bottom: 1.5rem; }
html:not(.js) .xmb-icon { height: auto; flex-direction: row; justify-content: flex-start; }
html:not(.js) .xmb-item { height: auto; justify-content: flex-start; padding: 0.35rem 0; }
html:not(.js) .panel-head { display: none; }
html:not(.js) .leaf { margin-bottom: 3rem; }

@media (prefers-reduced-motion: reduce) {
  .js .xmb-track, .js .xmb-col, .js .panel { transition: none !important; }
  /* xmb.js already flattens the lens for reduced motion; this is the
     belt-and-braces version in case the CSS loads without the JS. */
  .js .xmb { perspective: none; }
  .js .xmb-col { transform: none; filter: none; }
  .xmb-item { filter: none; transform: scale(var(--row-scale, 1)); }
  .marquee span { animation: none; padding-left: 0; }
  .tv-screen::before, .lastfm-status .dot.is-playing { animation: none; }
}

/* ============================================================
   15. boot sequence
   ------------------------------------------------------------
   The overlay only exists while <html class="booting">, which is
   set in the <head> before first paint and removed by boot.js.
   Nothing here runs for JS-off visitors.
   ============================================================ */
.boot { display: none; }
html.js.booting .boot {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 9999;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2.5rem;
  background: var(--ink-deep);
  cursor: pointer;
  opacity: 1;
  transition: opacity 0.65s ease;
}
html.js.booting .boot.is-leaving { opacity: 0; pointer-events: none; }

/* hold the interface back so it doesn't flash underneath */
html.js.booting .topbar,
html.js.booting .xmb,
html.js.booting .hint,
html.js.booting .grain { opacity: 0; }

.boot-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 9rem;
}

/* the hairline that opens outward from the middle, pre-logo */
.boot-line {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 1px;
  background: var(--glow);
  box-shadow: 0 0 12px var(--glow);
  transform: translate(-50%, -50%);
  opacity: 0;
}
.boot.is-running .boot-line { animation: boot-line 2.1s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
@keyframes boot-line {
  0%   { width: 0;     opacity: 0; }
  12%  { width: 3px;   opacity: 1; }
  45%  { width: min(22rem, 68vw); opacity: 0.9; }
  75%  { width: min(22rem, 68vw); opacity: 0.25; }
  100% { width: min(26rem, 76vw); opacity: 0; }
}

/* ---- the logo ----
   Swap the markup for <img class="boot-mark" src="logo.svg"> and this
   still works: the fade-in lives on .boot-mark, the per-word stagger
   lives on .boot-word and simply has nothing to apply to. */
.boot-mark {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 0.92;
  max-width: 78vw;
  opacity: 0;
}
img.boot-mark { width: min(22rem, 70vw); height: auto; }
.boot.is-running .boot-mark { animation: boot-mark 1.6s ease forwards 0.55s; }
@keyframes boot-mark {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.boot-word {
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 11vw, 5rem);
  color: var(--paper);
  letter-spacing: 0.02em;
  opacity: 0;
  filter: blur(10px);
  transform: translateY(0.35em);
}
.boot-word:nth-child(2) { color: var(--rust); }
.boot.is-running .boot-word { animation: boot-word 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
.boot.is-running .boot-word:nth-child(1) { animation-delay: 0.55s; }
.boot.is-running .boot-word:nth-child(2) { animation-delay: 0.78s; }
@keyframes boot-word {
  0%   { opacity: 0; filter: blur(10px); transform: translateY(0.35em); }
  60%  { opacity: 1; filter: blur(0);    transform: translateY(0); }
  /* the flare lands on the chord bloom in boot.js (BOOT.tune.bloomAt) */
  72%  { text-shadow: 0 0 26px var(--glow), 0 0 60px color-mix(in srgb, var(--glow) 45%, transparent); }
  100% { opacity: 1; filter: blur(0); transform: translateY(0); text-shadow: 0 0 10px color-mix(in srgb, var(--glow) 25%, transparent); }
}

/* "press any key" — the whole overlay is clickable, this is the
   visible affordance and the keyboard-reachable target. */
.boot-start {
  background: none;
  border: 1px solid var(--static);
  color: var(--paper-dim);
  font-family: var(--font-label);
  font-size: 0.85rem;
  letter-spacing: 0.22em;
  text-transform: lowercase;
  padding: 0.55rem 1.4rem;
  cursor: pointer;
  animation: boot-pulse 1.9s ease-in-out infinite;
}
.boot-start:hover, .boot-start:focus-visible { color: var(--paper); border-color: var(--rust); }
.boot.is-running .boot-start { opacity: 0; pointer-events: none; animation: none; transition: opacity 0.3s ease; }
@keyframes boot-pulse {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .boot.is-running .boot-line { display: none; }
  .boot.is-running .boot-mark,
  .boot.is-running .boot-word {
    animation: none;
    opacity: 1;
    filter: none;
    transform: none;
  }
  .boot-start { animation: none; opacity: 0.8; }
}

/* ============================================================
   16. lyrics
   ============================================================ */
.lyric-list { margin-top: 1.5rem; }

.lyric {
  border: 1px solid var(--static);
  border-left: 3px solid var(--bruise);
  background: color-mix(in srgb, var(--ink-soft) 72%, transparent);
  margin-bottom: 0.85rem;
}
.lyric[open] { border-left-color: var(--rust); }

.lyric > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.9rem;
  padding: 0.85rem 1rem 0.85rem 2.1rem;
  position: relative;
  cursor: pointer;
  list-style: none;
}
.lyric > summary::-webkit-details-marker { display: none; }

/* hand-rolled disclosure triangle so it follows the palette */
.lyric > summary::before {
  content: '';
  position: absolute;
  left: 0.95rem;
  top: 1.15rem;
  width: 0; height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  color: var(--paper-dim);
  transition: transform 0.18s ease;
}
.lyric[open] > summary::before { transform: rotate(90deg); color: var(--rust); }
.lyric > summary:hover { color: var(--paper); }
.lyric > summary:focus-visible { outline: 1px solid var(--rust); outline-offset: -2px; }

.lyric-title {
  font-family: var(--font-display);
  font-size: 1.08rem;
  color: var(--paper);
}
.lyric-meta {
  font-family: var(--font-label);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  color: var(--paper-dim);
  margin-left: auto;
}

.lyric-tracks {
  padding: 1.1rem 1.4rem 0.4rem 2.1rem;
  border-top: 1px dashed var(--static);
}
.lyric-track { margin-bottom: 0.6rem; }
.lyric-track:last-child { margin-bottom: 0; }
.lyric-track > summary { padding: 0.65rem 0.9rem 0.65rem 1.8rem; }
.lyric-track > summary::before {
  left: 0.7rem;
  top: 0.95rem;
}
.lyric-track .lyric-title { font-size: 1rem; }

.words {
  margin: 0;
  padding: 0.4rem 1.4rem 1.3rem 2.1rem;
  max-width: 52ch;
  line-height: 1.85;
  color: var(--paper-dim);
  font-size: 0.95rem;
  border-top: 1px dashed var(--static);
  padding-top: 1.1rem;
}
.lyric-credit { padding: 0 1.4rem 1.2rem 2.1rem; margin: 0; }

@media (max-width: 560px) {
  .lyric-meta { margin-left: 0; width: 100%; }
}

/* ============================================================
   17. icon presentation
   ------------------------------------------------------------
   One treatment, no skins: big artwork, a glow when selected.
   The frame belongs on the arch, not on every icon.
   ============================================================ */
.xmb-icon.is-sel svg {
  filter: drop-shadow(0 0 16px color-mix(in srgb, var(--glow) 55%, transparent));
}
.xmb-icon.is-sel .settings-toolbox {
  filter: brightness(0) invert(1) drop-shadow(0 0 10px var(--glow));
}

@media (prefers-reduced-motion: reduce) {
  .xmb-icon { transform: scale(var(--icon-scale, 1)); }
  .js .xmb-col { transform: scale(var(--col-scale, 1)); }
}

/* ============================================================
   18. handwriting
   ------------------------------------------------------------
   Assign has NO digits and NO punctuation — those glyphs are empty
   outlines. Anything wearing --font-hand must be letters and spaces
   only, or characters silently vanish. It's also thin, so it needs
   size and a touch more line-height than the body font.
   ============================================================ */
.tv-note {
  font-family: 'Comic Sans MS', 'Comic Neue', cursive;
  font-size: 1.35rem;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--paper);
  opacity: 0.9;
}

/* ============================================================
   19. gifs
   ============================================================ */

/* ---- beside, not behind ----
   For anything sitting next to a third-party iframe. The frame takes
   the space it needs and the gif takes what's left, down to a floor
   of 130px; below 760px the whole thing stacks. */
.gif-beside {
  display: flex;
  align-items: flex-start;
  gap: 1.25rem;
  margin-top: 1rem;
}
.gif-beside > *:first-child { flex: 1 1 auto; min-width: 0; }

.gif-aside {
  margin: 0;
  flex: 0 0 auto;
  width: clamp(130px, 22%, 200px);
  align-self: flex-end;
}
.gif-aside img {
  display: block;
  width: 100%;
  height: auto;
  image-rendering: pixelated;
  border: 1px solid var(--static);
  border-radius: 4px;
  background: var(--ink-deep);
  /* the gifs are all white-background crops from different sources,
     so a shared tint is what stops them looking pasted on */
  filter: saturate(0.85) contrast(1.05);
}

@media (max-width: 760px) {
  .gif-beside { flex-direction: column; }
  .gif-aside { width: min(60%, 190px); align-self: flex-start; }
}

@media (max-width: 480px) {
  :root {
    --col-w: 128px;
    --icon-size: 50px;
    --icon-h: 108px;
    --icon-gap: 12px;
  }
  .xmb-icon .label { font-size: 0.88rem; }
  .xmb-item { padding-inline: 0.35rem; font-size: 0.9rem; }
  .panel-inner { padding-top: 4rem; padding-bottom: 3rem; }
  .panel-head { gap: 0.65rem; margin-bottom: 1.35rem; }
  .back-btn, .setting-button, .setting-select {
    min-height: 2.5rem;
  }
  .setting-row {
    align-items: flex-start;
    flex-wrap: wrap;
  }
  .setting-row > div:first-child { flex: 1 1 12rem; }
  .setting-button, .setting-select { margin-left: auto; }
  .dsp-grid { grid-template-columns: 1fr; }
  .video-slot { padding: 0.55rem; }
  .mobile-hint {
    display: block;
    position: fixed;
    left: 50%;
    bottom: 0.8rem;
    z-index: 25;
    transform: translateX(-50%);
    width: calc(100% - 1.5rem);
    color: var(--paper-dim);
    font-family: var(--font-label);
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    text-align: center;
    text-shadow: 0 1px 3px var(--ink-deep), 0 0 8px var(--ink);
  }
  body.panel-open .mobile-hint { display: none; }
}

@media (max-width: 360px) {
  .panel-inner { padding-inline: 0.75rem; }
  .leaf h1 { font-size: 1.8rem; }
  .leaf h2 { font-size: 1.3rem; }
}

.leaf p,
.leaf li,
.leaf a,
.leaf h1,
.leaf h2,
.leaf h3 {
  overflow-wrap: anywhere;
}

/* ---- boot spinner ----
   Hidden until the sequence actually starts, so the "press any key"
   screen stays still. Fades out with the logo. */
.boot-spin {
  position: absolute;
  bottom: -2.5rem;
  left: 50%;
  width: 54px;
  height: auto;
  transform: translateX(-50%);
  image-rendering: pixelated;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s ease;
}
.boot.is-running .boot-spin { opacity: 0.85; transition-delay: 0.3s; }
.boot.is-leaving .boot-spin { opacity: 0; transition-delay: 0s; }

/* ---- idle mascot ----
   Parked off screen until xmb.js decides you've gone quiet. Runs in
   from the right, leaves the instant you touch anything. */
.idle-mascot {
  position: fixed;
  right: 1.5rem;
  bottom: 1.25rem;
  z-index: 40;
  width: 96px;
  height: auto;
  image-rendering: pixelated;
  pointer-events: none;
  opacity: 0;
  transform: translateX(160%);
  transition: transform 0.7s cubic-bezier(0.22, 0.61, 0.36, 1), opacity 0.4s ease;
}
body.idle .idle-mascot { opacity: 0.9; transform: translateX(0); }
body.panel-open .idle-mascot { opacity: 0; transform: translateX(160%); }

@media (max-width: 720px) {
  .idle-mascot { width: 68px; right: 0.75rem; bottom: 0.75rem; }
}

@media (prefers-reduced-motion: reduce) {
  .idle-mascot { display: none; }
  .boot-spin { display: none; }
}
