/*
 * Book Tracker component styles.
 *
 * Loaded ONLY on tracker pages, on top of the site shell's site.css (see
 * pages/templates/pages/base.html). The global reset, links, header, footer
 * and .small-heading live in site.css and are deliberately not repeated here.
 *
 * References tokens only — no raw values, enforced by
 * stylelint-declaration-strict-value (see .stylelintrc.json).
 *
 * Known duplication: .journey / .journey__grid and .btn also exist in
 * site.css, because the tracker's title landing page and the home page's ink
 * band are the same design section rendered by two apps. base.css loads last
 * so tracker pages take these definitions. Worth unifying — see CLAUDE.md §4.
 */

h1,
h2,
h3 {
  color: var(--color-text);
  font-family: var(--font-display);
  font-weight: var(--font-weight-light);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

h1 {
  font-size: var(--text-display);
}

h2 {
  font-size: var(--text-2xl);
}

h3 {
  font-size: var(--text-xl);
}

p,
li {
  font-family: var(--font-body);
  font-weight: var(--font-weight-light);
  font-size: var(--text-lead);
  line-height: var(--leading-relaxed);
  color: var(--color-text-muted);
  max-width: var(--measure-lead);
  text-wrap: pretty;
}

strong {
  color: var(--color-text);
  font-weight: var(--font-weight-medium);
}

.container {
  max-width: 42rem;
  margin: 0 auto;
  padding: var(--space-section-y-sm) var(--space-section-x-sm);
}

.container h1 {
  margin: 0 0 var(--space-block);
}

/* Full-bleed dark band, content capped at the page measure. Mirrors the
 * "Message in a bottle" section of the marketing site. */
.journey {
  background-color: var(--color-bg);
  color: var(--color-text);
}

.journey__grid {
  display: grid;
  grid-template-columns: 1fr 0.9fr;
  gap: var(--space-column-gap);
  align-items: center;
  max-width: var(--measure-page);
  margin: 0 auto;
  padding: var(--space-section-y) var(--space-section-x);
}

.journey__title {
  margin: 0 0 var(--space-block);
}

.journey__lead {
  max-width: var(--measure-lead);
  margin: 0 0 var(--space-block-lg);
  color: var(--color-text-muted);
  font-family: var(--font-body);
  font-weight: var(--font-weight-light);
  font-size: var(--text-lead);
  line-height: var(--leading-relaxed);
  text-wrap: pretty;
}

@media (width <= 900px) {
  .journey__grid {
    grid-template-columns: 1fr;
    gap: var(--space-column-gap-sm);
    padding: var(--space-section-y) var(--space-section-x-sm);
  }
}

@media (width <= 560px) {
  .journey__grid {
    padding: var(--space-section-y-sm) var(--space-section-x-sm);
  }
}

/* ---------------------------------------------------------------------
 * Buttons
 * ------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-gap-sm);
  padding: var(--space-btn-y) var(--space-btn-x);
  border: 1px solid var(--color-text);
  border-radius: var(--radius-full);
  background-color: transparent;
  color: var(--color-text);
  font-family: var(--font-label);
  font-size: var(--text-label);
  font-weight: var(--font-weight-regular);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out);
}

.btn:hover {
  background-color: var(--color-text);
  color: var(--color-text-on-accent);
}

.btn__arrow {
  flex-shrink: 0;
  width: var(--text-arrow);
  height: var(--text-arrow);
}

.btn-primary {
  border-color: var(--color-parchment);
  background-color: var(--color-parchment);
  color: var(--color-text-on-accent);
}

.btn-primary:hover {
  border-color: var(--color-accent);
  background-color: var(--color-accent);
  color: var(--color-text-on-accent);
}

/* The landing page's button row: "See the map" + "Buy a copy". Wraps to
 * two lines on a narrow phone rather than overflowing. */
.journey__buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-gap-sm);
}

/* ---------------------------------------------------------------------
 * Action links — the serif rows with hairline rules and a hover indent
 * ------------------------------------------------------------------- */

.action-links {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--color-border);
}

