#terminal-host, #wall-mobile-slot { display: none; }
/* the pile canvas only exists on desktop; on mobile the devices flow as a
   plain row and there's no fixed geometry for cables to run between */
.wall-cables, .wall-fusebox { display: none; }

/* Mobile-first default: a plain inline row, same as it used to sit inside
   .contact-items. Desktop (below) switches it to an absolute square stack
   parked next to the laptop. */
#social-icons { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.social-icon { display: block; position: relative; }
.social-icon img { width: 18px; height: 18px; filter: brightness(.85) saturate(.7); }

@keyframes icon-glitch {
  0%, 91%, 100% { clip-path: inset(0 0 0 0); transform: translate(0, 0); filter: brightness(.85) saturate(.7) hue-rotate(0deg); }
  92% { clip-path: inset(15% 0 45% 0); transform: translate(-2px, 0); filter: brightness(1.15) saturate(1.4) hue-rotate(35deg); }
  93% { clip-path: inset(55% 0 8% 0); transform: translate(2px, 0); filter: brightness(.65) saturate(1.5) hue-rotate(-25deg); }
  94% { clip-path: inset(0 0 70% 0); transform: translate(-1px, 0); filter: brightness(1.2) hue-rotate(15deg); }
  95%, 100% { clip-path: inset(0 0 0 0); transform: translate(0, 0); filter: brightness(.85) saturate(.7) hue-rotate(0deg); }
}

/* Junction box + the wires running off it — drawCables() in wall.js writes
   the actual path data from live device positions, so this only needs to
   supply the look, never coordinates. */
