/* One stylesheet, no framework, no web fonts. Nothing here is loaded from
   anywhere else: the pages have to work on a machine with no internet access
   beyond the crawler's own. */

:root {
  --bg: #fbfaf8;
  --fg: #1d1c1a;
  --muted: #6b6660;
  --line: #ddd8d0;
  --card: #ffffff;
  --accent: #2f5d50;
  --accent-fg: #ffffff;
  --tint: #f1efeb;
  --accent-soft: #e6efe9;
  --warn-bg: #fdf3e7;
  --warn-line: #e0b475;
  --danger: #8f3230;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #17181a;
    --fg: #e8e6e2;
    --muted: #9d9891;
    --line: #33353a;
    --card: #1e2023;
    --accent: #74b3a0;
    --accent-fg: #10201c;
    --tint: #23252a;
    --accent-soft: #21302b;
    --warn-bg: #2c2418;
    --warn-line: #6d5325;
    --danger: #e08b86;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 16px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* The page is the viewport at minimum, so the footer sits at the bottom of
     the screen even when the content is short; main grows to take the slack.
     `dvh` rather than `vh`: on a phone the address bar can be showing, and
     `vh` measures the taller, bar-hidden viewport, which pushes the footer
     below the fold on an otherwise short page until the reader scrolls. The
     `vh` line is the fallback for a browser without `dvh`. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

main {
  max-width: 46rem;
  /* `margin: 0 auto` stops a flex item stretching, so the column would size
     main to its content instead of the viewport. A page whose content has a
     wide min-content -- the nowrap tables on /watching -- then forces the whole
     document to scroll sideways. An explicit width keeps main at the viewport
     and lets each table scroll inside its own `.strip`. */
  width: 100%;
  margin: 0 auto;
  padding: 1.5rem 1rem 4rem;
  flex: 1;
}

.bar {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
  max-width: 46rem;
  margin: 0 auto;
  padding: 1rem;
  border-bottom: 1px solid var(--line);
}

/* The header stays put while the page scrolls, so the nav is always one tap
   away. It is opaque on purpose: the bar is narrower than the viewport, and a
   translucent one would leak the content behind it at the edges. */
header.bar {
  position: sticky;
  top: 0;
  z-index: 4;
  background: var(--bg);
}

/* Brand and nav sit together at the right, leaving the wordmark its own corner. */
header.bar nav, header.bar .nav-menu { margin-left: auto; }

footer.bar {
  border-bottom: 0;
  border-top: 1px solid var(--line);
  font-size: 0.85rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-decoration: none;
  color: var(--fg);
}

/* A small magnifier, because "lupe" is one. Drawn, not loaded: the page has to
   work with no internet beyond the crawler's own. */
.brand-mark {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
}

nav { display: flex; gap: 0.35rem; align-items: center; }

/* Nav links read as quiet text until touched: a soft wash marks the pointer,
   and the current page keeps an accent wash and weight so the reader can see
   where they are. */
nav a {
  color: var(--muted);
  text-decoration: none;
  padding: 0.32rem 0.6rem;
  border-radius: 6px;
  font-size: 0.95rem;
  white-space: nowrap;
}
nav a:hover { color: var(--fg); background: var(--tint); }
nav a[aria-current="page"] {
  color: var(--accent);
  background: var(--accent-soft);
  font-weight: 600;
}

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

h1 { font-size: 1.5rem; line-height: 1.25; margin: 1.5rem 0 0.75rem; }
h2 { font-size: 1.1rem; margin: 0 0 0.5rem; }
h3 { font-size: 1rem; margin: 0 0 0.35rem; }
.help-heading { margin-top: 1.25rem; }

.lede { color: var(--muted); }
.muted { color: var(--muted); }
.small { font-size: 0.85rem; }

blockquote {
  margin: 1rem 0;
  padding: 0.6rem 0.9rem;
  border-left: 3px solid var(--line);
  color: var(--muted);
  font-style: italic;
}

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 1rem 1.1rem;
  margin: 1.25rem 0;
}

.card.danger { border-color: var(--danger); }

.warn {
  background: var(--warn-bg);
  border: 1px solid var(--warn-line);
  border-radius: 8px;
  padding: 0.75rem 0.9rem;
}

label { display: block; font-weight: 600; margin-bottom: 0.35rem; }

input[type="text"],
input[type="password"],
textarea {
  width: 100%;
  padding: 0.55rem 0.65rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
}

textarea { resize: vertical; }

/* The scannable invoice. The inline SVG carries its own white ground and black
   modules, so the dark scheme must not invert it; this only frames and caps it.
   No fixed pixel width either way: the code is a vector and downscales cleanly. */
.qr { margin: 0.75rem 0; }
.qr a {
  display: inline-block;
  line-height: 0;
  padding: 0.4rem;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
}
.qr svg { display: block; max-width: 14rem; height: auto; }