.action-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-gap);
  padding: var(--space-row) var(--space-hairline);
  border-bottom: 1px solid var(--color-border);
  background-color: transparent;
  color: var(--color-text);
  font-family: var(--font-body);
  font-weight: var(--font-weight-regular);
  font-size: var(--text-action);
  text-decoration: none;
  transition:
    color var(--duration-fast) var(--ease-out),
    padding-left var(--duration-fast) var(--ease-out);
}

.action-link:hover {
  padding-left: var(--space-gap-sm);
  color: var(--color-accent);
}

.action-link__arrow {
  flex-shrink: 0;
  width: var(--text-arrow);
  height: var(--text-arrow);
  color: var(--color-text);
  opacity: 0.55;
}

.action-link:hover .action-link__arrow {
  color: var(--color-accent);
  opacity: 1;
}

/* ---------------------------------------------------------------------
 * Forms
 * ------------------------------------------------------------------- */

.field {
  margin-bottom: var(--space-5);
}

.field label {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--color-text-subtle);
  font-family: var(--font-label);
  font-size: var(--text-field-label);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-field-label);
  text-transform: uppercase;
}

.label-hint {
  font-style: italic;
}

/* The parenthetical examples stay editorial — Newsreader italic, sentence
 * case — so the uppercase label reads as the name of the field and the
 * hint reads as an aside rather than more shouting. */
.field label .label-hint {
  color: var(--color-text-subtle);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-light);
  letter-spacing: initial;
  text-transform: none;
}

/* A question asked directly by a form — the radio group's prompt, not body
 * copy. Like .help-text below, it opts out of the global `p, li` measure:
 * that 52ch cap is there so long prose stays readable, but a form control
 * spans its container, and a prompt that stops two thirds of the way across
 * reads as a wrapping accident rather than a deliberate column. */
.form-question {
  max-width: 100%;
  margin-bottom: 0;
  color: var(--color-text);
}

/* Help text: the small secondary line under a question or a field.
 *
 * The global `p, li` rule caps paragraphs at --measure-lead (52ch) for
 * readability, which is right for body copy but wrong here — help text
 * renders at --text-xs while the input above it is --text-base at width
 * 100%, so 52 characters of small type stops well short of the field's
 * right edge and reads as a ragged orphan. It follows its container
 * instead.
 *
 * Styled unscoped because it is used in two positions now: under a field,
 * and as the second line of a two-part question (catch / re-release ask
 * "Do you want to create a profile name…?" and then, quieter, "Select No
 * if you'd rather stay anonymous"). The tighter top margin is what makes
 * the pair read as one question with an aside rather than as two
 * paragraphs of equal weight. */
.help-text {
  max-width: 100%;
  margin-top: var(--space-1);
  font-family: var(--font-label);
  color: var(--color-text-subtle);
  font-size: var(--text-xs);
}

.field .help-text {
  margin-top: var(--space-2);
}

.help-text.is-warning {
  color: var(--color-warning);
}

.required-asterisk {
  color: var(--color-warning);
}

.profile-status-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
}

input[type="text"],
input[type="email"],
textarea {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background-color: var(--color-bg-raised);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-base);
}

input[type="checkbox"],
input[type="radio"] {
  flex-shrink: 0;
  accent-color: var(--color-accent);
}

.radio-option,
.checkbox-option {
  margin-bottom: var(--space-3);
}

/* A consent that belongs to the input above it rather than standing on its
 * own — every mailing-list box in the tracker sits inside the email field it
 * relates to (owner, 2026-08-31), so this is the treatment for all of them,
 * not a one-off. It occupies the
 * slot that field's help text would, so it takes the help text's treatment:
 * tight to the input, no trailing margin (the field carries one), and the
 * smaller fine-print size, since at --text-sm it was the largest thing in a
 * block whose label and hint are both smaller and it read as bottom-heavy.
 *
 * Note this makes one consent smaller than the standalone consents
 * elsewhere. Deliberate, and the limit of it: shrinking a consent is
 * generally the wrong instinct, and it is only right here because this box
 * is a field's fine print rather than a question in its own right. Do not
 * take it as licence to shrink the others. */
