/* ----------------------------------------------------------------------
   loader.css — CRT snap-on + boot-stamp loading screen.

   Inserted at the top of <body> in index.html, sits above everything
   on first paint, plays its sequence (~2.75s), then fades out and is
   removed from the DOM by js/loader.js.

   Layered architecture (ascending z-index):
     z   1000   .crt-loader__stamp   — boot text screen
     z   1010   .crt-loader__crt     — black CRT layer with the scan/flash
   The whole loader sits at .crt-loader z-index 9999, above the body
   frame keyline (z-index 100) and any sidebar/menu content.

   Accessibility: under prefers-reduced-motion: reduce the loader is
   removed entirely — the site renders straight to its normal state.
   ---------------------------------------------------------------------- */

.crt-loader {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  /* The site frame's body clip-path crops descendants to the rounded
     inset rect, which is what we want — the CRT effect happens
     "inside the bezel" and reads as turning on a small monitor. */
  background: var(--color-bg);
  color: var(--color-fg);
  font-family: 'Departure Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  opacity: 1;
  transition: opacity 0.55s ease;
}

.crt-loader.is-dismissed {
  opacity: 0;
}

/* ---------- Stage 1 — CRT snap-on ---------- */

.crt-loader__crt {
  position: absolute;
  inset: 0;
  background: #000;
  z-index: 1010;
  overflow: hidden;
}
.crt-loader__crt.is-gone { display: none; }

.crt-loader__scan {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  background: #b8ffc7;
  transform: translateY(-50%);
  box-shadow:
    0 0 10px #b8ffc7,
    0 0 30px #b8ffc7,
    0 0 60px var(--color-fg);
}
.crt-loader.is-running .crt-loader__scan {
  animation: crt-loader-scan 0.85s cubic-bezier(0.7, 0, 0.3, 1) forwards;
}

