:root {
  --safe-top: env(safe-area-inset-top);
  --safe-right: env(safe-area-inset-right);
  --safe-bottom: env(safe-area-inset-bottom);
  --safe-left: env(safe-area-inset-left);
  --header-height: 72px;
  --ink: #17202a;
  --muted: #667085;
  --line: #d9e1e8;
  --panel: #ffffff;
  --paper: #f7f4ef;
  --green: #1f9d63;
  --blue: #2f80ed;
  --weekend: #ffe4e0;
  --gold: #f3b33d;
  --sun: #ffd166;
  --shadow: 0 18px 50px rgba(23, 32, 42, 0.12);
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

:root[data-theme="dark"] {
  --ink: #edf4f8;
  --muted: #9ba8b4;
  --line: #33414d;
  --panel: #111820;
  --paper: #0b1117;
  --green: #4bd08d;
  --blue: #7bb5ff;
  --weekend: #331d27;
  --gold: #ffd166;
  --sun: #f3b33d;
  --shadow: 0 18px 50px rgba(0, 0, 0, 0.36);
}

* {
  box-sizing: border-box;
}

/* The rubber-band area past the content shows the root background, not body's.
   Without this it stays unset and a light band appears in dark mode.
   `--paper` is redefined on `:root[data-theme="dark"]`, and `html` *is* `:root`,
   so this one rule themes itself. Do not hardcode a dark colour here: the
   visible page background is `--paper` (#0b1117), not black, and any mismatch
   shows as a band wherever content does not fill the viewport. */
html {
  background: var(--paper);
}

body {
  margin: 0;
  color: var(--ink);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.72), rgba(247, 244, 239, 0.96)),
    radial-gradient(circle at 15% 8%, rgba(47, 128, 237, 0.18), transparent 34%),
    var(--paper);
  -webkit-tap-highlight-color: transparent;
}

:root[data-theme="dark"] body {
  background:
    linear-gradient(180deg, rgba(13, 20, 28, 0.9), rgba(8, 12, 17, 0.98)),
    radial-gradient(circle at 15% 8%, rgba(123, 181, 255, 0.16), transparent 34%),
    var(--paper);
}

button,
input,
select {
  font: inherit;
}

button {
  cursor: pointer;
  touch-action: manipulation;
}

.app-shell {
  width: min(1180px, calc(100% - 32px));
  margin: 0 auto;
  padding: 32px 0 48px;
}

.topbar {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 24px;
}

.eyebrow {
  margin: 0 0 8px;
  color: var(--blue);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

h1,
h2,
p {
  margin-top: 0;
}

h1 {
  max-width: 760px;
  margin-bottom: 0;
  font-size: clamp(2rem, 5vw, 4.8rem);
  line-height: 0.98;
  letter-spacing: 0;
}

h2 {
  margin-bottom: 8px;
  font-size: 1.15rem;
}

main {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
}

.timeline-panel,
.view-pane {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.88);
  box-shadow: var(--shadow);
}

:root[data-theme="dark"] .timeline-panel,
:root[data-theme="dark"] .view-pane {
  background: rgba(17, 24, 32, 0.92);
}

.timeline-panel,
.view-pane {
  grid-column: 1;
}

.timeline-panel {
  padding: 20px;
}

.timeline-heading,
.day-rail,
.city-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.timeline-heading p {
  margin-bottom: 0;
  color: var(--muted);
}

.icon-text {
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #f9fbfc;
  color: var(--ink);
  padding: 9px 12px;
  text-align: left;
}

:root[data-theme="dark"] .icon-text,
:root[data-theme="dark"] .city-action,
:root[data-theme="dark"] .control-button,
:root[data-theme="dark"] .settings-toggle,
:root[data-theme="dark"] input,
:root[data-theme="dark"] select,
:root[data-theme="dark"] .controls-grid,
:root[data-theme="dark"] .city-row,
:root[data-theme="dark"] .dialog-actions button {
  background: #121c25;
  color: var(--ink);
}

.icon-text span {
  display: block;
  color: var(--green);
  font-size: 0.72rem;
  font-weight: 900;
}

.day-rail {
  margin: 22px 0 6px;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 700;
}

.scrubber-wrap {
  position: relative;
  padding: 22px 0 18px;
  cursor: ew-resize;
  touch-action: pan-y;
  user-select: none;
}