.field .checkbox-option {
  margin-top: var(--space-1);
  margin-bottom: 0;
}

/* Consent and choice rows are sentence-case prose, not field names, so
 * they take the UI sans at reading size rather than the tracked caps of
 * .field label. */
.radio-option label,
.checkbox-option label {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  color: var(--color-text-muted);
  font-family: var(--font-label);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

.radio-option label:hover,
.checkbox-option label:hover {
  color: var(--color-text);
}

/* The mailing-list consent under the finder's email field.
 *
 * Set to match the question above it — "Do you want to create a profile
 * name…?" (owner, 2026-08-31). The two are the same kind of thing: a
 * sentence the person is being asked to agree to, not a caption. Applies to
 * every consent checkbox in the tracker: they all live inside their field.
 *
 * Note where that question's type actually comes from, because it is not
 * obvious and it caught me out: `.form-question` only sets measure, margin
 * and colour. The family, size and leading come from the `fieldset > p`
 * rule further down, which .form-question does not override. So the values
 * to match are that rule's, not the ones .form-question declares. Change
 * `fieldset > p` and change this too, or they drift.
 *
 * text-transform and letter-spacing are reset, not merely absent: `.field
 * label` sets uppercase and tracking for field names, and since
 * `.checkbox-option label` never declared either, putting this box inside a
 * field left it inheriting them. Declared here rather than beside
 * `.field .checkbox-option` so it follows the plain `.checkbox-option label`
 * it overrides — stylelint enforces that order. */
.field .checkbox-option label {
  /* The field-name grey (owner, 2026-08-31) — this box belongs to the field
   * above it, so it sits at the same weight of ink as that field's name
   * rather than the full-strength text of the question. */
  color: var(--color-text-subtle);
  font-family: var(--font-label);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-regular);
  line-height: var(--leading-normal);
  letter-spacing: normal;
  text-transform: none;
}

/* The question that introduces a group of choices. */
fieldset > p {
  margin: 0 0 var(--space-3);
  color: var(--color-text-muted);
  font-family: var(--font-label);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

fieldset {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-4);
  margin: 0 0 var(--space-4);
}

legend {
  color: var(--color-text);
  font-family: var(--font-label);
  letter-spacing: var(--tracking-nav);
  text-transform: uppercase;
  font-size: var(--text-sm);
  padding: 0 var(--space-2);
}

.errorlist {
  color: var(--color-danger);
  font-size: var(--text-sm);
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-2);
}

.notice {
  background-color: var(--color-danger-muted);
  border: 1px solid var(--color-danger);
  border-radius: var(--radius-sm);
  color: var(--color-text);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-5);
}

/* ---------------------------------------------------------------------
 * Journey trail / misc
 * ------------------------------------------------------------------- */

.event {
  border-left: 2px solid var(--color-border-accent);
  padding-left: var(--space-4);
  margin-bottom: var(--space-5);
}

.event__meta {
  color: var(--color-text-subtle);
  font-size: var(--text-sm);
}

.hint {
  color: var(--color-text-subtle);
  font-size: var(--text-sm);
  margin-top: var(--space-6);
}

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

.book-mockup {
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-bottom: var(--space-5);
}

.book-mockup img {
  display: block;
  width: 100%;
  height: auto;
}

.qr-page-preview {
  position: relative;
}

.qr-page-preview__value {
  position: absolute;
  left: 50%;

  /* Anchored so the text sits just above the ruled line, the way
   * handwriting does, rather than centered on top of it. */
  transform: translate(-50%, calc(-100% - 6px));
  color: var(--color-text-on-accent);
  white-space: nowrap;
  font-family: var(--font-handwriting);
  font-size: var(--text-2xl);
  font-weight: var(--font-weight-bold);
}

.qr-page-preview__value--name {
  top: 54.7%;
}

.qr-page-preview__value--number {
  top: 95.5%;
}

