/* The three-step page: a slim trip header, folder tabs, one panel below. */
/* Wider than --max ON PURPOSE, and not by changing --max.
   --max is 1080px because that is a good measure for READING, and the
   marketing pages, the footer and every .prose column share it. This is not a
   page you read; it is a workspace, and its two panes both want room -- the
   activity list was truncating real titles ("teamLab BOTANICAL GARD...") at
   380px while 700px of gutter sat empty either side of the shell.
   Capped rather than full-bleed: full width has no upper bound, and on an
   ultrawide the map becomes a wall and each list row a short title stranded at
   the far left of a 3000px strip. It also lines the workspace up better with
   nav.site, which is deliberately full-bleed (see the note on it in site.css)
   -- a 1080px column under a full-bleed header is the mismatch that note
   describes. */
.plan-shell { padding: 0 var(--space-gutter) 40px; max-width: 1500px; margin: 0 auto; }

.plan-head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 0 12px;
}
.plan-head .trip-name { flex: 1 1 240px; min-width: 0; }
.plan-head .trip-dest { flex: 0 1 180px; min-width: 0; }
.plan-head-spacer { flex: 1 1 auto; }
.plan-head input {
  border: 1px solid var(--border-strong); border-radius: 9px;
  padding: 8px 11px; font: inherit; background: var(--surface-2); color: var(--ink);
}
#p-saved { font-size: 0.75rem; color: var(--muted); }

/* Folder tabs: the active one shares the panel's background and loses the
   edge between them, so tab and panel read as one piece of card stock. */
.steps { display: flex; gap: 4px; align-items: flex-end; position: relative; z-index: 2; }
.step-tab {
  display: grid; gap: 1px; justify-items: start;
  border: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  border-radius: 12px 12px 0 0;
  background: var(--wash);
  color: var(--muted);
  padding: 9px 18px 8px;
  margin-bottom: -1px;              /* sit ON the panel's top edge */
  font: inherit; cursor: pointer;
  transition: background 140ms var(--ease), color 140ms var(--ease);
}
.step-tab .step-n {
  font: 700 0.66rem/1 var(--display); letter-spacing: 0.07em; text-transform: uppercase;
}
.step-tab .step-name { font: 600 0.95rem/1.25 var(--display); }
.step-tab:hover { color: var(--ink); }
.step-tab:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
.step-tab[aria-selected="true"] {
  background: var(--surface-2);
  color: var(--ink);
  border-bottom-color: var(--surface-2);   /* the folder's open mouth */
  padding-top: 12px;                        /* and it stands a little taller */
}
.step-tab[aria-selected="true"] .step-n { color: var(--step-hue, var(--accent-text)); }

/* ── Three folders, three colours ──────────────────────────────────────────
   Each step gets its own hue so the tab strip reads as a set of coloured
   folders rather than three identical ones. The hues are evenly spaced and
   none of them is the accent violet — the accent has to keep meaning
   "interactive", not "step 2".

   The SELECTED tab keeps the panel's own background, because that shared
   colour is what makes the folder look open; its hue shows in the eyebrow
   and in a drawn top edge instead. Only the resting tabs carry the pastel
   fill. Every pair here is measured: the resting label clears 4.5:1 on its
   own pastel in both themes, and each eyebrow clears 4.5:1 on the panel. */
/* Each step owns two values: its colour and the hue that marks it. The tab
   keeps that ONE colour selected or not — a tab that lightens on select reads
   as a different tab rather than the same one opened — and the panel takes it
   too, so the sheet and its tab are visibly one object. Selection is carried
   by the drawn top edge, the taller tab, and the open mouth along the bottom,
   not by a colour change.

   Three sheets of near-neutral stock: cool grey, warm grey, and a faintly
   green grey. They differ by TEMPERATURE, not by colour — 8-11% saturation,
   where the manila pass before this ran at about 50%. Half a screen of card
   is a lot of surface to sit in front of, and chroma that is pleasant on a
   40px chip is tiring at that size; this went through pastels and then manila
   before landing here.

   All three sit at ONE luminance (spread 0.08% light; 3.5% dark, which is a
   difference of 0.0004 in absolute luminance and below noticing), so moving
   between tabs changes the sheet and never the brightness.

   The saturated per-step hue stays on the drawn top edge and the STEP N
   eyebrow — the coloured label on an otherwise plain folder, which is how a
   real drawer tells one from another. Neutral stock is exactly what makes
   that label legible.

   Measured: ink 12.3:1, muted 4.98:1, label hue 4.77-4.97:1 light; ink
   14.5:1, muted 7.2:1, label hue 8.5-9.6:1 dark. Sheet sits 1.10:1 off the
   desk in both themes. */
#step-tab-0, .step-panel.tint-0 { --step-pastel: #E4E8E9; --step-hue: #0F6E66; }
#step-tab-1, .step-panel.tint-1 { --step-pastel: #E9E7E4; --step-hue: #A24B22; }
#step-tab-2, .step-panel.tint-2 { --step-pastel: #E6E8E4; --step-hue: #4A6B2A; }
/* The overview is not a step, so it takes the accent as its hue rather
   than a fourth step colour. Its sheet is the same near-neutral stock at
   the same luminance as the other three. */
#step-tab-3, .step-panel.tint-3 { --step-pastel: #E5E7EA; --step-hue: #1C4099; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) #step-tab-0, :root:not([data-theme]) .step-panel.tint-0 { --step-pastel: #191E20; --step-hue: #6FD0C4; }
  :root:not([data-theme]) #step-tab-1, :root:not([data-theme]) .step-panel.tint-1 { --step-pastel: #211D19; --step-hue: #F0A88A; }
  :root:not([data-theme]) #step-tab-2, :root:not([data-theme]) .step-panel.tint-2 { --step-pastel: #1B1E18; --step-hue: #A8CF8E; }
:root:not([data-theme]) #step-tab-3, :root:not([data-theme]) .step-panel.tint-3 { --step-pastel: #181D21; --step-hue: #A6BEF0; }
}
:root[data-theme="dark"] #step-tab-0, :root[data-theme="dark"] .step-panel.tint-0 { --step-pastel: #191E20; --step-hue: #6FD0C4; }
:root[data-theme="dark"] #step-tab-1, :root[data-theme="dark"] .step-panel.tint-1 { --step-pastel: #211D19; --step-hue: #F0A88A; }
:root[data-theme="dark"] #step-tab-2, :root[data-theme="dark"] .step-panel.tint-2 { --step-pastel: #1B1E18; --step-hue: #A8CF8E; }
:root[data-theme="dark"] #step-tab-3, :root[data-theme="dark"] .step-panel.tint-3 { --step-pastel: #181D21; --step-hue: #A6BEF0; }

.step-panel.tint-0, .step-panel.tint-1, .step-panel.tint-2 { background: var(--step-pastel); }

.step-tab { background: var(--step-pastel, var(--wash)); }
.step-tab .step-n { color: var(--step-hue, var(--muted)); }
.step-tab[aria-selected="true"] {
  background: var(--step-pastel, var(--surface-2));
  border-bottom-color: var(--step-pastel, var(--surface-2));
  box-shadow: inset 0 3px 0 0 var(--step-hue, transparent);
}
.step-tab:focus-visible { outline-color: var(--step-hue, var(--accent-text)); }

.step-panel {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 0 12px 12px 12px;
  box-shadow: var(--shadow);
  padding: 16px;
  min-height: 60vh;
}
/* The one piece of PROSE on the page, so it keeps a reading measure while the
   panes around it widen. Without the cap it stretches to the full shell and
   becomes a single thin ribbon of text across 1500px. */
.step-lede { margin: 0 0 14px; color: var(--muted); font-size: 0.9rem; max-width: 78ch; }

@media (max-width: 640px) {
  .step-tab { padding: 8px 11px 7px; }
  .step-tab .step-name { font-size: 0.82rem; }
  .plan-head { flex-wrap: wrap; }
}

/* Step 1: a list beside the map, the same two-pane rhythm as Step 2. */
/* 460px, not 380px. The list holds titles the user wrote and that carry the
   detail ("HARUKAS 300 -- Japan's tal..."), so a truncated row loses real
   information. Still a max, not a fraction: past this the row is mostly empty
   and the map is the pane that better uses the width. */
.step-1 { display: grid; grid-template-columns: minmax(280px, 460px) 1fr; gap: 14px; min-height: 58vh; }
/* The search box and the list it fills sit in a panel of their own, the same
   height as the map beside it. Both are grid children of .step-1, so they
   already stretched to the same row height — but only the map LOOKED like a
   container, which left the left column floating on the panel background.
   The rounded box, the border and the 58vh floor are the map's own, so the
   two read as a matched pair rather than a sheet next to some loose text.
   #s1-items takes the leftover height and scrolls inside it, so a long list
   never pushes this panel taller than the map. The search dropdown is
   position: fixed (see .w-results), so it escapes this overflow rather than
   being clipped by it. */
.s1-list {
  display: flex; flex-direction: column; gap: 10px; min-width: 0;
  min-height: 58vh;
  padding: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.s1-list #s1-items {
  flex: 1 1 auto;
  min-height: 0;      /* without this a flex child refuses to shrink and scroll */
  overflow-y: auto;
  overscroll-behavior: contain;
}
.s1-count { margin: 0; font-size: 0.78rem; color: var(--muted); }
.s1-map { position: relative; border-radius: 12px; overflow: hidden; border: 1px solid var(--border); min-height: 58vh; }
.s1-map #w-map { position: absolute; inset: 0; }

.s1-area-toggle {
  display: flex; align-items: center; gap: 6px; width: 100%;
  padding: 8px 10px; margin: 0;
  font: 600 0.78rem/1.3 var(--display);
  color: var(--ink); background: var(--wash); border: 0;
  border-bottom: 1px solid var(--border);
  cursor: pointer; text-align: left;
}
.s1-area-toggle:hover { background: var(--surface-2); }
.s1-chevron {
  width: 14px; height: 14px; flex-shrink: 0;
  color: var(--muted); transition: transform 200ms var(--ease);
}
.s1-area-toggle.is-open .s1-chevron { transform: rotate(90deg); }
.s1-area-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.s1-area-days { font-weight: 400; font-size: 0.68rem; color: var(--muted); margin-left: 6px; }
.s1-area-count {
  font: 700 0.6rem/1 var(--display);
  background: var(--surface); color: var(--muted);
  border-radius: 8px; padding: 2px 6px; flex-shrink: 0;
}

.s1-row {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px 6px 20px; border-bottom: 1px solid var(--border);
}
.s1-row-n {
  width: 20px; height: 20px; flex-shrink: 0;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--wash); color: var(--muted);
  font: 700 0.65rem/1 var(--display);
}
.s1-row-title {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 0.82rem;
}
.s1-day {
  font: 600 0.58rem/1 var(--display); text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); background: var(--wash); border-radius: 4px; padding: 2px 5px;
  flex-shrink: 0;
}
/* How important a place is: must-do, want, or if-there's-time. The day
   planner drops the last of those first when a day exceeds the pace.
   "Want" is the answer for most things, so it recedes to a plain word and
   only the two deliberate choices carry a tint — a row of identical loud
   chips would be noise on a list of twenty places. */
