/*
 * modem.dbhq.uk - plain CSS, no framework, consuming brand/tokens.css's
 * custom properties throughout. Never a hardcoded colour, type or
 * spacing value where a token exists for it - see ../brand/README.md.
 *
 * Component names below match the design spec's "Components" table and,
 * where the concept is the same, modem-tui's own naming: crt-frame,
 * terminal-line, dial-button, waterfall, phase-row, download-row,
 * endorsement.
 */

@import url("../brand/tokens.css");

/* The page container width. Not a brand/tokens.json value: that file is
   for colour, type and CRT-effect values the TUI and the page must
   agree on, and a browser container width has no terminal equivalent.
   One local constant so the hero and main line up at every viewport
   instead of two hand-matched numbers drifting apart. */
:root {
  --page-max-width: 1320px;
}

* {
  box-sizing: border-box;
}

html {
  color-scheme: dark;
}

body {
  margin: 0;
  background: var(--modem-ground);
  color: var(--modem-white-bright);
  font-family: var(--modem-type-face);
  font-size: var(--modem-type-scale-base);
  line-height: var(--modem-line-height-ratio);
}

/* Visually hidden but still reachable by assistive tech - used for the
   skip link's destination context and for text equivalents of anything
   this page otherwise conveys only visually (the glow, the waterfall). */
.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;
}

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  z-index: 100;
  background: var(--modem-ground);
  color: var(--modem-amber-bright);
  padding: var(--modem-space-xs) var(--modem-space-sm);
  border: 1px solid var(--modem-amber-bright);
}

.skip-link:focus {
  left: var(--modem-space-sm);
  top: var(--modem-space-sm);
}

/* Browser surfaces the design system otherwise leaves to the platform
   default - themed from the same phosphor rather than left grey. */
::selection {
  background: var(--modem-amber-bright);
  color: var(--modem-ground);
}

* {
  scrollbar-color: var(--modem-amber-dim) var(--modem-ground);
  scrollbar-width: thin;
}

*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

*::-webkit-scrollbar-track {
  background: var(--modem-ground);
}

*::-webkit-scrollbar-thumb {
  background: var(--modem-amber-dim);
  border: 2px solid var(--modem-ground);
}

a {
  color: var(--modem-amber-bright);
}

a:hover,
a:focus-visible {
  color: var(--modem-white-bright);
}

:focus-visible {
  outline: 2px solid var(--modem-amber-bright);
  outline-offset: 2px;
}

h1,
h2,
h3 {
  font-family: var(--modem-type-face);
  color: var(--modem-amber-bright);
  font-weight: normal;
  letter-spacing: 0.02em;
}

/* Type hierarchy: h1 (the wordmark) is its own clamp; h2 and h3 step
   down from it in size only, never weight - a real terminal signals
   emphasis with size and colour, not a bold face the font stack may
   not even carry. */
h2 {
  font-size: var(--modem-type-scale-xlarge);
  margin-top: 0;
  border-bottom: 1px solid var(--modem-white-dim);
  padding-bottom: var(--modem-space-xs);
}

h3 {
  font-size: var(--modem-type-scale-large);
  margin: 0;
}

p {
  max-width: 70ch;
}

/* ---------------------------------------------------------------------
 * crt-frame - the bezel and screen the whole page sits inside. Scanlines
 * are dark stripes composited with `multiply`, so they can only darken a
 * pixel, never lighten one - lightening the gap between two already-dark
 * pixels is what would actually erode contrast. Verified against the
 * brand's own bright phosphors: even worst-case (a stripe sitting
 * directly over a glyph) leaves white-bright at 11.4:1 and amber-bright
 * at 7.8:1 against the ground colour, both comfortably clear of WCAG
 * AA's 4.5:1 for body text.
 *
 * The vignette (::before) is the same idea taken to the edges of the
 * viewport: a plain black overlay composites identically whether or not
 * `multiply` is declared (multiplying anything by black is the same as
 * compositing opaque black at that alpha), so the number that matters is
 * the alpha itself. Capped at 0.3 and checked against the WCAG contrast
 * formula directly at the darkest corner stop: white-bright drops from
 * 16.2:1 to 9.1:1, amber-bright from 10.8:1 to 5.5:1 - both still clear
 * of AA's 4.5:1 even in the one place the vignette is strongest. Fixed,
 * not absolute, so it reads as the screen's own edge falloff rather than
 * a mark on the document, and stays put as the long page scrolls under
 * it - the same reason the scanlines are fixed.
 * ------------------------------------------------------------------- */
