/* thebtc.network - no external requests. The two webfonts are served from this
 * origin, from site/fonts/, so the footer's claim still holds: nothing is
 * fetched from a CDN or a font host. Both are SIL OFL, licences alongside them.
 *
 * Warm dark instrument panel. The page is a read-out, so measured values carry
 * the visual weight and everything else sits back.
 *
 * Orange is deliberately scarce. This page is mostly numerals, and saturated
 * orange at scale destroys their legibility, so the accent is spent only on
 * section labels, the two headline figures, the active window in the block
 * interval selector, and links. Never as a large fill.
 *
 * Contrast, measured against the three grounds (bg / surface / surface-2):
 *   text     #F3EDE6   16.15 / 15.12 / 13.82   AAA
 *   text-dim #A99B8C    6.93 /  6.49 /  5.93   AA
 *   accent   #F7931A    8.18 /  7.65 /  7.00   AAA, AA on surface-2
 *   ipv4     #58A6FF    7.43 /  6.96 /  6.36   AA+
 *   ipv6     #4FD1C5   10.07 /  9.42 /  8.61   AAA
 *   tor      #A87FD1    5.92 /  5.54 /  5.07   AA
 *   #15110E on accent fill: 8.18
 * accent-dim reaches only 4.07-4.76, so it is used for borders and rules and
 * never for text. --border is a separator at 1.27 and cannot identify a
 * control, so interactive elements use --border-control at ~3.0 instead.
 */
:root {
  color-scheme: dark;

  --bg:          #15110E;
  --surface:     #1E1813;
  --surface-2:   #272019;
  --field:       #120F0C;

  --border:      #342B22;   /* separators only */
  --border-control: #736152; /* interactive outlines, ~3.0 contrast */

  --text:        #F3EDE6;
  --text-dim:    #A99B8C;
  --placeholder: #8A7B6B;

  --accent:      #F7931A;
  --accent-dim:  #B86F14;
  --accent-bg:   rgba(247, 147, 26, .10);

  --ok:          #3FB950;
  --warn:        #D29922;
  --warn-bg:     rgba(210, 153, 34, .10);

  /* Categorical, for the network breakdown. Identity, not judgement. */
  --net-ipv4:    #58A6FF;
  --net-ipv6:    #4FD1C5;
  --net-tor:     #A87FD1;

  --mono: "JetBrains Mono Variable", ui-monospace, SFMono-Regular, "SF Mono",
          Menlo, Consolas, "Liberation Mono", monospace;
  --sans: "Inter Variable", system-ui, -apple-system, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;

  --radius: 10px;
}

/* Latin subsets, variable weight axis, served from this origin. The system
   stack stays behind them in --sans/--mono, so the page is fully legible
   before these arrive and for any codepoint outside the range below. */
@font-face {
  font-family: "Inter Variable";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/fonts/inter-latin-var.woff2") format("woff2");
  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+2074,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono Variable";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/fonts/jetbrains-mono-latin-var.woff2") format("woff2");
  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+2074,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  /* A single warm pool behind the masthead, as if lit by the banner itself.
     Very low alpha: ambience, not an orange fill. */
  background-image:
    radial-gradient(1000px 460px at 50% -140px, rgba(247,147,26,.07), transparent 70%);
  background-repeat: no-repeat;
  color: var(--text-dim);
  font: 16px/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--accent-dim); color: #fff; }

/* Motion. Every transition here is on a state the reader just caused, so it is
   short enough not to be waited on. Declared only under no-preference: the
   reduce case gets no transitions at all rather than shortened ones. */
@media (prefers-reduced-motion: no-preference) {
  a, button, summary, input[type=text],
  .tablist button[role="tab"], .win, .cell .histbtn, .dlg-close,
  #t-nodes td .addr, #t-nodes td .addr .ic, tbody tr td {
    transition: color .14s ease, background-color .14s ease,
                border-color .14s ease, box-shadow .14s ease,
                opacity .14s ease, filter .14s ease;
  }
  dialog#hashdlg[open] { animation: dlg-in .17s cubic-bezier(.2, .7, .3, 1); }
  @keyframes dlg-in {
    from { opacity: 0; transform: translateY(6px) scale(.994); }
  }
  dialog#hashdlg[open]::backdrop { animation: fade-in .17s ease; }
  @keyframes fade-in { from { opacity: 0; } }
}

