/* Design tokens, refreshed 2026-09-04 — "can you make it a bit more modern,
   looks quite dated".

   The dated part was the palette: a yellow-cream page with warm brown text and
   terracotta reads as a 2010s travel blog. What replaced it is a cooler, less
   saturated stone neutral with near-black text — current, and higher contrast,
   which matters here more than fashion.

   What was NOT taken from the modern playbook, because it would make this app
   worse for the person it is for:

   - **Not a stark white page.** Off-white cuts glare and is the standard
     dyslexia recommendation; pure #fff on a phone at night is the opposite of
     an accessibility win. The neutral got cooler and cleaner, not whiter.
   - **Not thin, light type.** Body weight stays solid and the base size went
     UP, not down. Hairline 300-weight text is the single most fashionable and
     least readable thing in current UI.
   - **No italics for emphasis.** Italic is measurably harder for dyslexic
     readers, and six rules were using it to mean "quieter". Colour and weight
     do that job now.
   - **Line height stays generous** at 1.55, above the 1.45 it was. Dyslexia
     guidance asks for more leading, and it also happens to look current. */
:root {
  /* Near-black rather than brown: more contrast against the page, and the
     warmth now comes from the surface instead of the text. */
  /* REPALETTED 2026-09-18. The cream-and-terracotta was a 2016 editorial
     look, warm and soft, and it was the second loudest thing dating the app
     after the emoji. Near-neutral with a single confident accent is where
     application design actually is, and it has a second benefit here: a
     quiet page lets the criteria colours and the photographs carry the
     meaning, instead of competing with a tinted background for attention.

     The greys are very slightly cool. Dead neutral reads as cheap on an OLED
     phone; a few degrees of blue reads as deliberate. */
  --ink: #14161a;
  --paper: #fbfbfc;
  --card: #ffffff;
  --accent: #2f6fed;
  /* The best-window marker. Dark enough that a 2px ring of it around a
     light-green `good` month reads as an outline rather than as a second
     fill — see the note on .mo-best. */
  --best: #17563a;
  --line: #e5e7ec;
  --muted: #61656f;


  /* ---- type scale -------------------------------------------------------
     393 font-size declarations across more than forty ad-hoc values, which is
     the single loudest "nobody designed this" signal a stylesheet can send.
     Eight steps now, biased slightly UP: most of the app was running at 13-15px
     where current phone practice is 15-17px, and generous type is the one place
     where looking current and being readable by a dyslexic editor agree.

     The two largest steps are fluid; the body steps are not, because text that
     shrinks on a small phone is the opposite of the point. */
  --t-xs:   0.75rem;    /* 12px — the floor. Chips, captions, source links. */
  --t-sm:   0.8125rem;
  --t-md:   0.875rem;
  --t-base: 0.9375rem;
  --t-lg:   1rem;
  --t-xl:   1.125rem;
  --t-2xl:  clamp(1.3rem, 1.15rem + 1.1vw, 1.55rem);
  --t-3xl:  clamp(1.55rem, 1.3rem + 1.8vw, 1.95rem);

  /* ---- spacing ----------------------------------------------------------
     Padding was picked per rule from a cloud of eight values. A scale is what
     makes unrelated cards look like they belong to the same application. */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;

  /* One radius scale instead of nine ad-hoc values. */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-pill: 999px;

  /* Surfaces that were hardcoded as seven near-identical creams, which is
     both sloppy and the reason dark mode broke: they stayed light while the
     text inverted. One tinted surface, one field background, and one neutral
     swatch — all of which flip. */
  --surface-2: #f2f3f6;
  --field: #ffffff;
  --swatch-none: #e6e8ed;
  /* Always dark, in BOTH modes. The verdict swatches keep their colours
     because they are data, so any text sitting on one must not follow --ink. */
  --on-swatch: #191817;

  /* Layered and very soft. A single hard shadow is what dates a card most. */
  /* Flatter. Current practice separates surfaces with a hairline and a tint
     rather than by floating them, and a drop shadow under every card is one
     of the things that reads as a template from five years ago. What is left
     is barely there, for the few things that genuinely sit above the page. */
  --shadow-1: 0 1px 2px rgba(15,17,22,0.03);
  --shadow-2: 0 1px 2px rgba(15,17,22,0.04), 0 6px 16px rgba(15,17,22,0.06);
}
* { box-sizing: border-box; }
/* The hidden attribute always wins. Any rule that sets display on a class —
   flex, grid, block — silently overrides the browser's [hidden]{display:none},
   and this file had collected three per-element patches for that trap before
   the fourth victim shipped: a "Save this proposal" box visible with nothing
   to save. One global rule ends the class of bug. */
[hidden] { display: none !important; }
/* Every select and text input starts from the same legible base, in both
   modes. This exists because three rules collided: a preference tint pinned
   dark text for its light-pink background, a later layout rule swapped the
   background to the --field token, and --field is dark in dark mode — dark
   text on a dark field, found by screenshot. Layout rules size controls; only
   this rule and the tint classes may colour them. */
