/* ── THEME ──────────────────────────────────────────────
   Brand palette, kept as plain custom properties because a few inline
   style="color:var(--primary)" etc. in app.js read them directly — and
   mirrored into Bootstrap's own --bs-* tokens so every Bootstrap component
   (buttons, links, focus rings, progress bars) picks up the same palette
   automatically instead of Bootstrap's stock blue. */
:root, [data-bs-theme=dark] {
  --primary: #3b82f6;
  --primary-dark: #1d4ed8;
  --accent: #f59e0b;
  --success: #10b981;
  --danger: #f87171;
  --text-muted: #94a3b8;

  --bs-primary: var(--primary);
  --bs-primary-rgb: 59, 130, 246;
  --bs-success: var(--success);
  --bs-success-rgb: 16, 185, 129;
  --bs-danger: var(--danger);
  --bs-danger-rgb: 248, 113, 113;
  --bs-warning: var(--accent);
  --bs-warning-rgb: 245, 158, 11;
  --bs-info: #06b6d4;
  --bs-info-rgb: 6, 182, 212;
  --bs-border-radius: .7rem;
  --bs-border-radius-sm: .5rem;
  --bs-border-radius-lg: 1rem;
  --bs-body-bg: #0f172a;
  --bs-body-color: #e2e8f0;
  --bs-secondary-color: var(--text-muted);
  --bs-tertiary-bg: #1e293b;
  --bs-secondary-bg: #1e293b;
  --bs-border-color: #334155;
  --bs-border-color-translucent: #334155;
  --bs-link-color: var(--primary);
  --bs-link-color-rgb: 59, 130, 246;
  --bs-link-hover-color: #60a5fa;
  --bs-emphasis-color: #f8fafc;
}

/* Safety net: nothing should ever be able to force horizontal scrolling —
   if any element's content is wider than expected, clip it here rather
   than letting it push the page edge off-screen on narrow phones. */
html, body { max-width: 100%; overflow-x: hidden; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }

/* ── LAYOUT ── */
.page { max-width: 900px; margin: 0 auto; padding: 2rem 1rem; box-sizing: border-box; }
.page-wide { max-width: 1200px; margin: 0 auto; padding: 2rem 1rem; box-sizing: border-box; }
.page-title { font-size: 1.6rem; font-weight: 800; margin-bottom: .3rem; }
.page-sub { color: var(--text-muted); font-size: .95rem; margin-bottom: 1.5rem; }
.text-sm { font-size: .85rem; }

/* Small utility shims for a few compound classes used throughout app.js
   templates — cheaper and lower-risk than rewriting every call site to
   Bootstrap's literal utility names, same visual result. Rows wrap by
   default so a title+button (or multi-button) header never overflows
   the screen edge on narrow phones — it stacks instead. */
.flex { display: flex; flex-wrap: wrap; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; row-gap: .5rem; column-gap: 1rem; }
.btn-icon { padding: .35rem .55rem; }

/* ── NAV ── */
.navbar-brand { cursor: pointer; }

/* ── LOGIN ── */
.login-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: radial-gradient(circle at 20% 20%, #1e3a8a 0%, #0f172a 55%, #0f172a 100%);
}
.login-card {
  max-width: 380px;
  width: 100%;
  padding: 2.5rem 2rem;
  box-shadow: 0 20px 60px rgba(0,0,0,.5);
}
.login-logo { font-size: 3rem; text-align: center; margin-bottom: .5rem; }
.login-title { text-align: center; font-size: 1.4rem; font-weight: 700; margin-bottom: .3rem; }
.login-sub { text-align: center; color: var(--text-muted); font-size: .9rem; margin-bottom: 2rem; }
.login-error { color: var(--danger); font-size: .85rem; margin-top: .5rem; text-align: center; }

/* ── FORMS ──
   Bootstrap needs form-control/form-select classes on inputs to theme them;
   instead of adding that class to every <input>/<select> in app.js (~50
   call sites), style by type-selector the same way the original stylesheet
   did, just pointed at Bootstrap's own dark-theme tokens. Same visual
   result as form-control, far smaller diff. */
