/* ENTRYPOINT — terminals: the entry CLI and the hero terminal */

/* ── CLI SCREEN — macOS Terminal ───────────────────────── */
#screen-cli {
  display: none;
  position: fixed; inset: 0; z-index: 200;
  background: var(--term-desk);
  background-image:
    radial-gradient(circle at 20% 10%, var(--term-desk-hi) 0%, var(--term-desk-lo) 100%);
  padding: 40px;
  align-items: center;
  justify-content: center;
  font-family: 'SF Mono', 'Menlo', 'Monaco', 'Courier New', monospace;
  transition: padding 320ms cubic-bezier(0.32, 0.72, 0, 1);
}
/* Opened by .cli-open on <html>, set by the inline script in <head>. Hidden by
   default: without JS (and for crawlers) the home shows up directly. */
.cli-open #screen-cli { display: flex; }
.cli-open body { overflow: hidden; }

/* The overlay used to COVER the page without removing it, and the cover is only
   paint: Tab from the prompt walked straight out of the terminal into sixteen
   controls nobody could see — the bar, the two hero buttons, the six portfolio
   cards, the footer — scrolling the hidden page along the way. A keyboard or
   screen-reader visitor crossed the whole site blind before getting back.
   `visibility: hidden` is what takes a subtree out of the tab order AND out of
   the accessibility tree while keeping its layout boxes, so nothing reflows when
   the terminal closes and every measurement the suite takes still lands.
   Written as "everything but the overlay" rather than as a list of three
   selectors: a section added to the home later is covered by default instead of
   quietly reopening the hole. And it is CSS, not the `inert` attribute, because
   the class is set by the inline <head> script before the first paint — the rule
   is already in force on the first frame, and dropping the class restores
   everything with no JS state to keep in sync. */
.cli-open body > :not(#screen-cli) { visibility: hidden; }

/* One exception, and it is the minimise. That animation shrinks the window
   toward the corner and only THEN fades the backdrop (see .is-min below), and
   what the fade is there to reveal is the site arriving underneath. With the
   page still hidden it revealed a flat grey screen instead — the overlay's own
   backdrop over an empty body, for the ~180ms before closeCLI drops the class.
   Nothing in the suite saw it: every assertion reads a settled state, and this
   only exists mid-transition. So the page comes back the moment the window
   starts leaving. It is focusable again for those frames, which costs nothing:
   the overlay is still on top and closeCLI lands immediately after.
   A browser without `:has()` drops this rule and keeps the hiding, so the
   minimise shows grey for those ~180ms — the degradation is cosmetic and lands
   on the exception, never on the containment above. */
.cli-open:has(#screen-cli.is-min) body > :not(#screen-cli) { visibility: visible; }

.term-window {
  width: 100%;
  max-width: 1040px;
  height: 100%;
  max-height: 640px;
  background: var(--term-bg);
  border-radius: 8px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.35), 0 8px 20px rgba(0,0,0,0.2);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
  transition:
    max-width     320ms cubic-bezier(0.32, 0.72, 0, 1),
    max-height    320ms cubic-bezier(0.32, 0.72, 0, 1),
    border-radius 320ms cubic-bezier(0.32, 0.72, 0, 1),
    transform     420ms cubic-bezier(0.65, 0, 0.35, 1),
    opacity       420ms ease-in;
}

/* ── TRAFFIC LIGHT — green: maximise ────────────────────────
   Toggles. Dimensions go from px to vw/vh because `none` does not interpolate:
   `max-width: 1040px → none` would cut the transition, `→ 100vw` animates it. */
#screen-cli.is-max { padding: 0; }
#screen-cli.is-max .term-window {
  max-width: 100vw;
  max-height: 100vh;
  border-radius: 0;
  box-shadow: none;
}

/* Full screen takes the title bar off stage — as in macOS fullscreen, no chrome
   between the terminal and the screen edge. It leaves via transform and the
   window's `overflow: hidden` clips it; it leaves the flow so the terminal body
   takes the full height. */
#screen-cli.is-max .term-titlebar {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 2;
  transform: translateY(-100%);
  transition: transform 200ms cubic-bezier(0.32, 0.72, 0, 1);
}
/* But it does not vanish for good: touch the top and it slides back — hiding it
   with no way back would remove the very green button that restores. The 10px
   strip is an ::after of the bar itself, so hovering it counts as hovering the
   bar. It costs no new HTML element and disappears when the window is restored. */
#screen-cli.is-max .term-titlebar::after {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 100%;
  height: 10px;
}
#screen-cli.is-max .term-titlebar:hover { transform: translateY(0); }

