/* ==========================================================================
   rkgrant/portfolio
   A near-black field with white ink and one accent colour.
   ========================================================================== */

/* The accent is registered as a colour so that it can fade from one body's
   colour to the next. */
@property --accent { syntax: "<color>"; inherits: true; initial-value: #f0a14a; }
@media (prefers-reduced-motion: no-preference) {
  :root { transition: --accent 0.6s ease; }
}

/* IBM Plex Mono in three weights. Each weight comes as a Latin file and a
   Latin Extended file, which holds the Czech letters. A browser downloads
   the second file only when the page uses one of its characters. */
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-400-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-400-latin-ext.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-600-latin.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-600-latin-ext.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-700-latin.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("../fonts/ibm-plex-mono-700-latin-ext.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --bg: #050608;
  --panel: rgba(6, 7, 10, 0.86);
  --ink: #eceae4;          /* white accent */
  --ink-2: #a8a9ae;
  --ink-3: #7a7c83;        /* the dimmest text, 4.8:1 on --bg */
  --ink-4: #33353b;
  --border: #62646b;       /* box borders and other drawn marks */
  --accent: var(--accent-default);
  --accent-ink: #1a1107;   /* text on the accent */
  /* The accent takes the colour of the body on screen: the one the page
     opens on, then the one in focus in the explorer. A moon without a
     colour here takes its planet's. */
  --accent-default: #f0a14a;
  --accent-sun: #ffc24a;
  --accent-mercury: #d6a47c;
  --accent-venus: #ecc76a;
  --accent-earth: #5ea9ff;
  --accent-moon: #a7b6d1;
  --accent-mars: #ec6f45;
  --accent-jupiter: #dd8f5f;
  --accent-saturn: #f0a14a;
  --accent-uranus: #7dd9d5;
  --accent-neptune: #6d8dff;
  --accent-pluto: #f0b49c;
  --accent-io: #e3d24f;
  --accent-europa: #d9a779;
  --accent-titan: #e9a246;
  --accent-enceladus: #b9e2ff;
  --accent-triton: #e6b7b0;
  --accent-halley: #8fc3ff;
  --accent-jwst: #f2c14e;
  --accent-proxima: #ff7a52;
  --accent-alphacena: #ffe0a0;
  --accent-alphacenb: #ffba74;
  --accent-siriusa: #bcd2ff;
  --accent-siriusb: #a8c2ff;
  --accent-vega: #c4d6ff;
  --accent-arcturus: #ffad66;
  --accent-polaris: #ffe2a8;
  --accent-betelgeuse: #ff8a52;
  --accent-rigel: #9fbaff;

  --font: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  /* Body text is 13px and secondary text 11px at every screen width. */
  --fs: 0.8125rem;
  --fs-sm: 0.6875rem;
  --lh: 1.25rem;           /* every frame snaps to this line grid */
  --bar-h: 2.75rem;
  --gutter: 2rem;
  --max: 72.5rem;

  color-scheme: dark;
}

@media (max-width: 45em) {
  :root { --gutter: 1rem; --bar-h: 2.5rem; }
}

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

html {
  background: var(--bg);
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--bar-h) + 1.5rem);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 var(--fs) / var(--lh) var(--font);
  font-variant-ligatures: none;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: inherit; text-decoration: none; }
p { margin: 0 0 var(--lh); }
p:last-child { margin-bottom: 0; }
/* In English the last paragraph on screen is followed by its hidden Czech
   copy, so it is not the last child and needs its margin removed here. */
