/* ==========================================================================
   Map viewer — full-viewport app shell with a docked side panel
   ========================================================================== */

html, body { height: 100%; overflow: hidden; }

.app {
  display: grid;
  /*
   * An explicit column, and one that is allowed to be narrower than its
   * contents.
   *
   * Without it the grid gets an implicit `auto` column, which sizes to the
   * max-content of its widest item — the header. On a 440pt phone the header
   * wants 582px, so the column became 582, and .app-body and .map-surface went
   * with it: the map was 142px wider than the screen and everything at its
   * right edge, including the zoom controls and the map tools, was off it.
   *
   * Nothing below 430px ever showed this, which is why a 402pt simulator did
   * not. minmax(0, 1fr) makes the header's own overflow the header's problem.
   */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: calc(var(--header-h) + var(--safe-top)) 1fr;
  height: 100vh; height: 100dvh;
}

.app-body { position: relative; display: flex; min-height: 0; }

/* ---------- side panel ---------- */

.panel {
  width: 356px; flex: none;
  display: flex; flex-direction: column;
  background: var(--surface); border-right: 1px solid var(--line);
  z-index: 20; min-height: 0;
}
.panel[hidden] { display: none; }

.panel-tabs { display: flex; border-bottom: 1px solid var(--line); flex: none; }
.panel-tab {
  flex: 1; padding: 11px 6px 9px; background: none; border: 0;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  font: inherit; font-size: .82rem; font-weight: 550; color: var(--ink-3);
  cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 3px;
}
.panel-tab svg { width: 17px; height: 17px; }
.panel-tab:hover { color: var(--ink); background: var(--paper-2); }
.panel-tab[aria-selected="true"] { color: var(--clay); border-bottom-color: var(--clay); }

.panel-scroll { flex: 1; overflow-y: auto; overscroll-behavior: contain; }
.panel-section { padding: 16px 16px 20px; border-bottom: 1px solid var(--line); }
.panel-section:last-child { border-bottom: 0; }
.panel-title {
  font-family: var(--font-sans); font-size: .72rem; font-weight: 650;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ink-3);
  margin: 0 0 11px; display: flex; align-items: center; gap: 8px;
}
/* The heading's own mark sits against its text; anything else in the row —
   a count, a button — is pushed to the far end. */
.panel-title .count,
.panel-title .button { margin-left: auto; }
.panel-title .count { font-weight: 500; letter-spacing: 0; text-transform: none; font-size: .76rem; }

/* The section mark. Sized to the cap height of the heading beside it so it
   reads as part of the word rather than as a button. */
.detail-block-mark {
  flex: none; display: inline-flex; width: 15px; height: 15px;
  color: var(--clay);
}
.detail-block-mark svg { width: 100%; height: 100%; }

/* The active map's own name is a title, not a section label — let it read as one. */
.panel-title.is-name {
  font-family: var(--font-serif); font-size: 1.12rem; font-weight: 600;
  letter-spacing: -.01em; text-transform: none; color: var(--ink); margin-bottom: 6px;
}

/* layer options */
.layer-option {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 8px 9px; border-radius: var(--radius); cursor: pointer;
  border: 1px solid transparent;
}
.layer-option:hover { background: var(--paper-2); }
.layer-option.is-selected { background: var(--clay-soft); border-color: color-mix(in srgb, var(--clay) 28%, transparent); }
.layer-option input { margin: 3px 0 0; accent-color: var(--clay); flex: none; width: 15px; height: 15px; }

/* One tile of the map, beside its name. Small enough to be a hint and big
   enough to tell a photograph from a topographic sheet at a glance. */
.layer-thumb {
  flex: none; width: 48px; height: 34px; border-radius: 4px;
  border: 1px solid var(--line); background: var(--surface-2);
  object-fit: cover; overflow: hidden; display: block;
}
.layer-thumb.is-drawn { display: inline-flex; }
.layer-thumb svg { width: 100%; height: 100%; display: block; }
.layer-row.is-selected .layer-thumb { border-color: var(--clay); }
.layer-option-text { min-width: 0; }
.layer-option-name { font-size: .9rem; font-weight: 500; }
.layer-option-desc { font-size: .77rem; color: var(--ink-3); line-height: 1.42; margin-top: 1px; }

.layer-group-label {
  font-size: .7rem; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-3); margin: 12px 0 5px 9px;
}
.layer-group-label:first-child { margin-top: 0; }

.opacity-row { display: flex; align-items: center; gap: 9px; padding: 2px 9px 8px 34px; }
/* Without this the explicit display beats [hidden], so every overlay shows a
   slider whether or not it is switched on. */
.opacity-row[hidden] { display: none; }
.opacity-row input[type="range"] { flex: 1; accent-color: var(--clay); height: 3px; }
.opacity-row output { font-size: .74rem; color: var(--ink-3); width: 34px; text-align: right; font-variant-numeric: tabular-nums; }

/* map list entries */
.map-entry {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 9px; border-radius: var(--radius); border: 1px solid transparent;
}
.map-entry:hover { background: var(--paper-2); }
.map-entry.is-active { background: var(--clay-soft); border-color: color-mix(in srgb, var(--clay) 26%, transparent); }
.map-entry-swatch { width: 4px; align-self: stretch; border-radius: 2px; flex: none; min-height: 26px; }
.map-entry-text { flex: 1; min-width: 0; cursor: pointer; }
.map-entry-name { font-size: .88rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.map-entry-meta { font-size: .76rem; color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.map-entry-actions { display: flex; gap: 1px; flex: none; }
.map-entry-actions .icon-button { width: 27px; height: 27px; }
.map-entry-actions .icon-button svg { width: 14px; height: 14px; }
.map-entry input[type="checkbox"] { accent-color: var(--clay); width: 15px; height: 15px; flex: none; }

/* drop zone */
.dropzone {
  border: 1.5px dashed var(--line-strong); border-radius: var(--radius);
  padding: 18px 14px; text-align: center; color: var(--ink-3); font-size: .84rem;
  background: var(--surface-2); cursor: pointer; transition: border-color .12s, background-color .12s;
}
.dropzone:hover, .dropzone.is-over { border-color: var(--clay); background: var(--clay-soft); color: var(--clay); }
.dropzone svg { width: 21px; height: 21px; display: block; margin: 0 auto 6px; }
.dropzone b { display: block; color: var(--ink-2); font-weight: 550; }

.drop-overlay {
  position: absolute; inset: 0; z-index: 60; display: none;
  align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--paper) 78%, transparent);
  border: 3px dashed var(--clay); backdrop-filter: blur(2px);
  font-family: var(--font-serif); font-size: 1.5rem; color: var(--clay);
}
.app.is-dragging .drop-overlay { display: flex; }

/* details */
.detail-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 10px; margin-bottom: 4px; }
.detail-stat-value { font-family: var(--font-serif); font-size: 1.24rem; line-height: 1.1; }
.detail-stat-label { font-size: .69rem; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-3); margin-top: 3px; }

.profile { width: 100%; height: 116px; display: block; touch-action: none; cursor: crosshair; }
.profile-readout {
  display: flex; gap: 14px; font-size: .77rem; color: var(--ink-3);
  font-variant-numeric: tabular-nums; margin-top: 5px; min-height: 1.3em;
}
.profile-readout b { color: var(--ink); font-weight: 600; }

.feature-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; }
.feature-list li { display: flex; gap: 9px; align-items: center; padding: 7px 9px; border-radius: var(--radius-sm); cursor: pointer; }
.feature-list li:hover { background: var(--paper-2); }
.feature-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; border: 1.5px solid var(--surface); box-shadow: 0 0 0 1px var(--line-strong); }
.feature-name { font-size: .86rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.feature-meta { font-size: .75rem; color: var(--ink-3); flex: none; }

.hint { font-size: .81rem; color: var(--ink-3); margin: 0; }

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

.map-surface { position: relative; flex: 1; min-width: 0; }
#map { position: absolute; inset: 0; background: var(--paper-2); }

.map-loading {
  position: absolute; top: 12px; left: 50%; transform: translateX(-50%); z-index: 15;
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 15px; font-size: .82rem; box-shadow: var(--shadow-md);
  display: flex; align-items: center; gap: 9px;
}
.map-loading[hidden] { display: none; }
.spinner {
  width: 13px; height: 13px; border: 2px solid var(--line-strong);
  border-top-color: var(--clay); border-radius: 50%; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2s; } }

.toast-stack { position: absolute; bottom: calc(34px + var(--safe-bottom)); left: 50%; transform: translateX(-50%); z-index: 40; display: grid; gap: 8px; width: min(430px, calc(100% - 32px)); }
.toast {
  background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--clay);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  padding: 10px 14px; font-size: .86rem; display: flex; gap: 10px; align-items: flex-start;
}
.toast.is-error { border-left-color: #c0392b; }
.toast.is-ok { border-left-color: var(--pine); }
.toast button { margin-left: auto; }

/*
 * Round, and its own size, so it is not read as part of the search bar.
 *
 * Both sat at the same height in the same surface with the same shadow, a
 * dozen pixels apart, and a rounded square beside a pill reads as one
 * segmented control rather than two - reported as "the hamburger is part of
 * the search". A circle is unambiguously a button on its own.
 */
.panel-toggle {
  position: absolute; top: 12px; left: 12px; z-index: 25;
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--surface); box-shadow: var(--shadow-md); border: 1px solid var(--line);
}

/*
 * Search, on the map.
 *
 * Left of the zoom controls and right of the panel toggle, which is the only
 * horizontal room there is on a phone. It grows to fill what is left rather
 * than taking a fixed width, because at 402pt a fixed one either overlaps the
 * tools or wastes half the bar.
 */
.map-search {
  position: absolute; z-index: 24; top: 12px; left: 64px;
  width: min(360px, calc(100% - 136px));
}
.map-search-mark {
  position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--ink-3); pointer-events: none;
}
.map-search input {
  width: 100%; height: 38px; padding: 0 12px 0 34px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink);
  font: inherit; font-size: .88rem; box-shadow: var(--shadow-md);
}
.map-search input:focus-visible { outline: 2px solid var(--clay); outline-offset: 1px; }
/* The browser's own clear affordance, which otherwise sits under the rounded
   edge on WebKit and is unreachable. */
.map-search input::-webkit-search-cancel-button { margin-right: 2px; }

.map-search-results {
  margin-top: 6px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  max-height: min(52vh, 340px); overflow-y: auto;
}
.map-search-result {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 9px 12px; border: 0; border-top: 1px solid var(--line);
  background: none; color: var(--ink); font: inherit;
}
.map-search-result:first-child { border-top: 0; }
/* Your own places, marked as such: a saved waypoint and a town of the same
   name are different answers and the list has to say which is which. */
.map-search-result.is-mine { border-left: 3px solid var(--clay); }
.map-search-result.is-mine .map-search-kind { color: var(--clay); }
.map-search-result:hover, .map-search-result:focus-visible { background: var(--paper-2); }
.map-search-name { display: block; font-size: .88rem; font-weight: 600; }
.map-search-where { display: block; font-size: .75rem; color: var(--ink-3); margin-top: 1px; }
.map-search-kind {
  float: right; font-size: .62rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-3); margin-left: 8px;
}
.map-search-note { padding: 10px 12px; font-size: .8rem; color: var(--ink-3); }

/*
 * No wide-screen exception any more.
 *
 * This rule pulled the search bar back to the left edge because the toggle was
 * hidden above 820px and the space was free. Making the toggle visible at
 * every width - which closing the panel by default required - left that rule
 * standing, so the bar started at x=12 underneath a button occupying 12 to 50.
 * The hamburger was not merely near the search; it was inside it.
 *
 * The one offset in the base rule now covers both, since the toggle is on
 * screen in both.
 */

/* ---------- GL library overrides ---------- */

.maplibregl-ctrl-group, .mapboxgl-ctrl-group { border-radius: var(--radius) !important; box-shadow: var(--shadow-md) !important; }

/*
 * One column in the top-right corner, not four stacks.
 *
 * The engine adds a group per control - zoom and compass, geolocate,
 * fullscreen - and this app adds a fourth for identify, drop-a-pin and the
 * camera. Four rounded slabs with gaps between them read as four unrelated
 * things and left the app's own tools looking like something bolted on.
 *
 * Reported as "two sets of icons", and the person reporting it suggested this.
 *
 * Done in CSS over whatever the corner holds rather than by reaching into the
 * engine's control objects: the groups are siblings in a known container, and
 * joining siblings is what CSS is for. It also stays right if a control is
 * added or removed later, which reaching into internals would not.
 */
.maplibregl-ctrl-top-right .maplibregl-ctrl-group,
.mapboxgl-ctrl-top-right .mapboxgl-ctrl-group {
  margin-bottom: 0 !important;
  border-radius: 0 !important;
}
.maplibregl-ctrl-top-right .maplibregl-ctrl-group + .maplibregl-ctrl-group,
.mapboxgl-ctrl-top-right .mapboxgl-ctrl-group + .mapboxgl-ctrl-group {
  /* A hairline where two groups meet, so the column still has joints and does
     not read as one undifferentiated strip of nine buttons. */
  border-top: 1px solid var(--line) !important;
}
.maplibregl-ctrl-top-right .maplibregl-ctrl-group:first-of-type,
.mapboxgl-ctrl-top-right .mapboxgl-ctrl-group:first-of-type {
  border-radius: var(--radius) var(--radius) 0 0 !important;
}
.maplibregl-ctrl-top-right .maplibregl-ctrl-group:last-of-type,
.mapboxgl-ctrl-top-right .mapboxgl-ctrl-group:last-of-type {
  border-radius: 0 0 var(--radius) var(--radius) !important;
  margin-bottom: 10px !important;
}
/* One group in the corner is still a rounded slab, not a strip with square
   ends: first-of-type and last-of-type both match it. */
.maplibregl-ctrl-top-right .maplibregl-ctrl-group:only-of-type,
.mapboxgl-ctrl-top-right .mapboxgl-ctrl-group:only-of-type {
  border-radius: var(--radius) !important;
}
.maplibregl-popup-content, .mapboxgl-popup-content {
  background: var(--popup-bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); padding: 13px 15px; font-family: var(--font-sans); max-width: 290px;
}
/* Set in from the corner. At the default position it overlapped the title on
   any pin whose name filled the line. */
.maplibregl-popup-close-button, .mapboxgl-popup-close-button {
  color: var(--ink-3); font-size: 17px; line-height: 1;
  width: 26px; height: 26px; top: 5px; right: 5px; border-radius: var(--radius-sm);
}
.maplibregl-popup-close-button:hover, .mapboxgl-popup-close-button:hover {
  color: var(--ink); background: var(--surface-2);
}
.maplibregl-popup-tip, .mapboxgl-popup-tip { border-top-color: var(--popup-bg) !important; border-bottom-color: var(--popup-bg) !important; }
.maplibregl-ctrl-attrib, .mapboxgl-ctrl-attrib { background: color-mix(in srgb, var(--surface) 84%, transparent) !important; }

/*
 * Lift the engine's own corner controls clear of the home indicator.
 *
 * They are positioned against the bottom of the map, which in the installed
 * app is the bottom of the screen — so the Mapbox wordmark and the attribution
 * were sitting under the gesture bar with their lower half cut off. That is
 * not only untidy: displaying the logo and the attribution is a condition of
 * using the tiles at all.
 */
