/* China 2026 — stylesheet.
 *
 * Mobile-first, no external fonts or CDN: everything ships with the files so
 * the page works offline and behind the firewall. Colours live in custom
 * properties so the dark theme is a single block of overrides.
 *
 * Theme resolution order:
 *   1. :root                                  -> light defaults
 *   2. @media (prefers-color-scheme: dark)    -> follows the OS
 *   3. [data-theme="light"|"dark"] on <html>  -> manual override, wins
 */

/* Palette: warm paper, cream cards, coral accent, olive for "done". The dark
 * theme keeps the same warmth instead of switching to a cold blue-grey. */

:root {
  color-scheme: light dark;

  --bg: #f4efe6;
  --bg-deep: #e9dfd0;
  --surface: #fffaf2;
  --text: #292824;
  --muted: #6f695c;
  --accent: #c76545;
  --accent-strong: #9f452c;
  --accent-soft: #f5e5de;
  --accent-text: #ffffff;
  --border: #d8cebf;
  --ok: #5f6a45;
  --ok-soft: #e5eadb;
  --warn: #946a1e;
  --warn-soft: #f3ead5;
  --danger: #b3302a;
  --blue: #6a8a95;
  --sage: #aeb79b;
  --note-bg: #edf0e7;
  --note-text: #50563e;
  --nav-bg: rgba(43, 42, 37, 0.94);

  --serif: Georgia, "Times New Roman", "Songti SC", "Noto Serif SC", serif;
  --radius: 1rem;
  --radius-lg: 1.4rem;
  --tap: 44px;
  --shadow: 0 1px 2px rgba(76, 58, 39, 0.05);
  --shadow-lg: 0 24px 80px rgba(76, 58, 39, 0.11);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1c1a17;
    --bg-deep: #2e2a25;
    --surface: #262320;
    --text: #efe8dc;
    --muted: #a9a192;
    --accent: #e38a6b;
    --accent-strong: #f0a488;
    --accent-soft: #3d2a22;
    --accent-text: #1c1a17;
    --border: #3d3831;
    --ok: #aab88a;
    --ok-soft: #2d3226;
    --warn: #dcae62;
    --warn-soft: #3a3121;
    --danger: #f27a66;
    --blue: #8fb0ba;
    --sage: #7f8a6b;
    --note-bg: #2a2e24;
    --note-text: #cdd5bb;
    --nav-bg: rgba(14, 13, 11, 0.94);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
    --shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.45);
  }
}

/* Manual override: repeated verbatim so it beats the media query regardless of
 * the OS setting. */
html[data-theme="light"] {
  color-scheme: light;

  --bg: #f4efe6;
  --bg-deep: #e9dfd0;
  --surface: #fffaf2;
  --text: #292824;
  --muted: #6f695c;
  --accent: #c76545;
  --accent-strong: #9f452c;
  --accent-soft: #f5e5de;
  --accent-text: #ffffff;
  --border: #d8cebf;
  --ok: #5f6a45;
  --ok-soft: #e5eadb;
  --warn: #946a1e;
  --warn-soft: #f3ead5;
  --danger: #b3302a;
  --blue: #6a8a95;
  --sage: #aeb79b;
  --note-bg: #edf0e7;
  --note-text: #50563e;
  --nav-bg: rgba(43, 42, 37, 0.94);
  --shadow: 0 1px 2px rgba(76, 58, 39, 0.05);
  --shadow-lg: 0 24px 80px rgba(76, 58, 39, 0.11);
}

html[data-theme="dark"] {
  color-scheme: dark;

  --bg: #1c1a17;
  --bg-deep: #2e2a25;
  --surface: #262320;
  --text: #efe8dc;
  --muted: #a9a192;
  --accent: #e38a6b;
  --accent-strong: #f0a488;
  --accent-soft: #3d2a22;
  --accent-text: #1c1a17;
  --border: #3d3831;
  --ok: #aab88a;
  --ok-soft: #2d3226;
  --warn: #dcae62;
  --warn-soft: #3a3121;
  --danger: #f27a66;
  --blue: #8fb0ba;
  --sage: #7f8a6b;
  --note-bg: #2a2e24;
  --note-text: #cdd5bb;
  --nav-bg: rgba(14, 13, 11, 0.94);
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.45);
}

/* ---------- base ---------- */

* {
  box-sizing: border-box;
}

html {
  /* Keeps the manual theme from flashing the wrong colour on the scrollbar. */
  background: var(--bg);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", "PingFang SC",
    "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  font-size: 16px;
  line-height: 1.5;
  /* Long Chinese station names and phrases must never widen the page. */
  overflow-wrap: break-word;
}

h1,
h2,
h3 {
  line-height: 1.25;
  margin: 0 0 0.5rem;
}

h1 {
  font-size: 1.25rem;
}

h2 {
  font-size: 1.15rem;
}

h3 {
  font-size: 1rem;
}

p {
  margin: 0 0 0.75rem;
}

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

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---------- layout ---------- */

.container,
.header-bar,
.tabs {
  max-width: 900px;
  margin: 0 auto;
  padding-left: 1rem;
  padding-right: 1rem;
}

.container {
  padding-top: 1rem;
  padding-bottom: 3rem;
}

/* ---------- failure bar ---------- */

/* Sits above the sticky header (z-index 3 against its 2) and stays in view, so
 * a failure is visible from whichever tab the user is on. */
.failure-bar {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.75rem;
  padding: 0.7rem 1rem;
  background: var(--danger);
  color: #ffffff;
  line-height: 1.4;
}

.failure-bar a {
  color: #ffffff;
  text-decoration: underline;
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
}

/* ---------- header ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.header-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-height: var(--tap);
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

.countdown {
  max-width: 900px;
  margin: 0 auto;
  padding: 0 1rem 0.5rem;
  color: var(--muted);
  font-size: 0.9rem;
}

.countdown:empty {
  display: none;
}

/* Colour is the whole point of the clock: it has to be readable as urgency
 * from across the room, not just as text. */
.countdown.urgent {
  color: var(--warn);
}

.countdown.critical {
  color: var(--danger);
}

.countdown.done {
  color: var(--ok);
}

.countdown-left {
  white-space: nowrap;
}

/* ---------- to-do overview (Boekingen) ---------- */

.todo-overview {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
}

.todo-overview h3 {
  margin-top: 0;
}

.todo-bucket {
  margin-top: 0.9rem;
}

/* The group title carries the urgency colour, so 'Nu' reads as an alarm and
 * 'Kan later' as reassurance before a single row is read. */
.todo-bucket-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.6rem;
  padding-bottom: 0.3rem;
  border-bottom: 2px solid var(--border);
}

.todo-bucket-title {
  margin: 0;
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.todo-bucket-now .todo-bucket-head {
  border-color: var(--danger);
}

.todo-bucket-now .todo-bucket-title {
  color: var(--danger);
}

.todo-bucket-soon .todo-bucket-head {
  border-color: var(--warn);
}

.todo-bucket-soon .todo-bucket-title {
  color: var(--warn);
}

.todo-bucket-later .todo-bucket-title,
.todo-bucket-onsite .todo-bucket-title {
  color: var(--muted);
}

.todo-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Each row is a button to its card, styled as a list row rather than a
 * button: full width, no frame, a hairline between rows. */
.todo-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.6rem;
  width: 100%;
  min-height: var(--tap);
  padding: 0.45rem 0;
  background: none;
  border: 0;
  border-top: 1px solid var(--border);
  border-radius: 0;
  text-align: left;
  color: var(--text);
}

.todo-list li:first-child .todo-row {
  border-top: 0;
}

.todo-row:hover,
.todo-row:focus-visible {
  border-color: var(--border);
  color: var(--accent-strong);
}

.todo-row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.todo-left {
  flex: 0 0 auto;
  min-width: 9rem;
  font-weight: 600;
  white-space: nowrap;
}

.todo-row.urgent .todo-left {
  color: var(--warn);
}

.todo-row.critical .todo-left {
  color: var(--danger);
}

