/* Design decisions live in DESIGN.md. */
:root {
  --bg: #0b0f14;
  --term: #11161d;
  --fg: #d6deeb;
  --dim: #7d8896;
  --green: #7ee787;
  --border: #2b3440;
  --grid: #2b3440; /* background grid lines (shader + CSS fallback) */
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  color-scheme: dark;
}
/* Light palette (proposal, see DESIGN.md): follows the OS unless theme.js set data-theme.
   The two copies must stay identical; bg.js re-reads the tokens on every theme change. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #eef1f5;
    --term: #f8fafc;
    --fg: #1f2933;
    --dim: #59636e;
    --green: #116329;
    --border: #d0d7de;
    --grid: #8c96a3;
    color-scheme: light;
  }
}
:root[data-theme="light"] {
  --bg: #eef1f5;
  --term: #f8fafc;
  --fg: #1f2933;
  --dim: #59636e;
  --green: #116329;
  --border: #d0d7de;
  --grid: #8c96a3;
  color-scheme: light;
}

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

/* CSS grid: no-JS/no-WebGL fallback for the shader in bg.js (same 32px cell, --grid at 60%). */
html {
  background-color: var(--bg);
  background-image:
    linear-gradient(color-mix(in srgb, var(--grid) 60%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--grid) 60%, transparent) 1px, transparent 1px);
  background-size: 32px 32px;
}

/* Shader layer: behind the page content, never catches clicks or selection. */
.bg {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
}

body {
  margin: 0;
  padding: 24px 12px;
  color: var(--fg);
  font: 15px/1.6 var(--mono);
  overflow-wrap: anywhere;
}
@media (min-width: 600px) {
  body { padding: 48px 24px; font-size: 16px; }
}

.term {
  position: relative; /* fs.js raises the last clicked terminal with z-index */
  max-width: 110ch;
  margin: 0 auto;
}

/* Terminals opened by fs.js: absolutely placed over the page with a mouse, in the flow below the opener on
   touch. No status line under them, so the window gets all four corners rounded. */
.term.child { width: min(80ch, 100vw - 48px); margin: 0; }
.child .window { border-radius: 8px; }
@media (pointer: fine) { .term.child { position: absolute; } }
@media not (pointer: fine) { .term.child { margin-top: 12px; } }
.dots .close { padding: 0; border: 0; width: 10px; height: 10px; border-radius: 50%; background: #ff5f57; cursor: pointer; }
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.window {
  background: var(--term);
  border: 1px solid var(--border);
  border-radius: 8px 8px 0 0;
}

.titlebar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  color: var(--dim);
  font-size: 0.8125rem;
}
.dots { display: flex; gap: 6px; flex: none; }
.dots span { width: 10px; height: 10px; border-radius: 50%; background: var(--border); }
.title { flex: 1; text-align: center; margin-right: 42px; }

/* Move/resize (window.js, mouse only): .term wraps the window and the status line, so they move together.
   Once resized, the session scrolls inside the window, so the title bar stays on top and the grip stays in
   the corner; the page-scroll reveal can't run in there. */
.window { position: relative; }
.term.resized { max-width: none; }
.resized .window { display: flex; flex-direction: column; }
.resized .session { flex: 1; min-height: 0; overflow: auto; }
.resized main section { animation: none; }
body { overflow-x: clip; } /* a window dragged past the edge must not add horizontal scroll */
.grip { display: none; }
@media (pointer: fine) {
  .movable .titlebar { cursor: move; user-select: none; }
  .movable .grip {
    display: block;
    position: absolute;
    right: 0;
    bottom: 0;
    width: 14px;
    height: 14px;
    cursor: nwse-resize;
    background: linear-gradient(135deg, transparent 50%, var(--border) 50%, var(--border) 60%, transparent 60%,
      transparent 75%, var(--dim) 75%, var(--dim) 85%, transparent 85%);
  }
}

.session { padding: 16px; }
@media (min-width: 600px) { .session { padding: 24px; } }

h1, h2, p, ul { margin: 0; font-size: inherit; font-weight: inherit; }
header, section { margin-bottom: 1.6em; }
.cmd { color: var(--fg); }
.prompt { color: var(--green); }
h1 { color: var(--green); font-size: 1.75em; font-weight: 700; line-height: 1.2; margin: 0.2em 0; }
.comment { color: var(--dim); }

/* File content is written as markdown structure (h3 title, em meta line, p, ul). In .doc windows (fs.js)
   it reads as a rendered preview; without JS the same rules style the static sections. */
.out h3 { margin: 0 0 0.4em; font-size: 1.25em; font-weight: 700; line-height: 1.3; color: var(--green); }
.out em { color: var(--dim); }
.out ul { padding-left: 2ch; }
/* 98 columns wide: shrink with the viewport so the picture keeps its shape on phones */
.out .ascii { margin: 0; font-size: min(1em, 1.5vw); line-height: 1.1; overflow-x: auto; }
.out > * + * { margin-top: 0.6em; }
.doc { line-height: 1.7; }