/* ---------------------------------------------------------------------------
 * Journey pages — the title map + stats, and one copy's trail.
 *
 * Tokens only, as everywhere else. Note that the map colors are NOT set here:
 * Leaflet draws to a canvas, so journey-map.js reads the same custom
 * properties at runtime rather than carrying an accent of its own.
 * ------------------------------------------------------------------------ */

/* The journey pages are wider than a form page — a map inside a 42rem
 * column is a postage stamp. */
.container--wide {
  max-width: var(--measure-page);
}

.journey-section {
  margin-top: var(--space-8);
  margin-bottom: var(--space-5);
}

/* A heading with an aside under it keeps almost none of its own bottom
 * margin — the two are one unit, and the aside supplies the space below.
 * Stated as a rule about the pair rather than as a negative margin on the
 * aside, so neither can be moved without the other following. */
.journey-section:has(+ .journey-note) {
  margin-bottom: var(--space-1);
}

/* "Find a copy" - the small self-serve lookup for a reader holding a
 * physical book who wants that COPY's own trail. Rendered on the title
 * landing page and the title journey page (see title_landing.html,
 * title_journey.html) whenever the view puts lookup_form in the context. */
.find-a-copy {
  margin-top: var(--space-6);
  padding: var(--space-5);
  border: var(--space-px) solid var(--color-border);
  border-radius: var(--radius-md);
  background-color: var(--color-bg-sunken);
}

.find-a-copy .field {
  margin-bottom: 0;
}

.find-a-copy__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-gap-sm);
}

/* Sized to what it holds (a 6-character copy number), not the full row —
 * overrides the global `input[type="text"] { width: 100% }`, which is what
 * forced this onto its own line above the button in the first place. */
.find-a-copy__row input {
  flex: 0 1 auto;
  width: var(--measure-copy-lookup-input);
  min-width: 8em;
}

/* A small pill, sized down from the standard .btn so it reads as part of
 * the field rather than a full call-to-action beside it. */
.find-a-copy__row .btn {
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-xs);
}

.find-a-copy .notice {
  margin-top: var(--space-4);
  margin-bottom: 0;
}

/* Title landing page only: the right-hand action rows drop a little to sit
 * optically centred against the taller left column, and the copy lookup is
 * the list's last row (owner, 2026-09-07) rather than a second control
 * under the page's button. It keeps the hairline and padding of a row but
 * stacks its label over the field, and it is not a link, so it gets none of
 * the hover indent. */
.journey__grid .action-links {
  margin-top: var(--space-6);
}

.action-link--lookup {
  cursor: default;
}

.action-link--lookup .find-a-copy__form {
  display: flex;
  align-items: center;
  gap: var(--space-gap);
  width: 100%;
}

/* Reads as the row's own words, like "Found a copy" beside it. */
.action-link--lookup .action-link__label {
  margin-bottom: 0;
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-action);
  letter-spacing: initial;
  text-transform: none;
  white-space: nowrap;
}

/* Not a boxed field: the raised ground, border and radius made it a widget
 * dropped into a list of plain rows. An underline in the same hairline the
 * rows already use keeps it typing-shaped without adding a second shape. */
.action-link--lookup input[type="text"] {
  flex: 0 1 auto;
  width: var(--measure-copy-lookup-input-sm);
  min-width: 0;
  padding: 0 0 var(--space-1);
  border: none;
  border-bottom: 1px solid var(--color-border);
  border-radius: 0;
  background-color: transparent;
  color: var(--color-text);
  font-size: var(--text-action);
}

.action-link--lookup input[type="text"]:focus {
  border-bottom-color: var(--color-accent);
  outline: none;
}

/* The submit is the row's arrow, not a pill: same glyph, same position and
 * same hover colour as the links above it. */
