/*! Calculingo -- calculingo.app
 * Copyright (c) 2026 Andrei Suslov. All rights reserved.
 *
 * Proprietary and source-available. Delivered to your browser so the page can
 * be drawn; that is not a grant of any right to use, copy, adapt or host it.
 * Permission can be asked for: truvord@gmail.com
 */
/* /calc: one input, one answer. Deliberately spare; light and dark. */

/* Inter, served from here rather than from Google. The hosted version cost two
 * extra hosts on the critical path -- a render-blocking stylesheet from
 * fonts.googleapis.com whose only job was to name a file on fonts.gstatic.com,
 * which then needed its own DNS and TLS before a glyph could be asked for.
 * Measured cold, the stylesheet alone was ~345ms of that, and preconnect only
 * shortens the handshake, it does not remove the hop. Same-origin the font
 * rides the connection the page already has.
 *
 * Inter is a variable font, so the three weights the page uses are three
 * instances of one file, not three files: `font-weight: 100 900` hands the
 * whole wght axis to the browser and it interpolates 400/500/600 from that.
 *
 * Two faces because the split pays for itself. The latin subset is everything
 * the page normally draws. The currency file is the five symbols Intl can hand
 * back that latin does not carry -- the shekel, rupee, won, peso and dong; the
 * euro is in latin already. Google's own latin-ext covers those five at 85KB,
 * which is more than the rest of the app put together, so it is subsetted down
 * to the currency block at 8KB. The unicode-range means it is only ever
 * fetched by someone who asks about one of those currencies.
 *
 * The ?v= is the first eight hex of the file's own sha256, not the date the
 * rest of the site is versioned by. These two files are the one thing here
 * that can be cached forever and never bumped again, and a date would undo
 * that: every deploy would rename them and re-send 48KB that had not changed
 * a byte. Hashed, the name moves when and only when the glyphs do. It has to
 * be repeated on the preload in index.html -- a preload for a URL that is not
 * the URL @font-face later asks for is simply a second download. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/inter-latin.woff2?v=c9407645") 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+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/inter-currency.woff2?v=bd8a360a") format("woff2");
  unicode-range: U+20A0-20AB, U+20AD-20C0;
}

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

/* The page ends where the page ends.
   A page whose content fits the window still moves under a two-finger swipe on
   every Apple browser: the header slides down off an empty band, the whole
   thing drifts sideways, and it springs back. It is the platform's rubber band
   and on a document with somewhere to go it is a useful thing, since it says
   which end you have arrived at. On a calculator that is one screen tall it
   says nothing at all, and what it looks like is a page that has come loose.

   `none` rather than `contain`, because there are two behaviours here and both
   have to go. `contain` stops the scroll being handed up from an inner box to
   the page, which is the fix for the board's own strip of hours running out
   under a finger; it leaves the page's own bounce exactly where it was. `none`
   is that plus the bounce.

   On html rather than body: the viewport takes its scrolling behaviour from
   whichever of the two propagates it, and with neither setting an overflow of
   its own that is html. Said on both anyway, since which one propagates is a
   rule two elements deep and a second declaration costs nothing. */
html,
body { overscroll-behavior: none; }