.s1-pri {
  flex: none;
  display: inline-flex; align-items: center;
  min-height: 24px;   /* WCAG 2.2 AA target size; it measured 18 */
  border: 1px solid transparent;
  background: none;
  cursor: pointer;
  font: 600 0.62rem/1 var(--display);
  letter-spacing: 0.03em;
  padding: 3px 7px;
  border-radius: 999px;
  color: var(--muted);
  white-space: nowrap;
  transition: color 140ms, background 140ms, border-color 140ms;
}
.s1-pri:hover { border-color: var(--border-strong); color: var(--ink); }
.s1-pri.is-must {
  color: var(--accent-text);
  background: var(--accent-soft);
  border-color: transparent;
}
.s1-pri.is-maybe { color: var(--muted); opacity: 0.7; font-style: italic; }
.s1-pri:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 1px; }

.s1-del {
  border: 0; background: none; color: var(--muted); cursor: pointer;
  font-size: 0.9rem; padding: 0 2px; opacity: 0; transition: opacity 150ms;
}
.s1-row:hover .s1-del { opacity: 1; }
.s1-del:hover { color: var(--danger); }
.s1-empty { color: var(--muted); font-size: 0.9rem; padding: 18px 4px; }
.s1-lodging .s1-row-title { font-weight: 600; }
.s1-bed { color: var(--map-hotel); flex-shrink: 0; }
/* Reserved for a future DOM-based lodging pin layer. Activity pins are
   mapkit.MarkerAnnotation, painted to a shared canvas -- confirmed in
   mapview.js (setActivityPins/highlightPin), they have NO per-annotation DOM
   element to attach a class to. The lodging distinction on the map today is
   carried by colour instead (--map-hotel/--map-hotel-ink, the same gold every
   hotel pin on this page already uses). This selector is a no-op placeholder
   until a lodging pin becomes a real DOM/custom annotation. */
.pin-lodging { border-color: var(--map-hotel); }
@media (max-width: 860px) {
  .step-1 { grid-template-columns: 1fr; }
  /* Stacked, the two panels are no longer side by side, so matching the map's
     height buys nothing and costs a screenful of empty panel above it. The
     list takes the height its content needs and stops scrolling internally. */
  .s1-list { min-height: 0; }
  .s1-list #s1-items { overflow-y: visible; flex: 0 0 auto; }
}

/* Step 2: the Where to Stay panes, reused wholesale from where.css --
   .where-shell, .where-bar, .where-panes and everything under them keep
   their own rules unchanged, including the narrow-width stack at 1079px.
   The one thing that does not carry over is where.css's height trick
   (body.where-page { height: 100dvh }), which only applies to the
   standalone page. An explicit height here gives .where-panes (flex: 1 1
   auto, from where.css) the same "fill what's left" behaviour inside the
   step panel instead of the viewport.

   .w-guide is also where.css's, bare-class like the rest, but its markup
   now sits at the top of Step 3's panel -- unscoped, so it needs nothing
   here to keep working there. */
.step-2.where-shell { height: 68vh; }
@media (max-width: 1079px) {
  /* where.css's own narrow-width rule sets .where-shell to height: auto so
     the panes stack and the page scrolls past them. .step-2 has higher
     specificity than that plain-class rule and would otherwise keep the
     shell pinned at 68vh here, so it has to step aside too. */
  .step-2.where-shell { height: auto; }
}

/* ── Step 2's stay lines: the entrance to the panel, above the where-bar.
   One line per hotel slot -- book it, say you already have one, or (once
   booked) edit its dates and confirmation number. Sits inside .step-2, so
   its own tokens ride the where-shell palette rather than inventing a new
   one for one section. */
/* .step-2.where-shell is a fixed 68vh flex column whose panes (the map, the
   activity/hotel lists) fill whatever this section leaves them -- see
   where.css's own comment on .where-panes. Five hotel lines run to ~375px
   of real content, which would crush those panes to a sliver the same way
   Step 3's day row was crushed before it got its own min-height fix. The
   fix here is the one where.css already establishes: the SECTION scrolls,
   the shell does not -- #s2-stay-lines caps at roughly two lines and keeps
   the rest a scroll away, so the map and hotel list always keep their room
   regardless of how many hotels the trip has. */
.s2-stays { padding: 6px var(--space-gutter) 0; flex: none; }
.s2-stays-head { display: flex; align-items: baseline; gap: 6px; margin-bottom: 2px; }
.s2-stays-head h2 { margin: 0; font: 700 0.82rem/1.2 var(--display); color: var(--ink); }
.s2-stays-sub { font-size: 0.68rem; color: var(--muted); }
#s2-stay-lines { max-height: 120px; overflow-y: auto; scrollbar-width: thin; padding-right: 2px; display: flex; flex-wrap: wrap; gap: 2px; }

.s2-line {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 3px 8px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 6px; font-size: 0.8rem;
}
.s2-line-blank { border-style: dashed; border-color: var(--border-strong); width: 100%; }
.s2-line-nodates {
  display: block; cursor: pointer; color: var(--muted);
  background: none; border-style: dashed;
}
.s2-line-nodates p { margin: 0; font-size: 0.78rem; }
/* "Your hotels" with nothing in it yet. An empty section is an invitation, so
   it names the next move rather than reporting a count of zero. */
.s2-stays-empty {
  margin: 0;
  padding: 10px 12px;
  font-size: 0.78rem;
  color: var(--muted);
}
.s2-line-nodates:hover { border-color: var(--accent-text); color: var(--ink); }

.s2-line-info { display: flex; flex-direction: column; gap: 0; flex: 1 1 140px; min-width: 0; }
.s2-line-info strong { font-size: 0.78rem; font-weight: 700; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.s2-line-dates, .s2-line-address { font-size: 0.68rem; color: var(--muted); }

.s2-line-actions { display: flex; gap: 6px; flex: none; }
.s2-line-actions button {
  border: 0; border-radius: 6px; padding: 4px 10px; font: inherit; font-size: 0.72rem;
  cursor: pointer;
}
.btn-book { background: var(--accent); color: var(--accent-ink); font-weight: 700; }
.btn-book:hover { filter: brightness(1.06); }
.btn-havehotel {
  background: none; color: var(--accent-text); font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--border-strong);
}
.btn-havehotel:hover { box-shadow: inset 0 0 0 1px var(--accent-text); }

.s2-line-filled { flex-wrap: nowrap; width: fit-content; margin-left: 4px; margin-right: 4px; }
.s2-line-name { display: flex; align-items: center; gap: 8px; }
/* ── What is known about a stop ──────────────────────────────────────────
   Underneath the activity, not beside it: these are things you read once you
   have found the row, so they must not compete with the title for the scan. */
.ov-notes { list-style: none; margin: 3px 0 0; padding: 0; display: grid; gap: 1px; }
.ov-note {
  display: flex; gap: 6px; align-items: baseline;
  font-size: 0.76rem; color: var(--muted); line-height: 1.45;
}
.ov-note-label {
  flex: none; min-width: 5.5rem;
  font-weight: 650; text-transform: uppercase;
  font-size: 0.62rem; letter-spacing: 0.05em;
  opacity: 0.7;
}
.ov-note-text { min-width: 0; }
/* A confirmation number is copied, so it gets a face you can read a character
   at a time -- 0 and O matter here in a way they do not in prose. */
.ov-note-confirmation .ov-note-text {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--ink);
}
/* The one note that is a warning rather than a fact. */
.ov-note-hours-warn .ov-note-text { color: var(--warn-text, var(--accent-text)); font-weight: 600; }
@media (max-width: 560px) {
  .ov-note { flex-direction: column; gap: 0; }
  .ov-note-label { min-width: 0; }
}

/* The hotel's confirmation, on the check-in line where it is asked for. */
.ov-stay-conf {
  display: flex; gap: 6px; align-items: baseline;
  margin-top: 2px; font-size: 0.76rem; color: var(--muted);
}
.ov-stay-conf-inline {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--muted);
}
.ov-stay-conf .ov-note-text {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--ink);
}

/* ── A day's travel, by mode ─────────────────────────────────────────────
   A summary line under the day heading, not a table: four numbers a reader
   glances at on the way into the day, and glancing is the whole job. */
.ov-day-travel {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px;
  margin: 2px 0 10px; padding: 0;
  font-size: 0.78rem; color: var(--muted);
}
.ov-travel-total { font-weight: 650; color: var(--ink); }
.ov-travel-part { white-space: nowrap; }
.ov-travel-mode { opacity: 0.75; }
/* The "≈" is load-bearing -- it is the difference between a measurement and
   an estimate -- so it is legible rather than decorative. */
.ov-travel-approx { font-weight: 700; cursor: help; }
.ov-travel-none { font-style: italic; opacity: 0.8; }
/* Not a mode, a missing one -- so it reads as a caveat rather than a number. */
.ov-travel-bus {
  font-style: italic; opacity: 0.85; cursor: help;
  border-bottom: 1px dotted currentColor;
}

/* The hotel's name is a control now (it shows the hotel on the map), but it
   must still READ as the heading it always was -- so it inherits type and
   colour and only reveals itself on hover and focus. */
.s2-line-name-btn {
  font: inherit; color: inherit; text-align: left;
  background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer; border-radius: 4px;
  text-decoration-color: transparent;
  transition: color 140ms, text-decoration-color 140ms;
}
.s2-line-name-btn:hover, .s2-line-name-btn:focus-visible {
  color: var(--accent-text);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: currentColor;
}

/* The confirmation, as an icon beside the stamp that says there is one.
   Icon-only, so it carries an aria-label and a title -- a button whose whole
   content is a decorative SVG has no accessible name at all otherwise.
   Sized to WCAG 2.2's 24px minimum target via PADDING rather than a bigger
   glyph, so it meets the floor without pushing the hotel name around. */
.doc-icon-btn {
  display: inline-grid; place-items: center; flex: none;
  margin-left: 6px; padding: 5px;
  background: none; border: 0; border-radius: 6px;
  color: var(--muted); cursor: pointer;
  transition: color 140ms, background 140ms;
}
.doc-icon-btn:hover, .doc-icon-btn:focus-visible {
  color: var(--accent-text); background: var(--wash);
}
.doc-icon-btn svg { display: block; }