.crt-frame {
  position: relative;
  min-height: 100vh;
  background: var(--modem-ground);
}

.crt-frame::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 49;
  background: radial-gradient(
    ellipse 85% 75% at 50% 45%,
    transparent 55%,
    rgba(0, 0, 0, 0.3) 100%
  );
}

.crt-frame::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 50;
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, var(--modem-scanline-opacity)) 0,
    rgba(0, 0, 0, var(--modem-scanline-opacity)) 1px,
    transparent 1px,
    transparent var(--modem-scanline-pitch)
  );
  mix-blend-mode: multiply;
}

@media (prefers-reduced-motion: reduce) {
  .crt-frame::after {
    /* The pattern itself is static already - this rule exists so any
       future flicker/animation added to the overlay is disabled here,
       not discovered missing later. */
    animation: none;
  }
}

/* ---------------------------------------------------------------------
 * crt-warmup - the one authored moment of motion: the tube collapsing
 * to a bright line and expanding back out, the reverse of how a CRT
 * looks switching off. A fixed overlay painted over everything else
 * (z-index above the scanline/vignette layers), never a transform on
 * the real content - the page is full height immediately and correct
 * underneath throughout, so there is no layout to mistime.
 * ------------------------------------------------------------------- */
.crt-warmup {
  position: fixed;
  inset: 0;
  z-index: 999;
  pointer-events: none;
  background: var(--modem-ground);
  animation: modem-crt-power-on 900ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}

@keyframes modem-crt-power-on {
  0% {
    transform: scaleY(0.004) scaleX(0.6);
    background: var(--modem-amber-bright);
    opacity: 1;
  }
  8% {
    transform: scaleY(0.012) scaleX(1);
    background: var(--modem-amber-bright);
    opacity: 1;
  }
  40% {
    transform: scaleY(1) scaleX(1);
    background: var(--modem-ground);
    opacity: 1;
  }
  100% {
    transform: scaleY(1) scaleX(1);
    background: var(--modem-ground);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .crt-warmup {
    display: none;
  }
}

/* ---------------------------------------------------------------------
 * Hero: web/hero.png with live text over the top, per the brief - never
 * baked into the image, so it stays sharp at any size and is editable
 * without regenerating anything.
 * ------------------------------------------------------------------- */
.hero {
  position: relative;
  overflow: hidden;
  border-bottom: 1px solid var(--modem-white-dim);
}

.hero__image {
  display: block;
  width: 100%;
  height: min(46vh, 380px);
  object-fit: cover;
  opacity: 0.55;
  /* The source render (web/hero.png) is green - a leftover from before
     the page committed to one phosphor colour throughout. There is no
     generator for this asset to re-render from, so the fix is a hue
     correction rather than a hand-edited or regenerated file: -67deg is
     the source image's own measured hue (~108°) rotated onto the brand
     amber's hue (~41°, from --modem-amber-bright/#FFB000), checked
     against a pixel sample of the rendered file, not guessed. */
  filter: hue-rotate(-67deg) saturate(1.1);
}

.hero__overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: linear-gradient(
    to top,
    rgba(10, 10, 10, 0.95) 0%,
    rgba(10, 10, 10, 0.35) 60%,
    rgba(10, 10, 10, 0.55) 100%
  );
}

/* Mirrors main's own max-width and horizontal padding, so the wordmark
   sits on the same left edge as the body copy below it however wide the
   viewport gets, while .hero__image and this gradient stay full-bleed. */
.hero__content {
  width: 100%;
  max-width: var(--page-max-width);
  margin: 0 auto;
  padding: 0 var(--modem-space-md);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--modem-space-xs);
}

