/* ==========================================================================
   Artisan Hosting — crt.css
   Loaded only under /for-developers/. Two tiers:

     Tier 2  (.dev-page)  — mono type, terminal breadcrumb, dark panel,
                            one faint sky-tinted scanline on the hero.
     Tier 3  (.crt-screen)— the full tube: warmup, phosphor bloom, scanlines,
                            vignette, flicker, blinking cursor, inverse video.

   Why the old sheet couldn't be reused as-is: it put `filter:` on <body>,
   and an element with a filter becomes the containing block for its
   position:fixed descendants. So the scanline and bloom overlays were
   positioned against the filtered body box rather than the viewport, which
   together with `body { overflow: hidden }` is why beta/index.html shipped
   an inline `<style>body{overflow:scroll}</style>` fighting its own sheet.
   Everything here is scoped to a wrapper with absolute overlays instead.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Tier 2 — signal, not spectacle
   -------------------------------------------------------------------------- */

.dev-page h1,
.dev-page h2 {
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: -0.02em;
}

.dev-page .card h3,
.dev-page .price { font-family: var(--font-mono); font-weight: 500; }

/* Terminal breadcrumb — the tonal carrier. Says "deep end" faster than a
   sentence of prose would. */
.crumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  color: var(--text-3);
}

.crumb a { color: var(--text-3); text-decoration: none; }
.crumb a:hover, .crumb a:focus-visible { color: var(--text-1); text-decoration: underline; }
.crumb-sep { color: var(--accent); }
.crumb-prompt { transition: color 0.15s ease; }
.crumb-prompt.is-flash { color: #ff6b6b; }
.crumb-typed { color: var(--text-1); }
.crumb-cursor::after { content: "_"; animation: crt-blink 1.1s steps(1) infinite; }
.crumb-cursor.is-typing::after { animation: none; opacity: 1; }

.dev-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding-block: var(--space-4);
  border-bottom: 1px solid var(--border);
}

/* Fork-bomb gag: the bar drops out like the shell just died. */
.dev-bar.is-crashed { opacity: 0; pointer-events: none; }

/* Full CRT treatment by token override — the dark room, phosphor-green.
   .dev-panel is the room; .dev-panel > .wrap is the screen. Every nested
   .card, .btn and .eyebrow goes phosphor for free, identically in light
   and dark theme — no per-component dark variants to write or keep in
   sync. The beta tube (.crt-screen) has its own, separate palette below —
   green haze, but Debian-tty monochrome text, not phosphor. */
.dev-panel {
  --surface-1: color-mix(in srgb, var(--crt-bg) 88%, var(--crt-phosphor) 4%);
  --surface-2: color-mix(in srgb, var(--crt-bg) 82%, var(--crt-phosphor) 6%);
  --surface-sunken: #000;
  --border: color-mix(in srgb, var(--crt-phosphor) 35%, transparent);
  --border-strong: color-mix(in srgb, var(--crt-phosphor) 55%, transparent);
  --text-1: var(--crt-phosphor);
  --text-2: color-mix(in srgb, var(--crt-phosphor) 78%, transparent);
  --text-3: color-mix(in srgb, var(--crt-phosphor) 58%, transparent);
  --text-strong: var(--crt-phosphor-bright);
  --link: var(--crt-phosphor-bright);
  --link-hover: var(--crt-phosphor);
  --btn-ghost-text: var(--crt-phosphor);
  --btn-ghost-hover: color-mix(in srgb, var(--crt-phosphor) 14%, transparent);
  --accent: var(--crt-phosphor);
  --accent-soft: rgba(51, 255, 51, 0.1);
  --shadow-color: rgba(0, 0, 0, 0.5);
  --shadow-soft: 0 16px 40px -28px rgba(0, 0, 0, 0.6);
  --code-bg: color-mix(in srgb, var(--crt-bg) 80%, var(--crt-phosphor) 8%);
  --code-text: var(--crt-phosphor);
}

.dev-panel {
  background: var(--crt-bg);
  color: var(--crt-phosphor);
  font-family: var(--font-mono);
  padding-block: clamp(28px, 5vw, 56px);
}

