:root {
    --bg-color: #ffffff;
    --text-color: #212529;
    --border-color: #dee2e6;
    --header-bg: #f8f9fa;
    --accent-color: #4f46e5;
    --muted-text: #495057;
    --hover-bg: #fdfdfd;
    --event-bg: #e0e7ff;
    /* The "Maybe" chip, on three pages that are never seen side by side. One definition so they
       cannot drift apart; the reasoning for the colours is on .entry-maybe-badge in CalendarRenderer. */
    --chip-maybe-bg: #facc15;
    --chip-maybe-fg: #422006;
    --chip-maybe-edge: #a16207;
    --font-main: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}

body {
    font-family: var(--font-main);
    color: var(--text-color);
    background-color: var(--bg-color);
    line-height: 1.5;
    margin: 1rem;
    padding: 0;
}

/* Shared FUTURE/ALL toggle for list views (see web/TimeFilterToggle), and the List/Calendar
   selector on the schedule-problems report (see web/ProblemViewToggle). Two different controls
   writing two different query parameters, sharing one look so they cannot drift apart. */
.time-toggle, .view-toggle {
    display: inline-flex;
    margin-top: 1rem;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid var(--border-color);
}
.time-toggle a, .view-toggle a {
    padding: 6px 16px;
    text-decoration: none;
    font-size: 0.9rem;
    color: var(--muted-text);
    background-color: var(--surface, #fff);
}
.time-toggle a + a, .view-toggle a + a {
    border-left: 1px solid var(--border-color);
}
.time-toggle a.active, .view-toggle a.active {
    background-color: var(--accent-color);
    color: #fff;
    font-weight: 600;
}

/* Anonymous viewer's entry-zone/browser-zone switch (see web/ZoneToggle). Buttons, not
   links: BrowserZoneScript swaps the times in place rather than reloading the page. */
.zone-toggle {
    display: inline-flex;
    margin-top: 1rem;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid var(--border-color);
}
.zone-toggle button {
    padding: 6px 16px;
    border: none;
    cursor: pointer;
    font-size: 0.9rem;
    font-family: inherit;
    color: var(--muted-text);
    background-color: var(--surface, #fff);
}
.zone-toggle button + button {
    border-left: 1px solid var(--border-color);
}
.zone-toggle button.active {
    background-color: var(--accent-color);
    color: #fff;
    font-weight: 600;
}

/* Shared chrome for `.page` list/detail views (see web/Page). Each renderer's
   own <style> sets only its `.page { max-width }` plus view-specific rules. */
.page { margin: 0 auto; padding: 1rem; }
.page h1 { margin: 0 0 1rem; font-size: 1.4rem; }

/* Shared lateral navigation across the read-only view pages (Page.viewNav).
   A flex bar that WRAPS on narrow viewports — never a horizontal scroller. */
/* Sticky, so it stays reachable down a long calendar. Three consequences:

   1. It needs an OPAQUE BACKGROUND — content scrolls behind it — and PADDING rather than
      margin for the horizontal inset, so that background runs the full content width.
   2. Z-INDEX SCALE. Keep new values on it:
           50  popups anchored to content (DisclosureMenu's day and fix menus)
           30  this nav
           10  the calendar's weekday header, which parks under the nav
      This bar creates a stacking context, so anything nested INSIDE it (the year overview
      panel) is scoped to 30 whatever z-index it names — it cannot out-stack the 50s. What
      keeps those apart is DisclosureMenu's one-open-at-a-time, not a z-index.
   3. Anything sticking BELOW it offsets by --nav-height, measured by StickyLayerHeights. The
      height is not a constant: the bar wraps on a narrow screen. Never a literal. */
.view-nav {
    position: sticky;
    top: 0;
    z-index: 30;
    background-color: var(--bg-color);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.3rem 1.1rem;
    padding: 0.55rem 0;
    margin-bottom: 1rem;
    font-size: 0.9rem;
}
.view-nav a { color: var(--accent-color); text-decoration: none; }
.view-nav a:hover { text-decoration: underline; }
.view-nav .active { color: var(--text-color); font-weight: 600; }

/* Empty-state message shared by the booked/planned list views. */
.empty-state { margin-top: 1rem; color: var(--muted-text); }

/* Utility: a value that must never break mid-word. Used to keep the parts of a stacked
   date/time (ZonedTimeTag.renderDateTimeStacking) or a City/Country pair intact while still
   allowing the cell to wrap *between* those parts on a narrow viewport — never a horizontal scroll. */
.nowrap { white-space: nowrap; }

/* Booking forms (book-flight, change-flight): a lookup box plus the two-column
   departure/arrival fieldsets. Pages add class="booking-form" to their .page. */
.booking-form { max-width: 920px; }

.booking-form .lookup {
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
}
.booking-form .lookup p {
    margin: 0 0 0.6rem;
    font-size: 0.8rem;
    color: var(--muted-text);
}
.booking-form .lookup-row {
    display: flex;
    gap: 0.5rem;
    align-items: flex-end;
    flex-wrap: wrap;
}
.booking-form .lookup-row .field { flex: 1; min-width: 8rem; }

.booking-form .service-field { margin-bottom: 1rem; }

.booking-form .fieldsets {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
    margin-bottom: 1rem;
}
.booking-form fieldset {
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 0.75rem 1rem;
    margin: 0;
}
.booking-form legend {
    font-weight: 600;
    padding: 0 0.4em;
    color: var(--muted-text);
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.booking-form .field { margin-bottom: 0.5rem; }
.booking-form .field:last-child { margin-bottom: 0; }
.booking-form .field label {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 0.8rem;
    color: var(--muted-text);
}
.booking-form .field input {
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--border-color);
    border-radius: 5px;
    font-size: 0.9rem;
    font-family: inherit;
    width: 100%;
    box-sizing: border-box;
}
.booking-form .field input:focus {
    outline: 2px solid var(--accent-color);
    outline-offset: 1px;
    border-color: transparent;
}
.booking-form .field-row {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}
.booking-form .field-row .field { flex: 2; margin-bottom: 0; }
.booking-form .field-row .field.narrow { flex: 1; }

.booking-form .error {
    display: block;
    margin-top: 2px;
    font-size: 0.8rem;
    color: #b00;
}
.booking-form .global-errors {
    margin: 0 0 1rem;
    padding-left: 1.2rem;
    font-size: 0.8rem;
    color: #b00;
}

/* The main form submit ("Book Flight" / "Save changes"); excludes the lookup and
   leg-choice buttons, which are also type=submit but styled separately below. */
.booking-form button[type=submit]:not(.lookup-btn):not(.leg-btn) {
    background: var(--accent-color);
    color: #fff;
    border: none;
    border-radius: 6px;
    padding: 0.45rem 1.4rem;
    font-size: 0.95rem;
    font-family: inherit;
    cursor: pointer;
}
.booking-form button[type=submit]:not(.lookup-btn):not(.leg-btn):hover { opacity: 0.88; }

/* Leg chooser: one full-width button per segment the lookup returned. Each part of
   the route (airport + time) is its own nowrap unit and the button is a wrapping
   flex row, so a narrow viewport stacks the parts instead of scrolling sideways. */
.booking-form .leg-options {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
.booking-form .leg-btn {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.5rem;
    width: 100%;
    text-align: left;
    background: var(--header-bg);
    color: inherit;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    padding: 0.45rem 0.7rem;
    font-size: 0.9rem;
    font-family: inherit;
    cursor: pointer;
}
.booking-form .leg-btn:hover { border-color: var(--accent-color); }
.booking-form .leg-caption {
    min-width: 5.5rem;
    font-weight: 600;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted-text);
}
.booking-form .leg-endpoint { white-space: nowrap; }
.booking-form .leg-airport { font-weight: 600; }
.booking-form .leg-time { color: var(--muted-text); }
.booking-form .leg-arrow { color: var(--muted-text); }

/* Lookup button: its own style (it is type=submit but excluded from the main
   submit rule above). The margin-bottom lifts it off the row's flex-end bottom
   so it lines up with the input boxes. */
.booking-form .lookup-btn {
    background: var(--accent-color);
    color: #fff;
    border: none;
    border-radius: 6px;
    padding: 0.4rem 1.4rem;
    margin-bottom: 0.5rem;
    font-size: 0.9rem;
    font-family: inherit;
    box-sizing: border-box;
    cursor: pointer;
    white-space: nowrap;
}
.booking-form .lookup-btn:hover { opacity: 0.88; }

/* The paste-and-fill widgets: the shared address one (templates/fragments/address-paste.html)
   and the Sessionize conference prefill (templates/fragments/sessionize-prefill.html), which
   share this row. Styling lives here rather than in each form's <style> so the markup, its
   script, and its styling travel together — the address widget was duplicated across four forms
   and drifted. Pages still supply their own .error rule, which the widgets' error divs borrow. */
.paste-row {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    align-items: flex-end;
}
.paste-row textarea {
    flex: 1;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--border-color);
    border-radius: 5px;
    font-size: 0.85rem;
    font-family: inherit;
    resize: vertical;
    min-height: 3.5rem;
    box-sizing: border-box;
}
.paste-row textarea:focus {
    outline: 2px solid var(--accent-color);
    outline-offset: 1px;
    border-color: transparent;
}
/* The Sessionize widget pastes one URL, so it is an input rather than a textarea. min-width: 0
   is load-bearing: a flex item defaults to min-width auto, and its long placeholder would
   otherwise refuse to shrink and push the row past a narrow viewport. */
.paste-row input[type=url] {
    flex: 1;
    min-width: 0;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--border-color);
    border-radius: 5px;
    font-size: 0.85rem;
    font-family: inherit;
    box-sizing: border-box;
}
.paste-row input[type=url]:focus {
    outline: 2px solid var(--accent-color);
    outline-offset: 1px;
    border-color: transparent;
}
.parse-btn {
    background: var(--accent-color);
    color: #fff;
    border: none;
    border-radius: 6px;
    padding: 0.35rem 0.9rem;
    font-size: 0.85rem;
    font-family: inherit;
    cursor: pointer;
    white-space: nowrap;
}
.parse-btn:hover { opacity: 0.88; }
/* The "why you are here" banner (templates/fragments/problem-context.html): what the fix link Ted
   clicked was about, shown above the form it landed on.

   Amber fill, whatever kind of problem it is — the same rule the problem calendar follows (Ted,
   2026-08-21): the first thing a marker has to say is "something here is wrong", and the kind is
   the second question. Kind survives as the 4px left edge, in the hue its column wears in the list
   view, so the banner and the band that was clicked read as the same thing. Amber, never red: a
   missing hotel is entirely recoverable — red is reserved for the irreversible. */