/* Copying the invoice is a convenience of script; without it the code and the
   wallet link still pay. The button stays out of the layout until site.js marks
   the document, and the invoice sits in a data attribute rather than an
   attribute the script rewrites, so a poll never sees the fragment change. */
[data-copy-invoice] { display: none; }
.js [data-copy-invoice] { display: inline-block; }

.secret-field { position: relative; }

.secret-field input[type="password"],
.secret-field input[type="text"] { padding-right: 3rem; }

.secret-field button[data-reveal] {
  position: absolute;
  top: 50%;
  right: 0.3rem;
  transform: translateY(-50%);
  width: 2.4rem;
  height: 2.4rem;
  padding: 0;
  display: inline-grid;
  place-items: center;
  border: 0;
  background: none;
  color: var(--muted);
}

.secret-field button[data-reveal]:hover { color: var(--fg); }

.secret-field button[data-reveal] svg { width: 1.3rem; height: 1.3rem; display: block; }

.secret-field button[data-reveal] .icon-eye-off { display: none; }

.secret-field button[data-reveal][aria-pressed="true"] .icon-eye { display: none; }
.secret-field button[data-reveal][aria-pressed="true"] .icon-eye-off { display: block; }

button, .button {
  display: inline-block;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--card);
  color: var(--fg);
  font: inherit;
  cursor: pointer;
  text-decoration: none;
}

button.primary, .button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-fg);
  font-weight: 600;
}

button.danger { border-color: var(--danger); color: var(--danger); }

button.linkish {
  border: 0;
  background: none;
  padding: 0;
  color: var(--accent);
  text-decoration: underline;
}

form.inline { display: inline; }
.row { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }

table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
th, td { text-align: left; padding: 0.5rem 0.4rem; border-bottom: 1px solid var(--line); }
th { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
td.num, th.num { text-align: right; }
td.actions { text-align: right; }

/* The label column is sized to its own longest label rather than a fixed
   width. A fixed 9rem held the English labels, but the German
   "Benachrichtigungen" is a shade wider, and the inherited
   `overflow-wrap: anywhere` then split the word before its last letter. A
   max-content track keeps each language's labels whole and leaves the value
   column the rest; below 36rem the pair collapses to one column anyway. */
dl { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 0.3rem 1rem; margin: 0; }
dt { color: var(--muted); overflow-wrap: normal; }
dd { margin: 0; }

ul.plain, ul.results { list-style: none; padding: 0; margin: 1rem 0; }

.result {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  padding: 0.9rem 1rem;
  margin-bottom: 0.75rem;
}

.facts { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 0.5rem; }

.fact {
  font-size: 0.85rem;
  padding: 0.1rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
}

.fact.tag { border-color: var(--accent); color: var(--accent); }

.choice { font-weight: 400; margin-bottom: 0.4rem; }
.choice input { margin-right: 0.5rem; }

.secret + button { margin-top: 0.75rem; }

.secret {
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 1.05rem;
  word-break: break-all;
  background: var(--card);
  border: 1px solid var(--accent);
  border-radius: 8px;
  padding: 0.9rem 1rem;
  user-select: all;
}

.spinner { display: flex; align-items: center; gap: 0.7rem; color: var(--muted); }

.spin {
  width: 1.1rem;
  height: 1.1rem;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: turn 0.9s linear infinite;
}

@keyframes turn { to { transform: rotate(360deg); } }

/* Somebody who has asked for less motion gets a mark that does not move. The
   page still reloads on its own; only the decoration stops. */
@media (prefers-reduced-motion: reduce) {
  .spin { animation: none; border-top-color: var(--line); }
}

/* The evidence strip on /watching: seven narrow columns of numbers that must
   not squeeze the page down, and must not make it wider than a phone either.
   The wrapper scrolls sideways; the page itself never does. */
.strip { overflow-x: auto; }
.strip table { font-size: 0.85rem; }
.strip th, .strip td { white-space: nowrap; }

/* A crawl's state, as a word rather than a colour. The colour is on top of the
   word and never instead of it: a badge only a colour distinguishes says
   nothing at all to a reader who cannot see it. */
.state { font-weight: 600; }
.state.bad { color: var(--danger); }

/* A narrow screen must retain every account action. Form controls without an
   explicit type are text inputs too; attribute selectors alone miss them. */
nav { flex-wrap: wrap; min-width: 0; max-width: 100%; }
.bar > * { min-width: 0; }
main, .card, .result, dd, blockquote { overflow-wrap: anywhere; }
input:not([type]), input[type="date"], select {
  width: 100%; min-width: 0; max-width: 100%;
  padding: 0.55rem 0.65rem; border: 1px solid var(--line);
  border-radius: 6px; background: var(--bg); color: var(--fg); font: inherit;
}
input, textarea, select { max-width: 100%; }
input[type="date"] { min-height: 2.8rem; }
button, .button { max-width: 100%; overflow-wrap: anywhere; }
form > button { margin-top: 0.5rem; }
nav a, nav .linkish { padding: 0.32rem 0.6rem; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.skip { position: absolute; left: 1rem; top: -8rem; z-index: 10; padding: 0.7rem; background: var(--card); }
.skip:focus { top: 0.5rem; }
/* The main navigation folds into one button on a narrow screen, where a row of
   links cannot sit beside the wordmark and the language picker and would
   instead wrap over several lines and thicken the bar. The drawer is a
   disclosure -- its summary is the button -- so it opens with no script. The
   markup ships it open, so a reader without script keeps every link; the
   script only closes it when the width leaves no room, and opens it again
   when room returns. */
.nav-menu > summary {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.28rem 0.6rem; border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg); color: var(--fg); font-size: 0.9rem; cursor: pointer;
  list-style: none;
}
.nav-menu > summary::-webkit-details-marker { display: none; }
.menu-mark {
  width: 1.1em; height: 1.1em; flex: none; fill: none;
  stroke: currentColor; stroke-width: 2; stroke-linecap: round;
}
.nav-menu[open] > summary { border-color: var(--accent); }
@media (max-width: 40rem) {
  /* Shut, the drawer keeps its links out of the bar: the markup ships it shut
     so a narrow screen never paints them open before the deferred script has
     chosen the state. */
  .nav-drawer { display: none; }
  .nav-menu[open] + .nav-drawer { display: flex; }
}
/* Just below the fold the open drawer's links leave the top line: the Menu
   button and the language picker keep their places there, and the links drop
   as a panel beneath the bar. Left in place the open drawer would grow past
   the room beside them, wrapping both onto lines of their own and thickening
   the bar. The panel is scoped just under the 40rem fold so the desktop
   layout, whose links live on a full-width row of their own, is untouched. */
@media (max-width: 39.99rem) {
  .nav-menu[open] + .nav-drawer {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    padding: 0.4rem 1rem 0.6rem;
    background: var(--bg);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.16);
  }
}
@media (min-width: 40rem) {
  /* On the wide bar the button and its disclosure disappear and the links are
     always on show, whether or not that disclosure is open, so a reader
     without script keeps the full row and screen readers keep the landmark. */
  .nav-menu { display: none; }
  .nav-drawer { display: flex; }
  /* The wordmark and the language picker keep the top line, and the navigation
     takes a line of its own below them. All three on one row cannot fit the
     reading column once the German labels are in play, and a wrap put a lone
     item on each line. A deliberate two-tier bar reads the same at every
     desktop width instead. */
  header.bar .brand { order: 1; }
  header.bar .language { order: 2; }
  header.bar .nav-drawer { order: 3; flex: 1 0 100%; margin-left: 0; }
  /* Full-width links sit a shade tighter so the longest set -- the German
     labels -- still lands on one line inside the reading column. */
  header.bar .nav-drawer a { padding-left: 0.5rem; padding-right: 0.5rem; }
}
/* The language picker: one compact control in the header rather than a row of
   buttons that wraps and pushes the bar wider. The menu is a disclosure, so it
   needs no script to open; the buttons inside stay ordinary form submitters. */
.language { position: relative; }
.language > summary {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.25rem 0.55rem; border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg); color: var(--fg); font-size: 0.9rem; cursor: pointer;
}
.language > summary::-webkit-details-marker { display: none; }
.language > summary::after { content: "▾"; font-size: 0.7rem; color: var(--muted); }
.language[open] > summary { border-color: var(--accent); }
.language .flag { font-size: 1rem; line-height: 1; }
.language-menu {
  position: absolute; right: 0; top: calc(100% + 0.35rem); z-index: 3;
  display: grid; gap: 0.1rem; min-width: 9rem; padding: 0.3rem;
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
}
.language-menu button {
  display: flex; align-items: center; gap: 0.5rem; width: 100%;
  margin: 0; padding: 0.4rem 0.5rem; border: 0; border-radius: 6px;
  background: transparent; color: var(--fg); text-align: left;
  font-size: 0.9rem; cursor: pointer;
}
.language-menu button:hover, .language-menu button:focus-visible { background: var(--bg); }
.language-menu [aria-current="true"] { font-weight: 700; }
[hidden] { display: none !important; }
summary { cursor: pointer; padding: 0.5rem 0; }
.search-list p { margin: 0.5rem 0; }
@media (max-width: 36rem) {
  nav { flex-basis: 100%; gap: 0.5rem 1rem; }
  dl { grid-template-columns: minmax(0, 1fr); }
  dd { margin-bottom: 0.5rem; }
}