.todo-main {
  flex: 1 1 12rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

/* The card a row jumps to sits under the sticky header; leave room for it,
 * then blink the edge once so the eye lands on the right card. */
.booking {
  scroll-margin-top: 5.5rem;
}

.booking.flash {
  animation: booking-flash 1.6s ease-out;
}

@keyframes booking-flash {
  0%, 40% {
    box-shadow: 0 0 0 3px var(--accent);
  }
  100% {
    box-shadow: var(--shadow);
  }
}

/* On a phone the countdown gets its own line so it never wraps mid-figure. */
@media (max-width: 480px) {
  .todo-left {
    flex-basis: 100%;
  }
}

/* ---------- navigation ---------- */

.tabs {
  display: flex;
  gap: 0.25rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  /* On a phone the six tabs do not fit; scroll them sideways instead of
   * letting them widen the page. */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

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

.tabs a {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 0.75rem;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 3px solid transparent;
}

.tabs a[aria-current="page"] {
  color: var(--text);
  font-weight: 600;
  border-bottom-color: var(--accent);
}

/* ---------- controls ---------- */

button,
select,
input,
textarea {
  font: inherit;
  color: inherit;
}

button {
  min-height: var(--tap);
  padding: 0 0.9rem;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
}

button:hover {
  border-color: var(--accent);
}

button.primary {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}

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

input[type="text"],
input[type="search"],
input[type="number"],
select,
textarea {
  width: 100%;
  min-height: var(--tap);
  padding: 0.5rem 0.65rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

textarea {
  min-height: 6rem;
  resize: vertical;
}

label {
  display: block;
  margin-bottom: 0.35rem;
  color: var(--muted);
  font-size: 0.9rem;
}

/* Checkable rows: the whole row is the tap target, not just the box. */
.check {
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
  min-height: var(--tap);
  padding: 0.4rem 0;
  cursor: pointer;
}

.check input[type="checkbox"] {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  margin-top: 0.35rem;
  accent-color: var(--accent);
}

.check.done > .check-label {
  color: var(--muted);
  text-decoration: line-through;
}

/* ---------- cards ---------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0.9rem 1rem;
  margin-bottom: 0.75rem;
}

.card > :last-child {
  margin-bottom: 0;
}

.card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.muted {
  color: var(--muted);
}

.small {
  font-size: 0.875rem;
}

/* Chinese characters need a slightly larger size to stay legible when shown
 * to a driver or a ticket clerk. */
.zh {
  font-size: 1.15em;
  line-height: 1.4;
}

.badge {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.8rem;
  white-space: nowrap;
}

.badge.ok {
  color: var(--ok);
  border-color: var(--ok);
}

.badge.warn {
  color: var(--warn);
  border-color: var(--warn);
}

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

.note {
  border-left: 3px solid var(--accent);
  padding-left: 0.75rem;
  color: var(--muted);
}

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 1rem;
}

ul.plain {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* ---------- schedule ---------- */

/* The city filter, above the day list. Deliberately not sticky: the header is
 * already sticky and its height changes (the countdown line appears in it), so
 * a second sticky bar underneath it would need a hard-coded offset that is
 * wrong the moment the header grows -- and on a phone this row wraps to several
 * lines, which would eat most of the screen on every scroll. */
.city-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.75rem;
}

/* A dozen secondary controls next to the day cards, so they read smaller than a
 * normal button -- but they are still buttons you hit with a thumb while
 * walking, so they get the full --tap height rather than the 36px that looked
 * right on a desktop and measured too small on the phone. */
.filter-chip {
  min-height: var(--tap);
  padding: 0 0.85rem;
  border-radius: 999px;
  font-size: 0.875rem;
  white-space: nowrap;
}

.filter-chip.active {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}

.day {
  padding: 0;
}

.day-summary {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.6rem;
  min-height: var(--tap);
  padding: 0.75rem 1rem;
  cursor: pointer;
  list-style: none;
}

/* Safari draws its own disclosure triangle through a pseudo element; the
 * arrow below replaces it so both engines look the same. */
.day-summary::-webkit-details-marker {
  display: none;
}

/* Absolutely placed rather than flowed: .day-title wraps to a full-width line,
 * which would otherwise push a margin-left:auto arrow down to the bottom of the
 * card, far away from the summary line it belongs to. */
.day-summary::after {
  content: "\25be";
  position: absolute;
  top: 0.75rem;
  right: 1rem;
  color: var(--muted);
  transition: transform 0.15s ease;
}

.day[open] > .day-summary::after {
  transform: rotate(180deg);
}

.day-date {
  flex: 0 0 auto;
  min-width: 4.5rem;
  font-weight: 600;
}

.day-city {
  color: var(--accent);
  font-size: 0.9rem;
}

/* Sits before .day-title, which wraps to its own line, so the count always
 * ends up on the first line next to the date. */
.day-count {
  flex: 0 0 auto;
  margin-left: auto;
  /* Clears the disclosure arrow that .day-summary::after puts in this corner. */
  margin-right: 1.25rem;
  padding: 0.05rem 0.45rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--muted);
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}

.day-count-full {
  border-color: var(--ok);
  color: var(--ok);
}

.day-title {
  flex: 1 1 100%;
  color: var(--muted);
  font-size: 0.9rem;
}

.day-body {
  padding: 0 1rem 1rem;
  border-top: 1px solid var(--border);
}

.day-intro {
  margin: 0.75rem 0 0.5rem;
}

/* ---------- schedule items ---------- */

.items {
  display: flex;
  flex-direction: column;
}

.item {
  display: flex;
  gap: 0.75rem;
  padding: 0.6rem 0;
  border-top: 1px solid var(--border);
}

.item:first-child {
  border-top: 0;
}

.item-time {
  flex: 0 0 3.25rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  /* Leaves room for the coloured kind marker without an extra element. */
  border-left: 3px solid var(--border);
  padding-left: 0.5rem;
}

.item-body {
  flex: 1 1 auto;
  min-width: 0;
}

/* The title is a <label> for the item checkbox, so it gets a label's
 * affordance: block-level, so the whole line is a tap target, and a pointer. */
.item-title {
  display: block;
  cursor: pointer;
  font-weight: 600;
}

.kind-transport > .item-time {
  border-left-color: var(--accent);
}

.kind-activity > .item-time {
  border-left-color: var(--ok);
}

.kind-food > .item-time {
  border-left-color: var(--warn);
}

.kind-logistics > .item-time {
  border-left-color: var(--muted);
}

.kind-rest > .item-time {
  border-left-color: var(--border);
}

/* Shown to a taxi driver or a ticket clerk: big enough to read from across a
 * counter, and one tap selects the whole string for copying into a maps app. */
.address {
  display: block;
  margin-top: 0.2rem;
  font-size: 1.25rem;
  user-select: all;
  -webkit-user-select: all;
}

.item-cost {
  margin-top: 0.2rem;
  color: var(--muted);
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}

.item-note {
  margin: 0.35rem 0 0;
}

/* The box sits between the time column and the content so ticking it is a
 * straight vertical run down the day.
 *
 * Drawn by hand rather than left to the browser: a native checkbox is a
 * replaced element, so it drops any padding we give it and stays a 20px target
 * that is genuinely hard to hit on a moving train. With appearance:none the
 * padding counts, which lets the tappable box reach --tap while the visible
 * square stays 1.5rem. The negative margin pulls that padding back over the
 * whitespace next to the row so nothing shifts. */
.item-check {
  appearance: none;
  -webkit-appearance: none;
  /* content-box on purpose: the global border-box rule would pull the padding
   * inwards and shrink the visible square instead of growing the tap area. */
  box-sizing: content-box;
  flex: 0 0 auto;
  /* Two knobs. --check-size is the filled square inside; --check-box is the
   * square the border draws, which is also the tap target and is --tap by
   * default. A dense list can shrink either; the label stays clickable, so a
   * smaller box is still an easy hit. */
  width: var(--check-size, 1.5rem);
  height: var(--check-size, 1.5rem);
  /* content + 2px border on each side, padded out to --check-box in total. */
  padding: calc((var(--check-box, var(--tap)) - var(--check-size, 1.5rem) - 4px) / 2);
  /* Pull the padding back over the whitespace beside the row: the same amount
   * that was padded on, so it follows --check-box, not --tap. */
  margin: calc((var(--check-size, 1.5rem) + 4px - var(--check-box, var(--tap))) / 2);
  margin-top: calc((var(--check-size, 1.5rem) + 4px - var(--check-box, var(--tap))) / 2 + 0.15rem);
  border: 2px solid var(--muted);
  border-radius: 5px;
  background: var(--surface);
  background-clip: content-box;
  cursor: pointer;
}

.item-check:checked {
  border-color: var(--accent);
  background-color: var(--accent);
}

/* The tick itself: a rotated rectangle with two borders, so it needs no image
 * and no font. Positioned against the content box, which is the visible square
 * rather than the padded tap area. */
.item-check:checked::before {
  content: "";
  display: block;
  width: 0.32rem;
  height: 0.62rem;
  margin: 0.12rem auto 0;
  border: solid var(--accent-text);
  border-width: 0 2.5px 2.5px 0;
  transform: rotate(45deg);
}

.item-check:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Only the title is struck through. The address, the cost and the note stay
 * fully legible: a done item is still the thing you show the driver. */
.item.done > .item-body > .item-title {
  color: var(--muted);
  text-decoration: line-through;
}

.item.done {
  opacity: 0.72;
}

/* ---------- day notes ---------- */

.day-notes {
  margin-top: 1rem;
  padding: 0.75rem 0.9rem;
  background: var(--bg);
  border: 1px solid var(--warn);
  border-radius: var(--radius);
}

.day-notes-title {
  margin: 0 0 0.4rem;
  color: var(--warn);
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.day-note {
  position: relative;
  padding-left: 1.1rem;
  margin-bottom: 0.35rem;
  font-size: 0.9rem;
}

.day-note:last-child {
  margin-bottom: 0;
}

.day-note::before {
  content: "!";
  position: absolute;
  left: 0;
  top: 0;
  color: var(--warn);
  font-weight: 700;
}

/* ---------- checklists ---------- */

.checklist-intro {
  margin: 0.35rem 0 0.75rem;
}

/* The progress bar sits in the card header, next to the list title. Allowed to
 * shrink: at 320px a rigid bar plus a long list title pushed the header a few
 * pixels past the screen edge. */
.progress {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 0 1 auto;
  min-width: 0;
}

.progress-text {
  color: var(--muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.progress-track {
  width: 5.5rem;
  /* The bar is decoration next to the count, so it gives up width first. */
  min-width: 2.5rem;
  flex: 0 1 auto;
  height: 0.5rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
}

.progress-fill {
  width: 0;
  height: 100%;
  background: var(--accent);
  /* Grows from the left whatever the writing direction, so the bar still
   * fills towards the end of the list and not away from it. */
  transition: width 0.15s ease-out;
}

.progress-full .progress-fill {
  background: var(--ok);
}

.check-item {
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
  padding: 0.55rem 0;
  border-top: 1px solid var(--border);
}

.check-item:first-child {
  border-top: 0;
}

.check-body {
  flex: 1 1 auto;
  min-width: 0;
}

.check-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem;
}

/* Overrides the global label rule: inside a row the text is the content, not
 * a caption above a field. */
.check-label {
  flex: 1 1 12rem;
  margin: 0;
  color: var(--text);
  font-size: 1rem;
  cursor: pointer;
}

.check-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

.check-detail {
  margin: 0.3rem 0 0;
}

/* Only the text is struck through. The detail stays fully legible: a packed
 * item is still the thing you check the requirements of. */
.check-item.done > .check-body > .check-head > .check-label {
  color: var(--muted);
  text-decoration: line-through;
}

.check-item.done {
  opacity: 0.72;
}

/* The checklist boxes report a shared status instead of recording a personal
 * one, so they are disabled. They must still read as a filled-in checklist
 * rather than as a broken control: full contrast, no greying out, and a
 * default cursor to say "not yours to click" without shouting about it. */
.checklist .item-check:disabled {
  opacity: 1;
  cursor: default;
}

/* ---------- bookings ---------- */

.booking-summary {
  margin: 0 0 1rem;
  font-weight: 600;
}

.booking-summary.danger {
  color: var(--danger);
}

.booking-group {
  margin-bottom: 1rem;
}

/* The whole header line is the tap target, with the same arrow as the day
 * cards in the schedule. */
.booking-group-summary {
  position: relative;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  min-height: var(--tap);
  padding: 0.6rem 2rem 0.6rem 0;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  list-style: none;
}

.booking-group-summary::-webkit-details-marker {
  display: none;
}

.booking-group-summary::after {
  content: "\25be";
  position: absolute;
  top: 0.6rem;
  right: 0.25rem;
  color: var(--muted);
  transition: transform 0.15s ease;
}

.booking-group[open] > .booking-group-summary::after {
  transform: rotate(180deg);
}

.booking-group-summary h3 {
  margin: 0;
}

.booking-group-body {
  padding-top: 0.5rem;
}

/* A left edge in the status colour: it survives being scanned at arm's length,
 * which the badge alone does not. */
.booking {
  border-left: 4px solid var(--border);
}

.booking[data-status="todo"] {
  border-left-color: var(--danger);
}

.booking[data-status="booked"] {
  border-left-color: var(--warn);
}

.booking[data-status="confirmed"] {
  border-left-color: var(--ok);
}

.booking[data-status="confirmed"] .booking-what {
  /* Once it is confirmed the instructions are history: keep them reachable,
   * stop them from competing with the entries that still need work. */
  opacity: 0.7;
}

.booking-header {
  flex-wrap: wrap;
  align-items: flex-start;
}

.booking-title {
  margin: 0;
  flex: 1 1 14rem;
  font-size: 1rem;
}

.booking-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

.booking-facts {
  margin: 0.6rem 0 0;
  display: grid;
  gap: 0.2rem;
}

.booking-fact {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.booking-fact.danger {
  color: var(--danger);
}

.booking-fact-key {
  flex: 0 0 5.5rem;
  color: var(--muted);
}

/* The booking URLs are long and must stay copyable in full, so they wrap
 * instead of stretching the card sideways on a phone. */
.booking-fact a {
  overflow-wrap: anywhere;
}

.booking-what {
  margin: 0.6rem 0 0;
  color: var(--muted);
}

.booking-options {
  margin-top: 0.6rem;
}

.booking-option {
  padding: 0.3rem 0;
}

.booking-option-price {
  font-weight: 600;
}

.booking-controls {
  display: grid;
  gap: 0.6rem;
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}

.booking-field label {
  margin-bottom: 0.2rem;
}

.booking-note {
  min-height: 3.5rem;
}

/* ---------- phrasebook ---------- */

/* Sticky so the box stays reachable while scrolling a long section: the whole
 * point is typing a word and reading the hits without scrolling back up. */
.phrase-searchbar {
  position: sticky;
  top: 0;
  z-index: 2;
}

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

.phrase-status {
  margin: 0.4rem 0 0;
}

.phrase-summary {
  font-weight: 600;
}

.phrase-title {
  flex: 1 1 auto;
  /* Clears the disclosure arrow .day-summary::after puts in the top corner. */
  padding-right: 1.25rem;
}

.phrase-count {
  margin-left: 0;
}

.phrase-body {
  padding: 0 1rem 0.9rem;
  overflow-x: auto;
}

/* Fixed layout, not auto: with auto the widest single sentence in a section
 * sets the column width for all 85 rows, which pushed the table past the screen
 * and made every row scroll sideways. Fixed takes the widths below and wraps
 * the long rows instead. */
.phrase-table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}

.phrase-table th,
.phrase-table td {
  padding: 0.45rem 0.5rem 0.45rem 0;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}

.phrase-table th {
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.phrase-table tr:last-child td {
  border-bottom: 0;
}

/* Shown to a waiter or a driver, so it is the largest column and one tap
 * selects the whole string for a translator or a maps app.
 *
 * Deliberately allowed to wrap: the longer rows are full sentences, and
 * keeping those on one line pushed the table into sideways scrolling on a
 * phone. Chinese breaks cleanly between characters, so a wrap costs nothing.
 */
.phrase-zh {
  font-size: 1.25rem;
  user-select: all;
  -webkit-user-select: all;
}

/* Column widths for the fixed layout above. The characters get the widest
 * share: that is the column actually held up to a waiter, while the Dutch and
 * the pronunciation hint read fine wrapped over a few lines. */
.phrase-table th:first-child,
.phrase-nl {
  width: 36%;
}

.phrase-table th:nth-child(2) {
  width: 36%;
}

.phrase-table th:last-child,
.phrase-sound {
  width: 28%;
}

/* Long sentences have no spaces to break at in the Dutch column either. */
.phrase-nl,
.phrase-sound {
  overflow-wrap: break-word;
}

.phrase-sound {
  color: var(--muted);
  font-size: 0.9rem;
}

.phrase-pinyin {
  display: block;
}

/* The filter sets .hidden on rows it wants gone; a display on the element
 * itself would beat the attribute, so it is spelled out for table rows. */
.phrase-row[hidden] {
  display: none;
}

/* ---------- show-and-point cards ---------- */

.card-deck h3 {
  margin: 0 0 0.35rem;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  gap: 0.6rem;
  margin-top: 0.9rem;
}

.card-tile {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-height: var(--tap);
  padding: 0.7rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.card-tile:hover {
  border-color: var(--accent);
}

.card-tile-title {
  font-weight: 600;
  line-height: 1.25;
}

/* The preview is a hint of what is on the card, not something to read out, so
 * it is clipped to two lines rather than allowed to stretch the tile. */
.card-tile-zh {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  color: var(--muted);
  font-size: 0.95rem;
}

.card-tile-fill {
  color: var(--accent);
}

/* ---------- the fullscreen card viewer ---------- */

/* A <dialog> in the top layer, stretched to the whole viewport: on a phone the
 * card is all there is on screen while it is being shown to someone. */
.card-view {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--surface);
  color: var(--text);
}

.card-view::backdrop {
  background: rgba(0, 0, 0, 0.75);
}

.card-view-panel {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 0.75rem 1rem 1rem;
  gap: 0.6rem;
}

.card-view-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex: 0 0 auto;
}

.card-view-title {
  margin: 0;
  font-size: 1rem;
  color: var(--muted);
  font-weight: 600;
}

.card-view-close {
  min-height: var(--tap);
  padding: 0 1.1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--accent-text);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

/* The characters take whatever room is left and stay centred in it. The font
 * size itself is measured and set by fitCard() in lang.js: line count alone
 * cannot predict the height because the long lines wrap. Scrolling a card
 * while holding it up to a driver is what this view exists to avoid. */
.card-view-zh {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.5rem;
  min-height: 0;
  overflow: auto;
  text-align: center;
  user-select: all;
}

.card-view-line {
  margin: 0;
  /* Set by the viewer once it has measured the box; the value here is only
   * the starting point it shrinks down from. */
  font-size: var(--card-font, 3.2rem);
  line-height: 1.3;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.card-view-fill {
  flex: 0 0 auto;
}

.card-fill {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.card-fill-label {
  color: var(--muted);
}

.card-fill-input {
  width: 100%;
  min-height: var(--tap);
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 1.05rem;
}

.card-view-nl {
  flex: 0 0 auto;
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.4;
}

/* ---------- expenses ---------- */

.spend-rate {
  margin: 0 0 0.75rem;
}

.spend-group h3,
.spend-totals h3,
.convert h3 {
  margin: 0 0 0.3rem;
}

.spend-group p {
  margin: 0 0 0.6rem;
}

/* The table has five columns and the narrowest phone cannot show them all at a
 * readable size. Scrolling the table sideways beats shrinking the type. */
.spend-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

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

.spend-table th,
.spend-table td {
  padding: 0.45rem 0.5rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}

.spend-table th {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}

.spend-what {
  min-width: 11rem;
}

.spend-title {
  display: block;
}

.spend-context {
  display: block;
  color: var(--muted);
}

.spend-basis {
  color: var(--muted);
  white-space: nowrap;
}

/* Amounts line up under each other, so a column of them can be scanned. */
.spend-amount {
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.spend-eur {
  color: var(--muted);
}

.spend-subtotal td {
  border-top: 1px solid var(--border);
  border-bottom: none;
  font-weight: 600;
}

.spend-paid {
  width: 6.5rem;
}

.spend-input {
  width: 100%;
  min-width: 5rem;
  min-height: var(--tap);
  padding: 0.35rem 0.5rem;
  font: inherit;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.spend-total-list {
  margin: 0;
}

.spend-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border);
}

.spend-total dt,
.spend-total dd {
  margin: 0;
}

.spend-total-value {
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* The converter is the most-used part of this tab on the road, so it gets the
 * accent border rather than blending into the stack of tables. */
.convert {
  border-color: var(--accent);
}

.convert-input {
  display: block;
  width: 100%;
  min-height: var(--tap);
  margin-top: 0.2rem;
  padding: 0.4rem 0.6rem;
  font: inherit;
  font-size: 1.125rem;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.convert-output {
  margin: 0.5rem 0 0;
  min-height: 1.6rem;
  font-size: 1.25rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* ---------- backup tab ---------- */

.backup-intro {
  margin-bottom: 0.75rem;
}

.backup-block h3 {
  margin-bottom: 0.4rem;
  font-size: 1.05rem;
}

.backup-block p {
  margin-bottom: 0.6rem;
}

/* The reset card is the one destructive control in the app, so it is marked as
 * such before the confirm dialog has to do the work. */
.backup-danger {
  border-color: var(--danger);
}

.backup-danger h3 {
  color: var(--danger);
}

.backup-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.6rem 0 0;
}

/* A file input cannot be given the same box as the text inputs without hiding
 * the browser's own button, so it only gets the tap height and the spacing. */
.backup-file {
  display: block;
  width: 100%;
  min-height: var(--tap);
  padding: 0.5rem 0;
}

.backup-paste-label {
  margin-top: 0.6rem;
}

.backup-paste,
.backup-json {
  width: 100%;
  /* Monospace so a stray character in a pasted state is visible; the stack is
   * all system fonts, nothing is downloaded. */
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8rem;
  line-height: 1.45;
  white-space: pre;
  overflow-wrap: normal;
}

.backup-json {
  color: var(--muted);
}

.backup-status {
  margin: 0.5rem 0 0;
  min-height: 1.3rem;
  color: var(--muted);
}

.backup-status.good {
  color: var(--ok);
}

.backup-status.bad {
  color: var(--danger);
}

.backup-steps {
  margin: 0 0 0.6rem;
  padding-left: 1.4rem;
}

.backup-steps li {
  margin-bottom: 0.45rem;
}

.backup-steps li:last-child {
  margin-bottom: 0;
}

/* ---------- responsive ---------- */

@media (min-width: 720px) {
  h1 {
    font-size: 1.5rem;
  }

  .tabs {
    overflow-x: visible;
    gap: 0.5rem;
  }

  .tabs a {
    padding: 0 1.1rem;
  }

  .card {
    padding: 1.1rem 1.25rem;
  }

  .container {
    padding-top: 1.5rem;
  }

  .day-title {
    flex: 1 1 auto;
  }

  .item-time {
    flex-basis: 4rem;
  }

  .booking-controls {
    grid-template-columns: 12rem 1fr;
    align-items: start;
  }

  /* On a laptop the viewer does not need the whole window; a centred sheet
   * keeps the characters at a comfortable reading distance. */
  .card-view {
    width: min(46rem, 92vw);
    height: min(38rem, 88vh);
    margin: auto;
    border-radius: var(--radius);
  }

  .card-view-panel {
    padding: 1rem 1.5rem 1.5rem;
  }
}

/* ---------- route ---------- */

.route-intro {
  margin-bottom: 1rem;
}

.route-subtitle {
  margin: 0 0 0.25rem;
  font-weight: 600;
}

.route-intro p {
  margin-top: 0;
}

/* The stops and the legs between them read as one vertical line: the leg rows
 * are indented under an accent-coloured rail that the stop cards interrupt. */
.stop {
  padding: 0;
  overflow: hidden;
}

/* The gallery sets its own display, which would beat the browser's
 * [hidden] rule; hidden must win while the card is collapsed. */
.stop-more[hidden] {
  display: none;
}

.stop-gallery {
  display: flex;
  gap: 0.5rem;
  margin: 0.75rem -1rem 0;
  padding: 0.5rem;
  border-top: 1px solid var(--border);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x mandatory;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}

.photo {
  flex: 0 0 min(80%, 320px);
  margin: 0;
  scroll-snap-align: start;
}

.photo img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: 6px;
  background: var(--border);
  cursor: zoom-in;
}

.photo figcaption {
  margin-top: 0.3rem;
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--muted);
}

.photo-caption {
  color: var(--text);
}

.photo-credit a {
  color: inherit;
}

.stop-body {
  padding: 0.8rem 1rem 1rem;
}

.stop-head {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  min-height: var(--tap);
  padding-right: 1.5rem;
  cursor: pointer;
}

.stop-head:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 6px;
}

.stop-chevron {
  position: absolute;
  top: 0.2rem;
  right: 0;
  color: var(--muted);
  transition: transform 0.15s ease;
}

.stop-open .stop-chevron {
  transform: rotate(180deg);
}

.route-toolbar {
  margin-bottom: 0.75rem;
}

.dropped-section {
  margin-top: 2rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

.dropped-title {
  margin-bottom: 0.25rem;
}

.stop-number-dropped {
  background: var(--muted);
}

.dropped-costs {
  margin: 0;
  padding-left: 1.2rem;
}

.dropped-costs li {
  margin-bottom: 0.3rem;
}

.stop-number {
  flex: 0 0 auto;
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-text);
  font-weight: 700;
  font-size: 0.9rem;
  line-height: 1.8rem;
  text-align: center;
}

.stop-title h3 {
  margin: 0;
  font-size: 1.2rem;
}

.stop-zh {
  margin-left: 0.5rem;
  font-weight: 400;
  color: var(--muted);
}

.stop-dates {
  font-size: 0.9rem;
}

.stop-blurb {
  margin: 0.75rem 0 0;
}

.stop-alert {
  position: relative;
  margin: 0.6rem 0 0;
  padding: 0.5rem 0.75rem 0.5rem 1.6rem;
  border: 1px solid var(--warn);
  border-radius: var(--radius);
  font-size: 0.9rem;
}

.stop-alert::before {
  content: "!";
  position: absolute;
  left: 0.7rem;
  top: 0.5rem;
  color: var(--warn);
  font-weight: 700;
}

.stop-days {
  margin-top: 0.6rem;
  border-top: 1px solid var(--border);
}

.stop-day {
  border-bottom: 1px solid var(--border);
}

/* Same disclosure treatment as the day cards in the schedule: no native
 * marker, one arrow on the right that turns when the day is open. */
.stop-day-summary {
  position: relative;
  display: flex;
  gap: 0.6rem;
  align-items: baseline;
  min-height: var(--tap);
  padding: 0.55rem 1.5rem 0.55rem 0;
  cursor: pointer;
  list-style: none;
}

.stop-day-summary::-webkit-details-marker {
  display: none;
}

.stop-day-summary::after {
  content: "\25be";
  position: absolute;
  top: 0.55rem;
  right: 0.25rem;
  color: var(--muted);
  transition: transform 0.15s ease;
}

.stop-day[open] > .stop-day-summary::after {
  transform: rotate(180deg);
}

.stop-day-date {
  flex: 0 0 4.5rem;
  color: var(--muted);
  font-size: 0.9rem;
  white-space: nowrap;
}

.stop-day-text {
  flex: 1 1 auto;
  min-width: 0;
}

.stop-day-leg {
  font-variant-numeric: tabular-nums;
}

.stop-day-body {
  padding: 0 0 0.75rem 0;
}

/* Inside a day row the gallery is a notch smaller than the stop gallery and
 * keeps to the row's own width. */
.day-gallery {
  margin: 0 0 0.6rem;
  border-top: 0;
  border-radius: 6px;
}

.day-gallery .photo {
  flex-basis: min(70%, 240px);
}

.stop-day-intro {
  margin: 0 0 0.4rem;
}

.stop-items {
  display: grid;
  gap: 0.3rem;
}

.stop-item {
  display: flex;
  gap: 0.6rem;
  align-items: baseline;
  font-size: 0.95rem;
}

.stop-item-time {
  flex: 0 0 4.5rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.stop-item-body {
  flex: 1 1 auto;
  min-width: 0;
}

.stop-item.kind-transport .stop-item-title {
  font-weight: 600;
}

.stop-item-address {
  color: var(--accent);
  text-decoration: none;
  overflow-wrap: anywhere;
}

.stop-item-address:hover {
  text-decoration: underline;
}

.stop-item-cost {
  font-size: 0.85rem;
}

.stop-day-link {
  color: inherit;
}

.stop-subtitle {
  margin: 0.8rem 0 0.4rem;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.sight-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.sight-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-height: 2.2rem;
  padding: 0.2rem 0.75rem 0.2rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font-size: 0.875rem;
  text-decoration: none;
}

.sight-chip:hover {
  border-color: var(--accent);
}

.sight-pin {
  width: 1rem;
  height: 1rem;
  fill: var(--accent);
  flex: 0 0 auto;
}

.sight-zh {
  font-size: 0.95em;
  color: var(--muted);
}

.stop-hotels {
  margin-top: 0.8rem;
}

.stop-hotel {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.6rem;
  padding: 0.3rem 0;
  font-size: 0.9rem;
}

.stop-hotel-title::before {
  content: 'Hotel: ';
  color: var(--muted);
}

.leg {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  margin: 0 0 0.75rem 0.9rem;
  padding: 0.5rem 0.75rem 0.5rem 0.9rem;
  border-left: 3px solid var(--accent);
}

.leg-linked {
  cursor: pointer;
}

.leg-icon {
  flex: 0 0 auto;
  display: inline-flex;
  width: 2rem;
  height: 2rem;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border);
}

.leg-icon svg {
  width: 1.2rem;
  height: 1.2rem;
  fill: var(--accent);
}

.leg-body {
  flex: 1 1 12rem;
  min-width: 0;
}

.leg-title {
  font-weight: 500;
}

.leg-facts {
  font-variant-numeric: tabular-nums;
}

/* Times and duration in the full text colour and a size up from the rest of
 * the line: readable at arm's length on a platform, the price is not. */
.leg-times {
  color: var(--text);
  font-size: 1rem;
}

.leg-note {
  margin: 0.25rem 0 0;
}

.leg-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  justify-content: flex-end;
}

.leg-badges a {
  text-decoration: none;
}

/* The map link sits next to the address it belongs to; the address itself
 * keeps its select-all behaviour, the link stays a link. */
.address-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 0.75rem;
}

.map-link {
  white-space: nowrap;
}

/* Full-screen photo. A <dialog> gets focus trapping and Escape for free. */
.lightbox {
  max-width: 100vw;
  max-height: 100vh;
  width: 100vw;
  height: 100vh;
  margin: 0;
  padding: 1rem;
  border: 0;
  background: rgba(0, 0, 0, 0.92);
  color: #fff;
  display: none;
  cursor: zoom-out;
}

.lightbox[open] {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
}

.lightbox::backdrop {
  background: rgba(0, 0, 0, 0.6);
}

.lightbox-img {
  max-width: 100%;
  max-height: calc(100vh - 5rem);
  object-fit: contain;
  border-radius: 6px;
}

.lightbox-caption {
  font-size: 0.95rem;
  text-align: center;
}

.lightbox-close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: var(--tap);
  height: var(--tap);
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  font-size: 1.6rem;
  line-height: 1;
}

@media (min-width: 720px) {
  .photo {
    flex-basis: 300px;
  }

  .stop-gallery {
    margin-left: -1.25rem;
    margin-right: -1.25rem;
  }

  .day-gallery {
    margin-left: 0;
    margin-right: 0;
  }

  .day-gallery .photo {
    flex-basis: 240px;
  }

  .leg {
    margin-left: 1.5rem;
  }
}

/* ---------- map ---------- */

.map-wrap {
  margin: 0.75rem 0 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}

.route-map {
  display: block;
  width: 100%;
  height: auto;
  font-family: inherit;
}

.map-sea {
  fill: #dfe9f3;
}

.map-land,
.map-provinces,
.map-river,
.map-leg {
  vector-effect: non-scaling-stroke;
}

.map-land {
  fill: #f1efe6;
  stroke: #b9b3a3;
  stroke-width: 0.8;
}

.map-provinces {
  fill: none;
  stroke: #cfc9b8;
  stroke-width: 0.6;
  stroke-dasharray: 3 2;
}

.map-river {
  fill: none;
  stroke: #8fb4d6;
  stroke-width: 1.4;
}

.map-province-label {
  font-size: 11px;
  fill: #8a8474;
  text-anchor: middle;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.map-leg {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.5;
  stroke-linecap: round;
}

.map-leg-flight {
  stroke-dasharray: 8 5;
}

.map-leg-car {
  stroke-dasharray: 2 4;
}

.map-leg-label rect {
  fill: var(--surface);
  stroke: var(--border);
}

.map-leg-label path {
  fill: var(--accent);
}

.map-leg-label text {
  font-size: 11px;
  font-weight: 600;
  fill: var(--text);
}

.map-edge-label {
  font-size: 11px;
  fill: var(--muted);
}

.map-via {
  fill: var(--surface);
  stroke: var(--muted);
  stroke-width: 1.5;
}

.map-via-label {
  font-size: 11px;
  fill: var(--muted);
}

.map-stop-dot {
  fill: var(--accent);
  stroke: var(--surface);
  stroke-width: 2;
}

.map-stop-number {
  font-size: 12px;
  font-weight: 700;
  fill: var(--accent-text);
}

.map-stop-label {
  font-size: 13px;
  font-weight: 600;
  fill: var(--text);
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: 3;
  stroke-linejoin: round;
}

.map-stop-nights {
  font-size: 11px;
  fill: var(--muted);
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: 3;
  stroke-linejoin: round;
}

/* The map keeps its light palette in dark mode: the labels sit on it with a
 * light halo, and a dark sea would need a second set of land colours. */
html[data-theme="dark"] .map-stop-label,
html[data-theme="dark"] .map-stop-nights,
html[data-theme="dark"] .map-leg-label rect,
html[data-theme="dark"] .map-stop-dot {
  stroke: #ffffff;
}

html[data-theme="dark"] .map-stop-label,
html[data-theme="dark"] .map-leg-label text {
  fill: #14181d;
}

html[data-theme="dark"] .map-leg-label rect {
  fill: #ffffff;
}

@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) .map-stop-label,
  html:not([data-theme="light"]) .map-stop-nights,
  html:not([data-theme="light"]) .map-leg-label rect,
  html:not([data-theme="light"]) .map-stop-dot {
    stroke: #ffffff;
  }

  html:not([data-theme="light"]) .map-stop-label,
  html:not([data-theme="light"]) .map-leg-label text {
    fill: #14181d;
  }

  html:not([data-theme="light"]) .map-leg-label rect {
    fill: #ffffff;
  }
}

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