.action-link__submit {
  display: inline-flex;
  margin-left: auto;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

/* The text arrow the home page's journey rows use, not the solid SVG glyph
 * (owner, 2026-09-07), so the two pages' row lists read the same. The SVG
 * partial is still used by release_a_copy_start.html. */
.action-link__meta {
  flex-shrink: 0;
  color: var(--color-text-subtle);
  font-size: var(--text-arrow-lg);
}

.action-link:hover .action-link__meta,
.action-link__submit:hover .action-link__meta {
  color: var(--color-accent);
}

.journey-map {
  height: 460px;
  margin-top: var(--space-6);
  border: var(--space-px) solid var(--color-border);
  border-radius: var(--radius-md);
  background-color: var(--color-bg-raised);
}

@media (width <= 700px) {
  .journey-map {
    height: 320px;
  }
}

/* Numbered stop pins on a copy's map. */
.journey-pin span {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-full);
  background-color: var(--color-accent);
  color: var(--color-text-on-accent);
  font-family: var(--font-label);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-medium);
  line-height: var(--leading-snug);
  box-shadow: var(--shadow-sm);
}

/* Leaflet's popups default to a white card, which is a hole punched in the
 * ink ground. Re-skin them onto the tokens. */
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
  background-color: var(--color-bg-raised);
  color: var(--color-text-muted);
  box-shadow: var(--shadow-md);
}

.leaflet-popup-content {
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
}

.leaflet-container a.leaflet-popup-close-button {
  color: var(--color-text-subtle);
}

/* Same story for the zoom buttons: Leaflet's white chips read as a hole in
 * the ink ground. Its own rules are scoped to `.leaflet-bar`/`.leaflet-container`,
 * so these are too. */
.leaflet-bar a,
.leaflet-bar a:hover {
  border-bottom-color: var(--color-border);
  background-color: var(--color-bg-raised);
  color: var(--color-text);
}

.leaflet-bar a:hover {
  background-color: var(--color-accent-muted);
}

.leaflet-bar {
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
}

/* Leaflet scopes its own attribution rule to `.leaflet-container`, so match
 * that specificity or its white bar wins and the credit is unreadable. */
.leaflet-container .leaflet-control-attribution {
  background-color: var(--color-bg-raised);
  color: var(--color-text-muted);
  font-family: var(--font-label);
  font-size: var(--text-xs);
}

.leaflet-container .leaflet-control-attribution a {
  color: var(--color-accent);
}

/* The eight aggregate numbers.
 *
 * Below 900px the columns are found by auto-fit, so the grid reflows to
 * whatever the width allows. Above it the count is FIXED at four, giving two
 * even rows of four — auto-fit would otherwise fit six across at full width
 * and leave a ragged row of two. */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-5);
  margin: 0;
}

@media (width >= 900px) {
  .stat-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

.stat {
  padding-top: var(--space-4);
  border-top: var(--space-px) solid var(--color-border-subtle);
}

.stat dt {
  color: var(--color-text-subtle);
  font-family: var(--font-label);
  font-size: var(--text-eyebrow);
  font-weight: var(--font-weight-medium);
  line-height: var(--leading-normal);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
}

.stat dd {
  margin-top: var(--space-2);
  margin-left: 0;
  color: var(--color-text);
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: var(--font-weight-light);
  line-height: var(--leading-tight);
}

.stat__unit {
  display: block;
  margin-top: var(--space-1);
  color: var(--color-text-subtle);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
}

/* A one-line aside under a section heading — quieter than the prose around
 * it, and italic so it reads as an instruction rather than as content. It
 * sits tight under its heading (they are one unit) and closes with the same
 * hairline the rows below use, so the pair reads as a header for the list
 * rather than as a first row of it. */
.journey-note {
  margin-top: 0;
  margin-bottom: var(--space-4);
  color: var(--color-text-subtle);
  font-size: var(--text-base);
  font-style: italic;
}

/* "Featured Stop" — one stop lifted out of the trail (owner, 2026-09-11).
 * It borrows the trail's own type so the same words read the same in both
 * places, sits close under the copy's facts as part of the same header
 * block, and keeps its distance from the actions below it. */
.featured-stop {
  margin-top: var(--space-4);
  margin-bottom: var(--space-6);
}

/* A SUB-section heading: reads as a heading, at h3 size rather than the
 * 32px of .journey-section, for a block that sits inside the page header
 * rather than opening a section of its own (owner, 2026-09-11). Named
 * generically because the next one of these should reuse it. */
.journey-subsection {
  margin-top: 0;
  margin-bottom: var(--space-2);
  font-size: var(--text-xl);
}

/* The copy's facts, as label/value rows rather than a sentence (owner,
 * 2026-09-11). Labels borrow the stat grid's quiet uppercase so the two
 * blocks on this page read as one system; values are set in body type
 * because some of them are a sentence's worth of place description. */
.copy-facts {
  margin: 0 0 var(--space-5);
}

.copy-facts__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: var(--space-px) solid var(--color-border-subtle);
}