.dev-panel h1, .dev-panel h2, .dev-panel h3 { color: var(--crt-phosphor-bright); font-weight: 500; text-shadow: 0 0 5px currentColor; }
.dev-panel p, .dev-panel li { color: var(--crt-phosphor); }
.dev-panel a { color: var(--crt-phosphor-bright); }
.dev-panel strong, .dev-panel b { color: var(--crt-phosphor-bright); }
.dev-panel ::selection { background: var(--crt-phosphor); color: #000; }

.dev-panel .eyebrow { color: var(--crt-phosphor); border-color: rgba(51, 255, 51, 0.3); background: rgba(51, 255, 51, 0.06); }

.dev-panel .card-link:hover,
.dev-panel .card-link:focus-visible { border-color: var(--crt-phosphor-bright); box-shadow: 0 0 28px rgba(51, 255, 51, 0.22); }

/* The screen. Scoped to .wrap so scanlines and the vignette sit inside a
   bordered box instead of bleeding across the full-width section — same
   shape as the tier-3 tube, just wearing the existing markup instead of a
   dedicated wrapper. */
.dev-panel > .wrap {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: clamp(20px, 3.5vw, 40px);
  border: 2px solid var(--crt-border);
  border-radius: 14px;
  box-shadow: 0 0 0 1px #000 inset, 0 24px 60px rgba(0, 0, 0, 0.45);
  animation: crt-flicker 7s linear infinite;
}

.dev-panel > .wrap::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(rgba(0, 0, 0, 0) 50%, rgba(0, 255, 0, 0.05) 50%);
  background-size: 100% 4px;
}

.dev-panel > .wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: radial-gradient(120% 120% at 50% 50%, transparent 55%, rgba(0, 0, 0, 0.55) 100%);
}

.dev-panel > .wrap > * { position: relative; z-index: 1; }

/* One faint scanline on the hero only. Sky-tinted, not green — at tier 2 it
   should read as paper texture, not as a CRT. */
.dev-hero { position: relative; isolation: isolate; overflow: hidden; }

.dev-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.5;
  background: repeating-linear-gradient(to bottom,
    transparent 0 3px,
    color-mix(in srgb, var(--sky) 7%, transparent) 3px 4px);
}

.dev-hero > * { position: relative; z-index: 2; }

/* --------------------------------------------------------------------------
   Tier 3 — the full tube
   -------------------------------------------------------------------------- */

/* Full width now — the tube is the page, not a box floating on it. Border
   and box-shadow keep it feeling like a screen; the horizontal padding
   moved onto .crt-inner (which also carries .wrap) so the frame runs edge
   to edge while the reading column stays a sane width.

   Palette is deliberately its own thing, not shared with .dev-panel: the
   glow — scanlines, vignette, border, bloom, text-shadow tint — stays
   phosphor green, but the glyphs themselves render Debian-tty monochrome
   (light gray, bright white on emphasis) instead of green-on-green. Reads
   as a real console sitting inside a CRT tube, rather than a green screen
   twice over. */