.form-group { margin-bottom: 1.1rem; }
.form-group label {
  display: block;
  font-size: .8rem;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: .35rem;
  text-transform: uppercase;
  letter-spacing: .04em;
}
/* font-size is 16px, not something smaller like .95rem — iOS Safari
   auto-zooms the viewport in on any focused input whose computed font-size
   is under 16px, and in this SPA (client-side render(), no full page
   reload) that zoom doesn't always cleanly reset once the input loses
   focus, leaving the page rendered at a slightly-off scale until a manual
   pinch gesture forces a recalculation — most noticeable navigating from
   the login form straight into the (never reloaded) app shell. */
input[type=text], input[type=date], input[type=time], input[type=password], input[type=number],
textarea, select {
  display: block;
  width: 100%;
  padding: .5rem .9rem;
  font-size: 16px;
  background: var(--bs-tertiary-bg);
  color: var(--bs-body-color);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  transition: border-color .15s, box-shadow .15s;
}
/* Safari (incl. iOS) centers the date/time value in its own pseudo-element
   regardless of the input's text-align — this is the documented hook to
   override that. */
input[type=date]::-webkit-date-and-time-value, input[type=time]::-webkit-date-and-time-value { text-align: left; }
.color-picker {
  -webkit-appearance: none;
  appearance: none;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  background: none;
}
.color-picker::-webkit-color-swatch-wrapper { padding: 0; border-radius: 50%; }
.color-picker::-webkit-color-swatch { border: 2px solid var(--bs-border-color); border-radius: 50%; }
.color-picker::-moz-color-swatch { border: 2px solid var(--bs-border-color); border-radius: 50%; }
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .3);
  background: var(--bs-tertiary-bg);
  color: var(--bs-body-color);
}
textarea { resize: vertical; min-height: 80px; }

/* ── CARDS ──
   Bootstrap's real .card only supplies border/radius/background; padding
   comes from a nested .card-body, which would mean retrofitting a wrapper
   div into ~16 existing card usages across app.js. Simpler and lower-risk:
   give .card its own padding directly, same as the original stylesheet did. */
.card {
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  padding: 1.25rem;
}
.card + .card { margin-top: 1rem; }

/* ── TRIP CARD ── */
.trip-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  /* 1fr auto-rows (rather than the default content-sized "auto") makes
     every row match the tallest row's height, not just the tallest item
     within its own row — trip destinations/flag counts/warning banners
     vary in length, so without this, Safari in particular was leaving
     tiles visibly different heights instead of stretching to match. */
  grid-auto-rows: 1fr;
  align-items: stretch;
  gap: 1rem;
  margin-top: 1.5rem;
}
/* .card + .card's margin-top (for vertically-stacked standalone cards
   elsewhere) was also matching trip-card siblings here, stacking an extra
   1rem on top of the grid's own row-gap — so row-to-row spacing came out
   ~2rem while column-to-column stayed at the intended 1rem. The grid's
   `gap` already spaces these consistently in both directions. */
