/* ==========================================================================
   Tokens
   ========================================================================== */

:root {
  --bg: #0b0b0b;
  --ink: #d8d8d8;
  --bright: #f2f2f2;
  --dim: #6b6b6b;
  --line: #262626;
  --accent: #e5484d;

  --mono: 'IBM Plex Mono', ui-monospace, Menlo, Consolas, monospace;

  --column: 90ch;
  --gutter: 1.5rem;
}


/* ==========================================================================
   Base
   ========================================================================== */

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

body {
  background: var(--bg);
  color: var(--ink);
  font: 400 15px/1.6 var(--mono);
  -webkit-font-smoothing: antialiased;
}

::selection {
  background: var(--ink);
  color: var(--bg);
}

:focus-visible {
  outline: 1px dashed var(--accent);
  outline-offset: 2px;
}

img,
video {
  display: block;
  max-width: 100%;
}

h1,
h2,
h3 {
  font-size: 1em;
  font-weight: 600;
  color: var(--bright);
}

b { font-weight: 600; color: var(--bright); }

.dim { color: var(--dim); }

/* The subtitle sits tight under the name. */
h1 + p { margin-top: 0 !important; }

code { font: inherit; color: var(--ink); }

a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--dim);
  text-underline-offset: 3px;
  transition: color .15s, text-decoration-color .15s;
}

a:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}


/* ==========================================================================
   Console
   ========================================================================== */

#console {
  max-width: calc(var(--column) + 2 * var(--gutter));
  margin: 0 auto;
  /* Room at the bottom so the prompt never hides behind the shortcut bar. */
  padding: 2.5rem var(--gutter) 6rem;
}

#screen > * + * { margin-top: 1rem; }

.output > * + * { margin-top: .9rem; }

.cmdline {
  margin-top: 1.75rem !important;
  color: var(--bright);
}

.ps {
  margin-right: .1ch;
  color: var(--accent);
}

/* The hidden `name` command's 张卓远 art: same 13px as the Liquid Chrome piece,
   with the line-height the art's cell grid was built for. */
.name-art {
  overflow: hidden;
  margin: 0;
  color: var(--ink);
  font: 400 13px/1.15 var(--mono);
  white-space: pre;
}

.accent { color: var(--accent); }

/* Blocks waiting to be printed by js/terminal.js. */
.pending { visibility: hidden; }
/* Media then draws itself in, top to bottom in scanline steps. No fill-mode forwards,
   so the clip is gone afterwards and focus outlines aren't cut off. */
.printing { animation: print-in 0.6s steps(16) backwards; }
@keyframes print-in {
  from { clip-path: inset(0 0 100% 0); }
  to { clip-path: inset(0); }
}

/* No bare prompt on an empty screen while the first screen prints. */
#console.is-booting #prompt { visibility: hidden; }

#prompt {
  display: flex;
  gap: 1ch;
  margin-top: 1.75rem;
}

.input-wrap {
  position: relative;
  flex: 1;
}

#cmd {
  width: 100%;
  padding: 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--bright);
  font: inherit;
  caret-color: transparent;
}

/* The block cursor; js/terminal.js moves it to the caret position. */
.cursor {
  position: absolute;
  top: .2em;
  left: 0;
  width: 1ch;
  height: 1.2em;
  background: var(--accent);
  opacity: .85;
  animation: blink 1.1s steps(1) infinite;
  pointer-events: none;
}

#prompt:not(:focus-within) .cursor {
  background: transparent;
  outline: 1px solid var(--accent);
  outline-offset: -1px;
  animation: none;
}

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


/* ==========================================================================
   Shortcut bar
   ========================================================================== */

#shortcuts {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 50;
  display: flex;
  justify-content: center;
  gap: .25rem 1.5ch;
  overflow-x: auto;
  padding: .7rem var(--gutter);
  border-top: 1px solid var(--line);
  background: var(--bg);
  scrollbar-width: none;
}

#shortcuts button,
button.cmd {
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  white-space: nowrap;
  cursor: pointer;
}