:root {
  color-scheme: light dark;
  --bg: #ffffff;
  --text: #1a1a1a;
  --muted: #6b7280;
  --faint: #9ca3af;
  --line: #e5e7eb;
  --chip: #f9fafb;
  /* The switch. One blue for a thrown one, and two inks for the glyph riding
     in the knob, which is on white in both themes and so never changes with
     the page. These live here rather than beside the board because the same
     switch is now in the nav of every page, and a control that only had its
     colours on one page would be two controls. */
  --tgl-on: #2563eb;
  --tgl-glyph-off: #9ca3af;
  --tgl-glyph-on: #1d4ed8;
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  /* The gap above the input. Named because the motto has to fill it exactly:
     two copies of this clamp would drift apart the first time either moved,
     and the price of them disagreeing is the input changing place. */
  --calc-top: clamp(3rem, 12vh, 8rem);
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg: #ffffff;
  --text: #1a1a1a;
  --muted: #6b7280;
  --faint: #9ca3af;
  --line: #e5e7eb;
  --chip: #f9fafb;
  --tgl-on: #2563eb;
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0f1115;
  --text: #f3f4f6;
  --muted: #9ca3af;
  --faint: #6b7280;
  --line: #262a33;
  --chip: #171a21;
  /* The knob stays white in both themes, so the track has to stay dark enough
     to hold it. */
  --tgl-on: #3b82f6;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1115;
    --text: #f3f4f6;
    --muted: #9ca3af;
    --faint: #6b7280;
    --line: #262a33;
    --chip: #171a21;
    --tgl-on: #3b82f6;
  }
}

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  /* So the footer can be held at the bottom of a short page rather than
     floating halfway up one. dvh as well as vh: on a phone vh is the window
     with the address bar hidden, which it is not when the page first draws,
     so vh alone reserves space for furniture that is still on screen. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* The column above is a flex container, which makes every wrap in it a flex
   item, and an auto margin across the flex axis cancels the stretch that would
   otherwise have made the item as wide as the page. Without a width of its
   own each wrap fell back to the width of whatever was inside it: the nav was
   as wide as the word "calculingo", and the calculator was as wide as its
   longest saved query, so the whole app changed width between one visit and
   the next and there was no telling which width you would get.

   width: 100% gives the cross size the stretch was meant to give it. The
   max-width then caps it and the auto margins centre it, which is what both
   were always there to do. */
.wrap {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 0 1.5rem;
}

.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ===== NAV ===== */
.nav {
  border-bottom: 1px solid var(--line);
  height: 64px;
  display: flex;
  align-items: center;
}

.nav .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.nav__logo {
  color: inherit;
  text-decoration: none;
  font-weight: 600;
  font-size: 0.9375rem;
}

/* ===== THE SWITCH =====
   The iOS control: a track the knob travels the length of, and a glyph riding
   inside the knob that changes as it goes. It was drawn for the clock board's
   bar and it is the site's switch now, so it lives here and the board's
   stylesheet keeps only the two glyphs that are the board's own.

   Everything is sized off --h, so no two switches on the site can drift apart
   and a third needs one number. */
.tgl {
  --h: 26px;
  --w: calc(var(--h) * 1.645);      /* 51 : 31, the iOS ratio */
  --pad: 2px;
  --dot: calc(var(--h) - var(--pad) * 2);
  --travel: calc(var(--w) - var(--h));
  --squish: calc(var(--h) * 0.2);
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  position: relative; display: inline-block; flex: none;
  width: var(--w); height: var(--h); vertical-align: middle;
}

/* The input is the whole control: full size, invisible, on top. That is what
   hands the hit target, the keyboard and the screen reader over to the browser
   rather than reimplementing all three on a div. */
.tgl input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer; z-index: 2;
}

.tgl__track {
  display: block; width: 100%; height: 100%; border-radius: 999px;
  background: var(--line);
  transition: background 0.3s var(--ease), box-shadow 0.15s ease;
}
.tgl__knob {
  position: absolute; top: var(--pad); left: var(--pad);
  width: var(--dot); height: var(--dot); border-radius: 999px;
  background: #fff; pointer-events: none; perspective: 240px;
  box-shadow: 0 3px 8px rgba(16, 26, 38, 0.18), 0 1px 1px rgba(16, 26, 38, 0.14);
  display: flex; align-items: center; justify-content: center;
  transition: transform 0.3s var(--ease), width 0.2s var(--ease);
}
.tgl input:checked ~ .tgl__track { background: var(--tgl-on); }
.tgl input:checked ~ .tgl__knob { transform: translateX(var(--travel)); }

/* The press: the knob stretches toward where it is headed. The one detail that
   separates this from a checkbox drawn as a lozenge. */
