:root {
  --bg: #0d1018; --panel: #11151f; --panel2: #1b2030; --border: #2a3346;
  --text: #e8ecf6; --muted: #828ca3; --accent: #4d6bff;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", Menlo, Consolas, monospace;
  /* day-overview column template - one source of truth for the five coupled grid rules */
  --tlc1: 92px; --tlc3: 78px;
  /* accent lifted for SMALL text on dark surfaces (raw --accent is ~4.2:1 there) */
  --accent-ink: #8b9dff;
  /* day-overview meeting-interval tint - pale amber, so it reads against the blue UI */
  --meet-tint: #ffcd6e;
  color-scheme: dark;
}
:root[data-theme="light"] {
  --bg: #f4f6fb; --panel: #ffffff; --panel2: #eef1f7; --border: #d6dde9;
  --text: #1a2230; --muted: #5a6478; --accent: #3552e6;
  --accent-ink: #3552e6; /* already AA on light surfaces */
  --meet-tint: #d8932a; /* deeper amber - the pale one washes out on white panels */
  color-scheme: light;
}
* { box-sizing: border-box; }
/* Keyboard focus ring (SC 2.4.7). --accent-ink clears 3:1 against every surface in BOTH themes
   (5.3-7.6:1), so one colour covers the whole app. Controls that sit inside an overflow:hidden
   ancestor need a NEGATIVE offset - an outside ring is clipped away by the ancestor and vanishes. */
:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
.trow .tin:focus-visible, .trow .tinbtn:focus-visible, .offchip:focus-visible,
.zlist li:focus-visible, .pitem :focus-visible, .meetbar.on :focus-visible,
.tabbar .tab:focus-visible { outline-offset: -2px; } /* the tab bar sits flush on #app's clipped edges */
/* Sheets and popovers take tabIndex -1 only so Tab order and Escape start inside them. Opened from
   the keyboard, :focus-visible still matches and rings the whole container, which reads as if the
   sheet itself were the focused control. The controls inside keep their own rings. */