.trip-grid .card + .card { margin-top: 0; }
.trip-card { cursor: pointer; transition: transform .15s, box-shadow .15s; overflow: hidden; height: 100%; }
.trip-past-divider {
  margin-top: 2rem;
  padding-top: 1rem;
  border-top: 1px solid var(--bs-border-color);
  color: var(--text-muted);
  font-weight: 700;
  text-transform: uppercase;
  font-size: .85rem;
  letter-spacing: .03em;
}
.trip-card:hover { transform: translateY(-3px); box-shadow: 0 10px 28px rgba(0,0,0,.4); }
.trip-card-header {
  background: linear-gradient(135deg, var(--primary) 0%, #06b6d4 100%);
  margin: -1.25rem -1.25rem 1rem -1.25rem;
  padding: 1.2rem 1.25rem;
  border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
  color: #fff;
  display: flex;
  flex-direction: column;
  gap: .4rem;
}
.trip-card-header-top { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.trip-card-header-dates { font-size: .9rem; font-weight: 600; opacity: .9; }
/* The chevron glyph itself is tiny (~1rem including its border strokes), so
   the click handler and a generous invisible padding live on this whole row
   instead of just the glyph — a much easier target to hit on a phone. */
.trip-card-chevron-row { display: flex; justify-content: center; align-items: center; margin-top: .1rem; padding: 1rem; cursor: pointer; }
/* Sits on the card's dark body background (not the colored header), so it
   needs the same white-on-dark override as the transport/hotel/activity
   chevrons rather than the default black. */
.trip-card-chevron-row .chevron-icon { border-right-color: #fff; border-bottom-color: #fff; }
.trip-card-dest { font-size: 1.65rem; font-weight: 700; overflow-wrap: break-word; min-width: 0; }
.trip-card-flags { font-size: 3rem; line-height: 1; min-width: 0; overflow-wrap: break-word; }
/* Real flag images (self-hosted SVGs, see countryFlagIcon() in data.js) in
   place of flag emoji — Windows doesn't render those as pictures, just as
   two-letter codes. Sized in em so it inherits whatever font-size each
   context (chips, table headers, trip-card-flags) already used for the
   emoji it replaces, rather than needing a size override per context. */
.flag-icon { width: 1.3em; height: 1.3em; object-fit: contain; vertical-align: middle; border-radius: 3px; margin: 0 .05em; }
.trip-card-edit { margin-left: auto; cursor: pointer; padding: .15rem; color: #000; font-weight: 900; font-size: 1.2rem; line-height: 1; opacity: .8; flex-shrink: 0; }
.trip-card-edit:hover { opacity: 1; }
/* Stacks the "..." edit affordance above the details chevron, both right-
   aligned in the trip header card. */
.trip-header-actions { display: flex; flex-direction: column; align-items: center; gap: .9rem; margin-left: auto; flex-shrink: 0; }
.trip-header-actions .trip-card-edit { margin-left: 0; }

/* Standalone version of .trip-card-header for pages where the gradient
   header is its own full card rather than the top of a taller card (which
   is why it needs its own border-radius/padding instead of the negative-
   margin bleed trick .trip-card-header uses to blend into a parent .card).
   Not nested in anything with overflow:hidden, so it needs its own
   wrap/clip safety net for long destinations or many-country flag strings. */
.trip-header-card {
  padding: 1.2rem 1.25rem;
  border-radius: var(--bs-border-radius);
  color: #fff;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  margin-bottom: 1rem;
  max-width: 100%;
  overflow: hidden;
  box-sizing: border-box;
}
.trip-meta { display: flex; flex-direction: column; gap: .5rem; }
.trip-meta-row { display: flex; justify-content: space-between; align-items: center; font-size: .88rem; }
.trip-meta-label { color: var(--text-muted); }
.trip-meta-value { font-weight: 600; }

/* ── DAY TABS ──
   Horizontally scrollable strip so it degrades gracefully on narrow
   screens instead of wrapping/overflowing awkwardly. */
.day-tabs {
  display: flex;
  gap: .35rem;
  flex-wrap: nowrap;
  overflow-x: auto;
  padding-bottom: .4rem;
  margin-bottom: 1rem;
}
.day-tab {
  flex-shrink: 0;
  white-space: nowrap;
  padding: .35rem .8rem;
  border-radius: var(--bs-border-radius-sm);
  border: 1px solid var(--bs-border-color);
  background: var(--bs-tertiary-bg);
  cursor: pointer;
  font-size: .8rem;
  font-weight: 500;
  transition: all .15s;
}
.day-tab:hover { border-color: var(--primary); }
.day-tab.active { background: var(--primary); color: #fff; border-color: var(--primary); }
.day-tab.has-data { border-color: var(--success); }
.day-tab.has-data.active { background: var(--primary); border-color: var(--primary); }

/* ── CHIPS (countries / activities) ── */
.activity-chips { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .5rem; }
.activity-chip {
  display: flex;
  align-items: center;
  gap: .3rem;
  padding: .3rem .7rem;
  border-radius: 999px;
  background: rgba(var(--bs-primary-rgb), .18);
  color: #93c5fd;
  font-size: .85rem;
  border: 1px solid rgba(var(--bs-primary-rgb), .35);
  overflow-wrap: break-word;
  max-width: 100%;
}
.chip-remove { cursor: pointer; font-size: .8rem; opacity: .65; margin-left: .1rem; line-height: 1; }
.chip-remove:hover { opacity: 1; }
/* Manage Trip's activity-entry chips specifically (not the country chips on
   Edit Trip Details, which share .chip-remove but weren't asked to
   change) — red to signal deletion, with confirm() gating the actual
   removal (see removeActivity() in app.js). */
#act-chips .chip-remove { color: var(--danger); opacity: .85; }
#act-chips .chip-remove:hover { opacity: 1; }
/* "Move to another day" select — appears on transport blocks, the hotel
   card, and activity chips wherever the entry is a master (not an
   auto-generated mirror of a multi-day span, which can't be moved on its own). */
.move-select {
  font-size: .75rem;
  padding: .15rem .3rem;
  border-radius: 6px;
  background: var(--bs-tertiary-bg);
  color: inherit;
  border: 1px solid var(--bs-border-color);
  max-width: 9rem;
}
.move-select-sm { max-width: 2.4rem; padding: .1rem .15rem; }

/* ── VIEW DAY ── */
.view-day-card { margin-bottom: 1rem; }
.view-day-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: .5rem;
  margin-bottom: 1rem;
  padding-bottom: 1rem;
  /* .view-day-card has no side padding of its own, unlike .trip-header-card
     (which carries its own 1.25rem) — without matching it here, the "..."
     below sits further right than the "..." in the colored trip-name box
     above, since that one is inset by its own padding on top of the page's. */
  padding-right: 1.25rem;
  border-bottom: 1px solid var(--bs-border-color);
}
.view-day-title { font-size: 1.1rem; font-weight: 700; }
/* Per-day "..." — jumps into Manage Trip already on this exact day. Right-
   aligned to match the "..." in the colored trip-name box above it (see
   .trip-card-edit), and matches that one's styling too. */
.view-day-edit { font-weight: 900; font-size: 1.2rem; line-height: 1; cursor: pointer; opacity: .8; flex-shrink: 0; }
.view-day-edit:hover { opacity: 1; }
.view-section { margin-bottom: 1rem; }
.view-no-plans { text-align: center; padding: 1.5rem 1rem; }
.view-section-title {
  font-size: .8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-muted);
  margin-bottom: .5rem;
}
.view-transport-box, .view-hotel-box, .view-activity-item {
  padding: .8rem 1rem;
  background: var(--bs-tertiary-bg);
  border-radius: var(--bs-border-radius-sm);
  border: 1px solid var(--bs-border-color);
}
.view-transport-box { display: flex; align-items: flex-start; gap: .8rem; }
.view-transport-list { display: flex; flex-direction: column; gap: .6rem; }
.view-transport-icon, .view-activity-icon { font-size: 1.3rem; flex-shrink: 0; }
.view-hotel-name { font-weight: 700; font-size: 1rem; overflow-wrap: break-word; min-width: 0; }
.view-activity-list { display: flex; flex-direction: column; gap: .5rem; }
.view-activity-item { display: flex; gap: .8rem; }
/* Always-visible summary line(s) shown above the collapsible .view-details —
   unlike .view-details, never hidden by the .expandable rules below. */
.view-quick-summary { margin-top: .3rem; }
.view-quick-summary:empty { margin-top: 0; }
/* Tap-to-expand: collapsed cards hide .view-details until .expanded is toggled on click */
.expandable { cursor: pointer; position: relative; padding-right: 2.2rem; }
.expandable .view-details { display: none; margin-top: .3rem; }
/* Flight/Train/Ship/Bus/Hotel's one-line collapsed summary is a full
   replacement, not an addition to the expanded details below — so unlike
   every other .view-quick-summary, it hides itself once its card is
   expanded. */
.view-expandable.expanded .view-collapsed-only { display: none; }
/* Flight's expanded view: fixed 4-row/2-column layout (Airline|Flight,
   From|Time, To|Time, Duration spanning both columns). */
.view-flight-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .1rem .75rem; }
.expandable.expanded .view-details { display: block; }
/* Manage Trip's Transport/Hotel/Activities cards are full of interactive
   fields, so only their header row (not the whole card) is .expandable —
   .view-details there is a sibling of the header, not a descendant, hence
   the separate sibling-combinator rules below (defaults to expanded). */
.expandable + .view-details { display: block; margin-top: 0; }
.expandable:not(.expanded) + .view-details { display: none; }
/* Manage Trip's collapsed-header summary ("No plans yet" / the type(s)
   already planned) — visible only alongside the collapsed header, gone
   once expanded so it doesn't sit above the real form. */
.manage-section-summary { font-weight: normal; margin-top: .2rem; }
.expandable.expanded .manage-section-summary { display: none; }
/* Wide, CSS-drawn chevron (not a font glyph, so it's not prone to being too
   thin/low-contrast on iOS) — a corner made of two borders rotated to point
   down. Points up once its (or its .expandable ancestor's) content is expanded. */
.chevron-icon {
  display: inline-block;
  width: .55rem;
  height: .55rem;
  border-right: .18rem solid rgba(0, 0, 0, .8);
  border-bottom: .18rem solid rgba(0, 0, 0, .8);
  transform: rotate(45deg);
  transition: transform .15s;
  vertical-align: middle;
}
.expandable .chevron-icon {
  position: absolute;
  top: 50%;
  right: .9rem;
  margin-top: -.32rem;
  /* Transport/hotel/activity cards sit on a dark tertiary background, where
     the default black chevron has poor contrast — white reads much better there. */
  border-right-color: #fff;
  border-bottom-color: #fff;
}
.expandable.expanded .chevron-icon,
.chevron-icon.expanded { transform: rotate(225deg); }

/* ── TRANSPORT EDITOR (multiple segments per day) ── */
.transport-block {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  padding: 1rem;
  margin-bottom: 1rem;
}
.transport-block:last-child { margin-bottom: 0; }
.transport-block-header { display: flex; gap: .5rem; align-items: center; margin-bottom: .8rem; }
.transport-block-header select { flex: 1; }
.transport-block-header .move-select { flex: 0 0 auto; }
.transport-group {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  padding: .6rem .6rem 0;
  margin: .6rem 0;
}
.transport-group-title {
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-muted);
  margin-bottom: .5rem;
}
.transport-group .form-group, .transport-details .form-group { margin-bottom: .7rem; }
.transport-group .form-group:last-child, .transport-details .form-group:last-child { margin-bottom: 0; }

/* ── CURRENCY ── */
.currency-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
@media (max-width: 500px) { .currency-grid { grid-template-columns: 1fr; } }
.rate-result { font-size: 2rem; font-weight: 700; text-align: center; padding: 1rem; color: var(--primary); }

/* ── COUNTRY COMPARISON ── */
.compare-selectors { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.compare-table th:first-child, .compare-table td:first-child { font-weight: 600; color: var(--text-muted); white-space: nowrap; }
.compare-table th, .compare-table td { vertical-align: top; }
.compare-country-name { text-transform: uppercase; }
/* In portrait, the first column is too narrow for long German compound
   labels (Bevölkerungsdichte, Durchschnittseinkommen, Arbeitslosenquote) to
   stay on one line — wrapping is allowed here so those break at their
   embedded soft hyphens (see data.js) instead of overflowing; landscape
   keeps the nowrap single-line layout above since there's room for it. */
@media (orientation: portrait) {
  .compare-table th:first-child, .compare-table td:first-child { white-space: normal; }
}

/* ── PACK LIST ── */
.pack-category { margin-bottom: 1.5rem; }
.pack-category-title {
  font-size: .8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-muted);
  margin-bottom: .5rem;
}
.pack-item { display: flex; align-items: center; gap: .7rem; padding: .4rem 0; border-bottom: 1px solid var(--bs-border-color); }
.pack-item:last-child { border-bottom: none; }
.pack-item input[type=checkbox] { width: 18px; height: 18px; accent-color: var(--success); flex-shrink: 0; }
.pack-item.packed > .pack-item-label { text-decoration: line-through; color: var(--text-muted); }
.pack-item-label { flex: 1; font-size: .95rem; }
.pack-item-shared { flex-shrink: 0; font-size: .9rem; cursor: default; }
.pack-add-row { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-top: .7rem; }
.pack-shared-toggle {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  font-size: .8rem;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
}
.pack-shared-toggle input[type=checkbox] { width: 16px; height: 16px; accent-color: var(--primary); }

/* ── ADMIN ── */
.admin-list { list-style: none; }
.admin-item { display: flex; align-items: center; gap: .8rem; padding: .7rem 0; border-bottom: 1px solid var(--bs-border-color); }
.admin-item:last-child { border-bottom: none; }
.admin-item-icon { font-size: 1.3rem; }
.admin-item-name { flex: 1; font-weight: 500; }
.admin-item-actions { display: flex; gap: .4rem; }

/* ── MODAL (custom overlay, restyled to match the dark theme — see
   plan note: kept the existing lightweight overlay/close-on-click-outside
   JS rather than switching to bootstrap.Modal instances). Named .dialog
   rather than .modal deliberately — Bootstrap's own real .modal component
   class defaults to display:none and would have to be fought with
   specificity; a distinct name sidesteps the collision entirely. ── */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,.6);
  display: flex; align-items: center; justify-content: center;
  z-index: 1050;
  padding: 1rem;
}
.dialog {
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-lg);
  padding: 1.75rem;
  width: 100%;
  max-width: 460px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: 0 20px 60px rgba(0,0,0,.5);
}
.modal-title { font-size: 1.15rem; font-weight: 700; margin-bottom: 1.4rem; }
.modal-actions { display: flex; justify-content: flex-end; gap: .6rem; margin-top: 1.4rem; }