:where(a, button, summary, input):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.wrap { max-width: 1060px; margin: 0 auto; padding: 0 22px; }

/* ------------------------------------------------------------- masthead */

header { padding: 44px 0 10px; text-align: center; }
header picture { display: block; }

/* The banner ships with its studio background removed, so the shadow it casts
   is drawn here and follows the pill silhouette rather than a rectangle.
   aspect-ratio reserves the space before the image loads. */
.banner {
  display: block;
  width: min(620px, 100%);
  height: auto;
  aspect-ratio: 1400 / 532;
  margin: 0 auto 22px;
  filter: drop-shadow(0 16px 22px rgba(0,0,0,.55))
          drop-shadow(0 3px 5px rgba(0,0,0,.45));
}

/* The banner is the heading. There is no heading text, so the h1 only has to
   avoid contributing space of its own; .banner carries the spacing. */
h1 { margin: 0; font-size: inherit; font-weight: inherit; }

.lede {
  color: var(--text-dim);
  max-width: 62ch;
  margin: 0 auto 8px;
  font-size: .95rem;
}

/* ------------------------------------------------------------------ tabs */

.tablist {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin: 34px 0 0; padding: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
}

.tablist button[role="tab"] {
  flex: 1 1 auto;
  padding: 11px 20px;
  font: 650 .76rem/1 var(--sans);
  text-transform: uppercase; letter-spacing: .11em;
  color: var(--text-dim); background: transparent;
  border: 1px solid transparent; border-radius: 8px;
  cursor: pointer; filter: none;
}
.tablist button[role="tab"]:hover {
  color: var(--text); background: var(--surface-2); filter: none;
}
/* Selected reads as raised and underlined rather than filled: a solid orange
   tab three times over would be exactly the large fill the palette forbids. */
.tablist button[role="tab"][aria-selected="true"] {
  color: var(--text); background: var(--surface-2);
  border-color: var(--border-control);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.tablist:not([hidden]) ~ section > .sechead > h2 { color: var(--text-dim); }

/* -------------------------------------------------------------- sections */

section { margin: 44px 0; }

/* Chain subsections. Stacked and always present rather than tabbed: each keeps
   its own timestamp in view, each is linkable, and nothing worth reading sits
   behind a click.

   The grouping is done by proximity and nothing else -- no panel, no box. A
   header sits far from whatever precedes it and close to what it labels, so it
   reads as belonging downward. The two numbers are the whole mechanism, and
   the ratio between them matters more than either value. */
.subsec { margin: 3.5rem 0 0; }
/* The anchor row is already the top of the content and supplies its own
   separation, so the first section does not need the full gap above it. */
.subsec:first-of-type { margin-top: 1.6rem; }
/* So that landing on /#issuance does not park the heading under the sticky
   bar above it. */
.subsec { scroll-margin-top: 5.5rem; }

.subsec > .sechead {
  /* Close to its own tiles: a quarter of the space above it. */
  margin-bottom: 1rem;
  padding-bottom: 0;
  /* No rule. With the size, weight and tick below doing the work, a hairline
     under every header added a fifth horizontal line to a page that already
     has tile borders, and made each section read as a closed box rather than
     as a heading with things under it. */
  border-bottom: 0;
}

/* Sized and weighted to sit above the tiles rather than beside them. Text
   colour, not accent: three orange headers would compete with the figures,
   which are the only things on this page allowed to be loud. */
.subsec > .sechead > h3 {
  position: relative;
  margin: 0;
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--text);
  text-transform: uppercase;
  letter-spacing: .1em;
  line-height: 1.25;
}

/* A tick, not a headline. Three pixels of accent in the gutter is enough to
   make the sections scannable at a glance; colouring the words would not be. */
.subsec > .sechead > h3::before {
  content: '';
  position: absolute;
  left: calc(-0.75rem - 3px);
  top: .16em;
  width: 3px;
  height: 1em;
  border-radius: 1px;
  background: var(--accent);
}