.crt-screen {
  --tty-fg: #d4d4d4;
  --tty-fg-bright: #ffffff;
  --tty-bg: #050604;

  --surface-1: color-mix(in srgb, #000 90%, #fff 4%);
  --surface-2: color-mix(in srgb, #000 85%, #fff 6%);
  --surface-sunken: #000;
  --border: color-mix(in srgb, #fff 22%, transparent);
  --border-strong: color-mix(in srgb, #fff 36%, transparent);
  --text-1: var(--tty-fg);
  --text-2: color-mix(in srgb, var(--tty-fg) 78%, transparent);
  --text-3: color-mix(in srgb, var(--tty-fg) 55%, transparent);
  --text-strong: var(--tty-fg-bright);
  --link: var(--tty-fg-bright);
  --link-hover: var(--crt-phosphor);
  --btn-ghost-text: var(--tty-fg);
  --btn-ghost-hover: color-mix(in srgb, #fff 12%, transparent);
  --accent: var(--tty-fg-bright);
  --accent-soft: rgba(255, 255, 255, 0.08);
  --shadow-color: rgba(0, 0, 0, 0.5);
  --shadow-soft: 0 16px 40px -28px rgba(0, 0, 0, 0.6);
  --code-bg: color-mix(in srgb, #000 85%, #fff 6%);
  --code-text: var(--tty-fg);

  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: clamp(20px, 3.5vw, 40px);
  border-block: 2px solid var(--crt-border);
  background: var(--tty-bg);
  color: var(--tty-fg);
  font-family: var(--font-mono);
  box-shadow: 0 0 0 1px #000 inset, 0 24px 60px rgba(0, 0, 0, 0.45);
}

.crt-inner { position: relative; z-index: 1; }

.crt-screen .callout--yes { border-left-color: var(--crt-phosphor); background: linear-gradient(180deg, rgba(51, 255, 51, 0.08), transparent 70%), var(--surface-1); }
.crt-screen .callout--no { border-left-color: #ff6b6b; background: linear-gradient(180deg, rgba(255, 107, 107, 0.08), transparent 70%), var(--surface-1); }

/* cat -b illusion — everything below the typed command is "file output",
   so it gets an approximated line-number gutter. One counter for the whole
   block; per-element, not per wrapped visual line, hence "approximated".
   Scoped to .cat-output, not .crt-inner, so the dev-bar (the prompt, not
   the file) never picks up a stray number.

   Numbers sit in one fixed-left column regardless of how deep the source
   element is nested (top-level paragraph vs. a card three divs in): each
   number is absolutely positioned against .cat-output itself, the shared
   containing block, with `top` left auto so it still falls in at that
   element's own natural line — only `left` is pinned. */
.cat-output {
  position: relative;
  counter-reset: cat-line;
  padding-left: clamp(2.75rem, 7vw, 3.75rem);
}

.cat-output h1,
.cat-output h2,
.cat-output h3,
.cat-output p,
.cat-output li,
.cat-output label.field > span:not(.field-hint) { counter-increment: cat-line; }

/* .grid-2 cards lay their headings/paragraphs/list items out side by side
   on desktop, not stacked — numbering them would put two numbers on the
   same visual line. Skip them rather than mis-position; a linear line-
   number gutter doesn't map onto parallel columns anyway. */
.cat-output .grid-2 h3,
.cat-output .grid-2 p,
.cat-output .grid-2 li { counter-increment: none; }

.cat-output h1::before,
.cat-output h2::before,
.cat-output h3::before,
.cat-output p::before,
.cat-output li::before,
.cat-output label.field > span:not(.field-hint)::before {
  content: counter(cat-line);
  position: absolute;
  left: 0;
  width: clamp(2rem, 5.5vw, 2.9rem);
  text-align: left;
  color: color-mix(in srgb, var(--tty-fg) 38%, transparent);
  font-weight: 400;
  user-select: none;
}

.cat-output .grid-2 h3::before,
.cat-output .grid-2 p::before,
.cat-output .grid-2 li::before { content: none; }

.crt-screen h1,
.crt-screen h2,
.crt-screen h3 {
  color: var(--tty-fg-bright);
  font-family: var(--font-mono);
  font-weight: 500;
  text-shadow: 0 0 6px rgba(51, 255, 51, 0.5), 0 0 1px rgba(255, 255, 255, 0.6);
}

.crt-screen p,
.crt-screen li { color: var(--tty-fg); }

.crt-screen a { color: var(--tty-fg-bright); text-decoration-color: color-mix(in srgb, var(--tty-fg-bright) 50%, transparent); }
.crt-screen a:hover { color: var(--crt-phosphor-bright); text-shadow: 0 0 6px rgba(51, 255, 51, 0.6); }

.crt-screen strong,
.crt-screen b { color: var(--tty-fg-bright); }

.crt-screen ::selection { background: var(--crt-phosphor); color: #000; }

/* Scanlines — absolute and scoped, not fixed to the viewport. */
.crt-screen::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(rgba(0, 0, 0, 0) 50%, rgba(0, 255, 0, 0.05) 50%);
  background-size: 100% 4px;
}

/* Vignette + implied curvature. The old sheet had no vignette at all — its
   only radial was the boot bloom, which is bright-in-center, the inverse. */
.crt-screen::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: radial-gradient(120% 120% at 50% 50%, transparent 55%, rgba(0, 0, 0, 0.55) 100%);
}

.crt-bloom { display: none; }

/* Boot: the filter lands on .crt-inner, never on body. */
.crt-screen[data-boot] .crt-inner {
  animation: crt-warmup 1.8s ease-out forwards;
  filter: brightness(0.05) contrast(220%);
}

.crt-screen[data-boot] .crt-bloom {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(circle at 50% 45%, rgba(102, 255, 102, 0.28), transparent 65%);
  animation: crt-flash 1.8s ease-out forwards;
}

/* Ported verbatim from beta/style.css. The overshoot at 70% and the dip at
   80% are the flyback surge — that pair is the whole illusion. The forwards
   resting state at contrast(130%) is what gives it a permanent phosphor
   punch after the boot finishes. */
@keyframes crt-warmup {
  0%   { filter: brightness(0.05) contrast(400%); }
  35%  { filter: brightness(0.2)  contrast(260%); }
  55%  { filter: brightness(0.45) contrast(220%); }
  70%  { filter: brightness(1.3)  contrast(320%); }
  80%  { filter: brightness(0.75) contrast(180%); }
  100% { filter: brightness(1)    contrast(130%); }
}

@keyframes crt-flash {
  0%   { opacity: 0; }
  40%  { opacity: 0.4; }
  60%  { opacity: 0.9; }
  80%  { opacity: 0.3; }
  100% { opacity: 0.05; }
}

@keyframes crt-blink { 50% { opacity: 0; } }

/* Barely-there mains hum. Subliminal at 7s; the old sheet had no flicker
   loop at all, so the tube went completely still after boot. */
@keyframes crt-flicker {
  0%, 97%, 100% { opacity: 1; }
  98%           { opacity: 0.94; }
  99%           { opacity: 0.98; }
}

.crt-screen:not([data-boot]) .crt-inner { animation: crt-flicker 7s linear infinite; }

.blinking-cursor::after { content: "_"; animation: crt-blink 1s steps(1) infinite; }

/* The typed command lives right in the breadcrumb now (see .crumb), so
   .crt-line just needs to fade in with the boot warmup, not its own block
   treatment — it's one inline flex item among others in .crumb. */
.crt-screen[data-boot] .crt-line { opacity: 0; }
.crt-screen[data-boot] .crt-line.is-shown { opacity: 1; }

/* Inverse-video terminal controls, ported from beta/style.css and scoped. */
.crt-screen input,
.crt-screen select,
.crt-screen textarea {
  background: #000;
  color: var(--tty-fg);
  border: 2px solid var(--tty-fg);
  border-radius: 0;
  font-family: var(--font-mono);
  padding: 9px 11px;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}

.crt-screen input:focus,
.crt-screen select:focus,
.crt-screen textarea:focus {
  outline: none;
  border-color: var(--tty-fg-bright);
  box-shadow: 0 0 0 3px rgba(51, 255, 51, 0.15);
}

.crt-screen ::placeholder { color: color-mix(in srgb, var(--tty-fg) 45%, transparent); }

.crt-screen .field > span { color: var(--tty-fg-bright); text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.8rem; }
.crt-screen .field > span.field-hint {
  color: color-mix(in srgb, var(--tty-fg) 60%, transparent);
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.78rem;
}

.crt-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 11px 16px;
  border: 2px solid var(--tty-fg);
  border-radius: 0;
  background: transparent;
  color: var(--tty-fg);
  font-family: var(--font-mono);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.crt-btn:hover:not([disabled]),
.crt-btn:focus-visible { background: var(--tty-fg-bright); color: #000; outline: none; }
.crt-btn[disabled] { opacity: 0.45; cursor: not-allowed; }

.crt-screen a:focus-visible:not(.crt-btn) { background: var(--tty-fg-bright); color: #000; outline: none; text-shadow: none; }

.crt-screen .form-error {
  background: rgba(255, 60, 60, 0.12);
  border-color: #ff6b6b;
  color: #ff9d9d;
  border-radius: 0;
}

.crt-screen .form-success {
  background: rgba(51, 255, 51, 0.12);
  border-color: var(--crt-phosphor);
  color: var(--crt-phosphor-bright);
  border-radius: 0;
}

/* Cap's widget renders in a shadow root, so it can't be fully themed from
   here. Give it a terminal-looking frame instead of leaving it floating. */
.crt-screen .captcha-slot:not(:empty) {
  padding: var(--space-3);
  border: 1px solid color-mix(in srgb, var(--tty-fg) 40%, transparent);
  background: rgba(255, 255, 255, 0.03);
}

/* --------------------------------------------------------------------------
   Reduced effects

   The head script resolves an explicit toggle over the OS setting and writes
   data-effects, so this is one gate. Reduced keeps green-on-black — that is
   brand identity, not motion — and drops motion, glow and overlays.
   The phosphor text-shadow goes too: at 0 0 5px it is a legibility problem
   in its own right, not only a motion one.
   -------------------------------------------------------------------------- */

:root[data-effects='reduced'] .crt-screen[data-boot] .crt-inner,
:root[data-effects='reduced'] .crt-screen:not([data-boot]) .crt-inner,
:root[data-effects='reduced'] .dev-panel > .wrap { animation: none; filter: none; }
:root[data-effects='reduced'] .crt-screen[data-boot] .crt-bloom { display: none; }
:root[data-effects='reduced'] .crt-screen::before,
:root[data-effects='reduced'] .dev-panel > .wrap::before,
:root[data-effects='reduced'] .dev-hero::after { display: none; }
:root[data-effects='reduced'] .crt-screen::after,
:root[data-effects='reduced'] .dev-panel > .wrap::after { background: none; }
:root[data-effects='reduced'] .crt-screen,
:root[data-effects='reduced'] .dev-panel { text-shadow: none; }
:root[data-effects='reduced'] .blinking-cursor::after,
:root[data-effects='reduced'] .crumb-cursor::after { animation: none; opacity: 1; }
:root[data-effects='reduced'] .crumb-typed { display: none; }
:root[data-effects='reduced'] .crumb-prompt.is-flash { color: var(--accent); }
:root[data-effects='reduced'] .crt-screen[data-boot] .crt-line { opacity: 1; }

/* Effects toggle sits in the dev bar, so it is reachable on tier 2 before
   anyone ever lands on the full-CRT beta page. */
.effects-toggle { font-family: var(--font-mono); font-size: 0.8rem; }

/* --------------------------------------------------------------------------
   Immersive chrome — beta.html only (body.crt-page). crt.js drives the
   header through three height tiers purely off scroll position: full at
   the top, half once scrolled past it, gone once the tube dominates the
   viewport (body.crt-immersive). There is no mid-scroll reveal — the only
   way back to full height is scrolling back up to the top. (An earlier
   version "peeked" the header back out on any upward scroll; that made it
   pop in and out on every wobble of a scroll gesture, so it's gone.)

   The header collapses its own height rather than just translating out of
   frame — a sticky element still reserves its flow space under a pure
   transform, which left a blank header-height gap up top instead of the
   tube reclaiming it. Scoped to .crt-page so the max-height/overflow pair
   never risks clipping nav dropdowns on the other /for-developers/ pages,
   which don't carry this class. -------------------------------------------- */

/* Footer stays put — no scroll-linked fade. */

body.crt-page header.nav {
  max-height: 120px;
  transition: max-height var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

/* The collapse/squish needs overflow:hidden to clip header.nav's own box,
   but .nav-dropdown-menu and the mobile .nav-links.open drawer are both
   position:absolute and spill out below it by design (see layout.css) —
   z-index alone (both are already above the tube) doesn't help once their
   own ancestor is clipping them. Drop the clip whenever either is actually
   open/hovered/focused so they render in full, at any header height tier. */
body.crt-page header.nav:not(:has(.nav-dropdown.open, .nav-dropdown:hover, .nav-dropdown:focus-within, .nav-links.open)) {
  overflow: hidden;
}

/* Scrolled past the top but not immersive yet: half height. Only a return
   to the very top restores full height — there is no mid-scroll "peek". */
body.crt-page.crt-scrolled header.nav {
  max-height: 60px;
}

body.crt-page.crt-immersive header.nav {
  max-height: 0;
  border-color: transparent;
  pointer-events: none;
}