.maplibregl-ctrl-bottom-left, .mapboxgl-ctrl-bottom-left,
.maplibregl-ctrl-bottom-right, .mapboxgl-ctrl-bottom-right {
  padding-bottom: var(--safe-bottom);
  padding-left: var(--safe-left); padding-right: var(--safe-right);
}
.maplibregl-ctrl-attrib a, .mapboxgl-ctrl-attrib a { color: var(--ink-2) !important; }

/* The pin's own mark beside its name, so the card identifies itself the way
   the map does. The right padding is for the close button, which used to sit
   on top of the last word of the title. */
.popup-head { display: flex; align-items: flex-start; gap: 9px; padding-right: 22px; margin-bottom: 8px; }
.popup-head-text { min-width: 0; }
.popup-mark {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: #fff; color: #2A2118; border: 2.5px solid var(--pin, #2A2118); margin-top: 1px;
}
.popup-mark svg { width: 15px; height: 15px; }
.popup-title { font-family: var(--font-serif); font-size: 1.02rem; font-weight: 600; margin: 0 0 2px; line-height: 1.25; }
.popup-kind { font-size: .68rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-3); }
.popup-desc { font-size: .86rem; color: var(--ink-2); margin: 0 0 9px; max-height: 170px; overflow-y: auto; white-space: pre-line; }

/* The newest field note, trimmed to a glance. */
.popup-note {
  display: flex; gap: 7px; align-items: flex-start;
  margin: 0 0 9px; padding: 8px 9px;
  background: var(--surface-2); border-radius: var(--radius-sm);
  border-left: 2px solid var(--clay);
}
.popup-note-mark { flex: none; color: var(--ink-3); display: inline-flex; }
.popup-note-mark svg { width: 13px; height: 13px; }
.popup-note-text { font-size: .81rem; color: var(--ink-2); line-height: 1.4; }
.popup-note-date { font-size: .68rem; color: var(--ink-3); margin-top: 3px; }
.popup-stats { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: .81rem; border-top: 1px solid var(--line); padding-top: 9px; }
/*
 * A step brighter than the panel's own labels.
 *
 * A popup sits over the map, not on a panel, so it is read against terrain and
 * imagery in whatever light the reader is standing in. --ink-3 on --paper is
 * comfortable on a desktop panel and disappears on a phone held at arm's
 * length, which is where this card is actually used.
 */
.popup-stats dt { color: var(--ink-2); }
.popup-stats dd { margin: 0; color: var(--ink); font-variant-numeric: tabular-nums; }
.mapboxgl-popup-content .icon-button,
.maplibregl-popup-content .icon-button { color: var(--ink-2); }
.mapboxgl-popup-content .icon-button:hover,
.maplibregl-popup-content .icon-button:hover { color: var(--ink); background: var(--popup-control); }

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

@media (max-width: 820px) {
  .panel {
    position: absolute; inset: 0 auto 0 0; width: min(88vw, 336px);
    box-shadow: var(--shadow-lg);
    transform: translateX(0); transition: transform .18s ease;
  }
  /* [hidden] is overridden above so the panel can slide rather than vanish.
     That leaves it rendered, so visibility:hidden is what actually removes it
     from hit-testing, the tab order and the accessibility tree — without it a
     closed panel still catches taps and keyboard focus off-screen. It is
     delayed so the slide-out animation still plays. */
  .panel[hidden] {
    display: flex; transform: translateX(-102%); visibility: hidden;
    transition: transform .18s ease, visibility 0s .18s;
  }
  .panel-toggle { display: inline-flex; }
}
/*
 * On a wide screen the toggle is what opens a panel that now starts closed.
 *
 * It used to be hidden above 820px because the panel was always open there and
 * the button would have done nothing but close it. With the panel closed until
 * asked for, hiding this leaves a desktop with no way to open it at all - so
 * it shows whenever the panel is shut and gets out of the way once it is open,
 * which is the same rule the narrow layout already used.
 */
@media (min-width: 821px) {
  .app.is-panel-open .panel-toggle { display: none; }
}

/* ==========================================================================
   Folders — the user's saved waypoints and tracks
   ========================================================================== */

.folder-actions { display: flex; gap: 7px; margin-bottom: 13px; flex-wrap: wrap; }
/* New folder and New trip share a line; Import has the next to itself. Three
   on one line clipped "New folder" at the panel's width. */
#tab-folders > .panel-section > .folder-actions .button { flex: 1 1 calc(50% - 4px); }
#tab-folders > .panel-section > .folder-actions #import-into-folder { flex: 1 0 100%; }

/* A pin's link, under its note rather than beside its name: the note is what
   you read, and this is what you do next. */
.popup-link-row { margin: 8px 0 0; }
.popup-link,
.popup-desc a,
.pin-description a { color: var(--accent, #b4441f); }
.popup-link {
  display: inline-block;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  /* A tap target, not a word in a sentence. */
  padding: 4px 0;
}

/* One labelled button where four sixteen-pixel icons used to sit.
   Four targets inside a thumb's width, with delete beside export, is not a
   toolbar anyone should be asked to hit on a phone — the actions moved into
   the panel it opens, where they can be words. */
/* A mark, not a word: the name gets the row. */
.folder-head .folder-menu-button {
  width: 28px; height: 28px; flex: none; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface-2); color: var(--ink-2);
}
.folder-head .folder-menu-button svg { width: 14px; height: 14px; }
.folder-menu-button:hover { border-color: var(--line-strong); color: var(--ink); }
.folder-menu-button.is-armed { border-color: var(--clay); color: var(--clay); }

/* The folder's own actions, below Apply and Cancel with a rule between them:
   one row changes how pins look, the other can remove the folder. */
/* Three buttons that fit the panel, whatever it is wide.
   `1fr` alone is not enough — a grid track's minimum is its content, so
   nowrapped labels pushed the row past the panel's right edge instead of
   shrinking. minmax(0, 1fr) lets them give. */
.editor-folder-actions {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px;
  margin-top: 11px; padding-top: 11px; border-top: 1px solid var(--line);
}
/* A mark and a word. Used wherever three buttons have to share a row that is
   narrower than three words — which on a phone is most rows. */
.button-with-icon,
.editor-folder-actions .button {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  min-width: 0; white-space: nowrap;
}
.editor-folder-actions { flex-wrap: wrap; }
.editor-folder-actions .button { min-height: 36px; padding: 6px 8px; }
.button-with-icon svg,
.editor-folder-actions .button svg { width: 14px; height: 14px; flex: none; }
.button-with-icon span,
.editor-folder-actions .button span {
  overflow: hidden; text-overflow: ellipsis;
}
.button.is-danger { color: var(--rust, #a33); }
.button.is-danger:hover { border-color: var(--rust, #a33); background: var(--rust-soft, rgba(170, 51, 51, .08)); }

/* Every tap target in the folder tree gets a thumb's worth of height. The
   per-pin edit and remove icons are the same complaint as the folder toolbar,
   one level down. */
@media (max-width: 820px) {
  .folder-menu-button { min-height: 40px; padding: 8px 13px; }
  .picker-row .button { min-height: 40px; }
  .folder-item .icon-button,
  .waypoint-card .icon-button { min-width: 38px; min-height: 38px; }
  .editor-folder-actions .button { min-height: 40px; }
}

.folder {
  border: 1px solid var(--line); border-radius: var(--radius);
  margin-bottom: 8px; overflow: hidden; background: var(--surface);
}
.folder.is-drop-target { border-color: var(--clay); box-shadow: 0 0 0 2px var(--clay-soft); }

/* A folder filed inside another sits inside its parent's body, above the
   parent's own pins, stepped in and tied to it by a rule down the left. The
   containment carries the depth, so the step is one fixed amount rather than
   a multiple of it: a branch of three would otherwise be indented six times
   and the innermost folder would have no width left for its name. */
.folder.is-nested {
  margin: 7px 7px 7px 12px;
  border-left-width: 3px;
  border-left-color: var(--line-strong);
}
/* A folder holding only other folders has no pins to rule off from its head. */
.folder-body.is-drawer { border-top: 0; }
/* Something that is set to show but cannot, because a folder above it is shut.
   The eye still reports its own switch; this says the switch is not the one
   deciding. */
.folder-eye.is-dimmed { opacity: .45; }

.folder-head { display: flex; align-items: center; gap: 8px; padding: 8px 9px; background: var(--surface-2); }
.folder-head:hover { background: var(--paper-2); }
.folder-disclosure {
  width: 20px; height: 20px; flex: none; padding: 0; border: 0; background: none;
  color: var(--ink-3); cursor: pointer; display: flex; align-items: center; justify-content: center;
  position: relative;
}
/* The tappable area, which is not the drawn one.
   A 20px glyph is about three and a half millimetres on a phone, well under
   the nine a thumb needs, while the folder's name beside it is ten times the
   width - which is why the name folded a folder every time and the chevron
   did not. The mark stays 13px; the target grows into the row's own padding
   and stops short of the name, where there is nothing else to hit. */
.folder-disclosure::after {
  content: ''; position: absolute; top: -12px; bottom: -12px; left: -10px; right: -5px;
}
.folder-disclosure svg { width: 13px; height: 13px; transition: transform .12s ease; }
.folder.is-collapsed .folder-disclosure svg { transform: rotate(-90deg); }

/* A label that wraps, not a field that clips. */
.folder-name {
  flex: 1; min-width: 0; font-size: .89rem; font-weight: 550; line-height: 1.3; text-align: left;
  white-space: normal; overflow-wrap: anywhere;
  background: none; border: 0; color: var(--ink); font-family: inherit; padding: 2px 0; margin: 0;
  cursor: pointer;
}
.folder-name:hover { color: var(--clay-2); }
.folder-rename {
  width: 100%; font: inherit; font-size: .88rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); padding: 7px 9px;
}
.editor-folder-name { display: block; }
.editor-folder-name .style-label { display: block; margin-bottom: 4px; }

.folder-count { font-size: .74rem; color: var(--ink-3); flex: none; font-variant-numeric: tabular-nums; }
.folder-head .icon-button { width: 23px; height: 23px; flex: none; }
.folder-head .icon-button svg { width: 13px; height: 13px; }
.folder-head-actions { display: flex; gap: 0; flex: none; margin-left: 2px; }
.folder-head input[type="checkbox"] { accent-color: var(--clay); width: 14px; height: 14px; flex: none; }

.folder-body { border-top: 1px solid var(--line); }
.folder.is-collapsed .folder-body { display: none; }

.folder-item {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 9px 6px 12px; border-bottom: 1px solid var(--line);
  cursor: grab; font-size: .85rem;
}
.folder-item:last-child { border-bottom: 0; }
.folder-item:hover { background: var(--paper-2); }
.folder-item.is-dragging { opacity: .45; }
.folder-item-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Never wider than a third of the row: the name is what the row is for. */
.folder-item-meta {
  font-size: .73rem; color: var(--ink-3); flex: 0 1 auto; max-width: 34%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.folder-item .icon-button { width: 23px; height: 23px; flex: none; }
.folder-item .icon-button svg { width: 12px; height: 12px; }
.folder-item-kind { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.folder-item-kind.is-track { border-radius: 1px; width: 10px; height: 3px; }

.folder-empty { padding: 11px 12px; font-size: .8rem; color: var(--ink-3); }

/* Inline picker used by "Add from an open file…" and "Move to…" */
.picker { border: 1px solid var(--line); border-radius: var(--radius); padding: 12px; margin-bottom: 12px; background: var(--surface-2); }
.picker h3 { font-family: var(--font-sans); font-size: .78rem; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-3); margin: 0 0 9px; }
.picker select, .picker input[type="text"] {
  width: 100%; font: inherit; font-size: .87rem; color: var(--ink); margin-bottom: 8px;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-sm); padding: 7px 9px;
}
.picker-row { display: flex; gap: 7px; }
/* Apply and Cancel were sitting flush against the icon grid above them, which
   read as part of it. They are the end of the form, not another row of it. */
.style-editor .picker-row { margin-top: 13px; }
.picker-note { font-size: .76rem; color: var(--ink-3); margin: 0 0 9px; }

/* ---------- floating map controls ---------- */


/*
 * On a phone the floating Layers and Folders buttons are pure duplication:
 * both do nothing but openTab(), which is what the hamburger and the panel's
 * own tabs already do — and they cost a corner of a screen that has none to
 * spare. Kept on wider screens, where the panel is docked and a shortcut to a
 * tab is worth its space.
 */
@media (max-width: 820px) {
  }

/* Buttons inside popups, e.g. "Save to folder". A mark plus a short word, so
   three of them fit one row of a 290px card instead of wrapping onto two. */
.popup-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 11px; align-items: center; }
.popup-actions button, .popup-actions select {
  font: inherit; font-size: .78rem; padding: 7px 9px; cursor: pointer;
  border-radius: var(--radius-sm); border: 1px solid var(--line-strong);
  background: var(--popup-control); color: var(--ink);
}
.popup-actions button:hover { background: var(--paper-3); border-color: var(--ink-3); }
/* The one action that commits something, given the weight the rest are not. */
.popup-actions button.is-primary {
  background: var(--clay); border-color: var(--clay); color: #fff; font-weight: 600;
}
.popup-actions button.is-primary:hover { background: var(--clay-2); border-color: var(--clay-2); }

/* Its own row: a folder name needs the full width, and it only appears at all
   when "New folder" is the selection. */
/* Saving asks nothing until it is asked for: one button, and the folder choice
   only after it is pressed. */
.popup-save { display: grid; gap: 6px; }
.popup-save > button { width: 100%; }
.popup-save-open { justify-content: center; }
.popup-save-open[hidden] { display: none; }
.popup-save-panel { display: grid; gap: 5px; }
.popup-save-panel[hidden] { display: none; }
.popup-save-panel select { width: 100%; }
.popup-save-confirm { display: grid; grid-template-columns: 1fr auto; gap: 6px; margin-top: 2px; }

/* Small caps above each field, so neither the folder nor its name is a guess.
   The same treatment the panel's own sections use. */
.popup-label {
  font-size: .64rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3);
}
.popup-label[hidden] { display: none; }

.popup-new-folder {
  font: inherit; font-size: .8rem; padding: 7px 8px;
  border-radius: var(--radius-sm); border: 1px solid var(--line-strong);
  background: var(--popup-control); color: var(--ink);
}
.popup-new-folder[hidden] { display: none; }
.popup-new-folder:focus { outline: none; border-color: var(--clay); }

/* Details and Close, side by side at the foot of the card. */
.popup-bar {
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px;
  margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--line);
}
.popup-bar .button { width: 100%; justify-content: center; }
.popup-actions .button-with-icon { gap: 5px; }
.popup-actions .button-with-icon svg { width: 13px; height: 13px; }

/* ==========================================================================
   Pin styling — colour and icon, single or bulk
   ========================================================================== */

.folder-item.is-selected { background: var(--clay-soft); }
.folder-item-pick { accent-color: var(--clay); width: 14px; height: 14px; flex: none; margin: 0; }

/* The pin preview mirrors what is drawn on the map: glyph on a coloured disc. */
/* The symbol in ink on white, the colour as the ring - as on the map. Fixed
   colours on purpose: the disc is white in the dark theme too, as it is on
   the map, so the glyph is fixed ink rather than the theme's. */
.folder-item-icon {
  width: 20px; height: 20px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: #fff; color: #2A2118; border: 2px solid var(--pin, #2A2118);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .12);
}
.folder-item-icon svg { display: block; }

.icon-button.is-armed { color: var(--clay); background: var(--clay-soft); }

.style-editor {
  border: 1px solid var(--line-strong); border-top: 0;
  background: var(--surface-2); padding: 12px 12px 13px;
}
.style-editor-title {
  font-family: var(--font-sans); font-size: .78rem; font-weight: 650;
  letter-spacing: .05em; text-transform: uppercase; color: var(--ink-3); margin: 0 0 10px;
}
.style-label {
  font-size: .72rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); margin: 12px 0 6px;
}
.style-name {
  width: 100%; font: inherit; font-size: .88rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); padding: 7px 9px;
}