/* Present, not competing. */
.subsec > .sechead > .stamp { font-size: .8rem; color: var(--text-dim); }

/* A table of contents that reads like the tab row above it but behaves like
   links: the sections it points at stay in the document and stay scrollable.
   Deliberately not orange -- this is section chrome, and the accent is
   reserved for figures. */
.secnav {
  position: sticky; top: 0; z-index: 5;
  display: flex; flex-wrap: wrap; gap: 4px;
  margin: 0 0 6px; padding: 9px 0 8px;
  /* Opaque, or the grid scrolls through it. */
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
/* Deliberately unlike a section header now that headers are large uppercase
   text: these are sentence case, small, and pill shaped, so they read as
   things to press rather than labels for what follows. The outline is the
   interactive one used elsewhere on this page for controls. */
.secnav a {
  padding: 6px 13px;
  font: 550 .8rem/1.35 var(--sans);
  letter-spacing: .01em;
  color: var(--text-dim); text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--border-control); border-radius: 999px;
  transition: color .12s, background-color .12s, border-color .12s;
}
@media (prefers-reduced-motion: reduce) { .secnav a { transition: none; } }
.secnav a:hover {
  color: var(--text); background: var(--surface-2);
  border-color: var(--text-dim);
}
.secnav a:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
/* Whichever section the reader is currently in. Filled rather than outlined,
   so the current one is distinguishable without adding a colour. */
.secnav a.on {
  color: var(--text); background: var(--surface-2);
  border-color: var(--text-dim);
  box-shadow: inset 2px 0 0 var(--accent);
}

.sechead {
  display: flex; flex-wrap: wrap; gap: 4px 18px;
  align-items: baseline; justify-content: space-between;
  border-bottom: 1px solid var(--border);
  padding-bottom: 10px;
  margin-bottom: 18px;
}

h2 {
  font-size: .82rem;
  margin: 0;
  text-transform: uppercase;
  letter-spacing: .16em;
  font-weight: 650;
  color: var(--accent);
}

h3 {
  font-size: .72rem; margin: 0 0 10px;
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--text-dim); font-weight: 650;
}

/* Timestamps carry a status dot. This reports whether the page's own data is
   current -- a fact about the collection pipeline, not a verdict on the
   network, which is never coloured. */
.stamp {
  margin: 0;
  font: .78rem/1.5 var(--mono);
  color: var(--text-dim);
  display: inline-flex; align-items: center; gap: 8px;
}
.stamp::before {
  content: ''; flex: none;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--ok);
  box-shadow: 0 0 0 3px rgba(63, 185, 80, .12);
}
.stamp.stale { color: var(--warn); font-weight: 600; }
.stamp.stale::before {
  background: var(--warn);
  box-shadow: 0 0 0 3px var(--warn-bg);
  animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .stamp.stale::before { animation: none; }
}

.note {
  color: var(--text-dim); font-size: .89rem;
  max-width: 76ch; margin: 0 0 16px;
}
.small { font-size: .84rem; color: var(--text-dim); }

a { color: var(--accent); text-decoration-color: var(--accent-dim); }
a:hover { text-decoration-thickness: 2px; }

/* ------------------------------------------------------------ stat cells */

.grid {
  display: grid; gap: 12px;
  /* The floor is in rem, not px, and that is the whole point. It used to be
     205px while the value inside is sized in rem, so the column stayed put
     while the text grew: at a browser font size of 22px the issuance totals
     ran past the cell and were cut off by .cell's overflow. Raising the font
     size is an ordinary accommodation and it was quietly destroying data.
     13rem is what the widest value needs (about 14 monospace characters at
     1.28rem, plus the cell padding); 14rem carries headroom for a longer one.
     At a 16px root this is 224px against the old 205px, so the default layout
     is unchanged -- it only diverges once the reader has asked for bigger
     text, which is exactly when it should. */
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
}

/* Tip and Mempool hold exactly three tiles, which auto-fill leaves stranded in
   a four- or two-column row. Given room for three at the floor width they take
   exactly three; below that they fall back to the shared rule above. The
   breakpoint is in rem for the same reason the floor is. */