.kicker {
  margin: 0;
  font-size: var(--modem-type-scale-base);
  color: var(--modem-white-bright);
  display: flex;
  align-items: center;
  gap: var(--modem-space-xs);
  flex-wrap: wrap;
}

.kicker__placeholder-tag {
  color: var(--modem-ground);
  background: var(--modem-error);
  font-size: var(--modem-type-scale-small);
  padding: 0 var(--modem-space-xs);
  border-radius: 2px;
  font-weight: bold;
  letter-spacing: 0.05em;
}

.wordmark {
  margin: 0;
  font-size: clamp(2.5rem, 10vw, 5rem);
  color: var(--modem-amber-bright);
  text-shadow: 0 0 var(--modem-bloom-radius) var(--modem-amber-bright);
  line-height: 1;
}

.hero__tagline {
  margin: 0;
  max-width: 60ch;
  font-size: var(--modem-type-scale-large);
  color: var(--modem-white-bright);
}

/* ---------------------------------------------------------------------
 * Layout - main is wider than the 70ch reading measure `p` keeps for
 * itself (below), so wide, non-prose content (the terminal, the
 * generated frames) has room to breathe without dragging body copy out
 * to an uncomfortable line length. Top padding is deliberately tighter
 * than bottom: the hero already separates itself with its own border,
 * so main does not need to repeat that gap before the first heading.
 * ------------------------------------------------------------------- */
main {
  max-width: var(--page-max-width);
  margin: 0 auto;
  padding: var(--modem-space-md) var(--modem-space-md) var(--modem-space-lg);
}

section {
  margin-bottom: var(--modem-space-lg);
}

/* ---------------------------------------------------------------------
 * terminal-line - a line of output. The typewriter reveal (added via
 * the `.terminal-line--typing` class while a line is being animated in)
 * is purely decorative timing; the full text is present in the DOM
 * throughout; see index.html's script for why that matters for
 * assistive tech and prefers-reduced-motion.
 * ------------------------------------------------------------------- */
.terminal {
  border: 1px solid var(--modem-white-dim);
  background: var(--modem-ground);
  padding: var(--modem-space-sm);
}

.terminal__output {
  min-height: calc(var(--modem-space-lg) * 3);
  max-height: 16em;
  overflow-y: auto;
  margin: 0 0 var(--modem-space-sm) 0;
  font-size: var(--modem-type-scale-small);
}

.terminal-line {
  margin: 0;
  color: var(--modem-white-bright);
  white-space: pre-wrap;
  word-break: break-word;
}

.terminal-line--command {
  color: var(--modem-amber-bright);
}

.terminal-line--phase {
  color: var(--modem-white-dim);
  padding-left: var(--modem-space-sm);
}

.terminal-line--phase::before {
  content: "- ";
}

/* The typewriter reveal used for AT responses (ATDT.../CONNECT 300).
   The full text is set on the element immediately, so a screen reader
   gets the whole line the instant it appears - this animates only the
   clip, purely cosmetic. */
.terminal-line--reveal {
  animation: modem-reveal 0.4s steps(20, end);
}