.s2-stamp {
  display: inline-block; flex: none;
  transform: rotate(-6deg);
  font: 900 0.45rem/1 var(--display);
  letter-spacing: 0.12em; text-transform: uppercase;
  /* Was a hardcoded gold at 0.6, which composited to 2.98:1 on the panel —
     a label below AA. The accent at 0.8 keeps the faded rubber-stamp look and
     measures 4.65:1 light / 4.64:1 dark. */
  color: var(--accent-text); opacity: 0.8;
  border: 1px solid currentColor;
  outline: 1px solid currentColor; outline-offset: 1px;
  padding: 1px 4px; border-radius: 2px;
  pointer-events: none; user-select: none; white-space: nowrap;
}
.s2-line-sub { font-size: 0.72rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Dates on a chosen hotel. Editable by default; read-only, with the reason,
   once a confirmation document supplied them. Both states sit on the same
   sub-line so the card does not change height when one becomes the other. */
.s2-dates-btn {
  border: 0; background: none; padding: 0; margin: 0; font: inherit;
  color: var(--muted); cursor: pointer;
  text-decoration: underline; text-decoration-color: var(--border-strong);
  text-underline-offset: 3px;
}
.s2-dates-btn:hover { color: var(--accent-text); text-decoration-color: var(--accent-text); }
.s2-dates-fixed { color: var(--muted); }
.s2-dates-edit {
  border: 0; background: none; padding: 0 0 0 6px; margin: 0; font: inherit;
  font-size: 0.68rem; color: var(--accent-text); cursor: pointer; text-decoration: underline;
  text-underline-offset: 3px;
}
.s2-dates-note { padding-left: 6px; font-size: 0.66rem; opacity: 0.85; }

.s2-dates-edit-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 6px 0 2px; font-size: 0.72rem; color: var(--muted);
}
.s2-dates-edit-row[hidden] { display: none; }
.s2-dates-edit-row label { display: inline-flex; align-items: center; gap: 4px; }
.s2-dates-edit-row input[type="date"] {
  font: inherit; color: var(--ink); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 6px; padding: 4px 6px;
  /* 24px is the WCAG 2.5.8 floor the hotel rows are already held to. */
  min-height: 24px;
}
.s2-dates-save, .s2-dates-cancel {
  font: inherit; cursor: pointer; border-radius: 6px; padding: 4px 10px; min-height: 24px;
  border: 1px solid var(--border);
}
.s2-dates-save { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.s2-dates-cancel { background: none; color: var(--muted); }
/* Errors sit next to the fields they are about, not at the top of the card. */
.s2-dates-error { color: var(--danger, #C22A11); font-size: 0.68rem; }
.s2-dates-error[hidden] { display: none; }

/* Why a confirmation could not be opened, said next to the button that was
   pressed. Wraps rather than truncates: it names an action, and a truncated
   instruction is no instruction. */
.w-doc-error {
  display: block; padding-top: 4px;
  font-size: 0.7rem; line-height: 1.4; color: var(--danger, #C22A11);
  max-width: 34ch;
}
.s2-remove {
  border: 0; background: none; color: var(--muted); cursor: pointer;
  font-size: 0.9rem; line-height: 1; padding: 1px 4px; flex: none;
}
.s2-remove:hover { color: var(--ink); }

/* A Step 1 item that looks like lodging, still unconfirmed -- "staying
   there?" -- rendered into its OWN sibling of #s2-stay-lines, deliberately
   uncapped: that box's 136px scroll is sized for lines, and a yes/no
   question sitting below its fold (with hotelCount >= 2) is a question a
   user can miss entirely. #s2-stay-cards has no max-height, so the card is
   always in view. */
#s2-stay-cards:not(:empty) { margin-top: 4px; }
.s2-card {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap;
  padding: 5px 10px; margin-bottom: 3px;
  background: var(--surface-2); border: 1px dashed var(--accent-text);
  border-radius: 6px;
}
.s2-card p { margin: 0; font-size: 0.78rem; color: var(--ink); flex: 1 1 160px; }
.s2-card-actions { display: flex; gap: 6px; flex: none; }

.s2-upload {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  margin: 3px 0 6px; padding: 6px 10px;
  border: 1px dashed var(--border); border-radius: 6px;
  cursor: pointer; transition: border-color 0.15s, background 0.15s;
  color: var(--muted); text-align: center;
}
.s2-upload:hover, .s2-upload:focus-visible, .s2-upload.is-over,
.s2-upload.is-asking {
  border-color: var(--accent-text); background: var(--surface-2);
}
/* "I already have one" sends the user here and opens the picker. If they
   cancel the file dialog, this is the only thing left saying where they were
   sent — without it the button reads as broken a second time. */
/* The whole section is a drop target for confirmations, so it says so while
   a file is over it. The strip inside keeps its own hover/over state for the
   click-to-browse route. */
.s2-stays.is-drop-target {
  outline: 2px dashed var(--accent);
  outline-offset: -4px;
  background: var(--accent-soft);
}
.s2-stays { transition: background 140ms; }

.s2-upload.is-asking { box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-text) 45%, transparent); }

/* The two trip inputs that shape the plan — how you get around, and how full
   you want the days — sit together between the score chips and the day strip,
   because both change what Replan produces rather than filtering what is
   already there. They wrap onto two rows on a narrow screen. */
.s3-inputs {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 8px 18px; margin: 0 0 10px;
}
.s3-pace { display: flex; align-items: center; gap: 8px; }
/* Why this day holds less than the others. A label, not a control — the
   trip's dates already decide it. */
.day-kind {
  display: block;
  font: 600 0.6rem/1.2 var(--display);
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--accent-text); opacity: 0.85;
}

.s3-pace-label {
  font: 600 0.72rem/1 var(--display);
  letter-spacing: 0.04em; color: var(--muted); white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) { .s2-upload { scroll-behavior: auto; } }
.s2-upload svg { opacity: 0.45; width: 14px; height: 14px; flex: none; }
.s2-upload span { font-size: 0.7rem; }
.s2-upload u { color: var(--accent-text); text-decoration-color: var(--accent-text); cursor: pointer; }
.s2-upload-hint { font-size: 0.62rem; opacity: 0.5; }

/* The Hotels knob's "still following the recommendation" note -- cleared the
   moment the user touches the knob (step2.js's wireToolbar). */
.s2-hotels-rec { margin-left: 4px; font-size: 0.72rem; color: var(--muted); white-space: nowrap; }

@media (max-width: 640px) {
  .s2-line-filled { flex-wrap: wrap; }
}

/* ── Step 3: the day planner, ported from planner.css ─────────────────────
   Not linked wholesale the way where.css is for Step 2: planner.css redefines
   .mini, .btn, .grid2 and the .item-row family at the bare class level with
   different values than site.css's shared versions of the same names --
   loading it as-is would silently restyle Step 1/2's header buttons (which
   use .mini/.btn already) and anything else on the page built on that shared
   vocabulary. So the rules below are the planner's OWN styling, copied and
   scoped to #step-body-2 (the panel) and dialog.sheet (the seven dialogs,
   which live at body level, outside #step-body-2, for stacking-context
   reasons -- see plan.html). Everything else -- .dayrow, .day-card,
   .workspace, .guide-col, .map-col, the pins, the calendar, place search,
   the toast -- uses class names nothing else on the page touches, so it
   carries over unscoped exactly as planner.css had it. */

/* planner.css's own custom properties this borrowed CSS depends on. --pl-
   is namespaced to avoid colliding with anything else; --z-results already
   matches the value where.css scopes to .where-shell, so results menus
   layer the same way whether they are Step 2's or Step 3's. */
:root ,
/* ── Step 3: the day planner, ported from planner.css ─────────────────────
   Not linked wholesale the way where.css is for Step 2: planner.css redefines
   .mini, .btn, .grid2 and the .item-row family at the bare class level with
   different values than site.css's shared versions of the same names --
   loading it as-is would silently restyle Step 1/2's header buttons (which
   use .mini/.btn already) and anything else on the page built on that shared
   vocabulary. So the rules below are the planner's OWN styling, copied and
   scoped to #step-body-3 (the panel) and dialog.sheet (the seven dialogs,
   which live at body level, outside #step-body-3, for stacking-context
   reasons -- see plan.html). Everything else -- .dayrow, .day-card,
   .workspace, .guide-col, .map-col, the pins, the calendar, place search,
   the toast -- uses class names nothing else on the page touches, so it
   carries over unscoped exactly as planner.css had it. */

/* planner.css's own custom properties this borrowed CSS depends on. --pl-
   is namespaced to avoid colliding with anything else; --z-results already
   matches the value where.css scopes to .where-shell, so results menus
   layer the same way whether they are Step 2's or Step 3's. */
:root {
  --pl-radius: 12px;
  --pl-radius-sm: 8px;
  --pl-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --z-results: 40;
  --z-toast: 200;
}

/* The panel gives .workspace real height to grow into, the way .step-2's
   height:68vh does for the where-shell pattern -- .workspace's own flex:1
   (below) needs a sized flex-column ancestor or it has nothing to grow into.
   min-height, NOT height: the old planner-shell was a floor the content
   could outgrow (the page scrolls past the fold — that is the point). A
   hard height made the flex children fight over 68vh, and the day box —
   the only child with overflow — lost: crushed to a sliver behind its own
   scrollbar on any real trip. tools/probes/step3-dense.js pins this. */
.step-3 { display: flex; flex-direction: column; min-height: 68vh; }

/* ── Autoplan: the overlay while it runs, the banner once it has ────────── */
.s3-planning {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px; margin-bottom: 10px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--pl-radius-sm);
  font-size: 0.86rem; font-weight: 600; color: var(--ink);
}
.spin {
  width: 15px; height: 15px; flex: none;
  border: 2px solid var(--border-strong);
  border-top-color: var(--accent-text);
  border-radius: 50%;
  animation: s3-spin 0.8s linear infinite;
}
@keyframes s3-spin { to { transform: rotate(360deg); } }

.s3-banner {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px; margin-bottom: 10px;
  background: var(--accent-soft); border: 1px solid var(--accent-text);
  border-radius: var(--pl-radius-sm);
  font-size: 0.86rem; color: var(--ink); line-height: 1.4;
}
/* Undo sits beside the dismiss, and reads as the action it is. */
.s3-banner-undo {
  margin-left: auto; flex: none;
  font: inherit; font-size: 0.78rem;
  padding: 3px 12px; border-radius: 999px;
  border: 1px solid var(--accent-text);
  background: none; color: var(--ink); cursor: pointer;
}
.s3-banner-undo:hover { background: var(--wash); }
.s3-banner-x {
  flex: none;
  width: 22px; height: 22px; display: grid; place-items: center;
  border: 0; background: none; color: var(--muted); cursor: pointer;
  font-size: 1.1rem; line-height: 1; border-radius: 50%;
}
.s3-banner-x:hover { background: var(--wash); color: var(--ink); }

/* ── Day row: hotel containers around day cards ─────────── */
.dayrow {
  display: flex; align-items: stretch; gap: 8px;
  overflow-x: auto; padding: 2px 0 12px;
  scrollbar-width: thin;
}
.datepick-card {
  display: inline-flex; align-items: center; gap: 12px;
  background: var(--accent); color: var(--accent-ink);
  border: 0; border-radius: var(--pl-radius); cursor: pointer;
  box-shadow: inset 0 0 0 1px var(--accent-text);
  padding: 14px 20px; font: inherit; text-align: left;
  transition: filter 0.15s var(--pl-ease), transform 0.12s var(--pl-ease);
}
.datepick-card:hover { filter: brightness(1.08); }
.datepick-card:active { transform: scale(0.99); }
.datepick-card svg { width: 22px; height: 22px; flex: none; }
.datepick-card strong { display: block; font-size: 0.98rem; font-weight: 800; }
.datepick-card small { display: block; font-size: 0.78rem; opacity: 0.85; }

/* The schedule grid: two columns per day. Hotel bars span row 1 — meeting
   halfway over a changeover day — and the day cards ride row 2. */
.day-grid {
  position: relative;
  display: grid; grid-auto-rows: auto; column-gap: 0;
  row-gap: var(--row-gap, 5px);
  /* Fills the row rather than shrink-wrapping, so its right edge is the
     row's right edge. */
  flex: 1 1 auto; min-width: 0;
}
.stay-bar {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: var(--bar-h, 26px); padding: 3px 11px; margin: 0 3px;
  background: var(--surface-2); border: 1px solid var(--border-strong);
  border-radius: 999px; cursor: pointer; font: inherit;
  color: var(--ink); font-size: 0.76rem; font-weight: 700;
  overflow: hidden; white-space: nowrap;
  transition: background 0.15s var(--pl-ease), border-color 0.15s var(--pl-ease);
}
.stay-bar:hover { background: var(--wash); border-color: var(--accent-text); }
.stay-bar svg { width: 13px; height: 13px; flex: none; color: var(--muted); }
.stay-bar .stay-name { overflow: hidden; text-overflow: ellipsis; }