.tgl input:active:not(:disabled) ~ .tgl__knob { width: calc(var(--dot) + var(--squish)); }
.tgl input:checked:active:not(:disabled) ~ .tgl__knob {
  transform: translateX(calc(var(--travel) - var(--squish)));
}

.tgl input:focus-visible ~ .tgl__track { box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.35); }

/* The turning card the two-sided glyphs ride on, one face out and one face
   away, so a switch that has two things to say says the second by turning
   over rather than by swapping one picture for another. */
.tgl__flip {
  position: relative; width: 100%; height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.42s var(--ease);
}
.tgl__face {
  position: absolute; inset: 0; display: grid; place-items: center;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  color: var(--tgl-glyph-off); transition: color 0.3s var(--ease);
}
.tgl__face--back { transform: rotateY(180deg); }
.tgl input:checked ~ .tgl__knob .tgl__flip { transform: rotateY(180deg); }
.tgl input:checked ~ .tgl__knob .tgl__face { color: var(--tgl-glyph-on); }

@media (prefers-reduced-motion: reduce) {
  .tgl__track, .tgl__knob, .tgl__flip, .tgl__face { transition: none; }
}

/* Day and night, which is the one switch on the site with no words beside it:
   the nav is a name and a control, and two more words in it would be reading
   matter on a page whose whole job is the box underneath. The sun and the moon
   carry it instead, and the screen reader is told in full. */
.tgl--theme .tgl__face svg {
  width: calc(var(--dot) * 0.56); height: calc(var(--dot) * 0.56);
  display: block;
}

/* ===== CALC ===== */
.calc {
  padding-top: var(--calc-top);
  padding-bottom: 4rem;
}

#q {
  display: block;
  width: 100%;
  font: inherit;
  font-size: clamp(1.5rem, 5vw, 2rem);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: inherit;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--muted);
  border-radius: 0;
  padding: 0 0 0.75rem;
  outline: none;
}

/* The placeholder is a generated example and is the last advert standing once
   the history has hidden the chips, so it is doing real work rather than
   decorating. --faint is 2.54:1 on white, which is a little over half the
   floor for text; --muted is 4.83:1 light and 7.44:1 dark (T-1390, J94). */
#q::placeholder { color: var(--muted); }
/* A focus indicator that survives a squint. This was a one-pixel underline
   changing from one grey to another, on the only control the page has, and
   after Enter the text is selected and an unfocused box with text in it looks
   the same. The underline thickens as well as recolours, and an outline is
   drawn under forced colours where a border colour carries nothing
   (T-1390, J95). */
#q:focus {
  border-bottom-color: var(--text);
  border-bottom-width: 2px;
  padding-bottom: calc(0.75rem - 1px);
}


/* Inline completion. What was typed is repeated here in transparent text, so
   the grey remainder starts exactly where the caret is whatever the font does
   with the letters before it: nothing is measured and nothing is inserted into
   the input. Every metric below has to match #q or the two drift apart. */
.field { position: relative; }

.ghost {
  position: absolute;
  inset: 0;
  overflow: hidden;
  white-space: pre;
  pointer-events: none;
  font: inherit;
  font-size: clamp(1.5rem, 5vw, 2rem);
  font-weight: 500;
  letter-spacing: -0.01em;
  padding: 0 0 0.75rem;
  border-bottom: 1px solid transparent;
  color: var(--faint);
}

.ghost span:first-child { color: transparent; }

/* A phone keyboard has no Tab key, so the grey text is its own tap target. */
.ghost span:last-child { pointer-events: auto; cursor: pointer; }
.ghost[hidden] { display: none; }

/* ...and while there is grey text in the line, the placeholder is not the
   thing to read. It only ever draws when the box is empty, and the box is
   empty at exactly one moment that matters here: the arrows have lifted the
   typed word out to make room for the offer. Both are grey, both start where
   the text starts, and the reader got the example printed through the middle
   of the suggestion. 27-page decides when, since only it knows the ghost is
   up. */
