/* Minimal on purpose: system fonts, one accent, dark mode via media query.
   The tokens and the site chrome match nixpkgs-multiverse, so the two
   pages read as one family. */
:root {
  --bg: #ffffff;
  --fg: #1a1a1a;
  --muted: #6a6a6a;
  --line: #e0e0e0;
  --accent: #4f6bed;
  --code-bg: #f4f4f4;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #16181d;
    --fg: #e6e6e6;
    --muted: #9a9a9a;
    --line: #33363d;
    --accent: #8fa3ff;
    --code-bg: #22252c;
  }
}
* {
  box-sizing: border-box;
}
body {
  margin: 0 auto;
  padding: 1rem;
  max-width: 60rem;
  background: var(--bg);
  color: var(--fg);
  font:
    15px/1.5 system-ui,
    sans-serif;
}
h1 {
  font-size: 1.3rem;
  margin: 0.2rem 0;
}
h1 a {
  color: inherit;
  text-decoration: none;
}

/* The site header: the title on the left, the top-level destinations on the
   right. Wraps to two lines on a narrow phone rather than shrinking. */
header.site {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.3rem 1rem;
  margin: 0.2rem 0 0.6rem;
}
nav.site {
  margin: 0;
  border: 0;
  display: flex;
  gap: 1rem;
  font-size: 14px;
}
nav.site a {
  display: inline;
  padding: 0;
  border: 0;
  color: var(--accent);
  text-decoration: none;
}
nav.site a:hover {
  text-decoration: underline;
}
h2 {
  font-size: 1.1rem;
  margin: 1rem 0 0.3rem;
}
.muted {
  color: var(--muted);
}
a {
  color: var(--accent);
}

/* The intro's opening claim, carried by weight alone so it does not add
   a decoration to a paragraph that is already full of links. */
.lede {
  font-weight: 600;
}

/* The closing claim. A hairline rule in the muted grey, offset tight
   enough that the line box does not grow. Grey rather than the accent
   because every link in this paragraph is a solid accent underline, and
   an accent rule under body-coloured text reads as a link. */
.claim {
  text-decoration: underline var(--muted);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
code {
  font:
    13px/1.5 ui-monospace,
    monospace;
  background: var(--code-bg);
  border-radius: 3px;
  padding: 0.1rem 0.3rem;
}

/* The walk form: the input takes the measure, the button rides beside it. */
#walk-form {
  display: flex;
  gap: 0.5rem;
}
#walk-form input {
  flex: 1;
  padding: 0.5rem;
  font:
    13px/1.5 ui-monospace,
    monospace;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: 4px;
}
#walk-form button {
  padding: 0.5rem 1rem;
  font: inherit;
  color: var(--fg);
  background: var(--code-bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  cursor: pointer;
}
#status {
  margin: 0.5rem 0;
}

/* The closure table: store paths are long, so the path column gets the
   measure and the size columns hug their content on the right. */
#result table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
#result th,
#result td {
  text-align: left;
  padding: 0.25rem 0.6rem 0.25rem 0;
  border-bottom: 1px solid var(--line);
}
#result th.size,
#result td.size {
  text-align: right;
  white-space: nowrap;
}
#result td.path {
  font:
    12px/1.6 ui-monospace,
    monospace;
  word-break: break-all;
}

/* Example links under the intro. */
.examples a {
  white-space: nowrap;
}

/* The lane tabs: search, ranges, store path. */
#lanes {
  margin: 1rem 0 0.8rem;
  border-bottom: 1px solid var(--line);
}
#lanes a {
  display: inline-block;
  border-bottom: 2px solid transparent;
  padding: 0.4rem 0.8rem;
  font: inherit;
  color: var(--muted);
  text-decoration: none;
  cursor: pointer;
}
#lanes a.active {
  color: var(--fg);
  border-bottom-color: var(--accent);
}

#ranges-form,
#walk-form {
  display: flex;
  gap: 0.5rem;
}
#ranges-form input,
#walk-form input {
  flex: 1;
  padding: 0.5rem;
  font:
    13px/1.5 ui-monospace,
    monospace;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: 4px;
}
#ranges-form button,
#walk-form button {
  padding: 0.5rem 1rem;
  font: inherit;
  color: var(--fg);
  background: var(--code-bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  cursor: pointer;
}
/* Autocomplete under the range box: a vertical dropdown, the way
   grail's is — attributes while typing a name, versions after "@". */
#ranges-lane {
  position: relative;
}
#ranges-complete {
  position: absolute;
  z-index: 10;
  left: 0;
  right: 0;
  margin: 0.15rem 0 0;
  padding: 0;
  list-style: none;
  max-height: 16rem;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  box-shadow: 0 4px 14px rgb(0 0 0 / 12%);
}
#ranges-complete li {
  padding: 0.3rem 0.6rem;
  font:
    13px ui-monospace,
    monospace;
  cursor: pointer;
}
#ranges-complete li:hover,
#ranges-complete li.selected {
  background: var(--code-bg);
}