/* ── TRAFFIC LIGHT — yellow: minimise ───────────────────────
   Shrinks toward the bottom corner (where the Dock would be) and fades; the JS
   calls closeCLI() when the animation ends. The light backdrop only starts to
   fade afterwards, so the window does not disappear before it shrinks. */
#screen-cli.is-min {
  opacity: 0;
  transition: opacity 160ms linear 260ms;
  pointer-events: none;
}
#screen-cli.is-min .term-window {
  transform: translate(-18vw, 75vh) scale(0.08);
  opacity: 0;
}

.term-titlebar {
  height: 28px;
  background: linear-gradient(to bottom, var(--term-chrome-top) 0%, var(--term-chrome-bottom) 100%);
  border-bottom: 1px solid var(--term-chrome-edge);
  display: flex;
  align-items: center;
  padding: 0 12px;
  position: relative;
  flex-shrink: 0;
}
.term-lights { display: flex; gap: 8px; align-items: center; }
.term-light {
  width: 12px; height: 12px; border-radius: 50%;
  border: 0.5px solid rgba(0,0,0,0.2);
  cursor: pointer;
}
.term-light.red    { background: var(--term-light-close); }
.term-light.yellow { background: var(--term-light-min); }
.term-light.green  { background: var(--term-light-max); }
.term-title-text {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-family: 'SF Pro', -apple-system, sans-serif;
  font-size: 12px;
  color: var(--term-chrome-fg);
  display: flex;
  align-items: center;
  gap: 6px;
}
.term-folder-icon {
  display: inline-block;
  width: 14px; height: 11px;
  background: var(--term-folder);
  border-radius: 2px;
  position: relative;
}
.term-folder-icon::before {
  content: '';
  position: absolute;
  top: -2px; left: 0;
  width: 6px; height: 3px;
  background: var(--term-folder);
  border-radius: 2px 2px 0 0;
}

.term-body {
  border: 1px solid var(--t-border-2);
  border-radius: 0 0 2px 2px;
  flex: 1;
  overflow-y: auto;
  /* Query container for the ASCII mark, the same way .hero-macterm-body is one.
     responsive.css sizes the art from 100cqw below 640px: the mark has to fit
     the box it is actually in, and this box is the viewport minus padding and
     borders — measuring the viewport instead left 35px of the 298 unused at
     320px, which on a mark already down at 3.4px is not slack to give away.
     Safe here because nothing inside .term-body is absolutely positioned
     against it (the title bar is a sibling; the block cursor is positioned in
     .term-input-field) — `container-type` implies `contain: layout`, which
     would otherwise become their containing block. */
  container-type: inline-size;
  padding: 12px 14px;
  color: var(--term-fg);
  font-family: 'SF Mono', 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: 13px;
  line-height: 1.4;
  background: var(--term-bg);
  scroll-behavior: smooth;
}
.term-body::-webkit-scrollbar { width: 14px; }
.term-body::-webkit-scrollbar-track { background: transparent; }
.term-body::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 7px; border: 4px solid transparent; background-clip: content-box; }

.cli-line {
  white-space: pre-wrap;
  color: var(--term-fg);
  font-family: inherit;
  font-size: 13px;
  line-height: 1.4;
}
.cli-line.ascii {
  color: var(--term-fg-bright);
  white-space: pre;
  font-size: 8px;
  line-height: 1;
  margin: 12px 0;
  letter-spacing: 0;
}
.cli-line.ascii .hl { color: var(--term-fg-bright); }
.cli-line.prompt-row { color: var(--term-fg); }
.cli-line.out-ok     { color: var(--term-ok); }
.cli-line.out-warn   { color: var(--term-warn); }
.cli-line.out-err    { color: var(--term-err); }
.cli-line.out-accent { color: var(--term-accent); }
/* Dimmed, not unreadable: the old #888680 measured 3.44:1 on --term-bg,
   and this class carries the only instruction on the entry screen ("Type help
   …") plus the whole "Possible causes" block on 404 — the lines that tell a
   visitor how to get out. Same role in the hierarchy, now at 4.67:1. */
.cli-line.out-dim    { color: var(--term-dim); }
.cli-line.spacer     { height: 6px; }

/* A link inside a terminal line (today, each portfolio company's domain). Colour
   is deliberately not declared: it inherits from the line, so the link follows
   .out-accent without becoming a second place where the terminal palette is
   decided. The dashes are how a terminal says "this is clickable" — base.css
   strips the underline from every <a>, so it comes back here, explicitly. */