.swatch-row { display: flex; flex-wrap: wrap; gap: 6px; }
.swatch {
  width: 24px; height: 24px; border-radius: 50%; padding: 0; cursor: pointer;
  background: var(--swatch); border: 2px solid transparent;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .16);
}
.swatch:hover { transform: scale(1.08); }
.swatch.is-chosen { border-color: var(--ink); box-shadow: inset 0 0 0 2px var(--surface); }
/* "Inherit" reads as the folder colour, dashed to say it is not a choice of its own. */
.swatch.is-inherit { box-shadow: inset 0 0 0 2px var(--surface); border-style: dashed; border-color: var(--ink-3); }

.icon-search {
  width: 100%; font: inherit; font-size: .84rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); padding: 7px 9px; margin-bottom: 6px;
}
.icon-picker {
  max-height: 244px; overflow-y: auto; overscroll-behavior: contain;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); padding: 8px;
}
.icon-group-label {
  font-size: .68rem; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-3); margin: 9px 0 5px 2px;
}
.icon-group-label:first-of-type { margin-top: 2px; }
.icon-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(34px, 1fr)); gap: 4px; }
.icon-choice {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink-2);
}
.icon-choice:hover { border-color: var(--line-strong); background: var(--paper-2); color: var(--ink); }
.icon-choice.is-chosen { background: var(--clay); border-color: var(--clay); color: #fff; }
.icon-choice.is-inherit { border-style: dashed; margin-bottom: 4px; }

/* ---------- layer availability ---------- */

.layer-option-name { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.layer-badge {
  font-size: .62rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  padding: 1px 6px; border-radius: 999px; white-space: nowrap;
  background: var(--paper-3); color: var(--ink-3); border: 1px solid var(--line);
}
.layer-badge.is-broken {
  background: color-mix(in srgb, #c0392b 14%, transparent);
  color: #c0392b; border-color: color-mix(in srgb, #c0392b 34%, transparent);
}
:root[data-theme="dark"] .layer-badge.is-broken,
:root:not([data-theme="light"]) .layer-badge.is-broken { color: #f0806f; }

/* Waiting on zoom, not failing. Amber rather than red: the layer is fine and
   the view is the thing that has to change. */
.layer-badge.is-waiting {
  background: color-mix(in srgb, #b7791f 14%, transparent);
  color: #8a5d12; border-color: color-mix(in srgb, #b7791f 34%, transparent);
}
:root[data-theme="dark"] .layer-badge.is-waiting,
:root:not([data-theme="light"]) .layer-badge.is-waiting { color: #e3b23c; }

/* ==========================================================================
   Compact layer rows — descriptions fold away behind (i)
   ========================================================================== */

/*
 * One column, tried at two and reverted.
 *
 * Two columns did halve the height and cost more than they saved: a layer name
 * carrying its state, a description that folds open, an opacity slider and a
 * colour key are a tall and ragged cell, and two of those side by side read as
 * a jumble rather than a list. The height was never really the problem - the
 * scanning was, and columns made that worse.
 *
 * `.layer-item` stays. Grouping a row with its description and slider is right
 * regardless of how they are laid out, and it is what stopped a description
 * opening under someone else's layer.
 */
.layer-item { min-width: 0; }

.layer-row {
  display: flex; align-items: center; gap: 4px;
  border-radius: var(--radius); border: 1px solid transparent;
}
.layer-row:hover { background: var(--paper-2); }
.layer-row.is-selected { background: var(--clay-soft); border-color: color-mix(in srgb, var(--clay) 28%, transparent); }

.layer-row .layer-option {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px;
  padding: 9px; margin: 0; border: 0; background: none; cursor: pointer; border-radius: var(--radius);
}
.layer-row .layer-option:hover { background: none; }
.layer-option-name { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; min-width: 0; }
.layer-option-label { font-size: .92rem; font-weight: 500; }

.layer-info {
  flex: none; width: 28px; height: 28px; margin-right: 5px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 50%;
  color: var(--ink-3); cursor: pointer;
}
.layer-info svg { width: 15px; height: 15px; }
.layer-info:hover { background: var(--paper-3); color: var(--ink); }
.layer-info.is-open { background: var(--clay-soft); color: var(--clay); }

.layer-desc {
  margin: -2px 9px 8px 38px; padding: 8px 11px;
  font-size: .81rem; line-height: 1.45; color: var(--ink-2);
  background: var(--surface-2); border-left: 2px solid var(--line-strong);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

/* ==========================================================================
   Mobile: the panel is an overlay, so it must get out of the way
   ========================================================================== */

.panel-close { display: none; }

@media (max-width: 820px) {
  /* The floating buttons and the hamburger both sit under the open panel —
     the hamburger was landing on top of the "Layers" tab label. The panel has
     its own close button, so neither is needed while it is open. */
  .app.is-panel-open .panel-toggle { display: none; }
  /* The panel covers the map at this width, so a search bar floating over it
     is a control for something nobody can see. */
  .app.is-panel-open .map-search { display: none; }

  .panel { width: min(92vw, 380px); box-shadow: var(--shadow-lg); }

  .panel-close {
    position: absolute; top: 6px; right: 6px; z-index: 3;
    display: flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; padding: 0;
    background: var(--surface); border: 1px solid var(--line);
    border-radius: 50%; color: var(--ink-2); cursor: pointer;
  }
  .panel-close svg { width: 16px; height: 16px; }
  .panel-tabs { padding-right: 44px; }
  .panel-tab { font-size: .74rem; padding: 11px 2px 9px; }

  /* Touch targets, and room for the tab bar of the phone browser. */
  .layer-row .layer-option { padding: 12px 9px; }
  .layer-info { width: 34px; height: 34px; }
  .panel-scroll { padding-bottom: 28px; }

  /* Header: the brand was wrapping onto two lines and pushing the controls. */
  .site-header .wrap { gap: 8px; padding: 0 10px; }
  .brand-mark { width: 26px; height: 26px; }
  .brand-name { font-size: .9rem; white-space: nowrap; }
  .brand-parent { display: none; }
  .nav { gap: 0; }
  .nav a { padding: 6px 7px; font-size: .82rem; }
  #units-toggle { padding: 6px 8px; font-size: .78rem; white-space: nowrap; }
  .site-header .icon-button { width: 30px; height: 30px; }
  .site-header .icon-button svg { width: 16px; height: 16px; }
}

/* Very narrow: drop the wordmark to the icon alone and shed optional buttons. */
/*
 * The name, on the screens the header cannot fit it on.
 *
 * Measured at 402px: the header's visible children already come to 323px with
 * 120px of gaps inside a 370px content box, and the title wants 143 more. So
 * below this breakpoint the header shows the mark alone and the panel carries
 * the name — opening the menu is where you see what this is.
 */
.panel-brand {
  display: none; align-items: center; gap: 8px;
  margin: 0; padding: 12px 40px 10px 16px;
  font-family: var(--font-serif); font-size: 1rem; font-weight: 600;
  color: var(--ink); border-bottom: 1px solid var(--line);
}
.panel-brand-mark { width: 22px; height: 22px; flex: none; border-radius: 5px; }

/*
 * The title goes before the header is forced to squeeze.
 *
 * 430px was one pixel too tight for the current crop of large phones: a 440pt
 * screen kept the title, the header then needed 582px, and it dragged the map
 * off the side of the screen with it. The panel carries the name below this
 * width, so nothing is lost by dropping it earlier.
 */
@media (max-width: 560px) {
  .brand-text { display: none; }
  #fit-button { display: none; }
  .panel-brand { display: flex; }
}

/*
 * A phone in portrait: the panel is the whole screen.
 *
 * At 92vw it left an eight-per-cent sliver of map down one side — too narrow
 * to read anything off, wide enough to catch a thumb and dismiss the panel by
 * accident, and it cost every list in the panel that much width. The panel is
 * already an overlay at this size with its own close button, so there is
 * nothing the sliver was doing.
 */
@media (max-width: 560px) {
  .panel { width: 100%; }
}

/*
 * A phone in landscape: 390 pixels of height, and 60 of them were header.
 *
 * The panel cannot be full width here — that is the whole screen and the map
 * with it — so it stays a column, and everything vertical gets thinner:
 * a shorter header, tighter tab bars, less padding per row. Keyed to the
 * height rather than the orientation, because a short window on a laptop has
 * exactly the same problem and no orientation at all.
 */
@media (max-height: 500px) and (min-width: 480px) {
  .app { grid-template-rows: calc(var(--short-header) + var(--safe-top)) 1fr; }
  .site-header { height: calc(var(--short-header) + var(--safe-top)); }
  .site-header .wrap { padding: 0 10px; }
  .brand-parent { display: none; }
  .site-header .icon-button { width: 28px; height: 28px; }
  .site-header .icon-button svg { width: 15px; height: 15px; }

  .panel { width: min(46vw, 330px); }
  .panel-tab { padding: 7px 2px 6px; font-size: .72rem; }
  .panel-scroll { padding-top: 8px; }
  .panel-section { margin-bottom: 12px; }
  .layer-row .layer-option { padding: 7px 9px; }

  /* The map keeps its controls off the bottom edge, where a landscape phone
     puts the home indicator across them. */
    .map-search { top: 8px; }
}

/* Destination picker beside the drop zone */
.drop-target-label {
  display: block; margin: 12px 0 5px;
  font-size: .72rem; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3);
}
.drop-target {
  width: 100%; font: inherit; font-size: .86rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); padding: 8px 9px;
}

/*
 * A trip's dates, under its folder name.
 *
 * Three things in order of how often they are read: when it is in words you do
 * not have to do arithmetic on, the dates themselves, and — once it is over —
 * what to do about the pins. The left edge carries the standing as colour, so
 * a glance down a list of folders says which one is live.
 */
.trip-bar {
  display: grid; gap: 7px;
  margin: 0 0 2px; padding: 8px 12px 10px 13px;
  border-left: 3px solid var(--line-strong);
  background: var(--paper-2);
}
.trip-bar.is-on { border-left-color: var(--pine); background: var(--pine-soft); }
.trip-bar.is-ahead { border-left-color: var(--clay); }
.trip-bar.is-over { border-left-color: var(--ink-3); opacity: .9; }

.trip-line { display: flex; align-items: center; gap: 7px; min-width: 0; }
.trip-mark { flex: none; display: inline-flex; color: var(--ink-3); }
.trip-mark svg { width: 14px; height: 14px; }
.trip-when { font-size: .78rem; font-weight: 600; color: var(--ink-2); white-space: nowrap; }
.trip-standing {
  margin-left: auto; font-size: .7rem; letter-spacing: .03em;
  color: var(--ink-3); text-align: right;
}
.trip-bar.is-on .trip-standing { color: var(--pine); font-weight: 600; }

.trip-dates { display: flex; align-items: center; gap: 6px; }
.trip-date {
  flex: 1; min-width: 0; font: inherit; font-size: .76rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); padding: 5px 7px;
}
.trip-dash { color: var(--ink-3); font-size: .78rem; }
.trip-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/*
 * What the queue actually costs.
 *
 * Collapsed by default because the trip strip above it is the glance, and this
 * is the answer you go looking for once the glance has worried you. The
 * headline sits in the summary so the glance carries the number anyway.
 */
.trip-plan { border-top: 1px solid var(--line); padding-top: 8px; }
.trip-plan-summary {
  display: flex; align-items: baseline; gap: 8px; cursor: pointer; list-style: none;
}
.trip-plan-summary::-webkit-details-marker { display: none; }
.trip-plan-summary::before {
  content: '▸'; color: var(--ink-3); font-size: .7rem; transition: transform .15s;
}
.trip-plan[open] > .trip-plan-summary::before { transform: rotate(90deg); }
.trip-plan-title {
  font-size: .64rem; font-weight: 650; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3);
}
.trip-plan-headline {
  margin-left: auto; font-size: .76rem; font-weight: 600; color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

/* The one line the whole feature exists for: does this fit the days you have? */
.trip-verdict {
  margin: 9px 0 0; padding: 7px 10px; border-radius: var(--radius-sm);
  font-size: .8rem; line-height: 1.4;
  border-left: 3px solid var(--line-strong); background: var(--surface); color: var(--ink-2);
}
.trip-verdict.is-fits { border-left-color: var(--pine); }
.trip-verdict.is-over { border-left-color: var(--clay); color: var(--ink); font-weight: 550; }

/* The road route: the button that asks for one, and what comes back. */
.trip-route-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
/* Handing a place to a navigation app: a label and three links, wrapping on a
   phone rather than pushing the panel wide. */
.directions-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }
.directions-label {
  font-size: .7rem; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-3); margin-right: 2px;
}
.identify-card .directions-row { margin-top: 11px; }
.directions-row .button { text-decoration: none; }
.trip-measured { margin: 8px 0 0; font-size: .86rem; font-weight: 550; color: var(--ink); }
/* Green, and matching the dashed line on the map, because the note and the
   geometry are the same statement made twice. */
.trip-walk-note {
  margin: 6px 0 0; padding-left: 9px; font-size: .82rem; color: var(--ink-2);
  border-left: 2px dashed #b58200;
}

.trip-days { list-style: none; margin: 9px 0 0; padding: 0; display: grid; gap: 7px; }
.trip-day {
  padding: 8px 10px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface);
}
.trip-day.is-long { border-color: var(--clay); }
.trip-day-head { display: flex; align-items: baseline; gap: 8px; }
.trip-day-name { font-size: .78rem; font-weight: 650; color: var(--ink); }
.trip-day-cost {
  margin-left: auto; font-size: .74rem; color: var(--ink-2);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.trip-day-route { margin-top: 3px; font-size: .76rem; color: var(--ink-2); line-height: 1.35; }
/* The line that answers "I forget to add time to eat, refuel and sleep". */
.trip-day-total { margin-top: 4px; font-size: .72rem; color: var(--ink-3); line-height: 1.4; }
.trip-day-warn { margin-top: 4px; font-size: .72rem; color: var(--clay); font-weight: 550; }
.trip-day-night { margin-top: 3px; font-size: .72rem; color: var(--ink-3); font-style: italic; }

.trip-queue { margin-top: 11px; }
.trip-queue-title {
  margin: 0 0 5px; font-size: .64rem; font-weight: 650; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3);
}
.trip-stops { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.trip-stop {
  display: flex; align-items: center; gap: 7px; min-width: 0;
  padding: 4px 2px; border-bottom: 1px solid var(--line);
}
.trip-stop:last-child { border-bottom: 0; }
.trip-stop-number {
  flex: none; width: 19px; height: 19px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--paper-3); color: var(--ink-2);
  font-size: .64rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.trip-stop-name {
  flex: 1; min-width: 0; font-size: .78rem; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The gap to the NEXT stop, on the row of the one you leave — which is the way
   round you read it when you are deciding what to cut. */
.trip-stop-gap {
  flex: none; font-size: .7rem; color: var(--ink-3);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.trip-stop-gap.is-last { font-style: italic; }
.trip-stop-moves { flex: none; display: flex; gap: 1px; }
.trip-move { width: 26px; height: 26px; }
.trip-move svg { width: 14px; height: 14px; }
.trip-move[disabled] { opacity: .3; cursor: default; }
.trip-optimise { width: 100%; margin-top: 8px; }

.trip-knobs { margin-top: 10px; }
.trip-knobs-summary {
  cursor: pointer; font-size: .7rem; letter-spacing: .04em; color: var(--ink-3);
}
.trip-knob-grid { display: grid; gap: 8px; margin-top: 8px; }
.trip-knob { display: grid; grid-template-columns: 1fr 74px; gap: 4px 8px; align-items: center; }
.trip-knob-label { font-size: .76rem; color: var(--ink-2); }
.trip-knob-input {
  font: inherit; font-size: .78rem; color: var(--ink); text-align: right;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); padding: 4px 6px;
}
.trip-knob-note { grid-column: 1 / -1; font-size: .68rem; color: var(--ink-3); }

/* Saved pin notes, under the row */
.style-desc {
  width: 100%; font: inherit; font-size: .86rem; line-height: 1.45; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); padding: 8px 9px; margin-top: 7px; resize: vertical;
}

/* Photos on a saved pin */
.photo-strip { display: flex; flex-wrap: wrap; gap: 7px; }
.photo-tile {
  position: relative; width: 68px; height: 68px; flex: none;
  border-radius: var(--radius-sm); overflow: hidden;
  border: 1px solid var(--line); background: var(--paper-2);
}
.photo-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-missing {
  display: flex; align-items: center; justify-content: center; height: 100%;
  font-size: .68rem; color: var(--ink-3);
}
.photo-remove {
  position: absolute; top: 2px; right: 2px;
  width: 20px; height: 20px; padding: 0; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; cursor: pointer; color: #fff;
  background: rgba(0, 0, 0, .55); border: 0; border-radius: 50%;
}
.photo-remove:hover { background: rgba(0, 0, 0, .8); }

/* Account / sync */
#account-panel input {
  width: 100%; font: inherit; font-size: .88rem; color: var(--ink); margin-bottom: 7px;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); padding: 8px 9px;
}
.account-actions { display: flex; flex-wrap: wrap; gap: 6px; }
/*
 * One thing per line. Nothing here is nowrap: an address that does not fit
 * wraps rather than ending in "sherm...", because a menu has the height a
 * header never did.
 */