.datepop:focus-visible, .modal:focus-visible { outline: none; }
.timeslider:focus-visible { outline-color: #fff; } /* its pill stays dark in both themes */
html, body { height: 100%; }
body { margin: 0; background: var(--bg); color: var(--text); overflow: hidden;
  font-family: -apple-system, "Segoe UI", Roboto, sans-serif; }
#app { width: 100%; height: 100vh; height: 100dvh; height: var(--app-h, 100dvh); padding: 12px;
  padding-left: max(12px, env(safe-area-inset-left)); padding-right: max(12px, env(safe-area-inset-right));
  display: flex; flex-direction: column; gap: 11px; overflow: hidden; }
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 14px; padding: 12px; }
.bar { display: flex; align-items: center; justify-content: space-between; gap: 14px; position: relative; min-height: 38px; }
.barleft { display: flex; align-items: center; gap: 14px; min-width: 0; }
.barright { display: flex; align-items: center; gap: 6px; }
.toggles { display: flex; gap: 6px; }
/* top-bar responsive collapse: search toggle + Go-live icon are hidden until it's tight */
.barright .searchtoggle { display: none; }
.livebtn .liveico { display: none; align-items: center; }
.livebtn .liveico .ico { width: 18px; height: 18px; display: block; }
.setdisp { display: flex; gap: 8px; }
.setdisp button { flex: 1; padding: 8px 0; border-radius: 8px; background: var(--panel2); border: 1px solid var(--border); color: var(--text); cursor: pointer; font-size: 13px; }
.setdisp button:hover { border-color: var(--accent); color: var(--accent-ink); }
.barright .toggles { display: none; } /* theme + 12/24h live in the cog menu at every width */
@media (max-width: 520px) {
  .bar .searchbar { display: none; }                                       /* search collapses to an icon… */
  .barright .searchtoggle { display: inline-flex; }
  .bar.searchopen .searchbar { display: flex; position: absolute; left: 6px; right: 6px; top: calc(100% + 6px); transform: none; width: auto; z-index: 40;
    background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 8px; box-shadow: 0 10px 28px #0007; }
}
@media (max-width: 430px) {                                                /* Go-live → icon only */
  .livebtn { min-width: 0; padding: 0 9px; }
  .livebtn .livetxt, .livebtn .dot { display: none; }
  .livebtn .liveico { display: inline-flex; }
}
.layout { display: grid; grid-template-columns: clamp(210px, 22%, 300px) 1fr; gap: 12px; align-items: stretch; flex: 1 1 auto; min-height: 0; }
.railcol { display: flex; flex-direction: column; gap: 11px; min-height: 0; }
.railcol > .rail { flex: 1 1 auto; }
.datebar { display: block; }
/* stacked (map narrower than a square): one column, map BELOW the tiles, whole thing scrolls */
.layout.stacked { grid-template-columns: 1fr; align-items: start; overflow-y: auto; }
.layout.stacked .rail { overflow: visible; }
.layout.stacked .mapPanel { aspect-ratio: 3 / 2; min-height: 220px; }
@media (max-width: 720px) { .layout { grid-template-columns: 1fr; } }
.mapPanel { position: relative; padding: 0; overflow: hidden; min-height: 200px; }
.timeline { position: relative; }
#mapCanvas { display: block; width: 100%; height: 100%; }
.mapLayer { position: absolute; inset: 0; pointer-events: none; }
/* zoom: city-name labels that fade in (biggest first), and the discreet zoom controls */
.maplabels { position: absolute; inset: 0; pointer-events: none; z-index: 4; overflow: hidden; }
.mlab { position: absolute; transform: translate(-50%, -50%); font-size: 9px; font-weight: 600; color: #fff; white-space: nowrap; text-shadow: 0 0 3px #000, 0 1px 2px #000; }
/* Country names (countryHtml in js/map.js): a quiet cartographic annotation UNDER the city labels.
   Uppercase + tracked so it reads as a different CLASS of thing at a glance rather than by size, and
   so it never competes with a mixed-case city name sitting next to it. Both colours come from the map
   palette (PAL.ctry / PAL.ctryHalo) through two custom properties the map writes on .maplabels once
   per theme - so this rule holds no colour of its own.
   Deliberately 9px on BOTH shells, unlike .mlab's 9/11: the half-widths the collision test runs on are
   measured ONCE at load, so a size that changed with the breakpoint would leave every box stale after
   a resize across it. It also keeps the city labels the larger of the two on mobile, which is the
   priority order the whole feature is built around. */
.mctry { position: absolute; transform: translate(-50%, -50%); font-size: 9px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ctry); white-space: nowrap; text-shadow: 0 0 3px var(--ctryhalo), 0 0 2px var(--ctryhalo); }
.mapzoom { position: absolute; right: 10px; top: 24px; display: none; flex-direction: column; gap: 5px; z-index: 6;
  transition: opacity .14s ease; }
.mapzoom.show { display: flex; }
/* a city card in this corner takes the space: the column steps aside rather than shoving the card
   sideways past where its arrow can still reach the dot (see peekAt) */
.mapzoom.duck { opacity: 0; pointer-events: none; }
.mz { width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  background: color-mix(in srgb, var(--panel) 82%, transparent); border: 1px solid var(--border); color: var(--text);
  border-radius: 7px; line-height: 0; padding: 0; }
.mz:hover { background: var(--panel2); border-color: var(--accent); color: var(--accent); }
.mz svg { width: 15px; height: 15px; display: block; }
.tabnum { font-variant-numeric: tabular-nums; }
.phead { margin: 2px 3px 9px; font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.phead-row { display: flex; align-items: center; justify-content: space-between; }
.clearcities { background: none; border: none; padding: 0; font: inherit; letter-spacing: .04em; text-transform: none;
  color: var(--muted); opacity: .8; cursor: pointer; }
.clearcities:hover { color: var(--danger, #e5686b); opacity: 1; }
.stclear { width: 100%; padding: 9px 11px; border-radius: 10px; background: var(--panel2); border: 1px solid var(--border);
  color: var(--danger, #e5686b); font-size: 12.5px; font-weight: 600; cursor: pointer; white-space: nowrap; }
.stclear:active { filter: brightness(1.2); }
.rail { padding: 11px; overflow: auto; min-height: 0; }
.trow { position: relative; display: grid; grid-template-columns: 1fr auto auto; gap: 6px; align-items: center;
  background: var(--panel2); border: 1px solid var(--border); border-left: 4px solid var(--border);
  border-radius: 10px; padding: 8px 9px; margin: 6px 0; }
.trow.home { background: color-mix(in srgb, var(--accent) 16%, var(--panel2)); border-color: var(--accent); }
.trow .hm { position: absolute; top: -7px; left: 9px; z-index: 2; font-size: 8px; background: color-mix(in srgb, var(--accent) 78%, #000); color: #fff; padding: 1px 6px; border-radius: 5px; }
.trow .nm { min-width: 0; }
.trow .nm { min-width: 0; overflow: hidden; }
.trow .nm b { display: block; font-size: 13px; line-height: 1.15; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trow .nm span { display: block; font-size: 9.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trow .nm .hd { color: color-mix(in srgb, var(--accent) 42%, var(--text)); font-weight: 600; } /* offset from Home (AA on panel2) */
.trow .tin { width: 86px; background: transparent; border: 1px solid transparent; color: var(--text);
  font-size: 17px; font-weight: 700; text-align: right; border-radius: 6px; padding: 2px 4px; }
.trow .tin:hover, .trow .tin:focus { border-color: var(--border); background: var(--bg); }
/* tile time as a button (coarse-pointer devices) - reads like the bold time, opens the clock picker */
.trow .tinbtn { width: auto; min-width: 0; background: transparent; border: 1px solid transparent;
  color: var(--text); font-family: var(--mono); font-weight: 700; font-size: 17px; text-align: right; padding: 2px 4px; cursor: pointer; }
.trow .tinbtn:active { background: var(--bg); border-color: var(--border); }
.meet-dur.meetdurbtn { cursor: pointer; }
.trow .dt { font-size: 10px; color: var(--muted); font-family: var(--mono); font-variant-numeric: tabular-nums; text-align: right; min-width: 6ch; }
.addz { width: 100%; text-align: center; font-size: 12px; color: var(--muted);
  border: 1px dashed var(--border); background: none; border-radius: 10px; padding: 8px; margin-top: 7px; cursor: pointer; }
.dnav { position: relative; display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; color: var(--text); }
.nav { width: 26px; height: 26px; border-radius: 8px; background: var(--panel2); border: 1px solid var(--border); color: var(--muted); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-size: 16px; line-height: 1; padding: 0; }
.dnav .nav { width: 30px; height: 30px; border-radius: 50%; background: var(--panel2); border: 1px solid var(--border); color: var(--muted); flex: none; }
.dnav .nav:hover { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--panel2)); }
.dnav .nav .chev { width: 16px; height: 16px; display: block; }
.dnav .datelabel { flex: 1 1 auto; min-width: 0; padding: 2px 6px; overflow: hidden; }
.datelabel .dtext { display: inline-block; white-space: nowrap; font-weight: 700; line-height: 1.05; }
.logo { position: relative; display: inline-block; height: 1em; overflow: visible; white-space: nowrap; vertical-align: middle;
  font-family: "Helvetica Neue", "Segoe UI", system-ui, -apple-system, Roboto, Arial, sans-serif; font-weight: 800; font-size: 28px; line-height: 1; letter-spacing: .005em;
  color: var(--accent); user-select: none; cursor: pointer; transition: width .24s cubic-bezier(.4, 0, .2, 1); }
/* sky-of-the-day gradient (same palette as the day overview), panning horizontally forever */
.logo > span { position: absolute; left: 0; top: 0; transition: opacity .18s ease;
  background-image: linear-gradient(90deg, rgb(48,72,128), rgb(56,86,146) 12%, rgb(72,114,180) 24%, rgb(112,164,222) 38%, rgb(180,214,250) 47%, rgb(240,248,255) 50%, rgb(180,214,250) 53%, rgb(112,164,222) 62%, rgb(72,114,180) 76%, rgb(56,86,146) 88%, rgb(48,72,128));
  background-size: 200% 100%; -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent; animation: skypan 6s linear infinite; }
@keyframes skypan { from { background-position: 0% 50%; } to { background-position: 200% 50%; } }
/* the clock is the "o" of "Zone" (its stroke uses the same shimmering sky gradient
   as the letters); it smoothly morphs (position + size, set inline by JS) into the
   standalone icon when collapsed */
.logo .zo { display: inline-block; width: .78em; }
.logo .clk { position: absolute; transition:
  left .34s cubic-bezier(.4, 0, .2, 1), top .34s cubic-bezier(.4, 0, .2, 1),
  width .34s cubic-bezier(.4, 0, .2, 1), height .34s cubic-bezier(.4, 0, .2, 1); }
.logo.collapsed .logofull { opacity: 0; }
.trow.home .secring { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
/* faint always-on track + a bright glowing "comet head" that sweeps once per minute - reads as a
   deliberate live indicator instead of a partly-drawn (broken-looking) border */
.trow.home .sectrack { fill: none; stroke: color-mix(in srgb, var(--accent) 26%, transparent); stroke-width: 2.5; }
.trow.home .secpath { fill: none; stroke: color-mix(in srgb, #fff 35%, var(--accent)); stroke-width: 3; stroke-linecap: round;
  filter: drop-shadow(0 0 4px var(--accent)); transition: opacity .3s ease; }
@media (prefers-reduced-motion: reduce) { .trow.home .secpath { display: none; } }
.datelabel { background: none; border: none; color: var(--text); font-family: var(--mono); font-size: 13px; font-variant-numeric: tabular-nums; cursor: pointer; padding: 2px 6px; border-radius: 7px; min-width: 11ch; text-align: center; }
.datelabel:hover { background: var(--panel2); }
.datepick { position: absolute; left: 34px; bottom: -2px; width: 1px; height: 1px; opacity: 0; pointer-events: none; border: 0; padding: 0; }
.homelabel { font-size: 12px; color: var(--muted); }
.homelabel b { color: var(--text); }
.spacer { flex: 1; }
.livebtn { display: inline-flex; align-items: center; justify-content: center; gap: 5px; min-width: 88px;
  font-size: 12px; border: 1px solid var(--border); background: var(--panel2); color: var(--muted); padding: 5px 10px; border-radius: 8px; cursor: pointer; }
.livebtn .dot { font-size: 9px; line-height: 1; }
.livebtn.on { color: #3ec97a; border-color: #2f6b4a; background: #16271d; }
:root[data-theme="light"] .livebtn.on { color: #1f8a4c; border-color: #9ad9b3; background: #e3f6ea; }
.setbtn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0;
  background: none; border: none; color: var(--muted); cursor: pointer; transform-origin: 50% 50%; transition: transform .45s ease; }
.setbtn:hover { color: var(--text); transform: rotate(90deg); }
.setbtn .ico { width: 20px; height: 20px; display: block; }
.overlay { position: fixed; inset: 0; background: #0008; display: grid; place-items: center; z-index: 50; }
.modal { background: var(--panel); border: 1px solid var(--border); border-radius: 14px; padding: 18px; width: 280px; display: flex; flex-direction: column; gap: 12px; }
.modal.compact { width: 252px; padding: 16px 18px; gap: 10px; }
.modal.compact h3 { margin: 0; font-size: 15px; }
.modal.compact input { padding: 5px 8px; } /* a touch roomier than the shared 4px 7px */
/* tile edit/add menu: tight header with a single ✕ close, no button row */
/* max-height, because the overlay CENTRES what it cannot scroll: a person section with trips open
   is ~230px taller than a plain one, and on a 720px laptop that pushed the Remove button off the
   TOP of the screen with no way back to it. The mobile block gives this dialog its own height. */
.modal.zedit { padding: 14px 16px; gap: 10px; max-height: calc(var(--app-h, 100vh) - 28px); overflow-y: auto; }
.modal.zedit .zhead { display: flex; align-items: center; justify-content: space-between; }
.modal.zedit h3 { margin: 0; font-size: 15px; }
.zclose { width: 26px; height: 26px; border: none; border-radius: 7px; padding: 0; cursor: pointer; background: none;
  color: var(--muted); display: inline-flex; align-items: center; justify-content: center; transition: background .14s ease, color .14s ease; }
.zclose:hover { background: var(--panel2); color: var(--text); }
.zclose svg { width: 15px; height: 15px; display: block; }
.modal label { display: flex; justify-content: space-between; align-items: center; font-size: 13px; gap: 10px; }
.modal select, .modal input { background: var(--bg); border: 1px solid var(--border); color: var(--text); border-radius: 7px; padding: 4px 7px; }
.modalbtns { display: flex; justify-content: flex-end; }
.modalbtns button { background: var(--accent); color: #fff; border: none; border-radius: 8px; padding: 6px 14px; cursor: pointer; }
.timeline { }
.tlrow { display: grid; grid-template-columns: var(--tlc1) 1fr var(--tlc3); gap: 10px; align-items: center; margin: 2px 0; }
.bandvp { position: relative; height: 15px; border-radius: 5px; }
/* clip only when multi-day (so single-day marker caps + edge hour labels can still spill) */
.bandvp.clip { overflow: hidden; }
/* extend the pill outward (negative margin) while padding the content back in, so the
   band columns stay aligned with the other rows but the text isn't flush to the edges */
.tlrow.home { margin: 4px -7px; padding: 4px 7px; background: color-mix(in srgb, var(--accent) 13%, transparent); border-radius: 10px; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent); }
.tlrow .nm { font-size: 11px; color: var(--text); }
.tlrow.home .nm { color: var(--accent-ink); font-weight: 700; }
.band { position: relative; height: 15px; border-radius: 5px; overflow: hidden; cursor: pointer; }
.band { cursor: ew-resize; }
.suitedge { display: grid; grid-template-columns: var(--tlc1) 1fr var(--tlc3); gap: 10px; margin: 2px 0; }
/* the Day Overview panel runs tight top/bottom so the bands aren't lost in padding */
.timeline { padding-top: 7px; padding-bottom: 4px; }
.timeline .phead { margin-bottom: 5px; }
.suitedge .line { height: 3px; border-radius: 2px; }
.locktoggle { position: absolute; top: 7px; right: 12px; z-index: 2; text-transform: none; letter-spacing: 0;
  display: inline-flex; align-items: center; justify-content: center; height: 24px; box-sizing: border-box;
  background: var(--panel2); border: 1px solid var(--border); color: var(--muted);
  font-size: 14px; line-height: 1; padding: 0 4px; border-radius: 6px; cursor: pointer; }
.locktoggle:hover { background: var(--border); color: var(--text); }
/* SEARCH MODE (desktop): the bar is position:FIXED in both states so `top`/`width` animate cleanly
   between the top bar and the centre of the screen - one element travelling, never a swap. Its
   resting position comes from --searchtop, measured from the real bar by js/search.js. */
.searchbar { position: fixed; left: 50%; top: var(--searchtop, 31px); transform: translate(-50%, -50%); z-index: 20;
  width: min(480px, 40vw); display: flex; align-items: center; justify-content: center; gap: 8px;
  transition: top .42s cubic-bezier(.3, .8, .3, 1), width .42s cubic-bezier(.3, .8, .3, 1); }
body.searching .searchbar { top: 38vh; width: min(620px, 86vw); z-index: 60; }
body.searching .qinput { font-size: 19px; padding-top: 15px; padding-bottom: 15px;
  border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
  box-shadow: 0 22px 70px rgba(0, 0, 0, .55), 0 0 0 4px color-mix(in srgb, var(--accent) 16%, transparent); }
/* the backdrop dims + blurs everything behind the field */
.searchveil { position: fixed; inset: 0; z-index: 55; background: rgba(6, 9, 16, .55);
  backdrop-filter: blur(9px) saturate(.75); -webkit-backdrop-filter: blur(9px) saturate(.75);
  opacity: 0; pointer-events: none; transition: opacity .42s ease; }
body.searching .searchveil { opacity: 1; pointer-events: auto; } /* click anywhere on it to exit */
body.searching #qdrop { z-index: 61; }
.searchhint { position: fixed; left: 50%; top: calc(38vh + 62px); transform: translateX(-50%); z-index: 60;
  font-size: 12.5px; color: var(--muted); opacity: 0; transition: opacity .3s ease .08s; pointer-events: none;
  white-space: nowrap; }
body.searching .searchhint { opacity: 1; }
/* the answer/suggestions drop into that same space - the hint yields to them */
body.searching:has(#qdrop.open) .searchhint { opacity: 0; }
.searchhint kbd { font: 11px var(--mono); background: var(--panel2); border: 1px solid var(--border);
  border-radius: 5px; padding: 1px 5px; color: var(--text); }
@media (prefers-reduced-motion: reduce) {
  .searchbar, .searchveil, .searchhint { transition: none; }
}
.qwrap { position: relative; flex: 1 1 auto; min-width: 0; display: flex; }
.qinput { flex: 1 1 auto; min-width: 0; background: var(--panel2); border: 1px solid var(--border);
  color: var(--text); font-size: 14px; padding: 8px 34px 8px 13px; border-radius: 10px; }
.qclear { position: absolute; right: 7px; top: 50%; transform: translateY(-50%); width: 19px; height: 19px;
  border: none; border-radius: 50%; padding: 0; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  background: var(--border); color: var(--muted); transition: background .14s ease, color .14s ease; }
.qclear:hover { background: var(--muted); color: var(--panel); }
.qclear svg { width: 11px; height: 11px; display: block; }
.qclear[hidden] { display: none; }
.qinput:focus { border-color: var(--accent); background: var(--bg); }
.qdrop { position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
  width: min(520px, 92vw); background: var(--panel); border: 1px solid var(--border); border-radius: 11px;
  box-shadow: 0 12px 34px #0008; padding: 5px; display: none; z-index: 45; }
.qdrop.open { display: block; }
.qres { font-size: 13.5px; padding: 6px 9px; }
.qres b { font-variant-numeric: tabular-nums; }
.qres.bad { color: #e1816f; }
.qres .muted { color: var(--muted); }
.qres .qco { color: var(--muted); font-weight: 400; } /* faded ", Country" confirming the resolved city */
.qsug { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 7px 9px;
  border-radius: 7px; cursor: pointer; font-size: 13px; }
.qsug:hover, .qsug.first { background: var(--panel2); }
.qsug .tab { font-size: 9px; color: var(--muted); border: 1px solid var(--border); border-radius: 4px; padding: 0 4px; }
/* live "it is X there" preview for a bare place. Sits above the suggestions and, unlike them, is an
   answer as well as an action - so it keeps the .qres type and gains a tappable Add affordance. */
.qnow { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.qnow.qadd { cursor: pointer; }
.qnow.qadd:hover, .qnow.qadd:active { background: var(--panel2); }
.qnow .qtag { flex: none; font-size: 10px; font-weight: 700; color: var(--accent-ink);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); border-radius: 5px; padding: 1px 6px; }
.iconbtn { display: inline-flex; align-items: center; justify-content: center; background: var(--panel2);
  border: 1px solid var(--border); color: var(--text); font-size: 13px; min-width: 32px; height: 30px; padding: 0 8px; border-radius: 8px; cursor: pointer; }
.iconbtn:hover { background: var(--border); }
.iconbtn .ico { width: 19px; height: 19px; display: block; }
/* share popover: copy link / copy times as text / download .ics */
.sharepop { position: fixed; z-index: 51; background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 12px 34px #0008; padding: 6px; display: flex; flex-direction: column; gap: 2px;
  /* ten rows now: at 200% zoom or in a short window the tail (Outlook/.ics/Zoom) must scroll into
     reach rather than fall off the bottom - the cap is not only the mobile sheet's problem */
  max-height: calc(var(--app-h, 100dvh) * 0.85); overflow-y: auto; }
.shopt { display: flex; align-items: center; gap: 9px; background: none; border: none; color: var(--text);
  font-size: 13px; padding: 8px 12px 8px 9px; border-radius: 8px; cursor: pointer; text-align: left; }
.shopt:hover { background: var(--panel2); }
.shopt .ico { width: 17px; height: 17px; flex: none; opacity: .75; }
.shopt.ok { color: #3ec97a; }
:root[data-theme="light"] .shopt.ok { color: #1f8a4c; }
/* share menu section headers (Copy / Calendar / Video) - visual grouping only, aria-hidden: the
   buttons' own labels already say enough for assistive tech. Same muted small-caps as .seclabel. */
.shgrp { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  padding: 8px 9px 2px; }
.shgrp:first-child { padding-top: 2px; }
.locktoggle .ico { width: 16px; height: 16px; display: block; }
.marker { position: absolute; top: -3px; bottom: -3px; width: 2px; background: #fff; box-shadow: 0 0 5px #fff; transition: left .08s ease; }
/* press-and-hold activation pulse: the marker flares to signal interval-draw mode is armed */
.marker.activating { animation: mkactivate .4s ease-out; }
@keyframes mkactivate {
  0% { box-shadow: 0 0 5px #fff; transform: scaleX(1); }
  35% { box-shadow: 0 0 4px 3px color-mix(in srgb, var(--accent) 80%, #fff), 0 0 12px 6px color-mix(in srgb, var(--accent) 55%, transparent); transform: scaleX(2.4); }
  100% { box-shadow: 0 0 5px #fff; transform: scaleX(1); }
}
/* unlocked meeting-mode clear button - a compact pill by the header */
/* same top + height as .locktoggle so the pill and the padlock share one centre line */
.meetclear { position: absolute; top: 7px; right: 42px; display: inline-flex; align-items: center; gap: 4px;
  height: 24px; box-sizing: border-box;
  background: color-mix(in srgb, var(--accent) 14%, var(--panel2)); border: 1px solid color-mix(in srgb, var(--accent) 34%, var(--border));
  color: var(--accent-ink, var(--accent)); font-size: 11px; font-weight: 700; padding: 0 10px; border-radius: 999px; cursor: pointer; line-height: 1; }
.meetclear svg { width: 11px; height: 11px; }
.meetclear:hover { background: color-mix(in srgb, var(--accent) 22%, var(--panel2)); }
/* per-row local-hour ticks shown inside each band when rows are locked */
.band:has(.hticks) { overflow: visible; } /* let edge hour numbers spill into the gaps instead of clipping */
.band .hticks { position: absolute; inset: 0; pointer-events: none; }
.band .ht { position: absolute; top: 1px; bottom: 1px; width: 1px; margin-left: -0.5px; background: rgba(255,255,255,.16); }
.band .ht.maj { top: 0; bottom: 0; width: auto; margin-left: 0; transform: translateX(-50%); background: none; display: flex; align-items: center; justify-content: center; font-size: 13px; line-height: 1; font-weight: 700; color: rgba(255,255,255,.95); font-variant-numeric: tabular-nums; text-shadow: 0 0 2px rgba(0,0,0,.6); }
.tlhours.hidden { visibility: hidden; } /* locked mode has per-row ticks, but KEEP the row's space so
   locked and unlocked are pixel-identical - only the colourbar differs */
.tlrow .lt { font-size: 11px; text-align: right; }
.tlrow .lt .dd { font-size: 9px; color: var(--muted); margin-left: 1px; }
.tlhours { display: grid; grid-template-columns: var(--tlc1) 1fr var(--tlc3); gap: 10px; margin-top: 3px; }
.tlhours .h { display: flex; justify-content: space-between; font-size: 8.5px; color: var(--muted); }
/* Cities / Countries / Time zones. Reuses .segrow + .seg (the spill-preference control in
   js/tiles.js) rather than inventing a second segmented control. min-width: 0 because .seg is
   nowrap: at a large accessibility font the three labels shrink instead of splitting the sheet. */
.ztabs { margin-bottom: -2px; }
.ztabs .seg { min-width: 0; }
/* Edit mode only: the tile as a person (name, attendance, their own good hours). It sits ABOVE the
   tabs because it describes the row you are already editing, while everything below re-points it. */
.zperson { display: grid; gap: 8px; }
.zperson .zname { width: 100%; box-sizing: border-box; }
.zperson .segrow .seg { min-width: 0; }
.zperson .hrow { margin: 1px 0 2px; }
/* the only labelled row in the section: "Board | Sat+Sun | Fri+Sat | Sun | None" does not say
   "weekend" on its own, the way Required/Optional and Board hours/Custom say what they are */
.zperson .advlab { margin: 2px 0 -3px; }
.zwknd .seg { padding: 0 2px; font-size: 11px; } /* five segments in a 280px dialog */
/* Traveling: a disclosure (its toggle borrows .ovltoggle, the Advanced panel's own) over up to four
   dated stays. One field per line: the dialog is 280px, and two native date inputs side by side in
   it clip their own year - the field would show "28/07/202". The remove button spans both dates,
   which is also what gives it a touch-sized box for nothing. */
.ztrav { margin-top: 1px; }
/* bounded like .zlist is, and for the same reason: four open trips are ~380px of dialog, and the
   overlay centres a modal it cannot scroll - the Remove button would go off the top of a laptop */
.ztrips:not(:empty) { margin-top: 7px; display: grid; gap: 7px; max-height: 232px; overflow: auto; }
.ztrip { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 5px 6px; align-items: center; }
.ztrip + .ztrip { padding-top: 7px; border-top: 1px solid var(--border); }
.ztrip .ztfrom { grid-column: 1; grid-row: 1; }
.ztrip .ztto { grid-column: 1; grid-row: 2; }
.ztrip .ztdel { grid-column: 2; grid-row: 1 / 3; height: 100%; }
.ztrip .ztdest, .ztrip .ztq { grid-column: 1 / 3; grid-row: 3; }
.ztrip .ztsug { grid-column: 1 / -1; grid-row: 4; }
.ztrip input { width: 100%; min-width: 0; box-sizing: border-box; font-size: 12px; }
/* held, not stored: a row is written only once both dates and a destination are set (tripRowValid) */
.ztrip.pending { opacity: .82; }
.ztrip.pending .ztq { border-style: dashed; }
.ztdest { font-size: 12px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ztdel { width: 24px; height: 24px; padding: 0; line-height: 1; background: none; border: 1px solid var(--border);
  border-radius: 7px; color: var(--muted); font-size: 14px; cursor: pointer; }
.ztdel:hover { color: var(--text); border-color: #e1503f; }
.ztadd { justify-self: start; background: none; border: 1px dashed var(--border); border-radius: 8px;
  color: var(--accent-ink); font: inherit; font-size: 11.5px; font-weight: 600; padding: 4px 10px; cursor: pointer; }
.ztadd:hover { border-color: var(--accent); border-style: solid; }
/* the destination autocomplete: the .zlist row shape at a smaller scale, in flow under its row
   (an absolutely positioned dropdown would be clipped by the sheet on a phone) */
.ztsug { list-style: none; margin: 0; padding: 0; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg); max-height: 156px; overflow: auto; }
.ztsug li { display: flex; align-items: baseline; gap: 7px; padding: 5px 9px; font-size: 12px;
  cursor: pointer; border-bottom: 1px solid var(--border); }
.ztsug li:last-child { border-bottom: 0; }
.ztsug li:hover, .ztsug li.on { background: var(--panel2); }
.ztsug .zc { font-weight: 600; white-space: nowrap; }
.ztsug .zmeta { flex: 1; min-width: 0; font-size: 10px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; }
.zlist .znone { display: block; padding: 14px 12px; font-size: 12.5px; color: var(--muted);
  cursor: default; border-bottom: 0; text-align: center; }
.zlist .znone:hover { background: none; }
.zlist { list-style: none; margin: 0; padding: 0; max-height: 260px; overflow: auto; border: 1px solid var(--border); border-radius: 8px; }
.zlist li { display: flex; align-items: baseline; gap: 8px; padding: 7px 10px; font-size: 13px; cursor: pointer; border-bottom: 1px solid var(--border); }
.zlist li:hover { background: var(--panel2); }
.zlist .zc { font-weight: 600; white-space: nowrap; }
.zlist .zmeta { flex: 1; min-width: 0; font-size: 10px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.zlist .zt { font-variant-numeric: tabular-nums; color: var(--text); }
.modalbtns .danger { background: #e1503f; }
.modalbtns #zcancel { background: var(--panel2); color: var(--text); border: 1px solid var(--border); }
.trow { cursor: pointer; }
.trow .tin { cursor: text; }
.pin { position: absolute; text-align: center; pointer-events: none;
  display: flex; flex-direction: column; align-items: center;
  /* anchor on the DOT: the dot is the first row, so centring it means shifting the box up by half
     a dot (6px) - the labels then hang below without moving the marker off its true coordinate */
  transform: translate(-50%, -6px); }
/* colliding neighbour: labels flip ABOVE the dot. column-reverse alone would also move the dot to
   the bottom of the box, so translate the box up by its label block to keep the dot in place. */
.pin.up { flex-direction: column-reverse; transform: translate(-50%, calc(-100% + 6px)); }
.pin .dot { width: 12px; height: 12px; border-radius: 50%; margin: 0 auto; border: 2px solid #0b0e16; box-shadow: 0 0 8px #000b; }
.pin .tlab { font-size: 10px; font-weight: 700; color: #fff; text-shadow: 0 1px 3px #000; }
.pin .clab { font-size: 8.5px; color: #e3e9f6; text-shadow: 0 1px 3px #000; white-space: nowrap; }
.hoverpin .dot { background: #fff; border-color: var(--accent); box-shadow: 0 0 0 4px rgba(125,160,255,.4), 0 0 8px #000a; }
.maptip .addhint { font-size: 9px; color: #8b9dff; margin-top: 2px; } /* the tip keeps a fixed dark palette in both themes, so the ink is fixed too */
.tzrule { position: absolute; top: 0; left: 0; right: 0; height: 17px; background: color-mix(in srgb, var(--panel) 78%, transparent); font-size: 8.5px; color: var(--muted); z-index: 3; pointer-events: none; }
/* Each label fills its own 15-degree cell (placeRule in js/map.js sets left/width), so the click
   target is the whole marker rather than a chip around the digits, and .act lines up exactly with
   the .tzcell fill painted behind it instead of floating inside it.
   The RULE itself keeps pointer-events: none above; only the labels take pointers back. They now
   tile the full width, so the wheel can no longer fall through to #mapCanvas anywhere along the
   strip - map.js attaches the SAME wheel handler to the rule for exactly that reason. Drag-pan from
   inside the 17px strip is the one gesture given up, and only while zoomed in. */
.tzrule .tzlab { position: absolute; top: 0; height: 17px; box-sizing: border-box; padding: 0;
  display: flex; align-items: center; justify-content: center;
  pointer-events: auto; cursor: pointer; overflow: hidden;
  background: none; border: 0; border-radius: 4px; font: inherit; color: inherit; }
.tzrule .tzlab:hover { color: var(--text); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.tzrule .tzlab.hot { color: #dce8ff; font-weight: 700; }
/* pinned: the cell behind is already filled at 0.18, so this layers ON TOP of it - that difference
   is what tells "I chose this band" apart from "I am merely hovering it" */
.tzrule .tzlab.act { color: #fff; font-weight: 700; background: color-mix(in srgb, var(--accent) 42%, transparent); }
.tzrule .tzlab:focus-visible { outline-offset: -2px; }
:root[data-theme="light"] .tzrule .tzlab.hot { color: var(--accent-ink); }
:root[data-theme="light"] .tzrule .tzlab.act { color: #fff; }
.tzrule .tzcell { position: absolute; top: 0; bottom: 0; background: rgba(125, 160, 255, 0.18); border-radius: 3px; pointer-events: none; } /* fills the whole offset cell, matching the map */
.maptip { position: absolute; z-index: 6; transform: translate(-50%, -120%); width: max-content; background: #0b0f1a; border: 1px solid #46557a; border-radius: 9px; padding: 6px 10px; pointer-events: none; box-shadow: 0 6px 20px #000a; } /* fully opaque - a translucent tip let a neighbouring city's label ghost through */
/* width: max-content - an edge-clamped tip must size to its content, never fold the Add button */
.maptip b { font-size: 12px; color: #fff; }
.maptip div { font-size: 10px; color: #9fb0d6; margin-top: 1px; }
/* touch peek: lifted well clear of the finger, a pointer arrow leads back to the city dot.
   --ax re-aims the arrow when the box gets clamped at a panel edge (set from map.js). */
.maptip.touch { transform: translate(-50%, calc(-100% - 36px)); }
.maptip.touch::before, .maptip.touch::after { content: ''; position: absolute; top: 100%;
  left: calc(50% + var(--ax, 0px)); transform: translateX(-50%);
  border: 8px solid transparent; border-bottom: none; border-top-color: #46557a; }
.maptip.touch::after { border-width: 7px; margin-top: -1.5px; border-top-color: #0b0f1a; }
/* no room above (map top edge): open below the dot, arrow points up */
.maptip.touch.flip { transform: translate(-50%, 46px); }
.maptip.touch.flip::before, .maptip.touch.flip::after { top: auto; bottom: 100%;
  border: 8px solid transparent; border-top: none; border-bottom-color: #46557a; }
.maptip.touch.flip::after { border-width: 7px; margin-top: 0; margin-bottom: -1.5px; border-bottom-color: #0b0f1a; }
/* the box was dodged so far clear of the zoom column that --ax can no longer reach the dot (map.js
   sets this past ~24px of shortfall) - an arrow pointing at open sea reads as a bug, so drop it.
   Must stay AFTER the .flip rules: same specificity, so source order is what wins. */
.maptip.touch.noaim::before, .maptip.touch.noaim::after { display: none; }
.sliderpill { position: absolute; left: 12px; right: 12px; bottom: 12px; cursor: pointer;
  padding: 9px 18px; border-radius: 999px; z-index: 4;
  background: rgba(10,16,30,0.74); border: 1px solid rgba(125,160,255,0.28);
  box-shadow: 0 6px 22px #0007; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.timeslider { display: block; width: 100%; height: 4px; -webkit-appearance: none; appearance: none;
  background: rgba(180,200,255,0.3); border-radius: 3px; cursor: pointer; }
.timeslider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 18px; height: 18px;
  border-radius: 50%; background: #fff; box-shadow: 0 2px 8px #000a, 0 0 0 5px rgba(125,160,255,0.30); cursor: grab; }
.timeslider:active::-webkit-slider-thumb { cursor: grabbing; }
.timeslider::-moz-range-thumb { width: 18px; height: 18px; border: none; border-radius: 50%;
  background: #fff; box-shadow: 0 2px 8px #000a, 0 0 0 5px rgba(125,160,255,0.30); cursor: grab; }
.info { max-width: 760px; margin: 0 auto; padding: 24px 16px 60px; }
.info h1 { font-size: 20px; margin: 10px 0 4px; }
.infosub { color: var(--muted); font-size: 13px; margin: 0 0 18px; }
.infoback { color: var(--accent-ink); font-size: 13px; text-decoration: none; }
.inforow { display: grid; grid-template-columns: 96px 72px 1fr; gap: 12px; align-items: baseline; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 13px; }
.inforow .ioff { color: var(--muted); }
.inforow .iabbr { font-weight: 700; }
.inforow .iname { color: var(--muted); }
.modalbtns.center { justify-content: center; }
/* dash-anchored grid: the "-" sits at the exact centre and the fields flank it, so the row
   stays visually centred even when an engine renders the two time inputs at different
   intrinsic widths (picker icon, font metrics) - flex centering drifted on some setups */
.hrow { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; font-variant-numeric: tabular-nums; }
.hrow > :first-child { justify-self: end; }
.hrow > :last-child { justify-self: start; }
/* Never wider than the 1fr column that holds it: a fixed width larger than the column made the
   end field (justify-self:start) overflow the modal's padding edge while the start field
   (justify-self:end) stayed flush, so the row read as off-centre and the focus ring escaped. */
.hrow input { width: 100%; max-width: 104px; min-width: 0; }
/* Good-hours time fields: draw OUR OWN clock glyph and hide the browser's.
   Chromium paints a native ::-webkit-calendar-picker-indicator here, Firefox paints none, and
   Brave suppresses it - so the field looked different on every engine. Hiding the native one
   (display:none also reclaims its layout width) and painting our own makes them identical;
   bar.js calls showPicker() when the glyph is clicked so the picker still opens where supported. */
.hrow input[type="time"] {
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20stroke%3D%22%23828ca3%22%20stroke-width%3D%221.6%22%20stroke-linecap%3D%22round%22%3E%3Ccircle%20cx%3D%228%22%20cy%3D%228%22%20r%3D%226.1%22%2F%3E%3Cpath%20d%3D%22M8%204.6V8l2.4%201.5%22%2F%3E%3C%2Fsvg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 15px 15px;
  padding-right: 27px;
}
:root[data-theme="light"] .hrow input[type="time"] { background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20stroke%3D%22%235a6478%22%20stroke-width%3D%221.6%22%20stroke-linecap%3D%22round%22%3E%3Ccircle%20cx%3D%228%22%20cy%3D%228%22%20r%3D%226.1%22%2F%3E%3Cpath%20d%3D%22M8%204.6V8l2.4%201.5%22%2F%3E%3C%2Fsvg%3E"); }
.hrow input[type="time"]::-webkit-calendar-picker-indicator { display: none; }

.seclabel { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: -5px; }
/* screen-reader/crawler-only text (the app shell has no other crawlable copy) */
.srtext { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
/* upcoming-DST chip on a tile (amber, tooltip has the exact change) */
.trow .nm .dst { display: inline-block; margin-left: 6px; padding: 0 4px; border-radius: 4px; font-size: 8.5px;
  font-weight: 700; letter-spacing: .05em; color: #d9a13c; border: 1px solid #d9a13c66; cursor: help; }
/* optional attendee, wherever a person is named: the tile, the why panel, the compare table.
   Same chip shape as .dst, in the muted ink - it is a fact about the meeting, not a warning.
   The second selector only outranks `.trow .nm span` (which turns every span on that line into a
   block, and would give the chip a full-width box of its own): same declarations, one specificity up. */
.optchip, .trow .nm .optchip { display: inline-block; margin-left: 6px; padding: 0 4px; border-radius: 4px; font-size: 8.5px;
  font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); border: 1px solid var(--border); }
/* travelling: where this tile's readings are actually being taken. Same chip shape and the same
   doubled selector as .optchip, in the accent ink rather than amber - it is not a warning, it is
   the reason the clock on this row says what it says. Mixed case: it carries a place name. */
.tripchip, .trow .nm .tripchip { display: inline-block; margin-left: 6px; padding: 0 4px; border-radius: 4px;
  font-size: 8.5px; font-weight: 700; letter-spacing: .02em; color: var(--accent-ink); white-space: nowrap;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent); }
/* ...and it shares the "vs home" line rather than the meta run, so the two facts about where this
   person is sit together on the line that has room for them (see buildTile) */
.trow .nm .tfoot .tripchip { margin-left: 0; }
.trow .nm .tfoot .hd { display: inline; margin-left: 6px; }
/* ...and this one line WRAPS rather than ellipsising: where the chip and the delta both fit they
   share it, and where they do not the delta takes a line of its own instead of becoming "-8h vs h…".
   The pill itself never breaks - a two-line chip would read as two chips. */
.trow .nm .tfoot { white-space: normal; }
/* a weekend day, in the why panel: amber like every other statement of fact about the calendar */
.wkndchip { display: inline-block; margin-left: 5px; padding: 0 4px; border-radius: 4px; font-size: 8.5px;
  font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: #d8932a;
  border: 1px solid #d8932a66; vertical-align: 1px; }
/* the tile's abbreviation (or bare offset) is a button - it lights that whole time band on the map.
   Its box is now exactly its text. The old +3px padding / -3px margin pair contributed no layout
   width (so `EEST · GMT+3:00 +5h vs home` still can't start ellipsing, and neither can it now) but
   left the HIT box overhanging the text either side, which fed Chrome's touch adjustment: taps
   meant for the city name landed on this smaller target. The comfortable 24px touch target lives in
   tiles.js instead, hit-tested against the raw pointerdown point - .nm clips its overflow, so real
   padding big enough for a finger would simply be cut off here. */
.trow .nm .ab { font: inherit; color: inherit; background: none; border: 0; padding: 0; margin: 0;
  border-bottom: 1px dotted currentColor; cursor: pointer; }
.trow .nm .ab:hover, .trow .nm .ab.on { color: color-mix(in srgb, var(--accent) 42%, var(--text)); } /* AA on panel2 */
.trow .nm .ab.on { font-weight: 700; border-bottom-style: solid; }
.trow .nm .ab:focus-visible { outline-offset: -1px; } /* .nm clips overflow - an outset ring would be cut */
/* presets in the settings modal: chip per preset (click loads, × deletes) + save row */
.plist { display: flex; flex-wrap: wrap; gap: 6px; max-width: 260px; }
.pnone { font-size: 12px; color: var(--muted); }
.pitem { display: inline-flex; align-items: stretch; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.pitem button { background: var(--panel2); border: none; color: var(--text); font-size: 12px; padding: 4px 9px; cursor: pointer; }
.pitem button:hover { background: var(--border); }
.pitem .pdel { border-left: 1px solid var(--border); color: var(--muted); padding: 4px 7px; }
.pitem .pdel:hover { color: #e1503f; }
.pitem .pcopy { display: inline-flex; align-items: center; justify-content: center;
  border-left: 1px solid var(--border); color: var(--muted); padding: 4px 7px; }
.pitem .pcopy:hover { color: var(--text); }
.pitem .pcopy .ico { width: 13px; height: 13px; }
.prow { display: flex; gap: 6px; }
.prow input { flex: 1; min-width: 0; }
.prow button { background: var(--panel2); border: 1px solid var(--border); color: var(--text); border-radius: 8px; padding: 4px 12px; cursor: pointer; }
.prow button:hover { background: var(--border); }
.srow { display: flex; align-items: center; justify-content: center; gap: 8px; }
.srow input { width: 58px; text-align: center; }
/* light popovers (share menu, calendar) are NON-modal: the overlay is only a positioning shell,
   so the map, the Day Overview, the tiles and the bar keep working underneath. bar.js
   re-implements dismissal at document level - a pointer-transparent overlay can never be clicked. */
.overlay.clear { background: transparent; pointer-events: none; }
.overlay.clear > * { pointer-events: auto; }
.datepop { position: fixed; z-index: 51; background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 12px 34px #0008; padding: 12px; display: flex; flex-direction: column; gap: 8px; }
.dpcal { display: flex; flex-direction: column; gap: 8px; }
.dpcalhead { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
/* the month/year takes the slack so ‹ and › keep flanking it once the ✕ joins the row */
.dptitle { display: inline-flex; flex: 1 1 auto; align-items: center; justify-content: center; gap: 7px; }
.dpclose { flex: none; }
.dpmonth { font-size: 15px; font-weight: 700; font-family: var(--mono); }
.dpyear { width: 5ch; text-align: center; background: var(--bg); border: 1px solid var(--border); color: var(--text);
  border-radius: 6px; font-size: 15px; font-weight: 700; font-family: var(--mono); font-variant-numeric: tabular-nums; padding: 3px 0; cursor: ns-resize; }
.dpyear:focus { border-color: var(--accent); cursor: text; }
.dpmnav { width: 28px; height: 28px; border-radius: 8px; background: var(--panel2); border: 1px solid var(--border); color: var(--muted); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; padding: 0; }
.dpmnav:hover { color: var(--accent); border-color: var(--accent); }
.dpmnav .chev { width: 15px; height: 15px; display: block; }
.dpweek { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.dpweek span { text-align: center; font-size: 10.5px; color: var(--muted); }
.dpweek span.we { color: #e0726a; }
.dpdays { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.dpday { min-width: 26px; aspect-ratio: 1; border: none; background: none; color: var(--text); font-size: 13.5px; border-radius: 8px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-variant-numeric: tabular-nums; }
.dpday:hover { background: var(--panel2); }
.dpday.blank { visibility: hidden; cursor: default; }
.dpday.we { color: #e0726a; }
.dpday.today { box-shadow: inset 0 0 0 1px var(--accent); color: var(--accent-ink); }
.dpday.sel { background: var(--accent); color: #fff; }
/* home tile: pinned at top, raised, with a larger gap below */
.trow.home { box-shadow: 0 5px 16px rgba(0,0,0,.28); margin-bottom: 15px; }
/* swipe-to-delete: red action sits behind each tile */
.tcell { position: relative; overflow: hidden; border-radius: 10px; margin: 6px 0; touch-action: pan-y; }
.tcell .trow { margin: 0; border-radius: 10px; } /* rounded always so the leading edge stays rounded while swiping/reordering */
/* swipe-to-delete: a liquid metaball "goo" delete action behind the tile */
.tdel { position: absolute; inset: 0; }
.tdel .goo { position: absolute; inset: -24px; opacity: 0; } /* -24 gives the blur room; the cell's overflow:hidden clips it to the rounded box */
.tcell.swiping .goo { filter: url(#tgoo); } /* only pay for the goo filter during an active swipe */
.tdel .goo > div { position: absolute; background: #e1503f; }                    /* pool + drop blobs */
.tdel .binico { position: absolute; top: 0; height: 100%; display: flex; align-items: center; color: #fff; opacity: 0; pointer-events: none; }
.tdel .binico svg { display: block; }
.tdel .binico.shake svg { animation: binshake .4s ease-in-out infinite; transform-origin: 50% 60%; }
.tcell.dragging .tdel { display: none; } /* reorder reveals the row's float, not the delete action */
@keyframes binshake { 0%,100% { transform: rotate(0); } 20% { transform: rotate(-13deg); } 40% { transform: rotate(10deg); } 60% { transform: rotate(-7deg); } 80% { transform: rotate(4deg); } }
/* transient "undo" tile shown in place of a just-deleted tile (half height) */
.tundo { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 6px 0; padding: 4px 12px; min-height: 26px; max-height: 48px; overflow: hidden; background: var(--panel2); border: 1px dashed var(--border); border-radius: 10px; font-size: 11.5px; color: var(--muted); transition: max-height .28s ease, opacity .28s ease, margin .28s ease, padding .28s ease, border-color .28s ease; }
.tundo.closing { max-height: 0; min-height: 0; opacity: 0; margin-top: 0; margin-bottom: 0; padding-top: 0; padding-bottom: 0; border-color: transparent; }
.tundo .ubtn { background: none; border: none; color: var(--accent-ink); font-weight: 700; font-size: 11.5px; cursor: pointer; padding: 2px 8px; border-radius: 6px; }
.tundo .ubtn:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); }
/* day overview: cap the rows at ~1/3 of the viewport and scroll them; the header,
   axis and best-time finder stay put. Wheel over the band (active area) scrubs time
   and preventDefaults, so it takes precedence over scrolling the list. */
/* fit ~6 rows before scrolling (was 26vh, which hid the 4th city on common heights); a fade mask at
   the bottom + a higher-contrast thumb signal there is more below when it does overflow */
.tlscroll { position: relative; max-height: 40vh; overflow-y: auto; overflow-x: hidden;
  margin: 0 -7px; padding: 0 7px; } /* ±7px so the home row's outdented pill isn't clipped */
.tlscroll::-webkit-scrollbar { width: 9px; }
.tlscroll::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--muted) 60%, transparent); border-radius: 5px; border: 2px solid transparent; background-clip: padding-box; }
.tlscroll::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.tlscroll::-webkit-scrollbar-track { background: transparent; }
/* day-overview drag overlay - gives the ↔ cursor across the whole band region */
.tldrag { position: absolute; cursor: ew-resize; z-index: 1; touch-action: none; }
/* date-picker month select */
/* hide number-input spinners in the cog Step field (kept as text now, but belt-and-braces) */
.srow input::-webkit-outer-spin-button, .srow input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.srow input[type=number] { -moz-appearance: textfield; }
.trow { user-select: none; -webkit-touch-callout: none; }
/* picked-up tile reads as lifted while reordering */
.tcell.dragging .trow { box-shadow: 0 10px 26px rgba(0, 0, 0, .45); border-color: var(--accent); }
/* reorder grip: hidden for mouse users (drag anywhere works there); on touch devices it is
   the visible, scroll-proof reorder affordance (touch-action none = the drag is ours) */
.trow .grip { display: none; }
@media (pointer: coarse) {
  .trow:not(.home) { grid-template-columns: auto 1fr auto auto; }
  /* The six dots stay where they were: the extra 22px is PADDING, so the 26px content box the glyph
     centres in is unmoved, and the matching negative margin keeps the grid column the same width.
     It is grab area only - reaching the last 26px of a phone's left edge with the right thumb is
     the awkward part, and a handle you have to hit exactly is a handle you avoid. */
  .trow .grip { display: flex; align-items: center; justify-content: center;
    width: 48px; padding-right: 22px; margin: -12px -22px -12px -8px; align-self: stretch;
    color: var(--muted); opacity: .6; touch-action: none; cursor: grab; }
}
/* first-visit empty state under the lone home tile */
.thint { margin: 10px 2px 4px; padding: 12px 14px; border: 1px dashed var(--border); border-radius: 12px;
  font-size: 13.5px; line-height: 1.5; color: var(--muted); }
.trow .tin { user-select: text; }
/* Recipient share card (js/sharecard.js): its own slot at the top of the rail column, above #tiles
   in both layouts. INFORMATIONAL, so it takes .thint's quiet frame and the accent - never the amber
   of .driftline/.dst, which in this app means "a fact about the clocks is about to change".
   flex: none because .railcol is a flex column whose rail takes the slack; :empty hides the slot
   entirely, so a dismissed (or never-mounted) card costs no box AND no column gap.
   No entrance animation, so nothing to silence under prefers-reduced-motion. */
#shareCard { flex: none; }
#shareCard:empty { display: none; }
.sharecard { display: flex; flex-direction: column; gap: 5px; padding: 10px 12px;
  background: var(--panel); border: 1px dashed var(--border); border-left: 3px solid var(--accent);
  border-radius: 12px; font-size: 12.5px; line-height: 1.4; }
.sharecard p, .sharecard ul { margin: 0; }
.sc-head { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.sc-big { font-size: 16px; font-weight: 700; color: var(--text); }
.sc-sub, .sc-why { color: var(--muted); }
.sc-people { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 3px; }
.sc-people li { display: flex; align-items: baseline; gap: 6px; }
.sc-who { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-when { margin-left: auto; white-space: nowrap; }
.sc-flag { flex: none; font-size: 9.5px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  color: var(--muted); border: 1px solid var(--border); border-radius: 5px; padding: 1px 4px; }
.sc-ok { align-self: flex-start; margin-top: 2px; background: var(--panel2); border: 1px solid var(--border);
  color: var(--accent-ink); font: inherit; font-size: 12px; font-weight: 600; padding: 4px 11px;
  border-radius: 8px; cursor: pointer; }
.sc-ok:hover { border-color: var(--accent); }
/* day-overview meeting interval: ONE rounded rectangle across all rows, panned with the
   shared horizontal scroll. The left edge is the solid start handle, the right a fainter
   end handle carrying an ✕ to clear the interval. */
.tloverlay { position: absolute; z-index: 2; pointer-events: none; overflow: hidden; }
.tloverlay-in { position: absolute; inset: 0; will-change: transform; }
/* fill + faint top/bottom edges only - the start/end handles draw the vertical edges, so the
   block has NO right border to compete with (double-line) the end handle */
/* two layers: an amber wash OVER a faint dark scrim, so the meeting window still reads amber even
   where it sits on the bright midday-blue part of the sky gradient (a lone translucent amber there
   was overwhelmed by the blue and read as flat gray) */
.mblock { position: absolute; top: 0; bottom: 0; border-radius: 6px;
  background:
    linear-gradient(color-mix(in srgb, var(--meet-tint) 40%, transparent), color-mix(in srgb, var(--meet-tint) 40%, transparent)),
    linear-gradient(rgba(14, 9, 0, .34), rgba(14, 9, 0, .34));
  border-top: 1px solid color-mix(in srgb, var(--meet-tint) 55%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--meet-tint) 55%, transparent);
  transition: left .08s ease, width .08s ease; } /* glide when snapping to the nearest step */
.mb-edge { position: absolute; top: 0; bottom: 0; width: 2px; border-radius: 2px; }
.mb-start { left: -1px; background: #fff; box-shadow: 0 0 6px rgba(255, 255, 255, .9); }
/* panel-level END handle - a line from the title row down through the bands, ✕ on top to clear;
   the whole strip is the resize grip (drag to change duration). The line + ✕ are centred on the
   handle's x; on hover the line thickens uniformly from its centre and brightens (no flashing). */
.mend { position: absolute; z-index: 6; width: 18px; transform: translateX(-50%); cursor: col-resize; touch-action: none; transition: left .08s ease; }
.mend-line { position: absolute; left: 50%; top: 13px; bottom: 0; width: 2px; transform: translateX(-50%); border-radius: 2px;
  background: rgba(255, 255, 255, .6); box-shadow: 0 0 5px rgba(255, 255, 255, .35); transition: width .12s ease, background .12s ease, box-shadow .12s ease; }
.mend:hover .mend-line, .mend.grab .mend-line { width: 4px; background: #fff; box-shadow: 0 0 9px rgba(255, 255, 255, .9); }
.mend-x { position: absolute; top: 0; left: 50%; transform: translateX(-50%); pointer-events: auto; cursor: pointer;
  width: 18px; height: 18px; padding: 0; display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%; background: var(--accent); color: #fff; box-shadow: 0 1px 4px rgba(0, 0, 0, .45); transition: transform .12s ease; }
.mend-x svg { width: 9px; height: 9px; display: block; }
.mend:hover .mend-x, .mend.grab .mend-x { transform: translateX(-50%) scale(1.15); }
/* per-row interval segment (UNLOCKED meeting mode) - translucent block, solid start edge */
.mseg { position: absolute; top: 0; bottom: 0; background:
  linear-gradient(color-mix(in srgb, var(--meet-tint) 42%, transparent), color-mix(in srgb, var(--meet-tint) 42%, transparent)),
  linear-gradient(rgba(14, 9, 0, .34), rgba(14, 9, 0, .34)); } /* amber over a dark scrim - reads amber over bright blue too */
/* mobile Day discovery cue: a ghost interval blooms from the marker, holds, and fades */
.ivlhint { position: absolute; top: 0; bottom: 0; border-radius: 5px; pointer-events: none;
  background: color-mix(in srgb, var(--meet-tint) 30%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--meet-tint) 45%, transparent);
  transform-origin: left center; animation: ivlbloom 1.6s cubic-bezier(.3, .7, .3, 1) forwards; }
.ivlhint.back { transform-origin: right center; }
@keyframes ivlbloom { 0% { transform: scaleX(0); opacity: 0; } 35% { transform: scaleX(1); opacity: 1; }
  62% { transform: scaleX(1); opacity: 1; } 100% { transform: scaleX(1); opacity: 0; } }
.mseg.head { border-left: 2px solid #fff; box-shadow: -1px 0 5px rgba(255, 255, 255, .55); border-radius: 3px 0 0 3px; }
/* horizontal scrollbar row - height is ALWAYS reserved (whether or not the bar shows) so the
   page never shifts when an interval grows past a day */
.tlscrollx { display: grid; grid-template-columns: var(--tlc1) 1fr var(--tlc3); gap: 10px; margin-top: 2px; }
.tlscrollx .sxv { height: 10px; overflow-x: auto; overflow-y: hidden; }
.tlscrollx .sxin { height: 1px; }
.tlscrollx .sxv::-webkit-scrollbar { height: 8px; }
.tlscrollx .sxv::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
.tlscrollx .sxv::-webkit-scrollbar-track { background: transparent; }
/* meeting / duration mode (tiles panel) */
/* meeting / duration mode - the off pill and the on-card's first row share MEET_H so
   clicking "Plan a meeting" doesn't nudge the tiles down; the ranked-alts row grows below */
.meetbar { margin: 0 0 7px; }
.meetbar.off .meet-plan { width: 100%; height: 36px; display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-size: 15px; font-weight: 600; cursor: pointer; color: var(--accent); border-radius: 10px; padding: 0 8px;
  background: color-mix(in srgb, var(--accent) 10%, var(--panel2)); border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--border)); }
.meetbar.off .meet-plan svg { width: 18px; height: 18px; }
/* dark theme: raw accent on the tinted pill is 3.3:1 - lift it to AA */
:root:not([data-theme="light"]) .meetbar.off .meet-plan { color: color-mix(in srgb, var(--accent) 58%, #fff); }
.meetbar.off .meet-plan:hover { background: color-mix(in srgb, var(--accent) 18%, var(--panel2)); }
.meetbar.on { display: grid; padding: 0; border-radius: 10px; overflow: hidden;
  background: color-mix(in srgb, var(--accent) 9%, var(--panel2)); border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--border)); }
.meet-ctrl { display: flex; align-items: center; gap: 7px; height: 34px; padding: 0 9px; }
.meet-ctrl .lab { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.meet-dur { width: 56px; text-align: center; font-weight: 700; font-size: 13.5px; color: var(--text);
  background: var(--bg); border: 1px solid var(--border); border-radius: 7px; padding: 3px 2px; }
.meet-dur:focus { border-color: var(--accent); }
.meet-find { margin-left: auto; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 26px;
  cursor: pointer; color: #fff; background: var(--accent); border: none; border-radius: 7px; }
.meet-find svg { width: 19px; height: 19px; display: block; }
.meet-find:hover { filter: brightness(1.08); }

/* Advanced tunables popover: good hours + the early/late preference. Flows BELOW the bar rather
   than floating - the meetbar card just grows, so there is nothing to clip or position. */
.meet-row { display: flex; gap: 6px; align-items: stretch; }
.meet-row .meet-plan { flex: 1; width: auto !important; }
.meet-adv { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 26px; flex: none;
  border: 1px solid var(--border); border-radius: 7px; background: var(--panel2); color: var(--muted); cursor: pointer; }
.meetbar.off .meet-adv { width: 38px; height: 36px; border-radius: 9px; }
.meet-adv svg { width: 17px; height: 17px; display: block; }
.meet-adv:hover { color: var(--text); border-color: var(--accent); }
.meet-adv.on { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, var(--panel2)); }
.meet-adv-pop { margin-top: 6px; padding: 10px 11px 11px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel2); }
.meetbar.on .meet-adv-pop { margin: 0; border-radius: 0; border-width: 1px 0 0; }
/* ...and as a sheet it IS the sheet body: the card chrome would be a box inside a box */
.advsheet .meet-adv-pop { margin: 0; padding: 0; border: none; background: none; }
.advlab { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin: 0 0 5px; }
.meet-adv-pop .hrow { margin-bottom: 10px; }
/* the popover is not inside .modal, so it needs the same field styling explicitly */
.meet-adv-pop input { background: var(--bg); border: 1px solid var(--border); color: var(--text); border-radius: 7px; padding: 4px 7px; font: inherit; }
.segrow { display: flex; gap: 4px; }
.seg { flex: 1; height: 27px; padding: 0 4px; border: 1px solid var(--border); border-radius: 7px; background: transparent;
  color: var(--muted); font-size: 12px; cursor: pointer; white-space: nowrap; }
.seg:hover { color: var(--text); }
.seg.on { background: color-mix(in srgb, var(--accent) 18%, transparent); border-color: var(--accent); color: var(--text); }
/* Policy presets: three whole postures, and one of them carries a longer label than any other
   segmented control in this panel. flex-basis auto (not .seg's own 0) so a narrow rail wraps the
   row onto a second line instead of squeezing a nowrap label out of its own button. */
.polrow { flex-wrap: wrap; }
.polrow .seg { flex: 1 1 auto; }
.advnote { margin: 8px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--muted); }
/* Recurring series: a third section of the same panel, so it gets the same .advlab heading and a
   rule above it - the two sections above tune ONE meeting, this one asks about a standing habit. */
.recur { margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--border); }
.recurrow { display: flex; align-items: center; gap: 8px; }
.recurtog { flex: 0 0 auto; min-width: 76px; }
.recurcnt { display: inline-flex; align-items: center; gap: 6px; margin-left: auto;
  font-size: 11.5px; color: var(--muted); }
.recurn { width: 56px; text-align: center; }
.recuranch { margin-top: 8px; }
/* the weeks that quietly move, and who they move for */
.recurdrift { margin-top: 9px; display: grid; gap: 3px; }
.recurdrift p { margin: 0; font-size: 11px; line-height: 1.4; color: #d8932a; }
.recurdrift .recurmore { color: var(--muted); }
/* the accumulated bill, one bar per person: width is their share of it, colour is how much of it
   they carry (the red end is whoever a rotation would relieve first) */
.ledger { margin-top: 9px; display: grid; gap: 4px; }
.ledgerrow { display: grid; grid-template-columns: minmax(0, 84px) minmax(0, 1fr) auto; align-items: center; gap: 7px; font-size: 11px; }
.ledgernm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.ledgertrack { height: 7px; border-radius: 4px; background: var(--bg); overflow: hidden; }
.ledgerbar { display: block; height: 100%; border-radius: 4px; min-width: 2px; }
.ledgermin { color: var(--muted); white-space: nowrap; }
.recurrot { margin: 9px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--text); }
.recurjump { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.rotjump, .spljump { background: var(--panel2); border: 1px solid var(--border); color: var(--accent-ink);
  font-size: 11.5px; font-weight: 600; padding: 4px 10px; border-radius: 8px; cursor: pointer; }
.rotjump:hover, .spljump:hover { border-color: var(--accent); }
/* Team overlap: a fourth section of the same panel, collapsed by default (an N x N scan is a lot of
   table for a rail this narrow). At rest it is just another .advlab-style heading, interactive this
   time; open, its table borrows Compare's own shape (.cmptable) and 26% color-mix tint, scaled down. */
.ovl { margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--border); }
/* .ztravtog rides along: the zone editor's "Traveling" disclosure is the same control in the same
   dress, and two implementations of one heading-that-opens would drift */
.ovltoggle, .ztravtog { display: block; width: 100%; margin: 0; padding: 0; background: none; border: none;
  font: inherit; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  text-align: left; cursor: pointer; }
.ovltoggle:hover, .ovltoggle[aria-expanded="true"],
.ztravtog:hover, .ztravtog[aria-expanded="true"] { color: var(--text); }
.ovlwrap { margin-top: 7px; overflow: auto; max-width: 100%; }
.ovltable { border-collapse: collapse; font-size: 11px; width: 100%; }
.ovltable th, .ovltable td { padding: 4px 6px; text-align: center; white-space: nowrap; border-bottom: 1px solid var(--border); }
.ovltable thead th { font-size: 9.5px; letter-spacing: .04em; text-transform: uppercase; font-weight: 600; color: var(--muted); }
.ovltable tbody th { text-align: left; font-weight: 600; color: var(--text); }
.ovltable td { color: var(--text); }
.ovltable td.ovldiag { color: var(--muted); }
/* Hand-offs: the same board read as a relay rather than as a matrix, and a fifth section of the
   same panel - its disclosure IS .ovltoggle above, since two dresses for one heading-that-opens
   would drift. One line per pass of the day; a gap takes the drift notes' warning ink. */
.hoff { margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--border); }
.hoffrows { margin-top: 7px; display: grid; gap: 4px; }
.hoffrow { margin: 0; font-size: 11.5px; line-height: 1.45; color: var(--text); }
.hoffrow b { font-weight: 600; }
.hoffgap { color: #d8932a; }
.hoffoff { color: var(--muted); }
.meet-x { background: none; border: none; color: var(--muted); font-size: 13px; line-height: 1; cursor: pointer; padding: 2px 5px; border-radius: 6px; }
.meet-x:hover { color: var(--text); background: var(--panel2); }
.meet-alts { display: flex; align-items: center; gap: 9px; padding: 0 9px 7px; }
.meet-step { display: inline-flex; align-items: center; gap: 4px; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 2px 4px; }
.meet-step button { background: none; border: none; color: var(--text); font-size: 15px; line-height: 1; cursor: pointer; padding: 1px 7px; border-radius: 5px; }
.meet-step button:disabled { color: var(--muted); opacity: .4; cursor: default; }
.meet-step button:not(:disabled):hover { background: var(--panel2); }
.meet-rank { font-size: 11.5px; font-weight: 700; min-width: 62px; text-align: center; font-variant-numeric: tabular-nums; }
.meet-fit { font-size: 11px; color: var(--muted); }
.meet-fit.partial { color: #d8932a; }
/* the alts row now carries the pin and the disclosure too - let it wrap rather than squeeze the
   rank pill on a narrow rail (the mobile block already wrapped it) */
.meet-alts { flex-wrap: wrap; row-gap: 4px; }
/* "Why this time?": a disclosure, so it reads as text rather than another icon in a row of icons */
.meet-why { background: none; border: none; padding: 2px 4px; border-radius: 6px; cursor: pointer;
  color: var(--accent-ink); font-size: 11px; font-weight: 600; }
.meet-why:hover { background: var(--panel2); }
.meet-why.on { text-decoration: underline; }
.meet-pin { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 24px; flex: none;
  border: 1px solid var(--border); border-radius: 7px; background: var(--panel2); color: var(--muted); cursor: pointer; }
.meet-pin svg { width: 15px; height: 15px; display: block; }
.meet-pin:hover:not(:disabled) { color: var(--text); border-color: var(--accent); }
.meet-pin.on { color: var(--accent-ink); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, var(--panel2)); }
.meet-pin:disabled { opacity: .45; cursor: default; }
/* the placement explained, one row per person */
.whypanel { padding: 7px 9px 8px; display: grid; gap: 3px; border-top: 1px solid color-mix(in srgb, var(--accent) 22%, var(--border)); }
.whyrow { display: grid; grid-template-columns: 8px minmax(0, 1fr) auto auto; align-items: center; gap: 7px; font-size: 11px; }
.whydot { width: 8px; height: 8px; border-radius: 50%; }
.whynm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.whyt { color: var(--text); white-space: nowrap; }
.whyt i { font-style: normal; color: var(--muted); margin: 0 2px; }
.whyt sup { font-size: 8px; font-weight: 600; color: var(--muted); margin-left: 1px; }
.whyst { color: var(--muted); text-align: right; white-space: nowrap; }
.whyst.off { color: #d8932a; }
.whyfoot { margin: 5px 0 0; font-size: 11px; line-height: 1.45; color: var(--muted); }
/* Advisor: the board saying, once, that no single time is comfortable. Amber like every other
   statement of fact about the clocks, and a disclosure rather than a paragraph - at rest it is one
   line. Its panel borrows .whypanel's shape so the meeting card keeps one vocabulary. */
.advisor { padding: 0 9px 7px; }
.advisorline { display: flex; align-items: center; gap: 6px; width: 100%; padding: 3px 5px;
  background: none; border: 1px solid transparent; border-radius: 7px; color: var(--muted);
  font-family: inherit; font-size: 11px; text-align: left; cursor: pointer; }
.advisorline:hover { background: var(--panel2); color: var(--text); border-color: color-mix(in srgb, #d8932a 40%, var(--border)); }
.advisorline[aria-expanded="true"] { color: var(--text); }
.advisorlab { flex: 1 1 auto; min-width: 0; }
/* the affordance that says this line OPENS somewhere - without it the amber line read as a verdict */
.advisormore { flex: none; color: var(--accent, #7aa7e0); font-weight: 600; font-size: 10.5px; letter-spacing: .02em; }
.widenbtn { background: var(--panel2); border: 1px solid var(--border); border-radius: 7px; color: var(--text);
  font-family: inherit; font-size: 11px; padding: 3px 10px; cursor: pointer; }
.widenbtn:hover { border-color: var(--accent, #7aa7e0); }
.advisordot { width: 7px; height: 7px; border-radius: 50%; background: #d8932a; flex: none; }
.advisorpanel { padding: 6px 5px 2px; }
.advisortx { margin: 0 0 5px; font-size: 11px; line-height: 1.45; color: var(--muted); }
/* pinned candidates: a chip per pin (tap the time to move the meeting there, x to drop it) */
.meet-pins { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 0 9px 8px; }
.pinchip { display: inline-flex; align-items: stretch; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.pinjump { background: none; border: none; color: var(--text); font-size: 11.5px; padding: 3px 8px; cursor: pointer; }
.pinjump b { color: var(--muted); font-weight: 600; margin-left: 5px; }
.pinjump:hover { background: var(--panel2); }
.pinx { background: none; border: none; border-left: 1px solid var(--border); color: var(--muted);
  font-size: 12px; line-height: 1; padding: 3px 7px; cursor: pointer; }
.pinx:hover { color: #e1503f; }
.cmp-open { background: var(--panel2); border: 1px solid var(--border); color: var(--accent-ink);
  font-size: 11.5px; font-weight: 600; padding: 4px 10px; border-radius: 8px; cursor: pointer; }
.cmp-open:hover { border-color: var(--accent); }
/* Compare: an anchored non-modal popover on a pointer device (the share menu's pattern), a sheet on
   a phone. Both render into #modalRoot, so the tile re-render cannot tear the table down mid-read. */
.cmppop { position: fixed; z-index: 51; background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 12px 34px #0008; padding: 10px; max-width: min(440px, 92vw); }
.cmpwrap { overflow: auto; max-height: 58vh; overscroll-behavior: contain; }
.cmptable { border-collapse: collapse; font-size: 11.5px; width: 100%; }
.cmptable th, .cmptable td { padding: 5px 8px; text-align: left; white-space: nowrap; border-bottom: 1px solid var(--border); }
.cmptable thead th { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; font-weight: 600; color: var(--muted); }
.cmptable tbody th { font-weight: 600; color: var(--text); max-width: 14ch; overflow: hidden; text-overflow: ellipsis; }
.cmptable td { color: var(--text); }
.cmptable td i { font-style: normal; color: var(--muted); margin: 0 1px; }
.cmptable td sup { font-size: 8px; font-weight: 600; color: var(--muted); margin-left: 1px; }
.cmptable .cmpfoot th, .cmptable .cmpfoot td { border-bottom: none; color: var(--muted); background: none; }
.cmptable .cmpfoot td.best { color: var(--accent-ink); font-weight: 700; }
/* DST drift radar. Amber throughout, the ink the tile's DST chip and the partial-fit label already
   use for "a fact about the clocks", never the red of an error. The line is a footnote under the
   Places header: one row high, quiet until hovered, and it says only the pair and the date - the
   windows themselves are one press away. font-family because a button does not inherit one, and
   this is a sentence sitting between two runs of panel text. */
.driftline { display: flex; align-items: center; gap: 6px; width: calc(100% - 6px); margin: -3px 3px 8px;
  padding: 3px 5px; background: none; border: 1px solid transparent; border-radius: 7px;
  color: var(--muted); font-family: inherit; font-size: 11px; text-align: left; cursor: pointer; }
.driftline:hover { background: var(--panel2); color: var(--text); border-color: color-mix(in srgb, #d8932a 40%, var(--border)); }
.driftdot { width: 7px; height: 7px; border-radius: 50%; background: #d8932a; flex: none; }
/* wraps rather than ellipsing: the DATE is the last word and the whole point of the line, and the
   desktop rail is 250px - a single nowrap line ate it. Two 11px lines is still a footnote. */
.drifttx { min-width: 0; line-height: 1.35; }
/* meeting mode: the same warning, compact, beside the fit count. Chip shape of .dst, and a button
   because it opens the panel - the tooltip alone is unreachable on touch. */
.driftchip { background: none; border: 1px solid #d9a13c66; border-radius: 6px; color: #d8932a;
  font-size: 10.5px; font-weight: 700; padding: 2px 6px; cursor: pointer; white-space: nowrap; }
.driftchip:hover { border-color: #d9a13c; }
/* the windows in full: anchored popover on a pointer device, sheet on a phone - Compare's sizing */
.driftpop { position: fixed; z-index: 51; background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 12px 34px #0008; padding: 10px; max-width: min(440px, 92vw); }
.driftwrap { overflow: auto; max-height: 58vh; overscroll-behavior: contain; }
.driftrow { padding: 6px 2px; border-bottom: 1px solid var(--border); }
.driftrow:last-child { border-bottom: none; }
.driftpair { font-size: 11.5px; line-height: 1.45; color: var(--text); }
.driftsub { font-size: 10.5px; color: var(--muted); margin-top: 2px; }
.driftoff { margin-top: 8px; background: var(--panel2); border: 1px solid var(--border); border-radius: 8px;
  color: var(--muted); font-size: 11px; padding: 5px 10px; cursor: pointer; }
.driftoff:hover { color: var(--text); border-color: var(--accent); }
/* tile in meeting mode: start–end instead of a single editable time */
.trow .tmeet { text-align: right; font-size: 13.5px; font-weight: 700; white-space: nowrap; color: var(--text); }
.trow .tmeet i { font-style: normal; font-weight: 400; color: var(--muted); margin: 0 3px; }
.trow .tmeet sup { font-size: 8px; font-weight: 600; color: var(--muted); margin-left: 1px; }
/* storage/privacy page section headings */
.info h2 { font-size: 15px; margin: 18px 0 4px; }
/* first-visit functional-storage notice (dismissible bottom bar) */
.cookienotice { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex; align-items: center; justify-content: center; gap: 14px; flex-wrap: wrap;
  padding: 10px 16px; font-size: 12.5px; line-height: 1.4; color: var(--muted);
  background: color-mix(in srgb, var(--panel) 92%, transparent); border-top: 1px solid var(--border);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.cookienotice a { color: var(--accent-ink); text-decoration: underline; }
/* Add-to-Home-Screen nudge (bottom banner, above the tab bar / safe area) */
.installnudge { position: fixed; left: 0; right: 0; bottom: 0; z-index: 41;
  display: flex; align-items: center; justify-content: center; gap: 14px; flex-wrap: wrap;
  padding: 10px 16px; padding-bottom: max(10px, env(safe-area-inset-bottom));
  font-size: 13px; color: var(--text);
  background: color-mix(in srgb, var(--panel) 94%, transparent); border-top: 1px solid var(--border);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
/* when the storage notice is also up, the install nudge stacks ABOVE it (both stayed at bottom:0
   before, so the higher-z nudge hid the required consent notice entirely) */
@media (min-width: 721px) and (min-height: 541px) { body.has-notice .installnudge { bottom: var(--notice-h, 60px); } }
.installnudge .in-msg { display: inline-flex; align-items: center; gap: 8px; }
.installnudge .in-msg svg { width: 18px; height: 18px; color: var(--accent-ink); flex: none; }
.installnudge .in-actions { display: inline-flex; align-items: center; gap: 6px; }
.installnudge .in-add { background: var(--accent); color: #fff; border: none; border-radius: 8px;
  padding: 7px 16px; font-size: 13px; font-weight: 600; cursor: pointer; }
:root:not([data-theme="light"]) .installnudge .in-add { background: color-mix(in srgb, var(--accent) 76%, #000); }
.installnudge .in-x { background: none; border: none; color: var(--muted); font-size: 15px; cursor: pointer; padding: 6px 8px; }
@media (max-width: 720px), (max-height: 540px) and (pointer: coarse) {
  .installnudge { bottom: calc(56px + env(safe-area-inset-bottom)); padding-bottom: 10px; }
  /* stack above BOTH the tab bar and the storage notice when the notice is showing */
  body.has-notice .installnudge { bottom: calc(56px + var(--notice-h, 60px) + env(safe-area-inset-bottom)); }
}
:root:not([data-theme="light"]) .cookienotice .cn-ok { background: color-mix(in srgb, var(--accent) 76%, #000); }
.cookienotice .cn-ok { flex: none; background: var(--accent); color: #fff; border: none; border-radius: 8px;
  padding: 6px 14px; font-size: 12.5px; cursor: pointer; }
.cookienotice .cn-ok:hover { background: color-mix(in srgb, var(--accent) 85%, #fff); }

/* ============================================================================
   SSR SEO pages (pre-hydration). The app shell leaves the injected #seo block
   unstyled and the page unscrollable - give the Google-facing mobile landing
   real typography, scrolling, and overflow-safe tables. While #seo exists the
   app shell stays hidden; hydration removes #seo and the app takes over.
   ========================================================================= */
body:has(> #seo) { overflow: auto; height: auto; }
body:has(> #seo) #app { display: none; }
/* the moment hydration begins (inline snippet sets data-hydrating), hide the crawler-only SEO table
   and reveal the app shell - so a deep link shows the centered boot mark, not the raw conversion
   table, during the module/data wait (instead of waiting on cities.json to remove #seo). */
html[data-hydrating] body:has(> #seo) { overflow: hidden; height: var(--app-h, 100dvh); }
html[data-hydrating] body:has(> #seo) #app { display: flex; }
html[data-hydrating] #seo { display: none; }
#seo { max-width: 760px; margin: 0 auto; padding: 24px 16px 48px; font-size: 15px; line-height: 1.55; }
#seo h1 { font-size: 22px; line-height: 1.25; margin: 6px 0 10px; }
#seo h2 { font-size: 17px; margin: 22px 0 8px; }
#seo a { color: var(--accent-ink); }
#seo p { margin: 8px 0; }
#seo .seo-conv { display: block; overflow-x: auto; border-collapse: collapse; font-variant-numeric: tabular-nums; white-space: nowrap; }
#seo .seo-conv th, #seo .seo-conv td { padding: 6px 12px; border-bottom: 1px solid var(--border); text-align: left; font-size: 14px; }
#seo .seo-breadcrumb { font-size: 12.5px; color: var(--muted); margin-bottom: 6px; }
#seo .seo-related ul { padding-left: 20px; }
/* provenance line: it is about the page, not content of it, so it reads at the breadcrumb's weight */
#seo .seo-foot { margin-top: 26px; font-size: 12.5px; color: var(--muted); }

/* ---- 404 ---- */
.seo.nf { text-align: center; padding-top: 12vh; }
.nf-gap { font: 800 clamp(52px, 11vw, 82px)/1 var(--mono); letter-spacing: .02em; display: inline-block; }
.nf-skip { color: #33405e; position: relative; }
.nf-skip::after { content: ""; position: absolute; left: -6%; right: -6%; top: 52%; height: 3px;
  background: var(--amber, #ffcd6e); transform: scaleX(0); transform-origin: left;
  animation: nfstrike 2.6s .5s cubic-bezier(.4, 0, .2, 1) infinite; }
@keyframes nfstrike { 0% { transform: scaleX(0); } 22%, 70% { transform: scaleX(1); } 92%, 100% { transform: scaleX(0); } }
.nf-clockrow { display: flex; align-items: center; justify-content: center; gap: 14px; }
.nf-big { font: 800 clamp(56px, 12vw, 88px)/1 var(--mono); letter-spacing: .02em;
  background-image: linear-gradient(90deg,#304880,#4872b4 24%,#70a4de 38%,#f0f8ff 50%,#70a4de 62%,#4872b4 76%,#304880);
  -webkit-background-clip: text; background-clip: text; color: transparent; }
.nf-clk { width: clamp(56px, 12vw, 88px); height: clamp(56px, 12vw, 88px); flex: none; }
.nf-msg { font-size: 17px; margin: 18px 0 6px; }
.nf-sub { color: var(--muted); font-size: 13.5px; margin: 0; }
.nf-cta { display: inline-flex; gap: 9px; margin-top: 22px; flex-wrap: wrap; justify-content: center; }
/* #seo a sets colour by id, so these need equal-or-higher specificity to win */
#nfwrap .nf-btn, .seo.nf .nf-btn { background: var(--accent); color: #fff; border-radius: 9px;
  padding: 10px 18px; font-size: 13.5px; font-weight: 600; text-decoration: none; }
#nfwrap .nf-btn.ghost, .seo.nf .nf-btn.ghost { background: transparent; color: var(--text);
  border: 1px solid var(--border); }
#nfwrap .nf-btn:hover, .seo.nf .nf-btn:hover { filter: brightness(1.08); }
@media (prefers-reduced-motion: reduce) { .nf-skip::after { animation: none; transform: scaleX(1); } }

/* touch niceties (all widths - safe for mouse too) */
button, [role="button"] { touch-action: manipulation; }
html { -webkit-tap-highlight-color: transparent; }
/* map tap-peek: explicit add action inside the tooltip (touch shows it at any width) */
.maptip .tipbtns { display: flex; align-items: stretch; gap: 5px; margin-top: 6px; }
.maptip .tipadd { flex: 1; min-height: 30px; background: var(--accent);
  color: #fff; border: none; border-radius: 8px; font-size: 12.5px; font-weight: 700; padding: 5px 10px; cursor: pointer; }
:root:not([data-theme="light"]) .maptip .tipadd { background: color-mix(in srgb, var(--accent) 80%, #000); }
.maptip .tipadded { flex: 1; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; color: #7fe0a3; }
.maptip .tipclose { flex: none; width: 30px; min-height: 30px; background: color-mix(in srgb, #fff 10%, transparent);
  color: #cfe; border: 1px solid #46557a; border-radius: 8px; font-size: 13px; cursor: pointer; }
/* ===== Material 3 time picker (centered floating dialog) ===== */
.overlay.tpcenter { place-items: center; background: rgba(0, 0, 0, .5); padding: 16px; }
.tpick { width: 328px; max-width: calc(100vw - 32px); box-sizing: border-box; background: var(--panel);
  border: 1px solid var(--border); border-radius: 28px; padding: 20px; display: flex; flex-direction: column; gap: 16px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, .5); animation: tppop .16s ease-out; }
@keyframes tppop { from { transform: scale(.94); opacity: .4; } }
.tp-title { font-size: 13px; color: var(--muted); }
.tp-time { display: flex; align-items: center; justify-content: center; gap: 8px; font-variant-numeric: tabular-nums; }
.tpick.keysmode .tp-time { display: none; } /* keyboard mode: the labelled Hour/Minute fields are the only input (no duplicate top display) */
.tpick.keysmode .tp-body { min-height: 0; } /* let the picker shrink to just the fields in keyboard mode */
.tp-field { font-family: var(--mono); font-size: 52px; font-weight: 500; line-height: 1; min-width: 92px; height: 76px;
  border-radius: 12px; border: 1px solid transparent; background: var(--panel2); color: var(--muted); cursor: pointer; }
.tp-field.active { background: color-mix(in srgb, var(--accent) 24%, var(--panel)); color: var(--text);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
.tp-sep { font-size: 46px; font-weight: 500; color: var(--text); }
.tp-body { display: flex; justify-content: center; align-items: center; min-height: 256px; }
.tp-dial { width: 256px; height: 256px; touch-action: none; }
.tp-face { fill: var(--panel2); }
.tp-hand { stroke: var(--accent); stroke-width: 2; }
.tp-hub { fill: var(--accent); }
.tp-sel { fill: var(--accent); }
.tp-mindot { fill: #fff; }
/* paint a face-coloured halo BEHIND each digit (paint-order: stroke) so the hand line - which passes
   through the inner ring to reach an outer-hour bubble - is hidden behind the digit instead of cutting
   visibly through its gaps and counters */
.tp-num { fill: var(--text); font-size: 15px; font-family: var(--mono); text-anchor: middle; dominant-baseline: central; user-select: none;
  paint-order: stroke; stroke: var(--panel2); stroke-width: 4px; stroke-linejoin: round; }
.tp-num.on { stroke: var(--accent); }
.tp-num.on { fill: #fff; font-weight: 600; }
.tp-keys { display: flex; align-items: flex-start; justify-content: center; gap: 10px; padding: 22px 0; }
.tp-ksep { font-size: 46px; color: var(--text); line-height: 1.3; }
.tp-kcol { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.tp-kin { width: 100px; font-family: var(--mono); font-size: 46px; font-weight: 500; text-align: center;
  background: color-mix(in srgb, var(--accent) 14%, var(--panel2)); border: 2px solid color-mix(in srgb, var(--accent) 55%, var(--border));
  border-radius: 10px; color: var(--text); padding: 8px 4px; }
.tp-kin:focus { border-color: var(--accent); }
.tp-klab { font-size: 11px; color: var(--muted); }
.tp-foot { display: flex; align-items: center; justify-content: space-between; }
.tp-kbd { width: 44px; height: 44px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; color: var(--muted); cursor: pointer; }
.tp-kbd svg { width: 24px; height: 24px; }
.tp-btns { display: flex; gap: 4px; }
.tp-cancel, .tp-ok { border: none; background: none; border-radius: 8px; padding: 10px 14px; font-size: 14px; font-weight: 600;
  color: var(--accent-ink); cursor: pointer; }
.tp-cancel:hover, .tp-ok:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* step number dialog reuses the tpick card */
.tstep .ts-row { display: flex; align-items: center; justify-content: center; gap: 16px; padding: 12px 0; }
.tstep .ts-dec, .tstep .ts-inc { width: 54px; height: 54px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--panel2); color: var(--text); font-size: 26px; line-height: 1; cursor: pointer; }
.tstep .ts-dec:active, .tstep .ts-inc:active { border-color: var(--accent); }
.tstep .ts-val { width: 110px; text-align: center; font-family: var(--mono); font-size: 44px; font-weight: 600;
  background: none; border: none; border-bottom: 2px solid var(--accent); color: var(--text); padding: 4px 0; }

.tabbar { display: none; }
/* brand wordmark shown atop the settings sheet on mobile (the header logo is always the
   collapsed clock there, so this gives the full name a home) - same shimmering sky gradient */
.setbrand { text-align: center; font-family: "Helvetica Neue", "Segoe UI", system-ui, -apple-system, Roboto, Arial, sans-serif;
  font-weight: 800; font-size: 24px; letter-spacing: .005em; margin: 2px 0 6px;
  background-image: linear-gradient(90deg, rgb(48,72,128), rgb(56,86,146) 12%, rgb(72,114,180) 24%, rgb(112,164,222) 38%, rgb(180,214,250) 47%, rgb(240,248,255) 50%, rgb(180,214,250) 53%, rgb(112,164,222) 62%, rgb(72,114,180) 76%, rgb(56,86,146) 88%, rgb(48,72,128));
  background-size: 200% 100%; -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent; animation: skypan 6s linear infinite; }
/* the animated clock replaces the "o" of "Zone", sitting on the text baseline */
/* the clock replaces the "o" of Zone; centre it ON the baseline (half above, half below the line
   the letters sit on): baseline-align puts its bottom on the baseline, then nudge down half its height */
.setbrand .setclk { display: inline-block; width: .82em; height: .82em; vertical-align: baseline; position: relative; top: .41em; margin: 0 .01em; }
.setbrand .setclksvg { width: 100%; height: 100%; display: block; }
/* settings sheet header: title + reset */
.modalhead { display: flex; align-items: center; justify-content: space-between; }
.modalhead h3 { margin: 0; }
.streset { background: none; border: 1px solid var(--border); color: var(--muted); font-size: 12px;
  padding: 5px 11px; border-radius: 8px; cursor: pointer; }
.streset:hover { border-color: var(--accent); color: var(--accent-ink); }
/* settings time/step fields as buttons (touch) that open the pickers */
.hbtn { background: var(--bg); border: 1px solid var(--border); color: var(--text); font-family: var(--mono);
  font-variant-numeric: tabular-nums; font-size: 16px; padding: 9px 14px; border-radius: 9px; cursor: pointer; }
.hbtn:active { border-color: var(--accent); }
/* desktop safety net: the mobile tab bar must never show above the breakpoint */
@media (min-width: 721px) and (min-height: 541px) { .tabbar { display: none !important; } }
/* DESKTOP only: reserve #app bottom space for the fixed banner(s) so the Day Overview's last row is
   never hidden behind them (mobile floats them above the tab bar, so it is excluded here). */
@media (min-width: 721px) and (min-height: 541px) {
  body.has-notice #app { padding-bottom: calc(12px + var(--notice-h, 60px)); }
  body.has-nudge #app { padding-bottom: calc(12px + var(--nudge-h, 56px)); }
  body.has-notice.has-nudge #app { padding-bottom: calc(16px + var(--notice-h, 60px) + var(--nudge-h, 56px)); }
}

@media (prefers-reduced-motion: reduce) {
  .logo > span { animation: none; }
  .modal, .sharepop, .datepop { animation: none !important; }
  /* every remaining running animation: the brand sky-pan, the swipe-delete bin shake, the
     Day discovery bloom, the marker activation pulse, and the sheet/dialog entrances */
  .setbrand, .tdel .binico.shake svg, .ivlhint, .tpick { animation: none !important; }
  .marker.activating { animation: none !important; }
  .mblock, .mseg, .mend, .tundo { transition: none !important; }
  /* the brand mark's collapse/expand morph and the cog's spin-on-hover are motion too */
  .logo, .logo > span, .logo .clk, .setbtn { transition: none !important; }
}

/* ============================================================================
   MOBILE APP SHELL (≤720px) - bottom tab bar switches Cities | Map | Day.
   Desktop above the breakpoint is untouched.
   ========================================================================= */
@media (max-width: 720px), (max-height: 540px) and (pointer: coarse) {
  :root { --tlc1: 74px; --tlc3: 8px; } /* time moved under the name, so the bars reclaim col3 */
  #app { padding: 0; gap: 0; }
  .layout { display: contents; } /* railcol + mapPanel become direct flex children of #app */

  /* --- top bar: logo · ‹ date › · share · settings --- */
  .bar { padding: 6px 8px; padding-top: max(6px, env(safe-area-inset-top)); gap: 4px;
    border-bottom: 1px solid var(--border); min-height: 52px; }
  .logo { font-size: 22px; }
  .bar .datebar { flex: 1 1 auto; min-width: 0; }
  .dnav { gap: 2px; touch-action: pan-y; } /* claim horizontal swipes for month-step (Brave/others steal them for scroll otherwise) */
  .dnav .nav { width: 42px; height: 42px; }
  .dnav .datelabel { min-width: 0; min-height: 42px; padding: 8px 2px; touch-action: pan-y; } /* the big centre area is the swipe target */
  .datelabel .dtext { font-size: 17px; }
  .barright { gap: 0; }
  .barright .toggles { display: none; }
  .barright .searchtoggle { display: none; } /* search lives in the Cities view */
  .iconbtn { min-width: 42px; height: 42px; background: none; border: none; }
  .iconbtn .ico { width: 21px; height: 21px; }
  .setbtn { width: 42px; height: 42px; }
  .setbtn .ico { width: 22px; height: 22px; }

  /* "Now" chip - a draggable pill that floats above the tab bar whenever the shown time isn't live.
     Default dock is bottom-RIGHT (right-thumb reach, clear of centred content); the user can drag it
     and it snaps to the nearest side (position persisted). Inline styles from the drag override these. */
  .livebtn { position: fixed; right: max(12px, env(safe-area-inset-right)); left: auto; transform: none; z-index: 30;
    bottom: calc(56px + env(safe-area-inset-bottom)); touch-action: none;
    transition: left .18s ease, right .18s ease, bottom .18s ease;
    min-width: 0; padding: 10px 20px; font-size: 14px; font-weight: 700;
    color: #fff; background: var(--accent); border: none; border-radius: 999px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .35); }
  .livebtn.dragging { transition: none; cursor: grabbing; box-shadow: 0 10px 26px rgba(0, 0, 0, .45); }
  .livebtn.on { display: none; }
  .livebtn .livetxt, .livebtn .dot { display: inline; }
  .livebtn .liveico { display: none; }
  body.has-notice .livebtn { bottom: calc(56px + var(--notice-h, 80px) + env(safe-area-inset-bottom)); }
  /* map: dock bottom-LEFT by default so it never sits under the zoom +/- controls (bottom-right) */
  #app[data-view="map"] .livebtn { left: max(12px, env(safe-area-inset-left)); right: auto; bottom: calc(64px + env(safe-area-inset-bottom)); }
  body.has-nudge #app[data-view] .livebtn { bottom: calc(68px + var(--nudge-h, 84px) + env(safe-area-inset-bottom)); } /* clear the install banner */

  /* --- views --- */
  .mapPanel, .timeline { display: none; }
  .railcol { display: flex; flex: 1 1 auto; min-height: 0; padding: 10px 10px 0; gap: 9px; }
  #app[data-view="map"] .railcol, #app[data-view="day"] .railcol { display: none; }
  #app[data-view="map"] .mapPanel { display: block; flex: 1 1 auto; min-height: 0; border: none; border-radius: 0; }
  /* content-height card, not a viewport-filling box: the rows only need ~40% of the screen, so
     flex:1 left a large bordered void below them. Sizing to content puts the app background under a
     compact card instead of a hollow bordered panel. (.tlscroll caps the row list, so it never
     overflows the view.) */
  #app[data-view="day"] .timeline { display: flex; flex-direction: column; flex: 0 0 auto; min-height: 0;
    max-height: calc(var(--app-h, 100dvh) - 118px); overflow: hidden; margin: auto 10px; padding: 10px 12px 6px; }

  /* --- Cities view --- */
  .railcol .searchbar { position: relative; left: auto; top: auto; transform: none; width: 100%; z-index: 30; transition: none; }
  body.searching .searchbar { top: auto; width: 100%; } /* search mode is a desktop affordance */
  .qinput { font-size: 16px; padding: 13px 28px 13px 14px; border-radius: 12px; } /* tighter right pad so the 14.5px placeholder fits (the clear-x only shows while typing) */
  /* keep the 16px value font (suppresses iOS focus-zoom) but shrink the placeholder so the full
     "Add a city, or convert ..." hint fits the narrow box instead of clipping mid-word */
  .qinput::placeholder { font-size: 14.5px; letter-spacing: -0.1px; }
  .qclear { width: 34px; height: 34px; right: 7px; }
  .qclear svg { width: 14px; height: 14px; }
  .qdrop { width: 100%; left: 0; transform: none; max-height: calc(var(--app-h, 100dvh) * 0.46); overflow-y: auto; overscroll-behavior: contain; }
  .qsug { padding: 13px 12px; font-size: 15px; }
  .qsug .tab { display: none; } /* there is no Tab key on a phone */
  .qres { font-size: 15px; padding: 11px 10px; }
  .railcol > .rail { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain;
    border: none; background: none; padding: 0 1px 84px; border-radius: 0; } /* clearance under the floating Now chip */
  .rail > .phead { display: none; } /* the tab bar already says Cities */
  .trow { padding: 12px; }
  .trow .nm b { font-size: 16px; line-height: 1.2; }
  .trow .nm span { font-size: 12px; }
  .trow .hm { font-size: 9px; }
  .trow .tin { font-size: 18px; padding: 9px 4px; min-height: 42px; } /* comfortable tap target */
  .trow .tinbtn { font-size: 18px; min-height: 42px; }
  .trow .dt { font-size: 12px; }
  .trow .nm .dst { font-size: 11px; }
  .trow .nm .tripchip { font-size: 11px; }
  .trow .tmeet { font-size: 15px; }
  .trow.home { margin-bottom: 15px; }
  .addz { padding: 14px; font-size: 14.5px; border-radius: 12px; }
  .tundo { font-size: 13px; min-height: 40px; max-height: 60px; padding: 6px 12px; }
  .tundo .ubtn { font-size: 14px; padding: 8px 12px; }
  .meetbar.off .meet-plan { height: 48px; font-size: 16px; border-radius: 12px; }
  .meet-ctrl { height: 48px; gap: 9px; }
  .meet-adv { width: 40px; height: 34px; }
  /* the off-state pair is one row: square the Advanced button to the 48px pill beside it and give it
     the same corner. The base `.meetbar.off .meet-adv` (38x36) outranks the plain `.meet-adv` above,
     so this needs the same two classes to land at all. */
  .meetbar.off .meet-adv { width: 48px; height: 48px; border-radius: 12px; }
  .meet-adv-pop input { font-size: 16px; padding: 9px 11px; border-radius: 9px; }
  .seg { height: 34px; font-size: 13px; }
  .advnote { font-size: 12.5px; }
  /* the recurring section is a third block in the same sheet: touch sizes, and the sheet itself has
     to scroll now that the panel can outgrow a phone. The cap is the --app-h one every other sheet
     respects (a bare dvh mis-resolves on some Android builds) rather than a viewport unit. */
  .recurcnt { font-size: 13px; }
  .recurn { width: 68px; }
  .recurdrift p, .ledgerrow, .recurrot { font-size: 13px; }
  .ledgerrow { grid-template-columns: minmax(0, 96px) minmax(0, 1fr) auto; }
  .ledgertrack { height: 9px; }
  .rotjump, .spljump { font-size: 14px; padding: 9px 14px; }
  .ovltoggle { font-size: 12.5px; padding: 2px 0; }
  .ovltable { font-size: 13px; }
  .ovltable th, .ovltable td { padding: 7px 8px; }
  .ovltable thead th { font-size: 10.5px; }
  .hoffrow { font-size: 13px; }
  .modal.advsheet { max-height: calc(var(--app-h, 100dvh) * 0.86); overflow-y: auto; overscroll-behavior: contain; }
  .meet-ctrl .lab { font-size: 11px; }
  .meet-dur { font-size: 16px; width: 66px; padding: 6px 2px; }
  .meet-find { width: 48px; height: 38px; border-radius: 9px; }
  .meet-x { font-size: 17px; padding: 9px 13px; }
  .meet-alts { padding: 0 9px 10px; flex-wrap: wrap; row-gap: 4px; }
  .meet-step button { font-size: 18px; padding: 9px 16px; }
  .meet-rank { font-size: 13px; min-width: 74px; }
  .meet-fit { font-size: 13px; }
  .meet-day { margin-left: auto; background: none; border: none; color: var(--accent-ink);
    font-size: 13px; font-weight: 700; padding: 8px 8px; border-radius: 8px; cursor: pointer; }
  .meet-why { font-size: 13px; padding: 7px 6px; }
  .meet-pin { width: 36px; height: 32px; }
  .meet-pin svg { width: 17px; height: 17px; }
  .whypanel { padding: 9px 9px 10px; gap: 5px; }
  .whyrow { font-size: 13px; }
  .wkndchip { font-size: 10.5px; }
  .whyfoot { font-size: 12.5px; }
  .advisor { padding: 0 9px 10px; }
  .advisorline { font-size: 13px; padding: 7px 6px; }
  .advisortx { font-size: 12.5px; }
  .meet-pins { padding: 0 9px 10px; gap: 7px; }
  .pinjump { font-size: 14px; padding: 8px 11px; }
  .pinx { font-size: 16px; padding: 8px 11px; }
  .cmp-open { font-size: 14px; padding: 9px 14px; }
  /* the compare sheet keeps the same --app-h cap every other sheet respects */
  .modal.cmpsheet { max-height: calc(var(--app-h, 100dvh) * 0.82); }
  .cmpsheet .cmpwrap { max-height: calc(var(--app-h, 100dvh) * 0.62); }
  .cmptable { font-size: 14px; }
  .cmptable th, .cmptable td { padding: 9px 10px; }
  .cmptable thead th { font-size: 11px; }
  /* the radar line is the first thing in the rail here (the Places header is hidden on a phone -
     the tab bar already says Cities), so it carries a full touch row of its own */
  .driftline { font-size: 12.5px; padding: 7px 6px; margin: 0 3px 9px; }
  .driftchip { font-size: 12px; padding: 6px 9px; }
  .modal.driftsheet { max-height: calc(var(--app-h, 100dvh) * 0.82); }
  .driftsheet .driftwrap { max-height: calc(var(--app-h, 100dvh) * 0.62); }
  .driftrow { padding: 9px 2px; }
  .driftpair { font-size: 14px; }
  .driftsub { font-size: 12.5px; }
  .driftoff { font-size: 13.5px; padding: 9px 13px; }
  /* the person section is another block above the list - give the sheet the height back */
  .modal.zedit:has(.zperson) { height: calc(var(--app-h, 100dvh) * 0.9); }

  /* --- Map view --- */
  .tzrule { display: none; } /* unreadable at phone width; the highlight still shows on the map */
  /* UTC-offset selector over the empty ocean above the strip: two interlocking rows of
     equilateral triangles (top points down, bottom points up) that mesh like shark jaws,
     giving a continuous toothed band. Even offsets top, odd offsets bottom. */
  .mapoffsets { position: absolute; top: 0; left: 0; right: 0; z-index: 5; overflow: hidden;
    padding: 8px 6px 10px; padding-top: max(8px, env(safe-area-inset-top));
    background: linear-gradient(to bottom, color-mix(in srgb, var(--bg) 78%, transparent), transparent); pointer-events: none; }
  .offrow { display: flex; gap: 2px; pointer-events: auto; touch-action: none; } /* claim the finger-sweep across chips (not page scroll) */
  .offrow.bot { transform: translateX(calc(100% / 24)); margin-top: -6px; } /* half a tooth right; slight overlap so slopes mesh but blunt tips stay visible */
  .offchip { position: relative; flex: 1 1 0; min-width: 0; aspect-ratio: 1.35; /* the SVG <path> draws the rounded trapezium */
    display: flex; justify-content: center; font-size: 9.5px; font-weight: 800; font-variant-numeric: tabular-nums;
    background: transparent; color: var(--text); border: none; cursor: pointer; padding: 0; }
  .offchip .offshape { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
  .offchip .offshape path { fill: color-mix(in srgb, var(--panel2) 84%, var(--accent)); transition: fill .12s; }
  .offchip .offlabel { position: relative; z-index: 1; } /* crisp text over the shape */
  .offrow.top .offchip { align-items: flex-start; } .offrow.top .offlabel { padding-top: 2px; }
  .offrow.bot .offchip { align-items: flex-end; } .offrow.bot .offlabel { padding-bottom: 2px; }
  .offchip.on .offshape path { fill: color-mix(in srgb, var(--accent) 52%, var(--panel2)); } /* an offset already on the dashboard */
  .offchip.act .offshape path { fill: var(--accent); } .offchip.act .offlabel { color: #fff; } /* the activated / highlighted offset */
  .offchip:active .offshape path { filter: brightness(1.3); }
  #app[data-view="map"] #mapCanvas { touch-action: none; } /* the map owns its gestures full-screen */
  /* sits just clear of the slider pill (which ends 50px up), not floating in mid-map */
  .mapzoom { display: flex; top: auto; bottom: 58px; right: 12px; gap: 9px; }
  /* reset reserves its slot (below +/-) even when hidden, so + and - never shift position */
  .mapzoom:not(.show) .mzreset { visibility: hidden; }
  .mz { width: 44px; height: 44px; border-radius: 12px; }
  .mz svg { width: 20px; height: 20px; }
  .sliderpill { left: 12px; right: 12px; bottom: 14px; padding: 13px 20px; touch-action: none; }
  .timeslider::-webkit-slider-thumb { width: 26px; height: 26px; }
  .timeslider::-moz-range-thumb { width: 26px; height: 26px; }
  .pin .dot { width: 14px; height: 14px; }
  .pin .tlab { font-size: 12px; }
  .pin .clab { font-size: 11px; }
  .mlab { font-size: 11px; }
  .maptip { padding: 10px 12px; }
  .maptip b { font-size: 14px; }
  .maptip div { font-size: 12px; }

  /* --- Day view --- */
  #app[data-view="day"] .timeline { justify-content: flex-start; } /* stack from the top so the lower colorbar follows the last row, not the panel floor */
  .timeline .phead { font-size: 12px; margin-bottom: 8px; }
  /* size the list to its rows (scroll only when they overflow) so the lower colorbar + axis
     sit right under the lowermost city instead of pinned to the panel bottom */
  .tlscroll { max-height: none; flex: 0 1 auto; min-height: 0; overscroll-behavior: contain; }
  /* stack the city name over its local time in the left column (tightly grouped: row-gap 0,
     with the inter-city space coming from the row margin), and let the band span both rows */
  .tlrow { margin: 9px 0; column-gap: 8px; row-gap: 0; grid-template-rows: auto auto; align-items: stretch; }
  .tlrow .nm { grid-column: 1; grid-row: 1; align-self: end; font-size: 14px; font-weight: 600; line-height: 1.15; }
  .tlrow .lt { grid-column: 1; grid-row: 2; align-self: start; text-align: left; font-size: 13px; line-height: 1.15; color: var(--muted); }
  /* meeting mode: the interval start-end shown compactly so it fits the narrow left column */
  .tlrow .ltrng { font-size: 10.5px; white-space: nowrap; }
  .tlrow .ltrng i { font-style: normal; opacity: .7; margin: 0 1px; }
  .tlrow.home .lt { color: color-mix(in srgb, var(--accent) 62%, var(--text)); }
  .tlrow .lt .dd { font-size: 10px; }
  .tlrow .bandvp { grid-column: 2; grid-row: 1 / span 2; }
  .band, .bandvp { height: 46px; border-radius: 9px; }
  .band .ht.maj { font-size: 14px; }
  .suitedge, .tlhours, .tlscrollx { gap: 8px; }
  .tlhours .h { font-size: 11px; }
  .tlhours .h span:not(:nth-child(6n+1)) { display: none; } /* 00 06 12 18 24 - 25 labels can't fit */
  /* lock toggle sized to the "DAY OVERVIEW" header text, sitting on its baseline */
  .locktoggle { top: 6px; right: 8px; padding: 2px 4px; background: none; border: none; }
  .locktoggle .ico { width: 16px; height: 16px; }
  /* keep the desktop invariant here too: same top as .locktoggle, and both are height:24px, so the
     pill, the padlock and the "DAY OVERVIEW" title share one centre line. The padding must stay
     horizontal-only - a vertical one eats the 24px box and drags the pill 3px above the padlock. */
  .meetclear { top: 6px; right: 36px; font-size: 12px; padding: 0 12px; }
  .meetclear svg { width: 12px; height: 12px; }
  .marker { width: 3px; }
  .mend { width: 34px; }
  .mend-x { width: 30px; height: 30px; }
  .mend-x svg { width: 12px; height: 12px; }
  .tldrag { touch-action: pan-y; } /* vertical = scroll the rows; horizontal = scrub (JS intent check) */

  /* --- bottom tab bar --- */
  .tabbar { display: flex; align-items: stretch; flex: none; z-index: 25;
    border-top: 1px solid var(--border); background: color-mix(in srgb, var(--panel) 94%, transparent);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    padding-bottom: env(safe-area-inset-bottom); }
  .tabbar .tab { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px; min-height: 0; padding: 5px 0; background: none; border: none; color: var(--muted);
    font-size: 11.5px; line-height: 1; font-weight: 600; cursor: pointer; }
  .tabbar .tab svg { width: 22px; height: 22px; }
  .tabbar .tab[aria-current="page"] { color: var(--accent-ink); }

  /* --- sheets (settings / zone editor / date picker / share) --- */
  .overlay { place-items: end stretch; grid-template-columns: minmax(0, 100%); overflow: hidden;
    height: var(--app-h, 100dvh); top: 0; bottom: auto; /* bottom-anchored sheets land on the TRUE visible bottom */
    background: rgba(0, 0, 0, .56); } /* cap the implicit track at the viewport - content-sized tracks let nowrap rows push the sheet off-screen */
  .modal { width: auto; min-width: 0; /* grid-stretch item: shrink below nowrap content or long IANA ids push the sheet past the viewport */
    border-radius: 18px 18px 0 0; border-left: none; border-right: none; border-bottom: none;
    padding: 26px 18px calc(18px + env(safe-area-inset-bottom));
    animation: sheetup .24s cubic-bezier(.3, 1, .4, 1); position: relative; }
  .modal.compact { width: auto; }
  .modal::before, .sharepop::before, .datepop::before { content: ""; position: absolute; top: 8px; left: 50%;
    transform: translateX(-50%); width: 40px; height: 4px; border-radius: 2px; background: var(--border); }
  .modal.zedit { height: calc(var(--app-h, 100dvh) * 0.78); display: flex; flex-direction: column; padding-top: 22px; }
  .modal.zedit .zlist { flex: 1 1 auto; max-height: none; overscroll-behavior: contain; }
  .modal.compact h3, .modal.zedit h3 { font-size: 17px; }
  .modal label { font-size: 15px; }
  .modal select, .modal input { font-size: 16px; padding: 9px 11px; border-radius: 9px; }
  .stclear { font-size: 14px; padding: 12px; } /* the sheet is wide - keep the touch-size button */
  .zclose { width: 42px; height: 42px; }
  .zclose svg { width: 17px; height: 17px; }
  .zlist li { padding: 13px 12px; font-size: 15px; }
  .zlist .zmeta { font-size: 12px; }
  .zlist .zt { font-size: 15px; }
  .modalbtns button { padding: 12px 20px; font-size: 15px; border-radius: 10px; }
  .setdisp button { padding: 12px 0; font-size: 15px; }
  .hrow input { max-width: 112px; }
  .zwknd .seg { font-size: 12.5px; }
  .ztrips:not(:empty) { max-height: calc(var(--app-h, 100dvh) * 0.34); overscroll-behavior: contain; }
  .ztrip { gap: 6px; }
  .ztrip input { font-size: 16px; } /* under 16px, iOS zooms the sheet on focus */
  .ztdest { font-size: 15px; }
  .ztsug li { padding: 9px 11px; font-size: 15px; }
  .ztsug .zmeta { font-size: 12px; }
  .ztadd { font-size: 14px; padding: 8px 14px; }
  .seclabel { font-size: 11px; }
  .plist { max-width: none; }
  .pitem button { font-size: 14px; padding: 8px 12px; }
  .pitem .pdel { padding: 8px 11px; }
  .pitem .pcopy { padding: 8px 11px; }
  .pitem .pcopy .ico { width: 15px; height: 15px; }
  .prow button { padding: 9px 16px; font-size: 14px; }
  .srow input { width: 76px; }
  .sharepop, .datepop { left: 0; right: 0; top: auto; bottom: 0; border-radius: 18px 18px 0 0;
    padding: 24px 14px calc(16px + env(safe-area-inset-bottom));
    animation: sheetup .24s cubic-bezier(.3, 1, .4, 1); }
  /* docked on top of the tab bar (bar.js measures it): the bar below already holds the safe area */
  .sharepop.ontabs, .datepop.ontabs { padding-bottom: 16px; }
  .shopt { font-size: 16px; padding: 14px 12px; border-radius: 10px; }
  .shopt .ico { width: 20px; height: 20px; }
  .shgrp { font-size: 11px; }
  /* three groups now run to ten-odd rows - cap the sheet so it can never push past the viewport's
     top edge (it is bottom-anchored via top:auto) and let it scroll internally instead */
  .sharepop { max-height: calc(var(--app-h, 100dvh) * 0.85); overflow-y: auto; }
  .dpcal { width: min(380px, 100%); margin: 0 auto; }
  .dpcalhead { padding: 0 2px; }
  .dpmnav { width: 44px; height: 44px; border-radius: 10px; }
  .dpmnav .chev { width: 18px; height: 18px; }
  .dpmonth { font-size: 16px; }
  .dpyear { font-size: 16px; padding: 6px 0; width: 6ch; }
  .dpweek span { font-size: 12px; }
  .dpweek, .dpdays { gap: 4px; }
  .dpday { font-size: 15px; border-radius: 10px; }

  /* press feedback (no hover on touch) */
  .iconbtn:active, .setbtn:active, .nav:active, .mz:active, .shopt:active, .qsug:active,
  .zlist li:active, .addz:active, .dpday:active, .dpmnav:active, .meet-plan:active,
  .meet-find:active, .ubtn:active, .cn-ok:active, .livebtn:active { opacity: .65; }

  /* cookie notice sits above the tab bar, inside the safe area */
  .cookienotice { bottom: calc(56px + env(safe-area-inset-bottom)); font-size: 13px; }
  .cookienotice .cn-ok { padding: 10px 18px; font-size: 13.5px; }
}
@keyframes sheetup { from { transform: translateY(24%); opacity: .6; } }
/* light theme: the weekend red fails contrast on white at small sizes */
:root[data-theme="light"] .dpweek span.we, :root[data-theme="light"] .dpday.we { color: #c0392b; }

/* ============================================================================
   LANDSCAPE (short-height touch: phones rotated, small tablets). The tab shell
   applies here too; reclaim vertical space and keep every view usable.
   ========================================================================= */
@media (max-height: 540px) and (orientation: landscape) and (pointer: coarse) {
  .bar { min-height: 42px; padding: 4px 10px; padding-top: max(4px, env(safe-area-inset-top)); }
  .logo { font-size: 20px; }
  .dnav .nav { width: 38px; height: 38px; }
  .datelabel .dtext { font-size: 16px; }
  .iconbtn, .setbtn { min-width: 40px; height: 40px; }
  /* bottom tab bar: shorter, icon+label side by side to save height */
  .tabbar .tab { flex-direction: row; gap: 6px; min-height: 40px; font-size: 12px; }
  .tabbar .tab svg { width: 20px; height: 20px; }
  /* Cities: keep a single column (the tile reorder/swipe math assumes vertical stacking);
     tiles simply stretch to the wider width. */
  .trow .nm b { font-size: 15px; }
  /* Map: offset selector + slider tighter over the short panel */
  .mapoffsets { padding: 4px 8px 6px; padding-top: max(4px, env(safe-area-inset-top)); }
  .offchip { padding: 5px 0; }
  .sliderpill { bottom: 10px; padding: 9px 16px; }
  .mapzoom { bottom: 64px; }
  /* Now chip clears the shorter tab bar */
  #app[data-view] .livebtn { bottom: calc(52px + env(safe-area-inset-bottom)); }
  body.has-notice .livebtn { bottom: calc(52px + var(--notice-h, 70px) + env(safe-area-inset-bottom)); }
  body.has-nudge #app[data-view] .livebtn { bottom: calc(64px + var(--nudge-h, 84px) + env(safe-area-inset-bottom)); } /* clear the install banner */
  /* Day view: slightly shorter bars so more cities fit the short height */
  .band, .bandvp { height: 34px; }
  .tlrow { margin: 3px 0; }
  /* sheets: cap height so the keyboard/clock never overflow a short viewport */
  .overlay .modal, .sharepop, .datepop { max-height: calc(var(--app-h, 100dvh) * 0.92); overflow-y: auto; }
  .modal.tpick .tp-body { min-height: 180px; }
  .tp-clock { width: 190px; height: 190px; }
}

/* Short LANDSCAPE phones can't fit the shell (map/day/sheets overflow) -> ask to rotate rather than
   render a broken layout. Tablets keep plenty of height even in landscape, so they never trip this. */
#rotatehint { display: none; }
@media (orientation: landscape) and (max-height: 450px) and (pointer: coarse) {
  #app, #modalRoot { display: none !important; }
  #rotatehint { display: grid; position: fixed; inset: 0; z-index: 300; place-items: center; text-align: center;
    padding: 20px; background: var(--bg); }
  #rotatehint .rh-in { max-width: 360px; display: grid; gap: 10px; justify-items: center; }
  #rotatehint svg { width: 44px; height: 44px; color: var(--accent-ink); }
  #rotatehint p { font-size: 18px; font-weight: 700; margin: 0; color: var(--text); }
  #rotatehint small { font-size: 13px; color: var(--muted); line-height: 1.5; }
}

/* --- micro-poll (/p/<id>, js/poll.js) ---------------------------------------- */
/* body.pollmode swaps the whole app surface for the poll card; the board still boots
   underneath (ephemeral, never persisted) and the brand link at the top restores it. */
#pollRoot { display: none; }
body.pollmode #app, body.pollmode #boot { display: none; }
body.pollmode #pollRoot { display: block; position: fixed; inset: 0; overflow-y: auto;
  background: var(--bg); padding: 20px 14px calc(28px + env(safe-area-inset-bottom)); }
/* The storage notice is a FIXED bar and the app's own reserve pads #app, which poll mode
   hides - so a first-time voter's Send button sat underneath it. Reserve here too. */
body.pollmode.has-notice #pollRoot { padding-bottom: calc(28px + var(--notice-h, 60px) + env(safe-area-inset-bottom)); }
.pollcard { max-width: 560px; margin: 0 auto; display: grid; gap: 14px; }
.pollbrand { color: var(--muted); text-decoration: none; font-size: 13px; }
.pollbrand:hover { color: var(--text); }
.pollcard h2 { margin: 0; font-size: 22px; overflow-wrap: anywhere; }
.pollmeta { margin: 0; color: var(--muted); font-size: 13px; }
.pollopt { background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  padding: 12px 14px; display: grid; gap: 9px; }
.pollopt.lead { border-color: var(--accent); }
.polltime { font-size: 16px; font-weight: 600; }
.pollnote { margin: 0; color: var(--muted); font-size: 12.5px; }
.polltally { font-size: 13px; color: var(--accent-ink); }
.pollstates { display: grid; gap: 4px; font-size: 12.5px; color: var(--muted); overflow-wrap: anywhere; }
.tri { display: flex; gap: 8px; }
.tri button { flex: 1; background: var(--panel2); color: var(--text); border: 1px solid var(--border);
  border-radius: 9px; padding: 8px 0; cursor: pointer; font-size: 13px; }
.tri button[aria-pressed="true"] { border-color: var(--accent); color: var(--accent-ink); background: none; }
.pollname { display: grid; gap: 8px; }
.pollname input { background: var(--panel); color: var(--text); border: 1px solid var(--border);
  border-radius: 9px; padding: 10px 12px; font-size: 14px; }
.pollerr { margin: 0; color: #e0646c; font-size: 13px; }
.pollbtn { background: var(--accent); color: #fff; border: none; border-radius: 10px;
  padding: 11px 16px; font-size: 14.5px; cursor: pointer; text-align: center; text-decoration: none; }
.pollbtn[disabled] { opacity: .45; cursor: default; }
.pollbtn.ghost { background: var(--panel2); color: var(--text); border: 1px solid var(--border); }
.pollchosen { background: var(--panel); border: 1px solid var(--accent); border-radius: 12px;
  padding: 14px; display: grid; gap: 9px; }
.polladmin { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.pollpop { display: grid; gap: 10px; }
.pollpop .polllink { background: var(--panel2); color: var(--text); border: 1px solid var(--border);
  border-radius: 9px; padding: 10px 12px; font-size: 13px; width: 100%; box-sizing: border-box; }
/* tie plateau: the finder's penalty is linear, so whole ranges of options score identically and
   the ORDER is the tie-break talking. Mark it, and let the why-panel say what it protects. */
.meet-rank .tiedmark { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-left: 5px; }
.whytie { margin: 6px 0 0; font-size: 12px; line-height: 1.5; color: var(--muted); }