.stay-bar.ghost {
  background: transparent; border-style: dashed;
  border-color: var(--border-strong); color: var(--muted);
}
.stay-bar.ghost:hover { border-color: var(--accent-text); color: var(--accent-text); }
.stay-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent-text); flex: none; }
.day-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  background: var(--surface-2); color: var(--ink);
  border: 2px solid var(--border); border-radius: 9px; cursor: pointer;
  font: inherit; padding: 4px 10px; margin: 0 3px; min-height: var(--card-h, 38px);
  justify-content: center; overflow: hidden;
  transition: border-color 0.15s var(--pl-ease), background 0.15s var(--pl-ease), transform 0.12s var(--pl-ease);
}
.day-card:hover { border-color: var(--border-strong); }
.day-card:active { transform: scale(0.98); }
.day-card strong { font-size: 0.78rem; font-weight: 800; line-height: 1.15; }
/* A fortnight on a phone leaves each day a few characters wide. The day
   number has to survive; the date is the first thing that can go. */
.day-grid.tight .day-card { padding: 4px 5px; }
.day-grid.tight .day-card span { display: none; }
.day-grid.tight .day-count { display: none; }
/* Nine days on a phone: the word "Day" costs more than it says. The chip
   keeps its number, centred, and sheds everything else. */
.day-grid.micro .day-card { padding: 4px 2px; margin: 0 1px; align-items: center; }
.day-grid.micro .day-card .day-word { display: none; }
.day-grid.micro .stay-bar { padding: 2px 3px; gap: 2px; }
/* The name stays: a bar spanning three days has room for "DoubleTre…", and
   an ellipsis on the one-night bars beats a row of identical bed icons. */
.day-card span {
  font-family: var(--mono); font-size: 0.62rem; color: var(--muted);
  line-height: 1.15; white-space: nowrap;
}
.day-card[aria-pressed="true"] {
  border-color: var(--accent-text); background: var(--accent-soft);
  box-shadow: 0 4px 0 -1px var(--accent-text);
}
/* How full a day is, without opening it. */
.day-count {
  position: absolute; top: 5px; right: 7px;
  font-family: var(--mono); font-size: 0.58rem; font-weight: 700;
  color: var(--muted);
}
.day-card { position: relative; }
.day-card[aria-pressed="true"] .day-count { color: var(--ink); }

/* Row 3: the day schedules — timeline gutter + per-day canvases */
.tl-gutter { position: relative; }
.tl-label {
  position: absolute; right: 8px;
  font-family: var(--mono); font-size: 0.74rem; font-weight: 600;
  color: var(--muted); font-variant-numeric: tabular-nums;
  white-space: nowrap; transform: translateY(-50%);
}
/* The window stretchers: one at each end of the hour column, one to pull both
   ends back in. 24px square is the WCAG 2.5.8 floor and the column is only
   wide enough for the hour labels, so they sit flush right beneath them. */
.tl-stretch {
  position: absolute; right: 6px;
  width: 24px; height: 24px; padding: 0;
  display: grid; place-items: center;
  font: 600 0.82rem/1 var(--mono); color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 6px; cursor: pointer;
}
.tl-stretch:hover:not(:disabled) { color: var(--ink); border-color: var(--border-strong); }
.tl-stretch:disabled { opacity: 0.32; cursor: default; }
.tl-stretch.is-early { top: -28px; }
.tl-stretch.is-late { bottom: -14px; }
.tl-stretch.is-trim { bottom: -14px; right: 34px; }

.day-acts.focused { margin: 0; }
/* A three-word activity stretched across a thousand pixels reads as a bar,
   not a card. Cap it so the hour grid stays visible beside it. */
.day-acts.focused .day-act.timed { right: auto; max-width: 620px; width: calc(100% - 8px); }
/* Room to breathe: the anytime strip's 130px cap was for nine-across. */
.day-acts.focused .da-strip .day-act { max-width: 320px; }
/* 24px, not 22: WCAG 2.2 AA Target Size (2.5.8) floors an interactive
   target at 24x24 CSS px. These chips were already floored at 22, so
   this costs two pixels and clears the standard. */
.day-acts.focused .day-act { font-size: 0.78rem; min-height: 24px; padding: 3px 9px; }
.day-acts.focused .day-act svg { width: 12px; height: 12px; }
.day-acts.focused .day-act .da-title { white-space: nowrap; }
.day-acts {
  display: flex; flex-direction: column;
  margin: 0; padding: 3px;
  background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 9px;
  box-shadow: var(--edge);
}
.da-strip {
  display: flex; gap: 3px; align-items: center;
  overflow-x: auto; scrollbar-width: none;
  /* Sits under the canvas now: the rule separates it from the timed day
     above, so an untimed chip cannot read as the day's 6 AM opener. */
  border-top: 1px solid var(--border);
  padding-top: 3px; margin-top: 3px; flex: none;
}
.da-canvas {
  position: relative; flex: none;
  /* a measurement grid: one faint line per 3 hours */
  background-image: linear-gradient(var(--border) 1px, transparent 1px);
  background-position: 0 0;
}
.day-act {
  display: flex; align-items: center; gap: 5px;
  background: transparent; border: 0; border-radius: 6px; cursor: grab;
  font: inherit; font-size: 0.7rem; color: var(--ink);
  padding: 1px 5px; text-align: left; min-height: 18px;
  transition: background 0.13s var(--pl-ease);
}
.day-act:hover { background: var(--wash-strong); }
.day-act:active { cursor: grabbing; }
.day-act svg { width: 10px; height: 10px; flex: none; color: var(--muted); }
.day-act .da-time {
  font-family: var(--mono); font-size: 0.72rem; font-weight: 700;
  color: var(--accent-text); font-variant-numeric: tabular-nums; flex: none;
}
.day-act .da-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.da-strip .day-act { flex: none; max-width: 130px; background: var(--wash); }
/* Chips shorter than their own line height would clip the text; hide the
   overflow rather than let it spill onto the activity below. */
.day-act.timed .da-title { line-height: 1.15; }
.day-act.timed {
  position: absolute; left: 4px; right: 4px; overflow: hidden;
  background: var(--wash); align-items: flex-start;
  /* A hairline all the way round, not a 2px stripe down one side. On a
     timeline the TOP edge is the start time, so a left stripe marked
     nothing — it was decoration, and the side-stripe-on-a-rounded-card is
     one of the most recognisable generated-UI tells. */
  border: 1px solid var(--border); border-radius: 4px;
}
.day-act.timed:hover { background: var(--wash-strong); z-index: 3; }

/* Meals wear the signal colour — breakfast, lunch, dinner read at a glance.
   Soft fill means category; the solid accent stays reserved for selection
   and primary actions. */
.day-act.meal { background: var(--accent-soft); border-left-color: var(--accent-text); }
.day-act.meal:hover { background: var(--accent-soft); filter: brightness(1.04); }
.day-act.meal .da-title { color: var(--accent-text); font-weight: 700; }
.day-act.meal svg { color: var(--accent-text); }
.da-strip .day-act.meal { background: var(--accent-soft); }
.backlog-chip.meal { border-color: var(--accent-text); }
.backlog-chip.meal svg { color: var(--accent-text); }
/* The drop-slot ghost that tracks the cursor while dragging. The cursor
   carries the browser's drag image, so this sits partly hidden — hence
   the glow (its edges read through) and the rail below (which doesn't). */
.da-ghost {
  position: absolute; left: 4px; right: 4px; z-index: 4;
  display: flex; align-items: center; padding-left: 7px;
  border: 2px solid var(--accent-text); border-radius: 4px;
  background: var(--accent); color: var(--accent-ink);
  box-shadow: 0 0 0 3px var(--accent-soft), 0 0 16px 2px var(--accent-soft);
  font-family: var(--mono); font-size: 0.64rem; font-weight: 700;
  white-space: nowrap; overflow: hidden;
  pointer-events: none;
}
.da-ghost.busy {
  border-color: var(--border-strong); background: var(--wash-strong);
  box-shadow: 0 0 0 3px var(--wash);
  color: var(--muted);
}

/* The rail: a guide line across every day and a time pill in the gutter,
   both clear of the cursor so the drag image can never cover them. */
.tl-rail { position: relative; pointer-events: none; z-index: 6; }
.tl-rail-line {
  position: absolute; left: 44px; right: 0; height: 0;
  border-top: 2px dashed var(--accent-text); opacity: 0.85;
}
.tl-rail.busy .tl-rail-line { border-top-color: var(--border-strong); opacity: 0.7; }
.tl-rail-pill {
  position: absolute; left: 1px; width: 42px; text-align: center;
  transform: translateY(-50%);
  background: var(--accent); color: var(--accent-ink);
  font-family: var(--mono); font-size: 0.62rem; font-weight: 700;
  padding: 3px 0; border-radius: 999px; white-space: nowrap;
  box-shadow: inset 0 0 0 1px var(--accent-text), var(--shadow);
}
.tl-rail.busy .tl-rail-pill { background: var(--muted); color: var(--surface); }
/* Aiming at an activity offers a trade, not a time. The rail is for times,
   so it stands down — the cue rides on the activity itself, which sits below
   the cursor and is fully readable. */
.day-act.swap-target {
  outline: 2px solid var(--accent-text); outline-offset: 1px;
  box-shadow: 0 0 0 4px var(--accent-soft), 0 0 18px 2px var(--accent-soft);
  filter: brightness(1.08);
}
.day-act.swap-target::after {
  content: "Swap?";
  position: absolute; right: 5px; top: 50%; transform: translateY(-50%);
  background: var(--accent); color: var(--accent-ink);
  font-family: var(--mono); font-size: 0.58rem; font-weight: 700;
  padding: 2px 7px; border-radius: 999px; white-space: nowrap;
  box-shadow: inset 0 0 0 1px var(--accent-text), var(--shadow);
}

/* Row 4: the collapsible activities box spanning every day */
.acts-box {
  margin: 0;
  background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 9px;
  box-shadow: var(--edge);
}
.acts-box > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 10px; padding: 9px 14px;
}
.acts-box > summary::-webkit-details-marker { display: none; }
.acts-box > summary:focus-visible { outline: 2px solid var(--accent-text); outline-offset: -2px; border-radius: 9px; }
.acts-box > summary h3 { font-size: 0.82rem; font-weight: 800; margin: 0; color: var(--ink); }
.acts-box > summary .acts-count { margin-left: auto; font-family: var(--mono); font-size: 0.68rem; color: var(--muted); }
.acts-box > summary .chev { color: var(--muted); display: grid; place-items: center; transition: transform 0.16s var(--pl-ease); }
.acts-box[open] > summary .chev { transform: rotate(90deg); }
.acts-box > summary { padding: 6px 12px; }
.acts-box .acts-body { padding: 0 12px 8px; }
.acts-box .add-row { margin-top: 2px; }
.backlog-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0 8px; }

