/* ===== Reset & base ===== */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

:root {
  --bg: #ffffff;
  --fg: #000000;
  --fg-dim: #3a3a3a;
  --accent: #000000;
  --line: #dcdcdc;
  --panel-pad: clamp(24px, 6vw, 96px);
  --font-display: "VCR OSD Mono", monospace;
  --font-body: "Cormorant Garamond", serif;
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: 1.15rem;
  line-height: 1.6;
}

/* Dark theme: main.js adds body.theme-dark while the Portfolio section (the
   server room) is on screen, and removes it when the visitor scrolls away. */
body.theme-dark {
  --bg: #04060a;
  --fg: #e6edf5;
  --fg-dim: #9aa7b6;
  --accent: #e6edf5;
  --line: #1a2230;
}
body, .panel, h1, h2, h3, a, #lang-switch button, #dot-nav a {
  transition: background-color 0.6s ease, color 0.6s ease, border-color 0.6s ease;
}

h1, h2, h3 { font-family: var(--font-display); letter-spacing: 0.03em; margin: 0 0 0.4em; color: var(--fg); }
h2 { font-size: clamp(1.8rem, 4.5vw, 2.8rem); }
h3 { font-size: clamp(1.1rem, 2.6vw, 1.5rem); color: var(--fg-dim); }
a { color: var(--fg); text-decoration-color: var(--fg); }

/* ===== Scroll container ===== */
#scroll-container {
  height: 100vh;
  height: 100dvh; /* phones: 100vh includes the hidden address bar area */
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  #scroll-container { scroll-behavior: auto; }
}
/* Portfolio detail content can be taller than one viewport (multiple images +
   description); mandatory snap fights free scrolling through it, so it's
   turned off while a detail view is open (see js/portfolio.js). */
#scroll-container.no-snap {
  scroll-snap-type: none;
}

.panel {
  min-height: 100vh;
  min-height: 100dvh;
  width: 100%;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: var(--panel-pad);
  border-bottom: 1px solid var(--line);
}

.panel-inner {
  max-width: 720px;
  width: 100%;
}

/* ===== Language switch ===== */
#lang-switch {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 50;
  display: flex;
  gap: 8px;
}
#lang-switch button {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--fg-dim);
  font-family: var(--font-display);
  font-size: 0.75rem;
  padding: 6px 10px;
  cursor: pointer;
  letter-spacing: 0.05em;
}
#lang-switch button.active,
#lang-switch button:hover {
  color: var(--bg);
  background: var(--accent);
  border-color: var(--accent);
}

/* ===== Dot nav ===== */
#dot-nav {
  position: fixed;
  right: 24px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
#dot-nav a {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--line);
  display: block;
}
#dot-nav a.active { background: var(--accent); }

@media (max-width: 640px) {
  #dot-nav { display: none; }
}

/* ===== Hero ===== */
/* Extra top clearance so the title never collides with the fixed lang-switch
   buttons in the corner, even on short viewports. */
#hero { gap: 32px; padding-top: calc(var(--panel-pad) + 56px); }
.hero-inner { display: flex; flex-direction: column; align-items: center; gap: 24px; }