.map-table th {
  text-align: left;
  font-size: 0.8rem;
  color: var(--muted);
  font-weight: 600;
  padding: 0.3rem 0.5rem;
  border-bottom: 1px solid var(--border);
}

.map-table td {
  padding: 0.4rem 0.5rem;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}

.map-row-stop td {
  background: var(--surface);
}

.map-row-leg td {
  padding-left: 1.25rem;
  color: var(--muted);
}

.map-leg-kind {
  display: inline-block;
  padding: 0 0.4rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.map-nights,
.map-duration {
  white-space: nowrap;
  text-align: right;
}

.map-dates {
  white-space: nowrap;
  color: var(--muted);
}

/* Route variants: the switch, what a variant gains and costs, and the nights
 * side by side. */
.map-variants {
  margin-top: 0.75rem;
}

.map-variant-intro p:first-child {
  margin-top: 0;
}

.map-variant-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0 1.25rem;
}

.map-variant-columns h4 {
  margin: 0.5rem 0 0.25rem;
  font-size: 0.85rem;
  color: var(--muted);
}

.map-variant-list {
  margin: 0;
  padding-left: 1.1rem;
}

.map-variant-list li {
  margin-bottom: 0.25rem;
}

.map-variant-list.is-pro li::marker {
  color: var(--ok);
}