#caches-input {
  width: 100%;
  min-height: 7rem;
  resize: vertical;
  padding: 0.6rem;
  font:
    13px/1.5 ui-monospace,
    monospace;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: 4px;
}
#ranges-results {
  margin: 0.5rem 0;
  font-size: 14px;
}

/* The package picker: a search box, a list of matches, and the chosen
   builds as removable chips. */
#search {
  width: 100%;
  padding: 0.5rem;
  font: inherit;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: 4px;
}
#search-results {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0.5rem 0;
}
#search-results p {
  margin: 0.2rem 0;
  width: 100%;
}

/* An attribute row and a version pill are the same box. The attribute
   row is a single button that opens its version list; the version pill
   holds two targets, the number that links to the index and the pick
   beside it. */
#search-results .attr,
#search-results .pick {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
  padding: 0.3rem 0.6rem;
  font: inherit;
  color: var(--fg);
  background: var(--code-bg);
  border: 1px solid var(--line);
  border-radius: 4px;
}
#search-results .attr {
  cursor: pointer;
}
#search-results .attr:hover,
#search-results .pick:not(.dead):hover {
  border-color: var(--accent);
}

/* A version already in the selection. Clicking the pill again takes it
   back out, so the tint is the affordance for that: hover is a border
   alone, which leaves the two states apart. */
#search-results .pick.on {
  border-color: var(--accent);
  background: color-mix(in oklab, var(--accent) 12%, var(--code-bg));
}

/* The pick half of a version pill. The pill already draws the box, so
   what sits inside it is bare text. */
#search-results .take {
  padding: 0;
  font: inherit;
  color: var(--muted);
  background: none;
  border: none;
  cursor: pointer;
}
#search-results .take:disabled {
  cursor: default;
}
#search-results .name {
  font:
    13px ui-monospace,
    monospace;
}

/* A link out to nixmultiverse.com. A dotted underline is the whole
   mark it gets: these links run one per row down a list a hundred rows
   long, so anything louder (an icon, a solid rule) is a hundred things
   to look past. Hover is where the link states itself. */
.out {
  color: inherit;
  text-decoration: underline dotted;
  text-decoration-color: var(--muted);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.out:hover {
  color: var(--accent);
  text-decoration: underline solid;
  text-decoration-color: var(--accent);
  text-underline-offset: 3px;
}

/* A version with no store path for this system. The row is shown
   rather than dropped, so the list matches the version count beside
   the attribute. The number is struck through and still links out,
   since the index is where that version's history is written out. */
#search-results .pick.dead {
  opacity: 0.6;
  border-style: dashed;
}
#search-results .pick.dead .name {
  text-decoration-line: underline line-through;
  text-decoration-style: dotted;
}

/* The reasons, said once under the list rather than on every row. */
#search-results .note {
  max-width: 42rem;
  font-size: 13px;
}
#selection {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0.5rem 0;
}

/* A chip carries the same two targets as a version pill: the label
   links to the index, the x removes the selection. */
#selection .chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.45rem;
  padding: 0.25rem 0.6rem;
  font:
    13px ui-monospace,
    monospace;
  color: var(--bg);
  background: var(--accent);
  border: none;
  border-radius: 999px;
}
#selection .chip .out {
  text-decoration-color: var(--bg);
}
#selection .chip .out:hover {
  color: var(--bg);
  text-decoration-color: var(--bg);
}
#selection .drop {
  padding: 0;
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

/* A selected path no configured cache holds. The boot is still
   offered, because a cache pasted into the caches lane may hold what
   cache.nixos.org has dropped, so this is a warning rather than a
   removal. It carries the accent's outline instead of its fill. */
#selection .chip.missing {
  color: var(--fg);
  background: var(--code-bg);
  border: 1px dashed var(--muted);
}
#selection .chip.missing .out {
  text-decoration-color: var(--muted);
}
#selection .chip.missing .out:hover {
  color: var(--accent);
}
#advanced {
  margin: 0.6rem 0;
}
#advanced summary {
  cursor: pointer;
  font-size: 13px;
}
.actions {
  display: flex;
  align-items: center;
  gap: 1rem;
}
#boot-button {
  padding: 0.5rem 1.4rem;
  font: inherit;
  color: var(--fg);
  background: var(--code-bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  cursor: pointer;
}
#boot-button:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Boot progress: one row per stage — label, bar, byte count. On a
   narrow screen the label and the count share a line and the bar gets
   the whole next one; the fixed columns left it a sliver on a phone. */