.problem-context {
    background: #fef3c7;
    color: #78350f;
    border-left: 4px solid #b45309;
    border-radius: 6px;
    padding: 0.6rem 0.8rem;
    margin-bottom: 1rem;
    font-size: 0.85rem;
}
.problem-context--bed { border-left-color: #1d4ed8; }
.problem-context--travel { border-left-color: #b45309; }
.problem-context--duplicate { border-left-color: #c2410c; }
.problem-context--clash-city { border-left-color: #7c3aed; }
.problem-context--clash-scheduling { border-left-color: #dc2626; }

.problem-context-title { font-weight: 600; }
.problem-context-detail { margin-top: 2px; }
.problem-context-causes {
    margin: 0.4rem 0 0;
    padding-left: 1.1rem;
    opacity: 0.9;
}
.problem-context-causes li { margin: 1px 0; }
.problem-context-back {
    display: inline-block;
    margin-top: 0.5rem;
    color: #78350f;
    text-decoration: none;
    font-weight: 600;
}
.problem-context-back:hover { text-decoration: underline; }

/* The link beside a rejected departure time, naming the leg already booked in that window.
   Shared by the four forms that refuse an overlapping journey (book/change x train/flight).
   Permanent underline, never hover-only: the iPad has no pointer, so a link that announces
   itself only on :hover is invisible there at every moment. */
.overlap-link {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.8rem;
    color: #b45309;
    text-decoration: underline;
}

/* "Add to Google" — the per-entry push into Google Calendar (see web/GoogleCalendarLink), shared
   by the conference, gathering and private-event lists. The icon is always visible and is itself
   the affordance; the label's underline is permanent for the same reason .overlap-link's is.
   Sized in em so it rides at the text size of whichever cell it lands in. */
.gcal-add {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: var(--accent-color);
    text-decoration: none;
    /* Padding cancelled by an equal negative margin: it buys a finger-sized hit area on the iPad
       without moving the glyph or the line it sits on. The icon-only form is otherwise a ~18px
       target, which is half what a thumb needs. */
    padding: 0.35rem;
    margin: -0.35rem;
}
.gcal-add svg {
    /* Absolute rather than em-relative: the icon-only form has no text beside it to take its size
       from, and at the label's 0.8rem it rendered as a speck. flex: none so a narrow column
       squeezes the label rather than the glyph. */
    width: 1.15rem;
    height: 1.15rem;
    flex: none;
}
.gcal-add .gcal-label {
    font-size: 0.8rem;
    text-decoration: underline;
}

/* The pencil (edit) and the bin (cancel), on the calendar, the itinerary and the conference detail
   page. One rule for both, here rather than in each renderer, because they share a slot and must
   match exactly: three hand-kept copies is how they drift (the .chip-maybe tokens above were moved
   here for the same reason). Settled with Ted 2026-09-22, after the 12px, 65%-opacity versions were
   too small and too faint to find on the iPad.
   - Accent colour at full opacity, the app's "this is a control" colour. Never red: cancelling a
     transfer or a train is recorded as a new event, and red is kept for what cannot be undone.
   - 16px glyph, and a 28x44 hit area from padding cancelled by an equal negative margin — the
     .gcal-add trick — so the glyph and the line it sits on do not move.
   - The hit area is 28 wide rather than 44 so a pencil and the bin beside it do not overlap: each
     reaches 6px past its glyph, and the 8px margin-left keeps the bin's area starting where the
     pencil's ends. Widen the padding and the margin has to grow with it, or a tap meant for edit
     lands on cancel. */
.edit-pencil,
.cancel-bin {
    display: inline-block;
    color: var(--accent-color);
    text-decoration: none;
    /* Centred on the capitals of the text beside it, not on its x-height. `middle` centres on
       baseline + half an x-height, which at 16px put the glyph visibly low, below the baseline of a
       title (WebKit, 2026-09-22). With the SVG a block, this box's baseline is its bottom margin
       edge, so this lifts the glyph's bottom to 0.35em (half a cap height) minus half the glyph. */
    vertical-align: calc(0.35em - 8px);
    line-height: 0;
    padding: 14px 6px;
    margin: -14px -6px -14px 0.5rem;
}
.edit-pencil svg,
.cancel-bin svg {
    /* Block, so the link has no line box of its own: its baseline is then its bottom margin edge,
       which is what the vertical-align above is measured from. Left inline, the baseline is the
       SVG's, and the offset would land somewhere else. */
    display: block;
    width: 16px;
    height: 16px;
}