/* Plaster bust (small) stacked directly above the wordmark logo. */
.brand-mark {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.brand-plaster {
  width: 64px;
  height: auto;
  object-fit: contain;
}

/* ===== Easter egg: the plaster bust ===== */
.plaster-btn {
  position: relative;
  display: block;
  padding: 0;
  margin: 0;
  background: none;
  border: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.plaster-btn:focus-visible { outline: 2px dashed var(--fg); outline-offset: 4px; }
.plaster-btn .brand-plaster { display: block; transform-origin: 50% 90%; }
.plaster-btn.angry .brand-plaster {
  /* white plaster flushes red; shakes in jerky 2-step motion */
  filter: sepia(1) saturate(9) hue-rotate(-46deg) brightness(0.95);
  animation: bust-shake 0.18s steps(2) infinite;
}
@keyframes bust-shake {
  0%   { transform: translate(-2px, 0) rotate(-5deg) scale(1.1); }
  100% { transform: translate(2px, -1px) rotate(5deg) scale(1.1); }
}

/* Pixel anger vein (the manga 💢): four little corner brackets, 3px "pixels". */
.anger-mark {
  position: absolute;
  top: -4px;
  left: -10px;
  width: 3px;
  height: 3px;
  background: #e32424;
  box-shadow:
    3px 0 #e32424, 0 3px #e32424,
    12px 0 #e32424, 15px 0 #e32424, 15px 3px #e32424,
    0 12px #e32424, 0 15px #e32424, 3px 15px #e32424,
    15px 12px #e32424, 12px 15px #e32424, 15px 15px #e32424;
  opacity: 0;
  pointer-events: none;
}
.plaster-btn.angry .anger-mark { opacity: 1; animation: anger-blink 0.3s steps(1) infinite; }
@keyframes anger-blink { 50% { opacity: 0.25; } }

/* Rage (4+ clicks): deeper red, two more veins, harder shake, steam puffs. */
.plaster-btn .anger-mark.extra,
.plaster-btn.angry .anger-mark.extra { opacity: 0; animation: none; } /* only the first vein until rage */
.extra-a { top: -16px; left: 24px; }
.extra-b { top: 26px; left: -18px; }
.plaster-btn.rage .anger-mark.extra { opacity: 1; animation: anger-blink 0.22s steps(1) infinite; }
.plaster-btn.rage .brand-plaster {
  filter: sepia(1) saturate(16) hue-rotate(-50deg) brightness(0.78) contrast(1.35) drop-shadow(0 0 5px #ff2a2a);
  animation: bust-rage 0.1s steps(2) infinite;
}
@keyframes bust-rage {
  0%   { transform: translateX(-6px) rotate(-9deg) scale(1.2); }
  100% { transform: translateX(6px)  rotate(9deg)  scale(1.2); }
}
.plaster-btn.rage .pixel-bubble { color: #c40000; }
.plaster-btn.rage .pixel-bubble { max-width: min(150px, calc(50vw - 56px)); }
/* square "pixel" steam puffs fired off the head (spawned by main.js) */
.puff {
  position: absolute;
  top: 6px;
  width: var(--s);
  height: var(--s);
  background: #fff;
  box-shadow: 0 0 0 2px #000;
  pointer-events: none;
  z-index: 4;
  animation: puff-fly 0.8s steps(8) forwards;
}
@keyframes puff-fly {
  0%   { transform: translate(0, 0) scale(0.6); opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)) scale(1.7); opacity: 0; }
}

/* Pixel speech bubble: a plain rectangle whose four edge-shadows leave notched
   corners (the classic 8-bit dialogue box), plus a stair-stepped tail. */
.pixel-bubble {
  position: absolute;
  left: calc(100% + 16px);
  top: -2px;
  width: max-content;
  max-width: min(132px, calc(50vw - 56px));
  padding: 8px 10px;
  background: #fff;
  color: #000;
  font-family: var(--font-display);
  font-size: 0.8rem;
  line-height: 1.3;
  text-align: left;
  letter-spacing: 0;
  box-shadow: 0 -4px 0 0 #000, 0 4px 0 0 #000, -4px 0 0 0 #000, 4px 0 0 0 #000;
  visibility: hidden;
  pointer-events: none;
  z-index: 5;
}
.pixel-bubble::before,
.pixel-bubble::after { content: ""; position: absolute; width: 4px; height: 4px; }
.pixel-bubble::before { /* tail outline (black), a left-pointing stair wedge */
  left: -16px;
  top: 16px;
  background: #000;
  box-shadow: 4px -4px #000, 4px 4px #000, 8px -8px #000, 8px 8px #000;
}
.pixel-bubble::after { /* tail fill (white) — also opens a gap in the bubble's border */
  left: -12px;
  top: 16px;
  width: 12px;
  background: #fff;
  box-shadow: 4px -4px #fff, 4px 4px #fff, 8px -8px #fff, 8px 8px #fff;
}
.plaster-btn.angry .pixel-bubble {
  visibility: visible;
  animation: bubble-pop 0.25s steps(3) both;
}
@keyframes bubble-pop {
  0%   { transform: scale(0.4); transform-origin: 0 50%; }
  100% { transform: scale(1);   transform-origin: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .plaster-btn.angry .brand-plaster,
  .plaster-btn.angry .anger-mark,
  .plaster-btn.rage .brand-plaster,
  .plaster-btn.rage .anger-mark.extra,
  .plaster-btn.angry .pixel-bubble { animation: none; }
  .puff { display: none; }
}

.brand-logo {
  width: min(280px, 60vw);
  height: auto;
  object-fit: contain;
}

/* Real photo of the reference CRT monitor (assets/img/crt-monitor.png,
   cropped from the user's reference shot). The video screen is an absolutely
   positioned overlay matched to that photo's actual screen rectangle —
   measured directly from the image's pixels (dark screen vs. gray bezel),
   as a percentage of the (fixed-aspect) photo: left 11.1%, top 13.0%,
   width 77.5%, height 64.2%. If the photo is ever swapped for a different
   crop, these percentages need re-measuring against the new image. */
.tv {
  position: relative;
  width: min(480px, 80vw);
  aspect-ratio: 1238 / 1092; /* crt-monitor.png's own pixel dimensions */
}
.tv-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  user-select: none;
}
.tv-screen {
  position: absolute;
  left: 11.1%;
  top: 13.0%;
  width: 77.5%;
  height: 64.2%;
  overflow: hidden;
  border-radius: 3%;
  background: #000;
  filter: saturate(1.1) contrast(1.05);
  cursor: pointer;
  box-shadow: inset 0 0 0 2px rgba(0,0,0,0.5);
}
.tv-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Colors left untouched on purpose — the scanline/vignette/noise/glass
     layers above already carry the CRT feel without tinting the footage. */
}
.tv-scanlines {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0,0,0,0.25) 0px,
    rgba(0,0,0,0.25) 1px,
    transparent 2px,
    transparent 4px
  );
  mix-blend-mode: multiply;
  pointer-events: none;
}
.tv-flicker {
  position: absolute;
  inset: 0;
  background: #fff;
  opacity: 0;
  animation: flicker 6s infinite;
  pointer-events: none;
}
.tv-noise {
  position: absolute;
  inset: 0;
  opacity: 0.05;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  animation: noise 0.4s steps(2) infinite;
}
.tv-vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 60px 18px rgba(0,0,0,0.55);
  border-radius: 3%;
}
.tv-glass {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(115deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0.04) 18%, transparent 34%);
}
/* Small VCR-style on-screen display, top-left of the screen — always visible,
   reflecting the video's actual current state (like a real VCR's transport
   readout) rather than only appearing on hover. */