#q.hushed::placeholder { color: transparent; }

.out {
  display: block;
  margin-top: 2rem;
  font-variant-numeric: tabular-nums;
  min-height: 6rem;
}

/* "can't read that yet" / "Dec 2026 has only 31 days" */
.out--note { color: var(--muted); font-size: 0.9375rem; }

/* The same refusal, when it has a reading to offer underneath it. */
.out__why { color: var(--faint); font-size: 0.9375rem; }
.out--note .detail { color: var(--muted); margin-top: 0.375rem; }

/* Nearly every answer is short enough that none of this matters. The ones that
   are not are one unbroken run of digits with nowhere to wrap: "pi to 100
   places" is a hundred-character word, and a word is what the browser treats
   it as, so it ran off the right-hand side and took the whole document with
   it. The page itself gained a horizontal scrollbar, which moved the nav, the
   chips and the footer sideways as well, none of which were too wide for
   anything. break-word rather than anywhere: normal answers still wrap between
   words, and only a run that cannot fit a line on its own gets split inside. */
.head {
  font-size: clamp(2rem, 8vw, 3rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
  overflow-wrap: break-word;
}

.detail {
  margin-top: 0.5rem;
  color: var(--muted);
  font-size: 0.9375rem;
}

/* What the word in the answer above means, for the answers whose word is the
   whole of what was asked: abundant, deficient, perfect, triangular. Lighter
   than the detail line above it and the size of the echo below it, because it
   is not part of the answer: the detail is what was worked out and this is
   what the words in it mean. Given the prose line's width and line height
   because it is a sentence rather than a row of fragments. */
.means {
  margin-top: 0.5rem;
  max-width: 42em;
  color: var(--faint);
  font-size: 0.875rem;
  line-height: 1.5;
}

/* The answer said in sentences, for the questions where a paragraph is what
   an almanac would print and a row of dot-separated fragments is not: an
   eclipse is read for the sense of it rather than scanned for a figure. Wider
   line height than the fragments above because it is prose and gets read left
   to right, and pre-line because a list of them is one paragraph each. */
.prose {
  margin-top: 0.75rem;
  max-width: 42em;
  line-height: 1.55;
  font-size: 1rem;
  white-space: pre-line;
}

/* The sum itself, in symbols and then with the numbers in it. Monospaced,
   because it is arithmetic being shown rather than prose being read, and the
   digits lining up is most of what makes it legible. */
.working {
  margin-top: 0.375rem;
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8125rem;
  line-height: 1.5;
  overflow-x: auto;
  white-space: nowrap;
}

/* Room to breathe after a paragraph. Against the fragment lines a quarter of
   a rem reads as the next line down, which is what it is; against prose it
   reads as a caption on the last sentence, which it is not. */
.prose + .echo {
  margin-top: 0.75rem;
}

.echo {
  margin-top: 0.25rem;
  color: var(--faint);
  font-size: 0.875rem;
}

/* The two pictures an answer can draw sit between the answer and the history
   list: the sun's ring under a sunset, the clock board under a conversion.
   Everything about how either of them looks is in its own sheet, which arrives
   with it. All this file does is reserve the place: both boxes are empty and
   hidden until an answer wants one, and a reader who asks about neither loads
   a byte of neither sheet.

   The caption under them is the exception, and it moved here from sun.css for
   the reason the pill did: it is the same caption under both pictures, saying
   the thing the picture cannot say for itself and carrying the link through to
   the full page. Left in sun.css it was a caption that only had its looks when
   a reader had also asked about a sunset, which is a stylesheet deciding
   whether a conversion answer is legible. */
.figure__note {
  margin-top: 0.5rem;
  color: var(--faint);
  font-size: 0.875rem;
}

.figure__note a {
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--line);
}

.figure__note a:hover,
.figure__note a:focus-visible {
  color: var(--text);
  text-decoration-color: var(--faint);
}