.wall-cables { position: absolute; inset: 0; z-index: 1; overflow: visible; pointer-events: none; }
.cable-wire {
  fill: none;
  stroke: #1b201e;
  stroke-width: 5;
  stroke-linecap: round;
  animation: cable-surge 7s steps(1) infinite;
}
.cable-wire:nth-of-type(2) { animation-delay: -2.3s; }
.cable-wire:nth-of-type(3) { animation-delay: -4.6s; }
.cable-wire:nth-of-type(4) { animation-delay: -1.1s; }
.cable-wire:nth-of-type(5) { animation-delay: -5.8s; }
/* an old wire arcing, not a neon flicker — warm amber only */
@keyframes cable-surge {
  0%, 88%, 92%, 100% { stroke: #1b201e; }
  89% { stroke: #a8621f; }
  90% { stroke: #ff9d42; }
  91% { stroke: #1b201e; }
}
.wall-fusebox { position: absolute; pointer-events: none; }
.wall-fusebox svg { width: 100%; height: auto; overflow: visible; filter: brightness(.8) saturate(.7) sepia(.12); }
.fuse-led { filter: drop-shadow(0 0 3px currentColor); animation: led-fuzz 6.4s steps(1) infinite; }

.media-wall {
  position: relative;
  color: var(--text-1);
  filter: drop-shadow(10px 12px 0 #02050c);
  user-select: none;
}
.wall-device {
  position: relative;
  display: block;
  cursor: crosshair;
  outline: none;
  transition: filter .2s ease, transform .2s ease;
}
.wall-device:focus-visible { outline: 2px solid var(--glitch-yellow); outline-offset: 5px; }
.wall-device:focus-visible { z-index: 8; }
.wall-device svg { width: 100%; height: auto; overflow: visible; filter: brightness(.78) saturate(.68) sepia(.12); }
.wall-device::after {
  content: '';
  position: absolute;
  inset: 12% 10%;
  background: repeating-linear-gradient(to bottom, transparent 0 3px, rgba(200, 216, 224, .09) 3px 4px);
  opacity: .28;
  pointer-events: none;
}
.wall-tv::before {
  content: '';
  position: absolute;
  left: 8.5%;
  top: 15%;
  z-index: 3;
  width: 66.5%;
  height: 1px;
  background: rgba(200, 216, 224, .7);
  box-shadow: 0 0 5px var(--glitch-cyan);
  opacity: 0;
  pointer-events: none;
}
/* Geometry derived from the CRT bezel rect in wall.html (x29 y35 w245 h166 in
   a 360x245 viewBox, 4px stroke centred on the edge → inner 31,37 241x162).
   Percentages are of the device box, so it tracks any device width. */
.wall-video {
  position: absolute;
  left: 8.61%;
  top: 15.1%;
  width: 66.94%;
  height: 66.12%;
  border: 0;
  border-radius: 1.4%;
  background: #081923;
  object-fit: cover;
  opacity: .3;
  pointer-events: none;
  transition: opacity .3s;
}
.wall-tv.is-on .wall-video { opacity: .82; }
#terminal-host .wall-terminal {
  position: relative;
  inset: auto;
  z-index: 2;
  width: 100%;
  transform: rotate(-1.5deg);
  transform-origin: bottom center;
}
.board-gif {
  position: absolute;
  top: 12.3%;
  left: 8.7%;
  z-index: 2;
  width: 82.9%;
  height: 73.6%;
  object-fit: cover;
  opacity: 0;
  filter: saturate(1.25) contrast(1.15) brightness(.78);
}
.board-gif.is-loaded { opacity: 1; }
.wall-marquee {
  position: relative;
  text-align: center;
  font-family: 'Berkeley Mono', ui-monospace, monospace;
  text-transform: uppercase;
  letter-spacing: .08em;
  overflow-wrap: break-word;
}
.marquee-title, .marquee-artist {
  display: block;
  line-height: var(--pitch);
  color: #262b2f;
  text-shadow: none;
  transition: color .4s ease, text-shadow .4s ease;
}
.marquee-title { font-size: .8rem; font-weight: 700; }
.marquee-artist { font-size: .68rem; }
.wall-marquee.is-on .marquee-title {
  color: var(--glitch-pink);
  text-shadow: 0 0 4px var(--glitch-pink), 0 0 12px var(--glitch-pink), 0 0 26px rgba(255, 45, 120, .5);
}
.wall-marquee.is-on .marquee-artist {
  color: var(--glitch-cyan);
  text-shadow: 0 0 4px var(--glitch-cyan), 0 0 10px var(--glitch-cyan);
}
.wall-marquee.is-on { animation: led-fuzz 6.4s steps(1) infinite; }
/* (movie-name marquee removed — the current channel is announced via the
   video title / aria-label only) */
.terminal-screen {
  position: absolute;
  top: 10%; left: 12.8%; width: 74.5%; height: 51%;
  z-index: 2;
  display: flex;
  flex-direction: column;
  padding: .2rem .35rem;
  overflow: hidden;
  color: #9fffb6;
  font-size: clamp(.38rem, .52vw, .5rem);
  line-height: 1.1;
  text-shadow: 0 0 7px #27d763;
  cursor: text;
}
.terminal-log { flex: 1; overflow: hidden; scrollbar-width: none; }
.terminal-line { display: block; white-space: pre-wrap; }
.terminal-command { color: #d0f4a6; }
.terminal-form { display: flex; gap: .3em; flex-shrink: 0; }
.terminal-input {
  min-width: 0;
  flex: 1;
  border: 0;
  outline: 0;
  padding: 0;
  background: transparent;
  color: #d0f4a6;
  font: inherit;
  line-height: inherit;
  caret-color: var(--glitch-pink);
}
.terminal-screen.is-vim > .terminal-log, .terminal-screen.is-vim > .terminal-form { display: none; }
.vim-editor {
  display: flex;
  min-height: 0;
  flex: 1;
  flex-direction: column;
  outline: 0;
  overflow: hidden;
}
.vim-editor[hidden] { display: none; }
.vim-buffer { flex: 1; overflow: hidden; }
.vim-line { min-height: 1.1em; white-space: pre; }
.vim-cursor { background: #9fffb6; color: #071219; text-shadow: none; }
.vim-status { flex-shrink: 0; padding-inline: .2em; background: #7fa88a; color: #071219; text-shadow: none; }
.vim-command { min-height: 1.1em; flex-shrink: 0; }
.scope-controls {
  position: absolute;
  top: 17%;
  right: 1%;
  z-index: 4;
  width: 48%;
  height: 79%;
  border: 0;
  background: transparent;
  cursor: crosshair;
}
.scope-controls:focus-visible { outline: none; }
.tv-channel { cursor: pointer; }
.tv-channel circle { transition: stroke .15s ease, fill .15s ease; }
.tv-channel:hover circle { stroke: var(--glitch-cyan); fill: #14202b; }
.tv-channel:hover path { stroke: var(--glitch-cyan); }
/* warm amber, not the glitch-pink neon — reads as an old incandescent
   indicator bulb on aged hardware, not a sci-fi accent light */
.power-led { fill: #d9822b; opacity: .3; filter: drop-shadow(0 0 2px currentColor); transition: opacity .2s; }
.wall-device.is-on .power-led { opacity: 1; filter: drop-shadow(0 0 6px #d9822b) drop-shadow(0 0 12px #d9822b); }
.speaker-cone { transform-box: fill-box; transform-origin: center; }
.wall-speaker.is-on .speaker-cone-low { animation: cone-pulse .7s ease-in-out infinite alternate; }
.wall-speaker.is-on .speaker-cone-high { animation: cone-pulse .43s ease-in-out infinite alternate-reverse; }
.wall-speaker-flip.is-on .flip-diaphragm { animation: flip-pulse .55s ease-in-out infinite alternate; transform-box: fill-box; transform-origin: center; }
.wall-tv.is-on::before { animation: crt-sweep 4.6s linear infinite; }
.wall-terminal.is-on .terminal-screen { animation: phosphor-fuzz 6.2s steps(1) infinite; }
.scope-trace { vector-effect: non-scaling-stroke; filter: drop-shadow(0 0 2px #63d47e); }

@keyframes cone-pulse { to { transform: scale(.965); } }
@keyframes flip-pulse { to { transform: scale(.985, .96); } }
@keyframes crt-sweep {
  0%, 12% { opacity: 0; transform: translateY(0); }
  14% { opacity: .45; }
  34% { opacity: .18; transform: translateY(8.8rem); }
  36%, 100% { opacity: 0; transform: translateY(8.8rem); }
}
@keyframes phosphor-fuzz {
  0%, 68%, 71%, 100% { opacity: 1; transform: translateX(0); }
  69% { opacity: .72; transform: translateX(1px); }
  70% { opacity: .9; transform: translateX(-1px); }
}
@keyframes led-fuzz {
  0%, 74%, 77%, 100% { opacity: .9; }
  75% { opacity: .3; }
  76% { opacity: 1; }
}
/* Desktop: wall widget lives in the header sidebar, devices piled and
   staggered inside a fixed-height absolute-position canvas. */
@media (min-width: 1100px) {
  /* Two clean columns, no overlap: text keeps its lane, wall keeps its own.
     The empty third row absorbs the slack (the wall column is taller than
     the text column, and without it the extra height lands *between* the
     bio and the contact row as a dead void). */
  #header {
    position: relative;
    grid-template-columns: minmax(0, 1fr) clamp(360px, 34vw, 580px);
    grid-template-areas:
      "info    wall"
      "contact wall";
    grid-template-rows: auto 1fr;
    column-gap: clamp(1rem, 3vw, 4rem);
  }
  #info { grid-area: info; }
  /* text column is shorter than the wall, so the wall sets the header's
     height and leaves slack below the bio; align-self:start puts contact-info
     at the top of that slack, and centerContact() in wall.js then nudges it
     down with margin-top, snapped to a whole pitch, so it lands centered
     between the bio and the PROJECTS bar instead of stuck to either edge. */
  #contact-info { grid-area: contact; align-self: start; }
  #wall-host { display: block; grid-area: wall; align-self: start; }
  #terminal-host {
    display: block;
    position: absolute;
    z-index: 3;
    width: 160px;
    /* left/top/width are written by placeTerminal() in wall.js */
  }
  /* parked beside the laptop by placeSocialIcons() in wall.js: a small row
     of square glitch-tiles instead of a plain inline icon row. */
  #social-icons {
    position: absolute;
    z-index: 5;
    flex-direction: row;
    /* wraps only when placeSocialIcons() in wall.js has to constrain the
       width to fit the laptop's own (narrower) empty box */
    flex-wrap: wrap;
    gap: var(--space-2);
  }
  .social-icon {
    /* --icon-size is only ever set inline as a last-resort shrink, when
       placeSocialIcons() in wall.js can't find room for the full 2-pitch
       size anywhere near the laptop */
    width: var(--icon-size, calc(2 * var(--pitch)));
    height: var(--icon-size, calc(2 * var(--pitch)));
    border: 1px solid var(--accent);
    background: var(--card-bg);
    box-shadow: var(--shadow-1);
    transition: border-color .2s ease, box-shadow .2s ease;
  }
  /* same four colors as the section bars, in the same order they appear
     down the page (cyan/pink/blue/yellow) — one glitch-bar color per icon */
  .social-icon:nth-child(1) { --accent: var(--glitch-cyan); }
  .social-icon:nth-child(2) { --accent: var(--glitch-pink); }
  .social-icon:nth-child(3) { --accent: var(--glitch-blue); }
  .social-icon:nth-child(4) { --accent: var(--glitch-yellow); }
  .social-icon:hover { box-shadow: var(--shadow-1), 4px 4px 8px var(--accent); }
  .social-icon img { width: 100%; height: 100%; object-fit: contain; padding: 22%; animation: icon-glitch 5.6s steps(1) infinite; }
  .social-icon:nth-child(2) img { animation-delay: -1.4s; }
  .social-icon:nth-child(3) img { animation-delay: -2.8s; }
  .social-icon:nth-child(4) img { animation-delay: -4.2s; }
  #contact-info { padding-right: 0; }
  /* pull projects bar closer to the contact row */
  #header + .band { margin-top: calc(-1 * var(--pitch)); }

  /* MUST stay a whole multiple of --pitch: the wall is the tallest thing in
     #header, so it sets the header height, and any remainder pushes every
     section bar below it off the scanline grid. A short canvas is what
     makes the pile read as a wall instead of a scattered desk: device pixel
     sizes come from their own width% (of the pile's fluid WIDTH) and don't
     shrink with this, so a shorter pile means the same-size devices sit
     close enough to overlap. */
  .wall-pile { position: relative; height: calc(21 * var(--pitch)); }
  .wall-device { position: absolute; }
  .wall-cables, .wall-fusebox { display: block; }
  /* tucked in the gap between the board and the TV — drawCables() reads its
     rendered box, so it just needs to sit somewhere that isn't already taken */
  .wall-fusebox { width: 11%; left: 1%; top: 21%; z-index: 2; }
  /* board kept narrow and pinned to the far left so it never reaches under
     the JBL, which sits on the TV's right-hand side */
  .wall-board { width: 24%; left: -2%; top: 0%; z-index: 2; transform: rotate(-3deg); transform-origin: top center; }
  .wall-tv { width: 56%; left: -2%; top: 27%; z-index: 4; }
  /* JBL rests ON the TV's lid, PA rests ON the scope: both anchored by
     `bottom` rather than `top`. The pile height is fixed but its width is
     fluid, so a top-anchored device whose own height changes with width
     would drift off whatever it's supposed to be sitting on — a fixed
     bottom edge keeps the contact point true at every viewport width. */
  .wall-speaker-flip { width: 22%; left: 32%; bottom: 71%; z-index: 6; transform: rotate(-2deg); transform-origin: bottom center; }
  .wall-speaker-pa { width: 22%; left: 68%; bottom: 52%; z-index: 5; transform: rotate(1deg); transform-origin: bottom center; }
  /* bottom edge matched to the TV's own bottom edge (at the pile's max
     580px width — the two are peers, not a rest relationship, so this is
     tuned for the common case rather than JS-anchored like the two above) */
  .wall-scope { width: 40%; left: 58%; top: 45%; z-index: 3; }
  .media-wall.layout-2 .wall-tv { width: 46%; left: 0; top: 65%; transform: rotate(-5deg); transform-origin: top left; }
  .media-wall.layout-2 .wall-scope { width: 48%; left: 50%; top: 75%; transform: rotate(2deg); transform-origin: bottom left; }
  .media-wall.layout-2 .wall-speaker-pa { width: 22%; left: 72%; top: 55%; transform: rotate(-4deg); }
  .media-wall.layout-2 .wall-speaker-flip { width: 24%; left: 6%; bottom: 34%; transform: rotate(-4deg); }
  .media-wall.layout-3 .wall-tv { width: 56%; left: -2%; top: 50%; transform: rotate(2deg); }
  .media-wall.layout-3 .wall-scope { width: 42%; left: 58%; top: 82%; transform: rotate(-3deg); transform-origin: bottom left; }
  .media-wall.layout-3 .wall-speaker-pa { width: 26%; left: 62%; top: 42%; transform: rotate(5deg); }
  .media-wall.layout-3 .wall-speaker-flip { width: 28%; left: 10%; bottom: 49%; transform: rotate(2deg); }
  .media-wall.layout-4 .wall-tv { width: 44%; left: 50%; top: 68%; transform: rotate(-3deg); transform-origin: bottom right; }
  .media-wall.layout-4 .wall-scope { width: 50%; left: 2%; top: 76%; transform: rotate(1deg); }
  .media-wall.layout-4 .wall-speaker-pa { width: 28%; left: 5%; top: 48%; transform: rotate(-5deg); }
  .media-wall.layout-4 .wall-speaker-flip { width: 22%; left: 58%; bottom: 31%; transform: rotate(-3deg); }
  .media-wall.layout-5 .wall-tv { width: 52%; left: 25%; top: 70%; transform: rotate(4deg); transform-origin: bottom center; }
  .media-wall.layout-5 .wall-scope { width: 38%; left: 0; top: 80%; transform: rotate(-2deg); }
  .media-wall.layout-5 .wall-speaker-pa { width: 20%; left: 78%; top: 60%; transform: rotate(-6deg); }
  .media-wall.layout-5 .wall-speaker-flip { width: 24%; left: 36%; bottom: 29%; transform: rotate(4deg); }
}

/* Mobile: no room for a piled, rotated canvas — the wall re-mounts (via
   wall.js) into #wall-mobile-slot between the portrait and the bio text,
   and devices disperse into a plain wrapping row instead of stacking. */
@media (max-width: 1099px) {
  #wall-mobile-slot { display: block; margin-block: var(--pitch); }
  .wall-pile {
    height: auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: flex-end;
    gap: var(--pitch) calc(var(--pitch) * .75);
  }
  /* Each device's SVG has its own natural aspect ratio (tv 360:245, pa
     220:300, flip 320:128, scope 500:280, board 310:220) — a single flat
     width made the tall/narrow pa speaker tower over the wide/flat tv.
     Widths below are tuned per device so rendered heights land close together. */
  .wall-device { width: min(28vw, 104px); flex: 0 0 auto; }
  .wall-tv { width: min(40vw, 150px); }
  .wall-speaker-pa { width: min(16vw, 60px); }
  .wall-speaker-flip { width: min(30vw, 112px); }
}

@media (prefers-reduced-motion: reduce) {
  .wall-device, .wall-device::before, .terminal-screen, .speaker-cone, .power-led, .scope-trace, .wall-marquee, .social-icon img, .cable-wire, .fuse-led { animation: none !important; transition: none; }
}