.cli-line .cli-link {
  color: inherit;
  text-decoration: underline dashed;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.cli-line .cli-link:hover { text-decoration-style: solid; }

.term-input-row {
  display: flex;
  align-items: baseline;
  padding: 0;
  margin-top: 2px;
}
.term-input-prompt {
  color: var(--term-fg);
  font-family: inherit;
  font-size: 13px;
  white-space: pre;
  flex-shrink: 0;
}
/* The one focusable on the site that does NOT take the accent ring from
   base.css, and the exception is deliberate rather than inherited: this input
   carries `autofocus`, so on every visit to the home it is focused on the first
   frame. A ring that is always there on arrival marks nothing — it cannot say
   "you are here" when nothing else could be. What says it is the cursor, which is
   also what a terminal uses: a block, blinking on the prompt line, in the
   prompt's own colour, like the cursor of the hero terminal (.mt-cursor). The
   other five controls in the overlay are not autofocused and do take the ring,
   so moving between them by keyboard stays legible. `tools/e2e.mjs` names this
   element as the single exception to the ring rule, with this reason. */
/* The field is the cursor's containing block — not .term-body, whose
   `container-type` would otherwise claim it (see .term-body). `overflow: hidden`
   because a block past the last column would widen the terminal sideways. */
.term-input-field {
  position: relative;
  display: flex;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  margin-left: 2px;
}
#cli-input {
  flex: 1;
  /* `flex: 1` alone does not shrink an <input> past its intrinsic width — the
     default `size` attribute gives it ~170px of min-content, and a flex item
     never goes below that without this. At 320px the prompt plus that floor ran
     to 357px: the row overflowed the window, the overflow was clipped rather
     than scrolled, and the caret walked off the right edge while typing. */
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  font-family: inherit;
  font-size: 13px;
  color: var(--term-fg);
  /* A native caret is a bar, and CSS cannot make it a block everywhere. So it
     is hidden, and .term-cursor below is drawn in its place by cli.js. */
  caret-color: transparent;
  padding: 0;
}
#cli-input::placeholder { color: var(--term-placeholder); }

/* The block cursor. cli.js moves it to the caret and copies in the character
   under it, drawn in the background colour — the inverse video of a real
   terminal. It exists only while the input has focus: opacity, not
   visibility, because `visibility: visible` here would outlive the overlay's
   own hiding. Under reduced motion the blink runs once and the block stays. */
.term-cursor {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  display: flex;
  align-items: center;
  min-width: 1ch;
  white-space: pre;
  font: inherit;
  font-size: 13px;
  color: var(--term-bg);          /* = .term-body background */
  background: var(--term-fg);     /* = .term-input-prompt */
  opacity: 0;
  pointer-events: none;
}
#cli-input:focus + .term-cursor { opacity: 1; animation: blink 1s step-end infinite; }
.term-cursor.is-selecting { display: none; }

.cli-cmd-link {
  color: var(--term-link);
  cursor: pointer;
  border-bottom: 1px dotted transparent;
  transition: border-color 0.15s;
}
.cli-cmd-link:hover {
  border-bottom-color: var(--term-link);
}

/* ── HERO — macOS terminal (mirrors the entry CLI) ──────── */
/* A window floating over the panel, not the panel itself: the insets leave the
   grid of .hero-right showing around it, and the window is half again as wide
   as the column — the panel is two thirds of it (see --hero-term-w in
   layout.css, which is where the width below comes from), so the remaining
   third overhangs the left column on purpose, hence `overflow: visible` on
   .hero.

   The window shrinks along with the hero text, which scales with the same vw,
   so it never lands on the headline: the visible width gives 528px at 1280px
   (the width the site was designed at) and 420px at 1025px, one pixel before
   the layout goes single column. The overhang over the left column is a third
   of that — 176px and 140px at those two widths.

   The right edge runs past the edge of the screen by --hero-term-bleed: the
   session is meant to read as continuing beyond the frame, so the viewport
   crops the window and .hero clips what spills (see layout.css) — without that
   clip the page would gain a horizontal scrollbar over a shadow.

   What the screen crops is the WINDOW, never the text inside it, and that is
   why the bleed is a variable: two rules have to agree on the same number. The
   width adds it back, so the LEFT edge lands where it would with no bleed at
   all — 605px on a screen of 1025px, 752px on one of 1280px, and it stops
   moving when the visible width hits the 748px ceiling, at 1800px; that edge is
   what .hero-left's padding is calculated against, and it must not move — which
   is why the panel's width is derived from the window's and not the reverse.
   And the body's padding-right subtracts it, so the content box — the 100cqw
   the ASCII mark below measures itself against — ends where the screen ends.
   Hard-code the bleed into one of the two and not the other, and the tail of
   the mark goes off-screen in silence: that is the bug this used to have. */