/* ===== EXAMPLES ===== */
.hints {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1rem;
}

.hints button {
  font: inherit;
  font-size: 0.8125rem;
  color: var(--muted);
  background: var(--chip);
  border: 1px solid var(--muted);
  border-radius: 999px;
  padding: 0.25rem 0.75rem;
  cursor: pointer;
}

.hints button:hover {
  color: var(--text);
  border-color: var(--muted);
}

/* The lettering hand, which both drawings are labelled in: the ring on /sun
   and the compass on /compass. It lived in sun.css, which is the sheet only
   /sun and a sun answer load, so /compass lettered its N, E, S and W in Inter
   while the machine it was built on had the face installed locally and drew it
   correctly for an audience of one. Here for the reason the pill below is:
   two figures want it and neither owns it.

   Named by the first eight hex of its own sha256, like the two Inter files
   beside it and for the same reason: the name moves when the glyphs move and
   never otherwise, so the year-long cache in _headers is a promise that can be
   kept. Declared for every page and downloaded by none that does not letter
   something in it, an @font-face nothing matches costing no bytes. */
@font-face {
  font-family: "Clipboard Script";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/clipboard-script.woff2?v=73e20a41") format("woff2");
}

:root { --hand: "Clipboard Script", "Inter", sans-serif; }

/* The pill: .hints button with an icon slot, which is the one component this
   system has beyond the hairline and the chip. It lived in sun.css until the
   clock board wanted one as well, and a component defined once per figure is
   a component that drifts. Here, both figures load it and neither owns it. */
.pill {
  font: inherit; font-size: 0.8125rem;
  display: inline-flex; align-items: center; gap: 0.4rem;
  color: var(--muted); background: var(--chip);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 0.25rem 0.75rem; cursor: pointer; white-space: nowrap;
}
.pill:hover { color: var(--text); border-color: var(--muted); }

/* ===== 404 ===== */
.notfound {
  padding-top: clamp(3rem, 12vh, 8rem);
  padding-bottom: 4rem;
  color: var(--muted);
}

.notfound a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.notfound a:hover { color: var(--text); }

/* ===== HISTORY ===== */
/* The query scrolls under a fade; short queries are unaffected because the
   mask only covers empty space, so no JS overflow check is needed. */
.log { list-style: none; margin-top: 2rem; }

.log li {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 0.4rem 0;
  border-top: 1px solid var(--line);
  font-size: 0.9375rem;
}

.log__q {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  white-space: nowrap;
  text-align: left;
  font: inherit;
  color: var(--muted);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent);
}

.log__q::-webkit-scrollbar { display: none; }
.log__q:hover { color: var(--text); }

/* A row of the list is one line, and stays one line however long the answer
   is: a saved query is something to scan past, and a row that grew to five
   lines of digits would push everything under it off the screen to say one
   thing nobody is reading yet. So the answer scrolls inside itself instead,
   exactly as the query beside it does, with the same hidden scrollbar and the
   same fade off the right-hand edge to say there is more of it.

   The answer keeps its natural width and the query takes what is left, which
   is right for every answer that fits and was the whole of the old rule. What
   was missing is what happens when it does not fit: an item that may not
   shrink does not, so the row stayed its own width and the answer hung out of
   the side of it. It may now give way, up to a point -- max-width is what
   keeps a hundred digits from taking the row and leaving the query nothing to
   be recognised by, and it is also what lets the query go on growing, since an
   answer capped at part of the row leaves the rest as free space rather than a
   shortage. An answer with room is untouched by all of it. */
/* A button rather than a span, so it can be reached from the keyboard and
   copied there (T-1390, J74). Every one of these is reset back to the text it
   was: the row is a line of an answer list and nothing about it should say
   "control" until it is focused. */
.log__a {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  flex: 0 1 auto;
  min-width: 0;
  max-width: 60%;
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
  font-variant-numeric: tabular-nums;
}