.copy-facts dt {
  flex: 0 0 auto;
  min-width: 9rem;
  color: var(--color-text-subtle);
  font-family: var(--font-label);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
}

.copy-facts dd {
  flex: 1 1 16rem;
  margin: 0;
  color: var(--color-text);
  font-size: var(--text-base);
}

.featured-stop__meta {
  margin: 0;
  max-width: none;
  color: var(--color-text);
  font-family: var(--font-label);
  font-size: var(--text-trail-meta);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-nav);
}

/* The trail — recent activity on a title, every stop on a copy. */
.trail {
  margin: 0;
  padding: 0;
  list-style: none;

  /* The rule that closes the heading block belongs to the LIST, not to the
   * aside above it: a paragraph and a list item measure 52ch in different
   * fonts, so a border on the aside would stop short of the row rules below
   * it. On the list, it lines up with them exactly. */
  border-top: var(--space-px) solid var(--color-border-subtle);
}

.trail__row {
  /* The base `li` rule caps a list item at 52ch, which would stop each row's
   * rule short of the list's own. The rules must line up, so the ROW spans
   * the full column and the prose inside it keeps its own measure. */
  max-width: none;
  padding-top: var(--space-4);
  padding-bottom: var(--space-4);
  border-bottom: var(--space-px) solid var(--color-border-subtle);
}

/* An activity row is two lines: the copy and what happened to it, then
 * where and when. They are ONE typographic treatment — same family, size,
 * tracking and color — so the pair reads as a single heading. The copy
 * number is told apart by being uppercase and by being the row's only link,
 * NOT by being bigger: an earlier version set it a step larger and it read
 * as a headline standing apart from the line under it. */
.trail__meta,
.trail__where {
  margin: 0;
  max-width: none;
  color: var(--color-text);
  font-family: var(--font-label);
  font-size: var(--text-trail-meta);

  /* Set tight and with no space between them, so the two lines read as one
   * heading block rather than as stacked sentences (owner, 2026-08-31). The
   * breathing room goes BELOW, between the block and the note. */
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-nav);
}

.trail__copy {
  text-transform: uppercase;
}

/* A hair of air under the first line — enough to separate the two, not
 * enough to break the pair apart. */
.trail__where {
  margin-top: var(--space-1);
}

.trail__stop {
  display: inline-block;
  min-width: 22px;
  margin-right: var(--space-2);
  border-radius: var(--radius-full);
  background-color: var(--color-accent-muted);
  color: var(--color-accent);
  font-size: var(--text-xs);
  line-height: var(--leading-normal);
  text-align: center;
}

/* The reading measure is lifted here (owner, 2026-09-11): these are
 * somebody's own words, and they should fill the width the device gives
 * them rather than being held to a 52-character column that leaves a
 * short note broken across more lines than it needs. */
.trail__detail,
.trail__note {
  margin-top: var(--space-3);
  margin-bottom: 0;
  max-width: none;
  font-size: var(--text-sm);
}

.trail__more {
  font-family: var(--font-label);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-nav);
  text-transform: uppercase;
  white-space: nowrap;
}

.sponsor-credit {
  color: var(--color-accent);
  font-size: var(--text-base);
  font-style: italic;
}

/* Found/release actions for the ONE copy this page is about, right next to
 * its number — see copy_journey.html. */
.copy-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-gap-sm);
  margin-top: var(--space-5);
}

/* Cover + buy link on a copy's page: whoever found this book is the likeliest
 * person to want one of their own. */