@keyframes modem-reveal {
  from {
    clip-path: inset(0 100% 0 0);
  }
  to {
    clip-path: inset(0 0 0 0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .terminal-line--reveal {
    animation: none;
  }
}

/* ---------------------------------------------------------------------
 * waterfall - the canvas and its axis furniture. Decorative: everything
 * it conveys (which phase is playing, whether there is carrier) also
 * exists as text elsewhere on the page, so it is safe to hide it from
 * assistive tech rather than attempt an ARIA description of a
 * spectrogram.
 * ------------------------------------------------------------------- */
.waterfall-wrap {
  display: flex;
  gap: var(--modem-space-xs);
  margin: var(--modem-space-sm) 0;
}

.waterfall-axis {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  font-size: var(--modem-type-scale-small);
  color: var(--modem-white-dim);
  text-align: right;
  width: 3.5ch;
  flex-shrink: 0;
}

.waterfall {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 960 / 200;
  background: var(--modem-ground);
  border: 1px solid var(--modem-white-dim);
  /* A wider native canvas (see index.html's width attribute) means more
     seconds of history are visible at once, not just a blown-up copy of
     the old one - keep upscaling crisp/blocky rather than smoothed,
     matching the rest of the CRT character-cell aesthetic. */
  image-rendering: pixelated;
}

.now-playing {
  font-size: var(--modem-type-scale-small);
  color: var(--modem-amber-bright);
  min-height: 1.4em;
}

/* ---------------------------------------------------------------------
 * dial-button - the primary action, and the mic-permission action in
 * its second state.
 * ------------------------------------------------------------------- */
.demo__controls {
  display: flex;
  gap: var(--modem-space-sm);
  flex-wrap: wrap;
  margin: var(--modem-space-sm) 0;
}

.dial-button {
  font-family: var(--modem-type-face);
  font-size: var(--modem-type-scale-base);
  background: transparent;
  color: var(--modem-amber-bright);
  border: 1px solid var(--modem-amber-bright);
  padding: var(--modem-space-xs) var(--modem-space-md);
  cursor: pointer;
  text-shadow: 0 0 var(--modem-bloom-radius) currentColor;
}

.dial-button:hover:not(:disabled),
.dial-button:focus-visible {
  background: var(--modem-amber-bright);
  color: var(--modem-ground);
  text-shadow: none;
}

.dial-button:disabled {
  color: var(--modem-white-dim);
  border-color: var(--modem-white-dim);
  cursor: not-allowed;
  text-shadow: none;
}

.diagnostic {
  font-size: var(--modem-type-scale-small);
  color: var(--modem-white-bright);
  min-height: 1.4em;
}

.diagnostic--warning {
  color: var(--modem-error);
}

/* ---------------------------------------------------------------------
 * The live endpoint panel (Task 2's real connection, wired up here)
 * ------------------------------------------------------------------- */
.endpoint {
  border: 1px solid var(--modem-amber-dim);
  padding: var(--modem-space-sm);
  margin-top: var(--modem-space-sm);
}

.endpoint__row {
  display: flex;
  gap: var(--modem-space-xs);
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: var(--modem-space-sm);
}

.endpoint input[type="text"] {
  font-family: var(--modem-type-face);
  background: var(--modem-ground);
  color: var(--modem-white-bright);
  border: 1px solid var(--modem-white-dim);
  padding: var(--modem-space-xs);
}

.endpoint button {
  font-family: var(--modem-type-face);
  background: transparent;
  color: var(--modem-white-bright);
  border: 1px solid var(--modem-white-dim);
  padding: var(--modem-space-xs) var(--modem-space-sm);
  cursor: pointer;
}

.endpoint button:hover:not(:disabled),
.endpoint button:focus-visible {
  border-color: var(--modem-amber-bright);
  color: var(--modem-amber-bright);
}

.endpoint button:disabled {
  color: var(--modem-white-dim);
  cursor: not-allowed;
}

.endpoint__status {
  color: var(--modem-amber-bright);
}

/* ---------------------------------------------------------------------
 * scenarios - the two ways to try modem: one device (split screen, the
 * default) and two devices (the real product). `win`/`bar`/`screen` name
 * the terminal-window chrome the same way modem-tui/examples/mockup.rs
 * names it for its own comparison page - same concept, same names, so
 * it reads as one product in both places.
 *
 * The frames inside `.screen` are generated - see web/_gen/frames.py
 * and the BEGIN/END markers around them in index.html - straight out
 * of App::render_into. Every colour on a cell is inline and literal on
 * purpose: it is the exact colour the real crate drew for that cell,
 * not a page decoration, so no token stands in for it here. The window
 * chrome around it is ordinary page furniture and uses tokens like
 * everything else.
 * ------------------------------------------------------------------- */
.scenario {
  margin-bottom: var(--modem-space-lg);
}

.scenario:last-child {
  margin-bottom: 0;
}

.scenario__label {
  margin-bottom: var(--modem-space-xs);
}

/* width: max-content so the window hugs the fixed-width monospace frame
   it holds, rather than stretching to fill the wider container and
   leaving a slab of empty chrome beside real content - the container is
   now wide enough that the frame renders at its natural size within it
   (max-width: 100% is the fallback once a viewport genuinely cannot fit
   that natural width, e.g. narrow phones, where .screen's own
   overflow-x still applies). */
.win {
  border: 1px solid var(--modem-white-dim);
  background: var(--modem-ground);
  width: max-content;
  max-width: 100%;
  margin: var(--modem-space-sm) 0 0 0;
}

.bar {
  background: var(--modem-ground);
  border-bottom: 1px solid var(--modem-white-dim);
  color: var(--modem-white-dim);
  font-size: var(--modem-type-scale-small);
  letter-spacing: 0.08em;
  padding: var(--modem-space-xs) var(--modem-space-sm);
}

.screen {
  overflow-x: auto;
  padding: var(--modem-space-sm);
}

/* Fixed-width, real terminal output - reflowing it would break every
   box-drawing alignment, so a narrow viewport scrolls the frame
   sideways instead (same choice mockup.rs's own comparison page makes
   for its `.row`) rather than shrinking it past legibility or letting
   it overflow the page. */
.screen pre {
  margin: 0;
  width: max-content;
  font-family: var(--modem-type-face);
  font-size: clamp(0.6rem, 1.3vw, 1rem);
  line-height: 1.15;
}

/* ---------------------------------------------------------------------
 * phase-row - one handshake phase in the explainer: label, description,
 * play control.
 * ------------------------------------------------------------------- */
.phase-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.phase-row {
  display: flex;
  gap: var(--modem-space-sm);
  align-items: flex-start;
  padding: var(--modem-space-sm) 0;
  border-bottom: 1px solid var(--modem-white-dim);
}

.phase-row:last-child {
  border-bottom: none;
}

.phase-row--active {
  background: rgba(255, 176, 0, 0.08);
}

.phase-row__play {
  font-family: var(--modem-type-face);
  flex-shrink: 0;
  background: transparent;
  color: var(--modem-amber-bright);
  border: 1px solid var(--modem-amber-bright);
  padding: var(--modem-space-xs) var(--modem-space-sm);
  cursor: pointer;
  min-width: 6ch;
}

.phase-row__play:hover:not(:disabled),
.phase-row__play:focus-visible {
  background: var(--modem-amber-bright);
  color: var(--modem-ground);
}

.phase-row__play:disabled {
  color: var(--modem-white-dim);
  border-color: var(--modem-white-dim);
  cursor: not-allowed;
}

.phase-row__body {
  flex: 1;
}

.phase-row__label {
  color: var(--modem-amber-bright);
  font-size: var(--modem-type-scale-base);
}

.phase-row__description {
  margin: var(--modem-space-xs) 0 0 0;
  color: var(--modem-white-bright);
}

/* ---------------------------------------------------------------------
 * download-row - platform, architecture, link.
 * ------------------------------------------------------------------- */
.download-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.download-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--modem-space-sm);
  padding: var(--modem-space-xs) 0;
  border-bottom: 1px solid var(--modem-white-dim);
}

.download-row:last-child {
  border-bottom: none;
}

.download-row__platform {
  color: var(--modem-white-bright);
}

.download-row__link {
  color: var(--modem-amber-bright);
  border: 1px solid var(--modem-amber-bright);
  padding: var(--modem-space-xs) var(--modem-space-sm);
  text-decoration: none;
}

.download-row__link:hover,
.download-row__link:focus-visible {
  background: var(--modem-amber-bright);
  color: var(--modem-ground);
}

.downloads__note {
  font-size: var(--modem-type-scale-small);
  color: var(--modem-white-dim);
}

/* ---------------------------------------------------------------------
 * endorsement - the DBHQ footer line. Nothing more.
 * ------------------------------------------------------------------- */
.endorsement {
  text-align: center;
  padding: var(--modem-space-md) 0 var(--modem-space-lg) 0;
  font-size: var(--modem-type-scale-small);
  color: var(--modem-white-dim);
}