#shortcuts button { color: var(--dim); font-size: .87rem; }
#shortcuts button:hover,
button.cmd:hover { color: var(--accent); }


/* ==========================================================================
   Printed content
   ========================================================================== */

/* The ASCII Magic piece (js/chrome.js picks one per load) sits permanently at the top of the
   console as real text, at the same 13px as the site's small text. It's decoration only. It's drawn
   in the text colour, contained so its frames only repaint its own box, and the mask fades the piece's faint dotted
   background out at the edges so it doesn't read as a box. */
.chrome-art {
  display: grid;
  overflow: hidden;
  width: max-content;
  max-width: 100%;
  margin: 0 auto 1.5rem;
  font: 400 13px/1 var(--mono);
  color: var(--ink);
  opacity: .85;
  contain: content;
  -webkit-mask-image: radial-gradient(ellipse closest-side, #000 60%, transparent 100%);
  mask-image: radial-gradient(ellipse closest-side, #000 60%, transparent 100%);
}

/* Three stacked layers of plain text, split by glyph weight in js/chrome.js so there
   are no per-character spans: mid tones rose, the faint glyphs inside the shape
   red, everything else (including the dotted backdrop) the text colour. */
.chrome-art > pre {
  grid-area: 1 / 1;
  margin: 0;
  font: inherit;
}

.chrome-mid { color: color-mix(in srgb, var(--accent) 50%, var(--ink)); }
.chrome-red { color: var(--accent); }

.banner > div > * + * { margin-top: .9rem; }

/* The command line already names the section, so printed copies drop the heading.
   The no-JS README keeps them. */
.output > h2 { display: none; }

article + article { margin-top: 2rem; }
article > * + * { margin-top: .4rem; }

.tags { color: var(--dim); font-size: .87rem; }
.tags a { margin-left: 1ch; }

/* key / value lines: experience dates, skill groups, help */
.row {
  display: grid;
  grid-template-columns: 19ch 1fr;
  gap: 2ch;
}

.row .key {
  color: var(--dim);
  text-align: left;
}

.row button.key { color: var(--ink); }
.row button.key:hover { color: var(--accent); }
.row > div > * + * { margin-top: .2rem; }
.output > .row + .row { margin-top: .6rem; }

.thumb {
  width: fit-content;
  max-width: min(420px, 100%);
  margin-top: .75rem !important;
  cursor: zoom-in;
}

.thumb:hover { outline: 1px solid var(--accent); }

/* Project media keeps its own shape, uncropped: landscape up to 420px wide,
   portrait up to 340px tall. width/height in the HTML reserve the shape. */
.thumb > :is(img, video) {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 340px;
}

.thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.thumbs > :is(img, video),
.thumbs figure > :is(img, video) {
  width: 120px;
  height: 120px;
  background: #000;
  object-fit: cover;
  cursor: zoom-in;
  transition: opacity .15s;
}

.thumbs > :is(img, video):hover,
.thumbs figure:hover > :is(img, video) { opacity: .8; outline: 1px solid var(--accent); }

.thumbs figure:hover figcaption { color: var(--accent); }

.thumbs .fit-whole { object-fit: contain; }

/* Art shows each piece whole: one row height, natural widths, like a photo strip. */
.thumbs.whole > :is(img, video) {
  width: auto;
  height: 160px;
  object-fit: contain;
}

/* The resume page is the one thumb worth reading at a glance. */
.thumbs.whole > .resume-thumb { height: 320px; }

.thumbs figure { width: 120px; }

.thumbs figcaption {
  margin-top: .3rem;
  color: var(--dim);
  font-size: .75rem;
  line-height: 1.4;
}

/* Inline command links in a sentence ("More: projects  misc") need a gap. */
button.cmd + button.cmd { margin-left: 1.5ch; }

/* The "Featured" strip under whoami: wider 16:10 thumbs, so the first
   screen shows real builds before anyone types a command. */
.thumbs.featured figure { width: 180px; cursor: pointer; }
.thumbs.featured figure > :is(img, video) { cursor: pointer; }

/* A project jumped to from Featured flashes briefly. */
[data-id] { scroll-margin-top: 1.5rem; }
.is-shown { animation: shown 1.6s ease-out; }

@keyframes shown {
  0%, 25% { box-shadow: -1ch 0 0 -.5ch var(--accent); background: rgba(229, 72, 77, .08); }
  100% { box-shadow: -1ch 0 0 -.5ch transparent; background: transparent; }
}

.thumbs.featured figure > :is(img, video) {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
}


/* ==========================================================================
   Without JavaScript: #content is a plain README; with it, the console takes over
   ========================================================================== */

.js #content { display: none; }

.no-js #gate,
.no-js #console,
.no-js #shortcuts,
.no-js .js-only { display: none; }

.no-js #content {
  max-width: calc(var(--column) + 2 * var(--gutter));
  margin: 0 auto;
  padding: 2.5rem var(--gutter) 4rem;
}

.no-js #content > * + * { margin-top: 2.5rem; }
.no-js #content section > * + * { margin-top: .9rem; }


/* ==========================================================================
   Intro gate (js/intro.js)
   ========================================================================== */

#gate {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--bg);
  cursor: pointer;
  transition: opacity .45s ease;
}