.account-who { min-width: 0; }
.account-name { font-size: .92rem; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.account-email { font-size: .8rem; color: var(--ink-2); overflow-wrap: anywhere; }
.account-edit { margin: 6px 0 8px; padding-left: 0; }
.account-meta { font-size: .76rem; color: var(--ink-3); margin-bottom: 8px; }
.account-actions .button-with-icon { gap: 5px; }
.account-actions .button-with-icon svg { width: 14px; height: 14px; }
.account-form { margin-top: 8px; }

/* ==========================================================================
   Waypoint index and pin details
   ========================================================================== */

.pin-head { display: flex; align-items: flex-start; gap: 11px; }
.pin-head-icon {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: #fff; color: #2A2118; border: 3px solid var(--pin, #2A2118);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .12);
}
.pin-description {
  margin: 11px 0 0; font-size: .87rem; line-height: 1.5; color: var(--ink-2);
  white-space: pre-line;
}

/* Label / value / copy, in a grid so the values line up down the panel. */
.detail-line {
  display: grid; grid-template-columns: 78px 1fr auto; gap: 8px;
  align-items: center; padding: 6px 0; border-bottom: 1px solid var(--line);
}
.detail-line:last-child { border-bottom: 0; }
.detail-line-label { font-size: .74rem; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); }
.detail-line-value {
  font-size: .86rem; font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere; line-height: 1.35;
}
.detail-copy { width: 26px; height: 26px; }
.detail-copy svg { width: 13px; height: 13px; }
.detail-copy.is-done { color: var(--pine); background: var(--pine-soft); }

/* The extra coordinate formats. Closed by default so the panel opens on the
   one number nearly everyone wants, and kept flush with the rows above it so
   opening it reads as the same list continuing. */
.coord-more { margin-top: 2px; }
.coord-more-summary {
  list-style: none; cursor: pointer; padding: 7px 0;
  font-size: .74rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-3); display: flex; align-items: center; gap: 6px;
}
.coord-more-summary::-webkit-details-marker { display: none; }
.coord-more-summary::after {
  content: ''; width: 5px; height: 5px; margin-top: -2px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg); transition: transform .12s ease;
}
.coord-more[open] > .coord-more-summary::after { transform: rotate(-135deg); margin-top: 2px; }
.coord-more-summary:hover { color: var(--ink-1); }
.coord-more-body { border-top: 1px solid var(--line); }
.coord-more-body .button { margin-top: 9px; }

@media (max-width: 820px) {
  .detail-line { grid-template-columns: 72px 1fr auto; }
  .waypoint-card { padding: 12px 9px; }
}

/* ==========================================================================
   Weather, land manager and field notes
   ========================================================================== */

.source-note { margin: 9px 0 0; font-size: .72rem; color: var(--ink-3); }

/*
 * A service's own legend graphic.
 *
 * Always black on transparent, so it needs a white bed of its own to stay
 * readable in the dark theme. Sized by height rather than width, and allowed to
 * run off the side: the temperature key comes back 1181 pixels wide, and
 * squeezing that into a 320px panel would put the type back to six pixels —
 * which is the problem the horizontal layout was asked for in the first place.
 * A long ramp is worth a swipe; an illegible one is worth nothing.
 */
.legend-image {
  display: block; height: 40px; width: auto; max-width: none; margin: 0;
}
.legend-image-wrap {
  margin: 9px 0 0; padding: 5px 6px; overflow-x: auto; overscroll-behavior-x: contain;
  background: #fff; border-radius: 6px; border: 1px solid var(--line);
}

/* Headline card: condition sets the wash behind it, so a glance reads the
   weather before any of the text does. */
.weather-now {
  position: relative; isolation: isolate;
  display: flex; align-items: center; gap: 13px;
  padding: 13px 14px; border-radius: var(--radius);
  border: 1px solid var(--line);
}
/*
 * The sky, on its own layer underneath the text.
 *
 * It used to be the card's own background, and dark mode dimmed the card with
 * `filter: brightness(.62)` to stop the daytime wash glowing. A filter applies
 * to everything inside the element, so it dimmed the temperature and the
 * conditions along with the sky — and a second rule then replaced the gradient
 * with a flat surface, leaving a filter whose only remaining job was making
 * the words hard to read. Which is exactly what it was doing.
 */
.weather-now::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background: linear-gradient(135deg, var(--wash-a, var(--paper-2)), var(--wash-b, var(--surface-2)));
}
/* The wash carries the forecast before you read a word of it, so these are
   proper skies rather than tinted greys: a clear day is warm and bright, an
   overcast one is flat and cool, a storm is dark. The glyph colour moves with
   the wash — a yellow sun on a blue sky, not a grey sun on everything. */
.weather-now.is-clear {
  --wash-a: #8ec9f0; --wash-b: #ffe9b0; --glyph: #f0a500; --wash-ink: #24405a;
}
.weather-now.is-partly {
  --wash-a: #9cc9e8; --wash-b: #eef1f4; --glyph: #e8a52c; --wash-ink: #2b4155;
}
.weather-now.is-cloud {
  --wash-a: #c3ccd6; --wash-b: #e4e8ec; --glyph: #6d7c8a; --wash-ink: #2f3944;
}
.weather-now.is-rain {
  --wash-a: #7d9cb8; --wash-b: #cfdbe6; --glyph: #2f6f9f; --wash-ink: #1f3446;
}
.weather-now.is-thunder {
  --wash-a: #55607f; --wash-b: #9aa3bd; --glyph: #f2c744; --wash-ink: #ffffff;
}
.weather-now.is-snow {
  --wash-a: #d8e6f2; --wash-b: #fbfdff; --glyph: #5f8bb0; --wash-ink: #2a3b4a;
}
.weather-now.is-fog {
  --wash-a: #cfd3d6; --wash-b: #e9ebec; --glyph: #7b858c; --wash-ink: #333b41;
}
.weather-now.is-wind {
  --wash-a: #a8c8bd; --wash-b: #e2ece7; --glyph: #3f7d68; --wash-ink: #23443a;
}

/* The card takes its text colour from the wash, so a dark storm sky keeps its
   numbers readable instead of printing near-black on near-black. */
.weather-now .weather-temp,
.weather-now .weather-when { color: var(--wash-ink, var(--ink)); }
.weather-now .weather-short { color: var(--wash-ink, var(--ink-2)); opacity: .85; }
.weather-now .weather-glyph { color: var(--glyph, var(--ink-2)); }

/* Dark theme: keep the hue, drop the brightness. The sky should still read as
   a sky at night, just not glow. */
/* Dark theme: dim the sky, not the forecast printed on it. The flat
   `background: var(--surface-2)` that used to sit here is gone with it — it
   threw the wash away and left the filter with nothing to do but grey out the
   text. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .weather-now::before { filter: saturate(.82) brightness(.62); }
  :root:not([data-theme="light"]) .weather-now .weather-temp,
  :root:not([data-theme="light"]) .weather-now .weather-when { color: #f7f3ec; }
  :root:not([data-theme="light"]) .weather-now .weather-short { color: #f7f3ec; opacity: .92; }
}
:root[data-theme="dark"] .weather-now::before { filter: saturate(.82) brightness(.62); }
:root[data-theme="dark"] .weather-now .weather-temp,
:root[data-theme="dark"] .weather-now .weather-when { color: #f7f3ec; }
:root[data-theme="dark"] .weather-now .weather-short { color: #f7f3ec; opacity: .92; }

.weather-glyph { flex: none; width: 42px; height: 42px; color: var(--ink-2); }
.weather-glyph svg { width: 100%; height: 100%; }
.weather-now-text { min-width: 0; }
.weather-when {
  font-size: .7rem; font-weight: 650; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-3);
}
.weather-temp { font-family: var(--font-serif); font-size: 1.7rem; line-height: 1.1; color: var(--ink); }
.weather-short { font-size: .82rem; color: var(--ink-2); margin-top: 2px; }

/* The headline card at night. The wash is the sky it is describing, so a clear
   night is a dark one — the daytime blue-and-gold behind the word "Overnight"
   was reading as a sunny afternoon. */