.tv-toggle {
  position: absolute;
  top: 10px;
  left: 12px;
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-display);
  font-size: 0.9rem;
  letter-spacing: 0.1em;
  color: #f2f0e6;
  text-shadow: 0 1px 2px rgba(0,0,0,0.8);
  pointer-events: none;
}
.tv-toggle-icon {
  display: inline-block;
  width: 0;
  height: 0;
}
/* stop = square */
.tv-toggle-icon.is-square {
  width: 9px;
  height: 9px;
  background: currentColor;
}
/* start/play = right-pointing triangle */
.tv-toggle-icon.is-triangle {
  width: 0;
  height: 0;
  background: none;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  border-left: 9px solid currentColor;
}
@keyframes flicker {
  0%, 96%, 100% { opacity: 0; }
  97% { opacity: 0.08; }
  98% { opacity: 0; }
  99% { opacity: 0.04; }
}
@keyframes noise {
  0% { transform: translate(0,0); }
  50% { transform: translate(-2%, 1%); }
  100% { transform: translate(1%, -1%); }
}

.scroll-hint {
  color: var(--fg-dim);
  font-size: 0.85rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  animation: bob 2s ease-in-out infinite;
}
@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}

/* ===== About ===== */
/* Centered, narrow-measure column so the intro reads as one tidy block
   instead of ragged full-width lines. */
#about .panel-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-width: 540px;
}
/* User-supplied "Musician's License" ID card image (photo/name/role/stamp
   already part of the graphic) — just framed with a drop shadow. */