.log__a::-webkit-scrollbar { display: none; }

/* The fade that says there is more of it off to the right, put on only where
   there is. The query beside it can wear one unconditionally because it is the
   half that takes all the leftover width, so its last 1.5rem is nearly always
   empty and the gradient falls on nothing. This one is only ever as wide as
   the answer in it, so an unconditional fade dimmed the end of every short
   answer that never overflowed anything: "12.4274 miles" lost its s. Which
   rows get it cannot be known from the stylesheet, so renderLog measures and
   says. */
.log__a--more {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent);
}

.clear {
  font: inherit;
  font-size: 0.8125rem;
  /* It says what it clears now, which makes it a longer label and the one
     control on the page that undoes something. --faint is below the floor for
     text and this is text somebody has to read before pressing it. */
  color: var(--muted);
  background: none;
  border: 0;
  padding: 0.5rem 0;
  cursor: pointer;
}

.clear:hover { color: var(--text); }
.hints[hidden], .clear[hidden] { display: none; }

/* Answers copy to the clipboard on click; the outline is the visible receipt.
   The head is a button too, for the reason the history answers are: copying
   the value was mouse-only on a page whose stated point is copying values. */
.head, .log__a { cursor: pointer; }
.head {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
}
.copied {
  outline: 2px solid var(--muted);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ===== RELEASE PAGER =====
   The release notes are one page per forty releases (T-1390, J100), and this
   is the list of them. A row of version ranges rather than page numbers,
   because a page number says nothing about what is on it and a version range
   is the only thing a reader looking for a change knows. */
.pager {
  margin: 3rem 0 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}

.pager ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  font-size: 0.9375rem;
}

.pager li { color: var(--muted); }
.pager li[aria-current="page"] { color: var(--text); font-weight: 500; }

/* ===== CURRENCY PICKER ===== */
/* No box: the list is an extension of the sentence, so it starts at the caret
   and carries no border, no card and no shadow. The current value is not in
   here at all, it is in the line itself, greyed. */
.picker {
  position: absolute;
  top: 100%;
  margin: 0.1em 0 0;
  padding: 0;
  list-style: none;
  z-index: 5;
  font-size: clamp(1.5rem, 5vw, 2rem);
  /* Five values are on show at a time and one of them is up in the line, so
     the box holds four. The rest are a scroll away, which is why the fade
     follows the window and not the list: whatever is at the top of the box
     reads first, whatever is at the bottom is on its way in. */
  max-height: calc(4 * 1.37em);
  overflow-y: auto;
  /* A row longer than the space left between the caret and the edge is read by
     scrolling it, and that is the only direction there is anything in: the
     list begins at the caret and runs right. Sideways it therefore neither
     bounces at its own start nor hands the swipe up to the page, which on a
     trackpad had a horizontal flick over the suggestions rocking the whole
     document. Down the list still stops at the list, as it always did. */
  overscroll-behavior-x: none;
  overscroll-behavior-y: contain;
  scrollbar-width: none;                 /* the arrows do the scrolling */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 18%, rgba(0, 0, 0, 0.8) 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 18%, rgba(0, 0, 0, 0.8) 100%);
}

.picker::-webkit-scrollbar { display: none; }

.picker[hidden] { display: none; }

.picker li {
  display: grid;
  grid-template-columns: minmax(4.5em, auto) 1fr auto;
  gap: 0.8em;
  align-items: baseline;
  line-height: 1.25;                     /* five of these make the window */
  padding: 0.06em 0;
  cursor: pointer;
  white-space: nowrap;
}

/* Both columns carry meaning and both were below the floor. The offer is the
   text a reader is being asked to choose between, and the second column is
   where the currency names live -- "euros", "British pounds" -- drawn in the
   colour the borders are drawn in, 1.24:1, which is a fifth of the floor and
   is not readable at all (T-1390, J93, J94). */
.picker__offer { color: var(--muted); }
.picker__alt { color: var(--muted); font-size: 0.5em; }
.picker__flag { font-size: 0.62em; }