@media (min-width: 47rem) {
  .grid.g3 { grid-template-columns: repeat(3, 1fr); }
}

.cell {
  position: relative;
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 13px 15px 14px;
  overflow: hidden;
}
.cell::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--border) 22%,
                              var(--border) 78%, transparent);
}

.cell .k {
  font-size: .66rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--text-dim); margin-bottom: 6px; font-weight: 600;
  line-height: 1.35;
}

.cell .v {
  font-family: var(--mono);
  /* Down a step from 1.5rem. Difficulty was clipping at the old size -- the
     monospace stack's advance is wider than the 0.6em the arithmetic assumed,
     so this is sized with headroom rather than to the measured edge: about 16
     characters fit the narrowest column, against Difficulty's 11 today. */
  font-size: clamp(1.05rem, .92rem + .5vw, 1.28rem);
  line-height: 1.2;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.015em;
  /* Wrapping allowed, breaking not. A value like "20,087,625 BTC" may fold at
     the space before its unit if the column is too narrow for it; a number is
     one word and will never be broken across lines, because "20,087," above
     "625 BTC" would be read as a different figure. The rem floor above should
     mean this never fires -- it is here so that the failure mode is a second
     line rather than a truncated number. */
  white-space: normal;
  overflow-wrap: normal;
  word-break: normal;
}

/* The two headline figures, and only those two. */
.cell.lead .v { color: var(--accent); }

.cell .sub {
  font-size: .72rem; color: var(--text-dim); margin-top: 6px;
  font-family: var(--mono); line-height: 1.45;
  white-space: normal;
}

.unknown { color: var(--text-dim); font-style: italic; letter-spacing: 0; }
.cell.lead .v.unknown { color: var(--text-dim); }

/* Window selector: the mean block interval cell and the history dialog. */
.winsel { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 11px; }
.win[disabled] { opacity: .45; cursor: not-allowed; }
.win {
  flex: 1 1 auto;
  padding: 5px 7px;
  font: 600 .68rem/1 var(--mono);
  letter-spacing: .04em;
  color: var(--text-dim); background: var(--field);
  border: 1px solid var(--border-control); border-radius: 5px;
  cursor: pointer; filter: none;
}
.win:hover {
  color: var(--text); border-color: var(--text-dim); filter: none;
}
.win.on {
  color: var(--bg); background: var(--accent); border-color: transparent;
}

/* --------------------------------------------------- nodes: head layout */

/* One column, every box the full width of the page. The totals used to run
   across as a row of tiles with the implementation table stranded under a
   third of it, which gave the version split -- the figure the headline count
   is made of -- less room than any tile explaining it, and at a 23-character
   version name that pushed the counts behind a scrollbar.

   A two-column split was tried and dropped. It read well but it had to be
   keyed to a width, and any such breakpoint is a guess about the reader: the
   floor scales with the root font while the page's max-width does not, so the
   same 1066px window is comfortable at a 16px root and six pixels short of
   fitting at 24px. Full width has no breakpoint to get wrong. The panel gets
   the whole measure at every size, the version names never fold, and the
   layout is the same one for everybody.

   Stacked rather than tiled: a row of tiles is what took the width away from
   the table in the first place.

   minmax(0, 1fr) rather than letting the column size itself. A grid item's
   automatic minimum is its min-content, so an implicit column takes the width
   of the widest thing in it -- and at a 24px root the version table's
   min-content is 358px against a 276px content box on a 320px screen, which
   pushed the whole page sideways. Pinning the minimum to 0 keeps the column
   at the measure and leaves the table to the horizontal scroll it already
   has, which is the one place that overflow belongs. */
.nodes-head {
  display: grid; gap: 14px;
  grid-template-columns: minmax(0, 1fr);
}

/* One tile per row, overriding both the auto-fill rule on .grid and its
   narrow-width override, which would otherwise pack the tiles two-up and
   leave the stack ragged. */
.nodes-head > .grid { grid-template-columns: 1fr; }

/* --------------------------------------------------------------- tables */

/* Capped at the container for the reason .nodes-head above is: the 300px
   floor was wider than a 320px screen's content box, so these panels hung
   24px off the right of the page. */