p[lang="en"]:has(+ p[lang="cs"]:last-child) { margin-bottom: 0; }
h1, h2, h3, h4 { margin: 0; font-size: inherit; font-weight: 600; }
pre { margin: 0; font: inherit; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
strong { font-weight: 700; color: var(--ink); }

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

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

/* Every piece of text exists in English and Czech; only the active one shows. */
html[lang="en"] body [lang="cs"],
html[lang="cs"] body [lang="en"] { display: none !important; }

.dim { color: var(--ink-3); }
.accent { color: var(--accent); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

.skip {
  position: fixed; left: 0.75rem; top: -3.75rem; z-index: 50;
  padding: 0.25rem 0.625rem; background: var(--ink); color: var(--bg);
}
.skip:focus { top: 0.75rem; }

/* ==========================================================================
   Sky: the ASCII planet canvases + a legibility veil
   ========================================================================== */
.sky {
  position: fixed; inset: 0; z-index: 0;
  width: 100vw; height: 100vh;
  pointer-events: none;
  opacity: var(--sky, 1);
  transition: opacity 0.6s ease;
}
/* The veil dims the planet behind the text. The HUD's labels sit above it
   at full strength, and they keep clear of the text themselves. */
.sky--hud { z-index: 2; opacity: 1; }

.veil {
  position: fixed; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(90deg, rgba(5, 6, 8, 0.94) 0%, rgba(5, 6, 8, 0.8) 34%, rgba(5, 6, 8, 0) 60%);
  opacity: var(--veil, 1);
}
@media (max-aspect-ratio: 1/1) {
  .veil { background: linear-gradient(180deg, rgba(5, 6, 8, 0) 26%, rgba(5, 6, 8, 0.78) 52%, rgba(5, 6, 8, 0.9) 100%); }
}

main, .foot { position: relative; z-index: 3; }

/* ==========================================================================
   Explore: the page fades away and the sky takes the whole screen
   ========================================================================== */
main, .foot, .bar, .veil { transition: opacity 0.7s ease, visibility 0s linear 0s; }
.is-exploring main, .is-exploring .foot, .is-exploring .bar, .is-exploring .veil {
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.7s ease, visibility 0s linear 0.7s;
}
.is-exploring, .is-exploring body { overflow: hidden; overscroll-behavior: none; }
.is-exploring .sky { opacity: 1; }

.explore {
  position: fixed; inset: 0; z-index: 20;
  font-size: var(--fs-sm); color: var(--ink-2);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.6s ease, visibility 0s linear 0.6s;
}
.is-exploring .explore { opacity: 1; visibility: visible; transition: opacity 0.8s ease 0.35s, visibility 0s; }
.explore__stage {
  position: absolute; inset: 0;
  cursor: grab; touch-action: none;
  pointer-events: auto;
}
.explore__stage.is-pick { cursor: pointer; }
.explore__stage.is-drag { cursor: grabbing; }
.explore__top, .explore__list, .explore__info, .explore__hint { position: absolute; pointer-events: auto; }
.explore__top {
  top: 0; left: 0; right: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1.75rem;
  padding: 0.75rem var(--gutter);
  background: linear-gradient(180deg, rgba(5, 6, 8, 0.85), rgba(5, 6, 8, 0));
}
.explore__title { margin: 0; color: var(--ink); text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap; }
.explore__date { margin-left: 1.5ch; color: var(--ink-2); font-variant-numeric: tabular-nums; letter-spacing: 0; }
.explore__set { padding: 0; color: inherit; font: inherit; letter-spacing: inherit; border-bottom: 1px dotted var(--ink-3); }
.explore__set:hover, .explore__set:focus-visible { border-bottom-color: var(--accent); }
.explore .explore__set[hidden] { display: none; }
/* under the date field: how it reads what is typed, moments to go to, and a calendar */
.explore .explore__go {
  display: block; position: absolute; top: calc(100% + 0.25rem); left: var(--gutter); z-index: 2;
  width: min(62ch, calc(100vw - 2 * var(--gutter)));
  max-height: calc(100vh - 6rem); overflow-y: auto; overscroll-behavior: contain;
  font-size: var(--fs-sm); line-height: var(--lh); color: var(--ink-2);
  text-transform: none; letter-spacing: 0; white-space: normal;
  background: rgba(5, 6, 8, 0.96);
}
.explore__go[hidden] { display: none; }
.explore__read, .explore__read span, .explore__moments, .explore__keys { display: block; }
.explore__read { color: var(--ink); min-height: var(--lh); }
.explore__read .dim { color: var(--ink-3); }
.explore__read.is-bad { color: var(--accent); }
.explore__moments { margin-top: var(--lh); }
.explore__moment { display: flex; gap: 1ch; width: 100%; padding: 0; color: var(--ink-2); text-align: left; }
.explore__mname { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.explore__mname::after { content: " ......................................................"; color: var(--ink-4); }
.explore__mdate { flex: none; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.explore__moment:hover, .explore__moment:focus-visible { color: var(--ink); }
.explore__moment:hover .explore__mdate, .explore__moment:focus-visible .explore__mdate { color: var(--accent); }
.explore .explore__cal { display: block; margin-top: var(--lh); padding: 0; color: var(--ink-2); }
.explore .explore__cal[hidden] { display: none; }
.explore__keys { margin-top: var(--lh); color: var(--ink-3); }
@media (hover: none) { .explore__keys { display: none; } }
/* on a phone the list gives the names room and leaves out the times */
@media (max-width: 45em) { .explore__mtime { display: none; } }
.explore__when {
  width: 17ch; padding: 0; border: 0; border-bottom: 1px solid var(--accent); border-radius: 0;
  background: transparent; color: var(--ink); font: inherit; letter-spacing: 0; outline: none;
}
/* a phone zooms into a text field smaller than 16px when it is tapped */
@media (pointer: coarse) { .explore__when, .terminal .terminal__line input { font-size: 1rem; } }
.explore__when[aria-invalid="true"] { color: var(--accent); border-bottom-style: dashed; }
#ex-warp { margin-left: 1.5ch; letter-spacing: 0; }
.explore__time { display: flex; gap: 1ch; }
.explore__close { margin-left: auto; color: var(--ink); }
.explore__list {
  left: var(--gutter); top: 50%; transform: translateY(-50%);
  display: flex; flex-direction: column; align-items: flex-start;
  /* with a planet's moons and a group open it can be taller than the
     screen: clear of the top bar and the hint, and scrolls past that */
  max-height: calc(100% - 11rem);
  overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain;
  padding: 0.25rem 0.75rem 0.25rem 0.25rem; margin-left: -0.25rem;
  scrollbar-width: thin; scrollbar-color: var(--ink-4) transparent;
}
/* each name keeps a dark backing, so it reads over the galaxy's stars */
.explore__list button { padding: 1px 0; color: var(--ink-3); text-transform: lowercase; text-align: left; background: rgba(5, 6, 8, 0.92); }
.explore__list button::before { content: "  "; white-space: pre; color: var(--accent); }
.explore__list button:hover, .explore__list button:focus-visible { color: var(--ink); }
.explore__list button[aria-current="true"] { color: var(--accent); }
.explore__list button[aria-current="true"]::before { content: "> "; }
.explore__list .explore__sub { padding-left: 2ch; }
.explore__list .explore__all { margin-top: 0.625rem; color: var(--ink-2); }
.explore__list .explore__all::before { content: ""; }
.explore__list [hidden] { display: none; }
.explore__list .explore__group { margin-top: 0.5rem; color: var(--ink-2); }
.explore__list .explore__group::before { content: "+ "; color: var(--border); }
.explore__list .explore__group[aria-expanded="true"]::before { content: "- "; color: var(--accent); }
.explore__list [data-in] { padding-left: 2ch; }
.explore__list .explore__sub[data-in] { padding-left: 4ch; }
.explore__note { margin: 0.625rem 0 0; color: var(--ink-2); max-width: 44ch; }
.explore .explore__info {
  position: absolute;
  right: var(--gutter); bottom: calc(1.875rem + var(--lh));
  width: min(46ch, calc(100vw - 2 * var(--gutter)));
  font-size: var(--fs-sm); line-height: var(--lh);
}
.explore__info .kv dt { width: 15ch; }
.explore__info .kv dd { font-variant-numeric: tabular-nums; }
.explore__hint {
  left: 0; right: 0; bottom: 0.875rem; margin: 0;
  text-align: center; color: var(--ink-3);
  text-shadow: 0 0 3px var(--bg), 0 0 6px var(--bg), 0 0 10px var(--bg);
  pointer-events: none;
}
.explore__hint .hint--touch { display: none; }
@media (hover: none) and (pointer: coarse) {
  .explore__hint .hint--mouse { display: none; }
  .explore__hint .hint--touch { display: inline; }
}
/* Phones. The body list is one row along the bottom that scrolls sideways,
   and tapping the info panel's top line folds it to one row. Upright, the
   time controls and the info panel sit above the list, within reach of a
   thumb, and the top bar keeps only the date and the way out. On its side
   the top bar keeps the time controls too, and the info panel stands at the
   right between the top bar and the list, scrolling inside when it runs
   longer. Either way everything stays clear of the screen's rounded corners
   and camera cut-out. */
.explore__fold { display: none; }
@media (max-width: 45em), (max-height: 35em) {
  .explore__list {
    top: auto; bottom: env(safe-area-inset-bottom); left: 0; right: 0; transform: none; margin: 0;
    flex-direction: row; flex-wrap: nowrap; align-items: baseline; gap: 0 2.2ch;
    max-height: none; overflow-x: auto; overflow-y: hidden;
    padding: 0 max(var(--gutter), env(safe-area-inset-right)) 0 max(var(--gutter), env(safe-area-inset-left)); scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--gutter), #000 calc(100% - var(--gutter)), transparent);
    mask-image: linear-gradient(90deg, transparent, #000 var(--gutter), #000 calc(100% - var(--gutter)), transparent);
  }
  .explore__list::-webkit-scrollbar { display: none; }
  .explore__list button { flex: none; padding: 0.75rem 0; white-space: nowrap; }
  .explore__list button::before, .explore__list button[aria-current="true"]::before { content: ""; }
  .explore__list .explore__sub::before, .explore__list .explore__sub[aria-current="true"]::before { content: "\00b7"; color: var(--ink-4); }
  .explore__list .explore__sub, .explore__list [data-in], .explore__list .explore__sub[data-in] { padding-left: 0; }
  .explore__list .explore__all, .explore__list .explore__group { margin-top: 0; }
  .explore__list .explore__group::before { content: "+"; }
  .explore__list .explore__group[aria-expanded="true"]::before { content: "-"; }
  .explore__hint { display: none; }
  .explore__note { margin-top: 0.375rem; }
  .explore__info .kv dt { width: 13ch; }
  .explore .explore__info .explore__fold { display: block; position: absolute; top: 0; left: 0; right: 0; height: var(--lh); z-index: 1; }
  .explore__info.is-folded .kv > :not(:first-child), .explore__info.is-folded .explore__note { display: none; }
}
@media (max-width: 45em) and (min-height: 35.0625em) {
  .explore__top { flex-wrap: nowrap; gap: 0 1.5ch; padding: 0.625rem var(--gutter); }
  .explore__name, .explore__close-text { display: none; }
  .explore__date { margin-left: 0; }
  .explore__time {
    position: fixed; left: 0; right: 0; bottom: calc(1.5rem + var(--lh) + env(safe-area-inset-bottom));
    justify-content: center; gap: 2ch;
  }
  .explore__time .btn { padding: 0.5rem 0.125rem; }
  .explore .explore__info { top: auto; bottom: calc(2.75rem + 2 * var(--lh) + env(safe-area-inset-bottom)); left: var(--gutter); right: var(--gutter); width: auto; }
}
@media (max-height: 35em) {
  .explore__top {
    flex-wrap: nowrap; gap: 0 2.5ch;
    padding: 0.625rem max(var(--gutter), env(safe-area-inset-right)) 0.625rem max(var(--gutter), env(safe-area-inset-left));
  }
  .explore__name, .explore__close-text { display: none; }
  .explore__date { margin-left: 0; }
  .explore .explore__info {
    top: calc(1.625rem + var(--lh)); bottom: auto; right: max(var(--gutter), env(safe-area-inset-right));
    width: min(46ch, 52vw); max-height: calc(100% - 3.375rem - 3 * var(--lh) - env(safe-area-inset-bottom));
    display: flex; flex-direction: column;
  }
  .explore__body { min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; }
  .explore__body::-webkit-scrollbar { display: none; }
}

/* ==========================================================================
   Status bar
   ========================================================================== */
.bar {
  position: fixed; inset: 0 0 auto 0; z-index: 10;
  height: var(--bar-h);
  display: flex; align-items: center; gap: 1.75rem;
  padding: 0 var(--gutter);
  background: linear-gradient(180deg, rgba(5, 6, 8, 0.94), rgba(5, 6, 8, 0.72));
  font-size: var(--fs-sm);
}
.bar::after {                       /* an ASCII hairline under the bar */
  content: "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
  position: absolute; left: 0; right: 0; bottom: calc(1px - 1em);
  height: 1em; line-height: 1; overflow: hidden; white-space: nowrap;
  color: var(--ink-4); letter-spacing: 0; pointer-events: none;
}
.bar__id { font-weight: 600; white-space: nowrap; }
.bar__nav { display: flex; gap: 1.375rem; margin-left: auto; }
.bar__nav a { color: var(--ink-2); transition: color 0.15s; }
.bar__nav a:hover, .bar__nav a.is-active { color: var(--ink); }
.bar__nav a.is-active .dim { color: var(--accent); }
.bar__clock { color: var(--ink-2); white-space: nowrap; font-variant-numeric: tabular-nums; }
.bar__lang { display: flex; align-items: center; gap: 0.6ch; white-space: nowrap; }
.bar__lang button { color: var(--ink-3); text-transform: lowercase; }
.bar__lang button:hover { color: var(--ink); }
.bar__lang button[aria-pressed="true"] { color: var(--accent); }
.bar__menu { display: none; color: var(--ink-2); }
.bar__explore { color: var(--accent); white-space: nowrap; }
.bar__explore:hover, .bar__explore:focus-visible { color: var(--ink); }
.bar__menu:hover { color: var(--ink); }

/* As the screen narrows the bar drops the clock, then the path, and then
   folds the section links into a menu. The widths are set for the Czech
   links, which are the longer ones. */
@media (max-width: 62em) { .bar__clock { display: none; } }
@media (max-width: 55.5em) { .bar__path { display: none; } }
@media (max-width: 50.5em) {
  .bar__menu { display: block; margin-left: auto; }
  .bar__nav {
    position: absolute; top: calc(var(--bar-h) + 0.625rem); right: var(--gutter);
    flex-direction: column; gap: 0; padding: 0.5rem 0;
    background: rgba(5, 6, 8, 0.96); border: 1px dashed var(--ink-4);
    display: none;
  }
  .bar__nav.is-open { display: flex; }
  .bar__nav a { padding: 0.375rem 1.125rem; }
}
@media (max-width: 45em) {
  .bar { gap: 0.75rem; }
  .bar__explore { display: none; }
}

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
  display: inline-block;
  padding: 0 0.125rem;
  color: var(--ink);
  white-space: nowrap;
  transition: background 0.12s, color 0.12s;
}
.btn:hover, .btn:focus-visible { background: var(--ink); color: var(--bg); }
.btn--solid { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.btn--solid:hover, .btn--solid:focus-visible { background: var(--ink); color: var(--bg); }
.btn--small { font-size: var(--fs-sm); color: var(--ink-2); }
.btn[aria-pressed="false"] { color: var(--ink-3); }

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: flex; align-items: center;
  padding: calc(var(--bar-h) + 2.5rem) var(--gutter) 6rem;
}
.hero__col { width: min(42.5rem, 100%); }

.banner { margin: 0 0 1.375rem; font-weight: 400; }
.banner__art {
  display: block;
  color: var(--ink);
  font-size: clamp(0.375rem, 0.7vw, 0.65625rem);
  line-height: 1.12;
  white-space: pre;
  text-shadow: 0 0 18px rgba(236, 234, 228, 0.18);
}
.banner__art--sm { display: none; font-size: min(2.28vw, 0.8125rem); line-height: 1.15; }
@media (max-width: 45em) {
  .banner__art--lg { display: none; }
  .banner__art--sm { display: block; }
}

.hero__role {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--fs-sm);
  color: var(--ink-2);
  margin-bottom: 0.875rem;
}
.hero__intro { color: var(--ink-2); max-width: 54ch; margin-bottom: 1.875rem; }
.hero__actions { display: flex; flex-wrap: wrap; gap: 0.875rem 1.375rem; margin-bottom: 1.375rem; }
.hero__explore { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.375rem 2ch; margin-bottom: 2.25rem; font-size: var(--fs-sm); }
.btn--explore { color: var(--accent); font-size: var(--fs); }
.btn--explore:hover, .btn--explore:focus-visible { background: var(--accent); color: var(--accent-ink); }
/* the note points at the button beside it, or above it on a narrow screen */
.hero__note::before { content: "<- "; content: "<- " / ""; }
@media (max-width: 35em) {
  .hero__note { flex-basis: 100%; }
  .hero__note::before { content: "^ "; content: "^ " / ""; }
}

.frame--telemetry { max-width: 56ch; font-size: var(--fs-sm); }

.hero__scroll {
  position: absolute; left: var(--gutter); bottom: 1.75rem;
  display: flex; flex-direction: column; align-items: flex-start;
  font-size: var(--fs-sm); line-height: 1rem; color: var(--ink-2);
}
.hero__arrow { animation: drift 2.4s ease-in-out infinite; color: var(--accent); }
@keyframes drift { 0%, 100% { transform: translateY(0); opacity: 0.4; } 50% { transform: translateY(5px); opacity: 1; } }

@media (max-aspect-ratio: 1/1) {
  .hero { align-items: flex-end; padding-top: 48vh; }
}

/* ==========================================================================
   Sections
   ========================================================================== */
.section {
  max-width: var(--max);
  margin: 0 auto;
  padding: calc(var(--lh) * 4) var(--gutter) calc(var(--lh) * 3);
}
.section, .hero { text-shadow: 0 0 8px var(--bg), 0 0 2px var(--bg); }

.rule {
  display: flex; align-items: baseline; gap: 1ch;
  text-transform: uppercase; letter-spacing: 0.08em;
  margin-bottom: calc(var(--lh) * 2);
  white-space: nowrap;
}
.rule__n { color: var(--accent); letter-spacing: 0; }
.rule__n::before { content: "["; color: var(--border); }
.rule__n::after { content: "]"; color: var(--border); }
.rule::after {
  content: "=================================================================================================================================================================================================================================";
  flex: 1; overflow: hidden; color: var(--ink-4); letter-spacing: 0; font-weight: 400;
}

/* Boxes side by side in a row take the height of the tallest one. */
.grid { display: grid; gap: calc(var(--lh) * 2) 5ch; align-items: stretch; }
.grid--about { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); }
.grid--work { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--stack { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--contact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 62.5em) {
  .grid--stack { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 53.75em) {
  .grid--about, .grid--work, .grid--stack, .grid--contact { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   ASCII frames. site.js draws each border in characters snapped to the
   line grid, into .frame__art. Without JavaScript a dashed border stands in.
   ========================================================================== */
.frame {
  position: relative;
  padding: var(--lh) 3ch;
  background: var(--panel);
  border: 1px dashed var(--ink-4);
  text-shadow: none;
}
/* A box on the page keeps a blank line inside its top and bottom borders.
   The explorer's boxes keep the tighter padding above, because they sit
   over the planets and have less room. */
main .frame { padding: calc(var(--lh) * 2) 4ch; }
@media (max-width: 45em) { main .frame { padding-left: 3ch; padding-right: 3ch; } }
.frame.is-drawn { border: 0; }
.frame__art {
  position: absolute; left: 0; top: 0;
  margin: 0;
  line-height: var(--lh);
  color: var(--border);
  white-space: pre;
  pointer-events: none;
  user-select: none;
  transition: color 0.2s;
}
.frame__art .t { color: var(--ink); }
.frame__art .m { color: var(--ink-2); }
.frame:hover .frame__art, .frame:focus-within .frame__art { color: var(--ink-2); }
.frame:hover .frame__art .t, .frame:focus-within .frame__art .t { color: var(--accent); }
.frame > :not(.frame__art, .sr-only) { position: relative; }
.frame p { color: var(--ink-2); }
.frame p strong { color: var(--ink); }

/* key ....... value lists */
.kv { margin: 0; }
.kv div { display: flex; gap: 1ch; }
.kv dt {
  flex: none; width: 16ch;
  overflow: hidden; white-space: nowrap;
  color: var(--ink-3);
  text-transform: uppercase;
}
.kv dt::after { content: " ......................................................"; color: var(--ink-4); }
/* on a phone the labels take less of the line, so the values wrap less */
@media (max-width: 45em) { .kv dt { width: 12ch; } }
.kv dd { margin: 0; color: var(--ink); min-width: 0; overflow-wrap: anywhere; }
.kv--tight dt { width: 12ch; }
.kv--links dt { width: 11ch; }
/* In a narrow box each label goes above its value, so values do not wrap.
   The widest row is the e-mail address with its copy button, which needs
   48 characters with the label in English and 53 in Czech. */
.kv--links { container-type: inline-size; }
@container (max-width: 48ch) {
  .kv--links div { flex-direction: column; gap: 0; }
  .kv--links dt { width: auto; }
  .kv--links dt::after { content: none; }
}
@container (max-width: 53ch) {
  html[lang="cs"] .kv--links div { flex-direction: column; gap: 0; }
  html[lang="cs"] .kv--links dt { width: auto; }
  html[lang="cs"] .kv--links dt::after { content: none; }
}
.kv a { border-bottom: 1px dashed var(--ink-3); }
.kv a:hover { color: var(--accent); border-color: var(--accent); }
.frame--telemetry .kv dd { font-variant-numeric: tabular-nums; }

.pulse { color: var(--accent); animation: pulse 1.6s steps(2, jump-none) infinite; }
@keyframes pulse { 50% { opacity: 0.2; } }

/* project cards: the links sit on the bottom line when a card is stretched */
.project { display: flex; flex-direction: column; }
.project__title {
  font-size: calc(var(--fs) * 1.7); line-height: calc(var(--lh) * 2);
  font-weight: 700; letter-spacing: 0.04em;
  color: var(--accent);
}
.project__status {
  margin-bottom: var(--lh);
  font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: 0.08em;
}
.frame .project__status { color: var(--ink-3); }
.frame .project__figures { color: var(--ink-3); font-size: var(--fs-sm); }
.project__title + .project__fig { margin-top: calc(var(--lh) / 2); }
.project .list { margin: 0 0 var(--lh); }
.project .list li + li { margin-top: calc(var(--lh) / 4); }

/* A figure drawn in characters. Each one is --cols characters wide, and on a
   screen too narrow for that its type gets smaller until it fits. */
.project__fig {
  margin: 0 0 var(--lh);
  padding: calc(var(--lh) / 2) 2ch;
  background: #0c0d10;     /* solid, so the planet does not show through */
  border-left: 1px solid var(--ink-4);
  container-type: inline-size;
}
.project__fig pre {
  color: var(--ink-2);
  font-size: min(var(--fs), 100cqw / (var(--cols, 44) * 0.6));
  line-height: min(var(--lh), 1.6em);
  overflow: hidden;
}
@media (max-width: 45em) {
  .project__fig { padding-left: 1ch; padding-right: 1ch; }
}
.project__fig .d { color: var(--ink-3); }
.project__fig .a { color: var(--accent); }
.project__fig b { color: var(--ink); font-weight: 400; }

/* the wide card puts its figure beside the text */
.project--wide {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 50ch);
  grid-template-rows: auto 1fr;
  grid-template-areas: "head fig" "body fig";
  column-gap: 4ch;
}
.project--wide .project__head { grid-area: head; margin-bottom: var(--lh); }
.project--wide .project__status { margin-bottom: 0; }
.project--wide .project__fig { grid-area: fig; align-self: start; margin: 0; }
.project--wide .project__body { grid-area: body; display: flex; flex-direction: column; }
@media (max-width: 53.75em) {
  .project--wide { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; grid-template-areas: "head" "fig" "body"; }
  .project--wide .project__fig { margin-bottom: var(--lh); }
}
.frame--wide { grid-column: 1 / -1; }
.frame code { color: var(--ink); }
.tags { list-style: none; margin: 0 0 var(--lh); padding: 0; display: flex; flex-wrap: wrap; gap: 0 2ch; color: var(--ink-3); }
.tags li::before { content: "#"; color: var(--ink-4); }
.project__links { display: flex; gap: 3ch; margin-top: auto; }
.project__links a { color: var(--ink); }
.project__links a:hover { color: var(--accent); }
.project__live::before { content: "> "; content: "> " / ""; }

/* skill bars (js renders the [####----] part to fit) */
.bars { list-style: none; margin: 0; padding: 0; max-width: 72ch; }
.bars li { display: flex; gap: 1ch; white-space: nowrap; color: var(--ink-2); }
.bars .bars__name { flex: none; width: 14ch; overflow: hidden; }
.bars .bars__bar { flex: 1; overflow: hidden; color: var(--border); }
.bars .bars__bar b { color: var(--ink); font-weight: 400; }
.bars .bars__val { flex: none; width: 7ch; text-align: right; color: var(--ink-3); }

/* skill lists */
.list { list-style: none; margin: 0; padding: 0; color: var(--ink-2); }
.list li { padding-left: 2ch; text-indent: -2ch; }
.list li::before { content: "+ "; color: var(--accent); }

/* other projects: name, what it is, link */
.more { list-style: none; margin: 0; padding: 0; }
.more li {
  display: grid;
  grid-template-columns: 20ch minmax(0, 1fr) auto;
  gap: 0 3ch;
  padding: var(--lh) 0 0;
}
.more li:first-child { padding-top: 0; }
.more__name { margin: 0; font-weight: 400; color: var(--ink); letter-spacing: 0.1em; }
.more__name::before { content: "> "; color: var(--accent); }
.more__body p { margin: 0; }
.frame .more__tags { color: var(--ink-3); font-size: var(--fs-sm); }
.more__link { white-space: nowrap; }
.more__link:hover { color: var(--accent); }
@media (max-width: 53.75em) {
  .more li { grid-template-columns: minmax(0, 1fr); }
}

/* log groups */
.sub {
  display: flex; align-items: baseline; gap: 1ch;
  margin: 0 0 var(--lh);
  font-size: var(--fs-sm); font-weight: 400; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-2);
}
.sub::before { content: "--"; color: var(--accent); }
.sub::after {
  content: "--------------------------------------------------------------------------------------------------------------------------------------------------------";
  flex: 1; overflow: hidden; white-space: nowrap; color: var(--ink-4);
}
.log + .sub { margin-top: calc(var(--lh) * 3); }

/* Copy button next to the e-mail address. On a screen too narrow for both
   it goes under the address, lined up with it. Both of its labels take the
   same place, so the button keeps its width when it changes to "copied". */
.kv dd:has(> .copy) { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 1ch; }
.copy { display: inline-grid; color: var(--ink-3); white-space: nowrap; text-align: left; }
.copy > span { grid-area: 1 / 1; }
.copy:hover { color: var(--accent); }
.copy .copy__done, .copy.done .copy__idle { visibility: hidden; }
.copy.done .copy__done { visibility: visible; color: var(--accent); }

/* flight log */
.log { list-style: none; margin: 0; padding: 0; }
.log__entry {
  display: grid;
  grid-template-columns: 16ch 5ch minmax(0, 1fr);
  padding-bottom: calc(var(--lh) * 2);
  position: relative;
}
.log__when { color: var(--ink-3); margin: 0; padding-right: 1ch; }
.log__entry::before {                         /* +-- connector */
  content: "+--";
  grid-column: 2; grid-row: 1;
  color: var(--accent);
}
.log__entry::after {                          /* | rail, clipped to the entry height */
  content: "|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A|\A";
  white-space: pre;
  position: absolute; left: 16ch; top: var(--lh); bottom: 0;
  overflow: hidden; color: var(--ink-4);
}
.log__entry:last-child { padding-bottom: 0; }
.log__entry:last-child::after { content: ""; }
.log__body { grid-column: 3; max-width: 70ch; }
.log__body h4 { margin: 0 0 0.125rem; }
.log__body p { color: var(--ink-2); }
.log__org { margin: 0; }
.log__points { list-style: none; margin: 0.375rem 0 0; padding: 0; color: var(--ink-2); }
.log__points li { padding-left: 2ch; text-indent: -2ch; }
.log__points li + li { margin-top: calc(var(--lh) / 4); }
.log__points li::before { content: "- "; color: var(--border); }
@media (max-width: 45em) {
  .log__entry { grid-template-columns: 4ch minmax(0, 1fr); }
  .log__when { grid-column: 2; grid-row: 1; }
  .log__entry::before { grid-column: 1; grid-row: 1; }
  .log__entry::after { left: 0; }
  .log__body { grid-column: 2; }
}

/* terminal: 16 lines tall, or as tall as the box beside it if that is
   taller. Its output scrolls inside it with the prompt kept in view below,
   so typing commands never changes the height of the page. */
.terminal { display: flex; flex-direction: column; min-height: calc(var(--lh) * 16); contain: size; }
.terminal__out {
  flex: 0 1 auto; min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-4) transparent;
}
.terminal__out p { margin: 0; color: var(--ink-2); white-space: pre-wrap; }
.terminal__out .cmd { color: var(--ink); }
.terminal__out .cmd::before { content: "$ "; color: var(--accent); }
.terminal__line { flex: none; display: flex; gap: 1ch; margin-top: var(--lh); }
.terminal__out:empty + .terminal__line { margin-top: 0; }
.terminal__line label { flex: none; }
.terminal__line input {
  flex: 1; min-width: 0;
  background: transparent; border: 0; padding: 0;
  font: inherit; color: var(--ink);
  caret-color: var(--accent);
  outline: none;
}

/* ==========================================================================
   Footer
   ========================================================================== */
.foot {
  max-width: var(--max);
  margin: calc(var(--lh) * 3) auto 0;
  padding: 0 var(--gutter) calc(var(--lh) * 2);
  font-size: var(--fs-sm); color: var(--ink-2);
  text-shadow: 0 0 8px var(--bg);
}
.foot__line { margin: 0 0 0.75rem; height: var(--lh); overflow: hidden; white-space: nowrap; color: var(--ink-4); }
.foot__line::before { content: "+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; }
.foot__row { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.75rem 1.5rem; }
.foot__row p { margin: 0; }
.foot__legal { white-space: nowrap; }
.foot__legal:hover, .foot__legal:focus-visible { color: var(--accent); }
.foot__ctrl { display: flex; gap: 2ch; }

/* ==========================================================================
   Reveal on scroll
   ========================================================================== */
.js .reveal { opacity: 0; transform: translateY(10px); transition: opacity 0.7s ease, transform 0.7s ease; }
.js .reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .js .reveal { opacity: 1; transform: none; }
}