select, input[type="text"], input[type="date"] {
  font: inherit; background: var(--field); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-sm);
}
body {
  margin: 0;
  /* The modern system stack, with the variable UI faces first. */
  font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
    "Segoe UI Variable Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--paper);
  color: var(--ink);
  /* Up from 1.45: more leading reads as current AND is what dyslexia guidance
     asks for, which is a rare case of fashion and accessibility agreeing. */
  line-height: 1.55;
  font-size: 16.5px;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* Headings tighten as they grow, which is most of what makes type look
   designed rather than defaulted. */
h1, h2, h3 { line-height: 1.25; letter-spacing: -0.015em; }
h1 { letter-spacing: -0.022em; }

.map-note { font-size: var(--t-xs); text-align: center; padding: 6px; background: #fdf3e7; color: #7a5a20; }

main { padding: 14px 12px 40px; }

.leg-card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
  margin-bottom: 12px; overflow: hidden; box-shadow: var(--shadow-1);
  scroll-margin-top: 12px;
}
/* The header is the row, and it is one block in two rows rather than a line
   with three more bands stacked under it. The controls used to live in a
   separate cream strip, the months in another, and the status was repeated in
   the body — four horizontal things all claiming to be "the row". */
.leg-header {
  display: flex; flex-direction: column; gap: 6px;
  padding: 10px 14px 8px; background: var(--leg); color: #fff; cursor: pointer;
}
.leg-header-top {
  display: flex; justify-content: space-between; align-items: center; gap: 8px 12px;
  flex-wrap: wrap;
}
.leg-header-left  { display: flex; align-items: flex-start; gap: 10px; flex: 1 1 12rem; min-width: 0; }
/* The criteria strip lives here, so this side has to be allowed to sit on its
   own line on a narrow screen rather than crushing the title. */
.leg-header-right { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.leg-num {
  width: 26px; height: 26px; border-radius: 50%; background: rgba(255,255,255,0.25);
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: var(--t-md); flex-shrink: 0; margin-top: 1px;
}
.leg-title-block { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.leg-title { font-size: var(--t-xl); font-weight: 700; }
.leg-sub   { font-weight: 400; opacity: 0.85; font-size: var(--t-md); }
/* The country, printed after the city rather than hidden in the flag's
   tooltip. Lighter than the title, because the city is what you are choosing
   between and the country is what the visa is about. */
.leg-country {
  font-weight: 500; opacity: 0.82; font-size: var(--t-md);
  margin-left: 6px; white-space: nowrap;
}
.leg-country::before { content: "· "; opacity: 0.65; }
.leg-dates { font-size: var(--t-sm); opacity: 0.9; }
.pill {
  font-size: var(--t-xs); background: rgba(255,255,255,0.22); padding: 4px 9px;
  border-radius: var(--r-md); font-weight: 600; white-space: nowrap;
}
/* The weather summary used to sit in the header. The header is the planning
   row now, and the weather criterion square says the same thing with a colour
   and a number, so the prose moved down here where prose belongs. */
.weather-line { margin: 0 0 12px; font-size: var(--t-sm); color: var(--muted); }

.status {
  margin: 0 0 12px; font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--muted);
}
/* One class per status rather than the two-state case that was hardcoded
   here. 'in progress' becomes 'in-progress': a class with a space in it is
   two classes, and it renders unstyled without complaining. */
.status-candidate   { color: var(--muted); }
.status-discussing  { color: #8a6d1f; }
.status-agreed      { color: #2f7a3d; }
.status-in-progress { color: #1f6f8a; }
.status-completed   { color: #5a4f8a; }

.stat-strip { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.stat { background: var(--surface-2); border-radius: var(--r-md); padding: 8px 12px; flex: 1; min-width: 92px; }
.stat-num   { font-size: var(--t-xl); font-weight: 700; display: block; }
.stat-label { opacity: 0.65; font-size: var(--t-xs); }

.box { padding: 8px 12px; border-radius: var(--r-sm); font-size: var(--t-sm); margin-bottom: 12px; }
.box-note { background: #fdf3e7; color: #6b4f22; border-left: 3px solid #d99a3d; }
.box-cost { background: #eef6ef; color: #1f4a2c; border-left: 3px solid #2f9e44; }

.stop { margin-bottom: 16px; }
.stop:last-of-type { margin-bottom: 0; }
.stop-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: var(--t-md); font-weight: 700; margin: 0 0 8px;
}
.stop-badge {
  width: 22px; height: 22px; border-radius: 50%; background: var(--leg); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--t-xs); font-weight: 800; flex-shrink: 0;
}
.stop-desc { font-weight: 400; opacity: 0.6; font-size: var(--t-sm); }

.poi-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.poi {
  display: block; text-decoration: none; color: inherit; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden;
  /* The card is a div so that the citation can be a second link, and the whole
     of it is still the tap target for directions: .poi-directions stretches an
     invisible pseudo-element over it and .poi-source sits above that. On a
     phone the difference between a whole-card target and an 11px line of text
     is the difference between a card and a paragraph. */
  position: relative; cursor: pointer;
}
.poi:active { opacity: 0.8; }
.poi-directions::after { content: ""; position: absolute; inset: 0; }
.poi-photo-wrap { display: block; position: relative; }
.poi-photo { width: 100%; height: 84px; object-fit: cover; display: block; background: var(--line); }
.poi-fallback {
  width: 100%; height: 84px; background: var(--line); font-size: var(--t-3xl);
  display: flex; align-items: center; justify-content: center;
}
.poi-info { display: block; padding: 7px 9px 9px; }
.poi-name { display: block; font-weight: 700; font-size: var(--t-sm); line-height: 1.25; margin-bottom: 2px; }
.poi-desc { display: block; font-size: var(--t-xs); opacity: 0.65; line-height: 1.3; margin-bottom: 4px; }
.poi-links {
  display: flex; gap: 10px; align-items: baseline;
  font-size: var(--t-xs); font-weight: 700;
}
.poi-links a { color: var(--accent); text-decoration: none; }
.poi-links a:active { opacity: 0.6; }
/* The citation, deliberately quieter than the action. It has to be reachable
   because a coordinate that cannot be checked is the failure the source column
   exists to stop; it does not have to compete with "Directions". Lifted above
   the stretched directions target, or the card would swallow it. */
.poi-links .poi-source {
  color: var(--muted); font-weight: 600; position: relative; z-index: 1;
}
/* How you get there, where the distance makes that a real question. Sits
   between the description and the actions because it is part of what the place
   IS from this base — Tainan is an hour and three quarters, not "far". */
.poi-reach {
  display: block; font-size: var(--t-xs); color: var(--muted);
  line-height: 1.35; margin-bottom: 4px;
}

.attribution {
  margin: 14px 0 0; padding-top: 10px; border-top: 1px solid var(--line);
  font-size: var(--t-xs); color: var(--muted);
}


.leaflet-popup-content { margin: 10px 12px; min-width: 180px; }
.popup-title { font-weight: 700; font-size: var(--t-base); margin-bottom: 2px; }
.popup-sub   { font-size: var(--t-sm); opacity: 0.7; }

.linkish {
  background: none; border: 1px solid var(--line); color: inherit;
  font: inherit; padding: 3px 10px; border-radius: var(--r-md); cursor: pointer;
}
.linkish:hover { background: rgba(244,239,228,0.15); }

/* Sign-in and enrolment. Deliberately plain: no map, no images, nothing that
   could leave a half-loaded page between someone and the way in. */
.auth-main { display: flex; justify-content: center; padding: 28px 14px 40px; }
.auth-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
  padding: 22px 20px; max-width: 26rem; width: 100%;
}
.auth-card h2 { margin: 0 0 10px; font-size: var(--t-xl); }
.auth-lead { margin: 0 0 18px; font-size: var(--t-base); }
.auth-button {
  width: 100%; padding: 14px 16px; font: inherit; font-weight: 700;
  color: #fff; background: var(--accent); border: none; border-radius: var(--r-md);
  cursor: pointer;
}
.auth-button:disabled { opacity: 0.55; cursor: default; }
/* The one-time sign-in link's single button: the whole page is this one tap,
   so it is the biggest thing on it. */
.auth-button-big { min-height: 3.5rem; font-size: var(--t-xl); }
.auth-status {
  margin: 14px 0 0; padding: 10px 12px; border-radius: var(--r-sm); font-size: var(--t-md);
}
.auth-info  { background: #fdf3e7; color: #7a5a20; }
/* "You have been signed out", shown only when the visitor arrived holding a
   session that had run out. Deliberately the INFORMATIONAL tint and not the
   error one: nothing has gone wrong, and a red box would teach both editors
   to expect a fault every thirty days. */
.auth-expired {
  margin: 0 0 14px; padding: 10px 12px; border-radius: var(--r-sm);
  font-size: var(--t-md); background: #fdf3e7; color: #7a5a20;
}
.auth-ok    { background: #eaf3ec; color: #2f5c3a; }
.auth-error { background: #fbeceb; color: #8c2f22; }
.auth-foot { margin: 16px 0 0; font-size: var(--t-sm); color: var(--muted); }
.auth-foot a { color: var(--accent); }


/* ---- the planning table -------------------------------------------------
   Seven criteria plus five planning columns is twelve columns, which does not
   fit a 6.3" screen one-handed. So the criteria are one cell containing a
   strip of small squares; on a wide screen the same strip expands into proper
   columns with the same data and the same colours.                          */

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* The window row: set once, scrolls away with everything else. */
.plan-window-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
  padding: 10px 12px; background: var(--card);
  border-bottom: 1px solid var(--line); font-size: var(--t-sm);
}

/* The fit, Save, and where to go — sticky, because this is what you look at
   while you are reordering legs and editing durations further down the page.
   Before this, the number telling you whether the plan still fits and the
   button that commits it both scrolled off as soon as you started work.

   z-index clears Leaflet, whose own controls sit at 1000 and would otherwise
   slide over the top of a bar that is meant to stay in front. */
.plan-bar {
  position: sticky; top: 0; z-index: 1100;
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
  padding: 10px 12px; background: var(--card);
  border-bottom: 1px solid var(--line); font-size: var(--t-sm);
  box-shadow: 0 2px 6px rgba(43,36,32,0.10);
}

/* Anchors land under a sticky bar unless told not to. */
#legs, #compare, #pool, #suggest { scroll-margin-top: 68px; }


/* On a phone the bar has to stay one or two rows or it is not worth sticking.
   "last leg ends …" is the first thing to go: it is derivable from the row of
   cards immediately below and is not what you are watching while you edit. */
@media (max-width: 700px) {
  .plan-bar .plan-ends { display: none; }
}
.plan-window { display: flex; align-items: center; gap: 6px; }
.plan-window-label { color: var(--muted); text-transform: uppercase;
  font-size: var(--t-xs); letter-spacing: 0.06em; font-weight: 700; }
.plan-window input[type="date"] {
  font: inherit; padding: 3px 6px; border: 1px solid var(--line);
  border-radius: var(--r-sm); background: var(--paper); color: inherit;
}
.plan-dash { color: var(--muted); }

.plan-fit { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.plan-fit strong { font-weight: 700; }
.plan-ends { color: var(--muted); font-size: var(--t-xs); }
/* Red is the whole point of this line: 46.6 weeks available against legs
   summing to 51 is what nothing could say out loud before. */
.plan-over strong { color: #b4382a; }
.plan-over .plan-ends { color: #b4382a; }

.plan-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.plan-save, .plan-undo {
  font: inherit; font-size: var(--t-sm); font-weight: 700; padding: 6px 12px;
  border-radius: var(--r-sm); cursor: pointer; border: 1px solid var(--line);
  background: var(--paper); color: inherit;
}
.plan-save { background: var(--accent); border-color: var(--accent); color: #fff; }
.plan-save:disabled { opacity: 0.4; cursor: default; }
.plan-state { font-size: var(--t-xs); }
.plan-state.plan-error { color: #b4382a; }
.plan-state.plan-ok    { color: #2f7a3d; }
.plan-state.plan-info  { color: var(--muted); }

.draft-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; font-size: var(--t-sm);
  background: #fdf3e7; border-bottom: 1px solid #e6d3b4; color: #7a5a20;
}
.draft-bar.draft-stale { background: #fbeceb; border-bottom-color: #e8c2bd; color: #8c2f22; }
.draft-bar button {
  font: inherit; font-size: var(--t-xs); font-weight: 700; padding: 4px 10px;
  border-radius: var(--r-sm); border: 1px solid currentColor; background: none;
  color: inherit; cursor: pointer;
}

/* The controls, in the header. On a translucent white scrim rather than the
   leg's own colour: a <select> and a text input have to stay legible against
   fifteen different saturated backgrounds, which is what the old cream band
   was really solving — at the cost of looking like a separate row. */
.leg-controls {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: var(--t-xs);
  background: rgba(255,255,255,0.16); border-radius: var(--r-sm); padding: 5px 7px;
}
.leg-controls .plan-dist { margin-left: auto; }
.plan-move { display: flex; gap: 2px; }
.plan-move button {
  font: inherit; font-size: var(--t-xs); line-height: 1; padding: 4px 6px;
  border: 1px solid rgba(255,255,255,0.45); background: rgba(255,255,255,0.12);
  color: #fff; border-radius: 4px; cursor: pointer;
}
.plan-move button:active { background: rgba(255,255,255,0.35); }
.leg-controls input[type="text"], .leg-controls select {
  font: inherit; font-size: var(--t-xs); padding: 3px 6px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--card); color: var(--ink); max-width: 8rem;
}
.leg-controls input.bad-input { border-color: #b4382a; background: #fbeceb; color: #8c2f22; }
.ctl-static { font-weight: 700; }
.status-chip {
  font-size: var(--t-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; background: rgba(255,255,255,0.2);
  border-radius: var(--r-md); padding: 2px 8px;
}
.plan-dist { opacity: 0.85; white-space: nowrap; }

/* ---- the visa chip ------------------------------------------------------ */

/* On every card, in both states, and never hidden. It used to appear only on
   a breach, so a leg comfortably inside its exemption rendered nothing at all
   and the hardest constraint in the research was invisible exactly where it
   was being decided.

   Four states, and the difference between them is the whole point: nothing to
   do, something to buy before flying, a morning in an immigration queue, and
   no route at all — which is the only one that means this is not a plan. */
.visa {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--t-xs); font-weight: 600; border-radius: var(--r-sm); padding: 3px 7px;
  background: rgba(255,255,255,0.9); color: var(--ink);
}
.visa-mark { font-weight: 800; }
.visa-cost { font-style: normal; font-weight: 400; opacity: 0.7; }
.visa-chip-free    { background: #dff0e2; color: #1d5c2c; }
.visa-chip-action  { background: #fdf1dc; color: #7a5410; }
.visa-chip-unknown { background: #eceae6; color: #6d675f; font-weight: 500; }
/* Not a bad colour on a scale with green at the other end. A leg no route
   covers is not a worse plan, it is not a plan. */
.visa-chip-breach  { background: #fbeceb; color: #8a2b1c; border: 1px solid #dd8071; }
/* A second entry nobody has established. Not a breach — it may well be fine —
   and emphatically not the green tick it used to get, because the routes
   describe one entry and nobody has checked whether a second is allowed. */
.visa-chip-reentry { background: #fdf3e7; color: #8a5a00; border: 1px solid #e8c65c; }

/* Where the chip's number came from, when it is the country run and not this
   leg. Deliberately quiet — it explains a number, it is not a warning. */
.visa-run {
  font-size: var(--t-xs); opacity: 0.75; white-space: nowrap;
}
/* Four weeks in one country in one place. A remark, so it reads as a remark:
   no red, no border, no exclamation mark. */
.visa-solo {
  font-size: var(--t-xs); padding: 3px 8px; border-radius: var(--r-md);
  background: var(--surface-2); color: #6b6156; font-weight: 600; white-space: nowrap;
  cursor: help;
}
/* The trip's shared rules, beside the window. Quiet: they are set once and
   then mostly read, unlike everything in the sticky bar. */
.plan-settings { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 6px; }
.plan-setting {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-sm); opacity: 0.9; cursor: help;
}
.plan-setting input { width: 4.2em; padding: 3px 5px; font: inherit; }
.plan-setting-unit { opacity: 0.7; }

/* A country's whole-year allowance, blown. Red, because unlike the
   single-base remark this one is not a matter of taste: it is a plan that
   cannot legally happen. */
.plan-period {
  margin: 6px 0 0; padding: 8px 12px; border-radius: 4px;
  background: #fbeceb; color: #8a2b1c; border: 1px solid #dd8071;
  font-size: var(--t-md); font-weight: 600;
}
/* The search refusing to run. Not an error and not a finding — it is the tool
   saying it did not look, and it has to read differently from both. */
/* The search finding nothing is the most important thing on the screen when
   it happens, and it used to be a grey paragraph you could scroll past. On
   2026-09-04 the one-visit-per-country rule made the whole trip infeasible and
   the only sign of it was this box — which was also invisible in dark mode. It
   is a proper callout now: an amber panel that names what to relax. */
.suggest-refused {
  background: #fdf3e7; color: #6b4f22;
  border: 1px solid #e8c65c; border-left: 4px solid #d99a3d;
  padding: 14px 16px; border-radius: var(--r-md); font-size: var(--t-base);
  line-height: 1.55; margin: 0 0 14px;
}
.suggest-refused strong { color: var(--accent); }
.visa-srcs { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.visa-srcs a {
  font-size: var(--t-xs); color: #fff; opacity: 0.8; text-decoration: underline;
  text-underline-offset: 2px;
}
.visa-srcs a:hover { opacity: 1; }

/* Every route in full, in the body. Deciding to stay four weeks longer is
   exactly when the route you are not using starts to matter. */
.routes { width: 100%; border-collapse: collapse; font-size: var(--t-xs); margin-top: 8px; }
.routes th, .routes td {
  text-align: left; padding: 5px 6px; border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.routes th { font-size: var(--t-xs); text-transform: uppercase; color: var(--muted); }
.routes .route-days { white-space: nowrap; font-weight: 700; }
.route-note { display: block; font-weight: 400; opacity: 0.7; margin-top: 2px; }

/* In the header, on the leg's own colour. The white hairline is what keeps
   the squares legible against six different backgrounds without having to
   know which one. */
.strip { display: flex; gap: 3px; }
.sq {
  width: 24px; height: 24px; border-radius: var(--r-sm);
  border: 1px solid rgba(255,255,255,0.55);
  padding: 0; cursor: pointer; font: inherit; display: flex;
  align-items: center; justify-content: center; overflow: hidden;
  color: var(--ink);
}
/* The icon, not an initial. This was W P C £ V M S T — eight arbitrary
   letters with a key to hold in your head, which is the worst available
   encoding for a dyslexic reader. The icon needs no key. Sized to be
   legible rather than decorative. */
/* --on-swatch is still pinned even though an icon is usually a colour glyph:
   ☀️ and 🛡️ fall back to a monochrome text glyph on some systems, and that
   glyph would follow --ink and vanish. A test insists. */
.sq-short { font-size: var(--t-base); line-height: 1; color: var(--on-swatch); }
.sq-wide  { display: none; }
/* An absent rating is an uncoloured square. A blank is honest; a guessed
   colour would be believed. */
.sq-none { background: var(--surface-2); }
.sq-good { background: #7bbf86; }
.sq-ok   { background: #e8c65c; }
.sq-bad  { background: #dd8071; }
.sq-inline {
  width: 12px; height: 12px; border-radius: 3px; display: inline-block;
  vertical-align: -1px; border: 1px solid rgba(0,0,0,0.12);
}

/* Wide screens get the same data as proper columns — same rows, same colours,
   just room for the words. Kept narrow enough that eight of them still share
   the header row with the leg title. */
@media (min-width: 900px) {
  .strip { gap: 4px; }
  .sq {
    width: auto; min-width: 5.2rem; max-width: 7rem; height: auto;
    padding: 4px 7px; justify-content: flex-start;
  }
  .sq-short { display: none; }
  .sq-wide { display: block; font-size: var(--t-xs); font-weight: 700; text-align: left; line-height: 1.25; }
  .sq-wide em { font-style: normal; font-weight: 400; opacity: 0.75; display: block; }
}

/* ---- the drill-down ----------------------------------------------------- */

.drill {
  position: fixed; inset: 0; background: rgba(20,16,12,0.5);
  display: flex; align-items: flex-end; justify-content: center; z-index: 1000;
}
.drill[hidden] { display: none; }
.drill-panel {
  background: var(--card); width: 100%; max-width: 46rem; max-height: 86vh; max-height: 86dvh;
  overflow-y: auto; border-radius: 14px 14px 0 0; padding: 0 14px 18px;
}
.drill-head {
  position: sticky; top: 0; background: var(--card); padding: 14px 0 10px;
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  border-bottom: 1px solid var(--line);
}
.drill-head h2 { margin: 0; font-size: var(--t-lg); }
.drill-head button {
  font: inherit; background: none; border: none; cursor: pointer;
  font-size: var(--t-xl); color: var(--muted); padding: 4px 8px;
}
.drill-when { margin: 12px 0 6px; font-size: var(--t-sm); color: var(--muted); }
.drill-empty { font-size: var(--t-sm); background: var(--surface-2); padding: 10px 12px; border-radius: var(--r-sm); }
.drill-year { width: 100%; border-collapse: collapse; font-size: var(--t-xs); }
.drill-year th, .drill-year td {
  text-align: left; padding: 6px 6px; border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.drill-year th { font-size: var(--t-xs); text-transform: uppercase; color: var(--muted); }
/* The months the leg currently occupies. The rest of the year is shown too —
   that is what answers "what would this be in October instead?". */
.drill-current { background: var(--surface-2); font-weight: 600; }
.drill-year a { color: var(--accent); }
.drill-docs { margin-top: 16px; }
.drill-docs h3 { font-size: var(--t-md); margin: 14px 0 6px; }
.drill-docs pre {
  white-space: pre-wrap; word-break: break-word; font: inherit;
  font-size: var(--t-sm); background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 10px 12px; margin: 0;
}
.drill-source { font-size: var(--t-xs); color: var(--muted); margin: 6px 0 0; }
.drill-source a { color: var(--accent); }
.drill-loading { font-size: var(--t-xs); color: var(--muted); }

/* ---- the shortlist ------------------------------------------------------ */

/* One card shape, two states. `.leg-pooled` on the article is the whole
   difference, and the list of what it hides is deliberately short now: a
   shortlisted place has no dates, so the reorder arrows, the date range and
   the flight distance are gone. Everything else — the controls, the visa, the
   status, the twelve months — is the same in both, because the questions they
   answer are the same in both. Toggling a class is what lets the browser move
   a card between the two without rebuilding it. */
.leg-card.leg-pooled .plan-only { display: none !important; }
.leg-card:not(.leg-pooled) .pool-only { display: none !important; }

.pool { border-top: 3px solid var(--accent); margin-top: 26px; background: var(--surface-2); }
/* The whole shortlist is a <details>. Native, so it survives plan.js
   re-rendering the cards inside it and needs no wiring of its own. */
.pool-details > summary.pool-head {
  padding: 14px; cursor: pointer; list-style: none;
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
}
.pool-details > summary.pool-head::-webkit-details-marker { display: none; }
.pool-details > summary.pool-head::after {
  content: "▾"; margin-left: auto; font-size: var(--t-base); color: var(--muted);
  transition: transform 0.15s;
}
.pool-details[open] > summary.pool-head::after { transform: rotate(180deg); }
.pool-hint { font-size: var(--t-xs); color: var(--muted); }
/* These paragraphs used to sit inside .pool-head and inherited its styling;
   they are direct children of the <details> now, so they carry it themselves. */
.pool-details > p {
  padding: 0 14px; margin: 0 0 8px;
  font-size: var(--t-sm); color: var(--muted); max-width: 62ch;
}
.pool-head { padding: 16px 14px 4px; }
.pool-head h2 {
  margin: 0 0 6px; font-size: var(--t-xl); letter-spacing: -0.01em;
  display: flex; align-items: center; gap: 8px;
}
.pool-count {
  font-size: var(--t-xs); font-weight: 700; background: var(--accent); color: #fff;
  border-radius: 20px; padding: 1px 8px;
}
.pool-empty { padding: 14px; font-size: var(--t-sm); color: var(--muted); }

.leg-pooled { opacity: 0.96; }
.leg-num-pool, .leg-pooled .js-num { background: none; color: var(--muted); }

/* ---- the season block --------------------------------------------------- */

/* Twelve marks, one per month, plus two tracks underneath that say where the
   best three months are and where this leg actually sits.

   The tracks are the fix for a real complaint: the best window used to be the
   words "best Mar-May" printed beside twelve unmarked bars, with nothing
   joining them. The eye compared the bars, disagreed with the words, and
   trusted neither. Drawing the window ON the bars settles it — and where the
   two tracks do not line up is the most actionable thing on the card. */
.season { padding: 8px 14px 10px; background: var(--surface-2); border-bottom: 1px solid var(--line); }

.months { display: flex; gap: 2px; }
.mo {
  flex: 1 1 0; min-width: 0; height: 22px; border-radius: 3px;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--t-xs); font-weight: 700; color: #4a4139;
  position: relative;
}
.mo-initial { opacity: 0.75; color: var(--on-swatch); }
.mo-none { background: var(--swatch-none); }
.mo-good { background: #7bbf86; }
.mo-ok   { background: #e8c65c; }
.mo-bad  { background: #dd8071; }
/* The best months are lifted rather than recoloured. Recolouring would fight
   the rating colour, which is the one thing on this row that must not be
   ambiguous — the mark keeps its good/ok/bad fill and gains a ring.

   The ring, the track and the key legend are all one colour because they are
   all one idea. It is a dark green (--best): green because the best window is
   the good news, and dark because `good` is already a light green fill on this
   exact row, so the two must not be able to read as the same thing. At 2px
   against #7bbf86 it is an outline, not a wash. */
.mo-best { box-shadow: 0 0 0 2px var(--best); z-index: 1; }
.mo-best .mo-initial { opacity: 1; }
.mo-now  { outline: 2px dashed rgba(43,36,32,0.55); outline-offset: -2px; }

/* Two 12-cell tracks under the bars, sharing their grid exactly, so a segment
   sits under the months it refers to and nowhere else. */
.track { display: flex; gap: 2px; margin-top: 3px; }
.tk { flex: 1 1 0; min-width: 0; height: 4px; border-radius: 2px; background: transparent; }
.track-best .tk-on { background: var(--best); }
.track-now  .tk-on { background: var(--ink); }

.season-key {
  margin: 6px 0 0; font-size: var(--t-xs); color: var(--muted);
  display: flex; gap: 12px; flex-wrap: wrap; align-items: baseline;
}
.key-best strong { color: var(--best); }
.key-now  strong { color: var(--ink); }
.key-best::before, .key-now::before {
  content: ""; display: inline-block; width: 14px; height: 4px;
  border-radius: 2px; margin-right: 5px; vertical-align: 2px;
}
.key-best::before { background: var(--best); }
.key-now::before  { background: var(--ink); }
/* Said out loud, because the bars and the window answer different questions
   and looking like they disagree is what makes a reader distrust both. */

/* The pin moved out of the header controls: it is set once and left, while
   the arrows, the duration and the status are the planning actions and stay
   where they can be reached without opening anything. */


.pool-add, .plan-remove {
  font-family: inherit; font-size: var(--t-xs); font-weight: 600;
  border: 1.5px solid var(--accent); border-radius: var(--r-sm); padding: 3px 9px;
  background: none; color: var(--accent); cursor: pointer;
}
.pool-add { background: var(--accent); color: #fff; }
.plan-remove { border-color: var(--line); color: var(--muted); }

/* A card whose leg no route covers is called out at the card level too, not
   just in its chip — a single chip in a list of fifteen is easy to scroll
   past, and this is the one state that means "this is not a plan". */
.leg-card.visa-breach { box-shadow: 0 0 0 2px #dd8071; }

.box-visa { background: var(--surface-2); border-left: 3px solid #4263eb; }

/* ---- the proposal ------------------------------------------------------- */

/* `suggest` printed to a terminal, which from January 2027 nobody has. */
/* Directly under the plan bar now. The bar states the problem — "31 days
   over" — and this answers it; at the foot of the page it competed with nine
   shortlist cards that look exactly like legs. Bordered top and bottom so it
   reads as a band between the fit and the itinerary rather than as the start
   of everything below it. */
.suggest {
  border-top: 3px solid var(--accent); border-bottom: 1px solid var(--line);
  margin: 0 0 4px; background: var(--surface-2);
}
.suggest-why { color: var(--muted); }
.suggest-head { padding: 16px 14px 10px; }
.suggest-head h2 { margin: 0 0 6px; font-size: var(--t-xl); letter-spacing: -0.01em; }
.suggest-head p { margin: 0 0 10px; font-size: var(--t-sm); color: var(--muted); max-width: 62ch; }
#suggestRun {
  font: inherit; font-size: var(--t-sm); font-weight: 700; padding: 6px 14px;
  border-radius: var(--r-sm); border: none; background: var(--accent); color: #fff; cursor: pointer;
}
#suggestRun[disabled] { opacity: 0.55; cursor: default; }
.suggest-state { font-size: var(--t-xs); color: var(--muted); margin-left: 10px; }
#suggestBody { padding: 0 14px 18px; }

.prop { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
        padding: 12px 14px; margin-bottom: 12px; }
.prop-current { background: var(--surface-2); }
.prop-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.prop-head h3 { margin: 0; font-size: var(--t-base); }
.prop-score { font-size: var(--t-xs); color: var(--muted); }
.prop-better { font-size: var(--t-xs); font-weight: 700; color: #2f7a3d; }
.prop-worse  { font-size: var(--t-xs); font-weight: 700; color: #8a2b1c; }

/* One row of chips is the whole plan. Four places and their lengths is the
   thing being decided; the table under it is detail. */
.prop-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
.chip {
  display: inline-flex; align-items: baseline; gap: 5px;
  background: var(--surface-2); border-radius: var(--r-sm); padding: 4px 9px; font-size: var(--t-xs);
}
.chip b { font-weight: 700; }
.chip span { color: var(--muted); font-size: var(--t-xs); }
/* A leg that needs a bought visa is not a breach, and must not look like one —
   but it is not nothing either, and a proposal that hides it is not actionable. */
.chip-action { box-shadow: inset 0 -2px 0 #d99a3d; }

.prop-when { font-size: var(--t-xs); color: var(--muted); margin: 0 0 8px; }
.prop-list { margin: 0 0 8px; padding-left: 1.1rem; font-size: var(--t-xs); }
.prop-list li { margin-bottom: 3px; }
.prop-reds  { color: #8a2b1c; }
.prop-acts  { color: #7a5410; }
.prop-load {
  font: inherit; font-size: var(--t-xs); font-weight: 700; padding: 5px 12px;
  border-radius: 7px; border: 1.5px solid var(--accent); background: none;
  color: var(--accent); cursor: pointer;
}
.prop-load:hover { background: var(--accent); color: #fff; }
.suggest-caveats {
  font-size: var(--t-xs); color: var(--muted); background: var(--surface-2);
  border-left: 3px solid var(--line); padding: 8px 12px; border-radius: var(--r-sm);
  margin-top: 4px;
}
.suggest-caveats p { margin: 0 0 6px; }
.suggest-caveats p:last-child { margin: 0; }

/* The country lives on the flag. Half the places are named for a city rather
   than the country the ratings describe, and a flag is only readable if you
   already know it. */
.leg-flag { cursor: help; }

/* ---- the numbers behind a colour ---------------------------------------- */

/* One row per component, one column per city. Columns rather than an average,
   because Sapporo's utilities are three times Osaka's and a leg that averaged
   them would hide the thing the plan turns on. */
.facts { width: 100%; border-collapse: collapse; font-size: var(--t-xs); margin: 12px 0 6px; }
.facts th, .facts td {
  text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line); vertical-align: top;
}
.facts th { font-size: var(--t-xs); text-transform: uppercase; color: var(--muted); }
.facts .facts-num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.facts-note { display: block; font-size: var(--t-xs); opacity: 0.7; font-weight: 400; }
.facts-gap { color: var(--muted); }
.facts-total td { font-weight: 800; border-top: 2px solid var(--ink); border-bottom: none; }
.facts a { color: var(--accent); font-size: var(--t-xs); }
.facts-gaps, .facts-why { font-size: var(--t-xs); color: var(--muted); margin: 4px 0 0; }
.facts-gaps { color: #8a5410; }

/* A pin is a constraint on the search, not a status, so it gets its own
   control rather than a sixth value in the status list — and its own colour,
   because "we fly into here" and "we have agreed to here" are different
   claims. */
.js-pinned.pin-on { background: #fdf1dc; color: #7a5410; font-weight: 700; }
.pin-chip {
  font-size: var(--t-xs); font-weight: 700; background: rgba(255,255,255,0.22);
  border-radius: var(--r-md); padding: 2px 8px; white-space: nowrap;
}

/* ---- comparing places --------------------------------------------------- */

.compare { border-top: 3px solid var(--accent); margin-top: 26px; background: var(--surface-2); }
.compare-head { padding: 16px 14px 8px; }
.compare-head h2 { margin: 0 0 6px; font-size: var(--t-xl); letter-spacing: -0.01em; }
.compare-head p { margin: 0 0 10px; font-size: var(--t-sm); color: var(--muted); max-width: 62ch; }
.compare-tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.compare-tabs button {
  font: inherit; font-size: var(--t-xs); font-weight: 600; padding: 4px 11px;
  border-radius: 20px; border: 1px solid var(--line); background: var(--card);
  color: var(--ink); cursor: pointer;
}
.compare-tabs button[aria-pressed="true"] { background: var(--accent); color: #fff; border-color: var(--accent); }
.compare-tabs button[disabled] { opacity: 0.45; cursor: default; }
.compare-state { font-size: var(--t-xs); color: var(--muted); }

/* Wide tables scroll inside their own box. The page body must never scroll
   sideways on a phone. */
#compareBody { padding: 8px 14px 18px; overflow-x: auto; }
.cmp { border-collapse: collapse; font-size: var(--t-xs); min-width: 100%; }
.cmp th, .cmp td { padding: 6px 9px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.cmp thead th {
  font-size: var(--t-xs); text-transform: uppercase; color: var(--muted);
  cursor: pointer; user-select: none; text-align: right; position: sticky; top: 0;
  background: var(--surface-2);
}
.cmp thead th:first-child { text-align: left; }
.cmp thead th[aria-sort] { color: var(--accent); }
.cmp td { text-align: right; font-variant-numeric: tabular-nums; }
.cmp td:first-child { text-align: left; white-space: normal; }
.cmp tbody tr:hover { background: var(--surface-2); }
/* A place in the plan is the thing being compared against, so it is marked —
   without hiding the rest, which is the whole point of a shortlist. */
.cmp .cmp-in-plan td:first-child { font-weight: 700; }
.cmp .cmp-in-plan td:first-child::after {
  content: "in the plan"; display: block; font-weight: 400; font-size: var(--t-xs);
  color: var(--muted);
}
.cmp-blank { color: var(--muted); }
.cmp-derived { color: var(--muted); }
.compare-missing { font-size: var(--t-xs); color: var(--muted); margin: 8px 0 0; }
.compare-empty { font-size: var(--t-sm); color: var(--muted); padding: 8px 0; }

/* A criterion with a table and no rows for this place. Rendering nothing would
   read as "there is nothing to say", which is not the same as "nobody has
   filled this in". */
.facts-none {
  font-size: var(--t-xs); color: var(--muted); background: var(--surface-2);
  border-left: 3px solid var(--line); padding: 8px 12px; border-radius: var(--r-sm);
  margin: 12px 0 0;
}

/* Provenance on every cell. A column read down is exactly where a
   weakly-sourced figure looks as solid as a strongly-sourced one, so the host
   is shown rather than hidden behind the word "source". */
.cmp td a { color: inherit; text-decoration: none; border-bottom: 1px dotted var(--muted); }
.cmp td a:hover { color: var(--accent); border-bottom-color: var(--accent); }
.cmp-host {
  display: block; font-size: var(--t-xs); color: var(--muted); font-weight: 400;
  letter-spacing: 0; margin-top: 1px;
}

/* Group headings. Safety is four kinds of danger — crime, traffic, natural
   hazard, legal jeopardy — and fifteen flat rows read as one list. */
.facts-group td {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); font-weight: 700; padding-top: 12px; border-bottom: 2px solid var(--line);
}
.cmp-group {
  display: block; font-size: var(--t-xs); letter-spacing: 0.04em; opacity: 0.65;
  font-weight: 400; text-transform: uppercase;
}

/* ---- preferences, saved proposals, and where you differ ------------------ */

/* Your own view of a place, on its place page since 2026-09-02. */
.plan-pref {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 8px; font-size: var(--t-md);
}
.plan-pref .pref-label { color: var(--muted); }
.plan-pref select { font-size: var(--t-md); padding: 5px 8px; }
/* Coloured on the level, so a glance down the open cards reads as a list of
   opinions rather than a column of identical dropdowns. Muted on purpose:
   these must not compete with the rating colours, which are the evidence. */
select.pref-must-have  { border-color: #17563a; background: #eaf4ee; color: #17563a; font-weight: 600; }
select.pref-keen       { border-color: #4c7a52; }
select.pref-rather-not { border-color: #b4382a; background: #fbeceb; color: #8c2f22; }

/* Said on the card because it is not your setting alone: one person marking
   must-have changes what every proposal has to contain, including the other
   editor's. */
.must-have-by {
  margin: 0 0 8px; font-size: var(--t-xs); color: #17563a;
  border-left: 3px solid #17563a; padding-left: 8px;
}

.suggest-save {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  padding: 0 14px 12px;
}
.suggest-save input {
  font: inherit; font-size: var(--t-sm); padding: 5px 8px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
}

/* The two folded panels share the shortlist's shape, because they are the same
   idea: something you open when you are deciding and leave shut when you are
   arranging. */
.saved, .standoff { border-top: 3px solid var(--accent); background: var(--surface-2); }
.standoff { background: var(--surface-2); }
.fold > .fold-head {
  padding: 14px; cursor: pointer; list-style: none;
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
}
.fold > .fold-head::-webkit-details-marker { display: none; }
.fold > .fold-head::after {
  content: "▾"; margin-left: auto; font-size: var(--t-base); color: var(--muted);
}
.fold[open] > .fold-head::after { transform: rotate(180deg); }
.fold > .fold-head h2 { margin: 0; font-size: var(--t-xl); letter-spacing: -0.01em; }
.fold-body { padding: 0 14px 14px; font-size: var(--t-sm); }

.saved-list, .stand-list { list-style: none; margin: 0; padding: 0; }
.saved-list li {
  border-top: 1px solid var(--line); padding: 10px 0;
}
.saved-meta { font-size: var(--t-xs); color: var(--muted); }
.saved-actions { margin-top: 6px; display: flex; gap: 8px; }
.saved-actions button, .suggest-save button, .prop-keep {
  font-family: inherit; font-size: var(--t-xs); font-weight: 600;
  border: 1.5px solid var(--accent); border-radius: var(--r-sm); padding: 4px 10px;
  background: none; color: var(--accent); cursor: pointer;
}
.saved-drop { border-color: #b4382a !important; color: #b4382a !important; }

.stand-list li { border-top: 1px solid var(--line); padding: 8px 0; }
/* Contested first and loudest. The whole reason this panel exists is that a
   place one of you must have and the other would rather not is the argument to
   have, and an average would call it neutral. */
.stand-verdict {
  display: inline-block; font-size: var(--t-xs); text-transform: uppercase;
  letter-spacing: 0.04em; border-radius: var(--r-pill); padding: 1px 8px;
  background: var(--line); color: var(--ink);
}
.stand-contested .stand-verdict { background: #dd8071; color: #3a1f1a; }
.stand-one-sided .stand-verdict { color: var(--on-swatch); background: #e8c65c; }
.stand-agreed .stand-verdict    { color: var(--on-swatch); background: #7bbf86; }
.stand-views { font-size: var(--t-xs); color: var(--muted); margin-top: 2px; }

/* "in the plan", on a country screen's city row. The only survivor of the My
   list screen, which the 2026-09-02 redesign retired. */
.mylist-where {
  display: block; font-size: var(--t-xs); color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}

/* Said before anything else in the proposal: if must-haves have taken four of
   five legs, nothing below makes sense until you know that. */
.forced-note {
  margin: 0 0 12px; padding: 10px 12px; font-size: var(--t-sm); line-height: 1.45;
  background: #fdf3e7; border-left: 3px solid var(--accent); color: #5a4632;
}
select.pref-first { border-color: #17563a; background: #e6f0ea; font-weight: 600; color: #17563a; }

/* Unsaved work has to be visible from any scroll position, which is what the
   sticky bar is for. A draft used to show only as a button that was no longer
   grey — easy to miss on a phone, and invisible if you had scrolled past the
   message that said so. */
.plan-dirty {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.04em;
  background: var(--accent); color: #fff; border-radius: var(--r-pill); padding: 2px 9px;
}
.plan-save.is-dirty {
  background: var(--accent); color: #fff; border-color: var(--accent);
  font-weight: 700;
}

/* ---- the settings screen ------------------------------------------------
   Its own page since 2026-08-31. The controls were taking a lot of room at the
   top of the page you actually work on, and they are set once and read rarely
   — the opposite of everything in the sticky bar. */
.settings-main { max-width: 46rem; margin: 0 auto; padding: 16px; }
.settings-lede { font-size: var(--t-base); opacity: 0.85; margin-bottom: 20px; }
.settings-group { margin-bottom: 26px; }
.settings-group h2 { font-size: var(--t-lg); margin: 0 0 10px; }
.settings-row {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 10px 0; border-top: 1px solid rgba(0,0,0,0.08);
}
.settings-row label { flex: 1; display: block; }
.settings-label { display: block; font-weight: 600; font-size: var(--t-base); }
.settings-help { display: block; font-size: var(--t-sm); opacity: 0.75; margin-top: 3px; }
.settings-control { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.settings-control input { width: 4.2em; padding: 4px 6px; font: inherit; }
.settings-control input.saved { outline: 2px solid #2f9e44; }
.settings-unit { font-size: var(--t-sm); opacity: 0.7; }
.people { margin-top: 28px; }
.people h2 { display: flex; align-items: center; gap: 8px; }
.people h2 svg { width: 22px; height: 22px; }
.people-list { list-style: none; margin: 0 0 8px; padding: 0; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--card); }
.people-row {
  display: grid; grid-template-columns: auto 1fr; gap: 2px 10px; align-items: baseline;
  padding: 10px 14px; border-top: 1px solid var(--line);
}
.people-row:first-child { border-top: 0; }
.people-name { font-weight: 600; font-size: var(--t-base); }
.people-tags { font-size: var(--t-sm); opacity: 0.7; }
.people-seen { grid-column: 1 / -1; font-size: var(--t-sm); }
.people-wait, .people-none { opacity: 0.8; }
/* Inviting and removing (peopleadmin.go). Big tap targets, one tap deeper. */
.people-invite { margin: 0 0 12px; }
.people-invite-open {
  display: flex; align-items: center; gap: 8px; min-height: 48px; padding: 0 14px;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--card);
  font-weight: 600; font-size: var(--t-base); color: var(--accent); cursor: pointer; list-style: none;
}
.people-invite-open::-webkit-details-marker { display: none; }
.people-invite-open svg { width: 20px; height: 20px; }
.people-invite-form { display: flex; flex-direction: column; gap: 10px; padding: 14px 4px 4px; }
.people-invite-form input[type="text"] { font: inherit; font-size: var(--t-lg); padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-md); }
.people-lang { border: 0; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 8px 20px; }
.people-lang legend { margin-bottom: 6px; }
.people-lang label { display: flex; align-items: center; gap: 8px; min-height: 44px; font-size: var(--t-base); }
.people-go {
  min-height: 48px; padding: 0 18px; border: 0; border-radius: var(--r-md);
  background: var(--accent); color: #fff; font: inherit; font-weight: 600; font-size: var(--t-base); cursor: pointer;
}
.people-acts { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.people-acts form { margin: 0; }
.people-act {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--card);
  color: var(--ink); font: inherit; font-size: var(--t-sm); font-weight: 600; cursor: pointer; list-style: none;
}
.people-act::-webkit-details-marker { display: none; }
.people-remove[open] > summary { opacity: 0.7; }
.people-remove form { padding-top: 8px; font-size: var(--t-base); }
.people-remove p { margin: 0 0 8px; }
.people-danger { border-color: #c92a2a; color: #c92a2a; }
.people-flash { padding: 10px 14px; border: 1px solid var(--line); border-left: 4px solid var(--accent); border-radius: var(--r-md); font-size: var(--t-base); }
.people-flash-no { border-left-color: #c92a2a; }
.invite-box { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
.invite-text {
  width: 100%; box-sizing: border-box; font: inherit; font-size: var(--t-base); line-height: 1.55;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--card); color: var(--ink);
  resize: vertical; word-break: break-all;
}
#invite-msg { word-break: normal; overflow-wrap: anywhere; }
@media (prefers-color-scheme: dark) { .people-danger { border-color: #ff8787; color: #ff8787; } .people-flash-no { border-left-color: #ff8787; } }
.settings-note { font-size: var(--t-md); opacity: 0.8; margin-top: 24px; }

/* What a place is like whatever month you go, under the twelve bars. Quiet by
   design: it is context for the row above rather than a competing headline. */
.standing {
  margin: 4px 0 0; font-size: var(--t-xs); font-weight: 600; letter-spacing: 0.01em;
  cursor: help;
}
.standing-good { color: #1d5c2c; }
.standing-ok   { color: #7a5410; }
.standing-bad  { color: #8a2b1c; }

/* ---- the country screen ---------------------------------------------------
   Countries are what you choose; cities are what the app works out. Its own
   page, like /settings, because it is a thing you come to do once and then
   leave — and because the plan page is already long. It reuses the settings
   page's frame and the pref colours deliberately: a preference control must
   look like a preference control wherever it is. */
.countries-main { max-width: 52rem; margin: 0 auto; padding: 16px; }
.countries-mine { font-size: var(--t-md); opacity: 0.8; margin: -8px 0 18px; }

/* The capacity meter. It answers one question — can what you have required
   actually fill the window — and it has to answer it while you are choosing,
   not at suggest time. Five of the fifteen countries are capped at four weeks
   in one visit, so a plausible-looking set of seven can quietly top out short. */
.capacity {
  border: 1px solid var(--line); border-left-width: 5px; border-radius: var(--r-sm);
  padding: 10px 12px; margin-bottom: 20px; background: var(--card);
}
.capacity-ok    { border-left-color: #2f9e44; }
.capacity-short { border-left-color: #c2410c; }
.capacity-line  { margin: 0; font-size: var(--t-base); }
.capacity-why   { margin: 6px 0 0; font-size: var(--t-md); opacity: 0.85; }
.capacity-note  { margin: 8px 0 0; font-size: var(--t-xs); opacity: 0.7; }

.country-list { list-style: none; margin: 0; padding: 0; }
.country-row { border-top: 1px solid var(--line); padding: 10px 0; }
.country-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.country-name { flex: 1 1 auto; font-weight: 600; font-size: var(--t-base); }
.country-flag { margin-right: 5px; }
.country-cap {
  font-size: var(--t-sm); color: var(--muted); white-space: nowrap; cursor: help;
}
.country-head select {
  flex: 0 0 auto; font-size: var(--t-sm); padding: 4px 6px;
}
.country-pref-read { font-size: var(--t-sm); color: var(--muted); }
.country-why { margin: 4px 0 0; font-size: var(--t-xs); opacity: 0.72; }
.country-other {
  display: inline-block; font-size: var(--t-xs); padding: 0 5px;
  border: 1px solid var(--line); border-radius: var(--r-pill);
}
.country-cities { margin-top: 6px; }
.country-cities > summary {
  cursor: pointer; font-size: var(--t-sm); color: var(--muted); list-style-position: outside;
}
.country-city-rows { list-style: none; margin: 6px 0 0; padding: 0 0 0 14px; }
.country-city {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 6px 0; border-top: 1px dotted var(--line);
}
.country-city-name { flex: 1 1 8rem; font-size: var(--t-md); }
.country-city-evidence { font-size: var(--t-xs); color: var(--muted); }
.country-city select {
  flex: 0 0 auto; font-size: var(--t-xs); padding: 3px 6px;
}

@media (max-width: 560px) {
  .country-head { gap: 6px; }
  .country-name { flex-basis: 100%; }
  .country-city-name { flex-basis: 100%; }
}

/* ---- the timeline band ----------------------------------------------------
   The trip as one picture. Twelve months across, one block per stay, shaded by
   what that month of that place is actually like.

   It is the first thing on the page because it answers the first question
   either editor asks — when are we where — and because that answer previously
   meant reading ten cards and holding twenty dates in your head. Comparing two
   proposals meant doing it twice.

   The block colours are the SAME four the twelve-month track uses, deliberately.
   A green square must mean one thing everywhere in this app, or the band
   becomes a fifth vocabulary to learn. The leg's own colour is a 3px bar along
   the bottom instead, so it ties the block to its card and its map pin without
   competing with the verdict. */
.timeline { padding: 10px 14px 8px; background: var(--card); border-bottom: 1px solid var(--line); }

.tl-ticks { position: relative; height: 13px; }
.tl-tick {
  position: absolute; top: 0; font-size: var(--t-xs); color: var(--muted);
  transform: translateX(1px); border-left: 1px solid var(--line); padding-left: 3px;
  line-height: 13px;
}

.tl-band {
  position: relative; height: 40px; border-radius: 4px;
  background: repeating-linear-gradient(90deg, #ece7dc 0 6px, #e4dfd3 6px 12px);
  overflow: hidden;
}
.tl-block {
  position: absolute; top: 0; bottom: 0;
  border-right: 1px solid rgba(255,255,255,0.75);
  border-bottom: 3px solid var(--leg, transparent);
  display: flex; align-items: center; overflow: hidden;
  cursor: pointer;
}
.tl-good { background: #7bbf86; }
.tl-ok   { background: #e8c65c; }
.tl-bad  { background: #dd8071; }
.tl-none { background: var(--swatch-none); }
.tl-name {
  font-size: var(--t-xs); font-weight: 700; color: #33291f;
  padding-left: 4px; white-space: nowrap; pointer-events: none;
}
/* Where the window ends. Drawn rather than scaled away: a plan that does not
   fit must not be able to look like one that does. */
.tl-over {
  position: absolute; top: -2px; bottom: -2px; width: 0;
  border-left: 2px dashed #b3261e; z-index: 2;
}

.tl-key {
  margin: 6px 0 0; font-size: var(--t-xs); color: var(--muted);
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
}
.tl-key .tl-swatch {
  display: inline-block; width: 11px; height: 11px; border-radius: 2px;
  margin-left: 8px;
}
.tl-key .tl-swatch:first-child { margin-left: 0; }
.tl-short { color: #8a5a00; margin-left: 4px; }

@media (max-width: 700px) {
  .timeline { padding: 8px 10px 6px; }
  .tl-band { height: 34px; }
}

/* Shorter than the trip's own minimum, and not an arrival or a departure. A
   remark and never a refusal — but it sits in the plan bar rather than on a
   card, because its consequence is about the whole plan: `suggest` has to obey
   the rule, so while this stands every proposal scores worse than the plan and
   the reason is not the trip. */
.plan-short {
  margin: 4px 14px 0; font-size: var(--t-xs); color: #8a5a00;
  border-left: 3px solid #e8c65c; padding-left: 8px;
}

/* The island or region, where the city name alone does not say where you are.
   Ubud is in Bali and nothing on the page said so, which is how somebody came
   to ask why Bali was not in the country list. Styled like the country beside
   it because it is the same kind of fact — where this place is — and it goes
   first because it is the narrower one. */
.leg-region {
  font-size: var(--t-xs); color: var(--muted); font-weight: 500; margin-left: 5px;
}
.leg-region::before { content: "· "; opacity: 0.65; }
.city-region { font-size: var(--t-xs); color: var(--muted); margin-left: 5px; }
.city-region::before { content: "· "; opacity: 0.65; }

/* What a plan does, in sentences, above everything numeric.
   Aldred read three sessions of work on the objective and said "I don't
   understand what the numbers mean". He was right to: a decision tool whose
   headline is a unitless total the reader cannot interpret is failing at the
   moment it matters. The score is still here; it is no longer the only thing
   on offer. */
.prop-words {
  margin: 6px 0 10px; padding: 8px 10px;
  background: var(--surface-2); border-left: 3px solid var(--accent); border-radius: 0 4px 4px 0;
}
.prop-words p { margin: 0 0 5px; font-size: var(--t-md); line-height: 1.45; }
.prop-words p:last-child { margin-bottom: 0; }
.prop-words strong { font-weight: 700; }

/* ==========================================================================
   The 2026-09-02 redesign.

   The single scroll of twelve sections became five screens with a bottom bar.
   The driving feedback: the person the map was built for found the old page
   overwhelming. So every screen now says one thing, the map is the front
   door, and the research is drawn into open-on-demand drawers rather than
   presented all at once. These styles are additive — the leg card, the
   season bars, the drill-down and the pool all kept their existing CSS above
   and are reused as-is; this is the frame around them.
   ========================================================================== */

/* Leave room for the fixed bottom bar on every screen that has one. */
body.trip-page, body.plan-page, body.ideas-page, body.more-page,
body.compare-page, body.place-page, body.todo-page { padding-bottom: 68px; }

/* ---- the bottom navigation ---- */
.bottomnav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 800;
  display: flex; background: rgba(255,255,255,0.92);
  backdrop-filter: saturate(1.6) blur(12px);
  -webkit-backdrop-filter: saturate(1.6) blur(12px);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
}
.bottomnav a {
  flex: 1; display: flex; flex-direction: column; align-items: center;
  gap: 2px; padding: 8px 4px 10px; text-decoration: none;
  color: var(--muted); font-size: var(--t-xs); font-weight: 600;
}
.bottomnav .nav-ic { line-height: 1; }
.bottomnav a.nav-on { color: var(--accent); }
.bottomnav a:active { background: rgba(0,0,0,0.04); }

/* ---- a shared top bar for the inner screens ---- */
.topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 13px 16px;
  background: rgba(255,255,255,0.9);
  backdrop-filter: saturate(1.6) blur(12px);
  -webkit-backdrop-filter: saturate(1.6) blur(12px);
  border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 20;
}
.topbar .back { text-decoration: none; color: var(--accent); font-weight: 700; }
.topbar .topbar-title { font-weight: 800; font-size: var(--t-xl); }
.topbar .who { font-size: var(--t-sm); color: var(--muted); }

/* ---- the Trip screen: map first ---- */
.trip-head {
  padding: 16px 18px 14px;
  background: #1c1b1a; color: #f7f5f2;
}
.trip-head h1 { margin: 0; font-size: var(--t-2xl); letter-spacing: -0.01em; }
.trip-line { margin: 3px 0 0; font-size: var(--t-base); opacity: 0.92; }
.trip-line strong { color: #fff; }
.trip-line .trip-over { color: #ffb59f; }
#map.trip-map {
  height: 62vh; height: 62dvh; min-height: 340px; width: 100%;
  border-top: 3px solid var(--accent); border-bottom: none; background: #dce3e0;
}
/* The itinerary as a sideways strip of doors, not a list. */
.tripstops {
  display: flex; gap: 8px; overflow-x: auto; padding: 12px 16px 16px;
  -webkit-overflow-scrolling: touch;
}
.tripstop {
  flex: 0 0 auto; min-width: 8.5rem; max-width: 12rem;
  border: 1px solid var(--line); border-left: 4px solid var(--leg, var(--accent));
  border-radius: var(--r-md); background: var(--card); padding: 10px 12px;
  box-shadow: var(--shadow-1);
  text-decoration: none; color: inherit;
  display: flex; flex-direction: column; gap: 2px;
}
.tripstop:active { background: rgba(0,0,0,0.03); }
.tripstop-num {
  font-size: var(--t-xs); font-weight: 800; color: #fff; background: var(--leg, var(--accent));
  border-radius: var(--r-pill); width: 1.4rem; height: 1.4rem;
  display: flex; align-items: center; justify-content: center;
}
.tripstop-name { font-weight: 700; font-size: var(--t-base); }
.tripstop-country { font-weight: 500; font-size: var(--t-xs); color: var(--muted); }
.chip-country { font-style: normal; font-weight: 500; font-size: var(--t-xs); opacity: 0.75; }
.tripstop-dates { font-size: var(--t-xs); color: var(--muted); }
.popup-link { display: inline-block; margin-top: 6px; font-weight: 700; color: var(--accent); text-decoration: none; }

/* ---- the place page ---- */
.place-main { max-width: 44rem; margin: 0 auto; padding: 16px; }
.place-head h1 { margin: 0 0 4px; font-size: var(--t-2xl); }
.place-sub { margin: 0 0 8px; font-size: var(--t-md); color: var(--muted); }
.place-chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 10px; }
.place-lede { font-size: var(--t-base); color: var(--muted); margin: 0 0 12px; }
.place-season { margin: 8px 0 18px; }
.place-planlink { margin: 18px 0 0; }
.place-planlink a { color: var(--accent); font-weight: 700; text-decoration: none; }

/* Open-on-demand drawers: the whole point of the redesign. Shut by default
   (bar one), a summary you tap, a body that unfolds. */
.sec {
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--card);
  margin: 0 0 10px; overflow: hidden; box-shadow: var(--shadow-1);
}
.sec > summary {
  list-style: none; cursor: pointer; padding: 12px 14px;
  display: flex; align-items: center; justify-content: space-between;
}
.sec > summary::-webkit-details-marker { display: none; }
.sec > summary::after { content: "▾"; color: var(--muted); transition: transform 0.15s; }
.sec[open] > summary::after { transform: rotate(180deg); }
.sec > summary h2 { margin: 0; font-size: var(--t-lg); }
.sec-body { padding: 0 14px 14px; }
.sec-hint { font-size: var(--t-sm); color: var(--muted); margin: 0 0 10px; }
.sec-empty { font-size: var(--t-md); color: var(--muted); }
.strip-wide { flex-wrap: wrap; }

/* ---- the Plan screen ---- */
.plan-main { max-width: 52rem; margin: 0 auto; padding: 12px 12px 0; }
.startpick {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 12px 14px; margin: 0 0 12px; font-size: var(--t-base);
}
.startpick select { padding: 5px 8px; }
.startpick .pref-state { flex-basis: 100%; font-size: var(--t-sm); color: var(--muted); }
/* The plan rows are the old leg cards, slimmer: on the Plan screen a place is
   controls and a year, and its research is a tap away rather than inline. */
.leg-row .leg-more {
  margin-left: auto; font-size: var(--t-xs); font-weight: 700;
  color: var(--accent); text-decoration: none;
}
.leg-link { color: inherit; text-decoration: none; }
.leg-link:active { color: var(--accent); }
.season-slim .months { margin-bottom: 4px; }

.plan-ideas-link {
  font-size: var(--t-sm); font-weight: 700; color: var(--accent);
  text-decoration: none; white-space: nowrap;
}

/* ---- the Ideas screen ---- */
.ideas-main { max-width: 48rem; margin: 0 auto; padding: 16px; }
.ideas-main .suggest, .ideas-main .saved, .ideas-main .standoff { margin: 0 0 18px; }

/* The proposal, picture-first (2026-09-02). The band is the same idiom as
   the Trip and Plan screens at two-thirds height; the chips carry each leg's
   colour; the words wait behind one drawer. */
.prop-band { position: relative; margin: 6px 0 8px; }
.prop-band .tl-ticks { position: relative; height: 14px; }
.tl-band-mini { height: 30px; }
.prop-headline { margin: 8px 0; font-size: var(--t-base); line-height: 1.5; }
.prop-do { display: flex; gap: 8px; margin: 8px 0; }
.prop-more {
  border-top: 1px solid var(--line); margin-top: 8px; padding-top: 6px;
}
.prop-more > summary {
  cursor: pointer; font-size: var(--t-sm); font-weight: 700; color: var(--muted);
  list-style: none;
}
.prop-more > summary::-webkit-details-marker { display: none; }
.prop-more > summary::after { content: " ▾"; }
.prop-more[open] > summary::after { content: " ▴"; }
.prop-more-body { padding-top: 6px; }
.suggest-caveats > summary {
  cursor: pointer; font-size: var(--t-sm); font-weight: 700; color: var(--muted);
}
.chip { border-left: 4px solid var(--leg, var(--line)); }

/* The computed ways out of an infeasible plan. Each is a decision relaxed and
   a real plan found — the most actionable thing on the screen when it shows. */
.suggest-alts {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 14px 16px; margin: 0 0 14px; box-shadow: var(--shadow-1);
}
.alts-head { margin: 0 0 10px; font-weight: 600; line-height: 1.5; }
.alt { border-top: 1px solid var(--line); padding: 10px 0 8px; }
.alt-change { margin: 0; font-weight: 700; color: var(--accent); font-size: var(--t-base); }
.alt-route { margin: 3px 0 0; font-size: var(--t-md); color: var(--muted); line-height: 1.5; }
.alts-note { margin: 10px 0 0; font-size: var(--t-sm); color: var(--muted); line-height: 1.5; }
.alts-note a { color: var(--accent); font-weight: 700; }

/* ---- the More screen ---- */
.more-main { max-width: 40rem; margin: 0 auto; padding: 16px; }
.more-list { display: flex; flex-direction: column; gap: 10px; }
.more-list a {
  display: flex; flex-direction: column; gap: 3px; text-decoration: none;
  color: inherit; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 16px 18px; box-shadow: var(--shadow-1);
}
.more-list a:active { background: rgba(0,0,0,0.03); }
.more-list strong { font-size: var(--t-lg); }
.more-list span { font-size: var(--t-sm); color: var(--muted); }
.more-who { margin: 20px 0 8px; font-size: var(--t-md); color: var(--muted); }

/* ---- the Compare screen ---- */
.compare-main { max-width: 60rem; margin: 0 auto; padding: 16px; }
.compare-switches { display: flex; flex-wrap: wrap; gap: 10px; margin: 8px 0; }
.compare-toggle { display: flex; }
.compare-toggle button {
  font: inherit; font-size: var(--t-sm); font-weight: 600; padding: 6px 12px;
  border: 1px solid var(--line); background: var(--field); color: var(--muted); cursor: pointer;
}
.compare-toggle button:first-child { border-radius: 8px 0 0 8px; }
.compare-toggle button:last-child { border-radius: 0 8px 8px 0; }
.compare-toggle button + button { border-left: 0; }
.compare-toggle button[aria-pressed="true"] { background: var(--ink); color: #fff; border-color: var(--ink); }
.cmp-country { font-size: var(--t-xs); color: var(--muted); font-weight: 500; }

/* On a phone the map should be nearly the whole screen — it is the point of
   the Trip view. */
@media (max-width: 700px) {
  #map.trip-map { height: 58vh; height: 58dvh; }
  .place-main, .plan-main, .ideas-main { padding-left: 10px; padding-right: 10px; }
}

/* ==========================================================================
   The start screen and Decide together (2026-09-04).

   "Does not seem to have much of a flow… maybe a few big tiles to start,
   where the user chooses a path." The app had no moment that asked what you
   wanted to do; it opened on a map. These tiles are that moment. Each is one
   whole tap target, an icon, two or three words and one live line — which is
   as much reading as a start screen should ask, and the live line is what
   keeps it from being a static menu.
   ========================================================================== */

body.start-page { padding-bottom: 24px; }
.start-head {
  padding: 26px 20px 20px;
  background: #1c1b1a; color: #f7f5f2;
}
.start-head h1 { margin: 0; font-size: var(--t-3xl); font-weight: 800; }
.start-line { margin: 5px 0 0; font-size: var(--t-base); opacity: 0.72; }

.tiles {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 12px; padding: 16px; max-width: 40rem; margin: 0 auto;
}
.tile {
  display: flex; flex-direction: column; gap: 5px;
  min-height: 9rem; padding: 18px 16px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
  text-decoration: none; color: inherit;
  box-shadow: var(--shadow-1);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.tile:active { transform: scale(0.98); box-shadow: var(--shadow-2); }
.tile-ic { font-size: var(--t-3xl); line-height: 1; }
.tile-name { font-weight: 700; font-size: var(--t-xl); margin-top: auto; letter-spacing: -0.01em; }
.tile-state { font-size: var(--t-sm); color: var(--muted); line-height: 1.4; }
/* Something is waiting on you. A colour, not a badge with a number in it —
   the sentence underneath already says how many. */
.tile-needs { border-color: var(--accent); box-shadow: var(--shadow-2); }
.tile-needs .tile-state { color: var(--accent); font-weight: 600; }
.start-who {
  text-align: center; font-size: var(--t-sm); color: var(--muted); margin: 4px 0 0;
}
.back-home {
  display: inline-block; margin-bottom: 6px; font-size: var(--t-sm);
  color: inherit; opacity: 0.85; text-decoration: none; font-weight: 600;
}

/* ---- Decide together ---- */
.decide-main { max-width: 44rem; margin: 0 auto; padding: 14px 14px 8px; }
.decide-lede {
  margin: 0 0 16px; font-size: var(--t-lg); font-weight: 600; line-height: 1.45;
}
.dgroup { margin: 0 0 20px; }
.dgroup h2 {
  font-size: var(--t-base); margin: 0 0 2px;
  display: flex; align-items: baseline; gap: 8px;
}
.dgroup-count {
  font-size: var(--t-xs); font-weight: 700; color: var(--muted);
  background: var(--line); border-radius: var(--r-pill); padding: 1px 8px;
}
.dgroup-why { display: block; margin: 2px 0 8px; font-size: var(--t-sm); color: var(--muted); line-height: 1.4; }
.dfold > summary { list-style: none; cursor: pointer; }
.dfold > summary::-webkit-details-marker { display: none; }
.dfold > summary h2::after { content: " ▸"; color: var(--muted); font-weight: 400; }
.dfold[open] > summary h2::after { content: " ▾"; }
/* Contested is the conversation to have, so it is the one with a colour. */
.dgroup-contested h2 { color: var(--accent); }

.dplace {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
  margin: 0 0 8px; overflow: hidden; box-shadow: var(--shadow-1);
}
.dgroup-contested .dplace { border-left: 4px solid var(--accent); }
.dplace > summary {
  list-style: none; cursor: pointer; padding: 12px 14px;
  display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
}
.dplace > summary::-webkit-details-marker { display: none; }
.dplace-who { flex: 1 1 auto; }
.dplace-name { font-weight: 700; font-size: var(--t-base); }
.dplace-country { font-size: var(--t-xs); color: var(--muted); }
.dplace-views { display: flex; flex-wrap: wrap; gap: 6px; }
.dview {
  font-size: var(--t-xs); padding: 2px 8px; border-radius: var(--r-pill);
  background: var(--paper); border: 1px solid var(--line); color: var(--muted);
}
.dview b { font-weight: 700; color: var(--ink); }
.dview-must-have  { background: #e6f0ea; border-color: #17563a; }
.dview-keen       { background: #eef5ef; }
.dview-rather-not { background: #fdeee9; border-color: var(--accent); }
.dview-exclude    { background: #fbe3dc; border-color: #8a2b1c; }
.dplace-body { padding: 0 14px 14px; }
.dsay-label { margin: 2px 0 8px; font-size: var(--t-md); font-weight: 600; }
/* Whole words you tap. A dropdown must be opened, read and decoded before it
   can be answered; these can be read and answered in one movement, and the
   one you have chosen is visible without opening anything. */
.dsay { display: flex; flex-wrap: wrap; gap: 8px; }
.dsay-btn {
  font: inherit; font-size: var(--t-md); font-weight: 600;
  padding: 10px 15px; min-height: 44px;
  border: 1.5px solid var(--line); border-radius: var(--r-md);
  background: var(--field); color: var(--ink); cursor: pointer;
  transition: transform 0.1s ease, background 0.1s ease;
}
.dsay-btn:active { transform: scale(0.97); }
.dsay-btn.is-on { background: var(--ink); color: #fff; border-color: var(--ink); }
.dsay-btn.dsay-must-have.is-on  { background: #17563a; border-color: #17563a; }
.dsay-btn.dsay-rather-not.is-on,
.dsay-btn.dsay-exclude.is-on    { background: #8a2b1c; border-color: #8a2b1c; }
.dsay-state { margin: 8px 0 0; font-size: var(--t-sm); color: var(--muted); min-height: 1.1em; }
.dplace-links { margin: 10px 0 0; }
.dplace-links a { color: var(--accent); font-weight: 700; text-decoration: none; font-size: var(--t-md); }

@media (max-width: 380px) {
  .tiles { grid-template-columns: 1fr; }
  .tile { min-height: 0; }
}

/* ---- one place at a time ------------------------------------------------
   A whole screen for one question. The shape every swipe-card group-travel
   app converged on, and the one a forty-five-row list cannot have: a picture,
   a name, and five words to choose between. */
.one-start {
  display: block; text-align: center; text-decoration: none;
  background: var(--ink); color: #fff; font-weight: 700; font-size: var(--t-base);
  padding: 15px; border-radius: var(--r-md); margin: 0 0 20px;
  box-shadow: var(--shadow-2);
}
.one-start:active { opacity: 0.85; }
.one-progress { font-size: var(--t-sm); color: var(--muted); font-weight: 600; }
.one-main { max-width: 34rem; margin: 0 auto; padding: 14px; }
.onecard {
  background: var(--card); border: 1px solid var(--line); border-radius: 20px;
  overflow: hidden; box-shadow: var(--shadow-2);
}
.onecard-photo {
  width: 100%; height: 40vh; height: 40dvh; min-height: 180px; max-height: 300px;
  object-fit: cover; display: block; background: var(--line);
}
.onecard-nophoto {
  display: flex; align-items: center; justify-content: center; font-size: var(--t-3xl);
}
.onecard-body { padding: 16px 18px 4px; }
.onecard-name { margin: 0; font-size: var(--t-3xl); font-weight: 800; }
.onecard-country { margin: 2px 0 0; font-size: var(--t-lg); color: var(--muted); }
.onecard-facts {
  margin: 10px 0 0; display: flex; flex-wrap: wrap; gap: 8px;
  font-size: var(--t-sm); color: var(--muted);
}
.onecard-in {
  background: var(--ink); color: #fff; border-radius: var(--r-pill); padding: 2px 10px;
  font-weight: 700;
}
.onecard-said { margin: 10px 0 0; font-size: var(--t-base); }
.onecard-said b { background: #e6f0ea; padding: 1px 7px; border-radius: var(--r-pill); }
.onecard-more { margin: 12px 0 0; }
.onecard-more a { color: var(--accent); font-weight: 700; text-decoration: none; font-size: var(--t-md); }
.onecard-ask { padding: 14px 18px 18px; }
.onecard-q { margin: 6px 0 12px; font-size: var(--t-xl); font-weight: 700; }
/* One row of big targets. Wrapping is fine; shrinking them is not. */
.onebtns { display: flex; flex-wrap: wrap; gap: 10px; }
.onebtn {
  flex: 1 1 8rem; font: inherit; font-size: var(--t-base); font-weight: 700;
  min-height: 56px; padding: 13px 10px;
  border: 2px solid var(--line); border-radius: var(--r-md);
  background: var(--field); color: var(--ink); cursor: pointer;
  transition: transform 0.1s ease;
}
.onebtn:active { transform: scale(0.98); }
.onebtn-must-have  { border-color: #17563a; color: #17563a; }
.onebtn-keen       { border-color: #2f7d4f; color: #2f7d4f; }
.onebtn-rather-not { border-color: var(--accent); color: var(--accent); }
.onebtn-exclude    { border-color: #8a2b1c; color: #8a2b1c; }
.oneskip {
  display: block; margin: 14px auto 0; font: inherit; font-size: var(--t-md);
  background: none; border: none; color: var(--muted); cursor: pointer;
  text-decoration: underline;
}
.one-done { text-align: center; padding: 40px 16px; }
.one-done-ic { font-size: var(--t-3xl); margin: 0 0 8px; }
.one-done h1 { font-size: var(--t-2xl); margin: 0 0 8px; }
.one-done-links { display: flex; flex-direction: column; gap: 10px; margin-top: 22px; }
.one-go {
  display: block; background: var(--ink); color: #fff; text-decoration: none;
  font-weight: 700; padding: 13px; border-radius: var(--r-md);
}

/* ---- the route ----------------------------------------------------------
   Countries in order, and the one next thing to settle. Leads with an
   instruction, not a status board: a rolling horizon has exactly one next
   thing, and everything past it is deliberately quiet. */
.route-main { max-width: 40rem; margin: 0 auto; padding: 14px 14px 8px; }
.route-next {
  margin: 0 0 18px; padding: 16px 18px; font-size: var(--t-lg); font-weight: 600;
  line-height: 1.5; background: var(--ink); color: #fff;
  border-radius: var(--r-lg); box-shadow: var(--shadow-2);
}
.steps { list-style: none; margin: 0; padding: 0; }
.step {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 13px 15px; margin: 0 0 8px; box-shadow: var(--shadow-1);
}
/* Past the next unsettled country, the sequence is context and should read
   like it. */
.step-idea { opacity: 0.62; }
.step-now { opacity: 1; border-color: var(--accent); border-width: 2px; }
.step-booked { opacity: 1; }
.step-head { display: flex; align-items: baseline; gap: 10px; }
.step-seq {
  font-size: var(--t-xs); font-weight: 800; color: #fff; background: var(--muted);
  border-radius: var(--r-pill); min-width: 1.5rem; height: 1.5rem;
  display: inline-flex; align-items: center; justify-content: center;
}
.step-now .step-seq { background: var(--accent); }
/* The name is a link into the country's own screen — card text with a
   chevron, not a bare blue link. */
.step-name { font-weight: 700; font-size: var(--t-lg); flex: 1 1 auto; color: inherit; text-decoration: none; }
.step-go { color: var(--muted); font-weight: 400; }
.step-state {
  font-size: var(--t-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; padding: 2px 9px; border-radius: var(--r-pill);
  background: var(--line); color: var(--muted);
}
.state-agreed { background: #e6f0ea; color: #17563a; }
.state-booked { background: #17563a; color: #fff; }
.step-when { margin: 6px 0 0; font-size: var(--t-sm); color: var(--muted); }
.step-cities { margin: 2px 0 0; font-size: var(--t-sm); color: var(--muted); }
.step-provisional { color: var(--muted); }
a.step-provisional { text-decoration: underline; text-underline-offset: 2px; }

/* ---- one country's screen: cities judged at the route's months ---------- */
.country-main { max-width: 860px; margin: 0 auto; padding: 14px 14px 90px; }
#countryMap.country-map {
  /* Modest on a phone, never the whole screen — the cards below are still
     the point. Empty and harmless with scripting off. */
  height: 34vh; height: 34dvh; min-height: 200px; max-height: 320px; width: 100%;
  border-bottom: 3px solid var(--accent); background: #dce3e0;
}
.country-visit { margin: 0 0 6px; font-size: var(--t-base); }
.country-hint { margin: 0 0 14px; font-size: var(--t-md); color: var(--muted); }
.country-sec { font-size: var(--t-sm); text-transform: uppercase; letter-spacing: 0.05em; margin: 18px 0 8px; }
.country-sec-note { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--muted); }
/* The base buttons: choosing a country's cities, jointly. The add is the
   same green as the country's own agree button — one shared "we agree", two
   sizes — and the remove is quiet, because undoing a base is allowed and
   not celebrated. */
.country-done {
  margin: 0 0 12px; padding: 8px 12px; border-radius: var(--r-sm);
  border-left: 4px solid #7bbf86; background: var(--surface-2); font-size: var(--t-base);
}
.base-form { margin: -4px 0 14px; }
.base-add {
  font: inherit; font-size: var(--t-base); font-weight: 700; min-height: 46px;
  padding: 11px 17px; border-radius: var(--r-md); border: 2px solid #17563a;
  background: #17563a; color: #fff; cursor: pointer;
  transition: transform 0.1s ease;
}
.base-add:active { transform: scale(0.97); }
.base-remove {
  font: inherit; font-size: var(--t-md); font-weight: 600; min-height: 40px;
  padding: 8px 14px; border-radius: var(--r-md); border: 2px solid var(--line);
  background: none; color: var(--muted); cursor: pointer;
}
.base-only { margin: -4px 0 14px; font-size: var(--t-sm); color: var(--muted); }
.country-planlink { margin: 18px 0 0; font-size: var(--t-base); }
.country-planlink a { color: var(--accent); font-weight: 700; text-decoration: none; }
/* The cards are the shared legrow, so pooled candidates arrive with their
   "now" track hidden as plan-only. Here they DO have dates to be judged
   against — the route's — so the track shows on this screen alone. */
.country-page .leg-card.leg-pooled .track-now.plan-only { display: flex !important; }
.step-block {
  margin: 10px 0 0; padding: 10px 12px; font-size: var(--t-md); line-height: 1.5;
  background: #fdf3e7; border-left: 3px solid var(--accent); border-radius: 0 8px 8px 0;
}
.step-do { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 0; }
/* Reorder arrows and the duration input, on every country. The arrows sit
   right of the name; the duration replaces the static "N wks" line. */
.step-move { margin-left: auto; }
.step-move form { display: inline-flex; gap: 2px; margin: 0; }
.step-arrow {
  font: inherit; font-size: var(--t-md); line-height: 1; padding: 4px 8px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--field); color: var(--ink); cursor: pointer;
}
.step-arrow:disabled { opacity: 0.3; cursor: default; }
.step-dur { display: inline-flex; align-items: center; gap: 5px; margin: 0; }
.step-weeks {
  width: 3.4rem; font: inherit; font-size: var(--t-md); padding: 4px 6px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--field); color: var(--ink); text-align: right;
}
.step-weeks-unit { font-size: var(--t-sm); color: var(--muted); }
.step-set {
  font: inherit; font-size: var(--t-sm); font-weight: 700; padding: 4px 10px;
  border: 1px solid var(--accent); border-radius: var(--r-sm);
  background: none; color: var(--accent); cursor: pointer;
}
.step-btn {
  font: inherit; font-size: var(--t-base); font-weight: 700; min-height: 46px;
  padding: 11px 17px; border-radius: var(--r-md); border: 2px solid var(--ink);
  background: var(--ink); color: #fff; cursor: pointer;
  transition: transform 0.1s ease;
}
.step-btn:active { transform: scale(0.97); }
.step-agree { background: #17563a; border-color: #17563a; }
.step-book  { background: var(--accent); border-color: var(--accent); }
.step-undo  { background: none; color: var(--muted); border-color: var(--line); font-weight: 600; }
.step-block-note { display: block; margin-top: 6px; font-size: var(--t-sm); opacity: 0.8; }
.route-fit {
  margin: 0 0 16px; padding: 12px 14px; border-radius: var(--r-md);
  background: var(--surface-2); border-left: 4px solid #7bbf86;
}
.route-fit-over  { border-left-color: var(--accent); background: #fdeee9; }
.route-fit-short { border-left-color: #e8c65c; background: #fdf3e7; }
.route-fit-line { margin: 0; font-size: var(--t-base); }
.route-fit-breach { margin: 6px 0 0; font-size: var(--t-md); color: var(--accent); }
/* The copy-the-itinerary anchor. Quiet on purpose: it is a way OUT of the
   app, not a decision, so it must not compete with Optimise or the agree
   buttons for the eye. Styled as a whole tappable pill all the same. */
.route-share { margin: 0 0 16px; display: flex; align-items: center; gap: 10px; }
.route-share-btn {
  display: inline-block; padding: 10px 16px; min-height: 24px;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--surface-2); color: var(--ink);
  font-size: var(--t-base); font-weight: 600; text-decoration: none;
}
.route-share-state { font-size: var(--t-md); color: var(--muted); }
.route-optimise { margin: 0 0 16px; display: flex; flex-direction: column; gap: 6px; }
.route-optimise-btn {
  align-self: flex-start; padding: 12px 18px; border: 0; cursor: pointer;
  border-radius: var(--r-pill); background: var(--accent); color: var(--paper);
  font-size: var(--t-lg); font-weight: 600;
}
.route-optimise-note { font-size: var(--t-md); color: var(--muted); line-height: 1.5; }
.route-optimise-done {
  margin: 0 0 10px; padding: 8px 12px; border-radius: var(--r-sm);
  border-left: 4px solid #7bbf86; background: var(--surface-2); font-size: var(--t-base);
}
/* The "worth a look" aside under the optimise result: same family, muted —
   it is a suggestion about the SET, which is the editors' call, not the
   button's. */
.route-optimise-aside {
  margin: 0 0 10px; padding: 8px 12px; border-radius: var(--r-sm);
  border-left: 4px solid var(--line); background: var(--surface-2);
  font-size: var(--t-base); color: var(--muted); line-height: 1.5;
}
.step-weather {
  margin: 8px 0 0; display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline;
  padding: 7px 10px; border-radius: var(--r-sm); border-left: 4px solid var(--line);
  background: var(--surface-2);
}
.step-weather-good { border-left-color: #7bbf86; }
.step-weather-ok   { border-left-color: #e8c65c; }
.step-weather-bad  { border-left-color: #dd8071; }
.wx-line { margin: 0; display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline; }
.wx-icon { font-size: var(--t-lg); }
.wx-now { font-weight: 700; font-size: var(--t-md); text-transform: capitalize; }
.wx-best { font-size: var(--t-sm); color: var(--muted); }
.step-weather-bad .wx-best, .step-weather-ok .wx-best { color: var(--accent); }
.step-weather-note { margin: 3px 0 0; font-size: var(--t-sm); color: var(--muted); line-height: 1.5; }
.wx-range { margin: 6px 0 0; font-size: var(--t-sm); }
.wx-detail > summary {
  margin: 6px 0 0; font-size: var(--t-sm); font-weight: 700; color: var(--muted);
  cursor: pointer; list-style: none;
}
.wx-detail > summary::-webkit-details-marker { display: none; }
.wx-detail > summary::after { content: " ▾"; }
.wx-detail[open] > summary::after { content: " ▴"; }
.wx-cities { margin: 4px 0 0; padding: 0 0 0 2px; list-style: none; font-size: var(--t-sm); color: var(--muted); }
.wx-cities li { padding: 1px 0; }
.wx-city { font-weight: 700; color: var(--ink); }
.step-consequence { margin: 6px 0 0; display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: baseline; }
.step-badfor { font-size: var(--t-sm); color: var(--muted); }
/* The way back in for a country: collapsed below the route, one card per
   researched country not on it. Same card language as the steps, quieter. */
.route-more { margin: 18px 0 0; }
.route-more-details > summary {
  cursor: pointer; list-style: none; display: flex; align-items: baseline; gap: 10px;
}
.route-more-details > summary::-webkit-details-marker { display: none; }
.route-more-details > summary h2 { margin: 0; font-size: var(--t-xl); display: inline; }
.route-more-count {
  font-size: var(--t-sm); font-weight: 700; color: var(--paper);
  background: var(--muted); border-radius: var(--r-pill); padding: 2px 9px;
}
.route-more-hint { font-size: var(--t-md); color: var(--muted); }
.route-more-card {
  margin: 12px 0 0; padding: 12px 14px; border-radius: var(--r-md);
  background: var(--surface-2); display: flex; flex-wrap: wrap;
  gap: 8px 14px; align-items: center;
}
.route-more-name { font-weight: 700; color: var(--ink); text-decoration: none; font-size: var(--t-lg); }
.route-more-facts { font-size: var(--t-md); color: var(--muted); }
.route-more-add { margin-left: auto; }
.route-more-note { margin: 12px 0 0; font-size: var(--t-md); color: var(--muted); line-height: 1.5; }
.country-set { margin: 18px 0 0; display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.country-set-note { font-size: var(--t-md); color: var(--muted); }
.route-why { margin: 18px 0 0; font-size: var(--t-sm); color: var(--muted); line-height: 1.5; }
.route-why a { color: var(--accent); font-weight: 700; }
.route-revisit {
  margin: 0 0 16px; padding: 12px 14px; font-size: var(--t-md); line-height: 1.5;
  background: #fdeee9; border-left: 4px solid var(--accent); border-radius: 0 10px 10px 0;
}
.step-revisit {
  display: inline-block; font-size: var(--t-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--accent); background: #fdeee9; border-radius: var(--r-pill); padding: 1px 8px;
}

/* ==========================================================================
   Dark mode, 2026-09-04. Cheap because everything above is tokenised: this
   re-points the variables and fixes the handful of places that hardcode a
   light surface.

   Worth having beyond fashion — the phone is the only device, a lot of this
   gets read in the evening, and a bright cream page in a dark room is the
   glare problem the light palette was chosen to avoid, arriving by the other
   door. Follows the system setting rather than adding a control: one less
   decision on a screen whose whole point is fewer decisions.

   Contrast is kept deliberately high and the surfaces are not pure black —
   near-black with a warm cast is easier to read against than #000, and dark
   grey text on black is the most common way dark modes fail. */
@media (prefers-color-scheme: dark) {
  :root {
    --ink: #e9ebf0;
    --paper: #0f1115;
    --card: #171a20;
    --accent: #6ea0ff;
    --best: #5cc98d;
    --line: #262a33;
    --muted: #949aa6;
    --shadow-1: 0 1px 2px rgba(0,0,0,0.4);
    --shadow-2: 0 2px 6px rgba(0,0,0,0.45), 0 8px 20px rgba(0,0,0,0.35);
    /* The three surfaces flip with everything else. --on-swatch does NOT:
       the verdict colours stay green/amber/red in both modes because they are
       data, so text on them stays dark. */
    --surface-2: #1e222a;
    --field: #232833;
    --swatch-none: #333944;
  }
  /* The dark banners invert: on a dark page they must be LIGHTER than the
     surface or the header disappears into it. */
  .start-head, .trip-head { background: #1b1f27; color: var(--ink); }
  .topbar { background: rgba(15,17,21,0.82); }
  .bottomnav { background: rgba(15,17,21,0.9); }
  /* Anything that hardcoded white as a surface. */
  .dsay-btn, .onebtn, .compare-toggle button,
  .startpick select, .settings-control input { background: var(--card); color: var(--ink); }
  .route-next, .one-start, .one-go, .step-btn { background: #2f333b; color: var(--ink); }
  .step-btn.step-undo { background: none; color: var(--muted); }
  /* The tinted callouts, re-mixed for a dark surface rather than left as
     pale washes that glow. */
  .step-block, .map-note, .box-note, .auth-info, .auth-expired, .draft-bar {
    background: #2a2620; color: #e8c9a0; border-bottom-color: #3d3529;
  }
  .box-cost, .auth-ok { background: #1d2b22; color: #a8d8ba; }
  .suggest-refused { background: #2a2620; color: #e8c9a0; border-color: #5a4a2a; }
  .route-fit-over { background: #30201d; }
  .route-fit-short { background: #2a2620; }
  .auth-error, .draft-bar.draft-stale, .leg-controls input.bad-input {
    background: #30201d; color: #f2b3a6;
  }
  select.pref-must-have, select.pref-first { background: #1d3328; color: #8fd6a8; border-color: #2f7d4f; }
  select.pref-rather-not { background: #33211c; color: #f2a595; border-color: #8a2b1c; }
  .visa-chip-free   { background: #1d3328; color: #8fd6a8; }
  .visa-chip-action { background: #302a1a; color: #e8c68a; }
  .visa-chip-breach { background: #33211c; color: #f2a595; }
  .box-visa, .prop-current, .suggest-caveats, .cmp thead th { background: var(--surface-2); }
  .route-revisit { background: #2d211d; color: #f0b8a4; }
  .dview-must-have, .dview-keen, .onecard-said b { background: #1d3328; }
  .dview-exclude { background: #3a1c16; }
  .dview-rather-not { background: #33211c; }
  .visa-chip-reentry { background: #2e2718; color: #e8c65c; }
  .poi-photo, .onecard-photo, .onecard-nophoto { background: #2a2d33; }
  /* Leaflet's own tiles are light; dim them so the map does not flare. */
  .leaflet-tile-pane { filter: brightness(0.82) contrast(1.05); }
}

/* ==========================================================================
   The place page's criterion panels (2026-09-05).

   Big tappable rows, one per criterion, each opening in place. Replaced an
   eight-square strip that was fiddly to hit and a modal that fetched and could
   fail. The closed row carries the colour dot and the verdict word; the open
   body carries the whole year, the numbers and the prose.
   ========================================================================== */
.crit-panels { margin: 8px 0 0; }
.crit {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-md); margin: 0 0 8px; overflow: hidden;
}
.crit > summary {
  list-style: none; cursor: pointer; padding: 15px 16px;
  display: flex; align-items: center; gap: 12px; min-height: 30px;
}
.crit > summary::-webkit-details-marker { display: none; }
.crit-icon { font-size: var(--t-2xl); line-height: 1; flex: 0 0 auto; }
.crit-label { font-weight: 700; font-size: var(--t-lg); flex: 1 1 auto; }
.crit-dot {
  width: 14px; height: 14px; border-radius: 50%; flex: 0 0 auto;
  border: 1px solid rgba(0,0,0,0.15);
}
.crit-dot-good { background: #7bbf86; }
.crit-dot-ok   { background: #e8c65c; }
.crit-dot-bad  { background: #dd8071; }
.crit-dot-none { background: var(--swatch-none); }
.crit-verdict {
  font-size: var(--t-sm); color: var(--muted); flex: 0 0 auto;
  min-width: 3.5rem; text-align: right; text-transform: capitalize;
}
.crit > summary::after {
  content: "▾"; color: var(--muted); flex: 0 0 auto; transition: transform 0.15s;
}
.crit[open] > summary::after { transform: rotate(180deg); }
.crit-body { padding: 0 16px 16px; }
.crit-summary {
  margin: 0 0 12px; font-size: var(--t-base); line-height: 1.5;
  padding: 10px 12px; background: var(--surface-2); border-radius: var(--r-sm);
}
.crit-empty { font-size: var(--t-base); color: var(--muted); line-height: 1.5; }

/* The year, as a stack of blocks — the note reads full width instead of
   crushed into a table's third column. Each window: a header line with the
   colour, the months, the figure and the verdict, then the note beneath. */
.year { list-style: none; margin: 0 0 12px; padding: 0; }
.yr {
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-sm);
  border-left-width: 4px; margin: 0 0 8px;
}
.yr-good { border-left-color: #7bbf86; }
.yr-ok   { border-left-color: #e8c65c; }
.yr-bad  { border-left-color: #dd8071; }
.yr-now  { background: var(--surface-2); }
.yr-head {
  margin: 0 0 4px; display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 8px;
}
.yr-when { font-weight: 700; }
.yr-value { font-weight: 700; color: var(--accent); }
.yr-rating { font-size: var(--t-sm); color: var(--muted); text-transform: capitalize; }
.yr-note { margin: 0; font-size: var(--t-md); line-height: 1.55; color: var(--ink); }
.year-here {
  font-size: var(--t-xs); font-weight: 800; text-transform: uppercase;
  letter-spacing: 0.03em; color: #fff; background: var(--accent);
  border-radius: var(--r-pill); padding: 1px 7px;
}
.yr-head .crit-dot { width: 12px; height: 12px; }

/* The breakdown table — a neat grid, values right-aligned and tabular-figured
   so the pounds line up, the source a small ↗ inside the cell rather than an
   empty column. */
.facts {
  width: 100%; border-collapse: collapse; font-size: var(--t-base); margin: 0 0 12px;
  background: var(--surface-2); border-radius: var(--r-sm); overflow: hidden;
}
.facts th {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); font-weight: 700; padding: 8px 12px;
  border-bottom: 1px solid var(--line); text-align: left;
}
.facts td {
  padding: 8px 12px; border-bottom: 1px solid var(--line); line-height: 1.4;
}
.facts tr:last-child td { border-bottom: none; }
.facts td:first-child { font-weight: 500; }
.facts-num {
  text-align: right; white-space: nowrap;
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum";
}
.facts-solo td:first-child { width: 60%; }
.facts-src { text-decoration: none; color: var(--muted); font-size: var(--t-sm); margin-left: 2px; }
.facts-num em { font-style: normal; color: var(--muted); }
.facts-num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.facts-group td { font-weight: 700; font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); background: rgba(0,0,0,0.03); }
.facts-total td { font-weight: 800; border-top: 2px solid var(--line); font-size: var(--t-base); }
.facts-derived td { color: var(--muted); }
.facts-unit { font-weight: 400; color: var(--muted); font-size: var(--t-sm); }
.facts-gap { color: var(--muted); }
.facts-gaps, .facts-none { font-size: var(--t-sm); color: var(--muted); margin: 0 0 12px; line-height: 1.5; }
.crit-doc { margin: 12px 0 0; }
.crit-doc h3 { font-size: var(--t-base); margin: 0 0 4px; }
.crit-doc-body { font-size: var(--t-md); line-height: 1.6; color: var(--ink); }
.crit-doc-body p { margin: 0 0 10px; }
.crit-doc-body h3, .crit-doc-body h4 { font-size: var(--t-base); margin: 14px 0 6px; }
.crit-doc-body ul, .crit-doc-body ol { margin: 0 0 10px; padding-left: 20px; }
.crit-doc-body li { margin-bottom: 7px; }
.crit-doc-body code {
  font-family: ui-monospace, monospace; font-size: 0.85em;
  background: var(--surface-2); padding: 1px 4px; border-radius: 4px;
}
.crit-doc-body blockquote {
  margin: 0 0 10px; padding-left: 10px; border-left: 3px solid var(--line);
  color: var(--muted);
}
.crit-doc-body hr { border: 0; border-top: 1px solid var(--line); margin: 14px 0; }
/* Research tables are narrow and wordy; let them scroll rather than squeeze. */
.crit-doc-body table { border-collapse: collapse; width: 100%; font-size: var(--t-sm); }
.crit-doc-body th, .crit-doc-body td {
  border: 1px solid var(--line); padding: 4px 6px; text-align: left; vertical-align: top;
}
.crit-doc-body th { background: var(--surface-2); }
.crit-doc-src { font-size: var(--t-sm); color: var(--muted); margin: 6px 0 0; }
.crit-doc-src a, .year a, .facts a { color: var(--accent); }

/* ==========================================================================
   The To-do screen: the editors' shared checklist. The whole card is the
   tick target; the ✕ is the only thing that deletes. Ticked items sit in a
   closed Done fold so the open list is all the screen says.
   ========================================================================== */
.todo-main { max-width: 40rem; margin: 0 auto; padding: 16px; }

.todo-add { display: flex; gap: 8px; margin: 0 0 12px; }
.todo-add-field { flex: 1; min-width: 0; }
.todo-add input[type="text"] {
  width: 100%; box-sizing: border-box; font: inherit; font-size: var(--t-lg);
  color: var(--ink); background: var(--field);
  border: 1px solid var(--line); border-radius: var(--r-md); padding: 10px 12px;
}
.todo-add button {
  font: inherit; font-size: var(--t-lg); font-weight: 600; cursor: pointer;
  color: #fff; background: var(--accent); border: 0;
  border-radius: var(--r-md); padding: 10px 16px;
}

.todo-list {
  list-style: none; margin: 0; padding: 0;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow-1); overflow: hidden;
}
.todo-item { display: flex; align-items: center; gap: 2px; }
.todo-item + .todo-item { border-top: 1px solid var(--line); }
.todo-tick { flex: 1; min-width: 0; }
.todo-tick button {
  display: flex; align-items: center; gap: 10px; width: 100%;
  font: inherit; text-align: left; color: inherit;
  background: none; border: 0; padding: 7px 0 7px 10px; cursor: pointer;
}
.todo-box {
  flex: 0 0 20px; height: 20px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid var(--muted); border-radius: 5px;
  font-size: var(--t-xs); line-height: 1;
}
.todo-text { font-size: var(--t-base); line-height: 1.3; overflow-wrap: anywhere; }
.todo-item-done .todo-box { border-color: var(--best); background: var(--best); color: #fff; }
.todo-item-done .todo-text { color: var(--muted); text-decoration: line-through; }

/* The item you have just ticked or unticked (?just=N). A tick moved it out of
   the list and into a fold at the bottom, and before this the page said
   nothing about where — so a mis-tap looked like the item had been lost. The
   anchor brings it on screen, the band says which one moved, and the line
   under it says the way back. Colour and a rule, no motion: nothing here to
   exempt in prefers-reduced-motion. */
.todo-item-just {
  scroll-margin-top: 72px; scroll-margin-bottom: 80px;
  background: color-mix(in oklab, var(--accent) 9%, transparent);
  box-shadow: inset 3px 0 0 var(--accent);
}
/* A column inside the button, so the hint sits under the words rather than
   beside them — and so the done item's line-through stops at .todo-text and
   does not strike the way back out. */
.todo-col { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.todo-just {
  margin-top: 2px;
  font-size: var(--t-xs); font-weight: 600; color: var(--accent);
}
.todo-by {
  flex: none; width: 20px; height: 20px; border-radius: var(--r-pill);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--t-xs); font-weight: 600;
  color: var(--muted); background: var(--surface-2);
}
.todo-del button {
  font: inherit; font-size: var(--t-base); color: var(--muted);
  background: none; border: 0; padding: 9px 10px; cursor: pointer;
}
.todo-empty { color: var(--muted); text-align: center; padding: 20px 0; }

.todo-done { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 2px; }
.todo-done > summary { list-style: none; cursor: pointer; padding: 8px 2px; }
.todo-done > summary::-webkit-details-marker { display: none; }
.todo-done > summary h2 {
  display: inline; margin: 0; font-size: var(--t-lg); letter-spacing: -0.01em;
}
.todo-done > summary h2::after { content: " ▸"; color: var(--muted); font-weight: 400; }
.todo-done[open] > summary h2::after { content: " ▾"; }
.todo-count {
  display: inline-block; min-width: 1.4em; text-align: center;
  font-size: var(--t-xs); font-weight: 600; color: var(--muted);
  background: var(--surface-2); border-radius: var(--r-pill); padding: 2px 7px;
  vertical-align: 2px;
}
.todo-done .todo-list { margin-top: 4px; }

/* Editing one item in place (?edit=N): the card becomes the form. */
.todo-editlink {
  flex: none; text-decoration: none; color: var(--muted);
  font-size: var(--t-base); padding: 9px 2px;
}
.todo-item-editing { padding: 6px 8px; }
.todo-edit { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; }
.todo-edit input[type="text"] {
  width: 100%; box-sizing: border-box; font: inherit; font-size: var(--t-lg);
  color: var(--ink); background: var(--field);
  border: 1px solid var(--line); border-radius: var(--r-sm); padding: 10px 12px;
}
.todo-edit button {
  font: inherit; font-size: var(--t-base); font-weight: 600; cursor: pointer;
  color: #fff; background: var(--accent); border: 0;
  border-radius: var(--r-sm); padding: 10px 14px;
}
.todo-cancel { font-size: var(--t-base); color: var(--muted); padding: 10px 4px; }

/* ---- Getting about: the flights / trains / ferries page -------------------
   Overview first. The summary strip is read before any card, each hop is one
   card, and everything volatile or wordy sits inside a details drawer.
   Colours come from the existing variables only — a hardcoded light
   background here would vanish in dark mode, which css_contrast_test.go
   exists to catch. */

.travel-main { padding: 0 12px 88px; max-width: 720px; margin: 0 auto; }
.travel-title { font-size: var(--t-2xl); margin: 14px 0 10px; }

.travel-sum {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px;
}
.fsum {
  flex: 1 1 0; min-width: 72px; text-align: center;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 8px 6px;
}
.fsum-n { display: block; font-size: var(--t-2xl); font-weight: 650; line-height: 1.1; }
.fsum-l { display: block; font-size: var(--t-sm); color: var(--muted); margin-top: 2px; }
.fsum-star .fsum-n { color: var(--best); }
.fsum-gap .fsum-n, .fsum-none .fsum-n { color: var(--accent); }

.travel-longest {
  margin: 0 0 14px; font-size: var(--t-base); color: var(--muted);
}

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

.hop {
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 12px; margin-bottom: 10px;
  border-left: 4px solid var(--line);
}
.hop-fly   { border-left-color: var(--accent); }
.hop-train,
.hop-bus,
.hop-boat  { border-left-color: var(--best); }
.hop-none  { border-left-color: var(--swatch-none); }

.hop-top { display: flex; align-items: baseline; gap: 8px; }
.hop-icon { font-size: var(--t-xl); flex: none; }
.hop-route { font-size: var(--t-lg); font-weight: 600; line-height: 1.35; }
.hop-arrow { color: var(--muted); margin: 0 4px; }
.hop-date { margin: 2px 0 0; font-size: var(--t-sm); color: var(--muted); }

.hop-facts {
  margin: 8px 0 0; display: flex; flex-wrap: wrap; gap: 6px;
  font-size: var(--t-sm);
}
.hop-facts > span {
  background: var(--surface-2); color: var(--ink);
  border-radius: var(--r-pill); padding: 2px 9px;
}
.hop-dur { font-weight: 650; }
.hop-km, .hop-unknown { color: var(--muted); }
.hop-alli-star { color: var(--best); font-weight: 600; }
.hop-alli-none, .hop-alli-part { color: var(--accent); }

.hop-more { margin-top: 10px; }
.hop-more > summary {
  cursor: pointer; font-size: var(--t-md); color: var(--muted);
  padding: 6px 0; list-style: none;
}
.hop-more > summary::-webkit-details-marker { display: none; }
.hop-more > summary::after { content: " ›"; }
.hop-more[open] > summary::after { content: " ⌄"; }

.hop-detail { font-size: var(--t-md); line-height: 1.5; }
.hop-note { margin: 4px 0 10px; }
.hop-row { margin: 0 0 6px; }
.hop-k {
  display: inline-block; min-width: 96px;
  color: var(--muted); font-size: var(--t-sm);
}
.hop-warn { color: var(--accent); font-size: var(--t-sm); }
.hop-src { margin: 10px 0 0; font-size: var(--t-sm); }

.travel-foot {
  margin: 18px 0 0; font-size: var(--t-sm); color: var(--muted); line-height: 1.5;
}

.route-travel { margin: 0 0 12px; }
.route-travel a {
  display: block; padding: 10px 12px;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-md);
  text-decoration: none; font-size: var(--t-base);
}

.tile-travel .tile-ic { color: var(--accent); }

/* Two times per hop: in the vehicle, and the whole day. The second is quieter
   because the first is what gets read; but it is the one that decides between
   a train and a plane, so it is on the card and not in the drawer. */
.hop-door { color: var(--muted); }

/* A revisit the research covers, said positively. The warning it replaces
   could never be answered, only endured. */
.step-revisit-ok { color: var(--best); font-size: var(--t-sm); }

/* Customs and health on the country screen. Closed drawers below the cities:
   read once before arriving, never in the way of choosing a base. */
.country-notes { margin: 18px 0 8px; }
.cnote {
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-md);
  margin-bottom: 8px;
}
.cnote > summary {
  cursor: pointer; padding: 12px; font-weight: 600; list-style: none;
}
.cnote > summary::-webkit-details-marker { display: none; }
.cnote > summary::after { content: " ›"; color: var(--muted); }
.cnote[open] > summary::after { content: " ⌄"; }
.cnote-body { padding: 0 12px 12px; font-size: var(--t-base); line-height: 1.65; }
.cnote-body p { margin: 0 0 12px; }
.cnote-body h3 {
  margin: 18px 0 8px; font-size: var(--t-base); color: var(--accent);
}
.cnote-body h3:first-child { margin-top: 4px; }
.cnote-body ul { margin: 0 0 12px; padding-left: 20px; }
/* Generous between bullets, not within them: the whole point is that one rule
   reads as one thing. */
.cnote-body li { margin-bottom: 10px; }
.cnote-body strong { font-weight: 650; }

.country-through { margin: 6px 0 0; font-size: var(--t-md); }
.country-through a { color: var(--accent); text-decoration: none; }

/* The countries list leads with the route. The pool built this screen and is
   still needed, but it is no longer what you came here for. */
.clist-head { font-size: var(--t-xl); margin: 20px 0 2px; }
.clist-sub { margin: 0 0 12px; font-size: var(--t-md); color: var(--muted); }
.country-on-route { border-left: 3px solid var(--accent); }
.clist-rest { margin-top: 18px; }
.clist-rest > summary {
  cursor: pointer; padding: 10px 0; color: var(--muted); font-size: var(--t-base);
}

/* The start screen in two groups. "Our trip" leads; "Still choosing" is
   quieter but present, because the pool is still in use. */
.tiles-head {
  grid-column: 1 / -1; margin: 6px 0 2px; font-size: var(--t-xl);
}
.tiles-head-later { margin-top: 22px; color: var(--muted); }
.tiles-sub {
  display: block; font-size: var(--t-sm); font-weight: 400; color: var(--muted);
  margin-top: 2px;
}
.tile-quiet { opacity: 0.82; }
.tile-quiet .tile-ic { filter: grayscale(0.4); }

/* Health: the whole trip first, then country by country in route order. */
.health-main { padding: 0 12px 88px; max-width: 720px; margin: 0 auto; }
.health-title { font-size: var(--t-2xl); margin: 14px 0 4px; }
.health-lede { margin: 0 0 16px; font-size: var(--t-md); color: var(--muted); }
.health-whole {
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-left: 4px solid var(--accent);
  border-radius: var(--r-md); padding: 4px 14px 2px; margin-bottom: 22px;
  font-size: var(--t-base); line-height: 1.65;
}
.health-whole p { margin: 0 0 12px; }
.health-whole h3 { margin: 18px 0 8px; font-size: var(--t-base); color: var(--accent); }
.health-whole ul { margin: 0 0 12px; padding-left: 20px; }
.health-whole li { margin-bottom: 10px; }
.health-sec { font-size: var(--t-xl); margin: 0 0 2px; }
.health-sub { margin: 0 0 12px; font-size: var(--t-md); color: var(--muted); }
.hcountry > summary { font-weight: 600; }
.hcountry-when { font-weight: 400; color: var(--muted); font-size: var(--t-md); }
.health-missing { margin-top: 14px; font-size: var(--t-md); color: var(--muted); }
.cnote-body em, .health-whole em { font-style: italic; color: var(--ink); }

/* Money. Read standing up, so: big local amounts, pounds beside them, no
   typing. Age shown on every rate because an undated rate reads as current. */
.rates-main { padding: 0 12px 88px; max-width: 720px; margin: 0 auto; }
.rates-title { font-size: var(--t-2xl); margin: 14px 0 4px; }
.rates-lede { margin: 0 0 16px; font-size: var(--t-md); color: var(--muted); }
.rate {
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-left: 4px solid var(--best);
  border-radius: var(--r-md); padding: 12px 14px; margin-bottom: 12px;
}
.rate-off { border-left-color: var(--line); opacity: 0.75; }
.rate-stale { border-left-color: var(--accent); }
.rate-head { margin: 0 0 8px; font-size: var(--t-lg); display: flex; flex-wrap: wrap;
  gap: 8px; align-items: baseline; }
.rate-sym { font-size: var(--t-2xl); font-weight: 650; }
.rate-where { font-size: var(--t-sm); color: var(--muted); font-weight: 400; }
.rate-line { margin: 10px 0 10px; }
/* The whole closed card is the tap target. */
/* Same shape on every card, so none wraps: symbol on the left, name over
   country beside it, the rate on the right and never broken. */
.rate-sum { list-style: none; cursor: pointer; display: flex; align-items: center;
  gap: 12px; padding-right: 28px; position: relative; }
.rate-sum::-webkit-details-marker { display: none; }
.rate-sum .rate-head { margin: 0; flex: 1 1 auto; min-width: 0; display: grid;
  grid-template-columns: auto 1fr; column-gap: 10px; row-gap: 2px; align-items: center; }
.rate-sum .rate-sym { grid-row: 1 / span 2; min-width: 1.6em; }
.rate-sum .rate-name { line-height: 1.2; }
.rate-sum .rate-where { line-height: 1.3; }
.rate-pound { font-size: var(--t-lg); font-weight: 650; white-space: nowrap; flex: none; }
.rate-sum::after { content: ""; position: absolute; right: 6px; top: 50%; width: 9px; height: 9px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-70%) rotate(45deg); }
details[open] > .rate-sum::after { transform: translateY(-30%) rotate(225deg); }
.rate-age { font-size: var(--t-sm); color: var(--muted); margin-left: 8px; }
.rate-age-old { color: var(--accent); }
.rate-none { color: var(--muted); font-size: var(--t-base); }
.rate-table { width: 100%; border-collapse: collapse; font-size: var(--t-lg); }
.rate-table th, .rate-table td { padding: 7px 4px; text-align: left; }
.rate-table th { font-weight: 600; width: 55%; }
.rate-table td { font-weight: 650; }
.rate-table tr + tr th, .rate-table tr + tr td { border-top: 1px solid var(--line); }
.rate-note { margin: 10px 0 0; font-size: var(--t-md); line-height: 1.55; }
/* The calculator. Big boxes and a big figure: it is used standing up. */
.rate-calc { display: flex; align-items: center; gap: 8px; margin: 12px 0 0; }
.rate-calc[hidden] { display: none; }
.rate-calc-f { flex: 1 1 0; min-width: 0; display: flex; align-items: center; gap: 6px;
  border: 1px solid var(--line); border-radius: 10px; padding: 0 10px; }
.rate-calc-u { font-weight: 600; font-size: var(--t-lg); }
.rate-calc-f input { flex: 1 1 auto; min-width: 0; width: 100%; border: 0; background: transparent;
  color: inherit; font: inherit; font-size: var(--t-xl); font-weight: 650; padding: 10px 0; }
.rate-calc-f input:focus { outline: none; }
.rate-calc-f:focus-within { border-color: var(--accent); }
.rate-calc-eq { font-size: var(--t-lg); font-weight: 600; }
.rate-src { margin: 8px 0 0; font-size: var(--t-sm); }
.rates-foot { margin: 18px 0 0; font-size: var(--t-md); color: var(--muted); line-height: 1.55; }

/* The choosing half of the start screen, folded. Nine tiles was a directory. */
.tiles-fold { max-width: 560px; margin: 4px auto 0; padding: 0 12px; }
.tiles-fold > summary {
  cursor: pointer; padding: 12px 2px; color: var(--muted); font-size: var(--t-base);
}
.tiles-fold[open] > summary { margin-bottom: 4px; }

/* What the other editor just did. Above the tiles, because it is news. */
.whatsnew {
  max-width: 560px; margin: 4px auto 10px; padding: 10px 12px;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-md);
  font-size: var(--t-md); line-height: 1.5;
}
.wn { margin: 0 0 4px; }
.wn:last-child { margin-bottom: 0; }
.wn-ago { color: var(--muted); font-size: var(--t-sm); }

/* ---- durations in days, beside the weeks ------------------------------- */
/* Weeks is how a stay is CHOSEN and stays the primary figure; days is what a
   visa cap and the window are counted in, so it sits beside as the secondary
   reading rather than replacing it. Asked for 2026-09-17. */
.step-days, .route-fit-days {
  font-variant-numeric: tabular-nums;
  opacity: 0.7;
  font-size: var(--t-sm);
  white-space: nowrap;
}
.route-fit-days { display: block; margin-top: 2px; }

/* ---- the vaccine summary ----------------------------------------------- */
/* Asked for so it can be taken to a travel clinic. Overview on the surface —
   name, deadline, countries — and the reasoning, doses and citation one tap
   deeper, which is the rule the whole redesign runs on. */
.health-countdown { margin: 4px 0 14px; font-size: var(--t-lg); }
.vax { margin: 18px 0 26px; }
.vax-cat {
  font-size: var(--t-base); margin: 20px 0 6px; padding-bottom: 4px;
  border-bottom: 2px solid var(--surface-2);
}
.vax-cat-some { color: var(--ink); }
/* Group cards: the same closed-card shape as the Money page. */
.vax-gcard { background: var(--card); color: var(--ink); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 12px 14px; margin-bottom: 10px; }
.vax-gsum { list-style: none; cursor: pointer; position: relative; padding-right: 28px; }
.vax-gsum::-webkit-details-marker { display: none; }
.vax-gsum::after { content: ""; position: absolute; right: 6px; top: 50%; width: 9px; height: 9px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-70%) rotate(45deg); }
.vax-gcard[open] > .vax-gsum::after { transform: translateY(-30%) rotate(225deg); }
.vax-gsum .vax-cat { margin: 0 0 6px; padding: 0; border: 0; }
.vax-gfacts { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; font-size: var(--t-md); }
.vax-gn { min-width: 1.8em; text-align: center; font-weight: 650; border-radius: 999px;
  background: var(--surface-2); padding: 1px 8px; }
.vax-gcost { font-weight: 650; font-size: var(--t-lg); }
.vax-gfree { color: var(--muted); }
.vax-gcard[open] > .vax-group { margin-top: 12px; }
.vax-group { display: flex; flex-direction: column; gap: 6px; }
.vax-row {
  background: var(--surface); border: 1px solid var(--surface-2);
  border-radius: 10px; overflow: hidden;
}
.vax-row > summary {
  padding: 10px 12px; cursor: pointer; display: flex;
  flex-direction: column; gap: 3px; list-style: none;
}
.vax-row > summary::-webkit-details-marker { display: none; }
.vax-name { font-weight: 700; font-size: var(--t-lg); }
.vax-free, .vax-paid {
  font-size: var(--t-xs); font-weight: 700; padding: 1px 5px; border-radius: 4px;
  vertical-align: 2px; margin-left: 4px;
}
.vax-free { background: #7bbf86; color: #10240f; }
.vax-paid { background: var(--surface-2); }
.vax-when { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: var(--t-md); }
.vax-need { opacity: 0.7; }
.vax-unknown { opacity: 0.7; font-style: italic; }
.vax-where { font-size: var(--t-sm); opacity: 0.85; }
/* A stay inside the stated risk season is the whole reason the season is
   stored — otherwise the page prints "May–Oct" next to "3 Feb – 10 Mar" and
   leaves somebody to compare them. */
.vax-inseason { color: #8a3b2c; font-weight: 700; }
.vax-offseason { opacity: 0.65; }
/* Overdue and soon are about the BOOKING advice biting, not about the dose
   being late — six weeks is the far end of "book four to six weeks ahead". */
.vax-overdue { border-left: 4px solid #dd8071; }
.vax-soon { border-left: 4px solid #e8c65c; }
.vax-body { padding: 0 12px 12px; font-size: var(--t-base); }
.vax-protects { font-weight: 600; margin: 2px 0 8px; }
.vax-facts { margin: 10px 0 0; }
.vax-facts dt {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.04em;
  opacity: 0.65; margin-top: 10px;
}
.vax-facts dd { margin: 2px 0 0; }
.vax-stays { margin: 4px 0 0; padding-left: 18px; }
.vax-stays li { margin-bottom: 5px; }
.vax-stay-note { display: block; opacity: 0.8; }
.vax-offroute { opacity: 0.8; }
/* Price quote: the subtotal is the thing, the per-dose breakdown is smaller. */
.vax-total {
  display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: baseline;
  margin: 8px 0 14px; padding: 10px 12px; border-radius: 8px;
  background: var(--surface-2); font-size: var(--t-md);
}
.vax-total strong { font-size: var(--t-xl); }
.vax-total-q { font-size: var(--t-sm); opacity: 0.75; }
.vax-price { display: flex; flex-wrap: wrap; gap: 0 8px; align-items: baseline; }
.vax-price-sum { font-weight: 700; font-size: var(--t-md); }
.vax-price-each, .vax-price-gp { font-size: var(--t-xs); opacity: 0.75; }
.vax-price-free .vax-price-sum { font-weight: 400; opacity: 0.55; }
.vax-src { margin-top: 12px; font-size: var(--t-sm); opacity: 0.75; }

@media (min-width: 760px) {
  /* Wide enough to read as a table: the three summary parts become columns. */
  .vax-row > summary {
    flex-direction: row; align-items: baseline; gap: 14px;
  }
  .vax-name { flex: 0 0 15rem; }
  .vax-when { flex: 0 0 19rem; flex-direction: column; gap: 1px; }
  .vax-where { flex: 1; }
}

/* A course whose derived start date falls after departure. The date is right
   and unusable — the clinic is in the UK — so the row explains rather than
   instructs. */
.vax-abroad { background: var(--surface-2); padding: 8px 10px; border-radius: 8px; }

/* A follow-up dose landing mid-trip is the common case on a 46-week trip, not
   the exception — four of the twelve. Marked on the surface because it is a
   thing to settle before leaving. */
.vax-followup, .vax-expires { font-size: var(--t-sm); font-weight: 600; }
.vax-expires { color: #8a3b2c; }
.vax-together {
  background: var(--surface); border: 1px solid var(--surface-2);
  border-radius: 10px; margin: 12px 0 18px;
}
.vax-together > summary { padding: 10px 12px; cursor: pointer; font-weight: 700; }
.vax-together-body { padding: 0 12px 12px; font-size: var(--t-base); }
.vax-together-body p { margin: 0 0 10px; }

/* live vs inactivated. Named on every row because the spacing question, the
   pregnancy caution and the immunosuppression caution all hang off it — and
   because a hand-written sentence claiming "everything except MMR" was wrong. */
.vax-kind { display: block; font-size: var(--t-sm); opacity: 0.8; margin-top: 2px; }
.vax-kind-live { font-weight: 600; opacity: 1; }

/* The three facts the table exists to answer, on the card face. Tabular and
   short so the column scans down the list rather than reading as a sentence. */
.vax-facts-line {
  display: flex; flex-wrap: wrap; gap: 4px 8px; font-size: var(--t-sm);
  align-items: baseline;
}
.vax-doses { font-weight: 700; }
.vax-kind-tag {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.03em;
  padding: 1px 5px; border-radius: 4px; background: var(--surface-2);
}
/* Live is the one with consequences — pregnancy, immunosuppression, spacing —
   so it is the one that gets the ink. */
.vax-kind-live { font-weight: 700; }
.vax-lasts { opacity: 0.8; }
/* The row opens. Nothing said so once the default triangle was removed. */
.vax-more { float: right; opacity: 0.45; font-weight: 400; }
.vax-row[open] .vax-more { transform: rotate(90deg); display: inline-block; }

/* ---- food ---------------------------------------------------------------- */
/* The verdict is the content. A dish nobody can eat is still worth listing —
   knowing pho is off is as useful as knowing the coffee is fine — so it stays
   on the page and carries its reason rather than being filtered away. */
.food-lede { margin-bottom: 12px; }
.food-who { background: var(--surface); border-radius: 10px; margin-bottom: 16px; }
.food-who > summary { padding: 10px 12px; cursor: pointer; font-weight: 700; }
.food-who-body { padding: 0 12px 12px; font-size: var(--t-md); }
.food-who-body h3 { margin: 10px 0 4px; font-size: var(--t-base); }
.food-who-body ul { margin: 0; padding-left: 18px; }
.food-rule-note { display: block; opacity: 0.8; }
.food-cty { font-size: var(--t-xl); margin: 20px 0 2px; }
.food-count { font-size: var(--t-sm); font-weight: 600; opacity: 0.75; }
.food-when { margin: 0 0 8px; font-size: var(--t-sm); opacity: 0.7; }
.dish {
  background: var(--surface); border: 1px solid var(--surface-2);
  border-radius: 10px; margin-bottom: 6px;
}
/* A green edge means both of you. It is the only thing most people will look
   at, so it is a colour and not a word. */
.dish-both { border-left: 4px solid #7bbf86; }
.dish-no { opacity: 0.88; }
.dish > summary { padding: 10px 12px; cursor: pointer; list-style: none; }
.dish > summary::-webkit-details-marker { display: none; }
.dish-name { font-weight: 700; display: block; }
.dish-local { font-weight: 400; opacity: 0.7; font-size: var(--t-md); }
.dish-tagline { display: block; font-size: var(--t-xs); opacity: 0.7; margin-top: 2px; }
.dish-verdicts { display: flex; flex-wrap: wrap; gap: 4px 8px; margin-top: 4px; }
.dv { font-size: var(--t-xs); padding: 1px 6px; border-radius: 4px; font-weight: 600; }
.dv-yes { background: #7bbf86; color: #10240f; }
.dv-ask { background: #e8c65c; color: #2b2410; }
.dv-no  { background: #dd8071; color: #2b1310; }
.dish-body { padding: 0 12px 12px; font-size: var(--t-base); }
.dish-why { margin: 8px 0; padding-left: 18px; }
.dv-line.dv-yes, .dv-line.dv-ask, .dv-line.dv-no { background: none; padding: 0; font-weight: 400; }
.dish-tags { font-size: var(--t-sm); opacity: 0.85; margin: 6px 0 0; }
.dish-askable { opacity: 0.75; }
/* Blended in rather than in pieces. Said plainly rather than hidden, because
   the verdict it produces is a YES and a silent pass would look like the page
   had missed the shallot. */
.dish-blended { opacity: 0.85; }
.dish-seasonnote { font-style: italic; opacity: 0.8; }
.dv-excused { display: block; font-size: var(--t-sm); opacity: 0.8; }
.food-rule-form { display: block; opacity: 0.85; }
.dish-season {
  font-size: var(--t-xs); padding: 1px 6px; border-radius: 4px;
  background: var(--surface-2); font-weight: 600;
}
.dish-season.out { opacity: 0.7; text-decoration: line-through; }
.dish-where { margin: 8px 0 0; }
.dish-src { margin-top: 10px; font-size: var(--t-sm); opacity: 0.75; }

/* The two folds under each country: the sentence to say, and the market.
   Both are behind a tap because the dish list is what the page is for — but
   the phrases sit ABOVE the dishes, since they work on the dishes nobody
   researched too. */
.food-fold {
  background: var(--surface); border: 1px solid var(--surface-2);
  border-radius: 10px; margin-bottom: 8px;
}
.food-fold > summary { padding: 10px 12px; cursor: pointer; font-weight: 700; }
.fold-icon { margin-right: 4px; }
.phrase-list { list-style: none; margin: 0; padding: 0 12px 12px; }
.phrase { padding: 8px 0; border-top: 1px solid var(--surface-2); }
.ph-en { display: block; font-weight: 600; font-size: var(--t-md); }
.ph-local { display: block; font-size: var(--t-xl); line-height: 1.5; }
.ph-roman { display: block; font-size: var(--t-md); opacity: 0.8; font-style: italic; }
.ph-note { display: block; font-size: var(--t-sm); opacity: 0.85; margin-top: 4px; }
.ph-src { font-size: var(--t-xs); opacity: 0.6; }
.prod { border-top: 1px solid var(--surface-2); }
/* Picture on the left, words on the right. The picture is half the answer when
   the thing is unfamiliar and the label is in a script you cannot read — so it
   is on the row, not behind the tap, and it degrades to an emoji. */
.prod > summary {
  padding: 8px 12px; cursor: pointer; list-style: none;
  display: flex; gap: 10px; align-items: flex-start;
}
.prod > summary::-webkit-details-marker { display: none; }
.prod-out { opacity: 0.6; }
.prod-photo-wrap { flex: 0 0 auto; position: relative; }
.prod-photo {
  width: 64px; height: 64px; object-fit: cover; display: block;
  border-radius: 8px; background: var(--line);
}
.prod-fallback {
  width: 64px; height: 64px; background: var(--line); font-size: var(--t-3xl);
  border-radius: 8px; display: flex; align-items: center; justify-content: center;
}
.prod-info { flex: 1 1 auto; min-width: 0; }
.prod-name { font-weight: 700; display: block; }
.prod-local { font-weight: 400; opacity: 0.7; font-size: var(--t-md); }
/* What it tastes like is the whole reason the table exists, so it is on the
   face of the row and not behind the tap. */
.prod-taste { display: block; font-size: var(--t-md); }
.prod-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.prod-chip {
  font-size: var(--t-xs); padding: 1px 6px; border-radius: 4px;
  background: var(--surface-2); font-weight: 600;
}
/* "Must be cooked" is occasionally a medical claim rather than a preference,
   so it is the one chip that is allowed to shout. */
.prod-cook { background: #e8c65c; color: #2b2410; }
.prod-season.out { text-decoration: line-through; opacity: 0.8; }
.prod-body { padding: 0 12px 12px; font-size: var(--t-md); }
.prod-body p { margin: 6px 0; }
.prod-prepnote { background: var(--surface-2); padding: 6px 8px; border-radius: 6px; }
.prod-price { opacity: 0.8; }

/* ---- you ----------------------------------------------------------------- */
/* The things that are yours. Two sections, never one flat list: a personal
   preference and a shared trip rule are different kinds of thing, and the
   whole application is arranged around keeping "mine" and "ours" apart. */
.you-main { padding: 0 12px 84px; }
.you-title { font-size: var(--t-2xl); margin: 14px 0 18px; }
.you-head { font-size: var(--t-lg); margin: 20px 0 2px; }
.you-sub { font-size: var(--t-md); opacity: 0.75; margin: 0 0 8px; }
.you-readonly { font-size: var(--t-md); opacity: 0.8; }
/* Your name, in the bar at the top of every screen, is a door rather than a
   label. It was plain text until 2026-09-18, while settings sat two taps deep
   under "Look things up" — a browse verb for a thing nobody browses. */
a.who { text-decoration: none; }
a.who:hover, a.who:focus { text-decoration: underline; }
.start-who a { color: inherit; text-decoration: none; }
.start-who a:hover, .start-who a:focus { text-decoration: underline; }

/* ---- what you eat -------------------------------------------------------- */
/* Pure form POSTs, no JavaScript, like the to-do list. One card per thing you
   avoid, because a table of selects is unreadable on a phone and worse for a
   dyslexic reader than one question at a time. */
.diet-main { padding: 0 12px 84px; }
.diet-title { font-size: var(--t-2xl); margin: 14px 0 4px; }
.diet-lede { font-size: var(--t-base); opacity: 0.8; margin: 0 0 14px; }
.diet-ok, .diet-err {
  padding: 8px 10px; border-radius: 8px; font-size: var(--t-md); margin: 0 0 12px;
}
.diet-ok { background: #7bbf86; color: #10240f; }
.diet-err { background: #dd8071; color: #2b1310; }
.diet-readonly { font-size: var(--t-md); opacity: 0.8; }
.diet-rule {
  background: var(--surface); border: 1px solid var(--surface-2);
  border-radius: 10px; padding: 12px; margin-bottom: 12px;
}
.diet-rule-name { font-size: var(--t-lg); margin: 0 0 10px; }
.diet-field { display: block; margin-bottom: 10px; }
.diet-field > span {
  display: block; font-size: var(--t-sm); opacity: 0.75; margin-bottom: 3px;
}
.diet-field select, .diet-field textarea {
  width: 100%; padding: 8px; border-radius: 8px;
  border: 1px solid var(--surface-2); background: var(--bg); color: inherit;
  font: inherit;
}
.diet-formnote { font-size: var(--t-sm); opacity: 0.75; margin: 0 0 10px; }
.diet-actions { display: flex; gap: 12px; align-items: baseline; }
.diet-actions button[type="submit"] {
  padding: 9px 16px; border-radius: 8px; border: 0;
  background: var(--accent, #4a7ebb); color: #fff; font: inherit; font-weight: 700;
}
.diet-actions .linkish { background: none; border: 0; color: inherit; opacity: 0.7; }
.diet-vocab { font-size: var(--t-sm); opacity: 0.75; }

/* The food table on Settings: one row per thing, one Save per person.
   The first version was a card and a Save button per rule, which read as a
   form generator rather than a settings screen (Aldred, 2026-09-18:
   "clunky and amateurish"). Columns on a phone are tight, so the note gets
   its own row underneath rather than a fifth column. */
.settings-links { margin-bottom: 18px; }
.settings-head { font-size: var(--t-xl); margin: 22px 0 4px; }
.diet-person { margin-bottom: 26px; }
.diet-who { font-size: var(--t-xl); margin: 0 0 6px; }
.diet-table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.diet-table th { text-align: left; font-weight: 600; }
.diet-table thead th { font-size: var(--t-xs); opacity: 0.6; padding-bottom: 4px; }
.diet-table tbody th { padding: 6px 6px 6px 0; white-space: nowrap; }
.diet-table td { padding: 4px 3px; }
.diet-table select {
  width: 100%; padding: 5px; border-radius: 6px; font-size: var(--t-sm);
  border: 1px solid var(--surface-2); background: var(--bg); color: inherit;
}
.diet-noterow td { padding: 0 0 10px; }
.diet-noterow textarea {
  width: 100%; padding: 6px; border-radius: 6px; font: inherit;
  font-size: var(--t-sm); border: 1px solid var(--surface-2);
  background: var(--bg); color: inherit;
}
.diet-cell-x { width: 26px; }
.diet-x {
  background: none; border: 0; color: inherit; opacity: 0.5;
  font-size: var(--t-xl); line-height: 1; padding: 2px 4px; cursor: pointer;
}
.diet-na { opacity: 0.4; }
.diet-save {
  margin-top: 8px; padding: 9px 16px; border-radius: 8px; border: 0;
  background: var(--accent, #4a7ebb); color: #fff; font: inherit; font-weight: 700;
}
.diet-add { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.diet-add select {
  flex: 1 1 44%; padding: 7px; border-radius: 8px; font-size: var(--t-sm);
  border: 1px solid var(--surface-2); background: var(--bg); color: inherit;
}
.diet-add button {
  padding: 7px 14px; border-radius: 8px; border: 1px solid var(--surface-2);
  background: var(--surface); color: inherit; font: inherit;
}
.diet-empty { font-size: var(--t-md); opacity: 0.75; }

/* ==========================================================================
   The 2026-09-18 refresh.

   Asked for as "modernise look and feel, make more dynamic, respect phone
   real estate". Everything here is plain CSS with no build step and nothing
   that blocks first paint, because the normal case for this app is a phone on
   bad hotel wifi in Asia. What is NOT here is as deliberate: no web fonts
   (they delay text), no heavy blur beyond the one bar that already had it, and
   no motion that a reader has not opted into.

   Research behind it: docs/design-research-2026-09.md, with browser support
   and dates for every feature used.
   ========================================================================== */

/* ---- movement between pages ---------------------------------------------
   This is a server-rendered multi-page app, so every tap has always been a
   full page load with a white flash. One rule gives the whole site a native
   cross-fade instead, with NO JavaScript: Safari 18.2+ and Chrome 126+ honour
   it, everything else navigates exactly as it does today. It costs nothing on
   a slow connection because it is not a download.

   This is the "more dynamic" of the brief, bought for four lines. */
@view-transition { navigation: auto; }

::view-transition-old(root) { animation-duration: 140ms; }
::view-transition-new(root) { animation-duration: 140ms; }

/* The bars are the frame, not the content: holding them still while the page
   beneath them changes is what makes the app feel like an app rather than a
   website. */
.topbar { view-transition-name: topbar; }
.bottomnav { view-transition-name: bottomnav; }

/* ---- motion, and the right to refuse it ---------------------------------
   The stylesheet had no prefers-reduced-motion block at all, which is an
   accessibility obligation rather than a nicety — for some readers motion is
   nausea, not delight. Everything above is turned off here, not softened. */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---- the press ----------------------------------------------------------
   A card that does not acknowledge the tap reads as a picture of a card. This
   is the cheapest "alive" there is: no JavaScript, no layout shift, and it
   only fires on touch. */
.tile, .dish, .prod, .food-fold, .more-list a, .poi, .phrase {
  transition: transform 120ms ease, box-shadow 120ms ease, background-color 120ms ease;
}
.tile:active, .more-list a:active, .dish > summary:active, .prod > summary:active {
  transform: scale(0.985);
}
/* Tap targets stop flashing a grey box on Android and get their own state. */
a, button, summary, label { -webkit-tap-highlight-color: transparent; }

/* ---- headings that do not break badly -----------------------------------
   `balance` stops the one-word last line, which is worth more here than
   anywhere: a ragged orphan is exactly the kind of thing that costs a
   dyslexic reader a re-read. Safari 17.5+, Chrome 114+, and where it is not
   understood the heading simply wraps as it does now. */
h1, h2, h3, .tile-name, .dish-name, .prod-name, .ph-en, .food-cty {
  text-wrap: balance;
}
/* Additive and unguarded: improves the rag on body prose where supported and
   does nothing at all where it is not. */
p, .tile-state, .dish-summary, .prod-taste, .more-list a span { text-wrap: pretty; }

/* ---- phone real estate --------------------------------------------------
   The two bars are fixed furniture on every screen, so every pixel they take
   is taken from the content on all of them. The top bar loses 4px of padding
   and gains the notch inset it never had; the bottom bar tightens and stops
   its label from wrapping on a narrow phone.

   `svh` rather than `dvh` for anything that must HOLD STILL: dvh recalculates
   during the iOS toolbar's show/hide animation, which is right for a map
   filling the screen and wrong for a bar. */
.topbar {
  padding: 9px var(--s-4);
  padding-top: max(9px, env(safe-area-inset-top));
  padding-left: max(var(--s-4), env(safe-area-inset-left));
  padding-right: max(var(--s-4), env(safe-area-inset-right));
}
.bottomnav {
  padding-bottom: max(2px, env(safe-area-inset-bottom));
  min-height: 3.25rem;
}

/* ---- long lists stay cheap ----------------------------------------------
   Skips layout and paint for sections below the fold until they are needed,
   which is the whole game on a low-end Android. The intrinsic size estimate
   is what stops the scrollbar jumping as they come into view. */
.food-country, .cmp-section, .search-group, .poi-grid {
  content-visibility: auto;
  contain-intrinsic-size: auto 400px;
}

/* ---- notes that grow ----------------------------------------------------
   A textarea that scrolls internally at four lines is a worse way to read
   back what you wrote than one that simply gets taller. Purely additive. */
textarea { field-sizing: content; min-height: 3lh; }

/* ---- the start screen, which is the one everybody sees ------------------
   Eight tiles at 9rem each is most of two screens on a phone. Tightened, and
   given a third column as soon as there is room for one — a container query
   rather than a viewport one, so the grid answers to the space it is actually
   in rather than to the size of the phone. */
.tiles { container-type: inline-size; gap: var(--s-3); padding: var(--s-4); }
.tile { min-height: 7.75rem; padding: var(--s-4); gap: var(--s-1); }
@container (min-width: 30rem) {
  .tiles { grid-template-columns: repeat(3, 1fr); }
}

/* A tile that wants attention says so with its own surface rather than a
   heavier border. Borders that thicken make a grid look uneven; a tint keeps
   the geometry still and reads faster, which is the whole argument for colour
   over words on this screen. */
.tile-needs {
  border-color: color-mix(in oklab, var(--accent) 45%, var(--line));
  background: color-mix(in oklab, var(--accent) 6%, var(--card));
}

/* ---- surfaces -----------------------------------------------------------
   Cards were a hairline border AND a shadow, which is belt and braces and
   reads slightly heavy at small sizes. The border stays because it is what
   holds the edge in dark mode, but it is mixed toward the surface so it
   separates without drawing. */
.dish, .prod, .food-fold, .diet-rule, .tile {
  border-color: color-mix(in oklab, var(--line) 70%, var(--card));
}

/* ---- lists that read as one thing ---------------------------------------
   The Look up and Settings lists were a stack of separately-bordered rows.
   One card, hairlines between: fewer edges, same information, and the tap
   target is still the whole row. */
.more-list {
  background: var(--card);
  border: 1px solid color-mix(in oklab, var(--line) 70%, var(--card));
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-1);
}
.more-list a {
  border: 0;
  border-bottom: 1px solid color-mix(in oklab, var(--line) 60%, var(--card));
  border-radius: 0;
  margin: 0;
  padding: var(--s-3) var(--s-4);
}
.more-list a:last-child { border-bottom: 0; }
.more-list a:active { background: color-mix(in oklab, var(--accent) 7%, var(--card)); }

/* ==========================================================================
   Chrome and surfaces, restyled — 2026-09-18, second pass.

   "Better but still feels dated." The first pass fixed the type and added
   movement; what was left dating it was structural. Three things, in the
   order they were doing damage: emoji as icons, a warm editorial palette, and
   a bordered-and-shadowed card around absolutely everything.
   ========================================================================== */

/* ---- icons --------------------------------------------------------------
   One size, one weight, following the text into dark mode because they are
   stroked in currentColor. Optical sizing is per context: a tile icon is
   furniture and can be large, a bottom-bar icon is a target and must not
   crowd its label. */
.ic { width: 1.5rem; height: 1.5rem; display: block; flex: none; }
.tile-ic .ic { width: 1.75rem; height: 1.75rem; }
.nav-ic .ic { width: 1.375rem; height: 1.375rem; }
.fold-icon .ic { width: 1.25rem; height: 1.25rem; }
.tile-ic, .nav-ic, .fold-icon { display: inline-flex; line-height: 1; }

/* The colour the emoji used to carry, moved to the tile itself. Recognising a
   screen by its colour before reading its name matters more here than in most
   applications — the tool exists for a visual learner as much as anybody — so
   losing the emoji must not mean losing the cue. */
.tile-trip   .ic { color: #2f6fed; }
.tile-route  .ic { color: #0e9f6e; }
.tile-travel .ic { color: #7c5cff; }
.tile-rates  .ic { color: #b7791f; }
.tile-budget .ic { color: #0f8b8d; }
.tile-food   .ic { color: #e4572e; }
.tile-health .ic { color: #d6336c; }
.tile-todo   .ic { color: #2b8a9c; }
.tile-you    .ic { color: var(--muted); }
.tile-decide .ic, .tile-plan .ic, .tile-look .ic { color: var(--muted); }

/* ---- surfaces -----------------------------------------------------------
   Shadow off, hairline on. A card that floats above the page was the house
   style of 2016; a card that is simply a lighter rectangle with a clean edge
   is the house style now, and it survives dark mode far better. */
.tile, .dish, .prod, .food-fold, .diet-rule, .more-list, .poi {
  box-shadow: none;
  border-color: var(--line);
}
.tile-needs { box-shadow: none; }

/* ---- the top bar --------------------------------------------------------
   A centred small title between two text links is a 2014 pattern. The title
   goes big and left, where the eye starts; the back link becomes an icon-only
   target, which also gives the title the width it needs on a narrow phone. */
.topbar {
  gap: var(--s-2);
  background: color-mix(in oklab, var(--card) 86%, transparent);
  border-bottom: 1px solid color-mix(in oklab, var(--line) 70%, transparent);
}
.topbar .back {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.25rem; height: 2.25rem; margin-left: -0.5rem;
  border-radius: var(--r-pill); color: var(--ink); text-decoration: none;
  font-size: 0; /* the word is replaced by the glyph below */
}
.topbar .back::before {
  content: ""; width: 1.4rem; height: 1.4rem; display: block;
  background: currentColor;
  -webkit-mask: var(--chev) center / contain no-repeat;
  mask: var(--chev) center / contain no-repeat;
}
.topbar .back:active { background: var(--surface-2); }
.topbar-title {
  font-size: var(--t-xl); font-weight: 700; letter-spacing: -0.015em;
  margin-right: auto;
}
.topbar .who {
  font-size: var(--t-sm); color: var(--muted); text-decoration: none;
  padding: 6px 10px; border-radius: var(--r-pill); background: var(--surface-2);
}
:root {
  --chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 19 8 12l7-7'/%3E%3C/svg%3E");
}

/* ---- page titles --------------------------------------------------------
   Large, tight, left. The single most reliable way to make a screen read as
   current, and it costs nothing but leading. */
h1, .food-title, .diet-title, .you-title, .settings-main h1, .tiles-head {
  font-size: var(--t-3xl); font-weight: 700; letter-spacing: -0.02em;
  line-height: 1.15;
}

/* ---- the bottom bar -----------------------------------------------------
   Tighter, quieter, and the active item marked by weight and colour rather
   than by a heavier box. */
.bottomnav a {
  font-size: var(--t-xs); font-weight: 600; color: var(--muted);
  padding: 7px 0 5px;
}
.bottomnav .nav-on { color: var(--accent); }
.bottomnav { border-top: 1px solid color-mix(in oklab, var(--line) 70%, transparent); }

/* ==========================================================================
   The menu, and room to breathe — 2026-09-18, third pass.

   "Still feels dated and no menues or hamburger or dynamic elennets cluttered
   and cramped over crowded." Three complaints, one cause: there was nowhere to
   put anything, so everything was on the screen. The menu is the cupboard; the
   spacing below is what the worktop looks like once it is not covered.
   ========================================================================== */

.nav-menu {
  flex: 1; display: flex; flex-direction: column; align-items: center;
  gap: 2px; padding: 7px 0 5px; border: 0; background: none;
  color: var(--muted); font: inherit; font-size: var(--t-xs); font-weight: 600;
  cursor: pointer;
}
.nav-menu:active { color: var(--accent); }

/* ---- the sheet ----------------------------------------------------------
   A bottom sheet rather than a side drawer, because a thumb reaches the
   bottom of a phone and does not reach the top left of one. */
.sheet {
  position: fixed; inset: auto 0 0 0; width: 100%; max-width: none;
  max-height: 88svh; margin: 0; padding: 0; overflow-y: auto;
  border: 0; border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: var(--card); color: var(--ink);
  box-shadow: 0 -8px 40px rgba(10,12,16,0.22);
  /* The entry animation. @starting-style is what makes a transition run on
     something that has just appeared, which a popover always has. */
  transition: translate 220ms cubic-bezier(0.2, 0.8, 0.2, 1),
              opacity 160ms ease,
              overlay 220ms allow-discrete,
              display 220ms allow-discrete;
  translate: 0 0;
  opacity: 1;
}
.sheet:popover-open { translate: 0 0; opacity: 1; }
@starting-style {
  .sheet:popover-open { translate: 0 100%; opacity: 0.4; }
}
.sheet::backdrop {
  background: rgba(8,10,14,0.42);
  transition: opacity 200ms ease, display 200ms allow-discrete;
  opacity: 1;
}
@starting-style { .sheet:popover-open::backdrop { opacity: 0; } }

/* The grab handle is not decoration: it is the thing that says "this came up
   from the bottom and can go back down", which is how anybody knows what a
   sheet is without being told. */
.sheet-handle {
  width: 2.5rem; height: 0.25rem; border-radius: var(--r-pill);
  background: var(--line); margin: var(--s-3) auto var(--s-2);
}
.sheet-body { padding: 0 var(--s-4) calc(var(--s-5) + env(safe-area-inset-bottom)); }
.sheet-head { font-size: var(--t-2xl); font-weight: 700; letter-spacing: -0.02em; margin: 0 0 var(--s-3); }
.sheet-group {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted);
  margin: var(--s-5) 0 var(--s-2);
}
.sheet-list { display: flex; flex-direction: column; }
.sheet-list a {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-2); margin: 0 calc(var(--s-2) * -1);
  border-radius: var(--r-md);
  color: inherit; text-decoration: none; font-size: var(--t-lg); font-weight: 500;
}
.sheet-list a .ic { color: var(--muted); }
.sheet-list a:active { background: var(--surface-2); }
.sheet-close {
  display: block; width: 100%; margin-top: var(--s-5);
  padding: var(--s-3); border: 0; border-radius: var(--r-md);
  background: var(--surface-2); color: var(--ink);
  font: inherit; font-size: var(--t-lg); font-weight: 600;
}

/* ---- room -------------------------------------------------------------
   "Cramped and overcrowded". Everything below is space, and nothing below
   changes what any screen says. Density was the app's real problem: it was
   built by adding one justified thing at a time, which is exactly how the
   2026-09-02 redesign happened and exactly what its lesson warned about —
   the constraint binds the SUM, not the addition. */
main { padding-bottom: calc(5.5rem + env(safe-area-inset-bottom)); }
.food-main, .diet-main, .settings-main, .more-main, .you-main {
  padding-left: var(--s-4); padding-right: var(--s-4);
}
.dish, .prod, .food-fold, .diet-rule, .more-list { margin-bottom: var(--s-3); }
.dish > summary, .food-fold > summary { padding: var(--s-3) var(--s-4); }
.dish-body, .prod-body, .food-who-body { padding: 0 var(--s-4) var(--s-4); }
.food-country { margin-bottom: var(--s-6); }
.food-cty { margin-top: var(--s-5); }
.more-list a { padding: var(--s-4); gap: var(--s-1); }
.sheet-list a, .more-list a, .bottomnav a, .nav-menu { min-height: 2.75rem; }

/* Lines of text that are too long to track are as tiring as text that is too
   small, and the fix is the same fix: this is the dyslexia guidance, and it
   happens to be what a well-set page looks like. */
.food-lede, .diet-lede, .settings-lede, p, .dish-summary, .prod-taste {
  max-width: 62ch;
}

/* ---- the hamburger, where people look for it ----------------------------
   The menu went into the bottom bar first, because that is where a thumb
   reaches. Aldred looked at the top right, found nothing, and reported no
   menu at all — which is the answer: a control nobody finds is a control that
   is not there, whatever the markup says. Both now, and the redundancy is
   the point rather than an oversight. */
.topmenu {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.5rem; height: 2.5rem; margin-right: -0.5rem;
  border: 0; border-radius: var(--r-pill);
  background: none; color: var(--ink); cursor: pointer;
}
.topmenu:active { background: var(--surface-2); }
.topmenu .ic { width: 1.5rem; height: 1.5rem; }
.sheet-who { font-size: var(--t-sm); color: var(--muted); margin: -4px 0 0; }

/* ---- Budget and what we paid (2026-10-11) -------------------------------
   Everything here uses the type tokens and existing colours; the criteria
   green/amber/red are data and are not borrowed. */
.budget-main { padding: 0 12px 88px; max-width: 720px; margin: 0 auto; }
.budget-title { font-size: var(--t-2xl); margin: 14px 0 4px; }
.budget-lede { margin: 0 0 14px; font-size: var(--t-md); color: var(--muted); }
.budget-sofar { background: var(--card); border: 1px solid var(--line);
  border-left: 4px solid #0f8b8d; border-radius: var(--r-md); padding: 12px 14px; margin: 0 0 14px; }
.budget-sofar-n { font-variant-numeric: tabular-nums; margin: 0; font-size: var(--t-3xl); font-weight: 700; letter-spacing: -0.01em; }
.budget-sofar-l { margin: 0; font-size: var(--t-md); color: var(--muted); }
.budget-sofar-note { margin: 8px 0 0; font-size: var(--t-base); line-height: 1.5; }
.budget-table { width: 100%; border-collapse: collapse; font-size: var(--t-base); }
.budget-table th, .budget-table td { padding: 10px 4px; text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.budget-table tbody th { white-space: normal; }
.budget-table th:first-child, .budget-table td:first-child { text-align: left; }
.budget-table thead th { font-size: var(--t-sm); color: var(--muted); font-weight: 600; }
.budget-table tbody th { font-weight: 600; }
.budget-table tbody tr + tr th, .budget-table tbody tr + tr td { border-top: 1px solid var(--line); }
.budget-none { color: var(--muted); }
.budget-table .budget-rowtotal { font-weight: 650; }
.budget-remaining { margin: 12px 0 4px; font-size: var(--t-base); }
.budget-projection { margin: 4px 0 14px; padding: 10px 12px; border: 1px dashed var(--line);
  border-radius: 10px; font-size: var(--t-base); line-height: 1.5; }
.budget-lines { margin: 18px 0; }
.budget-lines h2 { font-size: var(--t-lg); margin: 0 0 4px; }
/* How we are doing (2026-10-25). Green/amber are the same two text colours the
   criteria standing line uses, lightened in dark mode so they stay readable. */
.budget-page { --bd-good: #1d5c2c; --bd-warn: #7a5410; }
@media (prefers-color-scheme: dark) { .budget-page { --bd-good: #7bbf86; --bd-warn: #e0b25a; } }
.bd-under { color: var(--bd-good); font-weight: 650; }
.bd-over { color: var(--bd-warn); font-weight: 650; }
.bd-even { font-weight: 650; }
.budget-head { background: var(--card); border: 1px solid var(--line); border-left: 4px solid var(--line);
  border-radius: var(--r-md); padding: 12px 14px; margin: 0 0 14px; }
.budget-head.budget-under { border-left-color: var(--bd-good); }
.budget-head.budget-over { border-left-color: var(--bd-warn); }
.budget-head-n { margin: 0; font-size: var(--t-2xl); font-weight: 700; letter-spacing: -0.01em; }
.budget-under .budget-head-n { color: var(--bd-good); }
.budget-over .budget-head-n { color: var(--bd-warn); }
.budget-head-sub { margin: 6px 0 0; font-size: var(--t-base); color: var(--muted); font-variant-numeric: tabular-nums; }
.budget-track, .budget-whole, .budget-countries { margin: 18px 0; }
.budget-track h2, .budget-whole h2, .budget-countries h2 { font-size: var(--t-lg); margin: 0 0 4px; }
.budget-trk td.budget-diff { white-space: normal; }
.budget-trk tfoot th, .budget-trk tfoot td { border-top: 2px solid var(--line); padding: 10px 4px; font-weight: 700; }
.budget-trk tfoot th { text-align: left; }
.stay-country { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 4px 12px; margin-bottom: 10px; }
.stay-country > summary { cursor: pointer; list-style: none; padding: 10px 0; min-height: 2.75rem;
  display: flex; flex-direction: column; gap: 6px; }
.stay-country > summary::-webkit-details-marker { display: none; }
.sc-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-size: var(--t-lg); }
.sc-nights { font-size: var(--t-md); color: var(--muted); font-variant-numeric: tabular-nums; }
.sc-bar { display: block; height: 0.6rem; border-radius: 999px; background: var(--line); overflow: hidden; }
.sc-bar i { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.sc-figs, .sc-stopfigs { display: flex; flex-wrap: wrap; gap: 2px 12px; font-size: var(--t-base);
  font-variant-numeric: tabular-nums; }
.sc-stopfigs { margin: 0 0 6px; }
.sc-tobook { color: var(--muted); }
.est-lines { list-style: none; margin: 8px 0; padding: 0; }
.est-line { border-top: 1px solid var(--line); }
.est-line:first-child { border-top: 0; }
.est-line > details > summary { cursor: pointer; list-style: none; min-height: 2.75rem; padding: 8px 0;
  display: grid; grid-template-columns: 1fr auto; gap: 2px 12px; align-items: baseline; }
.est-line > details > summary::-webkit-details-marker { display: none; }
.el-name { font-weight: 600; font-size: var(--t-base); }
.el-amt { font-size: var(--t-base); text-align: right; font-variant-numeric: tabular-nums; }
.el-state { text-align: right; }
.budget-foot { margin: 8px 0 14px; font-size: var(--t-md); color: var(--muted); line-height: 1.5; }
.budget-group { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 4px 12px; margin-bottom: 10px; }
.budget-group > summary { display: flex; justify-content: space-between; align-items: baseline;
  gap: 8px; cursor: pointer; padding: 10px 0; min-height: 2.75rem; list-style: none; }
.budget-group > summary::-webkit-details-marker { display: none; }
.budget-group > summary h2 { margin: 0; font-size: var(--t-lg); }
.budget-gtotal { font-weight: 650; font-size: var(--t-lg); }
.stay-stop { border-top: 1px solid var(--line); padding: 8px 0; }
.stay-stop h3 { margin: 0 0 4px; font-size: var(--t-base); }

.costs { list-style: none; margin: 8px 0; padding: 0; }
.cost { padding: 8px 0; border-top: 1px solid var(--line); }
.cost:first-child { border-top: 0; }
.cost p { margin: 0; line-height: 1.5; }
.cost-title { font-weight: 600; font-size: var(--t-base); }
.cost-label { font-weight: 600; font-size: var(--t-base); }
.cost-main { font-size: var(--t-base); }
.cost-paid { font-size: var(--t-lg); }
.cost-meta, .cost-nights, .cost-note { font-size: var(--t-md); color: var(--muted); }
.cost-est { font-size: var(--t-sm); color: var(--muted); margin-left: 4px; }
.cost-hint, .cost-orphan { font-size: var(--t-sm); color: var(--muted); }
.cost-chip { font-size: var(--t-xs); font-weight: 600; color: #0f8b8d;
  border: 1px solid currentColor; border-radius: 999px; padding: 1px 8px; }
.cost-acts { font-size: var(--t-md); display: flex; gap: 16px; }
.cost-acts a { padding: 8px 0; min-height: 2.75rem; display: inline-flex; align-items: center; }
.stay-nights { margin: 8px 0; font-size: var(--t-base); display: flex; flex-wrap: wrap;
  gap: 4px 8px; align-items: center; }
.stay-nights meter { width: 100%; height: 0.6rem; }
.stay-daytoday { margin: 4px 0 8px; font-size: var(--t-sm); color: var(--muted, inherit); }
.stay-gap strong { color: var(--ink); }

.costadd { margin: 8px 0; }
.costadd > summary { cursor: pointer; font-weight: 600; font-size: var(--t-base);
  color: var(--accent); padding: 10px 0; min-height: 2.75rem; display: flex; align-items: center; }
.costform { display: flex; flex-direction: column; gap: 10px; padding: 4px 0 10px; }
.cf { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }
.cf > span { font-size: var(--t-md); color: var(--muted); }
.cf input, .cf select { font: inherit; font-size: var(--t-lg); color: var(--ink);
  background: var(--field); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 10px 12px; min-height: 2.75rem; box-sizing: border-box; width: 100%; }
.cf-row { display: flex; gap: 10px; }
.cf-save { font: inherit; font-size: var(--t-lg); font-weight: 600; cursor: pointer;
  color: #fff; background: var(--accent); border: 0; border-radius: var(--r-md);
  padding: 12px 16px; min-height: 2.75rem; }
.cf-danger { background: #b42318; }
.cost-error { margin: 0; padding: 10px 12px; border: 2px solid #b42318;
  border-radius: var(--r-md); font-size: var(--t-base); line-height: 1.5; }
.cost-cancel { font-size: var(--t-base); padding: 8px 0; }

/* Who is getting which vaccine. Chips use the neutral surface and the one
   accent; green/amber/red stay reserved for criteria. The late warning reuses
   the existing in-season brown, as .vax-expires does. */
.vax-decisions { margin: 0 0 12px; font-size: var(--t-md); }
.vax-decisions p { margin: 0 0 4px; }
.vax-dec-label { font-weight: 700; margin-right: 6px; }
.vax-dec-todo { color: var(--accent); }
.vax-dec-none { color: var(--muted); }
.vax-people { display: flex; flex-direction: column; gap: 3px; font-size: var(--t-sm); }
.vax-person { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; }
.vax-pname { font-weight: 700; }
.vax-chip { font-size: var(--t-xs); font-weight: 600; padding: 1px 8px; border-radius: 999px;
  background: var(--surface-2); color: var(--ink); }
.vax-chip-planned, .vax-chip-had { border: 1px solid var(--accent); color: var(--accent); }
.vax-chip-none, .vax-chip-skip { color: var(--muted); }
.vax-chip-late { color: #8a3b2c; border: 1px solid currentColor; background: transparent; }
.vax-pnote { font-size: var(--t-xs); color: var(--muted); }
.vax-who { margin: 8px 0 0; border: 1px solid var(--line); border-radius: 8px; }
.vax-who > summary { padding: 8px 10px; cursor: pointer; font-weight: 600; }
.vax-form { display: flex; flex-direction: column; gap: 8px; padding: 0 10px 10px; }
.vax-form label { display: flex; flex-direction: column; gap: 2px; font-size: var(--t-sm); }
.vax-form select, .vax-form input, .vax-form button { font: inherit; min-height: 44px; }

/* ---- the language switch (2026-10-27) ---------------------------------------
   The house control, after juan-lucas: a pill holding two options, each a
   flag and a code, the current one filled. Top right of every top bar and of
   the start and map headers, and in the menu. Colours come from currentColor
   on the dark start and map headers. */
.langtoggle {
  display: inline-flex; gap: 4px; padding: 4px; margin: 0 var(--s-1) 0 auto;
  border: 1px solid color-mix(in oklab, currentColor 30%, transparent);
  border-radius: var(--r-pill);
}
.lang-opt {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 2.5rem; padding: 0 12px; border: 0; border-radius: var(--r-pill);
  background: none; color: inherit; cursor: pointer;
  font: inherit; font-size: var(--t-sm); font-weight: 700; letter-spacing: 0.04em;
}
.lang-opt.on { background: var(--accent); color: #fff; cursor: default; }
.lang-opt:not(.on):active { background: color-mix(in oklab, currentColor 12%, transparent); }
.flag {
  width: 1.45em; height: 0.725em; flex: none; border-radius: 2px;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.22);
}
.start-head, .trip-head { position: relative; }
.start-head h1, .trip-head h1 { padding-right: 9.5rem; }
.lang-float { position: absolute; top: var(--s-3); right: var(--s-3); }
.lang-float .langtoggle { margin: 0; }
.sheet-lang {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3); padding: var(--s-2) 0; font-size: var(--t-lg); font-weight: 500;
}
.sheet-lang > span { display: flex; align-items: center; gap: var(--s-3); }
.sheet-lang .ic { color: var(--muted); }
.sheet-lang .langtoggle { margin: 0; }
.budget-outlook { margin: 10px 0 0; font-size: var(--t-base); font-weight: 650; font-variant-numeric: tabular-nums; }
.budget-outlook-note { margin: 2px 0 0; font-size: var(--t-sm); color: var(--muted); }
.budget-trk .trk-sub th { padding-left: 20px; font-weight: 400; }
.budget-trk .trk-sub td { font-size: var(--t-sm); }
