@font-face {
  font-family: "Instrument Sans";
  src: url("./fonts/instrument-sans-v2-subset.woff2") format("woff2");
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
}

@font-face {
  font-family: "Pretendard";
  src: url("./fonts/pretendard-v4-400-subset.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Pretendard";
  src: url("./fonts/pretendard-v4-700-subset.woff2") format("woff2");
  font-style: normal;
  font-weight: 700;
  font-display: swap;
}

:root {
  color-scheme: dark;
  --void: #070a12;
  --surface: #0d1320;
  --panel: #111a2a;
  --panel-raised: #162036;
  --text: #f5f1ea;
  --muted: #a9b1c5;
  --line: rgba(162, 174, 214, 0.16);
  --violet: #8c68ff;
  --cobalt: #4c8dff;
  --amber: #e7b769;
  --agent-violet: #9b7bff;
  --agent-green: #5ed4a1;
  --agent-amber: #f3bd67;
  --agent-blue: #66b7ff;
  --font-ko: "Pretendard", system-ui, -apple-system, BlinkMacSystemFont,
    "Segoe UI", "Apple SD Gothic Neo", sans-serif;
  --font-latin: "Instrument Sans", system-ui, -apple-system, BlinkMacSystemFont,
    "Segoe UI", "Apple SD Gothic Neo", sans-serif;
  --page-inline: clamp(1.5rem, 4vw, 4rem);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  background: var(--void);
  /* Keep the track from first paint so the layout never jumps sideways when a scrollbar
     appears. Setting the root's own overflow also stops body's overflow from propagating
     up to the viewport, which is what lets the curtain clip the page below. */
  overflow-y: scroll;
  /* Kill the rubber band at the document edges. In an in-app WebView that bounce drags
     the full-bleed hero with it, which reads as the image growing under the finger. */
  overscroll-behavior: none;
  scrollbar-gutter: stable;
}

body {
  min-width: 0;
  margin: 0;
  overscroll-behavior: none;
  background: var(--void);
  color: var(--text);
  font-family: var(--font-ko);
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

main,
section,
div,
figure {
  min-width: 0;
}

img {
  display: block;
  max-width: 100%;
}

p,
h1,
h2,
figure {
  margin: 0;
}

.hero {
  position: relative;
  min-height: max(42rem, 100svh);
  overflow: hidden;
  isolation: isolate;
  background: var(--void);
}

.hero-shell {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-rows: auto 1fr;
  width: min(90rem, calc(100% - 2 * var(--page-inline)));
  min-height: inherit;
  margin-inline: auto;
  padding-block-start: clamp(1.25rem, 3vw, 2.5rem);
}

.wordmark {
  width: max-content;
  color: var(--text);
  font-family: var(--font-latin);
  font-size: clamp(1.25rem, 1.7vw, 1.625rem);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1;
}

.wordmark-accent {
  color: var(--agent-blue);
}

.hero-copy {
  align-self: center;
  width: 42%;
  max-width: 38rem;
  padding-block: clamp(3.5rem, 8vh, 6.5rem);
}

.hero-title,
.dual,
.supporting,
.passage-copy h2,
.passage-copy p,
.closing-dual,
.closing-philosophy {
  overflow-wrap: normal;
  word-break: keep-all;
}

.hero-title,
.passage-copy h2 {
  text-wrap: balance;
}

.hero-title {
  max-width: 10.5em;
  color: var(--text);
  font-size: clamp(2.75rem, 5.2vw, 5.4rem);
  font-weight: 700;
  letter-spacing: -0.045em;
  line-height: 0.98;
}

/* Pulled up against the headline and pushed away from the quote: the title and the
   wordline are one statement, the line of dialogue underneath is a different voice. */
.dual {
  margin-block-start: clamp(1.25rem, 2.6vw, 2.6rem);
  color: var(--text);
  font-size: clamp(1.125rem, 1.65vw, 1.45rem);
  font-weight: 700;
  line-height: 1.3;
}

.supporting {
  display: grid;
  max-width: 30rem;
  margin-block-start: clamp(1.5rem, 2.2vw, 2rem);
  color: var(--muted);
  font-size: clamp(1rem, 1.2vw, 1.125rem);
  font-weight: 400;
  line-height: 1.72;
}

/* One line per hero beat, stacked in a single cell and crossfaded on the same 24s
   clock as the images, so the dialogue never drifts away from the scene it belongs to. */
.scene-line {
  --ambient-delay: 0s;
  grid-area: 1 / 1;
  opacity: 0;
}

.scene-line:first-child {
  opacity: 1;
}

.scene-line:nth-child(2) {
  --ambient-delay: -20s;
}

.scene-line:nth-child(3) {
  --ambient-delay: -16s;
}

.scene-line:nth-child(4) {
  --ambient-delay: -12s;
}

.scene-line:nth-child(5) {
  --ambient-delay: -8s;
}

.scene-line:nth-child(6) {
  --ambient-delay: -4s;
}

.ambient-enhanced .scene-line {
  animation: ambient-opacity 24s linear var(--ambient-delay) infinite both;
}

.title-accent {
  color: var(--agent-blue);
}

.nb {
  white-space: nowrap;
}

.hero-stage {
  position: absolute;
  z-index: 0;
  inset: 0;
}

.hero-image {
  --ambient-delay: 0s;
  --drift-from-x: -0.35%;
  --drift-from-y: -0.2%;
  --drift-mid-x: 0%;
  --drift-mid-y: 0%;
  --drift-to-x: 0.35%;
  --drift-to-y: 0.2%;
  position: absolute;
  inset: -1%;
  width: 102%;
  height: 102%;
  max-width: none;
  /* The frames are lit brighter than the page reads at, so they are pulled down here
     rather than baked in — the source stays untouched and the level stays tunable. */
  filter: brightness(0.74);
  object-fit: cover;
  object-position: 68% center;
  opacity: 0;
  transform: none;
}

.hero-image:not([data-ambient-slide="0"]) {
  object-position: 72% center;
}

.hero-image.is-active {
  opacity: 1;
}

/* Alternate the push and the pull. Every frame zooming the same way makes the loop feel
   like it keeps rewinding; this way each frame hands its scale to the next one at the
   seam and the motion reads as one continuous breath instead of six restarts. */
.hero-image:nth-child(odd) {
  --scale-start: 0.995;
  --scale-hold: 1.012;
  --scale-end: 1.075;
}

.hero-image:nth-child(even) {
  --scale-start: 1.075;
  --scale-hold: 1.058;
  --scale-end: 0.995;
}

/* Six beats, 4s each: a 24s cycle with a 1s crossfade at every seam. */
.hero-image:nth-child(2) {
  --ambient-delay: -20s;
  --drift-from-x: 0.4%;
  --drift-from-y: -0.18%;
  --drift-to-x: -0.4%;
  --drift-to-y: 0.18%;
}

.hero-image:nth-child(3) {
  --ambient-delay: -16s;
  --drift-from-x: -0.45%;
  --drift-from-y: 0.22%;
  --drift-to-x: 0.15%;
  --drift-to-y: -0.22%;
}

.hero-image:nth-child(4) {
  --ambient-delay: -12s;
  --drift-from-x: 0.45%;
  --drift-from-y: 0.24%;
  --drift-to-x: -0.15%;
  --drift-to-y: -0.24%;
}

.hero-image:nth-child(5) {
  --ambient-delay: -8s;
  --drift-from-x: -0.4%;
  --drift-from-y: -0.24%;
  --drift-to-x: 0.4%;
  --drift-to-y: 0.2%;
}

.hero-image:nth-child(6) {
  --ambient-delay: -4s;
  --drift-from-x: 0.35%;
  --drift-from-y: 0.2%;
  --drift-to-x: -0.35%;
  --drift-to-y: -0.2%;
}

.ambient-enhanced .hero-image {
  animation:
    ambient-opacity 24s linear var(--ambient-delay) infinite both,
    ambient-transform 24s linear var(--ambient-delay) infinite both;
}

/* 2.4s held, then a 1.6s dissolve at every seam. Linear on purpose: an eased opacity
   crowds most of the change into the middle of the fade and reads as a cut. */
@keyframes ambient-opacity {
  0%,
  10% {
    opacity: 1;
  }

  16.6667%,
  93.3333% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

/* The push runs across the whole span a frame is on screen — it is already moving as it
   fades in and still moving as it fades out, so nothing settles mid-scene. The return
   happens between 16.7% and 93.3%, while the frame is hidden. */
@keyframes ambient-transform {
  0% {
    transform: translate(var(--drift-mid-x), var(--drift-mid-y)) scale(var(--scale-hold));
  }

  16.6667% {
    transform: translate(var(--drift-to-x), var(--drift-to-y)) scale(var(--scale-end));
  }

  93.3333% {
    transform: translate(var(--drift-from-x), var(--drift-from-y)) scale(var(--scale-start));
  }

  100% {
    transform: translate(var(--drift-mid-x), var(--drift-mid-y)) scale(var(--scale-hold));
  }
}

.hero-shade {
  position: absolute;
  z-index: 1;
  inset: 0;
  pointer-events: none;
  /* Many close stops rather than a few far apart: a long flat run followed by a steep
     drop reads as a seam, and the brighter frames make that knee obvious. */
  background:
    linear-gradient(
      90deg,
      var(--void) 0%,
      var(--void) 45%,
      rgba(7, 10, 18, 0.97) 49%,
      rgba(7, 10, 18, 0.9) 53%,
      rgba(7, 10, 18, 0.78) 58%,
      rgba(7, 10, 18, 0.62) 63%,
      rgba(7, 10, 18, 0.44) 69%,
      rgba(7, 10, 18, 0.28) 75%,
      rgba(7, 10, 18, 0.16) 82%,
      rgba(7, 10, 18, 0.07) 90%,
      rgba(7, 10, 18, 0.02) 100%
    ),
    linear-gradient(
      0deg,
      rgba(7, 10, 18, 0.82) 0%,
      rgba(7, 10, 18, 0.52) 16%,
      rgba(7, 10, 18, 0.26) 30%,
      rgba(7, 10, 18, 0.1) 40%,
      rgba(7, 10, 18, 0) 48%
    );
}

.section-inner {
  width: min(90rem, calc(100% - 2 * var(--page-inline)));
  margin-inline: auto;
}

.passage-copy h2 {
  max-width: 9.5em;
  color: var(--text);
  font-size: clamp(2.25rem, 4.4vw, 4.75rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1.06;
}

.passage-copy > p:not(.integrity) {
  max-width: 30rem;
  margin-block-start: clamp(1.5rem, 2.6vw, 2.5rem);
  color: var(--muted);
  font-size: clamp(1rem, 1.2vw, 1.125rem);
  font-weight: 400;
  line-height: 1.72;
}

.work {
  display: grid;
  min-height: 44rem;
  align-items: center;
  padding-block: clamp(5rem, 8vw, 8rem);
  background: var(--surface);
}

.work-grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(3rem, 7vw, 8rem);
  align-items: center;
}

/* A terminal that keeps working while the boss rests. Lines cut in one at a time and
   the log rolls over, so the panel is always mid-shift whenever you scroll to it. */
.work-artifacts {
  position: relative;
  display: grid;
  min-height: clamp(22rem, 32vw, 28rem);
  align-content: center;
}

.terminal {
  overflow: hidden;
  width: min(34rem, 100%);
  border-radius: 0.875rem;
  margin-inline: auto;
  background: #080c16;
  box-shadow:
    0 1.75rem 3.5rem rgba(0, 0, 0, 0.6),
    0 0.375rem 1rem rgba(0, 0, 0, 0.45);
}

.terminal-bar {
  display: flex;
  padding: 0.7rem 0.9rem;
  border-block-end: 1px solid rgba(162, 174, 214, 0.12);
  background: rgba(162, 174, 214, 0.05);
  gap: 0.4rem;
}

.terminal-bar i {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: rgba(162, 174, 214, 0.26);
}

.terminal-body {
  display: grid;
  padding: 1.1rem 1.15rem 1.3rem;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;
  font-size: clamp(0.75rem, 1.05vw, 0.875rem);
  gap: 0.55rem;
  line-height: 1.5;
}

.term-line {
  overflow: hidden;
  margin: 0;
  color: var(--muted);
  opacity: 0;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.term-line b {
  color: var(--agent-green);
  font-weight: 400;
}

.term-line em {
  color: var(--agent-violet);
  font-style: normal;
  margin-inline-end: 0.6em;
}

.term-line:nth-child(4) em {
  color: var(--agent-blue);
}

.term-line:nth-child(5) em {
  color: var(--agent-amber);
}

.term-line:nth-child(6) em {
  color: var(--agent-green);
}

.term-caret {
  display: inline-block;
  width: 0.5em;
  height: 1em;
  animation: term-caret 1.5s steps(1, end) infinite;
  background: var(--muted);
  vertical-align: -0.14em;
}

.term-line {
  animation: term-roll 13s steps(1, end) infinite both;
}

.term-line:nth-child(1) {
  animation-delay: 0s;
}

.term-line:nth-child(2) {
  animation-delay: 0.75s;
}

.term-line:nth-child(3) {
  animation-delay: 1.7s;
}

.term-line:nth-child(4) {
  animation-delay: 2.75s;
}

.term-line:nth-child(5) {
  animation-delay: 3.7s;
}

.term-line:nth-child(6) {
  animation-delay: 4.75s;
}

.term-line:nth-child(7) {
  animation-delay: 5.9s;
}

.term-line:nth-child(8) {
  animation-delay: 6.6s;
}

@keyframes term-roll {
  0% {
    opacity: 0;
  }

  1%,
  88% {
    opacity: 1;
  }

  90%,
  100% {
    opacity: 0;
  }
}

@keyframes term-caret {
  0%,
  50% {
    opacity: 1;
  }

  50.01%,
  100% {
    opacity: 0;
  }
}

.history {
  padding-block: clamp(2.5rem, 5vw, 5rem);
  background:
    radial-gradient(circle at 24% 54%, rgba(231, 183, 105, 0.08), transparent 38%),
    var(--void);
}

.history-grid {
  display: grid;
  grid-template-areas: "media copy";
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  width: min(80rem, calc(100% - 2 * var(--page-inline)));
  gap: clamp(3rem, 6vw, 6rem);
  align-items: center;
}

.history-copy {
  grid-area: copy;
}

.history-copy::before {
  display: block;
  width: 4rem;
  height: 1px;
  margin-block-end: clamp(1.5rem, 3vw, 3rem);
  background: var(--amber);
  content: "";
}

.integrity {
  max-width: 28rem;
  margin-block-start: clamp(2rem, 4vw, 4rem);
  color: var(--text);
  font-size: clamp(1.15rem, 2vw, 1.65rem);
  font-weight: 700;
  line-height: 1.45;
}

.history-media {
  grid-area: media;
  overflow: hidden;
  aspect-ratio: 3 / 2;
  border-radius: 1rem;
  background: var(--panel);
  box-shadow:
    0 1.75rem 3.5rem rgba(0, 0, 0, 0.6),
    0 0.375rem 1rem rgba(0, 0, 0, 0.45);
}

.history-media img {
  width: 100%;
  height: 100%;
  filter: brightness(0.8);
  /* Cut to the frame's own 3:2, and the four sit dead centre — so no offset. */
  object-fit: cover;
  object-position: 50% 50%;
}

.closing {
  display: grid;
  min-height: 60svh;
  place-items: center;
  padding: clamp(5rem, 10vw, 9rem) var(--page-inline);
  background: var(--void);
}

.closing-inner {
  width: min(48rem, 100%);
  text-align: center;
}

.color-trace {
  width: min(18rem, 72%);
  height: 2px;
  margin: 0 auto clamp(2rem, 5vw, 4rem);
  background: linear-gradient(
    90deg,
    var(--agent-violet) 0 25%,
    var(--agent-green) 25% 50%,
    var(--agent-amber) 50% 75%,
    var(--agent-blue) 75% 100%
  );
}

.closing-dual {
  color: var(--text);
  font-size: min(clamp(2.5rem, 6vw, 6rem), 8.5vw);
  font-weight: 700;
  letter-spacing: -0.045em;
  line-height: 1.05;
}

.closing-philosophy {
  margin-block-start: clamp(1.5rem, 3vw, 2.5rem);
  color: var(--muted);
  font-size: clamp(1rem, 1.4vw, 1.25rem);
  font-weight: 400;
  line-height: 1.72;
}

.footer {
  padding: 0 var(--page-inline) clamp(2.5rem, 5vw, 4.5rem);
  color: var(--muted);
  font-family: var(--font-latin);
  font-size: 0.75rem;
  font-weight: 400;
  line-height: 1.4;
  text-align: center;
}

@media (max-width: 899.98px) {
  :root {
    --page-inline: clamp(1.125rem, 5vw, 1.5rem);
  }

  .hero {
    min-height: max(44rem, 100svh);
  }

  /* Uneven padding lifts the centred copy off the figures below it without moving the
     block out of the middle row. */
  .hero-copy {
    width: 100%;
    max-width: 22rem;
    padding-block-start: clamp(3rem, 8vh, 5rem);
    /* Trimming 30px off the lower pad drops the centred block by 15px. */
    padding-block-end: calc(clamp(10rem, 28vh, 15rem) - 1.875rem);
  }

  .hero-title {
    max-width: 9.4em;
  }

  /* The stage only occupies the lower band here, so its own top edge is a hard line no
     overlay can fully hide. Fade the image out at that edge instead of covering it.

     */
  .hero-stage {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 18%);
    mask-image: linear-gradient(to bottom, transparent 0%, #000 18%);
    inset: auto 0 0;
    height: 56%;
  }

  .hero-image,
  .hero-image:not([data-ambient-slide="0"]) {
    object-position: 72% center;
  }

  .hero-shade {
    background:
      linear-gradient(
        180deg,
        var(--void) 0%,
        var(--void) 40%,
        rgba(7, 10, 18, 0.92) 46%,
        rgba(7, 10, 18, 0.74) 54%,
        rgba(7, 10, 18, 0.5) 62%,
        rgba(7, 10, 18, 0.3) 70%,
        rgba(7, 10, 18, 0.18) 78%,
        rgba(7, 10, 18, 0.16) 88%,
        rgba(7, 10, 18, 0.24) 100%
      );
  }

  .supporting {
    text-shadow:
      0 1px 2px rgba(7, 10, 18, 0.96),
      0 0 0.6rem rgba(7, 10, 18, 0.82);
  }

  .work {
    min-height: 0;
    padding-block: clamp(6rem, 18vw, 8rem);
  }

  .work-grid {
    grid-template-columns: 1fr;
    gap: clamp(3.5rem, 12vw, 6rem);
  }

  .work-artifacts {
    min-height: 0;
  }

  .history {
    padding-block: clamp(5rem, 15vw, 8rem);
  }

  .history-grid {
    grid-template-areas:
      "media"
      "copy";
    grid-template-columns: 1fr;
    gap: clamp(3rem, 11vw, 5rem);
  }

  .history-media img {
    object-position: 50% 50%;
  }

  .closing {
    min-height: 60svh;
  }
}

@media (max-width: 340px) {
  .hero-image,
  .hero-image:not([data-ambient-slide="0"]) {
    object-position: 76% center;
  }

  .hero-title {
    font-size: 2.75rem;
  }

  .terminal-body {
    padding-inline: 0.85rem;
  }
}

@media (forced-colors: active) {
  :root {
    --void: Canvas;
    --surface: Canvas;
    --panel: Canvas;
    --panel-raised: Canvas;
    --text: CanvasText;
    --muted: CanvasText;
    --line: CanvasText;
    --violet: Highlight;
    --cobalt: Highlight;
    --amber: Highlight;
    --agent-violet: Highlight;
    --agent-green: Highlight;
    --agent-amber: Highlight;
    --agent-blue: Highlight;
  }

  .hero-shade {
    display: none;
  }

  .wordmark,
  .hero-copy {
    background: Canvas;
  }

  .hero-copy {
    padding-inline: 0.5rem;
  }

  .work,
  .history,
  .closing,
  .footer {
    background: Canvas;
  }

  .terminal,
  .history-media {
    /* A shadow carries no meaning in forced colours, so the edge comes back as a line. */
    border: 1px solid CanvasText;
    box-shadow: none;
  }

  .terminal {
    background: Canvas;
  }

  .term-line,
  .term-line b,
  .term-line em {
    color: CanvasText;
  }

  .color-trace,
  .history-copy::before,
  .terminal-bar i,
  .term-caret {
    background: Highlight;
  }
}

/* Clip the page to one screen while the curtain is up rather than switching the root to
   overflow: hidden. Hiding the root's overflow also drops the reserved scrollbar gutter,
   and the layout jumps sideways the moment the curtain leaves. */
body[data-entry="closed"] {
  height: 100svh;
  overflow: hidden;
}

/* The hero copy is withheld until the visitor presses through, then cuts in. */
[data-entry="closed"] .hero-copy > * {
  opacity: 0;
}

/* Hold the loop on its first beat behind the curtain. Pausing rather than starting late
   keeps the decode work done up front, so the first turn still paints on time. */
[data-entry="closed"] .ambient-enhanced .hero-image,
[data-entry="closed"] .ambient-enhanced .scene-line {
  animation-play-state: paused;
}

[data-entry="open"] .hero-copy > * {
  animation: entry-glitch 1s steps(1, end) both;
}

[data-entry="open"] .dual {
  animation-delay: 0.1s;
}

[data-entry="open"] .supporting {
  animation-delay: 0.2s;
}

@keyframes entry-glitch {
  0% {
    opacity: 0;
    clip-path: inset(0 0 100% 0);
    transform: translate3d(0, 0, 0);
  }

  8% {
    opacity: 1;
    clip-path: inset(44% 0 36% 0);
    transform: translate3d(-7px, 0, 0);
  }

  15% {
    opacity: 0.15;
    clip-path: inset(6% 0 78% 0);
    transform: translate3d(6px, 0, 0);
  }

  23% {
    opacity: 1;
    clip-path: inset(68% 0 10% 0);
    transform: translate3d(-4px, 0, 0);
  }

  31% {
    opacity: 0.3;
    clip-path: inset(22% 0 54% 0);
    transform: translate3d(5px, 0, 0);
  }

  39% {
    opacity: 1;
    clip-path: inset(0 0 0 0);
    transform: translate3d(-2px, 0, 0);
  }

  47% {
    opacity: 0.55;
    transform: translate3d(3px, 0, 0);
  }

  55% {
    opacity: 1;
    transform: translate3d(-1px, 0, 0);
  }

  63%,
  100% {
    opacity: 1;
    clip-path: inset(0 0 0 0);
    transform: none;
  }
}

/* The whole overlay is the control, so a press anywhere gives the gesture that an
   audible track needs. It stays translucent — the page behind it is the invitation. */
.curtain {
  position: fixed;
  z-index: 40;
  display: grid;
  padding: var(--page-inline);
  border: 0;
  backdrop-filter: blur(12px);
  background: rgba(7, 10, 18, 0.9);
  color: var(--text);
  cursor: pointer;
  font-family: var(--font-ko);
  gap: clamp(0.75rem, 1.6vw, 1.25rem);
  inset: 0;
  justify-items: center;
  place-content: center;
}

/* Leaving blooms outward from where the press landed: a soft circle grows and is
   subtracted from the veil, so darkness and blur clear together from the centre out. */
.curtain.is-leaving {
  animation: curtain-open 0.75s cubic-bezier(0.3, 0, 0.2, 1) forwards;
  -webkit-mask-image:
    radial-gradient(circle at 50% 50%, #000 0%, #000 44%, transparent 78%),
    linear-gradient(#000, #000);
  mask-image:
    radial-gradient(circle at 50% 50%, #000 0%, #000 44%, transparent 78%),
    linear-gradient(#000, #000);
  -webkit-mask-position: center, center;
  mask-position: center, center;
  -webkit-mask-repeat: no-repeat, no-repeat;
  mask-repeat: no-repeat, no-repeat;
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

@keyframes curtain-open {
  from {
    -webkit-mask-size: 0% 0%, 100% 100%;
    mask-size: 0% 0%, 100% 100%;
  }

  to {
    -webkit-mask-size: 360% 360%, 100% 100%;
    mask-size: 360% 360%, 100% 100%;
  }
}

.curtain:focus-visible {
  outline: 2px solid var(--cobalt);
  outline-offset: -6px;
}

/* A prompt waiting to be answered: the caret blinks, the command types itself in, and
   the caret goes on blinking after it. The box is a fixed 7.7ch so the line reveals
   left to right instead of re-centring on every character. */
.curtain-command {
  display: flex;
  width: 7.4ch;
  align-items: center;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;
  font-size: clamp(2.75rem, 7.5vw, 6rem);
  font-weight: 700;
  justify-content: flex-start;
  letter-spacing: normal;
  line-height: 1;
  text-shadow:
    0 2px 6px rgba(7, 10, 18, 0.96),
    0 0 1.4rem rgba(7, 10, 18, 0.88);
}

.curtain-typed {
  overflow: hidden;
  width: 7ch;
  animation: curtain-type 0.49s steps(7, end) both;
  white-space: nowrap;
}

.curtain-caret {
  width: 0.14ch;
  height: 0.86em;
  flex: none;
  margin-inline-start: 0.18ch;
  animation: curtain-caret 1.5s steps(1, end) infinite;
  background: currentColor;
}

/* Press acknowledged: the caret stops blinking and holds, the way a terminal shows it
   took your input while it finishes what it was doing. */
.curtain[data-state="armed"] .curtain-caret {
  animation: none;
  opacity: 1;
}

.curtain-note {
  animation: curtain-note-in 0.001s linear 0.49s both;
  color: var(--muted);
  font-size: clamp(1rem, 1.5vw, 1.2rem);
  line-height: 1.5;
  text-shadow:
    0 1px 3px rgba(7, 10, 18, 0.96),
    0 0 0.8rem rgba(7, 10, 18, 0.9);
}

@keyframes curtain-type {
  from {
    width: 0;
  }

  to {
    width: 7ch;
  }
}

@keyframes curtain-caret {
  0%,
  50% {
    opacity: 1;
  }

  50.01%,
  100% {
    opacity: 0;
  }
}

/* A hard cut the instant the last character lands — no fade, no flicker. The line is
   the prompt's answer, so it should simply be there. */
@keyframes curtain-note-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

.bgm-toggle {
  position: fixed;
  z-index: 10;
  right: var(--page-inline);
  bottom: clamp(1.25rem, 3vw, 2rem);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(13, 19, 32, 0.74);
  backdrop-filter: blur(10px);
  color: var(--muted);
  font-family: var(--font-ko);
  font-size: 0.8125rem;
  line-height: 1;
  cursor: pointer;
  transition:
    color 0.3s ease,
    border-color 0.3s ease,
    opacity 0.3s ease;
}

.bgm-toggle[hidden] {
  display: none;
}

/* On and off have to read at a glance: lit and moving, or dimmed and flat. */
.bgm-toggle[data-state="paused"] {
  opacity: 0.58;
}

.bgm-toggle[data-state="paused"] .bgm-bars i {
  height: 22%;
}

.bgm-toggle[data-state="paused"]:hover,
.bgm-toggle[data-state="paused"]:focus-visible {
  border-color: rgba(162, 174, 214, 0.36);
  color: var(--text);
  opacity: 1;
}

/* Lit in the same blue as 아이 and the .ai wordmark, so the one accent colour holds. */
.bgm-toggle[data-state="playing"],
.bgm-toggle[data-state="loading"] {
  border-color: rgba(102, 183, 255, 0.42);
  background:
    linear-gradient(rgba(102, 183, 255, 0.12), rgba(102, 183, 255, 0.12)),
    rgba(13, 19, 32, 0.74);
  color: var(--text);
}

.bgm-toggle[data-state="playing"] .bgm-bars i,
.bgm-toggle[data-state="loading"] .bgm-bars i {
  background: var(--agent-blue);
}

.bgm-toggle[data-state="playing"]:hover,
.bgm-toggle[data-state="playing"]:focus-visible {
  border-color: rgba(102, 183, 255, 0.72);
}

.bgm-toggle:focus-visible {
  outline: 2px solid var(--cobalt);
  outline-offset: 3px;
}

.bgm-toggle:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

.bgm-bars {
  display: flex;
  width: 0.875rem;
  height: 0.75rem;
  align-items: flex-end;
  flex: none;
  gap: 2px;
}

.bgm-bars i {
  height: 30%;
  flex: 1;
  border-radius: 1px;
  background: currentColor;
}

.bgm-toggle[data-state="loading"] .bgm-bars i,
.bgm-toggle[data-state="playing"] .bgm-bars i {
  animation: bgm-pulse 1.1s ease-in-out infinite;
}

.bgm-toggle[data-state="loading"] .bgm-bars i {
  animation-duration: 2.2s;
}

.bgm-toggle .bgm-bars i:nth-child(2) {
  animation-delay: 0.22s;
}

.bgm-toggle .bgm-bars i:nth-child(3) {
  animation-delay: 0.44s;
}

@keyframes bgm-pulse {
  0%,
  100% {
    height: 30%;
  }

  50% {
    height: 100%;
  }
}

@media (forced-colors: active) {
  .curtain {
    background: Canvas;
    color: CanvasText;
  }

  .bgm-toggle {
    background: Canvas;
    border-color: CanvasText;
    color: CanvasText;
  }

  .bgm-toggle[data-state="playing"] .bgm-bars i {
    background: Highlight;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ambient-enhanced .hero-image {
    animation: none;
    opacity: 0;
    transform: none;
  }

  .ambient-enhanced .hero-image:first-child {
    opacity: 1;
  }

  .ambient-enhanced .scene-line {
    animation: none;
    opacity: 0;
  }

  .ambient-enhanced .scene-line:first-child {
    opacity: 1;
  }

  [data-entry="open"] .hero-copy > * {
    animation: none;
    opacity: 1;
  }

  .bgm-toggle[data-state="playing"] .bgm-bars i {
    animation: none;
    height: 100%;
  }

  .curtain.is-leaving {
    animation: none;
    opacity: 0;
  }

  .curtain-typed {
    width: 7ch;
    animation: none;
  }

  .curtain-caret {
    animation: none;
    opacity: 1;
  }

  .curtain-note {
    animation: none;
    opacity: 1;
  }

  .term-line,
  .term-caret {
    animation: none;
    opacity: 1;
  }
}