.progress-row {
  display: grid;
  grid-template-columns: 10rem 1fr 12rem;
  gap: 0.8rem;
  align-items: center;
  margin: 0.35rem 0;
  font-size: 14px;
}
@media (max-width: 40rem) {
  .progress-row {
    grid-template-columns: 1fr auto;
    gap: 0.3rem 0.8rem;
    margin: 0.5rem 0;
  }
  .progress-row .bar {
    grid-column: 1 / -1;
  }
}
.progress-row .bar {
  height: 0.5rem;
  background: var(--code-bg);
  border-radius: 4px;
  overflow: hidden;
}
.progress-row .fill {
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width 0.15s linear;
}
.progress-row.done .fill {
  background: #3fa34d;
}
.progress-row.failed .fill {
  background: #c94f4f;
}
.progress-row .bytes {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The console: a title bar, the terminal, and the veil over it while
   the guest is still being brought up — what happens in between is
   plumbing, not output.

   Everything is painted with --console-bg, which the terminal sets to
   the background of its own palette (site/js/terminal.js). The grid of
   cells never divides the box evenly, and the strip left over at the
   right and the bottom is only invisible if the frame is the same
   colour as the cells. The frame stays dark in both site themes. */
:root {
  --console-bg: #16181d;
  --console-bar: #0f1013;
  --console-fg: #9a9a9a;
}
#console {
  margin-top: 0.8rem;
  border-radius: 6px;
  overflow: hidden;
  background: var(--console-bg);
}
#console-bar {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.35rem 0.75rem;
  background: var(--console-bar);
  color: var(--console-fg);
  font:
    12px/1.5 ui-monospace,
    monospace;
}
#console-body {
  position: relative;
}
#console-veil {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  background: var(--console-bg);
  color: var(--console-fg);
  font-size: 14px;
}
#console-veil[hidden] {
  display: none;
}
.spinner {
  width: 1rem;
  height: 1rem;
  border: 2px solid #444;
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: none;
  }
}

/* The key bar under the console: Esc, Tab, Ctrl, Alt, arrows — what a
   phone's keyboard lacks. Only a coarse pointer gets it; a keyboard
   has all of these. It scrolls sideways rather than wrapping, so it
   costs one row. An armed modifier is lit until the next key uses it. */
#keybar {
  display: none;
  gap: 0.35rem;
  padding: 0.4rem 0.6rem;
  overflow-x: auto;
  background: var(--console-bar);
  scrollbar-width: none;
}
@media (pointer: coarse) {
  #keybar {
    display: flex;
  }
}
#keybar button {
  flex: 0 0 auto;
  min-width: 2.6rem;
  padding: 0.45rem 0.6rem;
  font:
    13px ui-monospace,
    monospace;
  color: var(--console-fg);
  background: var(--console-bg);
  border: 1px solid #2a2d35;
  border-radius: 4px;
  /* A touch that starts on a key may drag the bar sideways, and must
     not pan the page: that is what scrolled the terminal away. */
  touch-action: pan-x;
}
#keybar button.armed {
  color: var(--console-bg);
  background: var(--accent);
  border-color: var(--accent);
}

/* The terminal itself: a fixed height, so the guest's rows are known
   before it says anything, and no padding on the right, where the
   leftover strip already is. */
#terminal {
  height: 30rem;
  padding: 0.5rem 0 0.5rem 0.6rem;
  background: var(--console-bg);
}

/* On a touch device the terminal's height is set from the visual
   viewport by site/js/terminal.js, so the key bar stays on screen
   above the soft keyboard; this is only the starting point. */
@media (pointer: coarse) {
  #terminal {
    height: max(12rem, calc(100dvh - 7rem));
  }
}

/* The debug log: closed until something goes wrong and the reader
   opens it. */
#debug {
  margin: 1rem 0;
}
#debug summary {
  cursor: pointer;
  font-size: 13px;
}
.debug-actions {
  margin: 0.4rem 0 0;
  font-size: 13px;
}
#copy-report {
  margin-right: 0.4rem;
  padding: 0.25rem 0.7rem;
  font: inherit;
  font-size: 13px;
  color: var(--fg);
  background: var(--code-bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  cursor: pointer;
}
#debug pre {
  max-height: 18rem;
  overflow: auto;
  margin: 0.4rem 0 0;
  padding: 0.6rem;
  background: var(--code-bg);
  border-radius: 4px;
  font:
    12px/1.5 ui-monospace,
    monospace;
  white-space: pre-wrap;
  word-break: break-all;
}

footer {
  margin-top: 2rem;
  padding: 0.8rem 0;
  border-top: 1px solid var(--line);
  font-size: 13px;
  text-align: center;
}
/* The second footer line: a store path is a single 68-character token —
   broken across lines it is unreadable and awkward to select. Hold it on
   one line and let the font shrink instead. */
#store {
  margin-top: 0.3rem;
}
#store code {
  font-size: min(12px, 2.1vw);
  white-space: nowrap;
  background: none;
  padding: 0;
}