.hero-terminal-stack {
  --hero-term-bleed: 64px;
  position: absolute;
  top: 64px;
  right: calc(-1 * var(--hero-term-bleed));
  bottom: 48px;
  /* The visible width plus what the screen crops: --hero-term-w is the part
     that shows, and the panel column is two thirds of that same variable. */
  width: calc(var(--hero-term-w) + var(--hero-term-bleed));
  display: flex;
  flex-direction: column;
  justify-content: center;
  z-index: 5;
}
.hero-macterm {
  background: var(--term-bg);
  border-radius: 8px;
  box-shadow: 0 30px 70px rgba(0,0,0,0.55), 0 10px 24px rgba(0,0,0,0.4);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* A window the height of the text block on the left (~600px), not the whole
     inset box: the ticker that used to take the rest of the column is gone, and
     a terminal stretched to 700px is a panel again. `0 1 auto` lets it shrink
     on short viewports; centred by the justify-content above. */
  flex: 0 1 auto;
  height: 600px;
  min-height: 280px;
}
.hero-macterm-bar {
  height: 26px;
  background: linear-gradient(to bottom, var(--term-chrome-top) 0%, var(--term-chrome-bottom) 100%);
  border-bottom: 1px solid var(--term-chrome-edge);
  display: flex; align-items: center; padding: 0 10px;
  position: relative; flex-shrink: 0;
}
.hero-macterm-lights { display: flex; gap: 6px; }
.hero-macterm-light {
  width: 11px; height: 11px; border-radius: 50%;
  border: 0.5px solid rgba(0,0,0,0.2);
}
.hero-macterm-light.red    { background: var(--term-light-close); }
.hero-macterm-light.yellow { background: var(--term-light-min); }
.hero-macterm-light.green  { background: var(--term-light-max); }
.hero-macterm-title {
  position: absolute; left: 50%; transform: translateX(-50%);
  font-family: -apple-system, 'SF Pro', sans-serif;
  font-size: 11px; color: var(--term-chrome-fg);
  display: flex; align-items: center; gap: 6px;
}
.hero-macterm-folder {
  display: inline-block; width: 13px; height: 10px;
  background: var(--term-folder); border-radius: 2px; position: relative;
}
.hero-macterm-folder::before {
  content: ''; position: absolute; top: -2px; left: 0;
  width: 5px; height: 3px; background: var(--term-folder); border-radius: 2px 2px 0 0;
}
.hero-macterm-body {
  flex: 1;
  /* Query container for the ASCII mark below: the window's width is not fixed
     (it is a clamp on the desktop and the whole panel when stacked), so the art
     sizes itself from the box it is in, not from the viewport. */
  container-type: inline-size;
  /* The right padding is the bleed plus the usual 16px: it pushes the content
     box back inside the viewport, so nothing written here is cropped even
     though the window is. */
  padding: 14px calc(16px + var(--hero-term-bleed)) 14px 16px;
  font-family: 'SF Mono', 'Menlo', monospace;
  font-size: 12px; line-height: 1.55;
  color: var(--term-fg);
  background: var(--term-bg);
  overflow-y: auto;
}
/* The mark: the same 130-column block as the entry CLI's, byte for byte (see
   tools/invariants.mjs), only smaller. 130 columns of a 0.6em-advance mono
   need 78 × the font size; dividing 100cqw — the width of the terminal body —
   by 80 leaves ~3% of slack for the fallback monospaces whose advance is not
   exactly 0.6em. 100cqw is the CONTENT box, which the padding above already
   pulled clear of the bleed — the mark measures the visible part of the
   window, not the part the screen cuts. The ceiling is the 8px the art has in the entry CLI: above
   that it stops being a mark at the top of the session and becomes the
   session. */
.hero-macterm .cli-line.ascii {
  font-size: min(8px, calc(100cqw / 80));
}

.hero-macterm-line { white-space: pre-wrap; color: var(--term-fg); }
.hero-macterm-line.mt-dim { color: var(--term-dim); }   /* 4.67:1 — see .out-dim above */
.hero-macterm-line.mt-ok  { color: var(--term-ok); }

.mt-prompt { color: var(--term-fg); }
.mt-cmd    { color: var(--term-accent); }
.mt-ok     { color: var(--term-ok); }
.mt-cursor {
  display: inline-block; width: 7px; height: 13px;
  background: var(--term-fg); vertical-align: middle;
  animation: blink 1s step-end infinite;
}
@keyframes blink { 50% { opacity: 0; } }