/* ── The backlog as a column beside the timeline ─────────── */
.acts-box.side {
  position: absolute; left: 0; top: 0; bottom: 0;
  width: calc(var(--backlog-w, 198px) - 8px);
  display: flex; flex-direction: column; margin: 0;
  min-height: 0; overflow: hidden;
}
.acts-box.side > .acts-head {
  flex: none; display: flex; align-items: baseline;
  flex-wrap: wrap; gap: 2px 8px; padding: 8px 10px;
  border-bottom: 1px solid var(--border);
}
.acts-box.side > .acts-head :is(h2, h3) { font-size: 0.82rem; font-weight: 800; margin: 0; color: var(--ink); }
.acts-box.side > .acts-head .acts-count {
  margin-left: 0; flex: 1 1 auto; min-width: 0; font-family: var(--mono);
  font-size: 0.66rem; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.acts-box.side > .acts-head .acts-count .drag-hint { display: none; }
.acts-box.side .acts-body {
  flex: 1; min-height: 0; max-height: none;
  display: flex; flex-direction: column;
  padding: 8px 10px 10px;
}
.acts-box.side .backlog-chips {
  flex: 1; min-height: 0; overflow-y: auto; scrollbar-width: thin;
  flex-wrap: nowrap; flex-direction: column; gap: 5px;
  margin: 0 -4px 8px 0; padding-right: 4px;
}
.acts-omni { position: relative; display: flex; gap: 6px; margin-bottom: 8px; }
.acts-omni .acts-find { flex: 1; width: auto; min-width: 0; margin: 0; }
.acts-add {
  flex: none; width: 34px; min-height: 34px; display: grid; place-items: center;
  border-radius: 8px; cursor: pointer;
  background: var(--surface-2); color: var(--muted);
  border: 1px solid var(--border-strong);
  transition: color 0.15s var(--pl-ease), border-color 0.15s var(--pl-ease);
}
.acts-add svg { width: 14px; height: 14px; flex: none; }
.acts-add:hover { border-color: var(--accent-text); color: var(--accent-text); }
.acts-add:focus-visible { outline: none; border-color: var(--accent-text); box-shadow: 0 0 0 3px var(--accent-soft); }
.acts-results {
  position: absolute; top: 100%; left: 0; right: 0; z-index: var(--z-results);
  margin-top: 4px; max-height: 240px; overflow-y: auto; scrollbar-width: thin;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--pl-radius-sm); box-shadow: var(--shadow);
}
.acts-results button {
  display: block; width: 100%; text-align: left; padding: 8px 10px;
  background: transparent; border: 0; font: inherit; font-size: 0.78rem;
  color: var(--ink); cursor: pointer;
}
.acts-results button:hover, .acts-results button.active { background: var(--wash); }
.acts-results .sub { display: block; font-size: 0.68rem; color: var(--muted); }
.acts-find {
  flex: none; width: 100%; margin: 0 0 8px;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border-strong); border-radius: 8px;
  font: inherit; font-size: 0.78rem; padding: 7px 10px; min-height: 34px;
  -webkit-appearance: none;
}
.acts-find::placeholder { color: var(--muted); opacity: 1; }
.acts-find:focus-visible { outline: none; border-color: var(--accent-text); box-shadow: 0 0 0 3px var(--accent-soft); }
.acts-head .acts-sort {
  flex: none; max-width: 96px;
  font: inherit; font-size: 0.7rem; cursor: pointer;
  background: var(--surface-2); color: var(--muted);
  border: 1px solid var(--border); border-radius: 7px; padding: 3px 6px;
  min-height: 24px;   /* WCAG 2.2 AA target size; it measured 22 */
}
.acts-head .acts-sort:hover { border-color: var(--border-strong); color: var(--ink); }
.acts-expand {
  flex: none; display: grid; place-items: center; width: 26px; height: 24px;
  cursor: pointer; border-radius: 7px; color: var(--muted);
  background: var(--surface-2); border: 1px solid var(--border);
  transition: color 0.15s var(--pl-ease), border-color 0.15s var(--pl-ease);
}
.acts-expand svg { width: 13px; height: 13px; }
.acts-expand:hover { border-color: var(--border-strong); color: var(--ink); }
.acts-expand[aria-pressed="true"] {
  background: var(--wash); border-color: var(--border-strong); color: var(--ink);
}
.acts-expand:focus-visible { outline: none; border-color: var(--accent-text); box-shadow: 0 0 0 3px var(--accent-soft); }
.acts-head .acts-sort:focus-visible { outline: none; border-color: var(--accent-text); box-shadow: 0 0 0 3px var(--accent-soft); }
.acts-group {
  margin: 10px 0 5px; font-size: 0.64rem; font-weight: 800;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
}
.acts-group:first-child { margin-top: 0; }
.acts-none { font-size: 0.78rem; color: var(--muted); margin: 6px 0; }
.acts-box.side .backlog-chip {
  width: 100%; max-width: none; border-radius: 7px; justify-content: flex-start;
}
.chip-grip {
  position: absolute; top: 0; right: 0; bottom: 0;
  display: grid; place-items: center; width: 26px; padding-right: 5px;
  opacity: 0; color: var(--muted); pointer-events: none;
  border-radius: 0 999px 999px 0;
  background: linear-gradient(to right, transparent, var(--surface) 45%);
  transition: opacity 0.12s var(--pl-ease);
}
.backlog-chip:hover .chip-grip,
.backlog-chip:focus-visible .chip-grip { opacity: 1; }
.chip-grip svg { width: 13px; height: 13px; }
.backlog-chips.expanded .chip-grip {
  bottom: auto; height: 35px; border-radius: 0 var(--pl-radius-sm) 0 0;
}
.reorder-line {
  flex: none; width: 100%; height: 2px; border-radius: 2px; margin: 1px 0;
  background: var(--accent-text);
}
.acts-box.side .add-row {
  flex: none; display: flex; flex-wrap: wrap; gap: 5px; margin-top: 0;
  border-top: 1px solid var(--border); padding-top: 8px;
}
.acts-box.side .add-row .mini { padding: 5px 9px; font-size: 0.72rem; }
.acts-box.side.dropover { outline-offset: -2px; }

.backlog-grip {
  position: absolute; top: 0; right: 0; bottom: 0; width: 7px;
  z-index: 3; cursor: col-resize; touch-action: none;
  background: transparent;
  transition: background 0.15s var(--pl-ease);
}
.backlog-grip:hover,
.backlog-grip:focus-visible,
.backlog-grip.dragging { background: var(--accent-soft); outline: none; }
.backlog-grip:focus-visible { box-shadow: inset -2px 0 0 var(--accent-text); }
body:has(.backlog-grip.dragging) { cursor: col-resize; user-select: none; }
.backlog-chip {
  position: relative;
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: 999px; cursor: grab;
  font: inherit; font-size: 0.78rem; font-weight: 600;
  padding: 4px 11px; min-height: 28px; max-width: 210px;
  transition: border-color 0.15s var(--pl-ease), transform 0.12s var(--pl-ease);
}
.backlog-chip:hover { border-color: var(--accent-text); }
.backlog-chip:active { cursor: grabbing; }
.backlog-chip svg { width: 13px; height: 13px; flex: none; color: var(--muted); }
.backlog-chip .da-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.backlog-chips .backlog-chip { flex: none; }
.backlog-chips.expanded .backlog-chip {
  flex-wrap: wrap; align-items: flex-start; height: auto;
  border-radius: var(--pl-radius-sm); padding: 7px 11px;
}
.backlog-chips.expanded .backlog-chip .da-title {
  flex: 1 1 auto; min-width: 0;
  white-space: normal; overflow: visible; text-overflow: clip;
}
.chip-detail {
  flex-basis: 100%; margin-top: 3px; text-align: left;
  font-size: 0.72rem; font-weight: 500; color: var(--muted); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.dragging { opacity: 0.4; }
.dropover { outline: 2px dashed var(--accent-text); outline-offset: 2px; }
.day-card.dropover { border-color: var(--accent-text); }

.dates-edit {
  align-self: stretch; margin: 0 0 0 8px;
  display: grid; place-items: center; width: auto; min-width: 38px; height: auto;
  background: transparent; border: 1px solid var(--border-strong);
  border-radius: 10px; color: var(--muted); cursor: pointer;
  transition: border-color 0.15s var(--pl-ease), color 0.15s var(--pl-ease);
}
.dates-edit:hover { border-color: var(--accent-text); color: var(--accent-text); }
.dates-edit svg { width: 16px; height: 16px; }

/* ── Workspace: guide | map ─────────────────────────────── */
.workspace {
  display: grid; grid-template-columns: minmax(0, 27rem) minmax(0, 1fr);
  gap: 16px; flex: 1;
  min-height: 420px;
}
.guide-col { max-height: calc(100dvh - var(--nav-h) - 14px); }
.acts-box .acts-body { overflow: visible; }
.guide-col {
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border);
  border-radius: var(--pl-radius);
  box-shadow: var(--edge);
  display: flex; flex-direction: column; min-height: 0;
}
.guide-head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 18px 10px; flex: none;
}
.guide-head h2 { font-size: 0.98rem; font-weight: 800; margin: 0; }
.guide-head .mini { margin-left: auto; }
.guide-days { flex: 1; overflow-y: auto; padding: 0 18px 8px; min-height: 0; }

.gday { padding: 4px 0 14px; }
.gday-head {
  display: flex; align-items: baseline; gap: 8px; width: 100%;
  background: none; border: 0; border-top: 1px solid var(--border);
  cursor: pointer; font: inherit; text-align: left;
  padding: 12px 0 4px; color: var(--ink);
}
.gday:first-child .gday-head { border-top: 0; }
.gday-head strong { font-size: 0.9rem; font-weight: 800; }
.gday-head .gd-date { font-family: var(--mono); font-size: 0.72rem; color: var(--muted); }
.gday-head .gd-open { margin-left: auto; color: var(--muted); display: grid; place-items: center; }
.gday-head .gd-open svg { width: 13px; height: 13px; }
.gday-head:hover strong { text-decoration: underline; }
.gday-stay {
  display: flex; align-items: center; gap: 8px;
  font-size: 0.8rem; color: var(--accent-text);
  background: var(--accent-soft); border-radius: var(--pl-radius-sm);
  padding: 7px 11px; margin: 6px 0;
}
.gday-stay svg { width: 13px; height: 13px; flex: none; }
.gday-stay b { font-weight: 700; }
.guide-empty {
  display: grid; justify-items: center; gap: 10px; text-align: center;
  color: var(--muted); font-size: 0.88rem; padding: 42px 20px;
}
.guide-empty svg { width: 26px; height: 26px; color: var(--muted); }
.guide-footnote { font-size: 0.78rem; color: var(--muted); padding: 6px 0 10px; }

.guide-foot { flex: none; border-top: 1px solid var(--border); padding: 10px 18px 12px; }
.trip-readout {
  font-family: var(--mono); font-size: 0.72rem; color: var(--ink);
  display: flex; gap: 13px; flex-wrap: wrap;
}
.trip-readout b { color: var(--accent-text); font-weight: 700; }
.foot-note { font-size: 0.72rem; color: var(--muted); margin-top: 6px; }