@keyframes crt-loader-scan {
  0%   { height: 2px;   background: #b8ffc7; }
  18%  { height: 2px;   background: #b8ffc7; }
  50%  { height: 100vh; background: #b8ffc7; }
  62%  { height: 100vh; background: #ffffff; }
  100% { height: 100vh; background: var(--color-bg); }
}

.crt-loader__flash {
  position: absolute;
  inset: 0;
  background: #ffffff;
  opacity: 0;
  mix-blend-mode: screen;
}
.crt-loader.is-running .crt-loader__flash {
  animation: crt-loader-flash 0.85s ease-out forwards;
}

@keyframes crt-loader-flash {
  0%, 55% { opacity: 0; }
  62%     { opacity: 0.50; }
  100%    { opacity: 0; }
}

/* ---------- Stage 2 — boot stamp ---------- */

.crt-loader__stamp {
  position: absolute;
  inset: 0;
  background: var(--color-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  opacity: 0;
  transition: opacity 0.25s ease;
}
.crt-loader__stamp.is-shown { opacity: 1; }
.crt-loader__stamp.is-gone  { display: none; }

/* Multiple lines of boot text — left-aligned column inside the
   centred stamp panel, so each line stacks beneath the previous. */
.crt-loader__lines {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.35em;
  font-size: clamp(16px, 1.8vw, 22px);
  letter-spacing: 0.06em;
  white-space: pre;
  color: var(--color-fg);
  min-width: min(540px, 80vw);
}

.crt-loader__line-item {
  /* Each row of typed text */
  display: inline-block;
}

/* Disk-boot sub-line — sits beneath the main boot line at a slightly
   dim phosphor, narrower font weight, indented. Visually reads as
   "expansion of the line above", like a BIOS/Linux boot log:
     > portfolio.benpattison.co.uk
       [████████] ok
   The cells fill in fast (200–250ms), then "ok" lands in brighter
   phosphor as the verdict for that bit. */
.crt-loader__disk {
  color: var(--color-muted);
  font-size: 0.86em;
  padding-left: 1.5em;
  letter-spacing: 0.04em;
}

.crt-loader__disk-ok {
  color: var(--color-link);
  margin-left: 0.4em;
  text-transform: lowercase;
  letter-spacing: 0.08em;
}

/* Solid disk bar — single container with a fill element inside that
   animates its width left → right. No segmentation. Each disk can
   use a different duration variant (fast/medium/slow/coffee) so the
   loader doesn't feel metronomic. */
.crt-loader__bar {
  display: inline-block;
  width: 11em;
  height: 0.32em;
  margin: 0 0.25em;
  vertical-align: middle;
  background: transparent;
  border: 1px solid currentColor;
  border-radius: 1px;
  box-sizing: border-box;
  overflow: hidden;
  position: relative;
}
.crt-loader__bar-fill {
  display: block;
  width: 0;
  height: 100%;
  background: currentColor;
}

.crt-loader__bar.is-filling-fast .crt-loader__bar-fill {
  animation: bar-fill-stuttered 260ms linear forwards;
}
.crt-loader__bar.is-filling-medium .crt-loader__bar-fill {
  animation: bar-fill-stuttered 460ms linear forwards;
}
.crt-loader__bar.is-filling-slow .crt-loader__bar-fill {
  animation: bar-fill-stuttered 720ms linear forwards;
}

/* Long variant — paired with bpatts so the first bar finishes around
   the same time as the coffee bar (which starts ~1s later but takes
   ~1.9s). Linear so it just trundles along through the typing + other
   bars filling in parallel beneath it. */
.crt-loader__bar.is-filling-long .crt-loader__bar-fill {
  animation: bar-fill-stuttered 2800ms linear forwards;
}

/* Coffee struggle — fast early, painful crawl on the last 10%. The
   keyframe percentages do the work; total duration 1900ms. */
.crt-loader__bar.is-filling-coffee .crt-loader__bar-fill {
  animation: bar-fill-coffee 1900ms linear forwards;
}

/* Stuttered fill — starts and stops a few times on its way to 100%.
   Same percentage shape used by every non-coffee variant; the variant's
   animation duration determines how long each pause lasts. Flat sections
   are deliberate "drive thinking" beats. */
@keyframes bar-fill-stuttered {
  0%   { width: 0%; }
  10%  { width: 14%; }
  20%  { width: 14%; }   /* pause */
  32%  { width: 36%; }
  44%  { width: 36%; }   /* pause */
  58%  { width: 60%; }
  70%  { width: 60%; }   /* pause */
  84%  { width: 87%; }
  92%  { width: 87%; }   /* short pause */
  100% { width: 100%; }
}

/* Coffee struggle + stutter — fast early, painful crawl on the last
   few percent, with extra pauses throughout to make it feel like a
   moody appliance. Total duration 1900ms. */
@keyframes bar-fill-coffee {
  0%   { width: 0%; }
  9%   { width: 22%; }
  15%  { width: 22%; }   /* pause */
  24%  { width: 48%; }
  32%  { width: 48%; }   /* pause */
  42%  { width: 70%; }
  52%  { width: 70%; }   /* pause */
  62%  { width: 83%; }
  72%  { width: 83%; }   /* longer pause */
  82%  { width: 91%; }
  90%  { width: 91%; }   /* pause */
  96%  { width: 96%; }
  100% { width: 100%; }
}

/* Coffee bloom — the cursor blinks next to the full bar, then is
   pinned in place (position:fixed at its current rect, transform-origin
   center) and BLOOMS outward in both axes simultaneously, fills the
   viewport with phosphor, then fades to reveal the site. Faster and
   more dramatic than the horizontal-then-vertical wipe variant.
   Stage 1 (0 → 62%): explosion outward to full viewport.
   Stage 2 (62 → 100%): fade, revealing the site. */
@keyframes crt-loader-cursor-wipe {
  0% {
    transform: scaleX(1) scaleY(1);
    opacity: 1;
  }
  62% {
    transform: scaleX(300) scaleY(100);
    opacity: 1;
  }
  100% {
    transform: scaleX(300) scaleY(100);
    opacity: 0;
  }
}

/* Faux progress bar — fixed-width chunk that updates in-place as the
   "tile load" counts up. */
.crt-loader__progress {
  color: var(--color-link);
  font-variant-numeric: tabular-nums;
}

.crt-loader__ready {
  /* Brighter phosphor on the "verdict" — same hue, hotter value */
  color: #b8ffc7;
}

.crt-loader__cursor {
  display: inline-block;
  width: 0.55em;
  height: 1em;
  background: var(--color-fg);
  vertical-align: text-bottom;
  margin-left: 4px;
  animation: crt-loader-blink 0.8s step-end infinite;
}

@keyframes crt-loader-blink {
  0%, 50%        { opacity: 1; }
  50.01%, 100%   { opacity: 0; }
}

/* ---------- Loader noise overlay ----------
   Same animated phosphor-tinted CRT static used on the main site
   (body::after), but bumped to 0.18 opacity so it's clearly visible
   on the loader's flat black bg — and so the hand-off to the main
   site (where it's at 0.06) has the texture already in place,
   rather than appearing fresh after the loader dismisses.

   Reuses the bg-noise-shimmer @keyframes defined in styles.css.
   z-index 1030 sits above both the CRT layer (1010) and the boot
   stamp (1000), so the static is visible through the whole sequence. */
.crt-loader::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1030;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='nl'><feTurbulence type='fractalNoise' baseFrequency='0.25' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0.302 0 0 0 0  1 0 0 0 0  0.659 0 0 0 0  0 0 0 1 0'/></filter><rect width='100%25' height='100%25' filter='url(%23nl)'/></svg>");
  background-repeat: repeat;
  background-size: 200px 200px;
  opacity: 0.12;
  mix-blend-mode: screen;
  animation: bg-noise-shimmer 0.5s steps(10) infinite;
}

/* ---------- Power-off (mirrors the scan-on) ----------
   After the boot stamp finishes, JS re-mounts the .crt-loader__crt
   layer and adds .is-shutting-down. The .scan element reappears at
   full screen (now masking the boot stamp underneath), flashes white,
   collapses to a thin phosphor line across the middle, contracts to a
   single bright dot, then fades — revealing the site. */
/* When the power-off kicks in, fade the boot stamp out fast so the
   text doesn't peek through the first frame of the off animation. */
.crt-loader.is-shutting-down .crt-loader__stamp {
  opacity: 0;
  transition: opacity 90ms ease;
}

.crt-loader.is-shutting-down .crt-loader__crt {
  display: block;
  background: transparent;
}
.crt-loader.is-shutting-down .crt-loader__scan {
  /* Inline overrides for the off animation. Mirrored timing curve so
     the contraction feels like a CRT cutting power: hold full, snap to
     line, hold, contract, fade. */
  animation: crt-loader-power-off 900ms cubic-bezier(0.5, 0, 0.85, 0.2) forwards;
  transform-origin: center center;
}

@keyframes crt-loader-power-off {
  0%   {
    height: 100vh;
    transform: translateY(-50%) scaleX(1);
    background: #ffffff;
    box-shadow: none;
    opacity: 1;
  }
  22%  {
    height: 100vh;
    transform: translateY(-50%) scaleX(1);
    background: #ffffff;
    opacity: 1;
  }
  42%  {
    height: 2px;
    transform: translateY(-50%) scaleX(1);
    background: #b8ffc7;
    box-shadow: 0 0 10px #b8ffc7, 0 0 30px #b8ffc7, 0 0 60px var(--color-fg);
    opacity: 1;
  }
  80%  {
    height: 2px;
    transform: translateY(-50%) scaleX(0.005);
    background: #b8ffc7;
    box-shadow: 0 0 10px #b8ffc7, 0 0 30px #b8ffc7, 0 0 60px var(--color-fg);
    opacity: 1;
  }
  100% {
    height: 2px;
    transform: translateY(-50%) scaleX(0.005);
    background: #b8ffc7;
    box-shadow: 0 0 10px #b8ffc7, 0 0 30px #b8ffc7;
    opacity: 0;
  }
}

/* ---------- Reduced-motion bypass ----------
   Anyone with prefers-reduced-motion: reduce gets straight to the site,
   no animation. JS also checks this and exits its sequence early if
   set, so the DOM cleans up the same way. */
@media (prefers-reduced-motion: reduce) {
  .crt-loader { display: none; }
}