/* Pointing at a row lifts it out of the fade. */
.picker li:hover { opacity: 1; }
.picker li:hover .picker__offer { color: var(--text); }
.picker li:hover .picker__alt { color: var(--muted); }

/* ...and so does arrowing onto it, which is the decision J96 asked for and
   the reason it was open.
 *
 * The source said this was unfinished rather than settled: a mouse got
 * feedback on every row and a keyboard got none, because the value being
 * browsed is written up into the line rather than shown in the list, and a
 * listbox that marks a row it is not showing the value of is a listbox
 * telling a screen reader something untrue.
 *
 * The decision is that the line and the list both show it. The value still
 * goes up into the line, which is what a reader typing needs; the row it came
 * from is marked here, which is what a reader arrowing needs; and
 * aria-selected is set on that row, which is what makes the two agree.
 * Marked with the hover treatment rather than a second one, because being
 * pointed at and being arrowed onto are the same state as far as a reader is
 * concerned. */
.picker li[aria-selected="true"] { opacity: 1; }
.picker li[aria-selected="true"] .picker__offer { color: var(--text); }
.picker li[aria-selected="true"] .picker__alt { color: var(--muted); }
.picker li[aria-selected="true"] { background: var(--line); }

/* Forced colours cannot preserve the transparent copy that positions the
   ghost, and masks obscure text instead of communicating overflow there. */
@media (forced-colors: active) {
  /* A border colour carries nothing under forced colours, so the one control
     on the page gets a real outline there (T-1390, J95). */
  #q:focus { outline: 2px solid Highlight; outline-offset: 2px; }
  #ghost { display: none; }
  .log__q, .log__a, .picker {
    -webkit-mask-image: none;
    mask-image: none;
  }
}

/* ===== MOTTO ===== */
/* It says what the page is to somebody who has just arrived, and then it stops
 * saying it. Three things here are one decision -- that the box underneath it
 * must not move, ever.
 *
 * Only opacity animates, so the line keeps its space when it goes. Nothing
 * beneath a caret is allowed to jump three seconds after the page loaded.
 *
 * It does not sit above the gap between the header and the input, it is that
 * gap: the negative margin pulls it up over the padding it would otherwise
 * begin below, and its height hands that padding back. The space above the
 * input is the number it always was, so the input has not moved -- but the
 * text is centred in the whole span now instead of pressed against the box.
 *
 * And its height comes from --calc-top rather than from the text it holds, so
 * the size of this line is a decision about this line. Set it larger and the
 * input does not follow it down the page. */
.motto {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: calc(-1 * var(--calc-top));
  height: calc(var(--calc-top) + 2.75rem);
  text-align: center;
  color: var(--muted);
  font-size: clamp(1.25rem, 4vw, 1.5rem);
  transition: opacity .45s ease;
}
.motto[data-gone] { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .motto { transition: none; } }

/* ===== FOOTER ===== */
/* No top margin of its own: every main above it ends in its own bottom
   padding, and two spacings stacked would put the rule further from the page
   than from the edge of the window. */
.foot {
  /* Pushed to the bottom when the page above does not reach there. On a long
     page this does nothing, the main having already filled the height. */
  margin-top: auto;
  border-top: 1px solid var(--line);
  padding: 1.25rem 0 2rem;
  font-size: 0.8125rem;
  color: var(--faint);
}

.foot .wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
}

.foot a {
  color: inherit;
  text-decoration: none;
}

.foot a:hover,
.foot a:focus-visible { color: var(--text); }

/* The page the reader is already on stays in the row, so the three do not
   shuffle about between pages, and is darkened rather than dropped: a link
   that goes nowhere new is still the answer to "where am I". */
.foot [aria-current="page"] { color: var(--muted); }