.tables {
  display: grid; gap: 14px; margin-top: 22px;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
}

.tbl {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 15px 16px 8px;
}

table { width: 100%; border-collapse: collapse; font-size: .89rem; }

th, td {
  text-align: left; padding: 7px 8px;
  border-bottom: 1px solid var(--border);
}
thead th { border-bottom-color: var(--border-control); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--surface-2); }

th {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--text-dim); font-weight: 650;
}

td { color: var(--text-dim); }
td.n, th.n {
  text-align: right; font-family: var(--mono);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
td.n { color: var(--text); }
td.sub { padding-left: 26px; color: var(--text-dim); }

.scroll { overflow-x: auto; }
.mono { font-family: var(--mono); }

/* Categorical swatches for the network breakdown. These identify a transport;
   no colour here ranks or judges one. */
.netlabel { display: inline-flex; align-items: center; gap: 9px; }
.dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--text-dim); flex: none;
}
.dot.net-ipv4 { background: var(--net-ipv4); }
.dot.net-ipv6 { background: var(--net-ipv6); }
.dot.net-tor  { background: var(--net-tor); }

/* ------------------------------------------------------------ node list */

#t-nodes {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 4px 16px;
}
/* Fixed layout keeps every column inside the panel: the address column was
   sized by 67-character onion addresses and pushed the table past its edge,
   with the horizontal scrollbar stranded at the foot of 104 rows. */
#t-nodes table { font-size: .82rem; table-layout: fixed; }
#t-nodes th:nth-child(1), #t-nodes td:nth-child(1) {
  width: 32%; white-space: normal; overflow-wrap: anywhere;
}
#t-nodes th:nth-child(2), #t-nodes td:nth-child(2) {
  width: 21%; white-space: normal; overflow-wrap: anywhere;
}
/* Columns 1 and 2 already break anywhere; 3 to 5 did not, so "Storage" and
   "archival" ran out of their cells on a narrow screen instead of folding. */
#t-nodes th, #t-nodes td { overflow-wrap: anywhere; }
#t-nodes th:nth-child(3), #t-nodes td:nth-child(3) { width: 15%; }
#t-nodes th:nth-child(4), #t-nodes td:nth-child(4) { width: 14%; }
#t-nodes th:nth-child(5), #t-nodes td:nth-child(5) { width: 18%; }

/* Address and its action share the cell rather than taking a column of their
   own: the node table is table-layout:fixed with widths tuned so 67-character
   onion addresses do not push it past the panel edge, and a sixth column would
   have meant re-apportioning all five. Wrapping instead costs nothing and puts
   the button under the address on a narrow screen. */
#t-nodes td .addrcell {
  display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px;
  max-width: 100%;
}

/* Check sends the address to the checker tab. Outlined, not filled: it is one
   of a hundred-odd rows and a column of solid buttons would be louder than
   anything on the page. */
#t-nodes td .chk {
  font: 600 .74rem/1 var(--sans);
  color: var(--text-dim); background: none;
  border: 1px solid var(--border-control); border-radius: 6px;
  padding: 4px 9px; cursor: pointer; filter: none;
}
#t-nodes td .chk:hover:not(:disabled) {
  color: var(--accent); border-color: var(--accent-dim);
  background: var(--surface-2);
}
/* Ten of the nodes seen are on other ports. The control stays in place and
   refuses, so the reason is discoverable where the absence of a button would
   just look like an omission. */
#t-nodes td .chk:disabled {
  opacity: .38; cursor: not-allowed; border-style: dashed;
}

/* The check result, opened under the row it belongs to. Full width, left
   aligned and unstyled by the numeric column rules above -- it is prose and a
   grid, not a figure in a column. */
#t-nodes tr.checkrow td {
  width: auto; text-align: left; white-space: normal;
  padding: 0 10px 16px; background: var(--surface-2);
}
#t-nodes tr.checkrow .result { margin-top: 0; }