.timeline-range {
  width: 100%;
  accent-color: var(--green);
}

.scrubber-wrap.dragging .timeline-range {
  cursor: ew-resize;
}

.selection-marker {
  position: absolute;
  left: 50%;
  top: 0;
  width: 3px;
  height: 52px;
  border-radius: 999px;
  background: var(--green);
  transform: translateX(-50%);
  pointer-events: none;
}

.selection-marker::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -7px;
  width: 0;
  height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-bottom: 9px solid var(--green);
  transform: translateX(-50%);
}

.now-marker {
  position: absolute;
  top: -8px;
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  color: var(--gold);
  font-size: 0.68rem;
  font-weight: 900;
  transform: translateX(-50%);
  border: 0;
  background: transparent;
  padding: 0;
  pointer-events: auto;
}

.now-marker i {
  width: 2px;
  height: 42px;
  background: var(--gold);
}

.now-marker:focus-visible {
  outline: 3px solid rgba(243, 179, 61, 0.34);
  outline-offset: 4px;
}

.controls-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  width: 100%;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.96);
}

label {
  display: grid;
  gap: 6px;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 800;
}

input,
select {
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
  color: var(--ink);
  padding: 10px 11px;
  /* iOS Safari zooms the page when a focused control computes under 16px. These
     controls sit inside a label, and `font: inherit` pulls in the label's small
     type, so the size has to be stated here. max() keeps a larger accessibility
     default if the user has one. */
  font-size: max(16px, 1rem);
}

.city-action,
.control-button,
.settings-toggle {
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
  color: var(--ink);
  padding: 8px 10px;
}

.settings-toggle {
  display: grid;
  place-items: center;
  min-width: 44px;
  min-height: 44px;
  list-style: none;
  font-size: 1.1rem;
  font-weight: 900;
  line-height: 1;
}

.settings-toggle::-webkit-details-marker {
  display: none;
}

.settings-menu[open] .settings-toggle {
  border-color: rgba(31, 157, 99, 0.42);
  background: rgba(31, 157, 99, 0.1);
}


.view-pane {
  display: block;
  padding: 12px;
}

.city-rows {
  display: grid;
  gap: 10px;
}

/* On a tablet or desktop canvas the content is far shorter than the viewport, so
   without this the page ends around 60% down and leaves a dead band of
   background. Let the shell fill the viewport and give the slack to the city
   list, which is the part worth more room. Rows never shrink below their normal
   height, so a long list still scrolls as before.
   Scoped above the 880px breakpoint to leave the phone layout alone. */
@media (min-width: 881px) {
  .app-shell {
    display: flex;
    min-height: 100dvh;
    flex-direction: column;
  }

  main {
    flex: 1;
    grid-template-rows: auto minmax(0, 1fr);
  }

  .view-pane {
    display: flex;
    flex-direction: column;
  }

  .city-rows {
    flex: 1;
    grid-auto-rows: minmax(112px, 1fr);
  }
}

.city-row {
  min-height: 112px;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 14px;
  background: #fff;
  transition: transform 150ms ease, box-shadow 150ms ease, border-color 150ms ease, opacity 150ms ease;
}