.map-variant-list.is-con li::marker {
  color: var(--warn);
}

.map-compare-wrap {
  overflow-x: auto;
  margin-bottom: 1rem;
}

.map-compare {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}

.map-compare th,
.map-compare td {
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
}

.map-compare th {
  font-size: 0.8rem;
  color: var(--muted);
  font-weight: 600;
}

.map-compare .num {
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.map-compare th.is-selected,
.map-compare td.is-selected {
  background: var(--surface);
  color: var(--text);
  font-weight: 700;
  box-shadow: inset 2px 0 0 var(--accent), inset -2px 0 0 var(--accent);
}

.map-compare-total td {
  border-top: 2px solid var(--border);
}

.map-row-note td {
  padding-top: 0;
  padding-left: 1.25rem;
  border-bottom: 1px solid var(--border);
}

/* ---------- weather ---------- */

.forecast-status {
  margin: 0.25rem 0 0.5rem;
}

.forecast-place {
  border-top: 1px solid var(--border);
}

.forecast-summary {
  min-height: var(--tap);
  padding: 0.6rem 0;
  cursor: pointer;
}

.forecast-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
  margin-bottom: 0.75rem;
}

.forecast-table th {
  text-align: left;
  font-size: 0.75rem;
  color: var(--muted);
  padding: 0.2rem 0.4rem;
  border-bottom: 1px solid var(--border);
}