.ls { display: flex; flex-wrap: wrap; gap: 0 2ch; padding: 0; list-style: none; }
/* ls entries (fs.js): folders in --green with a trailing /, files in --fg, like `ls --color`. */
.ls button { padding: 0; border: 0; background: none; font: inherit; color: var(--fg); cursor: pointer; text-align: left; }
.ls .dir { color: var(--green); }
/* Typed CV commands (fs.js): one result per line (git log, grep), green keys and hits, man-page indent. */
.ls.rows { display: block; }
.key { color: var(--green); font-weight: 700; }
.ind { padding-left: 4ch; }
.swatches { display: flex; margin-top: 0.4em; }
.swatches span { width: 3ch; height: 1.2em; }
.ghost { visibility: hidden; } /* untyped rest of the welcome line keeps its space */

/* Hover/focus details. The underline is always set but transparent, so its colour can fade in. */
.ls button, a { text-decoration: underline transparent; text-underline-offset: 0.2em; }
a { color: var(--green); }
.ls button:enabled:hover, a:hover { text-decoration-color: currentColor; }
.ls .dir:enabled:hover, a:hover { color: var(--fg); }
.ls button:disabled { cursor: default; } /* older listings are scrollback (fs.js) */
:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }

.caret {
  display: inline-block;
  width: 1ch;
  height: 1.2em;
  vertical-align: text-bottom;
  background: var(--green);
  animation: blink 1s steps(1) infinite;
}
/* Typed command line (fs.js): looks like the prompt line, no box. 16px min so iOS doesn't zoom on focus. */
.caret-line { display: flex; flex-wrap: wrap; }
.caret-line .prompt { white-space: pre-wrap; } /* a flex item would drop the space after `$` */
.caret-line input {
  flex: 1 0 10ch;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--fg);
  font: inherit;
  font-size: max(16px, 1em);
  caret-color: var(--green);
  outline: none; /* the text caret shows focus, like a terminal */
}
@keyframes blink { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .caret { animation: none; }
}

/* Status line under the window, tmux/vim style. */
.status {
  display: flex;
  flex-wrap: wrap;
  gap: 0 2ch;
  padding: 2px 12px;
  background: var(--border);
  color: var(--fg);
  font-size: 0.8125rem;
}
.status .mode { color: var(--bg); background: var(--green); padding: 0 1ch; margin-left: -12px; }
/* Theme toggle (theme.js unhides it): a plain-text checkbox, "[x]" when light is on. */
.status .theme { margin-left: auto; }
/* Print button (print.js unhides it): opens the browser print dialog; .status is hidden in print. */
.status button { padding: 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer; }
.status button:hover { text-decoration: underline; }
.status .theme::before { content: "[ ] "; content: "[ ] " / ""; }
.status .theme[aria-pressed="true"]::before { content: "[x] "; content: "[x] " / ""; }

/* All new motion lives here, so reduced motion gets none of it (the caret has its own rule above). */
@media (prefers-reduced-motion: no-preference) {
  .ls button, a { transition: color 0.15s, text-decoration-color 0.15s; }

  /* Load fade: a page shorter than the viewport never scrolls, so the reveal below can't show there. */
  .session { animation: reveal 0.9s ease-out both; }

  /* Scroll-reveal; without view() support the sections just show. Done once fully entered (or, if
     taller than the viewport, once it fills it), so nothing rests part-faded. translateY only: 320px safe. */
  @supports (animation-timeline: view()) {
    main section {
      animation: reveal ease-out both;
      animation-timeline: view();
      animation-range: entry 0% entry 100%;
    }
  }
}
@keyframes reveal { from { opacity: 0; transform: translateY(2.5em); } }

/* Print / Save as PDF: the whole CV as plain black-on-white A4, no shader, chrome or terminal UI.
   fs.js hides .fs, shrinks .hero to sr-only and adds ls/scrollback/input to .session; undo all of that. */
@page { size: A4; margin: 15mm; }
@media print {
  :root { color-scheme: light; }
  html, body { background: #fff; color: #000; }
  body { padding: 0; font-size: 10pt; line-height: 1.35; }
  .bg, .titlebar, .status, .grip, .term.child, .session > :not(.hero, .fs), .cmd { display: none !important; }
  .term, .window { transform: none !important; width: auto !important; height: auto !important; max-width: none; }
  .window { display: block; background: none; border: 0; }
  .session, .resized .session { padding: 0; overflow: visible; animation: none; }
  main section { animation: none; }
  .fs[hidden] { display: block !important; }
  .hero.sr-only { position: static; width: auto; height: auto; overflow: visible; clip-path: none; white-space: normal; }
  h1, .out h3, .comment, .out em, a { color: #000; }
  .out h3 { font-size: 1.1em; margin-bottom: 0.2em; }
  section:has(.ascii) { display: none; } /* the picture is screen-only: keeps the CV within two A4 pages */
  .out > * + * { margin-top: 0.3em; }
  header, section { margin-bottom: 0.6em; }
  section { break-inside: avoid; }
  [data-dir="skills"] { columns: 2; column-gap: 4ch; } /* 18 short files: keeps the CV to two pages */
}