/* Held at the foot of the window rather than at the foot of the document.
   The releases page is several screens of history and the version number sat
   at the end of all of them, which is a strange place to keep the one line a
   reader may have come for: the footer is where the version lives and where
   the other pages are reached from, and neither should cost a scroll.

   Sticky rather than fixed, so it still occupies its own space and the last
   release is not printed underneath it. On a page shorter than the window
   this does nothing at all -- the footer was already in view, and `margin-top:
   auto` above had already pushed it down.

   Prose pages only. The calculator has a suggestion list that opens downward
   out of the input and would spend the rest of its life arguing with this for
   the same few pixels. */
.page ~ .foot {
  position: sticky;
  bottom: 0;
  /* Opaque, or the history scrolls through it. */
  background: var(--bg);
}

/* ===== PROSE PAGES ===== */
.page {
  padding-top: clamp(2.5rem, 8vh, 5rem);
  padding-bottom: 4rem;
}

.page h1 {
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 1.75rem;
}

.page p {
  margin-bottom: 1rem;
  color: var(--muted);
  max-width: 60ch;
}

.page a {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.page a:hover { color: var(--muted); }

/* A key on a keyboard, drawn rather than spelled. The glyph is a path and not
   a character: ⏎ and ↵ are missing from enough fonts to fall back to a box,
   and an emoji would be a picture of a key rather than the key.

   The size and the colours live on the element as attributes, not here. This
   rule draws the keycap around a glyph that has already drawn itself, so a
   stale copy of this file costs the reader a border and nothing more. Set
   here, a stylesheet that had not arrived yet left an 11-pixel arrow filling
   the width of the page in solid black, which is what an SVG with no width
   and no fill does. */
.key {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.15em 0.35em;
  margin: 0 0.05em;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 0.3rem;
  background: var(--chip);
  color: var(--muted);
  vertical-align: -0.15em;
}

.key svg { display: block; }

/* ===== RELEASES ===== */
.rel { margin-bottom: 2.25rem; }

.rel__when {
  font-size: 0.8125rem;
  color: var(--faint);
  margin-bottom: 0.5rem;
}

.rel h2 {
  font-size: 0.9375rem;
  font-weight: 600;
  margin-bottom: 0.75rem;
}

.rel ul { list-style: none; max-width: 60ch; }

/* The marker is text rather than a list-style so it takes the faint colour
   without the row taking it too. */
.rel li {
  color: var(--muted);
  padding-left: 1.1rem;
  text-indent: -1.1rem;
  margin-bottom: 0.4rem;
}

.rel li::before {
  content: "·";
  color: var(--faint);
  padding-right: 0.6rem;
}

/* The version, at the far end of the footer row, so it is on every page
   without being the first thing on any of them. It is there to be quoted in
   a bug report, which means it has to be visible and does not have to be
   prominent. */
.foot__v {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

.rel__v {
  display: inline-block;
  min-width: 3.5rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* A change shown rather than described. A sentence explaining that a summer
   day was being labelled as crossing a midnight it never crossed takes longer
   to read than the two answers side by side, and is less convincing: the
   query and what it used to say and what it says now is the whole of the
   claim, and a reader can check it in the box above.

   The marker and the hanging indent come off, the query supplying its own
   left edge. */
.rel li.rel__eg {
  padding-left: 0;
  text-indent: 0;
  margin-bottom: 0.9rem;
}
.rel li.rel__eg::before { content: none; }

.rel__q {
  display: block;
  color: var(--text);
  margin-bottom: 0.15rem;
}

/* Was and now, one under the other, so the eye drops rather than scans. The
   labels are in the stylesheet because they are furniture: every one of these
   rows says the same two words and none of them is content. */
.rel__was,
.rel__now { display: block; padding-left: 1.1rem; }

.rel__was::before,
.rel__now::before {
  display: inline-block;
  min-width: 3.2rem;
  color: var(--faint);
}
.rel__was::before { content: "was"; }
.rel__now::before { content: "now"; }
.rel__was { color: var(--faint); }
.rel__now { color: var(--muted); }