#gate.is-leaving {
  opacity: 0;
  pointer-events: none;
}

/* The intro's 张卓远 as text (js/intro.js sets the font size), centred on screen. */
#gate {
  display: grid;
  place-items: center;
}

.gate-art {
  margin: 0;
  color: var(--ink);
  font-family: var(--mono);
  line-height: 1.15;
  white-space: pre;
}

.gate-hint {
  position: absolute;
  right: 1.5rem;
  bottom: 1.25rem;
  color: var(--dim);
  font-size: .75rem;
}


/* ==========================================================================
   Lightbox (js/lightbox.js)
   ========================================================================== */

#lightbox {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 1.5rem;
  background: rgba(8, 8, 8, .96);
  cursor: zoom-out;
}

#lightbox.is-open { display: flex; }

#lightbox :is(img, video) {
  max-width: 92vw;
  max-height: 80vh;
  border: 1px solid var(--line);
}

#lightbox [hidden] { display: none; }

.lightbox-caption {
  max-width: 640px;
  color: var(--dim);
  font-size: .85rem;
  text-align: center;
}

.lightbox-caption strong {
  display: block;
  color: var(--bright);
  font-weight: 600;
}

.lightbox-count {
  color: var(--dim);
  font-size: .75rem;
}

.lightbox-nav {
  position: absolute;
  top: 50%;
  z-index: 1;
  padding: .3rem .7rem;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--ink);
  font: 1.25rem/1 var(--mono);
  cursor: pointer;
  transform: translateY(-50%);
}

.lightbox-nav:hover { border-color: var(--accent); color: var(--accent); }

.lightbox-prev { left: 1rem; }
.lightbox-next { right: 1rem; }


/* ==========================================================================
   Small screens, reduced motion, high contrast
   ========================================================================== */

@media (max-width: 640px) {
  :root { --gutter: 1rem; }

  body { font-size: 14px; }

  .row {
    grid-template-columns: 1fr;
    gap: .1rem;
  }

  #shortcuts { justify-content: flex-start; }

  /* 72 characters at 13px is wider than a phone; the art is decoration only. */
  .chrome-art { display: none; }

  .thumbs { gap: .6rem; }

  .thumbs > :is(img, video),
  .thumbs figure > :is(img, video),
  .thumbs figure {
    width: 96px;
  }

  .thumbs > :is(img, video),
  .thumbs figure > :is(img, video) { height: 96px; }

  .thumbs.whole > :is(img, video) { width: auto; height: 110px; }

  /* Two across on a phone; the third wraps underneath. */
  .thumbs.featured figure { width: calc(50% - .3rem); }
  .thumbs.featured figure > :is(img, video) { width: 100%; height: auto; }
}

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