.forecast-table td {
  padding: 0.25rem 0.4rem;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.forecast-date {
  color: var(--muted);
}

.forecast-temp,
.forecast-rain {
  font-variant-numeric: tabular-nums;
}

/* The days the route is at this place: the rows that matter. */
.forecast-there td {
  background: var(--bg);
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
  font-weight: 600;
}

.forecast-there .forecast-date {
  color: var(--accent);
}

.climate {
  margin-top: 1.5rem;
}

.climate-grid {
  display: grid;
  gap: 0.75rem;
}

.climate-card {
  margin-bottom: 0;
}

.climate-name {
  margin: 0;
  font-size: 1rem;
}

/* The trip as day numbers under a place name, the days there marked. Eleven
 * columns keep 1-22 October on two rows, wide enough to read on a phone. */
.trip-dates {
  margin-top: 0.4rem;
}

.trip-dates-label {
  font-weight: 600;
}

.trip-strip {
  display: grid;
  grid-template-columns: repeat(11, 1fr);
  gap: 3px;
  list-style: none;
  margin: 0.35rem 0 0;
  padding: 0;
}

.trip-day {
  display: block;
  text-align: center;
  font-size: 0.75rem;
  line-height: 1.6rem;
  border-radius: 4px;
  color: var(--muted);
  background: var(--bg);
  font-variant-numeric: tabular-nums;
}

.trip-day-full {
  background: var(--accent);
  color: var(--accent-text);
  font-weight: 600;
}

.trip-day-travel {
  background: color-mix(in srgb, var(--accent) 14%, var(--surface));
  color: var(--accent);
  font-weight: 600;
  box-shadow: inset 0 0 0 1.5px var(--accent);
}

.trip-legend-item {
  display: inline-block;
  margin-right: 1rem;
}

.trip-legend-swatch {
  display: inline-block;
  width: 1.1rem;
  height: 1.1rem;
  line-height: 1.1rem;
  vertical-align: -0.2rem;
}

.climate-facts {
  margin: 0.6rem 0 0;
  display: grid;
  gap: 0.2rem;
}

.climate-facts div {
  display: flex;
  gap: 0.5rem;
}

.climate-facts dt {
  flex: 0 0 5rem;
  color: var(--muted);
}

.climate-facts dd {
  margin: 0;
}

.climate-notes {
  margin: 0.6rem 0 0;
  padding-left: 1.1rem;
  color: var(--muted);
}

.clothing {
  margin-top: 1.5rem;
}

.clothing-grid {
  display: grid;
  gap: 1rem;
}

.clothing-grid h4 {
  margin: 0 0 0.4rem;
}

.clothing-list {
  margin: 0;
  padding-left: 1.1rem;
}

.clothing-list li {
  margin-bottom: 0.4rem;
}

@media (min-width: 720px) {
  .climate-grid {
    grid-template-columns: 1fr 1fr;
  }

  .clothing-grid {
    grid-template-columns: 1fr 1fr;
  }
}

.counts {
  margin-top: 1.5rem;
}

.counts-switch {
  margin: 0.75rem 0;
}

.counts-card {
  margin-bottom: 0;
}

.counts-name {
  margin: 0;
  font-size: 1rem;
}

.counts-note {
  margin: 0.2rem 0 0;
}

.counts-list {
  margin: 0.6rem 0 0;
  padding: 0;
  list-style: none;
}


/* The tally in front of a garment: minus, 3/6, plus, on one line. */
.counts-stepper {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  flex: 0 0 auto;
  margin-top: 0.05rem;
}

.counts-step {
  width: 1.7rem;
  min-width: 1.7rem;
  height: 1.7rem;
  min-height: 1.7rem;
  padding: 0;
  font-size: 1rem;
  line-height: 1;
  border-radius: 5px;
}

.counts-step:disabled {
  opacity: 0.35;
}

.counts-tally {
  min-width: 2.1rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
  color: var(--muted);
}

.counts-item.done .counts-tally {
  color: var(--accent);
  font-weight: 600;
}

/* The shared label rule gives the text a 12rem basis, which wraps it onto a
 * line of its own in a column this narrow. */
.counts-item .check-head {
  flex-wrap: nowrap;
}

.counts-item .check-label {
  flex: 1 1 auto;
}

.counts-actions {
  margin-top: 0.9rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}

/* ---------- city maps ---------- */

.city-map-section {
  margin-top: 1.5rem;
}

.city-map-section h3 {
  margin-bottom: 0.25rem;
}

/* Full width at every size: small street names are read by zooming in (map.js,
 * mapZoom), not by scrolling sideways. */
.zoom-wrap {
  position: relative;
}

.city-map {
  display: block;
  width: 100%;
  height: auto;
}

.zoomable {
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

.zoomable.is-zoomed {
  cursor: grab;
}

.zoomable.is-zoomed:active {
  cursor: grabbing;
}

.map-zoom {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  display: flex;
  gap: 0.35rem;
}

.map-zoom button {
  min-width: var(--tap);
  padding: 0 0.7rem;
  font-weight: 700;
  box-shadow: var(--shadow);
}

.map-zoom button:disabled {
  opacity: 0.5;
  cursor: default;
}

.city-legend-name {
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  font-weight: 700;
  text-align: left;
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}

.city-place circle {
  stroke: #ffffff;
  stroke-width: 3;
}

.city-place-sight circle {
  fill: #b3272d;
}

.city-place-option circle {
  fill: #d9822b;
}

.city-place-hotel circle {
  fill: #1a7f4b;
}

.city-place-transport circle {
  fill: #5c6672;
}

.city-place-metro rect {
  fill: #1f5fa8;
  stroke: #ffffff;
  stroke-width: 3;
}

.city-place-metro .city-place-label {
  fill: #1f5fa8;
}

.city-place-hotel circle {
  stroke-width: 4;
}

.city-place-number {
  font-size: 15px;
  font-weight: 700;
  fill: #ffffff;
}

.city-place-label {
  font-size: 17px;
  font-weight: 700;
  fill: #14181d;
  paint-order: stroke;
  stroke: #ffffff;
  stroke-width: 5;
  stroke-linejoin: round;
}

.city-legend {
  margin: 0.5rem 0 0;
  padding-left: 1.5rem;
  columns: 2;
  column-gap: 1.5rem;
  font-size: 0.9rem;
}

.city-legend li {
  break-inside: avoid;
  padding: 0.15rem 0;
}

.city-legend-sight::marker { color: #b3272d; }
.city-legend-option::marker { color: #d9822b; }
.city-legend-hotel::marker { color: #1a7f4b; }
.city-legend-transport::marker { color: #5c6672; }
.city-legend-metro::marker { color: #1f5fa8; }

@media (max-width: 600px) {
  .city-legend {
    columns: 1;
  }
}

/* ---- packing tab -------------------------------------------------------- */

/* The hide-packed switch: a real toggle, visually distinct from the category
 * chips below it because it changes behaviour (ticked rows disappear), not
 * which categories show. */
.pack-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: var(--tap);
  margin-top: 0.5rem;
  border: none;
  background: none;
  padding: 0;
  font-size: 0.9rem;
  cursor: pointer;
}

.pack-toggle-track {
  display: inline-block;
  width: 2.4rem;
  height: 1.4rem;
  border-radius: 999px;
  background: var(--border);
  position: relative;
  transition: background 0.15s ease;
  flex-shrink: 0;
}

.pack-toggle-knob {
  position: absolute;
  top: 0.15rem;
  left: 0.15rem;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: var(--bg);
  transition: left 0.15s ease;
}

.pack-toggle.on .pack-toggle-track {
  background: var(--accent);
}

.pack-toggle.on .pack-toggle-knob {
  left: 1.15rem;
}

/* Category cards fold shut like the day cards; same replaced marker so both
 * engines draw the same arrow. */
.pack-cat {
  padding: 0;
}

.pack-cat-summary {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.75rem;
  min-height: var(--tap);
  padding: 0.75rem 2rem 0.75rem 1rem;
  cursor: pointer;
  list-style: none;
}

.pack-cat-summary::-webkit-details-marker {
  display: none;
}

.pack-cat-summary::after {
  content: "\25be";
  position: absolute;
  top: 0.75rem;
  right: 1rem;
  color: var(--muted);
  transition: transform 0.15s ease;
}

.pack-cat[open] > .pack-cat-summary::after {
  transform: rotate(180deg);
}

.pack-cat-summary h3 {
  margin: 0;
}

.pack-cat-body {
  padding: 0 1rem 0.75rem;
}

.pack-cat-body .checks,
.pack-cat-body .pack-empty {
  margin: 0;
}

/* The '+' on a category card and the '+' filter chip: square tap targets
 * with an oversized glyph, since a bare plus reads small. */
.pack-add-btn {
  min-height: var(--tap);
  min-width: var(--tap);
  margin-left: auto;
  margin-right: 0.75rem;
  padding: 0 0.5rem;
  font-size: 1.1rem;
  line-height: 1;
}

.pack-cat-summary .pack-remove {
  margin-left: auto;
}

.pack-cat-summary .pack-remove + .pack-add-btn {
  margin-left: 0;
}

.pack-plus {
  min-width: var(--tap);
  font-size: 1.1rem;
}

.pack-add-inline {
  margin-top: 0.5rem;
}

/* Delete button on a user-added item: small and at the end of the label
 * line, but still a full tap target. */
.pack-remove {
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0 0.5rem;
  margin-left: auto;
  line-height: 1;
}

.pack-add {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.pack-add input[type="text"] {
  flex: 1 1 12rem;
}

.pack-add select {
  flex: 1 1 10rem;
}

.pack-add input[hidden] {
  display: none;
}

/* ======================================================================== */
/* ---- field-guide skin --------------------------------------------------- */
/* ======================================================================== */

/* The visual layer on top of the layout rules above: serif headings, cream
 * cards with generous radii, pill controls and a floating tab bar on phones.
 * Kept in one block at the end so it wins by source order and every rule
 * above still describes the structure. Fonts are system fonts on purpose:
 * Georgia ships with iOS, macOS, Windows and Android, so nothing is fetched. */

body {
  background:
    radial-gradient(circle at 12% 3%, rgba(255, 255, 255, 0.55), transparent 28rem),
    var(--bg);
  background-attachment: fixed;
}

html[data-theme="dark"] body {
  background: var(--bg);
}

@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) body {
    background: var(--bg);
  }
}

h1,
h2,
h3,
h4 {
  font-family: var(--serif);
  font-weight: 500;
  letter-spacing: -0.01em;
}

h3 {
  font-size: 1.1rem;
}

/* The section title reads as the page title: large serif, like a magazine
 * spread, with the accent line underneath doing the work of a divider. */
main > section > h2 {
  margin: 0.5rem 0 1rem;
  font-size: clamp(2rem, 9vw, 3rem);
  line-height: 1;
  letter-spacing: -0.035em;
}

.eyebrow {
  margin: 0 0 0.55rem;
  color: var(--accent);
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

/* ---- header ---- */

.site-header {
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid color-mix(in srgb, var(--muted) 20%, transparent);
}

.header-bar {
  min-height: 3.75rem;
}

.wordmark {
  margin: 0;
  font-family: var(--serif);
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0;
}

.wordmark-zh {
  margin-right: 0.3rem;
  color: var(--accent);
  font-weight: 400;
}

#theme-toggle {
  min-height: 2.25rem;
  padding: 0 0.85rem;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--muted);
  font-size: 0.75rem;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

.countdown {
  font-size: 0.82rem;
  line-height: 1.45;
}

/* ---- controls ---- */

button {
  border-radius: 999px;
  background: var(--surface);
  transition: border-color 0.15s, background 0.15s, color 0.15s;
}

button.primary,
.filter-chip.active {
  box-shadow: 0 8px 22px color-mix(in srgb, var(--accent) 25%, transparent);
}

input[type="text"],
input[type="search"],
input[type="number"],
select,
textarea {
  border-radius: 0.8rem;
}

input[type="text"]:focus,
input[type="search"]:focus,
input[type="number"]:focus,
select:focus,
textarea:focus {
  border-color: var(--accent);
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.check input[type="checkbox"] {
  accent-color: var(--ok);
}

/* ---- cards ---- */

.card,
.todo-overview {
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border-color: color-mix(in srgb, var(--border) 92%, transparent);
  border-radius: 1.2rem;
  box-shadow: var(--shadow);
}

.card-header h3,
.card > h3:first-child {
  font-size: 1.3rem;
}

/* Status badges as soft filled pills, the colour carried by the fill as well
 * as the text so they still read at a glance in sunlight. */
.badge {
  padding: 0.15rem 0.6rem;
  border-color: transparent;
  background: var(--bg-deep);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 700;
}

.badge.ok {
  background: var(--ok-soft);
  border-color: transparent;
}

.badge.warn {
  background: var(--warn-soft);
  border-color: transparent;
}

.badge.danger {
  background: var(--accent-soft);
  border-color: transparent;
}

/* Notes become the field-note panel: sage wash, serif text. */
.note {
  padding: 0.85rem 1rem;
  border-left: 0;
  border-radius: 0.9rem;
  background: var(--note-bg);
  color: var(--note-text);
  font-family: var(--serif);
}

.day-notes {
  background: var(--warn-soft);
  border: 0;
  border-radius: 0.9rem;
}

/* ---- progress ---- */

.progress-track {
  height: 0.42rem;
  background: var(--bg-deep);
  border: 0;
}

.progress-fill,
.progress-full .progress-fill {
  background: var(--ok);
  border-radius: inherit;
}

/* ---- hero (top of the schedule) ---- */

.hero {
  margin: 0.25rem 0 1.75rem;
  overflow: hidden;
  background: var(--surface);
  border-radius: 1.6rem;
  box-shadow: var(--shadow-lg);
}

.hero-image-wrap {
  position: relative;
  height: 12.5rem;
  margin: 0;
  overflow: hidden;
}

.hero-image-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(transparent 45%, rgba(31, 28, 23, 0.4));
  pointer-events: none;
}

.hero-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.88) contrast(0.96);
}

.hero-stamp {
  position: absolute;
  right: 1rem;
  bottom: 1.6rem;
  z-index: 1;
  display: grid;
  place-content: center;
  width: 4.3rem;
  height: 4.3rem;
  border: 1px solid rgba(255, 255, 255, 0.75);
  border-radius: 50%;
  background: rgba(107, 75, 55, 0.3);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: #ffffff;
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-align: center;
  text-transform: uppercase;
}

.hero-stamp span {
  font-family: var(--serif);
  font-size: 1rem;
}

.hero-credit {
  position: absolute;
  left: 0.75rem;
  bottom: 0.45rem;
  z-index: 1;
  color: rgba(255, 255, 255, 0.8);
  font-size: 0.6rem;
}

.hero-copy {
  padding: 1.5rem 1.35rem 1.35rem;
}

.hero-title {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(2.6rem, 12vw, 4.7rem);
  font-weight: 500;
  line-height: 0.92;
  letter-spacing: -0.045em;
}

.hero-title em {
  color: var(--accent);
  font-weight: 400;
}

.hero-intro {
  margin: 1.1rem 0;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.6;
}

.hero-facts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

.hero-facts span {
  color: var(--muted);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.hero-facts b {
  display: block;
  margin-bottom: 0.15rem;
  color: var(--text);
  font-family: var(--serif);
  font-size: 1.15rem;
  letter-spacing: 0;
  text-transform: none;
}

/* ---- schedule ---- */

.filter-chip {
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  border-color: var(--border);
}

.day {
  overflow: hidden;
}

.day-summary {
  padding: 1rem 1.15rem;
  gap: 0.2rem 0.7rem;
}

.day-summary::after {
  top: 1.05rem;
  right: 1.15rem;
}

.day-date {
  color: var(--accent);
  font-family: var(--serif);
  font-size: 1.15rem;
}

.day-city {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.day-title {
  color: var(--text);
  font-family: var(--serif);
  font-size: 1.2rem;
  line-height: 1.25;
}

.day-count {
  border-color: transparent;
  background: var(--bg-deep);
  font-weight: 700;
}

.day-count-full {
  background: var(--ok-soft);
}

.day[open] > .day-summary {
  border-bottom: 1px solid var(--border);
}

.day-body {
  border-top: 0;
  padding: 0 1.15rem 1.15rem;
}

.day-intro {
  color: var(--muted);
  font-size: 0.92rem;
}

/* Timeline: the coloured kind marker becomes a dot on a hairline, instead
 * of a bar along the time. */
.item {
  border-top-color: color-mix(in srgb, var(--border) 65%, transparent);
}

.item-time {
  position: relative;
  flex: 0 0 3.6rem;
  padding-left: 0;
  padding-right: 1rem;
  padding-top: 0.2rem;
  border-left: 0;
  font-size: 0.75rem;
  font-weight: 700;
}

.item-time::after {
  content: "";
  position: absolute;
  top: 0.45rem;
  right: 0.1rem;
  width: 0.55rem;
  height: 0.55rem;
  border: 2px solid var(--surface);
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

.kind-transport > .item-time::after {
  background: var(--blue);
  box-shadow: 0 0 0 1px var(--blue);
}

.kind-activity > .item-time::after {
  background: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

.kind-food > .item-time::after {
  background: var(--warn);
  box-shadow: 0 0 0 1px var(--warn);
}

.kind-logistics > .item-time::after {
  background: var(--muted);
  box-shadow: 0 0 0 1px var(--muted);
}

.kind-rest > .item-time::after {
  background: var(--sage);
  box-shadow: 0 0 0 1px var(--sage);
}

.item-title {
  font-family: var(--serif);
  font-size: 0.98rem;
  line-height: 1.35;
}

.item-note,
.item-cost {
  font-size: 0.85rem;
}

.item-check {
  border: 1.5px solid color-mix(in srgb, var(--muted) 60%, transparent);
  border-radius: 0.4rem;
}

.item-check:checked {
  border-color: var(--ok);
  background-color: var(--ok);
}

/* ---- navigation ---- */

.tab-icon {
  display: none;
  width: 1.25rem;
  height: 1.25rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Phones and tablets: a floating dark bar at the bottom, in reach of a thumb.
 * Ten tabs do not fit, so the bar scrolls sideways; the fade on the right
 * edge says there is more. */
@media (max-width: 979px) {
  .tabs {
    position: fixed;
    left: 0.65rem;
    right: 0.65rem;
    bottom: calc(0.65rem + env(safe-area-inset-bottom, 0px));
    z-index: 4;
    max-width: none;
    margin: 0;
    padding: 0.35rem;
    gap: 0;
    background: var(--nav-bg);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 1.25rem;
    box-shadow: 0 16px 36px rgba(34, 30, 25, 0.28);
    -webkit-backdrop-filter: blur(16px);
    backdrop-filter: blur(16px);
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 1.5rem), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 1.5rem), transparent);
  }

  .tabs a {
    flex: 0 0 4.4rem;
    flex-direction: column;
    justify-content: center;
    gap: 0.2rem;
    min-height: 3.4rem;
    padding: 0.3rem 0.2rem;
    border-bottom: 0;
    border-radius: 0.9rem;
    color: rgba(255, 255, 255, 0.6);
    font-size: 0.62rem;
    font-weight: 600;
  }

  .tabs a:last-child {
    margin-right: 1.25rem;
  }

  .tab-icon {
    display: block;
  }

  .tabs a[aria-current="page"] {
    color: #ffffff;
    background: rgba(255, 255, 255, 0.08);
    font-weight: 700;
  }

  .tabs a[aria-current="page"] .tab-icon {
    color: #ef9a78;
  }

  /* Room under the last card so the floating bar never hides it. */
  .container {
    padding-bottom: calc(6.5rem + env(safe-area-inset-bottom, 0px));
  }
}

/* Desktop: pills in a row under the header. */
@media (min-width: 980px) {
  .tabs {
    gap: 0.25rem;
    padding-top: 0.75rem;
    padding-bottom: 0.25rem;
    background: transparent;
    border-bottom: 0;
  }

  .tabs a {
    min-height: 2.5rem;
    padding: 0 0.95rem;
    border-bottom: 0;
    border-radius: 999px;
    font-size: 0.85rem;
  }

  .tabs a[aria-current="page"] {
    background: var(--bg-deep);
    color: var(--text);
  }
}

/* ---- wider screens ---- */

@media (min-width: 720px) {
  .hero {
    display: grid;
    grid-template-columns: 55% 45%;
    min-height: 26rem;
  }

  .hero-image-wrap {
    height: auto;
    min-height: 26rem;
  }

  .hero-copy {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 2.5rem;
  }

  .hero-title {
    font-size: clamp(3.4rem, 6.5vw, 4.8rem);
  }

  .card {
    padding: 1.25rem 1.4rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  button {
    transition: none;
  }
}

/* ---- dag voor dag ------------------------------------------------------- */

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

.section-kicker {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 0.65rem;
}

.section-kicker span {
  font-family: var(--serif);
  font-size: 1.35rem;
}

.section-kicker small {
  color: var(--muted);
  font-size: 0.7rem;
}

/* Bleeds to the screen edge on a phone so the swipe feels like a strip, not
 * a box; the padding puts the first day back in line with the cards. */
.dayrail {
  display: flex;
  gap: 0.55rem;
  margin: 0 -1rem;
  padding: 0.25rem 1rem 0.9rem;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

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

.dayrail-day {
  flex: 0 0 4.45rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 5.6rem;
  padding: 0.6rem 0.35rem;
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  border: 1px solid var(--border);
  border-radius: 1rem;
  scroll-snap-align: center;
  transition: transform 0.18s, background 0.18s, color 0.18s;
}

.dayrail-day small {
  color: var(--muted);
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.dayrail-day strong {
  font-family: var(--serif);
  font-size: 1.5rem;
  font-weight: 500;
  line-height: 1.15;
}

.dayrail-day span {
  width: 100%;
  overflow: hidden;
  color: var(--muted);
  font-size: 0.6rem;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dayrail-day.selected {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
  transform: translateY(-0.15rem);
  box-shadow: 0 10px 26px color-mix(in srgb, var(--accent) 28%, transparent);
}

.dayrail-day.selected small,
.dayrail-day.selected span {
  color: color-mix(in srgb, var(--accent-text) 80%, transparent);
}

.dayview {
  padding: 0;
  overflow: hidden;
  border-radius: 1.4rem;
}

.dayview-heading {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 1rem;
  padding: 1.35rem 1.25rem;
  border-bottom: 1px solid var(--border);
}

.dayview-number {
  display: grid;
  place-content: center;
  width: 3.2rem;
  padding-right: 1rem;
  border-right: 1px solid var(--border);
  color: var(--accent);
  font-family: var(--serif);
  font-size: 2rem;
}

.dayview-title {
  margin: 0;
  font-size: 1.6rem;
  line-height: 1.1;
}

.dayview-summary {
  margin: 0.55rem 0 0;
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.5;
}

.dayview-body {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  padding: 1.1rem 1.25rem 1.25rem;
}

.dayview-body > * {
  margin: 0;
}

/* On the day the journey is a card of its own: icon, then the text across
 * the full width, the booking badge underneath instead of in a column that
 * squeezes the times. */
.dayview-legs .leg {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 0.75rem;
  margin: 0 0 0.5rem;
  padding: 0.85rem 0.9rem;
  background: var(--bg);
  border-left: 0;
  border-radius: 1rem;
}

.dayview-legs .leg-badges {
  grid-column: 2;
  justify-content: flex-start;
  margin-top: 0.45rem;
}

.dayview-legs .leg-badges:empty {
  display: none;
}

.dayview-legs .leg:last-child {
  margin-bottom: 0;
}

/* Timeline: a hairline runs from dot to dot down the day. */
.dayview-timeline .item {
  position: relative;
  border-top: 0;
  padding: 0.7rem 0;
}

.dayview-timeline .item::before {
  content: "";
  position: absolute;
  left: calc(3.6rem - 0.1rem - 0.4rem);
  top: 1.45rem;
  bottom: -0.75rem;
  width: 1px;
  background: var(--border);
}

.dayview-timeline .item:last-child::before {
  display: none;
}

.item-kind {
  display: block;
  margin-bottom: 0.1rem;
  color: var(--muted);
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

.dayview-stop {
  padding: 1rem;
  background: var(--bg);
  border-radius: 1rem;
}

.dayview-stop-name {
  margin: 0 0 0.15rem;
  font-size: 1.2rem;
}

.dayview-stop .stop-hotels {
  margin-top: 0.6rem;
}

.dayview-more {
  margin-top: 0.75rem;
}

.dayview-more > summary {
  min-height: var(--tap);
  display: flex;
  align-items: center;
  color: var(--accent);
  font-size: 0.85rem;
  font-weight: 800;
  cursor: pointer;
}

.dayview-more > * + * {
  margin-top: 0.75rem;
}

.dayview-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border-top: 1px solid var(--border);
}

.dayview-pager button {
  background: transparent;
  border: 0;
  color: var(--accent);
  font-size: 0.85rem;
  font-weight: 800;
}

.dayview-pager button:disabled {
  opacity: 0.3;
  cursor: default;
}

.dayview-pager span {
  color: var(--muted);
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}

.dayview-host + .dropped-section {
  margin-top: 2rem;
}

.dayview-footnote {
  margin-top: 1.5rem;
}

@media (min-width: 720px) {
  .dayrail {
    margin: 0;
    padding-left: 0.1rem;
    padding-right: 0.1rem;
  }

  .dayview-heading {
    padding: 1.7rem;
  }

  .dayview-body {
    padding: 1.2rem 1.7rem 1.5rem;
  }
}

.dayview-stop .stop-blurb {
  font-size: 0.92rem;
  line-height: 1.55;
}