/* Extras accordion inside the guide column */
details.extras { flex: none; border-top: 1px solid var(--border); }
details.extras > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 10px; padding: 12px 18px;
}
details.extras > summary::-webkit-details-marker { display: none; }
details.extras > summary:focus-visible { outline: 2px solid var(--accent-text); outline-offset: -2px; }
details.extras > summary h3 { font-size: 0.88rem; font-weight: 800; margin: 0; }
details.extras > summary .sect-sum { margin-left: auto; font-size: 0.78rem; color: var(--muted); }
details.extras > summary .chev { color: var(--muted); display: grid; place-items: center; transition: transform 0.16s var(--pl-ease); }
details.extras[open] > summary .chev { transform: rotate(90deg); }
details.extras .extras-body { padding: 0 18px 14px; max-height: 40dvh; overflow-y: auto; }

/* ── Map column ─────────────────────────────────────────── */
.map-col {
  display: flex; flex-direction: column; min-height: 0;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--pl-radius); box-shadow: var(--edge); overflow: hidden;
}
#map { flex: 1; min-height: 0; background: #EDE9E3; }
.map-foot {
  display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap;
  padding: 8px 14px; flex: none;
}
.map-legend { display: flex; gap: 14px; flex-wrap: wrap; font-family: var(--mono); font-size: 0.64rem; color: var(--muted); }
.map-legend .swatch { display: inline-block; width: 20px; height: 0; border-top: 3px solid; vertical-align: middle; margin-right: 5px; border-radius: 2px; }
.map-legend .swatch.road { border-top-color: var(--map-road); }
.map-legend .swatch.transit { border-top-style: dashed; border-top-color: var(--map-transit); }
.map-legend .swatch.flight { border-top-style: dashed; border-top-color: var(--map-accent); }
.map-hint { margin-left: auto; font-size: 0.72rem; color: var(--muted); }

/* Pins — fixed colours, because they sit on the light basemap in both themes. */
.pin-roundel {
  display: grid; place-items: center;
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--map-pin); color: var(--map-pin-ink);
  border: 2.5px solid var(--map-pin-ink);
  font-family: var(--mono); font-weight: 700; font-size: 0.82rem;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.35);
}
.pin-roundel.star::after {
  content: "★"; position: absolute; top: -9px; right: -8px;
  font-size: 0.7rem; color: var(--map-accent); text-shadow: 0 1px 2px rgba(255, 255, 255, 0.9);
}
.pin-hotel {
  display: grid; place-items: center;
  width: 32px; height: 32px; border-radius: 10px;
  background: var(--map-hotel); color: var(--map-hotel-ink);
  border: 2px solid var(--map-pin-ink);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.35);
}
.leaflet-container .leaflet-control-attribution {
  background: #FFFFFF;
  color: var(--map-transit);
}
.leaflet-container .leaflet-control-attribution a { color: var(--map-accent); }
.leaflet-container { font-family: var(--display); }
.leaflet-popup-content { font-size: 0.85rem; }
.leaflet-popup-content .pk { font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--map-transit); display: block; }

/* ── Icon buttons — unique to the planner, no sitewide equivalent ──────── */
.iconbtn {
  display: inline-grid; place-items: center; width: 32px; height: 32px;
  border: 0; border-radius: var(--pl-radius-sm); background: transparent;
  color: var(--muted); cursor: pointer;
  transition: background 0.15s var(--pl-ease), color 0.15s var(--pl-ease), opacity 0.15s var(--pl-ease);
}
.iconbtn:hover { background: var(--wash); color: var(--ink); }
.iconbtn svg { width: 15px; height: 15px; }
.iconbtn.danger:hover { background: var(--danger-soft); color: var(--danger); }
.iconbtn[aria-pressed="true"] { color: var(--accent-text); background: var(--accent-soft); }

/* ── .mini / .btn / .grid2 / the item-row family — site.css already owns
   these names for the rest of the site, so the planner's own values are
   scoped to where the planner actually renders: inside the Step 3 panel,
   and inside its seven body-level dialogs. Everywhere else on the page
   (the header, index.html, where.html) keeps site.css's plain versions. */
#step-body-2 .mini, dialog.sheet .mini ,
/* ── .mini / .btn / .grid2 / the item-row family — site.css already owns
   these names for the rest of the site, so the planner's own values are
   scoped to where the planner actually renders: inside the Step 3 panel,
   and inside its seven body-level dialogs. Everywhere else on the page
   (the header, index.html, where.html) keeps site.css's plain versions. */
#step-body-3 .mini, dialog.sheet .mini {
  min-height: 36px; padding: 7px 13px;
  font-size: 0.84rem; font-weight: 600;
  border: 1px solid var(--border-strong); border-radius: var(--pl-radius-sm);
  background: var(--surface-2); color: var(--ink);
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  transition: background 0.15s var(--pl-ease), border-color 0.15s var(--pl-ease);
}
#step-body-2 .mini:hover, dialog.sheet .mini:hover ,
#step-body-3 .mini:hover, dialog.sheet .mini:hover { background: var(--wash); border-color: var(--accent-text); }
#step-body-2 .mini svg, dialog.sheet .mini svg ,
#step-body-3 .mini svg, dialog.sheet .mini svg { width: 14px; height: 14px; color: var(--muted); }
#step-body-2 .mini.danger, dialog.sheet .mini.danger ,
#step-body-3 .mini.danger, dialog.sheet .mini.danger { color: var(--danger); }
#step-body-2 .mini.danger svg, dialog.sheet .mini.danger svg ,
#step-body-3 .mini.danger svg, dialog.sheet .mini.danger svg { color: var(--danger); }
#step-body-2 .mini.danger:hover, dialog.sheet .mini.danger:hover ,
#step-body-3 .mini.danger:hover, dialog.sheet .mini.danger:hover { border-color: var(--danger); background: var(--danger-soft); }
dialog.sheet .btn { border-radius: 10px; transition: filter 0.15s var(--pl-ease), transform 0.12s var(--pl-ease); }
dialog.sheet .btn:active { transform: scale(0.98); }
dialog.sheet .grid2 { gap: 14px; }

/* ── Item rows (divider list, inside the guide column) ─────────── */
#step-body-2 .item-row ,
/* ── Item rows (divider list, inside the guide column) ─────────── */
#step-body-3 .item-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 12px;
  align-items: center; padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
#step-body-2 .gday .item-row:last-of-type ,
#step-body-3 .gday .item-row:last-of-type { border-bottom: 0; }
#step-body-2 .item-kind ,
#step-body-3 .item-kind {
  display: grid; place-items: center; width: 32px; height: 32px;
  border-radius: 9px; background: var(--wash); flex: none;
}
#step-body-2 .item-kind svg ,
#step-body-3 .item-kind svg { width: 16px; height: 16px; color: var(--muted); }
#step-body-2 .item-main ,
#step-body-3 .item-main { min-width: 0; }
#step-body-2 .item-main strong ,
#step-body-3 .item-main strong { display: block; font-size: 0.9rem; font-weight: 700; line-height: 1.35; }
#step-body-2 .item-main > span ,
#step-body-3 .item-main > span { display: block; font-size: 0.78rem; color: var(--muted); line-height: 1.45; }
#step-body-2 .item-main .time ,
#step-body-3 .item-main .time { font-family: var(--mono); font-weight: 600; color: var(--ink); margin-right: 8px; }
#step-body-2 .item-main .notes ,
#step-body-3 .item-main .notes { overflow-wrap: anywhere; }
.rowplace {
  display: inline-flex; align-items: center; gap: 3px; margin-right: 8px;
  color: var(--muted); font-size: 0.74rem;
}
.rowplace svg { width: 11px; height: 11px; }
.badges { display: inline-flex; gap: 5px; margin-left: 8px; vertical-align: 1px; }
.badge {
  font-size: 0.64rem; font-weight: 700; letter-spacing: 0.05em;
  padding: 2px 7px; border-radius: 999px;
}
.badge.doc { background: var(--accent-soft); color: var(--accent-text); }
.badge.warn { background: var(--danger-soft); color: var(--danger); }
#step-body-2 .item-actions ,
#step-body-3 .item-actions { display: flex; gap: 2px; align-items: center; }
@media (hover: hover) and (min-width: 980px) {
  #step-body-2 .item-row .item-actions .iconbtn:not([aria-pressed="true"]) ,
  #step-body-3 .item-row .item-actions .iconbtn:not([aria-pressed="true"]) { opacity: 0; }
  #step-body-2 .item-row:hover .item-actions .iconbtn,
  #step-body-2 .item-row:focus-within .item-actions .iconbtn ,
  #step-body-3 .item-row:hover .item-actions .iconbtn,
  #step-body-3 .item-row:focus-within .item-actions .iconbtn { opacity: 1; }
}
.add-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
#step-body-2 .empty-note ,
#step-body-3 .empty-note { font-size: 0.84rem; color: var(--muted); padding: 8px 0; }

/* ── Forms (dialogs) ──────────────────────────────────────────────── */
.sect label, dialog.sheet label, .extras-body label {
  font-size: 0.76rem; font-weight: 700; color: var(--muted);
  display: block; margin-bottom: 5px;
}
.field-note { font-size: 0.78rem; color: var(--muted); margin: 0 0 10px; }
dialog.sheet input, dialog.sheet select, dialog.sheet textarea {
  border-color: var(--border-strong); border-radius: var(--pl-radius-sm); min-height: 42px;
  transition: border-color 0.15s var(--pl-ease), box-shadow 0.15s var(--pl-ease);
}
dialog.sheet textarea { min-height: 64px; }
dialog.sheet input:focus-visible, dialog.sheet select:focus-visible, dialog.sheet textarea:focus-visible {
  outline: none; border-color: var(--accent-text); box-shadow: 0 0 0 3px var(--accent-soft);
}
.transport-opts { display: flex; gap: 22px; flex-wrap: wrap; }
.transport-opts .opt, .editor-toggles .opt {
  display: flex; align-items: center; gap: 9px;
  font-size: 0.9rem; font-weight: 600; min-height: 28px; margin: 0;
  color: var(--ink); cursor: pointer;
}
.transport-opts input[type="checkbox"], .editor-toggles input[type="checkbox"],
.vehicle-window input[type="checkbox"] {
  width: 17px; height: 17px; min-height: 0; accent-color: var(--accent-text); margin: 0; cursor: pointer;
}
.editor-toggles { display: flex; gap: 18px; flex-wrap: wrap; margin-top: 2px; }
.vehicle-window { display: none; gap: 14px; align-items: end; margin-top: 14px; }
.vehicle-window.show { display: flex; flex-wrap: wrap; }
.vehicle-window .vw-field { flex: 1; min-width: 140px; }