.city-row.weekend {
  background: linear-gradient(90deg, var(--weekend), #fff 42%);
}

:root[data-theme="dark"] .city-row.weekend {
  background: linear-gradient(90deg, var(--weekend), #121c25 46%);
}

.city-meta {
  width: min(240px, 35%);
}

.city-name-line {
  display: flex;
  align-items: center;
  gap: 8px;
}

.reordering-cities .city-row {
  cursor: grabbing;
}

.reorder-mode .city-row {
  border-color: rgba(31, 157, 99, 0.44);
  cursor: grab;
  box-shadow: 0 10px 24px rgba(31, 157, 99, 0.14);
  touch-action: none;
}

.city-row.moving {
  border-color: var(--green);
  opacity: 0.72;
  transform: scale(0.985);
  box-shadow: 0 18px 36px rgba(31, 157, 99, 0.24);
}

.reorder-mode .mini-timeline {
  pointer-events: none;
  opacity: 0.68;
}

.city-name {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 850;
}

.star {
  color: var(--gold);
  font-weight: 900;
}

.offset,
.location-label,
.date-relation,
.group-count,
.sun-labels {
  color: var(--muted);
  font-size: 0.86rem;
  font-weight: 700;
}

.group-count,
.sun-labels {
  display: block;
  margin-top: 4px;
}

.location-label {
  color: var(--ink);
  font-size: 0.82rem;
}

.time-readout {
  min-width: 120px;
  text-align: right;
}

.time-readout strong {
  display: block;
  font-size: 1.55rem;
}

.time-readout span {
  color: var(--muted);
  font-size: 0.82rem;
}

.time-display {
  display: grid;
  gap: 1px;
  width: 100%;
  padding: 4px;
  text-align: right;
}

.time-display span {
  color: var(--green);
  font-size: 0.72rem;
  font-weight: 850;
}

/* Tapping a city's time returns the app to now. It has to read as plain text, so
   the button chrome is stripped and the inherited font is preserved -- the
   time-of-day modules style `.city-row .time-readout strong`, which is inside. */
.time-reset {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: inherit;
  -webkit-tap-highlight-color: transparent;
}

.time-reset:active {
  opacity: 0.55;
}

.mini-timeline {
  position: relative;
  flex: 1;
  height: 48px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: repeating-linear-gradient(90deg, #eef3f7 0 3.9%, #d8e8fb 3.9% 4.16%);
  cursor: ew-resize;
  touch-action: none;
  user-select: none;
}

:root[data-theme="dark"] .mini-timeline {
  background: repeating-linear-gradient(90deg, #172431 0 3.9%, #20354b 3.9% 4.16%);
}

.mini-timeline:focus-visible {
  outline: 3px solid rgba(31, 157, 99, 0.3);
  outline-offset: 2px;
}

.mini-timeline.dragging {
  border-color: var(--green);
  box-shadow: 0 0 0 4px rgba(31, 157, 99, 0.12);
}

.sun-window {
  position: absolute;
  top: 7px;
  bottom: 7px;
  left: var(--sun-start);
  width: var(--sun-width);
  border-radius: 999px;
  background: rgba(255, 209, 102, 0.72);
  box-shadow: 0 0 0 1px rgba(243, 179, 61, 0.32);
  z-index: 1;
}

.local-marker {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--local-position);
  width: 4px;
  border-radius: 999px;
  background: var(--green);
  transform: translateX(-50%);
  z-index: 3;
}

.edit-city-list {
  display: grid;
  gap: 8px;
}

.edit-city-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 10px;
}

.edit-city-row span {
  display: block;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 700;
}

.edit-city-row button {
  min-height: 40px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
  color: var(--ink);
  padding: 0 12px;
}

.time-dialog {
  width: min(360px, calc(100% - 32px));
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: var(--shadow);
  padding: 0;
  /* Without this the dialog falls back to the UA Canvas, which under
     color-scheme: dark is a neutral grey that clashes with the blue-tinted
     panels inside it. */
  background: var(--panel);
  color: var(--ink);
}

.time-dialog::backdrop {
  background: rgba(23, 32, 42, 0.38);
}

.time-dialog form {
  display: grid;
  gap: 16px;
  padding: 18px;
}

.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.dialog-actions button {
  min-height: 44px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
  padding: 0 14px;
}


@media (max-width: 880px) {
  .topbar,
  main {
    display: block;
  }

  .view-pane {
    margin-top: 16px;
  }

  .controls-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .city-row {
    display: grid;
  }

  .city-meta,
  .time-readout {
    width: 100%;
    min-width: 0;
    text-align: left;
  }
}

@media (max-width: 560px) {
  body {
    background: #f4f7f8;
  }

  :root[data-theme="dark"] body {
    background: #0b1117;
  }

  .app-shell {
    width: 100%;
    padding: 10px 10px calc(10px + env(safe-area-inset-bottom));
  }

  .topbar {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 10px;
    margin-bottom: 10px;
  }

  h1 {
    max-width: 22ch;
    font-size: 1.55rem;
    line-height: 1.05;
  }

  .topbar .eyebrow {
    margin-bottom: 4px;
  }

  

  .settings-menu {
    justify-self: end;
  }

  .settings-menu .controls-grid {
    right: 0;
    grid-template-columns: 1fr;
  }

  

  .timeline-panel {
    position: relative;
    z-index: 10;
    margin: 0 -10px;
    border-right: 0;
    border-left: 0;
    border-radius: 0 0 8px 8px;
    padding: 10px;
  }

  .timeline-heading {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    gap: 10px;
  }

  .timeline-heading h2 {
    margin-bottom: 2px;
  }

  .timeline-heading p {
    max-width: none;
    font-size: 0.82rem;
  }

  .icon-text {
    min-width: 82px;
    min-height: 44px;
    padding: 8px;
  }

  .icon-text strong {
    display: none;
  }

  .day-rail {
    margin-top: 12px;
  }

  .scrubber-wrap {
    padding: 16px 0 8px;
  }

  .timeline-range {
    min-height: 44px;
  }

  label {
    font-size: 0.72rem;
  }

  input,
  select {
    min-height: 44px;
    padding: 9px;
  }

  .view-pane {
    margin-top: 10px;
    border: 0;
    background: transparent;
    box-shadow: none;
    padding: 0;
  }

  .city-rows {
    display: grid;
    gap: 10px;
    overflow-x: visible;
    padding: 0 0 12px;
    scroll-snap-type: none;
  }

  .city-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "meta time"
      "timeline timeline";
    grid-template-rows: auto 30px;
    min-height: 96px;
    scroll-snap-align: none;
    align-items: center;
    gap: 8px;
    padding: 9px;
    border-radius: 8px;
    box-shadow: 0 12px 28px rgba(23, 32, 42, 0.1);
  }

  .city-meta {
    grid-area: meta;
    width: 100%;
  }

  .city-name {
    font-size: 1rem;
  }

  .date-relation,
  .group-count,
  .location-label,
  .time-readout > span {
    display: none;
  }


  .mini-timeline {
    grid-area: timeline;
    grid-column: 1 / -1;
    width: 100%;
    height: 30px;
    margin: 0;
  }

  .time-readout {
    grid-area: time;
    min-width: 92px;
    align-self: start;
  }

  .time-readout strong {
    font-size: 1.35rem;
  }

  

  .time-display {
    text-align: left;
  }

  .time-readout .time-display span {
    display: none;
  }

  

}

.brand-block {
  min-width: 0;
}

.app-subtitle,
.selected-home-time {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: 0.9rem;
  font-weight: 750;
}

.selected-home-time {
  color: var(--ink);
  font-weight: 850;
  font-variant-numeric: tabular-nums;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* The left group holds the edit control and, when it is on, the language toggle. Flex
   gap is only drawn between rendered children, and `.icon-button[hidden]` sets
   `display: none`, so with the toggle off this group is exactly as wide as the edit
   button alone -- the default header reserves no space for a control it is not showing. */
.header-lead {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.icon-button {
  display: grid;
  place-items: center;
  min-width: 48px;
  min-height: 48px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
  color: var(--ink);
}

/* `.icon-button { display: grid }` outranks the user-agent `[hidden] { display:
   none }` rule (both (0,1,0), author beats UA), so the `hidden` attribute alone
   is a no-op on any icon-button. Restore it explicitly. */
.icon-button[hidden] {
  display: none;
}

.primary-action {
  display: inline-flex;
  gap: 6px;
  justify-content: center;
  min-width: 0;
  padding: 0 12px;
  border-color: rgba(31, 157, 99, 0.38);
  background: rgba(31, 157, 99, 0.1);
  color: var(--ink);
  font-weight: 850;
}

.language-toggle {
  grid-template-columns: auto auto;
  gap: 5px;
  min-width: 66px;
  padding: 0 10px;
  font-size: 0.78rem;
  font-weight: 900;
}

.language-toggle.active {
  border-color: rgba(31, 157, 99, 0.42);
  background: rgba(31, 157, 99, 0.12);
}

.header-language-toggle {
  min-width: auto;
  padding: 0 10px;
  font-size: 0.78rem;
  font-weight: 600;
}

/* The "+" is a real, aria-hidden element (not ::before generated content):
   generated content is still folded into the accessible name computation,
   so an ::before "+" alongside font-size: 0 text reads as "+Edit cities"
   to a screen reader. Hiding the label visually (not via display/visibility,
   which would drop it from the accessibility tree) keeps the accessible
   name exactly "Edit cities" while showing only the glyph. */
.header-text-button-plus {
  display: none;
}

.header-text-button.compact .header-text-button-plus {
  display: inline;
  font-size: 1.5rem;
  line-height: 1;
}

.header-text-button.compact .header-text-button-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

.action-label {
  white-space: nowrap;
}

/* The sort control cycles, so unlike the old + it must always show which mode is
   active. The label stays visible at every width, unless the header language
   toggle is on and needs the room back (see `.sort-cycle.icon-only` below) —
   the button's aria-label already names the sort order independent of this
   visible text, so hiding the label never costs the accessible name. */
.sort-cycle {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  justify-content: center;
  min-width: 0;
  padding: 0 10px;
  color: var(--ink);
}

.sort-cycle .action-label {
  font-size: 0.78rem;
  font-weight: 800;
}

.sort-cycle.icon-only {
  gap: 0;
  padding: 0;
}

.sort-cycle.icon-only .action-label {
  display: none;
}

.nav-icon {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.timeline-range {
  min-height: 52px;
}

.timeline-range::-webkit-slider-runnable-track {
  height: 14px;
  border-radius: 999px;
  background: linear-gradient(90deg, #d7e3ec, #eef5f9);
}

/* The track colours are literals, so dark mode needs its own pair -- otherwise a
   bright rail sits on the dark panel. A pseudo-element cannot inherit a custom
   property through the shadow boundary reliably here, hence the explicit values. */
:root[data-theme="dark"] .timeline-range::-webkit-slider-runnable-track {
  background: linear-gradient(90deg, #1c2836, #2b3a47);
}

.timeline-range::-webkit-slider-thumb {
  width: 30px;
  height: 30px;
  margin-top: -8px;
}

.mini-timeline {
  cursor: ew-resize;
  touch-action: none;
}

.reorder-mode .city-row {
  touch-action: pan-y;
}

.edit-city-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto auto auto;
}

.drag-handle {
  display: grid;
  place-items: center;
  width: 32px;
  height: 44px;
  color: var(--muted);
  font-weight: 900;
}

.sheet-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.sheet-heading h2 {
  margin: 0;
}

.sheet-dialog {
  overflow: hidden;
}

/* Vertical only. `overflow: auto` permits both axes, so anything that ever grows wider
   than the sheet -- a long translated label, a wide select -- turns the whole menu into a
   two-dimensional pannable surface rather than clipping. Nothing overflows horizontally
   today at 320px or 390px, measured, so this changes nothing now and bounds the failure
   later. `touch-action: pan-y` says the same thing to the gesture layer, which is what
   actually decides whether a WebView starts a sideways drag. */
.sheet-dialog form {
  max-height: min(720px, calc(100dvh - 48px));
  overflow-x: hidden;
  overflow-y: auto;
  touch-action: pan-y;
  -webkit-overflow-scrolling: touch;
}

.sheet-dialog .controls-grid {
  position: static;
  grid-template-columns: 1fr;
  max-height: none;
  overflow: visible;
  box-shadow: none;
}

.sheet-dialog .controls-grid section {
  display: grid;
  gap: 12px;
}

.sheet-dialog .controls-grid h3 {
  margin: 0;
  font-size: 0.82rem;
  color: var(--muted);
  text-transform: uppercase;
}

.sheet-dialog .controls-grid,
/* `:root[data-theme="dark"] .controls-grid` scores (0,3,0) and would otherwise
   beat the (0,2,0) rule above, tinting these sections a different dark than the
   sheet they sit in. */
:root[data-theme="dark"] .sheet-dialog .controls-grid {
  gap: 22px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
}

.sheet-dialog .controls-grid section {
  gap: 10px;
}

.sheet-dialog .controls-grid section + section {
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

.sheet-dialog .controls-grid label,
.setting-choice-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(152px, 0.95fr);
  align-items: center;
  gap: 14px;
  min-height: 48px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
}

.sheet-dialog .controls-grid label > span,
.setting-choice-row > span {
  color: var(--ink);
  font-size: 0.92rem;
  font-weight: 720;
}

.sheet-dialog .controls-grid select {
  width: 100%;
  min-height: 44px;
}

.sheet-dialog .language-toggle {
  grid-template-columns: auto 1fr;
  justify-content: start;
  width: 100%;
  min-height: 48px;
  border-radius: 6px;
  background: rgba(47, 128, 237, 0.08);
  color: var(--blue);
}

.language-rotation {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0.5rem 0;
}

/* No max-height, and so no scroller of its own. Capping this list made it a second
   scrolling region nested inside the sheet's: dragging over the languages moved the list
   while dragging anywhere else moved the sheet, and with eleven languages the inner list
   had 155px to travel. Two scrollers competing for the same gesture is what made the
   settings sheet feel unbounded. The list now flows, and the sheet's form is the single
   scroller for the whole menu. */
.language-rotation-list {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.language-rotation-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.15rem 0;
}

/* `.sheet-dialog .controls-grid label` (a two-column grid row with a
   min-height and border-bottom, meant for the label+select settings rows)
   otherwise outranks `.language-rotation-row` above by specificity and
   turns each checkbox row into a stretched grid cell. Match its selector
   weight to opt this row back out into a plain checkbox list item. */
.sheet-dialog .controls-grid .language-rotation-row {
  display: flex;
  min-height: auto;
  padding: 0.15rem 0;
  border-bottom: 0;
}

/* Same specificity fix as `.language-rotation-row` above: opt this checkbox
   row out of the two-column label grid used for label+select rows. */
.sheet-dialog .controls-grid .setting-check {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: auto;
  padding: 6px 0;
}

/* min-height is load-bearing, not redundant with height: the narrow-screen
   `input, select { min-height: 44px }` rule otherwise stretches this checkbox
   to 20x44 next to the 20x20 rotation checkboxes right above it. */
.setting-check input[type="checkbox"] {
  width: 20px;
  height: 20px;
  min-width: 20px;
  min-height: 20px;
}

.language-rotation-row input[type="checkbox"] {
  width: 20px;
  height: 20px;
  min-width: 20px;
  min-height: 20px;
  flex: none;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 4px;
}

.theme-options {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
}

.theme-choice {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 44px;
  border: 0;
  border-right: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
}

.theme-choice:last-child {
  border-right: 0;
}

.theme-choice.active {
  background: var(--blue);
  color: #fff;
}

.theme-choice:focus-visible {
  z-index: 1;
  outline: 3px solid rgba(47, 128, 237, 0.36);
  outline-offset: -3px;
}

.theme-choice .nav-icon {
  width: 21px;
  height: 21px;
}

.theme-choice .auto-sun {
  position: absolute;
  width: 15px;
  height: 15px;
  transform: translate(-5px, -5px);
}

.theme-choice .auto-moon {
  position: absolute;
  width: 15px;
  height: 15px;
  transform: translate(5px, 5px);
}

.sheet-dialog #edit-cities {
  min-height: 48px;
  border-color: var(--line);
  border-radius: 6px;
  background: var(--panel);
  color: var(--blue);
  font-weight: 780;
}

.city-picker-results {
  display: grid;
  gap: 8px;
  max-height: min(58vh, 520px);
  overflow: auto;
}

.city-result {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  min-height: 64px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
  color: var(--ink);
  padding: 10px;
  text-align: left;
}

.city-result small,
.city-result em,
.picker-status,
.empty-state {
  display: block;
  color: var(--muted);
  font-size: 0.8rem;
  font-style: normal;
  font-weight: 750;
}

:root[data-theme="dark"] .icon-button,
:root[data-theme="dark"] .city-result {
  background: #121c25;
  color: var(--ink);
}

@media (max-width: 560px) {
  .app-shell {
    padding: calc(var(--safe-top) + 8px) max(10px, var(--safe-right)) calc(var(--safe-bottom) + 12px) max(10px, var(--safe-left));
    padding-top: calc(var(--safe-top) + 8px);
  }

  .topbar {
    position: static;
    z-index: auto;
    align-items: center;
    min-height: var(--header-height);
    padding: 4px 0;
    background: transparent;
    backdrop-filter: none;
  }

  h1 {
    font-size: 1.15rem;
  }

  

  

  .language-toggle {
    min-width: 58px;
    padding: 0 8px;
  }

  .header-actions,
  .header-lead {
    flex-shrink: 0;
    gap: 6px;
  }

  .primary-action {
    min-width: 48px;
    padding: 0;
  }

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

  

  

  

  

  

  .sheet-dialog {
    width: 100%;
    max-width: none;
    max-height: calc(88dvh - var(--safe-top));
    margin: auto 0 0;
    border-radius: 16px 16px 0 0;
  }

  .sheet-dialog form {
    max-height: calc(88dvh - var(--safe-top));
    /* Longhand deliberately. `overflow: auto` here is shorthand for both axes, so it
       silently reset the `overflow-x: hidden` set on this same selector above and handed
       the sheet its sideways drag back. Only the vertical axis is being changed. */
    overflow-y: auto;
    padding-bottom: calc(18px + var(--safe-bottom));
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    scroll-behavior: auto !important;
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/* Native utility pass: the city list is intentionally quiet and edge-to-edge. */
body {
  background: var(--paper);
}

:root[data-theme="dark"] body {
  background: #000;
}

.topbar {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}

.header-text-button,
.mode-title {
  min-height: 44px;
  border: 0;
  background: transparent;
  color: var(--blue);
  font-weight: 750;
}

.header-text-button {
  position: relative;
}

.mode-title {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  /* The user-agent button padding costs the title ~12px of the narrow header row,
     which is the difference between a translated mode name fitting and ellipsising. */
  padding: 0;
  color: var(--ink);
  font-size: 1.15rem;
  font-weight: 850;
  text-align: center;
}

.mode-title > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mode-chevron {
  width: 18px;
  height: 18px;
}

.header-actions,
.header-lead {
  gap: 4px;
}

.header-actions .icon-button,
.header-lead .icon-button {
  border: 0;
  background: transparent;
}

.primary-action {
  color: var(--blue);
}

.city-details-content {
  display: grid;
  gap: 16px;
}

.detail-current-time {
  font-size: 3.4rem;
  font-weight: 500;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.detail-date {
  margin: -8px 0 0;
  color: var(--muted);
  font-weight: 700;
}

.detail-facts {
  display: grid;
  margin: 0;
  border-top: 1px solid var(--line);
}

.detail-facts div {
  display: grid;
  grid-template-columns: minmax(108px, 0.9fr) minmax(0, 1.1fr);
  gap: 16px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.detail-facts dt,
.detail-facts dd {
  margin: 0;
}

.detail-facts dt {
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 700;
}

.detail-facts dd {
  overflow-wrap: anywhere;
  font-weight: 760;
  text-align: right;
}

.detail-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-top: 16px;
}

.detail-actions button,
.editor-heading button,
.editor-add-city {
  min-height: 44px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
  color: var(--ink);
  font-weight: 760;
}

.detail-actions .detail-primary {
  grid-column: 1 / -1;
  border-color: var(--blue);
  background: var(--blue);
  color: #fff;
}

.detail-actions .detail-destructive {
  color: #c83d3d;
}

.editor-heading {
  position: sticky;
  top: 0;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(64px, 1fr) minmax(0, 1.5fr) minmax(64px, 1fr);
  align-items: center;
  gap: 8px;
  padding: 0 0 12px;
  background: var(--panel);
}

.editor-heading h2 {
  margin: 0;
  font-size: 1rem;
  text-align: center;
}

.editor-heading button {
  border-color: transparent;
  color: var(--blue);
  text-align: left;
}

.editor-heading button:last-child {
  text-align: right;
}

.editor-helper,
.editor-status {
  margin: 0 0 12px;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.4;
}

.editor-status {
  min-height: 1.15em;
  color: #b46c00;
}

.edit-city-list {
  gap: 0;
  border-top: 1px solid var(--line);
}

.edit-city-row {
  position: relative;
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) 44px;
  align-items: center;
  gap: 8px;
  min-height: 68px;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  padding: 8px 0;
}

.drag-handle {
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  cursor: grab;
}

.drag-handle:active {
  cursor: grabbing;
}

.edit-city-row.drop-before {
  box-shadow: inset 0 2px 0 var(--blue);
}

.edit-city-row.drop-after {
  box-shadow: inset 0 -2px 0 var(--blue);
}

.edit-city-copy {
  min-width: 0;
}

.edit-city-copy strong,
.edit-city-copy span {
  display: block;
}

.edit-city-copy strong {
  overflow: hidden;
  font-size: 0.98rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.edit-city-copy span {
  margin-top: 3px;
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 700;
}

.home-badge {
  display: inline-block !important;
  margin-left: 4px;
  color: var(--blue) !important;
  font-size: 0.72rem !important;
}

.edit-city-menu {
  position: relative;
}

.edit-city-menu summary {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 6px;
  color: var(--muted);
  cursor: pointer;
  list-style: none;
}

.edit-city-menu summary::-webkit-details-marker {
  display: none;
}

.edit-menu-actions {
  position: absolute;
  top: calc(100% - 4px);
  right: 0;
  z-index: 4;
  display: grid;
  min-width: 148px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
  box-shadow: var(--shadow);
}

.edit-menu-actions button {
  min-height: 44px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  padding: 0 12px;
  text-align: left;
}

.edit-menu-actions button:last-child {
  border-bottom: 0;
  color: #c83d3d;
}

.editor-add-city {
  position: sticky;
  bottom: 0;
  width: 100%;
  margin-top: 16px;
  border-color: var(--blue);
  background: var(--panel);
  color: var(--blue);
}

@media (max-width: 560px) {
  

  .topbar {
    position: static;
    z-index: auto;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 4px;
    min-height: 52px;
    margin-bottom: 10px;
    padding: 0;
    background: transparent;
    backdrop-filter: none;
  }

  .header-text-button {
    min-width: 48px;
    padding: 0 4px;
    font-size: 0.94rem;
  }

  .mode-title {
    font-size: 1.08rem;
  }

  .header-actions,
  .header-lead {
    gap: 0;
  }

  .header-actions .icon-button,
  .header-lead .icon-button {
    min-width: 44px;
    min-height: 44px;
    padding: 0;
  }

  /* The sort control carries a text label, so it needs room the icon-only
     buttons do not — unless the header language toggle is on, in which case
     the label is hidden and the sort button should be as tight as the other
     icon buttons. The extra class in the selector below outranks this rule's
     equal specificity by source order alone otherwise. */
  .header-actions .sort-cycle {
    padding: 0 8px;
  }

  .header-actions .sort-cycle.icon-only {
    padding: 0;
  }

  .header-lead .header-language-toggle {
    padding: 0 8px;
    font-size: 0.74rem;
  }

  

  

  

  

  .sheet-dialog {
    max-height: calc(100dvh - var(--safe-top));
    border-radius: 12px 12px 0 0;
  }

  .sheet-dialog form {
    max-height: calc(100dvh - var(--safe-top));
  }
}

.itinerary-person,
.itinerary-companions {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(140px, 0.95fr);
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
}

.itinerary-person > span,
.itinerary-companions > span {
  color: var(--ink);
  font-size: 0.92rem;
  font-weight: 720;
}

.itinerary-stop-list,
.itinerary-companion-list {
  border-top: 1px solid var(--line);
}

.itinerary-stop-row {
  display: grid;
  /* The date input needs its intrinsic width (a browser date control clips
     the year under ~148px) rather than a shrinkable fr track, or the year
     is cut off at narrow (390px) widths. */
  grid-template-columns: minmax(148px, auto) minmax(0, 1fr) 44px;
  align-items: center;
  gap: 8px;
  min-height: 56px;
  border-bottom: 1px solid var(--line);
  padding: 8px 0;
}

.itinerary-stop-city {
  overflow: hidden;
  border: 0;
  background: transparent;
  color: var(--blue);
  font-weight: 700;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}

.itinerary-stop-remove {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font-size: 1.1rem;
  cursor: pointer;
}

.itinerary-companion-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 48px;
  border-bottom: 1px solid var(--line);
  padding: 6px 0;
}

.itinerary-companion-row button {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font-size: 1.05rem;
  cursor: pointer;
}

/* .editor-add-city is position: sticky; bottom: 0 (shared with the single
   #edit-add-city button in the city editor). This dialog has two such
   buttons, and both sticking to the same bottom edge resolve to the same
   rect, leaving "Add stop" unreachable underneath "Add city". Neither is
   the last control in the form (Reset to default follows), so stickiness
   is not needed here the way it is in the single-list city editor: give
   both their normal static position, immediately under the list each one
   extends. */
#itinerary-add-stop,
#itinerary-add-companion {
  position: static;
}

.itinerary-banner {
  margin: 0 0 10px;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  color: var(--ink);
  font-size: 0.9rem;
}

.itinerary-banner p {
  margin: 0 0 10px;
}

.itinerary-banner-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.itinerary-banner-actions button {
  min-height: 36px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--paper);
  color: var(--ink);
  padding: 0 12px;
  cursor: pointer;
}