.weather-now.is-night.is-clear   { --wash-a: #1b2740; --wash-b: #2c3d5c; --glyph: #dbe4f5; --wash-ink: #eef2fa; }
.weather-now.is-night.is-partly  { --wash-a: #232f47; --wash-b: #3a4763; --glyph: #cbd6ea; --wash-ink: #eef2fa; }
.weather-now.is-night.is-cloud   { --wash-a: #2b323c; --wash-b: #3f4854; --glyph: #aab5c2; --wash-ink: #eceff3; }
.weather-now.is-night.is-rain    { --wash-a: #1f2e3c; --wash-b: #33475a; --glyph: #8fbfe0; --wash-ink: #e9f1f7; }
.weather-now.is-night.is-thunder { --wash-a: #23283a; --wash-b: #3a4058; --glyph: #f2c744; --wash-ink: #f3f1fa; }
.weather-now.is-night.is-snow    { --wash-a: #2b3a48; --wash-b: #43566a; --glyph: #cfe4f5; --wash-ink: #f0f6fb; }
.weather-now.is-night.is-fog     { --wash-a: #2e3336; --wash-b: #454b4f; --glyph: #b3bcc2; --wash-ink: #eef0f1; }
.weather-now.is-night.is-wind    { --wash-a: #22352f; --wash-b: #374f45; --glyph: #9ccbb9; --wash-ink: #ecf5f1; }

.weather-strip {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px;
  margin: 9px 0 11px;
}
.weather-chip {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 8px 4px; border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: linear-gradient(160deg, var(--chip-a, var(--surface-2)), var(--chip-b, var(--surface-2)));
  color: var(--chip-ink, var(--ink));
}
.weather-chip-when {
  font-size: .63rem; text-transform: uppercase; letter-spacing: .04em;
  color: inherit; opacity: .75; text-align: center; line-height: 1.2;
}
.weather-chip-glyph { width: 20px; height: 20px; color: var(--chip-glyph, currentColor); }
.weather-chip-glyph svg { width: 100%; height: 100%; }
.weather-chip-temp { font-size: .88rem; font-weight: 600; font-variant-numeric: tabular-nums; }

/* The chips take the same sky colours as the headline card, at lower strength.
   Four days of weather should be readable as a sequence — brightening, turning
   — without reading a word of it. Full strength on something this small would
   fight the numbers rather than support them.

   Set as custom properties rather than as a background shorthand, because the
   dark theme has to restate them. The first version dimmed the whole chip with
   `filter: brightness(.66)` instead — and a filter applies to an element's
   children, so the near-white text was darkened by exactly as much as the pale
   background behind it. The two met in the middle and the cards went blank. */
.weather-chip.is-clear   { --chip-a: #bfe0f5; --chip-b: #ffeec4; --chip-glyph: #e09400; --chip-ink: #24405a; }
.weather-chip.is-partly  { --chip-a: #cbe3f4; --chip-b: #f2f5f7; --chip-glyph: #d99a28; --chip-ink: #2b4155; }
.weather-chip.is-cloud   { --chip-a: #dbe1e8; --chip-b: #eef1f4; --chip-glyph: #667583; --chip-ink: #2f3944; }
.weather-chip.is-rain    { --chip-a: #b3c9dc; --chip-b: #dee8f1; --chip-glyph: #2f6f9f; --chip-ink: #1f3446; }
.weather-chip.is-thunder { --chip-a: #9098b4; --chip-b: #c2c8da; --chip-glyph: #7a5c00; --chip-ink: #23283a; }
.weather-chip.is-snow    { --chip-a: #e6f0f8; --chip-b: #fdfeff; --chip-glyph: #5f8bb0; --chip-ink: #2a3b4a; }
.weather-chip.is-fog     { --chip-a: #dfe2e4; --chip-b: #f0f1f2; --chip-glyph: #78828a; --chip-ink: #333b41; }
.weather-chip.is-wind    { --chip-a: #cbe0d8; --chip-b: #eef4f1; --chip-glyph: #3f7d68; --chip-ink: #23443a; }

/* Night periods get their own, darker set. A pale daytime sky behind "Tuesday
   Night" was the other half of why these did not read. */
.weather-chip.is-night.is-clear   { --chip-a: #1b2740; --chip-b: #2c3d5c; --chip-glyph: #cfd8ec; --chip-ink: #eef2fa; }
.weather-chip.is-night.is-partly  { --chip-a: #232f47; --chip-b: #3a4763; --chip-glyph: #c3cee4; --chip-ink: #eef2fa; }
.weather-chip.is-night.is-cloud   { --chip-a: #2b323c; --chip-b: #3f4854; --chip-glyph: #aab5c2; --chip-ink: #eceff3; }
.weather-chip.is-night.is-rain    { --chip-a: #1f2e3c; --chip-b: #33475a; --chip-glyph: #8fbfe0; --chip-ink: #e9f1f7; }
.weather-chip.is-night.is-thunder { --chip-a: #23283a; --chip-b: #3a4058; --chip-glyph: #f2c744; --chip-ink: #f3f1fa; }
.weather-chip.is-night.is-snow    { --chip-a: #2b3a48; --chip-b: #43566a; --chip-glyph: #cfe4f5; --chip-ink: #f0f6fb; }
.weather-chip.is-night.is-fog     { --chip-a: #2e3336; --chip-b: #454b4f; --chip-glyph: #b3bcc2; --chip-ink: #eef0f1; }
.weather-chip.is-night.is-wind    { --chip-a: #22352f; --chip-b: #374f45; --chip-glyph: #9ccbb9; --chip-ink: #ecf5f1; }

/* Dark theme restates the DAY colours rather than filtering them. Night chips
   are excluded: their palette is already dark and correct in both themes, and
   without the :not() these selectors outrank it on specificity — which put a
   gold sunrise wash behind the words "Tuesday Night". */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .weather-chip:not(.is-night).is-clear   { --chip-a: #33506b; --chip-b: #6b5f3d; --chip-ink: #f4efe2; --chip-glyph: #f0b429; }
  :root:not([data-theme="light"]) .weather-chip:not(.is-night).is-partly  { --chip-a: #3a4d60; --chip-b: #4b5560; --chip-ink: #eef0f3; --chip-glyph: #e6ae4a; }
  :root:not([data-theme="light"]) .weather-chip:not(.is-night).is-cloud   { --chip-a: #3b434c; --chip-b: #4c545c; --chip-ink: #eceff2; --chip-glyph: #b6c1cc; }
  :root:not([data-theme="light"]) .weather-chip:not(.is-night).is-rain    { --chip-a: #2f4457; --chip-b: #435a6e; --chip-ink: #eaf2f8; --chip-glyph: #8fc3e8; }
  :root:not([data-theme="light"]) .weather-chip:not(.is-night).is-thunder { --chip-a: #333a52; --chip-b: #474e69; --chip-ink: #f2f2f8; --chip-glyph: #f2c744; }
  :root:not([data-theme="light"]) .weather-chip:not(.is-night).is-snow    { --chip-a: #44535f; --chip-b: #5c6c78; --chip-ink: #f2f7fb; --chip-glyph: #cfe4f5; }
  :root:not([data-theme="light"]) .weather-chip:not(.is-night).is-fog     { --chip-a: #3f4447; --chip-b: #52585c; --chip-ink: #eef0f1; --chip-glyph: #bcc4ca; }
  :root:not([data-theme="light"]) .weather-chip:not(.is-night).is-wind    { --chip-a: #2f4A40; --chip-b: #40584d; --chip-ink: #edf5f1; --chip-glyph: #9ccbb9; }
}
:root[data-theme="dark"] .weather-chip:not(.is-night).is-clear   { --chip-a: #33506b; --chip-b: #6b5f3d; --chip-ink: #f4efe2; --chip-glyph: #f0b429; }
:root[data-theme="dark"] .weather-chip:not(.is-night).is-partly  { --chip-a: #3a4d60; --chip-b: #4b5560; --chip-ink: #eef0f3; --chip-glyph: #e6ae4a; }
:root[data-theme="dark"] .weather-chip:not(.is-night).is-cloud   { --chip-a: #3b434c; --chip-b: #4c545c; --chip-ink: #eceff2; --chip-glyph: #b6c1cc; }
:root[data-theme="dark"] .weather-chip:not(.is-night).is-rain    { --chip-a: #2f4457; --chip-b: #435a6e; --chip-ink: #eaf2f8; --chip-glyph: #8fc3e8; }
:root[data-theme="dark"] .weather-chip:not(.is-night).is-thunder { --chip-a: #333a52; --chip-b: #474e69; --chip-ink: #f2f2f8; --chip-glyph: #f2c744; }
:root[data-theme="dark"] .weather-chip:not(.is-night).is-snow    { --chip-a: #44535f; --chip-b: #5c6c78; --chip-ink: #f2f7fb; --chip-glyph: #cfe4f5; }
:root[data-theme="dark"] .weather-chip:not(.is-night).is-fog     { --chip-a: #3f4447; --chip-b: #52585c; --chip-ink: #eef0f1; --chip-glyph: #bcc4ca; }
:root[data-theme="dark"] .weather-chip:not(.is-night).is-wind    { --chip-a: #2f4a40; --chip-b: #40584d; --chip-ink: #edf5f1; --chip-glyph: #9ccbb9; }

/* Field notes */
.note-list { display: grid; gap: 7px; margin-bottom: 10px; }
.note {
  padding: 8px 10px; border-radius: var(--radius-sm);
  background: var(--surface-2); border-left: 2px solid var(--line-strong);
}
.note-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.note-date {
  font-size: .7rem; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-3);
}
.note-head .icon-button { width: 22px; height: 22px; }
.note-head .icon-button svg { width: 12px; height: 12px; }
.note-text { margin: 2px 0 0; font-size: .85rem; line-height: 1.45; white-space: pre-line; }

/* ==========================================================================
   Offline regions
   ========================================================================== */

.region-list { display: grid; gap: 8px; margin: 4px 0 10px; }

.region {
  padding: 9px 10px; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--line);
}
.region.is-active { border-color: var(--line-strong); background: var(--paper-2); }

.region-head { display: flex; align-items: center; gap: 4px; }
.region-name {
  flex: 1; min-width: 0; padding: 3px 5px; font: inherit; font-size: .88rem; font-weight: 550;
  color: var(--ink); background: transparent; border: 1px solid transparent; border-radius: 4px;
}
.region-name:hover { border-color: var(--line); }
.region-name:focus { border-color: var(--line-strong); background: var(--surface); outline: none; }
.region-head .icon-button { width: 24px; height: 24px; }
.region-head .icon-button svg { width: 13px; height: 13px; }

.region-meta {
  margin-top: 3px; font-size: .73rem; color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/* The saved-offline list under Folders. A region there is a record of what is
   on the device rather than a control, so its name is text and not a field —
   an input invites editing, and this is not where a region is edited. */
.region-name-static {
  flex: 1; min-width: 0; font-size: .88rem; font-weight: 550; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* A picture is its own evidence, so the thumbnail carries the row and the text
   beside it only says where and when. */
.region.snapshot { display: flex; align-items: center; gap: 9px; }
.snapshot-thumb {
  width: 64px; height: 44px; flex: none; object-fit: cover;
  border-radius: 4px; border: 1px solid var(--line); background: var(--paper-2);
}
.snapshot-body { flex: 1; min-width: 0; }
.snapshot-body .region-meta { margin-top: 0; }
.snapshot-body .region-meta:first-child {
  color: var(--ink); font-size: .82rem; font-weight: 550;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.region-controls { display: flex; align-items: center; gap: 5px; margin-top: 7px; flex-wrap: wrap; }
/* The global select rule sets a 150px minimum, which here would push the cost
   onto its own line and break the side-by-side reading the panel depends on. */
.region-zoom {
  flex: none; min-width: 0; width: auto;
  padding: 2px 4px; font: inherit; font-size: .76rem;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: 4px;
}
.region-to { font-size: .73rem; color: var(--ink-3); }

/* The cost sits beside the zooms it comes from, so raising one visibly moves
   the other — which is the whole argument for the z14 cap. */
.region-cost {
  margin-left: auto; font-size: .76rem; color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.region-cost.is-over { color: var(--clay-2); font-weight: 600; }

.region-warning {
  margin: 9px 0 0; padding: 8px 10px; border-radius: var(--radius-sm);
  font-size: .76rem; line-height: 1.45;
  color: var(--clay-2); background: var(--clay-soft);
}

/* ==========================================================================
   Layer groups and colour keys
   ========================================================================== */

.layer-group { border-bottom: 1px solid var(--line); }
.layer-group:last-of-type { border-bottom: 0; }

.layer-group-summary {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 9px 4px; cursor: pointer; list-style: none;
  font-size: .82rem; font-weight: 600; letter-spacing: .02em; color: var(--ink-2);
}
.layer-group-summary::-webkit-details-marker { display: none; }

/* A caret that turns, so open and closed are legible without reading. */
.layer-group-summary::before {
  content: ''; flex: none; width: 0; height: 0;
  border-left: 4px solid currentColor;
  border-top: 3.5px solid transparent; border-bottom: 3.5px solid transparent;
  transition: transform .15s ease; transform-origin: 25% 50%;
}
.layer-group[open] > .layer-group-summary::before { transform: rotate(90deg); }
.layer-group-summary:hover { color: var(--ink); }

.layer-desc-text { margin: 0; }

/* Colour key. Third-party raster tiles carry no attribute data, so the only
   way to say what a colour means is to write it down. */
.legend { list-style: none; margin: 7px 0 0; padding: 0; display: grid; gap: 4px; }
/* A long ramp reads down one column and up the next rather than becoming a
   stripe taller than the panel, which pushes every layer under it out of
   reach. Short keys stay in one column: two columns of three reads as a
   mistake. */
.legend.is-symbols { grid-template-columns: repeat(2, 1fr); column-gap: 12px; gap: 6px 12px; }
.legend-symbol { flex: none; display: inline-flex; line-height: 0; }
.legend.is-split { grid-template-columns: repeat(2, 1fr); column-gap: 12px; }
.legend.is-wide { grid-template-columns: repeat(3, 1fr); column-gap: 8px; }
/* Numbered ramps only: at three across, the cell is about four characters, so
   a word wraps and the column stops lining up. */
.legend.is-wide .legend-item { font-size: .69rem; gap: 5px; }
.legend.is-wide .legend-swatch { width: 11px; height: 11px; }
@media (max-width: 380px) {
  .legend.is-split { grid-template-columns: 1fr; }
  .legend.is-wide { grid-template-columns: repeat(2, 1fr); }
}
.legend-item { display: flex; align-items: center; gap: 7px; font-size: .75rem; color: var(--ink-2); }
.legend-swatch {
  flex: none; width: 13px; height: 13px; border-radius: 3px;
  border: 1px solid rgba(0, 0, 0, .18);
}
/* A swatch the service drew rather than one we wrote down: its own size, and
   no rounded corners, because some of them are lines rather than blocks. */
.legend-swatch.is-image {
  width: auto; height: 15px; border-radius: 2px; background: #fff;
  border-color: rgba(0, 0, 0, .1);
}

/* Public / private land banner in the details panel */
.land-badge {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; margin-bottom: 8px; border-radius: var(--radius-sm);
  font-size: .82rem; font-weight: 550;
}
.land-badge-mark { display: flex; flex: none; }
.land-badge-mark svg { width: 15px; height: 15px; }
.land-badge.is-public { color: #1b5e20; background: #e6f2e6; }
.land-badge.is-private { color: var(--clay-2); background: var(--clay-soft); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .land-badge.is-public { color: #a5d6a7; background: rgba(46, 125, 50, .18); }
}
:root[data-theme="dark"] .land-badge.is-public { color: #a5d6a7; background: rgba(46, 125, 50, .18); }

/* ---------- eclipses ---------- */

/*
 * Built like the weather headline, for the same reason: a wash read before any
 * of the words, one number at size, and the sentence under it. The wash here
 * is the colour the moon actually goes.
 */
.eclipse-now {
  position: relative; isolation: isolate;
  display: flex; align-items: center; gap: 13px;
  padding: 13px 14px; border-radius: var(--radius);
  border: 1px solid var(--line);
}
.eclipse-now::before {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: linear-gradient(135deg, var(--wash-a), var(--wash-b));
}
.eclipse-now.is-total     { --wash-a: #2b1a14; --wash-b: #5c2d1c; --ink-on: #f7ece3; }
.eclipse-now.is-partial   { --wash-a: #262a35; --wash-b: #4a4436; --ink-on: #f4efe6; }
.eclipse-now.is-penumbral { --wash-a: #2a2c31; --wash-b: #43464c; --ink-on: #eff0f2; }

.eclipse-now-text { min-width: 0; }
.eclipse-when {
  font-size: .7rem; font-weight: 650; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-on); opacity: .8;
}
.eclipse-headline {
  font-family: var(--font-serif); font-size: 1.7rem; line-height: 1.1;
  color: var(--ink-on); font-variant-numeric: tabular-nums;
}
.eclipse-short { font-size: .82rem; color: var(--ink-on); opacity: .9; margin-top: 2px; }

/*
 * The moon's face with the shadow across it, to scale in moon radii.
 *
 * The umbra is about 2.7 moon radii, which is why the terminator during an
 * eclipse is a gentle arc rather than the sharp curve of a crescent — and that
 * shallow curve is the thing that says "eclipse" rather than "phase".
 */
.eclipse-diagram { flex: none; width: 54px; height: 54px; }
.eclipse-diagram svg { width: 100%; height: 100%; overflow: visible; }
.eclipse-face { fill: #f2e8d4; }
/* Penumbral shading is genuinely faint — most people cannot see it at all,
   and a picture that shows it strongly promises something the sky will not. */
.eclipse-penumbra { fill: rgba(40, 22, 14, .28); }
/* The umbra is not black. The only light reaching the moon there is sunlight
   bent through the whole thickness of the earth's atmosphere, which is why a
   totally eclipsed moon is copper rather than invisible. */
.eclipse-umbra { fill: #6d2c17; }
.eclipse-now.is-total .eclipse-umbra { fill: #a8482a; }
/* A thin warm rim, so the disc reads as lit rather than as a flat circle. */
.eclipse-rim { fill: none; stroke: rgba(255, 214, 170, .5); stroke-width: 1; }
.eclipse-chip .eclipse-rim { stroke-width: .7; }

/* The stages, the way four days of weather are a strip. Each chip draws the
   moon where it actually is, so the bite grows and shrinks across the row. */
.eclipse-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(52px, 1fr));
  gap: 6px; margin: 10px 0 12px;
}
.eclipse-chip {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 7px 2px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface-2); color: var(--ink);
}
/* Below the horizon is not a failure, but it is not a plan either. */
.eclipse-chip.is-down { opacity: .55; }
/* A fixed two lines, so the moons line up across the row however long a stage
   is called. Without it "FIRST BITE" wraps, "GREATEST" does not, and the
   sequence the strip exists to show is pushed out of step. */
.eclipse-chip-when {
  font-size: .58rem; text-transform: uppercase; letter-spacing: .03em;
  color: var(--ink-3); text-align: center; line-height: 1.15;
  min-height: 2.3em; display: flex; align-items: center;
}
.eclipse-diagram.is-plain { width: 30px; height: 30px; }
/* Never wrapped: "1:26 AM" breaking after the colon put the meridiem on its
   own line and made every chip a different height. */
.eclipse-chip-time {
  font-size: .7rem; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.eclipse-chip-alt { font-size: .58rem; color: var(--ink-3); }

.eclipse-verdict {
  margin: 0 0 10px; padding: 9px 11px; border-radius: var(--radius-sm);
  font-size: .84rem; line-height: 1.45;
  border-left: 3px solid var(--line-strong); background: var(--paper-2); color: var(--ink-2);
}
.eclipse-verdict.is-yes { border-left-color: var(--pine); background: var(--pine-soft); color: var(--ink); }
.eclipse-verdict.is-part { border-left-color: var(--warn); }
.eclipse-verdict.is-no { border-left-color: var(--ink-3); }


.eclipse-later-title {
  margin: 14px 0 6px; font-size: .64rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3);
}
.eclipse-later { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; }
.eclipse-later li { display: flex; align-items: center; gap: 8px; font-size: .82rem; color: var(--ink-2); }
.eclipse-later-when { margin-right: auto; }
.eclipse-later-kind { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); }
.eclipse-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; background: var(--ink-3); }
.eclipse-dot.is-total { background: #b4552f; }
.eclipse-dot.is-partial { background: #d8a15f; }
.eclipse-dot.is-penumbral { background: var(--line-strong); }

/* Everything about taking the map away, in one dropdown. */
.offline-drop { width: min(330px, calc(100vw - 24px)); max-height: 70vh; overflow-y: auto; padding: 14px; }
.offline-heading {
  margin: 0 0 8px; font-size: .64rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3);
}
.offline-heading + .folder-actions { margin-bottom: 14px; }
/* The picture and the download each on a line of their own; the exports
   share one, and so do the download and the draw. */
.offline-actions .button { flex: 1 1 0; }
.offline-actions .button-full { flex: 1 0 100%; }
.offline-drop .folder-actions + .offline-heading { margin-top: 4px; }

/* The map's own two tools, stacked under the engine's controls. */
.map-tools { overflow: hidden; }
.map-tool {
  display: grid; place-items: center;
  width: 29px; height: 29px; padding: 0; cursor: pointer;
  background: var(--surface); color: var(--ink-2);
  border: 0; border-bottom: 1px solid var(--line);
}
.map-tool:last-child { border-bottom: 0; }
.map-tool:hover { background: var(--paper-2); color: var(--ink); }
.map-tool svg { width: 17px; height: 17px; }
/* Armed: the next tap asks a question instead of dropping a pin, and the
   button has to say so — a mode with no visible state is a trap. */
.map-tool.is-on { background: var(--clay); color: #fff; }
.app.is-probing .maplibregl-canvas-container,
.app.is-probing .mapboxgl-canvas-container { cursor: crosshair; }

/* What a tap turned out to be sitting on. */
.identify-card { min-width: 244px; }
/*
 * Everything you can do about the place, under one line and in two rows: what
 * this app can do with it, then who else can take you to it.
 *
 * Three equal columns rather than flex, so the two rows line up with each
 * other - a row of three over a row of three that did not share edges read as
 * six unrelated buttons. Labels wrap inside their button instead of widening
 * the card, which is capped at 340px over a map.
 */
.popup-tail { margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--line); }
.popup-row { display: grid; gap: 6px; }
.popup-row + .popup-row { margin-top: 6px; }
.popup-row .button { width: 100%; justify-content: center; text-decoration: none; }
/*
 * Three across is the tight case; one or two is the roomy one.
 *
 * A row of three over a 340px card has no width for icon-beside-label, so the
 * icon goes above and the label wraps under it. A row of two does have the
 * room, and taking it means Details and Close on an identify card look like
 * Details and Close on a dropped pin, which is the same pair of buttons.
 */
.popup-row-tight { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.popup-row-wide { grid-template-columns: repeat(var(--popup-cols, 2), minmax(0, 1fr)); }
.popup-row-tight .button {
  text-align: center; font-size: .78rem; line-height: 1.15; padding: 6px 4px;
}
.popup-row .button-with-icon { gap: 4px; }
/*
 * Wrapped, not truncated.
 *
 * button-with-icon ellipses its label, which is right for a row that must not
 * widen and wrong for a grid cell with height to spare: three cells across a
 * 340px card gave "Add to T…", "Save to …", "Apple M…". Two short lines say
 * the whole word.
 */
.popup-row .button span {
  overflow: visible; text-overflow: clip; white-space: normal; overflow-wrap: anywhere;
}
/* Stacked only where the width forces it. A row of two has room for the icon
   beside the label; a row of three does not. */
.popup-row-tight .button-with-icon { flex-direction: column; gap: 3px; }
.popup-row .button svg { width: 13px; height: 13px; flex: none; }
/* The save picker drops below both rows, full width. */
.popup-tail .popup-save-panel { margin-top: 8px; }
/*
 * No corner mark, and so no gutter to keep clear of one.
 *
 * The × lived at the top right for a while, which meant every line that could
 * be the card's first one had to reserve 22px on its right - the title, the
 * first group's source and designation, and the empty state's paragraph, which
 * was forgotten and got the mark laid through the middle of its first
 * sentence. Close is a labelled button on the last row now, like every other
 * popup in the app, and none of that reserving is needed.
 */
/* Only ever on screen on its own, while the services answer, so it carries no
   gap below it for content that is not there yet. */
.identify-title {
  font-family: var(--font-serif); font-size: 1.02rem; font-weight: 600;
  margin: 0; color: var(--ink);
}
.identify-group + .identify-group { margin-top: 12px; padding-top: 11px; border-top: 1px solid var(--line); }
/* Action links under an identify card - they wrap rather than widening the
   popup, which is capped at 340px and has a map behind it. */
.identify-links { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.identify-links .button { text-decoration: none; }
/* The agency is a heading, not a footnote: both of them say "open to all
   vehicles" and neither means quite the same thing by it. */
.identify-source {
  margin: 0; font-size: .64rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3);
}
.identify-designation {
  margin: 2px 0 6px; font-size: .88rem; font-weight: 600; color: var(--ink);
}
/* Standing alone, it is the first thing in the group and needs no gap above
   the kicker that is not there. */
.identify-designation:first-child { margin-top: 0; }
/*
 * Striped, because these cards run long.
 *
 * A label and its value are two grid cells on the same row, and at seven rows
 * of wrapping text it stops being obvious which value belongs to which label.
 * Shading alternate rows ties each pair together across the gap; lines between
 * every row would do the same job and add six more rules to a card that is
 * already busy.
 */
.identify-body .popup-stats {
  border-top: 0; padding-top: 0; gap: 0;
  border-radius: var(--radius-sm); overflow: hidden;
}
.identify-body .popup-stats dt,
.identify-body .popup-stats dd { padding: 7px 9px; align-self: stretch; }
.identify-body .popup-stats dt:nth-of-type(odd),
.identify-body .popup-stats dd:nth-of-type(odd) { background: var(--popup-stripe); }
.identify-body .popup-stats dt { color: var(--ink-2); }
.identify-body .source-note { margin-top: 10px; }

/* Dropped-pin popup */
.drop-pin { min-width: 236px; }
.drop-pin-name {
  width: 100%; padding: 7px 9px; margin-bottom: 9px;
  font: inherit; font-size: .95rem; font-weight: 600; color: var(--ink);
  background: var(--popup-control); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
}
.drop-pin-name:focus { border-color: var(--clay); outline: none; }
.drop-pin-coords { font-variant-numeric: tabular-nums; }
.drop-pin .popup-stats dd { display: flex; align-items: center; gap: 4px; }

/* Symbol and note, set before the pin is saved rather than after. */
.drop-pin-fields { display: grid; gap: 6px; margin-top: 10px; }
.drop-pin-note {
  width: 100%; font: inherit; font-size: .8rem; padding: 7px 8px;
  color: var(--ink); background: var(--popup-control);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
}
.drop-pin-note { resize: vertical; min-height: 42px; }
.drop-pin-note:focus { outline: none; border-color: var(--clay); }

/*
 * The symbol, shown as itself.
 *
 * A <select> of names opens a full-height native wheel on iOS listing forty
 * words, and "Plain pin" describes nothing about the mark that ends up on the
 * map. The button carries the current symbol; pressing it opens the symbols.
 */
.drop-pin-symbol {
  display: flex; align-items: center; gap: 8px; width: 100%;
  font: inherit; font-size: .8rem; padding: 6px 9px; cursor: pointer;
  color: var(--ink); background: var(--popup-control);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
}
.drop-pin-symbol svg { width: 20px; height: 20px; flex: none; color: var(--clay); }
.drop-pin-symbol:hover { border-color: var(--ink-3); }

.drop-pin-symbols .icon-search { margin-bottom: 7px; }
.drop-pin-symbols {
  padding: 8px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--popup-control);
  max-height: 208px; overflow-y: auto;
}
.drop-pin-symbols[hidden] { display: none; }
.drop-pin-symbols-group {
  margin: 6px 0 4px; font-size: .62rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3);
}
.drop-pin-symbols-group:first-child { margin-top: 0; }
.drop-pin-symbols-row { display: flex; flex-wrap: wrap; gap: 5px; }
.drop-pin-symbol-choice {
  display: inline-flex; align-items: center; justify-content: center;
  /* 38px so it is a real target for a thumb, not a 20px glyph. */
  width: 38px; height: 38px; cursor: pointer; color: var(--ink-2);
  background: var(--popup-bg); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.drop-pin-symbol-choice:hover { border-color: var(--ink-3); color: var(--ink); }
.drop-pin-symbol-choice.is-on {
  color: #fff; background: var(--clay); border-color: var(--clay);
}

/* A section that came back empty for a reason worth stating quietly */
.panel-section.is-quiet { opacity: .72; }

.legend-note { margin: 6px 0 0; font-size: .71rem; line-height: 1.45; color: var(--ink-3); }

/* Which build is running. Small, permanent, and the answer to "did my change
   deploy" without opening a second tab. */
.build-stamp {
  /* Stuck to the foot of the panel rather than sitting at the end of the
     scroll. At the end of a long Layers tab it is below eleven basemaps and
     five overlay groups, which is the same as not being there — and the whole
     point of it is being findable without looking for it. */
  position: sticky; bottom: 0; z-index: 2;
  /* Flush with the panel, not fourteen pixels outside it. `.panel-scroll` has
     no horizontal padding to cancel, so the negative margins this used to
     carry hung the stamp over both edges — invisible while the panel was
     narrower than the screen, and off the right of a phone the moment the
     panel filled the width. */
  margin: 14px 0 0; padding: 7px 14px;
  border-top: 1px solid var(--line);
  background: var(--surface);
  font-size: .68rem; letter-spacing: .03em; color: var(--ink-3);
  font-variant-numeric: tabular-nums; text-align: center;
}

/* Repeat count on a toast that is still firing */
.toast-count {
  flex: none; padding: 1px 6px; border-radius: 999px;
  font-size: .7rem; font-weight: 700; font-variant-numeric: tabular-nums;
  background: rgba(0, 0, 0, .1); color: inherit;
}

/* ==========================================================================
   "Where should these waypoints go?" — asked after an import, over the map
   ========================================================================== */

.import-ask {
  position: absolute; left: 50%; bottom: 26px; transform: translateX(-50%);
  z-index: 18; max-width: min(520px, calc(100% - 32px));
}
.import-ask[hidden] { display: none; }

.import-ask-card {
  padding: 13px 15px; border-radius: var(--radius-md, 8px);
  background: var(--surface); border: 1px solid var(--line-strong);
  box-shadow: var(--shadow-lg, 0 8px 28px rgba(0, 0, 0, .18));
}
.import-ask-title {
  margin: 0; font-size: .95rem; font-weight: 600; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.import-ask-text { margin: 4px 0 11px; font-size: .8rem; color: var(--ink-3); line-height: 1.45; }
.import-ask-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; }
.import-ask-select {
  min-width: 0; max-width: 190px; padding: 4px 7px; font: inherit; font-size: .82rem;
  color: var(--ink); background: var(--surface); border: 1px solid var(--line); border-radius: 5px;
}

@media (max-width: 560px) {
  .import-ask { left: 12px; right: 12px; transform: none; max-width: none; bottom: 16px; }
  .import-ask-actions .button { flex: 1 1 auto; }
}

/* Whose numbers are these? A file's totals are not a folder's. */
.detail-scope {
  display: flex; align-items: center; gap: 6px; margin-bottom: 5px;
  font-size: .7rem; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-3);
}
.detail-scope-mark { display: flex; }
.detail-scope-mark svg { width: 12px; height: 12px; }

/* ==========================================================================
   Sun & moon
   ========================================================================== */

/* The day as one bar. Reading a photographer's day is reading proportions —
   how much golden hour there is relative to the twilight either side — and a
   list of times does not show that at all. */
.sky-bar {
  /*
   * `overflow: visible`, and the rounding moved onto the bands.
   *
   * The now marker has to stand above and below the bar to be findable, and
   * clipping it to the bar is what made it a two-pixel sliver among a dozen
   * coloured ones. The bands carry the rounded ends now, so the bar still
   * looks the same.
   */
  position: relative; display: flex; height: 13px; margin: 7px 0 4px;
  border-radius: 3px; overflow: visible;
  border: 1px solid var(--line);
}
.sky-band:first-child { border-radius: 2px 0 0 2px; }
.sky-band:last-child { border-radius: 0 2px 2px 0; }
.sky-band { display: block; min-width: 1px; }
.sky-band.is-astronomical { background: #1b2440; }
.sky-band.is-nautical     { background: #2c3f6b; }
.sky-band.is-blue         { background: #3f63a8; }
.sky-band.is-sunrise      { background: #d2743f; }
.sky-band.is-sunset       { background: #d2743f; }
.sky-band.is-golden       { background: #f0b429; }
.sky-band.is-day          { background: #9ecbe8; }

/* Where we are in the day. Without it the bar is a diagram; with it, it is a
   clock you can read in one glance. */
/*
 * Findable against any band, which is the whole difficulty.
 *
 * The bands run from near-black at midnight to pale blue at noon, so no single
 * colour contrasts with all of them - a dark line vanishes into night and a
 * pale one vanishes into day. A white core inside a dark halo reads against
 * both, and the tab above it is what the eye actually lands on: at a glance the
 * bar is a clock with a hand, rather than a diagram with a hairline in it.
 */
.sky-now {
  position: absolute; top: -5px; bottom: -5px; width: 3px;
  background: #fff;
  box-shadow: 0 0 0 1.5px rgba(0, 0, 0, .85);
  border-radius: 2px;
  transform: translateX(-1.5px);
  pointer-events: none;
}
/* A cap at the top, because at this size a triangle reads as a smudge. */
.sky-now::before {
  content: ""; position: absolute; left: 50%; top: -3px;
  width: 9px; height: 3px; transform: translateX(-50%);
  border-radius: 2px;
  background: #fff; box-shadow: 0 0 0 1.5px rgba(0, 0, 0, .85);
}

.sky-bar-ends {
  display: flex; justify-content: space-between;
  font-size: .68rem; color: var(--ink-3); font-variant-numeric: tabular-nums;
}

/* The four times worth setting an alarm for, on two lines over a sky wash.
   Same device as the weather card and for the same reason: the panel should
   read before the text does. Warm at the sunrise end, cool at the sunset end,
   so the two halves are the two ends of the day without a label saying so. */
.light-hero {
  margin: 12px 0 4px; border-radius: var(--radius);
  border: 1px solid var(--line); overflow: hidden;
  background: linear-gradient(135deg, #f6d9a8 0%, #eec98f 38%, #9fb6d4 72%, #7d97bd 100%);
  --light-ink: #2a2b38; --light-ink-soft: #4b4f63;
}
.light-row { display: grid; grid-template-columns: 1fr 1fr; }
.light-row.is-secondary {
  border-top: 1px solid rgba(255, 255, 255, .45);
  background: rgba(255, 255, 255, .28);
}
.light-half {
  display: flex; align-items: center; gap: 9px; min-width: 0;
  padding: 10px 11px;
}
.light-half + .light-half { border-left: 1px solid rgba(255, 255, 255, .45); }
.light-glyph { flex: none; display: flex; color: var(--light-ink); }
.light-glyph svg { display: block; }
.light-text { min-width: 0; }
.light-label {
  font-size: .64rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--light-ink-soft);
}
.light-time {
  font-family: var(--font-serif); font-size: 1.12rem; line-height: 1.15;
  color: var(--light-ink); font-variant-numeric: tabular-nums;
}
.light-note {
  font-size: .68rem; color: var(--light-ink-soft); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Four things that are all "more about the sky here" used to be a text
   dropdown, an always-open card and a lone button — three affordances for one
   idea. One row of buttons, one box below. */
/* Three across, then whatever is left filling the row under them.
   A two-column grid put five tabs on three rows; flex-basis of a third with
   room to grow gives 3 + 2, and stays right if a sixth is ever added rather
   than hard-coding where the break falls. */
/*
 * A grid of four, not a flex row of thirds.
 *
 * Three to a row put a seventh tab on a third row of its own, stretched to the
 * full width by the flex basis and reading as a button that belonged to
 * something else. A grid also gives every cell the same height, which is what
 * lets the one long label wrap instead of being cut to "Milky W…".
 */
.sky-tabs {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px; margin: 12px 0 0;
}
.sky-tab {
  min-width: 0;
  padding: 7px 4px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface-2);
  font: inherit; font-size: .74rem; font-weight: 600; color: var(--ink-2);
  cursor: pointer; white-space: nowrap;
}
.sky-tab:hover { border-color: var(--line-strong); color: var(--ink); }
.sky-tab.is-open {
  background: var(--ink); border-color: var(--ink); color: var(--paper);
}
.sky-panel {
  margin-top: 10px; padding: 11px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface-2);
}

.sky-phases { display: grid; gap: 3px; margin: 4px 0 0; }
.sky-phase {
  display: grid; grid-template-columns: 10px 1fr auto auto; gap: 8px;
  align-items: center; font-size: .75rem;
}
.sky-swatch { width: 10px; height: 10px; border-radius: 2px; }
.sky-swatch.is-astronomical { background: #1b2440; }
.sky-swatch.is-nautical     { background: #2c3f6b; }
.sky-swatch.is-blue         { background: #3f63a8; }
.sky-swatch.is-sunrise, .sky-swatch.is-sunset { background: #d2743f; }
.sky-swatch.is-golden       { background: #f0b429; }
.sky-swatch.is-day          { background: #9ecbe8; }
.sky-phase-name { color: var(--ink-2); }
.sky-phase-time, .sky-phase-span { color: var(--ink-3); font-variant-numeric: tabular-nums; }
.sky-phase-span { min-width: 52px; text-align: right; }

/* The moon, drawn at the phase it is actually at. */
.moon-card {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border-radius: var(--radius-sm);
  background: linear-gradient(140deg, #1d2436, #38415c);
  --moon-lit: #f4efe0; --moon-dark: #2b3247; --moon-edge: rgba(255, 255, 255, .28);
}
.moon-face { flex: none; width: 42px; height: 42px; }
.moon-face svg { width: 100%; height: 100%; display: block; }
.moon-text { min-width: 0; }
.moon-name { font-size: .92rem; font-weight: 600; color: #f2ede1; }
.moon-meta { font-size: .74rem; color: #b9c0d2; font-variant-numeric: tabular-nums; }

.sky-lines-toggle { width: 100%; margin-bottom: 11px; }

/*
 * The map toggle and, once it is on, the slider and the numbers under it.
 *
 * A block of its own in every sky tab, so the control sits below whatever that
 * tab is about rather than in the middle of it, and so the button keeps the
 * full-width shape every other primary action in these panels has. The grid
 * gap spaces it, so the shared toggle rule's own bottom margin is dropped.
 */
.sky-map { display: grid; gap: 8px; margin: 12px 0 2px; }
.sky-map .sky-lines-toggle { margin-bottom: 0; }
.sky-map .scrubber { margin-bottom: 0; }

/*
 * What the lines that carry no label are.
 *
 * Every bearing is labelled along itself; the band and the night track are
 * not, because a label repeated along a sampled curve is an unreadable mess.
 * The first thing anyone asks about the drawing is what that pale violet line
 * is, so it is answered here, with swatches drawn the way the map draws them.
 */
.line-key { display: grid; gap: 7px; margin: 10px 0 0; }
.line-key-row { display: flex; align-items: flex-start; gap: 9px; }
.line-key-swatch { flex: none; width: 26px; height: 12px; margin-top: 2px; border-radius: 2px; }
.line-key-swatch.is-band {
  background: linear-gradient(to bottom, transparent 22%, #7b4fa8 22%, #7b4fa8 78%, transparent 78%);
  opacity: .8;
}
/* Drawn as the dashes the map draws, so the key and the line are the same
   thing rather than two descriptions of it. */
.line-key-swatch.is-track {
  background: repeating-linear-gradient(to right,
    #7b4fa8 0 4px, transparent 4px 9px) center / 100% 2px no-repeat;
  opacity: .75;
}
.line-key-swatch.is-spoke {
  background: linear-gradient(to bottom, transparent 30%, #7b4fa8 30%, #7b4fa8 70%, transparent 70%);
}
.line-key-text { display: grid; gap: 1px; min-width: 0; }
.line-key-name { font-size: .78rem; font-weight: 600; color: var(--ink-2); }
.line-key-note { font-size: .72rem; line-height: 1.4; color: var(--ink-3); }

/* Grows the tree rather than paging it: you drag things around in here, and a
   page boundary you can drag across is worse than a long list. */
.folder-more {
  width: 100%; margin-top: 4px; padding: 7px 9px;
  border: 1px dashed var(--line-strong); border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-3);
  font: inherit; font-size: .73rem; cursor: pointer;
}
.folder-more:hover { color: var(--ink); border-color: var(--ink-3); background: var(--paper-2); }

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

/* A card rather than a row: an imported pin's note is usually the only thing
   telling it apart from the forty others on the same creek, so it gets a line
   of its own instead of being hidden behind a click. */
.waypoint-card {
  display: grid; grid-template-columns: auto 1fr auto; gap: 9px; align-items: start;
  padding: 9px 10px; margin-bottom: 6px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); cursor: pointer;
}
.waypoint-card:hover { border-color: var(--line-strong); background: var(--paper-2); }
.waypoint-card.is-selected { border-color: var(--accent, var(--ink)); background: var(--paper-2); }
.waypoint-card:focus-visible { outline: 2px solid var(--accent, var(--ink)); outline-offset: 1px; }

/* The symbol as a mark beside the name. Spelling it out in a column as well
   would be saying the same thing twice. */
.waypoint-mark {
  flex: none; width: 24px; height: 24px; border-radius: 999px;
  display: grid; place-items: center;
  background: #fff; color: #2A2118; border: 2px solid var(--pin, #2A2118);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .1);
}
.waypoint-body { min-width: 0; }
.waypoint-head { display: flex; align-items: baseline; gap: 7px; }
.waypoint-name {
  font-size: .84rem; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.waypoint-blurb {
  margin: 2px 0 0; font-size: .73rem; line-height: 1.35; color: var(--ink-2);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* The newest field note. Set apart from the description above it — one is what
   the place is, the other is what it was like last time. */
.waypoint-note {
  display: flex; gap: 5px; align-items: flex-start;
  margin: 5px 0 0; padding-left: 6px;
  border-left: 2px solid var(--clay);
  font-size: .72rem; line-height: 1.35; color: var(--ink-2);
}
.waypoint-note-mark { flex: none; color: var(--ink-3); display: inline-flex; margin-top: 1px; }
.waypoint-note-mark svg { width: 11px; height: 11px; }
.waypoint-note span:last-child {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.waypoint-foot {
  display: flex; gap: 8px; margin-top: 3px;
  font-size: .68rem; color: var(--ink-3);
}
.waypoint-symbol::before { content: '· '; }
.waypoint-edit { flex: none; align-self: center; }

/* Paging, because a real export runs to thousands and the panel has to open. */
.waypoint-pager {
  display: flex; align-items: center; justify-content: space-between; gap: 9px;
  padding: 9px 2px 2px; margin-top: 4px; border-top: 1px solid var(--line);
}
.waypoint-pager-note { font-size: .72rem; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.waypoint-pager button[disabled] { opacity: .4; cursor: default; }

/* ---- the editor inside a pin's popup ---- */

.popup-edit { display: grid; gap: 7px; }
.popup-edit-name, .popup-edit-note, .popup-edit-icon {
  width: 100%; font: inherit; font-size: .8rem;
  padding: 6px 8px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
}
.popup-edit-note { resize: vertical; min-height: 52px; }
.popup-edit-colours { display: flex; flex-wrap: wrap; gap: 5px; }
.popup-swatch {
  width: 20px; height: 20px; border-radius: 999px; padding: 0;
  border: 1px solid rgba(0, 0, 0, .18); cursor: pointer;
}
.popup-swatch.is-on { box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px var(--ink); }

/* Visibility is an eye, and a hidden folder says so at a glance rather than
   only by the icon it is drawn with. */
.folder-eye { flex: none; }
.folder-eye.is-hidden { color: var(--ink-3); opacity: .65; }
.folder-head.is-hidden .folder-name { opacity: .55; text-decoration: line-through; }

/* Shown when the basemap on screen is not the one that was picked. Amber
   rather than red: it is working, just not as itself. */
.basemap-fallback {
  display: flex; gap: 8px; align-items: flex-start;
  margin: 0 0 11px; padding: 9px 10px; border-radius: var(--radius-sm);
  border: 1px solid #d9b871; background: #fdf5e3;
  font-size: .74rem; line-height: 1.4; color: #6b4f16;
}
.basemap-fallback-mark {
  flex: none; width: 16px; height: 16px; border-radius: 999px;
  display: grid; place-items: center; margin-top: 1px;
  background: #c9922c; color: #fff; font-size: .66rem; font-weight: 700;
}
.basemap-fallback-fix { margin-top: 3px; color: #8a6a24; }
:root[data-theme="dark"] .basemap-fallback,
:root:not([data-theme="light"]) .basemap-fallback {
  border-color: #7a6128; background: #2c2617; color: #e6ce9b;
}
:root[data-theme="dark"] .basemap-fallback-fix,
:root:not([data-theme="light"]) .basemap-fallback-fix { color: #c4a373; }

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

/* Severity sets the edge, because the difference between a flood watch and a
   tornado warning is the whole message and reading it should not require
   reading the words. */
.storm-card {
  padding: 9px 11px; border-radius: var(--radius-sm); margin-bottom: 8px;
  border: 1px solid var(--line); border-left: 4px solid var(--storm-edge, var(--ink-3));
  background: var(--surface-2);
}
.storm-card.is-extreme { --storm-edge: #b3261e; }
.storm-card.is-severe  { --storm-edge: #d97706; }
.storm-card.is-moderate { --storm-edge: #b58900; }
.storm-event { font-size: .84rem; font-weight: 600; color: var(--ink); }
.storm-motion {
  display: flex; align-items: center; gap: 6px; margin-top: 3px;
  font-size: .78rem; color: var(--ink-2);
}
.storm-motion.is-quiet { color: var(--ink-3); font-size: .72rem; }
.storm-arrow { display: flex; color: var(--storm-edge, var(--ink-2)); }
.storm-area { font-size: .71rem; color: var(--ink-3); margin-top: 3px; }
.storm-expires { font-size: .71rem; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* ---- the galactic core ---- */

/* A night sky, because the thing being described is one. The wash carries the
   verdict before the numbers do: a good night is deep and clear, a poor one is
   washed out the way a moonlit sky actually is. */
.core-hero {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 11px 12px; border-radius: var(--radius-sm);
  background: linear-gradient(140deg, #10162b, #26315a 60%, #3c3a63);
  color: #eae7f5;
}
.core-hero.is-fair { background: linear-gradient(140deg, #1c2036, #3a3f63 60%, #5b5680); }
.core-hero.is-poor { background: linear-gradient(140deg, #34364a, #5c5d75 60%, #83829a); }
/* No cloud forecast yet, or none published here. Deliberately not the "good"
   wash: an unknown sky should not look like a clear one. */
.core-hero.is-unknown { background: linear-gradient(140deg, #232838, #3d4258 60%, #545a72); }
.core-headline { min-width: 0; }
.core-window {
  font-family: var(--font-serif); font-size: 1.16rem; line-height: 1.2;
  font-variant-numeric: tabular-nums;
}
.core-window-note { font-size: .71rem; color: #c2bdd8; margin-top: 2px; }
.core-peak { flex: none; text-align: right; }
.core-peak-value {
  font-family: var(--font-serif); font-size: 1.5rem; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.core-peak-note { font-size: .66rem; color: #c2bdd8; margin-top: 3px; }

.core-verdict {
  font-size: .68rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: #d8d3ec; margin-bottom: 3px;
}

/* The "why this time" box. Folded away because the card above answers "is
   tonight worth it" and this answers "what time do I set the alarm for" — a
   question you only ask once the first one comes back yes. */
.core-guide { margin-top: 11px; }
.core-guide-summary {
  display: flex; align-items: center; gap: 7px; cursor: pointer; list-style: none;
  padding: 7px 9px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--paper);
  font-size: .76rem; font-weight: 600; color: var(--ink-2);
}
.core-guide-summary::-webkit-details-marker { display: none; }
.core-guide-summary:hover { border-color: var(--line-strong); color: var(--ink); }
/* A mark, like every other button in the app carries, rather than a lone
   italic "i" in a grey disc that matched nothing else on the page. */
.core-guide-mark {
  flex: none; display: inline-flex; color: var(--clay);
}
.core-guide-mark svg { width: 15px; height: 15px; }
.core-guide[open] .core-guide-summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.core-guide-body {
  padding: 10px 9px; border: 1px solid var(--line); border-top: 0;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm); background: var(--paper);
}
.core-moment {
  display: grid; grid-template-columns: 78px 1fr; gap: 9px;
  padding: 6px 0; border-top: 1px solid var(--line);
}
.core-moment:first-child { border-top: 0; padding-top: 0; }
.core-moment-time {
  font-size: .74rem; font-weight: 600; color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.core-moment.is-primary .core-moment-time { color: var(--accent, var(--ink)); }
.core-moment.is-primary .core-moment-name { font-weight: 700; }
.core-moment-name { font-size: .76rem; color: var(--ink); }
.core-moment-why { font-size: .7rem; color: var(--ink-3); margin-top: 1px; }

.core-rows { display: grid; gap: 5px; margin-top: 11px; }
.core-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  font-size: .75rem;
}
.core-row-label { display: flex; align-items: center; gap: 6px; color: var(--ink-3); flex: none; }
.core-row-value {
  color: var(--ink-2); text-align: right; font-variant-numeric: tabular-nums;
}

/* ---------------- vehicle advisory ---------------- */

/*
 * Amber, and shaped like a notice rather than a card.
 *
 * The thing it is saying — that a clean route is not a checked route — has to
 * survive being read for the fortieth time on the fortieth trip, so it is not
 * dismissible and it does not fold away. It is amber rather than red because
 * red here would read as "this route is dangerous", which is not the claim:
 * the claim is that the router cannot see what it cannot see.
 */
.rv-advisory {
  margin-top: 11px; padding: 10px 11px;
  border: 1px solid color-mix(in srgb, var(--warn) 55%, transparent);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--warn) 9%, transparent);
}
.rv-advisory-head {
  display: flex; align-items: center; gap: 7px;
  font-size: .74rem; font-weight: 650; color: var(--ink);
}
.rv-advisory-mark { display: inline-flex; line-height: 0; color: var(--warn); }
.rv-advisory-mark svg { width: 15px; height: 15px; }
.rv-dims { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
.rv-dim {
  padding: 3px 8px; border-radius: 999px; font-size: .7rem;
  border: 1px solid var(--line); background: var(--paper);
  color: var(--ink-2); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.rv-caveat { margin: 8px 0 0; font-size: .74rem; line-height: 1.45; color: var(--ink-2); }

/* ---------------- fog ---------------- */

/*
 * Grey, all of it, and the band does the work.
 *
 * The other sky cards wash themselves in the colour of the thing they are
 * about - a night sky, an aurora. Fog has one colour and it is the absence of
 * colour, so the card leans on that instead of inventing a palette: the
 * likelihood is the only thing that changes, and it changes the depth of the
 * grey rather than its hue.
 */
.fog-hero {
  padding: 11px 12px; border-radius: var(--radius-sm);
  background: linear-gradient(140deg, #3f434a, #6b7078 60%, #8d939b);
  color: #f2f3f4;
}
.fog-hero:has(.is-no) { background: linear-gradient(140deg, #2c3138, #454b54 60%, #5b626c); }
.fog-hero:has(.is-unlikely) { background: linear-gradient(140deg, #363b43, #565c66 60%, #737a84); }
.fog-band {
  font-size: .67rem; font-weight: 650; letter-spacing: .08em;
  text-transform: uppercase; color: #dfe2e6;
}
.fog-headline {
  font-family: var(--font-serif); font-size: 1.16rem; line-height: 1.2; margin-top: 2px;
}
.fog-headline:empty { display: none; }
.fog-when { font-size: .78rem; color: #dfe2e6; margin-top: 3px; font-variant-numeric: tabular-nums; }

/*
 * An hour a block, and an hour with no answer is a gap rather than a zero.
 *
 * "The grid did not publish a dewpoint for this hour" and "this hour will be
 * clear" are different statements, and a strip that drew them the same way
 * would be the card's one chance to mislead: a run of clear-looking hours that
 * are actually unknown ones.
 */
.fog-strip-wrap { margin-top: 12px; }
.fog-strip { display: flex; gap: 1px; height: 15px; border-radius: 4px; overflow: hidden; }
.fog-block { flex: 1; background: var(--paper-3); }
.fog-block.is-no { background: var(--paper-3); }
.fog-block.is-unlikely { background: #b9c0c8; }
.fog-block.is-possible { background: #7d8894; }
.fog-block.is-likely { background: #4b545e; }
.fog-block.is-unknown {
  background: repeating-linear-gradient(135deg,
    var(--paper-3) 0 3px, transparent 3px 6px);
}

.core-marks { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; margin-top: 11px; }
.core-marks-label {
  width: 100%; font-size: .67rem; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-3);
}
.core-mark {
  padding: 3px 7px; border-radius: 999px; font-size: .69rem;
  border: 1px solid var(--line); background: var(--paper);
  color: var(--ink-2); font-variant-numeric: tabular-nums;
}

.core-nights { margin-top: 13px; padding-top: 11px; border-top: 1px solid var(--line); }
.core-nights-label {
  font-size: .67rem; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 5px;
}
.core-night {
  display: grid; grid-template-columns: 1fr auto auto auto; gap: 10px;
  padding: 3px 0; font-size: .75rem; font-variant-numeric: tabular-nums;
}
.core-night-date { color: var(--ink-2); }
.core-night-span { color: var(--ink); font-weight: 600; }
.core-night-moon { color: var(--ink-3); min-width: 62px; text-align: right; }
/* Blank for every night the forecast does not reach, which is most of them —
   an empty cell says "not known" more honestly than a dash would. */
.core-night-cloud { color: var(--ink-3); min-width: 58px; text-align: right; }
.core-night-cloud.is-clear { color: #2f7d5c; font-weight: 600; }

/* Which line on the map is which. Matches the line colours exactly, because a
   key whose colours are close but not equal is worse than none. */
.dir-dot {
  width: 9px; height: 9px; border-radius: 999px; flex: none;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .12);
}
.dir-dot.is-sun { background: #d87708; }
.dir-dot.is-moon { background: #3d6ea8; }
.dir-dot.is-core { background: #7b4fa8; }
.dir-dot.is-now { box-shadow: 0 0 0 2px var(--paper), 0 0 0 3.5px currentColor; }


/* ==========================================================================
   Account in the header
   ========================================================================== */

.account-drop {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  width: min(300px, calc(100vw - 24px)); padding: 13px 14px;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-md, 8px); box-shadow: var(--shadow-lg, 0 10px 30px rgba(0, 0, 0, .18));
  text-align: left;
}
.account-drop[hidden] { display: none; }

/* Units and display. One button, because distance and temperature are two
   different questions and a single mi/ft toggle only ever answered one. */
.settings-menu { position: relative; }
/*
 * Capped and scrollable, because this menu now carries the sign-in form as
 * well as three rows of choices - about 630px of it on a signed-out phone,
 * which is taller than a short landscape window.
 */
.settings-drop {
  width: min(268px, calc(100vw - 24px)); padding: 12px 13px;
  max-height: min(78vh, 640px); overflow-y: auto; overscroll-behavior: contain;
}
.settings-row + .settings-row { margin-top: 13px; }
.settings-label {
  font-size: .7rem; font-weight: 650; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 6px;
}
/* Sized to the row, not to two: the theme offers three. */
.settings-choices { display: grid; grid-template-columns: repeat(var(--choices, 2), minmax(0, 1fr)); gap: 6px; }
.settings-choice {
  font: inherit; font-size: .8rem; padding: 7px 6px; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink-2);
  min-height: 36px;
}
.settings-choice:hover { border-color: var(--line-strong); color: var(--ink); }
/* The theme row is three marks; the word is on the tooltip. */
.settings-choice svg { width: 18px; height: 18px; display: block; margin: 0 auto; stroke-width: 1.9; }
.settings-choice.is-on {
  border-color: transparent; background: var(--clay-soft); color: var(--clay-2); font-weight: 600;
}
/* The account, under the same button as the other preferences. */
.settings-account { margin-top: 14px; padding-top: 13px; border-top: 1px solid var(--line); }
.settings-account #account-panel { margin-top: -2px; }
/* Account errors quote a URL, and a URL does not break at spaces. */
#account-panel .hint { overflow-wrap: anywhere; }
/* One plan, named. The sentence about it is in the FAQ, not the menu. */
.plan-name { font-size: .9rem; font-weight: 600; color: var(--ink); }

/* ==========================================================================
   One "Open a file" block, and foldable detail sections
   ========================================================================== */

.files-block > summary,
.detail-block-summary {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  cursor: pointer; list-style: none;
}
.files-block > summary::-webkit-details-marker,
.detail-block-summary::-webkit-details-marker { display: none; }

.files-block > summary {
  padding: 9px 11px; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: .86rem; font-weight: 600;
}
.files-block > summary:hover { border-color: var(--line-strong); }
.files-block > summary::before { content: '+ '; color: var(--ink-3); }
.files-block[open] > summary::before { content: '– '; }
.files-block[open] > summary { margin-bottom: 11px; }

/*
 * The Details panel's own gutter.
 *
 * The folding sections are appended straight to the panel while the plain ones
 * are `.panel-section`, which carries its own 16px — so half the panel was
 * inset and half of it ran into the edge of the glass. The gutter belongs to
 * the column, not to each section, so it is set once here and the sections
 * inside it stop setting their own.
 */
#details-body { padding: 4px 16px 20px; }
#details-body .panel-section { padding-left: 0; padding-right: 0; }

/* Each detail section folds, and stays folded — the panel is eight sections
   long now and which of them matter is personal and stable. */
.detail-block {
  padding: 0 0 11px;
  border-bottom: 1px solid var(--line);
}
.detail-block:last-of-type { border-bottom: 0; }
.detail-block-summary {
  padding: 12px 0 8px;
  font-size: .74rem; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3);
}
.detail-block-summary:hover { color: var(--ink-2); }
/* The only thing telling you a section folds, so it is drawn to be seen. */
.detail-block-summary::after {
  content: ''; flex: none; width: 0; height: 0; margin-left: auto;
  border-left: 5px solid var(--ink-3);
  border-top: 4.5px solid transparent; border-bottom: 4.5px solid transparent;
  transition: transform .15s ease;
}
.detail-block-summary:hover::after { border-left-color: var(--ink-1); }
.detail-block[open] > .detail-block-summary::after { transform: rotate(90deg); }
.detail-block-summary .count { margin-left: 0; }

/* Shown only when the page is behind what is deployed, which is a state the
   reader can do something about — so it is a button, not a note. */
.build-newer {
  display: block; width: 100%; margin: 6px 0 0; padding: 6px 8px;
  font: inherit; font-size: .72rem; text-align: left; cursor: pointer;
  color: var(--ink-1); background: var(--surface-2, rgba(180, 68, 31, .1));
  border: 1px solid var(--line); border-radius: var(--radius-sm, 6px);
}
.build-newer:hover { border-color: var(--accent, #b4441f); }

/* One night, end to end. The ends are labelled because a slider with no scale
   on it is a guess, and the whole point is that this range has real edges. */
.scrubber { margin: 0 0 10px; }
.scrub-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  margin-bottom: 4px;
}
.scrub-label { font-size: .72rem; font-weight: 600; color: var(--ink-2); }
.scrub-time { font-size: .72rem; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.scrub-range { width: 100%; margin: 0; accent-color: #7b4fa8; }
/* Small, because it is a way back rather than the main control. */
.scrub-now {
  font: inherit; font-size: .66rem; padding: 1px 7px; cursor: pointer;
  color: var(--ink-2); background: transparent;
  border: 1px solid var(--line); border-radius: 999px;
}
.scrub-now:hover { border-color: #7b4fa8; color: var(--ink-1); }
.scrub-ends {
  display: flex; justify-content: space-between;
  font-size: .66rem; color: var(--ink-3); font-variant-numeric: tabular-nums;
}
/* The moments worth knowing while dragging, under the moment they name. The
   track is relative so a percentage lands where the slider thumb would. */
.scrub-marks { position: relative; list-style: none; margin: 2px 0 0; padding: 0; height: 22px; }
.scrub-mark { position: absolute; top: 0; transform: translateX(-50%); text-align: center; }
/*
 * A mark at either end keeps its tick where it belongs and moves its label in.
 *
 * The night's peak is often the first minute of astronomical dark, which put
 * "peak" centred on 0% — half of it off the left edge of the panel. The tick
 * has to stay on the moment it names; only the words move.
 */
.scrub-mark.at-start { transform: none; text-align: left; }
.scrub-mark.at-start .scrub-mark-tick { margin-left: 0; }
.scrub-mark.at-end { transform: translateX(-100%); text-align: right; }
.scrub-mark.at-end .scrub-mark-tick { margin-right: 0; }
.scrub-mark-tick {
  display: block; width: 1px; height: 4px; margin: 0 auto 1px;
  background: #7b4fa8; opacity: .7;
}
.scrub-mark-label { font-size: .6rem; color: var(--ink-3); white-space: nowrap; }

/* Five tabs where there were four. The glyph carries the recognition so the
   words can shrink instead of the row wrapping into two rows of unrelated
   buttons. */
.sky-tab { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.sky-tab-icon { display: block; line-height: 0; }
.sky-tab-icon svg { width: 16px; height: 16px; }
.sky-tab-text {
  font-size: .68rem;
  /* Four to a row in a 320px panel is about seventy pixels a tab, which "Milky
     Way" does not fit on one line. Wrapped rather than ellipsed: the grid rows
     equalise, so the second line costs the strip nothing, and a cut label is a
     label nobody can read. */
  max-width: 100%; white-space: normal; overflow-wrap: anywhere; line-height: 1.15;
}
@media (max-width: 360px) { .sky-tab-text { font-size: .62rem; } }


/* The download control under a saved region. The status sits beside the button
   rather than under it, so a running download does not reflow the list. */
.region-download { margin-top: 8px; }
.region-download .region-controls { align-items: center; gap: 10px; }
.region-download .source-note { margin: 0; }

/* Storage figure and the button that clears it, on one line until the panel is
   too narrow to hold both. */
.offline-storage {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 8px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line);
}
.offline-storage .source-note { margin: 0; }

/* An out-of-date download. Marked rather than removed, so it has to read as a
   note about ground you still have — not as an error about ground you lost. */
.region-stale {
  margin-top: 8px; padding: 8px 10px; border-radius: 8px;
  border: 1px solid var(--warn-line, var(--line));
  background: var(--warn-wash, color-mix(in srgb, var(--accent) 7%, transparent));
}
.region-stale-why {
  margin: 0 0 8px; font-size: .74rem; line-height: 1.45; color: var(--ink-2);
}
.region-stale .region-controls { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.region-stale-status {
  font-size: .72rem; color: var(--ink-3); font-variant-numeric: tabular-nums;
}


/* ==========================================================================
   The table editor
   --------------------------------------------------------------------------
   Over everything, because editing a hundred waypoints is the whole job while
   it is happening - not something done in a column beside a map. The panel is
   for one pin at a time; this is for the set.
   ========================================================================== */

.table-editor {
  position: fixed; inset: 0; z-index: 60;
  display: flex; flex-direction: column;
  background: var(--surface); color: var(--ink);
  /* Under the notch and above the home bar on a phone. */
  padding: max(10px, env(safe-area-inset-top)) max(10px, env(safe-area-inset-right))
    max(10px, env(safe-area-inset-bottom)) max(10px, env(safe-area-inset-left));
  gap: 9px;
}
.table-editor[hidden] { display: none; }
/* The map keeps running behind it; stopping the page scrolling stops the
   table's own scroller handing its overflow to the document on iOS. */
body.has-table { overflow: hidden; }

.table-head { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; justify-content: space-between; }
.table-title { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.table-title h2 { margin: 0; font-size: 1.02rem; font-weight: 650; }
.table-tools { display: flex; gap: 7px; align-items: center; flex: 1 1 320px; justify-content: flex-end; }
.table-search { flex: 1 1 180px; min-width: 120px; }
.table-editor input,
.table-editor select {
  font: inherit; font-size: .84rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm, 6px); padding: 5px 7px; min-width: 0;
}
.table-editor input:focus-visible,
.table-editor select:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }

/* Always present, so ticking a row does not shift the table under the cursor. */
.table-bulk {
  display: flex; flex-wrap: wrap; gap: 7px; align-items: center;
  padding: 7px 9px; border-radius: var(--radius-md, 8px);
  background: var(--paper-2, rgba(0, 0, 0, .04)); border: 1px solid var(--line);
}
.table-bulk.is-idle { color: var(--ink-3); font-size: .84rem; }
.table-bulk-count { font-size: .84rem; font-weight: 600; }

.table-scroll { flex: 1 1 auto; overflow: auto; -webkit-overflow-scrolling: touch; }
.table-grid { width: 100%; border-collapse: collapse; font-size: .84rem; }
.table-grid th {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface); text-align: left; font-weight: 650;
  padding: 6px 7px; border-bottom: 1px solid var(--line-strong); white-space: nowrap;
}
.table-grid td { padding: 4px 7px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.table-grid tr.is-picked td { background: var(--paper-2, rgba(0, 0, 0, .05)); }
.table-tick { width: 30px; }
.table-tick input { width: 17px; height: 17px; padding: 0; }
.table-mark { width: 34px; }
/* The pin as the map draws it, at the size a row can hold. */
.table-pin {
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: #fff; border: 2.4px solid var(--pin);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .25);
}
.table-pin svg { display: block; }
.table-name { width: 100%; min-width: 130px; }
.table-note { width: 100%; min-width: 150px; }
.table-link { width: 100%; min-width: 140px; }
.table-symbol, .table-color, .table-folder { max-width: 150px; }

/* A heading you can press, that still looks like a heading. */
.table-sort {
  font: inherit; font-weight: 650; color: inherit;
  background: none; border: 0; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; gap: 2px; white-space: nowrap;
}
.table-sort:hover { text-decoration: underline; text-underline-offset: 3px; }
.table-sort.is-on { color: var(--accent, #b4441f); }
.table-sort:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: 3px; }

.table-filter-symbol, .table-filter-color, .table-filter { max-width: 200px; }

.table-foot { display: flex; gap: 10px; align-items: center; justify-content: center; }

/* A phone cannot show eight columns at once, so the table keeps its width and
   scrolls sideways rather than folding into something unreadable. */
@media (max-width: 720px) {
  .table-grid { min-width: 760px; }
  .table-title h2 { font-size: .95rem; }
}