/* ── Place search / attachments ─────────────────────────── */
.place-field { position: relative; }
.place-results {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: var(--z-results);
  background: var(--surface-2); border: 1px solid var(--border-strong); border-radius: 10px;
  box-shadow: var(--shadow-lg); overflow: hidden auto; max-height: 240px;
}
.place-results button {
  display: block; width: 100%; text-align: left; padding: 10px 14px;
  background: var(--surface-2); border: 0; cursor: pointer; font: inherit; font-size: 0.88rem; color: var(--ink);
}
.place-results button:hover, .place-results button:focus-visible,
.place-results button.active { background: var(--wash); outline: none; }
.place-results .sub { display: block; font-size: 0.74rem; color: var(--muted); }
.place-picked {
  display: flex; align-items: center; gap: 8px; margin-top: 8px;
  font-size: 0.84rem; color: var(--ink); background: var(--wash);
  border-radius: var(--pl-radius-sm); padding: 8px 12px;
}
.place-picked svg { width: 14px; height: 14px; flex: none; color: var(--muted); }
.place-picked .clear {
  margin-left: auto; background: none; border: 0; cursor: pointer;
  color: var(--muted); font-size: 1.05rem; line-height: 1; padding: 4px; border-radius: 6px;
}
.place-picked .clear:hover { color: var(--danger); }
.attach-zone {
  border: 1.5px dashed var(--border-strong); border-radius: 10px; padding: 13px;
  text-align: center; font-size: 0.85rem; color: var(--muted); cursor: pointer;
  transition: border-color 0.15s var(--pl-ease), background 0.15s var(--pl-ease);
}
.attach-zone:hover, .attach-zone.drag { border-color: var(--accent-text); background: var(--wash); }
.attach-chip {
  display: flex; align-items: center; gap: 8px; background: var(--accent-soft);
  border-radius: var(--pl-radius-sm); padding: 9px 12px;
  font-size: 0.84rem; color: var(--ink);
}
.attach-chip svg { width: 14px; height: 14px; flex: none; color: var(--accent-text); }
.attach-chip .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attach-chip .rm {
  margin-left: auto; background: none; border: 0; cursor: pointer;
  color: var(--muted); font-size: 1.05rem; line-height: 1; padding: 4px; border-radius: 6px;
}
.attach-chip .rm:hover { color: var(--danger); }

/* ── Extras lists ───────────────────────────────────────── */
.extra-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: center;
  padding: 10px 0; border-bottom: 1px solid var(--border);
}
.extra-row:last-child { border-bottom: 0; }
.extra-row .glyph {
  display: grid; place-items: center; width: 32px; height: 32px;
  border-radius: 9px; background: var(--wash);
}
.extra-row .glyph svg { width: 15px; height: 15px; color: var(--muted); }
.extra-row strong { font-size: 0.9rem; font-weight: 700; display: block; }
.extra-row span { font-size: 0.78rem; color: var(--muted); }
.phone-rows { display: grid; gap: 8px; margin-top: 8px; }
.phone-rows .pr { display: grid; grid-template-columns: 1fr 1fr auto; gap: 8px; align-items: center; }

/* ── Dialogs ────────────────────────────────────────────── */
dialog.sheet {
  margin: auto;
  border: 1px solid var(--border); border-radius: 14px; padding: 0; max-width: 500px;
  width: calc(100vw - 40px); background: var(--surface-2); color: var(--ink);
  max-height: calc(100dvh - 48px); overflow-y: auto;
  box-shadow: var(--edge), var(--shadow-lg);
}
dialog.sheet::backdrop { background: var(--scrim); }
dialog.sheet[open] { animation: sheet-in 0.2s var(--pl-ease); }
@keyframes sheet-in {
  from { opacity: 0; transform: translateY(10px) scale(0.985); }
  to { opacity: 1; transform: none; }
}
dialog.sheet .card { margin: 0; box-shadow: none; border: 0; background: transparent; padding: 22px; border-radius: 14px; }
dialog.sheet h3 { font-size: 1.02rem; font-weight: 800; margin-bottom: 16px; }
/* Flight form */
.fl-leg {
  border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 12px 12px; margin-top: 12px;
}
.fl-leg legend {
  font-size: 0.74rem; font-weight: 700; color: var(--muted);
  padding: 0 6px; text-transform: uppercase; letter-spacing: 0.06em;
}
.fl-grid { display: grid; grid-template-columns: 1.3fr 0.8fr 0.9fr 1.3fr; gap: 8px; }
.fl-grid input { min-height: 40px; }
@media (max-width: 480px) { .fl-grid { grid-template-columns: 1fr 1fr; } }
.attach-first { margin-bottom: 2px; }
.sheet-actions { margin-top: 18px; width: 100%; }
.swap-body { font-size: 0.9rem; line-height: 1.55; color: var(--ink); }
.swap-body strong { font-weight: 800; }

/* ── Calendar ───────────────────────────────────────────── */
.cal-card { min-width: 320px; }
.cal-nav { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.cal-nav strong { font-size: 0.95rem; font-weight: 800; }
.cal-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
  text-align: center;
}
.cal-dow {
  font-family: var(--mono); font-size: 0.64rem; color: var(--muted);
  padding: 4px 0 6px; font-weight: 600;
}
.cal-day {
  font: inherit; font-size: 0.86rem; font-weight: 600; color: var(--ink);
  background: transparent; border: 0; border-radius: var(--pl-radius-sm);
  min-height: 38px; cursor: pointer;
  transition: background 0.12s var(--pl-ease);
}
.cal-day:hover { background: var(--wash); }
.cal-day.in-range { background: var(--accent-soft); border-radius: 0; }
.cal-day.sel { background: var(--accent); color: var(--accent-ink); box-shadow: inset 0 0 0 1px var(--accent-text); }
.cal-day.sel-start { border-radius: var(--pl-radius-sm) 0 0 var(--pl-radius-sm); }
.cal-day.sel-end { border-radius: 0 var(--pl-radius-sm) var(--pl-radius-sm) 0; }
.cal-day.sel-start.sel-end { border-radius: var(--pl-radius-sm); }
.cal-foot { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
.cal-readout { font-family: var(--mono); font-size: 0.74rem; color: var(--muted); flex: 1; }
#cal-apply:disabled { opacity: 0.45; cursor: default; }

/* ── Narrow screens ─────────────────────────────── */
/* Under 780px only the focused day gets a schedule (see SINGLE_DAY in
   stays.js), but every day keeps its own column in the row above, so the
   days line up with the timeline instead of scrolling beside it. */
.day-grid.narrow { width: 100%; }
.day-grid.single { width: 100%; }
.day-grid.single .day-acts { margin: 0; }
.day-grid.single .stay-bar { margin: 0; justify-content: center; }

@media (max-width: 979px) {
  /* Release the fixed height so the stacked column below can grow with its
     content instead of clipping to 68vh. */
  .step-3 { height: auto; }
  .workspace { display: flex; flex-direction: column; }
  .map-col { order: 0; height: 46dvh; min-height: 300px; }
  /* The guide stops being a self-scrolling column here and flows down the
     page instead. Releasing the height cap is what makes that safe: capped
     but overflowing visibly, its days spilled straight over Extras and the
     footer beneath them. */
  .guide-col { order: 1; max-height: none; }
  .guide-days { overflow: visible; }
  .map-hint { display: none; }
  /* Between tablet and phone the days can still sit together, but they may
     outgrow the width — let the board scroll rather than crush the columns. */
  .dayrow { overflow-x: auto; }
}

@media (max-width: 780px) {
  /* One day fills the width, so the schedule can be read and tapped. */
  .day-grid.single .day-act { font-size: 0.76rem; min-height: 22px; padding: 3px 8px; }
  .day-grid.single .day-act svg { width: 12px; height: 12px; }
  /* Touch has no hover and no drag; make the tap target itself comfortable. */
  .backlog-chip { min-height: 36px; padding: 7px 13px; }
}

/* Touch devices can't drag: say what to do instead of implying a gesture
   that will never fire. */
@media (hover: none) {
  .day-act, .backlog-chip { cursor: pointer; }
  .acts-box > summary .acts-count .drag-hint { display: none; }
  /* Reordering is the same drag gesture, so the grip would be advertising the
     same thing the hint above already declines to. display:none, not opacity:
     it also gives the title back its width on the narrowest screens. */
  .chip-grip { display: none; }
}

/* ── Toast ──────────────────────────────────────────────── */
/* Inverted against the page in both themes, so it reads as an overlay. */
.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(8px);
  background: var(--ink); color: var(--surface); border: 0;
  border-radius: 999px; padding: 10px 22px; font-size: 0.88rem; z-index: var(--z-toast);
  box-shadow: var(--shadow-lg);
  opacity: 0; pointer-events: none; transition: opacity 0.2s var(--pl-ease), transform 0.2s var(--pl-ease);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }


/* ── Trip Overview ────────────────────────────────────────────────────────
   The fourth tab is pushed to the right edge of the strip. margin-left:auto
   on a flex child is the whole mechanism — no absolute positioning, so it
   still wraps with the others on a narrow screen instead of overlapping them. */
.steps { display: flex; }
.step-tab-overview { margin-left: auto; }
/* It carries no step number, so its label needs the height the eyebrow would
   have taken, or it sits shorter than its neighbours. */
.step-tab-overview .step-name { display: block; padding-block: 6px 0; }

/* One column. The evidenced layering reads top to bottom — trip-level framing,
   then what the trip still needs, then the day-by-day, then reference material
   — and a side column would have put the reference beside the narrative rather
   than after it. */
.step-body.step-4 { display: block; }
.ov-main { max-width: 62rem; }