/* ── MISC ── */
.empty-state { text-align: center; padding: 3rem 1rem; color: var(--text-muted); }
.empty-state-icon { font-size: 3rem; margin-bottom: 1rem; }

/* ── FIXED NAV ──
   The nav is position:fixed (see .navbar in app.js) so it stays put while
   page content scrolls underneath it (main's padding-top is set in JS to
   match the bar's real height). Below the lg breakpoint the hamburger menu
   must overlay rather than expand the bar's own height, otherwise opening
   it would grow the fixed bar and cover page content by that same amount. */
@media (max-width: 991.98px) {
  #nav-content.show, #nav-content.collapsing {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--bs-tertiary-bg);
    border-bottom: 1px solid var(--bs-border-color);
    padding: 0 1rem 1rem;
    max-height: calc(100vh - 60px);
    overflow-y: auto;
  }
}

/* ── SAVING INDICATOR ──
   Small centered pill shown for the duration of an autosave (see
   showSavingIndicator()/hideSavingIndicator() in app.js). Fades in and out
   via opacity transition rather than snapping, and stays hidden (display:
   none) at rest so it can never intercept a tap even with opacity at 0. */
#saving-indicator {
  display: none;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(0, 0, 0, .8);
  color: #fff;
  padding: .9rem 1.7rem;
  border-radius: 10px;
  font-size: .95rem;
  font-weight: 600;
  z-index: 1000;
  opacity: 0;
  transition: opacity .35s ease;
  pointer-events: none;
}