.musician-license {
  display: block;
  width: min(420px, 100%);
  height: auto;
  margin: 12px 0 28px;
}
.about-body {
  color: var(--fg-dim);
  font-size: 1.25rem;
  line-height: 1.7;
  margin: 0 0 36px;
}
.about-body span {
  display: block;
}
.education-list {
  list-style: none;
  padding: 0;
  margin: 0;
  color: var(--fg-dim);
  width: 100%;
}
.education-list li {
  padding-top: 14px;
  margin-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 0.95rem;
  letter-spacing: 0.02em;
}

/* ===== Music ===== */
/* The user's MP3-player photo (assets/img/mp3-player.png, 1099×1431). Every
   overlay below is positioned as a % of that image's own pixels:
   LCD  x313–787, y312–647          → left 28.5%, top 21.8%, w 43.1%, h 23.5%
   ◀◀ centre (373,965), ▶▶ (726,965), ▶❚❚ (555,1130)
   Re-measure if the photo is ever swapped. */
#music .panel-inner, .music-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.mp3 {
  position: relative;
  width: min(430px, calc(60vh * 1099 / 1431), 100%);
  aspect-ratio: 1099 / 1431;
  margin: 8px 0 14px;
  container-type: inline-size; /* so the LCD text can scale with the player */
  filter: drop-shadow(0 18px 24px rgba(0, 0, 0, 0.28));
}
.mp3-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
}
/* LCD: only album art + track title, drawn in the screen's dark-blue ink. */
.mp3-lcd {
  position: absolute;
  left: 28.5%;
  top: 21.8%;
  width: 43.1%;
  height: 23.5%;
  display: flex;
  align-items: center;
  gap: 6%;
  padding: 4% 5%;
  color: #14202b;
  text-align: left;
  pointer-events: none;
  /* opaque — hides the "MP3" icon / battery / progress bar printed on the photo's screen */
  background: linear-gradient(to bottom, #97a9b3, #8e9fac);
  box-shadow: inset 0 0 10px rgba(10, 25, 40, 0.28);
}
.mp3-lcd::after { /* faint LCD pixel rows */
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(to bottom, rgba(10, 25, 45, 0.07) 0, rgba(10, 25, 45, 0.07) 1px, transparent 1px, transparent 3px);
}
.mp3-art {
  height: 84%;
  aspect-ratio: 1;
  object-fit: cover;
  flex: none;
  filter: grayscale(1) brightness(1.7) contrast(1.15);
  mix-blend-mode: multiply;
  opacity: 0.9;
  image-rendering: pixelated;
}
.mp3-title {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-size: 4.2cqw;
  line-height: 1.25;
  letter-spacing: 0.02em;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
.mp3-btn {
  position: absolute;
  border: none;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}
.mp3-btn:hover { background: radial-gradient(circle, rgba(255, 255, 255, 0.4), transparent 70%); }
.mp3-btn:active { background: radial-gradient(circle, rgba(0, 0, 0, 0.22), transparent 70%); transform: scale(0.95); }
.mp3-btn:focus-visible { outline: 2px solid #17222f; outline-offset: -4px; }
.mp3-prev { left: 28.5%; top: 63.2%; width: 10.9%; height: 8.4%; }
.mp3-next { left: 60.6%; top: 63.2%; width: 10.9%; height: 8.4%; }
.mp3-play { left: 43%;   top: 75.1%; width: 15%;   height: 7.7%; }
/* The Spotify embed that actually produces the sound: kept in the DOM (it
   must be, to play) but invisible and unclickable. */
.spotify-hidden {
  position: fixed;
  right: 0;
  bottom: 0;
  width: 300px;
  height: 80px;
  opacity: 0;
  pointer-events: none;
  overflow: hidden;
}
.more-music-link {
  display: inline-block;
  font-size: 0.95rem;
  text-decoration: none;
  border-bottom: 1px solid var(--accent);
  padding-bottom: 2px;
}
.more-music-link:hover { opacity: 0.75; }

.portfolio-more-link {
  display: inline-block;
  margin-top: 28px;
  font-size: 0.95rem;
  text-decoration: none;
  color: var(--fg);
  border-bottom: 1px solid var(--fg);
  padding-bottom: 2px;
}
.portfolio-more-link:hover { opacity: 0.75; }

/* ===== Portfolio — the server room ===== */
#portfolio.panel {
  padding-top: 64px;
  padding-bottom: 20px;
  gap: 6px;
}
#portfolio h2 { margin: 0; }
/* Stage = the server-room photo (1672×941) with the monitors laid on top of
   its screens as %-positioned overlays (generated by js/portfolio.js). */
.stage-scroll {
  width: 100%;
  display: flex;
  justify-content: center;
  overflow-x: auto;
  scrollbar-width: none;
}
.stage-scroll::-webkit-scrollbar { display: none; }
.server-stage {
  position: relative;
  flex: none;
  width: min(100%, calc((100vh - 215px) * 1672 / 941));
  aspect-ratio: 1672 / 941;
}
.server-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
}
/* The lit photo sits on top of the dark one and is invisible until the lights
   come on — then it flickers up (like a fluorescent tube catching). */
.server-lit { opacity: 0; }
.lights-on .server-lit { animation: lights-on 1.5s linear forwards; }
@keyframes lights-on {
  0%   { opacity: 0; }
  10%  { opacity: 0.55; }
  16%  { opacity: 0.05; }
  30%  { opacity: 0.85; }
  38%  { opacity: 0.2; }
  52%  { opacity: 1; }
  60%  { opacity: 0.7; }
  72%  { opacity: 1; }
  100% { opacity: 1; }
}
.stage-caption {
  margin: 0;
  min-height: 1.4em;
  font-family: var(--font-display);
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  color: var(--fg-dim);
  text-align: center;
}
.more-contents {
  background: none;
  border: none;
  border-bottom: 1px solid var(--fg);
  color: var(--fg);
  font-family: var(--font-display);
  font-size: 1rem;
  letter-spacing: 0.14em;
  padding: 4px 2px;
  cursor: pointer;
}
.more-contents:hover { opacity: 0.75; }

/* --- CRT monitor overlay --- */
.monitor {
  position: absolute;
  display: block;
  overflow: hidden;
  isolation: isolate;
  cursor: pointer;
  z-index: 3;
  /* dead until the lights come on, then each powers up in turn */
  opacity: 0;
  pointer-events: none;
}
.lights-on .monitor {
  pointer-events: auto;
  animation: crt-on 0.8s ease-out both;
  animation-delay: calc(1.1s + var(--n, 0) * 0.22s);
}
@keyframes crt-on {
  0%   { opacity: 0; transform: scale(1, 0.02); filter: brightness(4); }
  35%  { opacity: 1; transform: scale(1, 0.02); filter: brightness(4); }
  60%  { opacity: 1; transform: scale(1, 1.04); filter: brightness(2.2); }
  100% { opacity: 1; transform: none; filter: none; }
}
.monitor > span {
  position: absolute;
  inset: 0;
  display: block;
  pointer-events: none;
}
.m-img {
  background: var(--img) center / cover no-repeat;
  filter: saturate(0.95) contrast(1.05) brightness(1.2);
  animation: m-jitter var(--p) steps(1) infinite var(--d);
}
.m-ghost {
  background: var(--img) center / cover no-repeat;
  mix-blend-mode: screen;
  opacity: 0;
}
.m-ghost-r { filter: url(#ch-r); animation: m-ghost-a var(--p) steps(1) infinite var(--d); }
.m-ghost-c { filter: url(#ch-c); animation: m-ghost-b var(--p) steps(1) infinite var(--d); }
.m-tint { background: rgba(110, 165, 255, 0.14); mix-blend-mode: overlay; }
.m-scan {
  background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.2) 1px, transparent 1px, transparent 3px);
  animation: m-flicker 0.15s steps(2) infinite;
}
.m-bar { /* slow rolling brightness band, like a refresh line */
  background: linear-gradient(to bottom, transparent, rgba(190, 220, 255, 0.16), transparent);
  height: 35%;
  bottom: auto;
  animation: m-roll 5s linear infinite var(--d);
}
.m-vig { box-shadow: inset 0 0 20px 4px rgba(0, 0, 12, 0.5); }
.m-ch {
  inset: auto;
  top: 10%;
  left: 13%;
  font-family: var(--font-display);
  font-size: clamp(6px, 0.85vw, 12px);
  letter-spacing: 0.1em;
  color: #cfe6ff;
  text-shadow: 0 0 4px rgba(120, 180, 255, 0.9);
  opacity: 0.85;
}
.monitor:hover .m-img,
.monitor:focus-visible .m-img { filter: saturate(1.05) contrast(1.05) brightness(1.4); }
.monitor:hover .m-ghost-r,
.monitor:focus-visible .m-ghost-r { animation: m-ghost-a 0.5s steps(1) infinite; }
.monitor:hover .m-ghost-c,
.monitor:focus-visible .m-ghost-c { animation: m-ghost-b 0.5s steps(1) infinite; }
.monitor:hover .m-img,
.monitor:focus-visible .m-img { animation: m-jitter 0.5s steps(1) infinite; }
.monitor:focus-visible { outline: none; }

/* --- Empty monitors: click for a test signal + "NO SIGNAL" --- */
.monitor-empty {
  background: transparent;
  border: 0;
  padding: 0;
  container-type: size; /* lets the message scale with each screen's size */
}
.monitor-empty:hover { background: rgba(255, 255, 255, 0.22); }
.m-signal {
  opacity: 0;
  background: #000;
  filter: saturate(0.85) brightness(0.92);
}
.m-signal > span { position: absolute; left: 0; right: 0; display: block; }
.m-bars {
  top: 0;
  height: 68%;
  background: linear-gradient(to right, #c0c0c0 0 14.28%, #c0c000 0 28.57%, #00c0c0 0 42.85%, #00c000 0 57.14%, #c000c0 0 71.42%, #c00000 0 85.71%, #0000c0 0);
}
.m-bars-low {
  top: 68%;
  height: 32%;
  background: linear-gradient(to right, #0000c0 0 14.28%, #111 0 28.57%, #c000c0 0 42.85%, #111 0 57.14%, #00c0c0 0 71.42%, #111 0 85.71%, #c0c0c0 0);
}
.m-nosig {
  top: 50%;
  left: 50% !important;
  right: auto !important;
  transform: translate(-50%, -50%);
  background: #000;
  color: #fff;
  font-family: var(--font-display);
  font-size: clamp(4px, 10cqw, 40px);
  letter-spacing: 0.12em;
  line-height: 1;
  padding: 0.4em 0.7em;
  white-space: nowrap;
  animation: m-blink 1.6s steps(1) infinite;
}
.monitor-empty .m-scan,
.monitor-empty .m-vig { opacity: 0; }
.monitor-empty.show-signal .m-signal { opacity: 1; animation: m-tune 0.5s steps(1); }
.monitor-empty.show-signal .m-scan,
.monitor-empty.show-signal .m-vig { opacity: 1; }
.monitor-empty.show-signal:hover { background: transparent; }
@keyframes m-blink { 0%, 70% { opacity: 1; } 71%, 100% { opacity: 0; } }
/* brief white-out + shift as the signal "locks on" */
@keyframes m-tune {
  0% { filter: brightness(4) contrast(0.3); transform: translateX(-4%); }
  35% { filter: brightness(2) contrast(0.6); transform: translateX(3%) skewX(6deg); }
  70% { filter: brightness(1.3); transform: none; }
}

/* Mostly steady; a short burst of displacement / RGB-split / hue shift near
   the end of each (per-monitor) cycle. */
@keyframes m-jitter {
  0%, 88%, 100% { transform: none; }
  89% { transform: translateX(-3%) skewX(4deg); filter: saturate(1.4) contrast(1.4) brightness(1.1) hue-rotate(35deg); }
  90% { transform: translateX(4%); }
  91% { transform: translate(-2%, 2%) scaleY(1.06); filter: saturate(0.4) contrast(1.6) brightness(1.3); }
  92% { transform: translateX(2%) skewX(-5deg); }
  93% { transform: none; }
  95% { transform: translateY(-3%); }
  96% { transform: none; }
}
@keyframes m-ghost-a {
  0%, 88%, 96%, 100% { opacity: 0; transform: none; }
  89% { opacity: 0.85; transform: translateX(-5%); }
  91% { opacity: 0.85; transform: translateX(3%); }
  93% { opacity: 0.7; transform: translateX(-2%); }
}
@keyframes m-ghost-b {
  0%, 88%, 96%, 100% { opacity: 0; transform: none; }
  89% { opacity: 0.85; transform: translateX(5%); }
  91% { opacity: 0.85; transform: translateX(-3%); }
  93% { opacity: 0.7; transform: translateX(2%); }
}
@keyframes m-flicker {
  0% { opacity: 1; }
  100% { opacity: 0.88; }
}
@keyframes m-roll {
  0% { transform: translateY(-120%); }
  100% { transform: translateY(320%); }
}
@media (prefers-reduced-motion: reduce) {
  .m-img, .m-ghost, .m-scan, .m-bar, .m-nosig, .m-signal { animation: none !important; }
}

/* --- Windows (More contents list + work detail) --- */
.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(20, 22, 26, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.modal[hidden] { display: none; }
body.modal-open #scroll-container { overflow: hidden; }
#lang-switch { z-index: 150; }
.modal-window {
  --bg: #ffffff;
  --fg: #000000;
  --fg-dim: #3a3a3a;
  --accent: #000000;
  --line: #dcdcdc;
  position: relative;
  width: min(960px, 100%);
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  padding: 28px;
  color: var(--fg);
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.3);
}
.modal-close {
  position: sticky;
  top: 0;
  float: right;
  z-index: 2;
  margin: -12px -12px 0 12px;
  background: #fff;
  border: 1px solid var(--fg);
  color: var(--fg);
  font-family: var(--font-display);
  font-size: 0.9rem;
  width: 34px;
  height: 34px;
  cursor: pointer;
}
.modal-close:hover { background: var(--fg); color: #fff; }
.modal-title { margin: 0 0 4px; color: var(--fg); }

.portfolio-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
  margin-top: 24px;
}
/* [hidden] is normally display:none via the UA stylesheet, but the class rule
   above has equal specificity and comes later, so it silently wins. This
   attribute+class selector outranks it so JS toggling .hidden actually works. */
.portfolio-grid[hidden] { display: none; }
.portfolio-grid--small { margin-top: 12px; }

/* Polaroid-style cards: thick white bottom margin, thin frame, slight scatter
   of rotation per card (nth-child) so the grid reads as tossed-down photos
   rather than a rigid grid — reset to straight + lifted on hover. */
.portfolio-card {
  cursor: pointer;
  background: #fff;
  border: 1px solid #e8e8e8;
  box-shadow: 0 6px 16px rgba(0,0,0,0.12);
  padding: 10px 10px 0;
  overflow: visible;
  text-align: left;
  display: flex;
  flex-direction: column;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.portfolio-card:nth-child(4n+1) { transform: rotate(-2.5deg); }
.portfolio-card:nth-child(4n+2) { transform: rotate(1.5deg); }
.portfolio-card:nth-child(4n+3) { transform: rotate(-1deg); }
.portfolio-card:nth-child(4n+4) { transform: rotate(2.5deg); }
.portfolio-card:hover {
  transform: rotate(0deg) translateY(-4px);
  box-shadow: 0 14px 28px rgba(0,0,0,0.18);
}
.portfolio-card img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
  background: #eee;
  filter: sepia(0.12) contrast(1.02);
}
.portfolio-card figcaption {
  padding: 12px 4px 18px;
  font-size: 0.85rem;
  font-family: var(--font-body);
  font-style: italic;
}
/* The polaroid is always white, so its text is fixed dark (not theme-driven). */
.portfolio-card .p-title { color: #111; font-size: 1rem; }
.portfolio-card .p-meta { color: #555; font-size: 0.8rem; }

.portfolio-detail {
  max-width: 960px;
  width: 100%;
  margin-top: 12px;
}
.modal.portfolio-detail { max-width: none; margin-top: 0; width: auto; }
.portfolio-back {
  background: none;
  border: none;
  color: var(--fg-dim);
  font-family: var(--font-body);
  cursor: pointer;
  margin-bottom: 16px;
  padding: 0;
  font-size: 0.9rem;
}
.portfolio-back:hover { color: var(--accent); }
.modal-close.portfolio-back { padding: 0; margin: -12px -12px 0 12px; background: #fff; border: 1px solid var(--fg); color: var(--fg); font-family: var(--font-display); font-size: 0.9rem; }
.modal-close.portfolio-back:hover { background: var(--fg); color: #fff; }
.portfolio-detail-content img {
  width: 100%;
  max-height: 62vh;
  object-fit: contain;
  display: block;
  margin-bottom: 12px;
  background: #eee;
}
.portfolio-detail-content .p-desc { color: var(--fg-dim); margin: 16px 0 32px; white-space: pre-line; }
.portfolio-detail-content .p-link {
  display: inline-block;
  margin: -16px 0 32px;
  font-size: 0.95rem;
  text-decoration: none;
  color: var(--fg);
  border-bottom: 1px solid var(--fg);
  padding-bottom: 2px;
}
.portfolio-detail-content .p-link:hover { opacity: 0.75; }

/* ===== Commission form — filled out directly on the user's own form photo =====
   Coordinates below are measured from assets/img/commission-form.png's own
   pixels (1103×1426): each ruled line's position as a % of the image, so the
   overlay tracks correctly as the image scales responsively. Re-measure if
   that photo is ever replaced with a different layout/crop. */
.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;
}
.commission-inner { max-width: 640px; }
.commission-sheet {
  position: relative;
  width: 100%;
  box-shadow: 0 18px 40px rgba(0,0,0,0.16);
}
.commission-photo {
  display: block;
  width: 100%;
  height: auto;
}
.commission-form-overlay { position: absolute; inset: 0; }
.cf-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.cf-field {
  position: absolute;
  background: transparent;
  border: none;
  border-radius: 2px;
  color: #111;
  font-family: var(--font-body);
  font-size: clamp(0.75rem, 2.6vw, 1.05rem);
  line-height: 1.3;
  padding: 0 0.3em;
}
.cf-field:focus {
  outline: none;
  background: rgba(255, 230, 150, 0.35);
}

/* NAME (required): */
.cf-name   { left: 30.1%; width: 57.6%; top: calc(31.00% - 1.3em); height: 1.5em; }
/* EMAIL (required): */
.cf-email  { left: 30.7%; width: 57.0%; top: calc(37.52% - 1.3em); height: 1.5em; }
/* COMPANY (optional): */
.cf-company{ left: 34.7%; width: 53.0%; top: calc(44.04% - 1.3em); height: 1.5em; }
/* COMMISSION DETAILS (briefly) — spans all five ruled lines as one field */
.cf-message {
  left: 12.5%;
  width: 75.3%;
  top: 61.5%;
  height: 19%;
  resize: none;
  line-height: 1.7;
}

.commission-footer {
  margin-top: 28px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}
.commission-footer button {
  background: transparent;
  color: var(--fg);
  border: 2px solid var(--fg);
  padding: 10px 32px;
  font-family: var(--font-display);
  font-size: 1.05rem;
  letter-spacing: 0.08em;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.commission-footer button:hover:not(:disabled) {
  background: var(--fg);
  color: var(--bg);
}
.commission-footer button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.commission-status {
  min-height: 1.2em;
  font-size: 0.9rem;
}
.commission-status.error { color: #b23b3b; }
.commission-status.success { color: #2f6b3a; }

/* ===== Phones ===== */
@media (max-width: 700px) {
  /* the language buttons would sit on top of a window's ✕ */
  body.modal-open #lang-switch { display: none; }

  /* The studio is too wide to read at phone width, so it's shown larger and
     can be panned sideways (main.js starts it centred on the middle monitor). */
  .stage-scroll { justify-content: flex-start; -webkit-overflow-scrolling: touch; }
  .server-stage { width: 720px; }

  /* iOS Safari zooms the page when a field under 16px is focused. Keep the
     fields at 16px and shrink them back to the printed-form size with a
     transform (sizes/offsets below are the desktop ones ÷ 0.75). */
  .cf-field {
    font-size: 16px;
    transform: scale(0.75);
    transform-origin: left top;
  }
  .cf-name    { width: 76.8%;  height: 1.5em; top: calc(31.00% - 0.975em); }
  .cf-email   { width: 76%;    height: 1.5em; top: calc(37.52% - 0.975em); }
  .cf-company { width: 70.67%; height: 1.5em; top: calc(44.04% - 0.975em); }
  .cf-message { width: 100.4%; height: 25.33%; top: calc(61.5% - 5px); }
}