.book-card {
  display: flex;
  gap: var(--space-6);
  align-items: flex-start;
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: var(--space-px) solid var(--color-border-subtle);
  flex-wrap: wrap;
}

/* The card supplies its own top spacing; the heading inside it must not add
 * a second helping. */
.book-card .journey-section {
  margin-top: 0;
}

.book-card__cover {
  width: 180px;
  border-radius: var(--radius-image);
  box-shadow: var(--shadow-book);
}

/* The city disambiguation prompt — only ever shown when a typed city really
 * did match more than one place. */
.city-choice {
  margin-top: var(--space-3);
  padding: var(--space-4);
  border: var(--space-px) solid var(--color-border-accent);
  border-radius: var(--radius-sm);
  background-color: var(--color-accent-muted);
}

.city-choice__question {
  margin-top: 0;
  margin-bottom: var(--space-2);
  color: var(--color-text);
  font-size: var(--text-base);
}

/* ---------------------------------------------------------------------------
 * The journey-updates question, asked on the confirmation pages.
 *
 * Yes and No are deliberately the SAME button, side by side. A prominent Yes
 * next to a faint No is a dark pattern, and it would undermine the whole
 * reason for asking this way instead of with a checkbox.
 * ------------------------------------------------------------------------ */

.consent-ask {
  margin: var(--space-6) 0;
  padding: var(--space-5);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
}

.consent-ask__title {
  margin: 0 0 var(--space-2);
  font-size: var(--text-xl);
}

.consent-ask__question {
  margin: 0 0 var(--space-2);
  font-size: var(--text-lead);
}

.consent-ask__note {
  margin: 0 0 var(--space-5);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.consent-ask__actions {
  display: flex;
  gap: var(--space-3);
}

.consent-ask__actions .btn {
  min-width: var(--measure-consent-button);
  cursor: pointer;
}

.consent-ask__done {
  margin: var(--space-6) 0;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  background: var(--color-bg-raised);
  font-size: var(--text-sm);
}

/* ---------------------------------------------------------------------------
 * The copies table (owner, 2026-09-12)
 *
 * One row per book on the city, profile and sponsor pages. Set in the label
 * face at the trail's own size so a copy number reads the same here as it
 * does in an activity row, and paginated at ten because a busy city carries
 * dozens and a long table buries the map under it.
 * ------------------------------------------------------------------------ */
.copy-table {
  width: 100%;
  border-collapse: collapse;
}

.copy-table th,
.copy-table td {
  padding: var(--space-3) var(--space-4) var(--space-3) 0;
  border-bottom: var(--space-px) solid var(--color-border-subtle);
  text-align: left;
  vertical-align: baseline;
}

.copy-table th {
  border-bottom-color: var(--color-border);
  color: var(--color-text-subtle);
  font-family: var(--font-label);
  font-size: var(--text-eyebrow);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
}

.copy-table td {
  color: var(--color-text);
  font-family: var(--font-label);
  font-size: var(--text-trail-meta);
  letter-spacing: var(--tracking-nav);
}

.copy-table__number {
  text-transform: uppercase;
}

/* Dates and counts are scanned down the column, so they line up. */
.copy-table__cell {
  font-variant-numeric: tabular-nums;
}

.copy-table__pager {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: center;
  margin-top: var(--space-4);
}

.pager-btn {
  padding: var(--space-2) var(--space-4);
  border: var(--space-px) solid var(--color-border);
  border-radius: var(--radius-full);
  color: var(--color-text);
  font-family: var(--font-label);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-nav);
  text-decoration: none;
  text-transform: uppercase;
}

.pager-btn:hover {
  border-color: var(--color-border-accent);
  color: var(--color-accent);
}

/* The end of the list: shown rather than hidden, so the row does not
 * reflow as a reader pages through it. */
.pager-btn--off {
  border-color: var(--color-border-subtle);
  color: var(--color-text-subtle);
}

.pager-status {
  color: var(--color-text-subtle);
  font-family: var(--font-label);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-nav);
  text-transform: uppercase;
}