/* A tab left open across a deploy never re-fetches app.js/data.js on its
   own — see initUpdateCheck() in app.js — so this banner is how the user
   finds out a new version is live without guessing to reload. */
#update-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1001;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: .6rem 1rem;
  background: var(--primary);
  color: #fff;
  font-size: .9rem;
  font-weight: 600;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .3);
}
#update-banner button {
  background: #fff;
  color: var(--primary-dark);
  border: none;
  border-radius: .4rem;
  padding: .3rem .8rem;
  font-size: .85rem;
  font-weight: 700;
  cursor: pointer;
}

/* ── RESPONSIVE ── */
@media (max-width: 600px) {
  .page, .page-wide { padding: 1.25rem .75rem; }
  .trip-grid { grid-template-columns: 1fr; }
}

/* ── PRINT ──
   Dark theme is wasteful/hard to read on paper, so force a light printout
   regardless of the on-screen theme. */
@media print {
  body, .card, .view-day-card { background: #fff !important; color: #000 !important; }
  .navbar, .btn, .day-tabs, #update-banner { display: none !important; }
  .view-day-card { break-inside: avoid; border: 1px solid #ccc !important; box-shadow: none !important; }
  .view-transport-box, .view-hotel-box, .view-activity-item { background: #f5f5f5 !important; color: #000 !important; }
  /* Printing should still output every day, not just the one currently
     swiped into view on screen. */
  .carousel-item { display: block !important; opacity: 1 !important; transform: none !important; position: static !important; }
}