/* Shortened onion address: reads as text, behaves as a copy control. */
#t-nodes td .addr {
  display: inline-flex; align-items: center; gap: 7px;
  max-width: 100%; white-space: normal;
  font: inherit; letter-spacing: inherit; text-align: left;
  color: var(--text-dim); background: none;
  border: 1px solid var(--border-control); border-radius: 6px;
  padding: 3px 8px;
  cursor: pointer; filter: none;
}
#t-nodes td .addr:hover {
  color: var(--accent); border-color: var(--accent-dim);
  background: var(--surface-2); filter: none;
}
#t-nodes td .addr .ic {
  width: 13px; height: 13px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.35;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: .55;
}
#t-nodes td .addr:hover .ic, #t-nodes td .addr:focus-visible .ic { opacity: 1; }
/* Was nowrap. That kept a shortened address on one line and let it run past
   the column whenever it could not fit: at a large browser font size the onion
   rows already overflowed their cell before anything here changed, and making
   every address a control extended the same failure to IPv4. Wrapping folds
   the address inside its own button instead, which is the graceful version of
   running out of room. */
#t-nodes td .addr .addr-t { min-width: 0; overflow-wrap: anywhere; }
#t-nodes td .addr.copied {
  color: var(--accent); border-color: var(--accent-dim);
  background: var(--accent-bg);
}
#t-nodes td .addr.copyfail { color: var(--warn); border-color: var(--warn); }

/* 104 rows: keep the column headings visible while scrolling past them. */
#t-nodes thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface);
}

/* ------------------------------------------------------------ block strip */

.strip-panel {
  margin-top: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 15px 16px 14px;
}
.strip-panel .winsel { margin: 0 0 12px; }

.epochnav {
  display: flex; align-items: center; gap: 8px;
  flex: 1 1 100%; margin-top: 4px;
}
.epochnav .win { flex: 0 0 auto; }
.epochlab {
  flex: 1 1 auto; text-align: center;
  font: 600 .68rem/1 var(--mono);
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--text-dim);
}
.strip-panel .note { margin: 12px 0 0; font-size: .78rem; max-width: none; }

.strip { display: block; width: 100%; height: auto; }
.strip .grid line { stroke: var(--border); stroke-width: 1; }
/* The ceiling gets a dashed rule so a full block reads as touching something. */
.strip .grid line.limit {
  stroke: var(--border-control); stroke-dasharray: 3 3;
}
.strip .grid text, .strip .xaxis text {
  fill: var(--text-dim); font: .6rem var(--mono);
}
/* Muted rather than saturated: 2,016 bars of full accent would be the large
   orange fill the palette exists to avoid. */
.strip .blk { fill: var(--accent-dim); }
.strip .blk.hot { fill: var(--accent); }

.readout {
  margin: 10px 0 0;
  font: .76rem/1.5 var(--mono);
  color: var(--text);
  min-height: 1.5em;
}
.readout.idle { color: var(--text-dim); }
/* A timestamp at or before its predecessor. Marked, not corrected. */
.strip .blk.back { fill: var(--warn); }

/* ---------------------------------------------------------- history graph */

/* The affordance. A preview plus a word: a bare cell gives no sign that
   history exists, and a click target hidden on the value is not discoverable. */
.cell .histbtn {
  display: flex; align-items: center; gap: 9px;
  width: 100%; margin-top: 11px; padding: 6px 9px;
  background: var(--field);
  border: 1px solid var(--border-control); border-radius: 6px;
  color: var(--text-dim);
  font: 600 .68rem/1 var(--mono);
  text-transform: uppercase; letter-spacing: .08em;
  cursor: pointer; filter: none;
}
.cell .histbtn:hover {
  color: var(--accent); border-color: var(--accent-dim);
  background: var(--surface-2); filter: none;
}
.cell .histbtn .spark { flex: none; }
.cell .histbtn .spark path {
  fill: none; stroke: var(--accent); stroke-width: 1.5;
  stroke-linejoin: round; stroke-linecap: round;
}
.histbtn-t { white-space: nowrap; }