.ov-main { display: flex; flex-direction: column; gap: 22px; min-width: 0; }
.ov-side { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.ov-block { display: flex; flex-direction: column; gap: 10px; }
.ov-h {
  font: 800 0.95rem/1.2 var(--display);
  letter-spacing: 0.01em; color: var(--ink); margin: 0;
  padding-bottom: 6px; border-bottom: 1px solid var(--border);
}

/* The guide keeps its own card, but on this tab it is a column of its own
   rather than half a workspace. */
.step-body.step-4 .guide-col { max-height: none; }

/* Step 3 lost its left column when the guide moved here. */
.workspace-solo { grid-template-columns: minmax(0, 1fr); }

/* ── Trip Overview content ────────────────────────────────────────────── */
.ov-summary, .ov-todo, .ov-days { display: flex; flex-direction: column; gap: 10px; }
.ov-para { margin: 0; font-size: 0.95rem; line-height: 1.6; color: var(--ink); max-width: 68ch; }
.ov-para-note, .ov-empty, .ov-done { margin: 0; font-size: 0.78rem; color: var(--muted); }
.ov-done { color: var(--w-good, var(--muted)); font-weight: 600; }

.ov-todo-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.ov-todo-item { display: flex; align-items: baseline; gap: 9px; }
.ov-todo-tag {
  flex: none; min-width: 82px;
  font: 700 0.62rem/1.6 var(--display); letter-spacing: 0.05em; text-transform: uppercase;
}
.ov-todo-item.is-blocking .ov-todo-tag { color: var(--danger); }
.ov-todo-item.is-todo .ov-todo-tag { color: var(--w-warn, var(--muted)); }
.ov-todo-item.is-optional .ov-todo-tag { color: var(--muted); }
/* Every row is a way back to the step that fixes it, so it reads as text but
   behaves as a link. */
.ov-todo-go {
  border: 0; background: none; padding: 2px 0; margin: 0;
  font: inherit; font-size: 0.85rem; color: var(--ink);
  text-align: left; cursor: pointer; min-height: 24px;
  text-decoration: underline; text-decoration-color: var(--border-strong);
  text-underline-offset: 3px;
}
.ov-todo-go:hover { color: var(--accent-text); text-decoration-color: var(--accent-text); }

/* The day is the primary structure of this page, and it used not to look like
   one: the heading was 0.9rem against 0.88rem activity titles — a 0.3px
   difference — with a hairline rule that all but disappears on the dark
   ground. On a nine-day trip this list runs past 12,000px, so a reader
   scrolling at speed had nothing to catch the boundary on.

   The hierarchy is size, space and rule TOGETHER, not colour alone: the title
   is ~1.7x the activity titles under it, each day opens on a wide band of air,
   and the meta line drops away to muted so the title owns the row by itself. */
.ov-day {
  display: flex; flex-direction: column; gap: 6px;
  padding: 34px 0 20px; border-top: 1px solid var(--border);
}
.ov-day:first-child { border-top: 0; padding-top: 6px; }
.ov-day-h { display: flex; flex-direction: column; gap: 4px; margin: 0 0 8px; }

/* Fluid, because this header is the one thing that must stay legible from a
   485px phone to a wide desktop without a breakpoint to babysit it. The floor
   is 1.3rem and not lower: measured at 485px, a 1.15rem floor rendered 19.6px
   against 14.1px rows -- 1.39x, which reads as "same level, slightly off"
   rather than as a heading. tools/probes/overview-day-separation.js holds the
   1.4x line at both widths. */
.ov-day-t {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  font: 800 clamp(1.3rem, 1.05rem + 0.9vw, 1.5rem)/1.15 var(--display);
  color: var(--ink);
}
/* "DAY 1" carries the accent and the tracking; the place carries the ink. Two
   jobs, one line: find the day, then know where you are. */
.ov-day-n { text-transform: uppercase; letter-spacing: 0.06em; color: var(--accent-text); }
.ov-day-sep { font-weight: 400; color: var(--border-strong); }
.ov-day-place { color: var(--ink); }

.ov-day-meta { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.ov-day-date { font: 400 0.8rem/1.3 var(--display); color: var(--muted); }
.ov-day-kind {
  font: 700 0.6rem/1 var(--display); letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--accent-text);
}
.ov-stay { margin: 0; }
/* "Check out by 11:00 AM" is the actionable half of the line — the hotel name
   beside it is already a link and already loud enough. Only ever rendered
   when the booking STATED an hour. */
.ov-stay-lead { font-weight: 600; color: var(--ink); }
/* A hotel line sits IN the day now, so it reads as one of the day's events.
   Held slightly back from the activities around it: leaving and arriving are
   the frame of the day, not the point of it. */
.ov-stay-item .ov-item-body { color: var(--muted); }
.ov-stay-item .ov-stay-lead { font-size: 0.88rem; }
.ov-stay-go {
  border: 0; background: none; padding: 2px 0; margin: 0; cursor: pointer;
  font: inherit; font-size: 0.78rem; color: var(--muted); min-height: 24px;
  text-decoration: underline; text-decoration-color: var(--border);
  text-underline-offset: 3px;
}
.ov-stay-go:hover { color: var(--accent-text); }

.ov-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.ov-item { display: flex; gap: 12px; align-items: baseline; padding: 5px 0; }
.ov-time {
  flex: none; min-width: 76px;
  font: 600 0.74rem/1.5 var(--mono); font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.ov-item-body { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; min-width: 0; }
.ov-item-title {
  border: 0; background: none; padding: 2px 0; margin: 0; cursor: pointer;
  font: inherit; font-size: 0.88rem; font-weight: 600; color: var(--ink);
  text-align: left; min-height: 24px;
}
.ov-item-title:hover { color: var(--accent-text); }
.ov-item-empty { color: var(--muted); font-size: 0.82rem; padding-left: 88px; }
.ov-must {
  font: 700 0.58rem/1 var(--display); letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--accent-text); background: var(--accent-soft);
  padding: 3px 7px; border-radius: 999px; white-space: nowrap;
}
/* The leg between two activities. Indented under the time column and set
   quieter than the activities, because it is the gap, not the point. */
.ov-leg {
  /* The spelled-out train steps sit UNDER the summary line, not beside it,
     so the row has to be allowed to wrap. */
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-left: 88px; padding: 2px 0;
  font-size: 0.76rem; color: var(--muted);
}
.ov-leg-rule { flex: none; width: 14px; height: 1px; background: var(--border-strong); }

/* Which train, how many stops, where to change -- the part a reader looks at
   while standing on a platform, so it is set apart from the summary line
   above it rather than folded into the same sentence. */
.ov-steps {
  flex-basis: 100%;
  margin: 5px 0 7px 22px; padding: 0;
  list-style: none;
  display: flex; flex-direction: column; gap: 1px;
  border-left: 2px solid var(--border-strong);
}
.ov-step {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 6px 12px; flex-wrap: wrap;
  padding: 3px 0 3px 12px;
  font-size: 0.75rem; color: var(--ink);
}
.ov-step-change, .ov-step-walk { color: var(--muted); }

/* Cards fold to their summary line. The disclosure is the browser's own
   <details>; only the marker is ours, so it cannot get out of sync. */
.ov-leg-d { flex: 1; min-width: 0; }
.ov-leg-sum {
  cursor: pointer; list-style: none;
  display: flex; align-items: baseline; gap: 6px;
}
.ov-leg-sum::-webkit-details-marker { display: none; }
.ov-leg-sum::before {
  content: "\25B8";
  flex: none; font-size: 0.7em;
  transition: transform 0.12s ease;
}
.ov-leg-d[open] > .ov-leg-sum::before { transform: rotate(90deg); }

.ov-legs-tools {
  display: flex; align-items: center; gap: 8px;
  margin: 2px 0 10px;
}
.ov-legs-tools-label {
  font-size: 0.72rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted);
}
.ov-legs-btn {
  font: inherit; font-size: 0.76rem;
  padding: 3px 10px; border-radius: 999px;
  border: 1px solid var(--border-strong);
  background: none; color: var(--ink); cursor: pointer;
}
.ov-legs-btn:hover { border-color: var(--ink); }

/* The transport-data notice. Quiet, but never hidden: the ODPT Developer
   Guideline requires a general user be able to reach it easily. */
.ov-source { margin-top: 4px; }
.ov-source a { color: inherit; }
.ov-step-meta { flex: none; font-variant-numeric: tabular-nums; color: var(--muted); }
/* The doing of a change -- which signs, which gates -- on its own quieter
   line under the change row. */
.ov-step-hint {
  flex-basis: 100%;
  font-size: 0.72rem; line-height: 1.45; color: var(--muted);
  padding-left: 12px;
}

/* The score strip on the overview. In a flex column it stretched to the
   column's full height, and in a narrower column the longest chip label
   ("Hotel Stay Area") wrapped to two lines and sat higher than its
   neighbours — the strip has to read as one row of equals. */
.ov-main > .w-guide {
  align-self: start;
  align-items: center;
  flex: none;
  width: 100%;
  overflow-x: auto;
}
.ov-main > .w-guide .w-chip { flex: none; }
.ov-main > .w-guide .w-chip-label { white-space: nowrap; }

.ov-header { display: flex; flex-direction: column; gap: 2px; }
.ov-trip-name { margin: 0; font: 800 1.35rem/1.2 var(--display); letter-spacing: -0.01em; color: var(--ink); }
.ov-trip-meta { margin: 0; font-size: 0.85rem; color: var(--muted); }

/* The clock sits under its block label, quieter: the block is how the day
   reads, the time is the detail behind it. */
.ov-clock { display: block; font-size: 0.68rem; opacity: 0.75; font-weight: 400; }

.ov-estimates {
  margin: 14px 0 0; font-size: 0.74rem; color: var(--muted); max-width: 60ch;
}

/* Generic reminders, fenced off from the computed list above so the page never
   implies it checked them. */
.ov-ref {
  margin-top: 14px; padding: 12px 14px;
  border: 1px dashed var(--border-strong); border-radius: 10px;
  display: flex; flex-direction: column; gap: 6px;
}
.ov-ref-h { margin: 0; font: 700 0.75rem/1.2 var(--display); color: var(--ink); }
.ov-ref-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 3px;
  font-size: 0.8rem; color: var(--muted); }
.ov-ref-note { margin: 0; font-size: 0.72rem; color: var(--muted); font-style: italic; }

/* The guide panel used to be a side column with its own header bar. On the
   overview it is a section like the others, so its head reads as the section
   heading rather than a card title. */
.step-body.step-4 .guide-col { border: 0; padding: 0; background: none; overflow: visible; }
.step-body.step-4 .guide-head h2 { font: 800 1rem/1.2 var(--display); letter-spacing: -0.01em; }

/* ── Day list: the pin number ────────────────────────────────────────────
   Occupies the same slot the kind icon does, so a numbered row and an
   unnumbered one still line up. It is the map's roundel, quietened: same
   shape, same reading, without competing with the title. */
#step-body-3 .item-row .item-no,
#step-body-2 .item-row .item-no {
  width: 22px; height: 22px; flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--wash);
  color: var(--ink);
  font: 700 0.68rem/1 var(--display);
  font-variant-numeric: tabular-nums;
}

/* ── Day planner pins, on the shared MapKit map ──────────────────────────
   DOM annotations, because MapKit culls a MarkerAnnotation whose forced label
   cannot be placed -- which reads as a broken layer rather than a busy one. */
.day-pin {
  display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: 50%;
  font: 700 0.72rem/1 var(--display);
  font-variant-numeric: tabular-nums;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
  border: 2px solid #fff;
  transform: translate(-50%, -50%);
}
.day-pin-stop { background: var(--map-road, #123032); color: #fff; }
.day-pin-stop.is-must { background: var(--accent, #0C6570); }
.day-pin-hotel { background: var(--map-hotel, #7A5C26); color: #fff; }
.day-pin-hotel svg { display: block; stroke: currentColor; fill: none; stroke-width: 2; }
/* The map fills its panel the same way Steps 1 and 2 do. */
.s3-map { position: relative; min-height: 260px; }

/* The two ends of a day. The numbers carry direction between the stops; these
   say which end is which, so a line to a hotel cannot be read backwards. */
.day-pin-hotel { position: relative; }
.day-pin-hotel.is-start { background: var(--map-hotel, #7A5C26); }
.day-pin-hotel.is-end { background: var(--ink, #101819); }
.day-pin-role {
  position: absolute; top: calc(100% + 3px); left: 50%; transform: translateX(-50%);
  font: 700 0.55rem/1 var(--display);
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink); background: rgba(255, 255, 255, .92);
  padding: 2px 4px; border-radius: 3px; white-space: nowrap;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .2);
}

/* Turn-by-turn under a walking step. Filled in only when a traveller opens
   the card — see fillWalkDirections() — so an unopened overview costs nothing.
   Indented under its step so it reads as detail OF that step, not as more
   steps of the journey. */
.ov-walk-steps {
  list-style: decimal;
  margin: .35rem 0 .1rem;
  padding-left: 1.25rem;
  display: grid;
  gap: .2rem;
}
.ov-walk-step {
  font-size: .82rem;
  line-height: 1.45;
  color: var(--ink-2, #55606b);
}
.ov-walk-step .ov-step-meta { margin-left: .4rem; opacity: .75; }

/* The line's own colour, as a dot rather than a word: Japanese signage is
   colour-first, and "#9ACD32" tells a traveller nothing. Decorative, so it is
   aria-hidden in the markup and carries no meaning a screen reader loses. */
.ov-line-dot {
  display: inline-block;
  width: .6rem; height: .6rem;
  border-radius: 50%;
  margin-right: .4rem;
  vertical-align: baseline;
  box-shadow: 0 0 0 1px rgb(0 0 0 / .18) inset;
}
/* Whose train it is — JR, Tokyo Metro, Keisei. Which gates you go through. */
.ov-step-op {
  margin-left: .4rem;
  font-size: .74rem;
  letter-spacing: .02em;
  text-transform: uppercase;
  opacity: .7;
}