dialog#hashdlg {
  width: min(880px, calc(100vw - 28px));
  background: var(--surface);
  color: var(--text-dim);
  border: 1px solid var(--border-control);
  border-radius: 12px;
  padding: 20px 22px 22px;
}
dialog#hashdlg::backdrop { background: rgba(0, 0, 0, .62); }
.dlg-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; margin-bottom: 14px;
}
.dlg-close {
  padding: 6px 12px;
  font: 650 .7rem/1 var(--sans);
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--text-dim); background: transparent;
  border: 1px solid var(--border-control); border-radius: 6px;
  cursor: pointer; filter: none;
}
.dlg-close:hover {
  color: var(--text); border-color: var(--text-dim); filter: none;
}
#hashdlg .winsel { margin: 0 0 14px; }
#hashdlg .note { margin: 12px 0 0; font-size: .78rem; max-width: none; }

.chart { display: block; width: 100%; height: auto; }
.chart .grid line { stroke: var(--border); stroke-width: 1; }
.chart .grid text, .chart .xaxis text {
  fill: var(--text-dim); font: .6rem var(--mono);
}
.chart .line {
  fill: none; stroke: var(--accent); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round;
}

/* --------------------------------------------------------------- checker */

form { margin: 0 0 14px; }
label {
  display: block; font-size: .8rem; color: var(--text-dim);
  margin-bottom: 8px; text-transform: uppercase; letter-spacing: .07em;
}
.row { display: flex; gap: 10px; flex-wrap: wrap; }

input[type=text] {
  flex: 1 1 260px; min-width: 0;
  padding: 11px 13px;
  font: 15px var(--mono);
  color: var(--text); background: var(--field);
  border: 1px solid var(--border-control); border-radius: 8px;
}
input[type=text]::placeholder { color: var(--placeholder); }
input[type=text]:focus { border-color: var(--accent-dim); outline-offset: 1px; }

button {
  padding: 11px 22px;
  font: 650 15px var(--sans); cursor: pointer;
  color: var(--bg); background: var(--accent);
  border: 1px solid transparent; border-radius: 8px;
  letter-spacing: .01em;
}
button:hover { filter: brightness(1.08); }
button[disabled] { opacity: .5; cursor: progress; filter: none; }

.result {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px; margin-top: 14px;
}
.result .cell { background: var(--field); }

/* The result grid holds values no cell can fit on one line: a v3 onion address
   is 62 characters, and the client string is not far behind. They were being
   clipped mid-string by .cell's overflow, which reads as text too large for its
   box. They wrap here instead, and sit a step below the chain grid because a
   transient read-out should not carry the weight of the page's headline
   figures. */
.result .cell .v {
  font-size: clamp(.95rem, .86rem + .34vw, 1.12rem);
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: 1.32;
}

/* The address gets the full row: at 62 characters it would otherwise wrap to
   four lines in a single column and drag the whole row's height with it. */
.result .cell.wide { grid-column: 1 / -1; }

/* --------------------------------------------------------------- notices */

.nojs, .msg {
  background: var(--warn-bg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--warn);
  color: var(--text);
  padding: 12px 15px; border-radius: 6px;
  font-size: .89rem; margin: 12px 0 0;
}

/* ---------------------------------------------------------------- footer */

footer {
  border-top: 1px solid var(--border);
  margin-top: 56px; padding: 22px 0 46px;
  color: var(--text-dim);
  /* 11.5px: clearly subordinate to the 0.89rem section notes. Not smaller than
     this -- it still has to be readable, and --text-dim holds 6.93:1 on the
     page ground, which only counts if the text can be read at all. */
  font-size: .72rem;
  line-height: 1.6;
}
footer p { max-width: 74ch; margin: 0 0 9px; }
footer p:last-child { margin-bottom: 0; }
/* .small is .84rem elsewhere; inside the footer that would out-size the footer
   itself, which is how the last line ended up the largest thing down here. */
footer .small { font-size: inherit; }

@media (max-width: 460px) {
  header { padding-top: 28px; }
  .banner { margin-bottom: 18px; }
  /* rem for the same reason as the floor it overrides: a px column against
     rem type is what let the values be cut off. 9.5rem is 152px at a default
     root, so the narrow layout is unchanged until the reader asks for larger
     text, at which point this collapses to a single wider column instead of
     two that cannot hold a figure. */
  .grid { grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); }
}
