/* app.css — Clerkenwell Trains, primary stylesheet.
 *
 * Single base stylesheet loaded by EVERY page (matches the Lunar pattern
 * of one shared CSS file).  Contains all components, layout, typography
 * and reset rules used across the site.
 *
 * Companion stylesheets:
 *   - fonts.css        - @font-face declarations only
 *   - theme-dark.css   - CSS variables for dark mode (the default)
 *   - theme-light.css  - :root[data-theme="light"] overrides
 *   - continue.css     - additional rules for the booking-handoff page
 *   - special-offers.css - additional rules for the deals page
 *
 * History: built 2026-05-11 by concatenating styles.css (everything-else
 * pages' chrome) + home.css (the home-page search-form + result cards).
 * The previous .field naming collision was resolved by renaming the
 * home-page search-form fields to .search-field.
 */


/* =================================================================
 * Part 1: shared chrome (was styles.css)
 * ================================================================= */

/***************************************************************************************/
/**** styles.css — UK-rail journey planner.  Visual identity follows Trainline for an ***/
/**** internal-testing baseline (navy header, cream hero, journey-card grid with       ***/
/**** Standard + First price columns, side panel on results, mint accent button).      ***/
/***************************************************************************************/

/***************************************************************************************/
/**** Reset + typography.                                                           ****/
/***************************************************************************************/

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    height: 100%;
    font-family: "Noto Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    color: var(--text);
    background: var(--bg);
    line-height: 1.4;
}

body {
    margin: 0;
    min-height: 100%;
    background: var(--bg);
    /**** Sticky-footer flex pattern — body fills viewport, content area grows to push   ****/
    /**** the .site-footer to the bottom whenever the content is shorter than the        ****/
    /**** viewport.  Stops the page from being 29px taller than the viewport (which      ****/
    /**** forced the right-hand scrollbar to be permanent).                                ****/
    display: flex;
    flex-direction: column;
}

.site-footer { margin-top: auto; }

/***************************************************************************************/
/**** FAQ section — homepage-only block that handles trust + objection questions     ****/
/**** before the user reaches the footer.  Native <details>/<summary>, no JS.        ****/
/**** All colours use theme tokens so light + dark render correctly with no per-     ****/
/**** theme overrides.                                                                ****/
/***************************************************************************************/

.faq {
    max-width: 820px;
    margin: 48px auto 32px;
    padding: 0 24px;
}

.faq h2 {
    font-size: 24px;
    color: var(--text-bright);
    margin: 0 0 20px 0;
    text-align: center;
}

.faq-item {
    background: var(--panel);
    border: 1px solid var(--panel-border);
    border-radius: 8px;
    margin-bottom: 10px;
    overflow: hidden;
}

.faq-question {
    padding: 16px 52px 16px 20px;
    cursor: pointer;
    font-weight: 600;
    color: var(--text-bright);
    list-style: none;
    position: relative;
    user-select: none;
}

.faq-question::-webkit-details-marker { display: none; }
.faq-question::marker                 { content: ""; }

.faq-question::after {
    content: "+";
    position: absolute;
    right: 20px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 22px;
    font-weight: 400;
    line-height: 1;
    color: var(--text-dim);
    transition: transform 0.2s;
}

.faq-item[open] .faq-question::after {
    transform: translateY(-50%) rotate(45deg);
}

.faq-answer {
    padding: 0 20px 18px 20px;
    color: var(--text);
    line-height: 1.6;
    font-size: 15px;
}

.faq-answer p { margin: 0; }


/***************************************************************************************/
/**** Information tabs container — wraps "How it works" + FAQ in a single tab strip. ****/
/**** Inherits .tab-button / .tab-panel / .tabs-bar styles from app.css (1495+).      ****/
/***************************************************************************************/

.info-tabs {
    max-width: 820px;
    margin: 48px auto 32px;
    padding: 0 24px;
}

.info-tabs .tabs-bar {
    border-radius: 8px 8px 0 0;
}

.info-tabs .tab-panel.active {
    padding-top: 24px;
}

.info-tabs h2 {
    font-size: 24px;
    color: var(--text-bright);
    margin: 0 0 16px 0;
    text-align: center;
}

/**** "How it works" content — pricing+sign-in explainer.                            ****/

.how-lead {
    color: var(--text);
    line-height: 1.65;
    font-size: 15px;
    margin: 0 0 24px 0;
    text-align: center;
}

.how-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin-bottom: 24px;
}

@media (max-width: 640px) {
    .how-grid { grid-template-columns: 1fr; }
}

.how-card {
    background: var(--panel);
    border: 1px solid var(--panel-border);
    border-radius: 8px;
    padding: 18px 20px;
}

.how-card h3 {
    font-size: 16px;
    color: var(--text-bright);
    margin: 0 0 10px 0;
}

.how-card p {
    color: var(--text);
    line-height: 1.6;
    font-size: 14px;
    margin: 0;
}

.how-compare {
    background: var(--panel);
    border: 1px solid var(--panel-border);
    border-radius: 8px;
    padding: 18px 20px;
}

.how-compare h3 {
    font-size: 16px;
    color: var(--text-bright);
    margin: 0 0 10px 0;
}

.how-compare ul {
    margin: 0 0 12px 0;
    padding-left: 20px;
    color: var(--text);
    line-height: 1.65;
    font-size: 14px;
}

.how-compare li { margin-bottom: 6px; }

.how-footnote {
    color: var(--text-dim);
    font-size: 13px;
    line-height: 1.55;
    margin: 0;
}


/***************************************************************************************/
/**** Browser-family cross-link strip in the footer.  Sits above the legal copy and  ****/
/**** points to Jade / Lunar / Hedgehog so visitors who care about ad-block / VPN /   ****/
/**** kids-safe discover them.  Uses inherit colours so the strip styles itself in    ****/
/**** both dark and light mode without per-theme overrides.                            ****/
/***************************************************************************************/

.footer-browsers {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    gap: 14px;
    margin-bottom: 14px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--panel-border);
    font-size: 12px;
}

.footer-browsers-label {
    color: var(--text-dim);
}

.footer-browser-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--text);
    text-decoration: none;
    transition: opacity 0.12s;
}

.footer-browser-link:hover {
    opacity: 0.75;
}

.footer-browser-link img {
    width: 16px;
    height: 16px;
    border-radius: 3px;
    display: inline-block;
    vertical-align: middle;
}

/***************************************************************************************/
/**** Universal button feedback — quick press scale, plus animated dots when loading. ****/
/***************************************************************************************/

button:not(:disabled):active { transform: scale(0.97); }

button.is-loading::after {
    content: "";
    display: inline-block;
    min-width: 18px;
    text-align: left;
    margin-left: 6px;
    animation: btn-dots 1.2s steps(3, end) infinite;
}

@keyframes btn-dots {
    0%   { content: " ."; }
    33%  { content: " .."; }
    66%  { content: " ..."; }
    100% { content: " ."; }
}

h1, h2, h3, h4 {
    margin: 0 0 0.5em 0;
    font-weight: 700;
    letter-spacing: -0.01em;
}

a {
    color: inherit;
    text-decoration: none;
}

input, select, button {
    font: inherit;
    color: inherit;
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 24px;
}

/***************************************************************************************/
/**** Header — dark navy bar with wordmark + nav links.                             ****/
/***************************************************************************************/

.site-header {
    background: #1a1a3b;
    color: #fff;
    padding: 14px 0;
}

.site-header .container {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.wordmark {
    font-size: 22px;
    font-weight: 800;
    letter-spacing: -0.02em;
    color: #ffffff;
}

.wordmark-accent {
    color: #2560e6;
    margin-left: 1px;
}

.top-nav a {
    margin-left: 24px;
    font-size: 14px;
    font-weight: 500;
    opacity: 0.9;
}

.top-nav a:hover {
    opacity: 1;
}

/***************************************************************************************/
/**** Search hero.  Cream backdrop + central rounded card.                          ****/
/***************************************************************************************/

.search-hero {
    padding: 56px 0 80px 0;
    background: linear-gradient(var(--bg) 0%, var(--panel) 100%);
}

.hero-title {
    font-size: 34px;
    margin-bottom: 28px;
    color: var(--text-bright);
}

.search-card {
    background: var(--panel);
    border-radius: 14px;
    padding: 10px;
    box-shadow: 0 4px 24px rgba(30, 30, 60, 0.08);
    display: grid;
    grid-template-columns: 1.4fr 1.4fr 1fr 0.7fr 0.55fr 0.9fr 1.05fr auto;
    gap: 4px;
    align-items: stretch;
}

.field--pax { min-width: 70px; }

.field {
    position: relative;
    padding: 10px 14px;
    border-radius: 10px;
    /**** Use the theme's panel token so dark mode flips correctly.  Hardcoded #fafafa */
    /**** was invisible on /jit-calendar dark mode (labels disappeared into a white     */
    /**** rectangle).                                                                      */
    background: var(--panel);
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.field:hover {
    background: var(--panel-border);
}

.field label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 2px;
    white-space: nowrap;
}

.field input,
.field select {
    border: none;
    outline: none;
    background: transparent;
    width: 100%;
    font-size: 15px;
    font-weight: 500;
    padding: 0;
}

.search-btn {
    background: #1a73e8;
    color: #ffffff;
    border: none;
    border-radius: 10px;
    padding: 0 22px;
    font-weight: 700;
    font-size: 15px;
    cursor: pointer;
    transition: background 0.12s;
    min-width: 170px;
}

.search-btn:hover {
    background: #155ab9;
}

/**** Suggestions dropdown under from/to inputs. ****/
.suggestions {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    margin: 4px 0 0 0;
    padding: 4px 0;
    list-style: none;
    background: #fff;
    border-radius: 8px;
    box-shadow: 0 4px 18px rgba(30, 30, 60, 0.14);
    z-index: 10;
    max-height: 260px;
    overflow-y: auto;
}

.suggestions li {
    padding: 8px 14px;
    cursor: pointer;
    font-size: 14px;
    display: flex;
    justify-content: space-between;
}

.suggestions li:hover,
.suggestions li.selected {
    background: #f0f5ff;
}

.suggestions li .crs {
    font-family: "Liberation Mono", "SF Mono", "Cascadia Mono", "Cascadia Code", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", monospace;
    font-size: 12px;
    color: var(--text-dim);
    letter-spacing: 0.04em;
}

/***************************************************************************************/
/**** Styled-select dropdown — replaces native <select> popups so the highlight uses ****/
/**** our own .suggestions colour instead of the OS / Chromium high-contrast default.****/
/****                                                                                ****/
/**** The native <select> stays in the DOM but display: none-d by the JS module.     ****/
/**** Our .styled-select-btn proxies it visually + dispatches click events to the    ****/
/**** sibling .suggestions list which carries the existing dropdown styling.         ****/
/***************************************************************************************/

.styled-select-wrap {
    position: relative;
    display: block;
}

.styled-select-btn {
    /* Match the .search-field select dimensions so the proxy button slots into the */
    /* same form rows without a layout shift on first paint.                         */
    background: var(--bg);
    border: 1px solid var(--panel-border);
    color: var(--text);
    padding: 8px 28px 8px 12px;
    font-size: 14px;
    font-family: inherit;
    border-radius: 4px;
    height: 38px;
    line-height: 1.2;
    width: 100%;
    box-sizing: border-box;
    text-align: left;
    cursor: pointer;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='%23a3b3c8' d='M0 0l5 6 5-6z'/></svg>");
    background-repeat: no-repeat;
    background-position: right 10px center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.styled-select-btn:focus {
    outline: none;
    border-color: var(--endpoint);
}

/* The proxy list inherits .suggestions look + .suggestions li hover/.selected     */
/* highlight, so the popup uses the same gentle rgba(59,130,246,0.15) we use for   */
/* the From / To autocomplete.                                                     */
.styled-select-list {
    text-align: left;
}

.styled-select-list li {
    padding: 8px 14px;
    cursor: pointer;
    font-size: 14px;
}

/* The native-select layout used per-id widths (e.g. select#ticket-type {       */
/* width: 140px }) which the now-display:none native elements no longer carry  */
/* visible weight for.  Rather than mirror those tiny fixed widths onto the    */
/* proxy WRAP (which makes the ticket-type box look forlorn on a wide row),   */
/* we promote .form-row--ticket children to a real 2-column layout and the    */
/* railcard row to full-width — applied at ALL viewports so there is no       */
/* discontinuity at the 720px breakpoint.  The btn's existing width:100%      */
/* then fills the now-properly-sized parent.                                  */
/* Generic baseline: every .search-field inside any .form-row flexes to fill   */
/* its share of the row at all viewports (no breakpoint cliff at 720px where  */
/* the legacy @media rule used to shrink fields back to their fixed pixel     */
/* widths).  Default is 50% — flex-wrap on .form-row means 3 items go 50%+   */
/* 50% with the third dropping to a new row, which matches existing layouts. */
.form-row > .search-field { flex: 1 1 calc(50% - 6px); min-width: 0; }

/* Per-row overrides for non-50% layouts: */
.form-row--railcard > .search-field { flex: 1 1 100%; }
.form-row--offers   > .search-field { flex: 1 1 calc(33.333% - 8px); }

/* Inputs inside fields carry legacy fixed pixel widths (220px on station    */
/* inputs, 140px on time, etc).  Override to fill the now-flexible field.    */
.form-row > .search-field > input,
.form-row > .search-field > .styled-select-wrap > .styled-select-btn {
    width: 100%;
    box-sizing: border-box;
}

/***************************************************************************************/
/**** Results layout — main journey list on the left, summary panel on the right.   ****/
/***************************************************************************************/

.results {
    padding: 28px 0 60px 0;
    background: var(--bg);
}

.results-grid {
    display: grid;
    grid-template-columns: 1fr 320px;
    gap: 24px;
    align-items: start;
}

@media (max-width: 900px) {
    .results-grid { grid-template-columns: 1fr; }
}

.journey-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.journey-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 0 12px 10px 12px;
}

.journey-header h2 {
    font-size: 22px;
    color: var(--text-bright);
}

.journey-header .count {
    font-size: 13px;
    color: var(--text-dim);
}

/**** Per-journey card. ****/
.j-card {
    background: #fff;
    border-radius: 12px;
    padding: 16px 20px;
    display: grid;
    grid-template-columns: 1.2fr 0.9fr 1fr 1fr;
    gap: 20px;
    align-items: center;
    box-shadow: 0 1px 3px rgba(30, 30, 60, 0.06);
    border: 2px solid transparent;
    transition: border-color 0.12s;
}

.j-card:hover {
    border-color: #e0d9cd;
}

/**** Visual distinction between frontier and non-frontier journeys. ****/
.j-card.j-dominated {
    opacity: 0.55;
}

.j-card.j-frontier {
    border-color: #66ff8a;
}

.j-card.j-anchor {
    border-color: #1a1a3b;
    border-width: 2px;
}

.j-times {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.j-times .big {
    font-size: 22px;
    font-weight: 700;
    color: var(--text-bright);
}

.j-times .arrow {
    font-size: 13px;
    color: var(--text-dim);
}

.j-times .dur {
    font-size: 12px;
    color: var(--text-dim);
}

.j-meta {
    font-size:      13px;
    color:          var(--text-dim);
    /**** Stay as a native table-cell — `display: flex` on a <td> breaks the row's   */
    /**** vertical-align:middle (it overrides the table-cell layout).  With the      */
    /**** default table-cell display the single <b> line sits centred vertically     */
    /**** against the adjacent two-line cells thanks to the global td rule.          */
    text-align:     left;
    vertical-align: middle;
}

.j-meta .op {
    color: var(--text-bright);
    font-weight: 600;
    font-size: 14px;
}

.j-badge {
    display: inline-block;
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.3;
}

.j-badge--save {
    background: #ddffe3;
    color: #0d5a24;
}

.j-badge--premium {
    background: #fff1dd;
    color: #805812;
}

.j-badge--anchor {
    background: #e6e6f0;
    color: var(--text-bright);
}

.j-price {
    text-align: right;
}

.j-price .pounds {
    font-size: 22px;
    font-weight: 700;
    color: var(--text-bright);
}

.j-price .class-tag {
    font-size: 12px;
    color: var(--text-dim);
}

/***************************************************************************************/
/**** Split-ticket alternatives section — rendered below the journey list when any  ****/
/**** split pivot beats the direct cheapest on the searched route.                  ****/
/***************************************************************************************/

.split-alternatives {
    margin-top: 24px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.split-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 4px;
}

.split-header h2 {
    font-size: 18px;
    color: var(--text-bright);
}

.split-hint {
    font-size: 13px;
    color: var(--text-dim);
    font-weight: 500;
}

.split-card {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-rows: auto auto auto;
    column-gap: 16px;
    row-gap: 4px;
    background: var(--panel);
    border-radius: 12px;
    padding: 16px 18px;
    border: 1px solid var(--panel);
    box-shadow: 0 1px 2px rgba(30, 30, 60, 0.04);
}

.split-badge {
    grid-row: 1 / span 3;
    align-self: start;
    background: #0c8155;
    color: #ffffff;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 6px 10px;
    border-radius: 6px;
}

.split-route {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-bright);
}

.split-meta {
    font-size: 12px;
    color: var(--text-dim);
}

.split-legs {
    display: flex;
    flex-direction: column;
    gap: 6px;
    font-size: 13px;
    color: #3c3c55;
    margin-top: 6px;
}

.split-leg {
    display: grid;
    grid-template-columns: 42px 120px 1fr auto;
    gap: 10px;
    align-items: center;
    padding: 4px 8px;
    background: var(--panel);
    border-radius: 6px;
}

.split-leg .leg-tag {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.split-leg .leg-times {
    font-weight: 700;
    color: var(--text-bright);
    white-space: nowrap;
}

.split-leg .leg-route {
    font-size: 12px;
    color: var(--text-dim);
}

.split-leg .leg-price {
    font-weight: 700;
    color: var(--text-bright);
    font-size: 13px;
}

.split-prices {
    grid-row: 1 / span 3;
    grid-column: 3 / 4;
    align-self: center;
    text-align: right;
}

.split-total {
    font-size: 22px;
    font-weight: 800;
    color: var(--text-bright);
    line-height: 1.1;
}

.split-vs {
    font-size: 12px;
    color: #8a8aa0;
    text-decoration: line-through;
}

.split-save {
    margin-top: 4px;
    font-size: 13px;
    font-weight: 700;
    color: #0c8155;
}

/**** Right-hand summary — stack of cards: selected-price + CTA, journey detail, window stats. */
.results-summary {
    position: sticky;
    top: 16px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.sum-card {
    background: #fff;
    border-radius: 14px;
    padding: 18px 20px;
    box-shadow: 0 1px 3px rgba(30, 30, 60, 0.06);
}

.sum-card--primary {
    background: #1a1a3b;
    color: #fff;
}

.sum-card--stats {
    background: #fff;
    color: var(--text-bright);
}

.sum-card-label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 6px;
}

.sum-card--primary .sum-card-label {
    color: rgba(255,255,255,0.6);
}

.sum-card-title {
    font-size: 15px;
    font-weight: 600;
    margin-bottom: 10px;
}

.sum-total-label {
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.04em;
    color: rgba(255,255,255,0.7);
    text-transform: uppercase;
}

.sum-total {
    font-size: 34px;
    font-weight: 800;
    line-height: 1.1;
    margin: 4px 0 10px 0;
    color: #fff;
}

.sum-hint {
    font-size: 12px;
    color: rgba(255,255,255,0.7);
    margin-bottom: 14px;
    line-height: 1.45;
}

.sum-cta {
    width: 100%;
    background: #2560e6;
    color: #ffffff;
    border: none;
    border-radius: 10px;
    padding: 14px 0;
    font-weight: 700;
    font-size: 16px;
    cursor: pointer;
    margin-bottom: 8px;
    transition: background 0.12s;
}

.sum-cta:hover { background: #1d4fc2; }

.sum-cta-secondary {
    width: 100%;
    background: transparent;
    color: #fff;
    border: 1px solid rgba(255,255,255,0.3);
    border-radius: 10px;
    padding: 10px 0;
    font-size: 13px;
    cursor: pointer;
}

.sum-cta-secondary:hover { border-color: rgba(255,255,255,0.6); }

.sum-journey {
    font-size: 14px;
    color: var(--text-bright);
    margin-bottom: 10px;
}

.sum-journey strong { font-size: 16px; }

.sum-journey .dot {
    margin: 0 6px;
    color: #b3b3c7;
}

.sum-row {
    display: flex;
    justify-content: space-between;
    font-size: 13px;
    color: var(--text-dim);
    padding: 4px 0;
}

.sum-row .value {
    color: var(--text-bright);
    font-weight: 600;
}

/**** Selected journey card outline in the left-hand list. ****/
.j-card.j-selected {
    border-color: #1a1a3b;
    box-shadow: 0 4px 16px rgba(30,30,60,0.12);
}

/**** Horizontal date strip across the top of results — quick "try another day" UI. ****/
.date-strip {
    display: grid;
    grid-template-columns: auto repeat(7, 1fr) auto;
    gap: 6px;
    background: #fff;
    border-radius: 12px;
    padding: 8px;
    margin-bottom: 14px;
    box-shadow: 0 1px 3px rgba(30,30,60,0.06);
}

.date-cell {
    text-align: center;
    padding: 10px 4px;
    border-radius: 8px;
    cursor: pointer;
    font-size: 12px;
    color: var(--text-dim);
    transition: background 0.12s;
    border: 1px solid transparent;
}

.date-cell:hover { background: #f4f0ea; }

.date-cell.selected {
    background: #1a1a3b;
    color: #fff;
    border-color: #1a1a3b;
}

.date-cell .dow {
    font-weight: 600;
    font-size: 11px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    display: block;
    margin-bottom: 2px;
}

.date-cell .dom {
    font-size: 16px;
    font-weight: 700;
    color: var(--text-bright);
    display: block;
}

.date-cell.selected .dom,
.date-cell.selected .dow,
.date-cell.selected .from {
    color: #fff;
}

.date-cell .from {
    font-size: 11px;
    color: var(--text-bright);
    font-weight: 600;
    margin-top: 3px;
    display: block;
}

.date-cell.no-data .from {
    color: #b3b3c7;
    font-weight: 400;
}

.date-nav {
    background: transparent;
    border: 1px solid #e0d9cd;
    border-radius: 8px;
    padding: 10px 8px;
    cursor: pointer;
    font-size: 16px;
    color: var(--text-dim);
}

.date-nav:hover {
    border-color: #1a1a3b;
    color: var(--text-bright);
}

/***************************************************************************************/
/**** Empty/error states.                                                           ****/
/***************************************************************************************/

.empty {
    background: #fff;
    border-radius: 12px;
    padding: 60px 24px;
    text-align: center;
    color: var(--text-dim);
}

.empty h3 {
    color: var(--text-bright);
    margin-bottom: 8px;
}

/***************************************************************************************/
/**** Footer.                                                                       ****/
/***************************************************************************************/

.site-footer {
    padding: 20px 24px;
    background: var(--panel);
    color: var(--text-dim);
    text-align: center;
    font-size: 12px;
    border-top: 1px solid var(--panel-border);
}
.site-footer .container { padding: 0; }
.site-footer small { font-size: inherit; }

/***************************************************************************************/
/**** Three-dot kebab menu — desktop hidden, mobile reveals as the nav trigger.    ****/
/***************************************************************************************/

.menu-toggle {
    display: none;
}

/* Hamburger-menu breakpoint raised from 720 → 900px (2026-05-28).  The 4    */
/* top-nav links + wordmark + theme switch + 24px-per-link margins need     */
/* roughly 830px of header width before "Sign in" wraps onto a second line. */
/* Below 900px we collapse to the hamburger to avoid the wrap-zone entirely.*/
/* NOTE: the form-row mobile stacking (.search-field--station 100%, etc.)   */
/* still uses its own 720px breakpoint in a separate @media block lower in  */
/* this file — header transition and form-stacking are deliberately decoup-  */
/* led so they can move independently.                                       */
@media (max-width: 900px) {

    /**** Tighten the page-wide gutter from 24px → 12px on mobile.  Affects every  ****/
    /**** .container — header AND body sections — so wordmark left edge aligns    ****/
    /**** with the body content below.                                              ****/
    .container { padding: 0 12px; }

    .site-header {
        position: relative;
    }

    .site-header .container {
        position: relative;
        flex-wrap: nowrap;
    }

    .menu-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: flex-end;
        background: transparent;
        border: 0;
        padding: 8px 0 8px 10px;
        color: #fff;
        cursor: pointer;
        border-radius: 4px;
        height: 36px;
        min-width: 24px;
    }

    .menu-toggle:hover, .menu-toggle:focus {
        background: rgba(255, 255, 255, 0.08);
        outline: none;
    }

    .menu-toggle svg {
        display: block;
    }

    .top-nav {
        position: absolute;
        top: calc(100% + 6px);
        right: 24px;
        background: #1a1a3b;
        border: 1px solid #2a2a55;
        border-radius: 6px;
        padding: 6px 0;
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
        display: none;
        flex-direction: column;
        z-index: 100;
        width: max-content;
    }

    .site-header.open .top-nav {
        display: flex;
    }

    .top-nav a {
        margin-left: 0;
        padding: 10px 14px;
        display: block;
        font-size: 15px;
        opacity: 1;
        line-height: 1.2;
        white-space: nowrap;
    }

    .top-nav a:hover {
        background: rgba(255, 255, 255, 0.06);
    }

    .top-nav a.active {
        text-decoration: none;
    }

    .top-nav a.active::before {
        content: "\203A  ";
        color: #2560e6;
    }

}



/* =================================================================
 * Part 2: home-page-specific components (was home.css)
 * ================================================================= */
/* ================================================================================
 * home.css — Clerkenwell Trains, home page styles.
 *
 * Extracted from index.html's inline <style> block 2026-05-11 as part of the
 * Lunar-style refactor.  Sole consumer: index.html.  Other pages have their own
 * page-specific CSS files alongside styles.css + theme.css.
 *
 * MAINTENANCE:
 *   - When the visual home-page layout changes, edit here.
 *   - When colour scheme / theme tokens change, edit theme.css (not here).
 *   - When a rule applies across multiple pages, lift to styles.css instead.
 *
 * SECTIONS (in document order):
 *   1. CSS reset + :root tokens
 *   2. Top nav / brand strip
 *   3. Hero headline + tagline
 *   4. Trust bar + operator strip
 *   5. Search form (.form-row, .search-field, .from-stack)
 *   6. Plan CTA + cta-microcopy
 *   7. Savings banner
 *   8. Planner tabs (Outbound / Return / Data)
 *   9. Journey card / lane card / split card components
 *  10. Empty / loading / error states
 *  11. Footer
 *  12. Mobile @media (breakpoint TBD post-consolidation)
 * ================================================================================ */

        * { box-sizing: border-box; margin: 0; padding: 0; }
        body {
            font-family: "Noto Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            background: var(--bg);
            color: var(--text);
            margin: 0;
            min-height: 100vh;
            /**** Sticky-footer flex pattern — keeps the page exactly viewport-tall when    ****/
            /**** content is short, so the right-hand scrollbar only appears when content   ****/
            /**** actually overflows.                                                       ****/
            display: flex;
            flex-direction: column;
        }

        .site-footer {
            margin-top: auto;
            padding: 20px 24px;
            background: var(--panel);
            color: var(--text-dim);
            text-align: center;
            font-size: 12px;
            border-top: 1px solid var(--panel-border);
        }
        .site-footer-inner { max-width: 1200px; margin: 0 auto; }
        .site-footer small { font-size: inherit; }

        .page-body { padding: 24px; }

        h1 {
            font-size: 24px;
            color: var(--text-bright);
            margin-bottom: 4px;
        }

        /***************************************************************************************/
        /**** Site header — dark navy bar matching index.html so every page shares a brand.   ****/
        /***************************************************************************************/

        .site-header { background: #1a1a3b; color: #fff; padding: 14px 0; }
        .site-header .site-header-inner {
            padding: 0 24px;
            display: flex; align-items: center; justify-content: space-between;
        }
        .wordmark { font-size: 22px; font-weight: 800; letter-spacing: -0.02em; color: #fff; text-decoration: none; }
        .wordmark-accent { color: #2560e6; margin-left: 1px; }
        .top-nav a { margin-left: 24px; font-size: 14px; font-weight: 500; color: #fff; text-decoration: none; opacity: 0.88; }
        .top-nav a:hover { opacity: 1; }
        .top-nav a.active { opacity: 1; text-decoration: underline; text-underline-offset: 4px; }

        /**** Three-dot kebab — hidden at desktop, revealed by the @media (max-     ****/
        /**** width: 900px) block higher in this file.  This duplicate would       ****/
        /**** otherwise re-assert display:none at module scope AFTER the @media    ****/
        /**** block, killing the hamburger in the 720-900 zone.  Deferred to the   ****/
        /**** module-scope rule at line 1097.                                       ****/
        /* .menu-toggle { display: none; }  REMOVED 2026-05-28 — see comment above. */

        /**** Long / short station-name swap — desktop shows the full name, mobile shows ****/
        /**** the National-Rail-style short form (Short_Station_Name() in the page JS).  ****/
        .name-short { display: none; }

        .tagline {
            font-size: 13px;
            color: var(--text-dim);
            margin-bottom: 20px;
        }

        /***************************************************************************************/
        /**** Trust bar — social-proof row sat between the headline and the form.            ****/
        /**** Small icons + short claims, wraps gracefully on narrow viewports.                ****/
        /***************************************************************************************/
        .trust-bar {
            display: flex;
            flex-wrap: wrap;
            gap: 8px 18px;
            margin: 6px 0 14px;
            padding: 10px 14px;
            background: var(--panel);
            border: 1px solid var(--panel-border);
            border-radius: 8px;
            font-size: 13px;
            color: var(--text-dim);
        }
        .trust-item {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            white-space: nowrap;
        }
        .trust-icon {
            font-size: 14px;
            line-height: 1;
        }

        /***************************************************************************************/
        /**** Operator strip — text-only list of TOCs we search.  Sits below the trust bar  ****/
        /**** on initial paint so the legitimacy signal lands before the user starts typing.   ****/
        /***************************************************************************************/
        .operator-strip {
            display: flex;
            flex-wrap: wrap;
            align-items: baseline;
            gap: 6px 12px;
            margin: 0 0 18px;
            padding: 0 4px;
            font-size: 12px;
            color: var(--text-dim);
        }
        .op-strip-label {
            font-weight: 600;
            color: var(--text);
            margin-right: 4px;
        }
        .op-strip-name {
            position: relative;
            white-space: nowrap;
        }
        .op-strip-name + .op-strip-name::before {
            content: "·";
            margin-right: 8px;
            color: var(--text-dim);
            opacity: 0.5;
        }

        /***************************************************************************************/
        /**** CTA microcopy — friction-reducer line directly under the Plan button row.       ****/
        /***************************************************************************************/
        .cta-microcopy {
            margin: 20px 0 20px 0;
            text-align: center;
            font-size: 12px;
            color: var(--text-dim);
        }

        /***************************************************************************************/
        /**** Savings banner — surfaces "You saved £X.XX vs the standard fare" at the top of ****/
        /**** the results panel.  Render_Results prepends this when state.savings_p > 0.        */
        /***************************************************************************************/
        .savings-banner {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 12px 16px;
            margin: 0 0 14px;
            background: linear-gradient(180deg, rgba(16, 185, 129, 0.18) 0%, rgba(16, 185, 129, 0.06) 100%);
            border: 1px solid rgba(16, 185, 129, 0.40);
            border-radius: 6px;
            color: #a7f3d0;
            font-size: 15px;
            font-weight: 600;
        }
        .savings-banner-amount { color: #10b981; font-size: 17px; font-weight: 800; }
        .savings-banner-icon   { font-size: 18px; }

        :root[data-theme="light"] .savings-banner {
            background: linear-gradient(180deg, rgba(16, 185, 129, 0.14) 0%, rgba(16, 185, 129, 0.04) 100%);
            border-color: rgba(16, 185, 129, 0.55);
            color: #065f46;
        }
        :root[data-theme="light"] .savings-banner-amount { color: #047857; }

        .form-row {
            display: flex;
            gap: 12px;
            flex-wrap: wrap;
            /**** Top-align so .search-field labels all line up at the top of the row regardless */
            /**** of taller items (the From-stack with its under-input toggle).            */
            align-items: flex-start;
            padding: 16px;
            background: var(--panel);
            border-radius: 8px;
            /**** Cap the panel so it doesn't stretch across wide viewports — the form fields    */
            /**** themselves are fixed-width and the row otherwise leaves giant gaps that strand  */
            /**** the action buttons miles away from the form on desktop.  Centre to match the     */
            /**** FAQ block below (also max-width / margin auto).                                    */
            max-width: 720px;
            margin: 0 auto 20px;
        }

        /**** Buttons have no label; flex-end keeps them aligned with input bottoms       */
        /**** instead of floating at the top of the row.                                    */
        .form-row > button {
            align-self: flex-end;
        }

        .search-field { display: flex; flex-direction: column; gap: 4px; }

        /**** Forced flex-wrap break — sibling element with full-width flex-basis pushes  */
        /**** subsequent .search-field children onto a new row regardless of viewport width.      */
        .form-row-break { flex-basis: 100%; height: 0; margin: 0; }

        /**** Equal-width spacer used to centre the jitter toggle on the action row.       */
        /**** Two .form-spacer siblings flanking the toggle push it visually to the         */
        /**** middle while Plan + Reset stay at the right edge.                              */
        .form-spacer { flex: 1; min-width: 12px; }

        /**** Hidden flexbox children — `hidden` attribute is `display: none !important`   */
        /**** by spec, but some user-agent stylesheets weaken it inside flex contexts.     */
        /**** Nail it down so the return-leg row collapses cleanly when single is chosen.   */
        .form-row [hidden] { display: none !important; }

        .search-field label {
            font-size: 12px;
            font-weight: 500;
            letter-spacing: 0;
            color: var(--text-dim);
        }

        .search-field input, .search-field select {
            background: var(--bg);
            border: 1px solid var(--panel-border);
            color: var(--text);
            padding: 8px 12px;
            font-size: 14px;
            font-family: inherit;
            border-radius: 4px;
            width: 110px;
            box-sizing: border-box;
            height: 38px;
            line-height: 1.2;
            -webkit-appearance: none;
            -moz-appearance: none;
            appearance: none;
        }

        .search-field select {
            background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='%23a3b3c8' d='M0 0l5 6 5-6z'/></svg>");
            background-repeat: no-repeat;
            background-position: right 10px center;
            padding-right: 28px;
        }

        .search-field select option { background: var(--bg); color: var(--text); }

        .search-field input:focus, .search-field select:focus { outline: none; border-color: var(--endpoint); }

        /**** Placeholder text — dim + italic so an empty field's hint is visually    */
        /**** distinguishable from a real value the user typed.  Without this Chrome   */
        /**** renders placeholders at near-full opacity which reads as stale input.   */
        .search-field input::placeholder {
            color: var(--text-dim);
            opacity: 0.55;
            font-style: italic;
        }

        /**** Date input needs extra room for the dd/mm/yyyy format + calendar icon —     */
        /**** 110px clips the year on Chrome's native date widget, leaving the user to    */
        /**** scroll horizontally inside a tiny field.                                     */
        .search-field input[type="date"], .search-field input#date, .search-field input#return-date { width: 170px; }

        /**** Time input needs a little extra room for the AM/PM glyph + clock icon.       */
        /**** Time-window number input matches so the row 4 sizing reads as a pair.        */
        .search-field input[type="time"] { width: 140px; }
        .search-field input#window      { width: 140px; }

        /**** Split selector's options (e.g. "Auto (cheapest)") need more room than 110px. ****/
        .search-field select#split { width: 170px; }

        /**** Ticket-type selector now carries "Open Return" which clips at the default      */
        /**** 110px — bump to 140px so the longest option fits comfortably.                   */
        .search-field select#ticket-type { width: 140px; }

        /**** Class is just Standard / First — only ~70px needed.  Railcard fills a full   */
        /**** row of the flex-wrap form-row on mobile (rule at the bottom of the @media     */
        /**** (max-width: 720px) block forces flex-basis 100%) so the Plan button stays on  */
        /**** its own row rather than wedging next to a half-row Railcard.  On desktop the  */
        /**** field naturally sizes from its select width.                                    */
        .search-field select#class    { width: 110px; }
        .search-field select#railcard { width: 100%; }

        /**** Station-picker: wider input + absolute-positioned suggestion dropdown.       ****/
        .search-field--station { position: relative; }
        .search-field--station input { width: 220px; }

        .suggestions {
            position: absolute;
            top: 100%;
            left: 0;
            right: 0;
            z-index: 50;
            margin: 4px 0 0;
            padding: 4px 0;
            list-style: none;
            max-height: 280px;
            overflow-y: auto;
            background: var(--panel);
            border: 1px solid var(--panel-border);
            border-radius: 4px;
            box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5);
        }
        .suggestions[hidden] { display: none; }
        .suggestions li {
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: 12px;
            padding: 6px 12px;
            font-size: 13px;
            cursor: pointer;
            color: var(--text);
        }
        .suggestions li:hover, .suggestions li.selected { background: rgba(59, 130, 246, 0.15); color: var(--text-bright); }
        .suggestions li .crs {
            font-family: "Liberation Mono", "SF Mono", "Cascadia Mono", "Cascadia Code", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", monospace;
            font-size: 11px;
            color: var(--text-dim);
        }

        button {
            background: var(--endpoint);
            color: white;
            border: none;
            padding: 0 20px;
            height: 38px;
            box-sizing: border-box;
            font-size: 14px;
            font-weight: 600;
            border-radius: 4px;
            cursor: pointer;
            font-family: inherit;
        }

        button:hover { background: #155ab9; }
        button:disabled { background: #475569; cursor: wait; }


        /**** Press-state — quick scale tap-feedback for both mouse and touch. ****/
        button:not(:disabled):active { transform: scale(0.97); }

        /**** Loading state — animate three dots after the button text so the user      ****/
        /**** gets immediate visual confirmation that work is underway.  Three steps     ****/
        /**** cycling through "." / ".." / "..." for the duration of the busy state.     ****/
        button.is-loading::after {
            content: "";
            display: inline-block;
            min-width: 18px;
            text-align: left;
            margin-left: 6px;
            animation: btn-dots 1.2s steps(3, end) infinite;
        }

        /**** Recalc button — sits in the .tabs-bar row on /selected-journeys,        */
        /**** aligned right via margin-left:auto so it pushes itself away from the    */
        /**** Outbound / Return tabs.  Re-fires home.js's Run() against the current   */
        /**** URL-derived form state when clicked.  Uses theme tokens so both modes    */
        /**** render correctly without per-theme overrides.                            */
        .recalc-btn {
            margin-left: auto;
            background: transparent;
            border: 1px solid var(--panel-border);
            color: var(--text-dim);
            font-size: 13px;
            font-weight: 500;
            padding: 6px 12px;
            border-radius: 6px;
            cursor: pointer;
            display: inline-flex;
            align-items: center;
            gap: 6px;
            height: auto;
            line-height: 1;
            font-family: inherit;
            transition: color 0.12s, border-color 0.12s, background 0.12s;
            align-self: center;
        }

        .recalc-btn:hover {
            color: var(--text-bright);
            border-color: var(--text-dim);
        }

        .recalc-btn:disabled {
            opacity: 0.5;
            cursor: wait;
        }

        .recalc-icon {
            font-size: 16px;
            line-height: 1;
            display: inline-block;
        }


        /**** Search-in-progress holding state.  Sits inside #output on               */
        /**** /selected-journeys while Run() is fetching from /trains_api/jit-plan.         */
        /**** Render_Results replaces #output.innerHTML when the response arrives,    */
        /**** so this placeholder vanishes automatically when results are ready.      */
        /****                                                                          */
        /**** Three real dot spans with staggered opacity animation — bulletproof    */
        /**** vs the `content:` keyframe approach which Chrome animates but several   */
        /**** other engines don't.                                                     */
        .search-status {
            text-align: center;
            color: var(--text-dim);
            font-size: 18px;
            padding: 60px 20px;
            font-weight: 500;
        }

        .search-status .dots {
            display: inline-block;
            margin-left: 4px;
            letter-spacing: 2px;
        }

        .search-status .dot {
            display: inline-block;
            opacity: 0;
            animation: search-dot-pulse 1.4s infinite;
        }

        .search-status .dot:nth-child(2) { animation-delay: 0.2s; }
        .search-status .dot:nth-child(3) { animation-delay: 0.4s; }

        @keyframes search-dot-pulse {
            0%, 80%, 100% { opacity: 0; }
            40%           { opacity: 1; }
        }

        @keyframes btn-dots {
            0%   { content: " ."; }
            33%  { content: " .."; }
            66%  { content: " ..."; }
            100% { content: " ."; }
        }

        /**** Plan button — the primary CTA on the page.  Significantly larger than the   ****/
        /**** other inputs so the click action reads as obvious, while the Reset button   ****/
        /**** sits next to it as a quiet escape hatch.  Asymmetric ~70/30 width split      ****/
        /**** keeps the row aligned with the rest of the form but visually skews weight     ****/
        /**** toward Plan.                                                                  ****/
        #go {
            flex: 3 1 0;
            height: 56px;
            font-size: 17px;
            font-weight: 700;
            letter-spacing: 0.2px;
            padding: 0 28px;
        }

        /**** Reset button — secondary visual weight next to the primary Plan button.     ****/
        /**** Muted panel background so it reads as an escape hatch rather than a second  ****/
        /**** call-to-action.  Narrower flex grow, same height as Plan so the row stays    ****/
        /**** orderly without the secondary action competing for the eye.  ID selector     ****/
        /**** beats the mobile-breakpoint `.form-row > button` rule on specificity.         ****/
        #reset-btn {
            background: transparent;
            color: var(--text-dim);
            border: 1px solid var(--panel-border);
            flex: 1 1 0;
            height: 56px;
            font-size: 14px;
            font-weight: 500;
        }
        #reset-btn:hover {
            background: var(--panel);
            color: var(--text-bright);
            border-color: var(--text-dim);
        }

        /***************************************************************************************/
        /**** Tabbed planner output — Outbound / Return / Data.  Tabs sit between the form    */
        /**** and the rendered results so the same screen real-estate hosts the outbound       */
        /**** grid, the return-leg planner, and the per-operator data grid (pre-prod debug).   */
        /***************************************************************************************/

        .planner-tabs { margin-bottom: 20px; }
        .tabs-bar {
            display: flex;
            gap: 2px;
            background: var(--panel);
            border-radius: 8px 8px 0 0;
            padding: 6px 6px 0;
            border-bottom: 1px solid var(--panel-border);
        }
        .tab-button {
            background: transparent;
            color: var(--text-dim);
            padding: 8px 18px;
            height: auto;
            border: none;
            border-bottom: 2px solid transparent;
            border-radius: 4px 4px 0 0;
            font-weight: 500;
            font-size: 13px;
            cursor: pointer;
        }
        .tab-button:hover { background: rgba(59, 130, 246, 0.08); color: var(--text); }
        .tab-button.active {
            color: var(--text-bright);
            background: var(--bg);
            border-bottom-color: var(--endpoint);
        }
        .tab-panel { display: none; padding-top: 16px; }
        .tab-panel.active { display: block; }

        /**** Leg label — non-interactive heading that replaces the Outbound/Return tab  */
        /**** buttons on /selected-journeys.  Mirrors .tab-button.active visually so the */
        /**** existing tab-bar surface (recalc button, border, panel padding) is reused */
        /**** without reflowing the layout.                                              */
        .leg-label {
            color: var(--text-bright);
            background: var(--bg);
            border-bottom: 2px solid var(--endpoint);
            border-radius: 4px 4px 0 0;
            padding: 8px 18px;
            font-weight: 600;
            font-size: 13px;
            display: inline-block;
            line-height: 1.4;
        }

        /**** Operator × route data grid — pre-production diagnostic showing every per-leg    */
        /**** quote keyed by (route, TOC).  Useful for spot-checking that the planner's       */
        /**** chosen split actually used the cheapest TOC for each leg.                         */
        .data-grid-wrap {
            background: var(--panel);
            border-radius: 8px;
            padding: 12px;
            overflow-x: auto;
        }
        .data-grid {
            width: 100%;
            border-collapse: collapse;
            font-size: 12px;
        }
        .data-grid th, .data-grid td {
            padding: 5px 9px;
            border: 1px solid var(--panel-border);
            text-align: right;
            white-space: nowrap;
        }
        .data-grid th {
            background: var(--bg);
            color: var(--text-bright);
            font-weight: 600;
            font-family: "Liberation Mono", "SF Mono", "Cascadia Mono", "Cascadia Code", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", monospace;
            font-size: 11px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }
        .data-grid th:first-child,
        .data-grid td:first-child {
            text-align: left;
            color: var(--text-dim);
            font-family: "Liberation Mono", "SF Mono", "Cascadia Mono", "Cascadia Code", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", monospace;
            position: sticky;
            left: 0;
            background: var(--panel);
            z-index: 2;
        }
        .data-grid th:first-child { background: var(--bg); z-index: 3; }
        .data-grid td.has-fare { color: var(--text-bright); }
        .data-grid td.cheapest { color: #10b981; font-weight: 600; }
        .data-grid td.empty { color: var(--text-dim); opacity: 0.35; }
        .data-grid caption {
            text-align: left;
            color: var(--text-dim);
            font-size: 12px;
            padding: 4px 0 8px;
        }

        /**** Jitter checkbox — sits at the end of the form-row, mirrors the planes           */
        /**** "expand-metro" toggle.  When ticked, the planner fans out from the entered      */
        /**** station to every known metro neighbour (London → EUS/KGX/STP/PAD/...).           */
        .jitter-toggle {
            display: flex;
            align-items: center;
            gap: 8px;
            color: var(--text-dim);
            font-size: 13px;
            user-select: none;
            cursor: pointer;
        }
        .jitter-toggle input { margin: 0; cursor: pointer; }
        .jitter-toggle:hover { color: var(--text); }

        /**** From-stack — vertical pairing of the From .search-field with the nearby-stations   */
        /**** toggle, so the toggle sits directly beneath the From input without breaking  */
        /**** the rest of the row's flex layout.                                             */
        .from-stack {
            display: flex;
            flex-direction: column;
            gap: 6px;
        }
        .field-toggle {
            font-size: 11px;
            padding: 2px 0;
            width: 220px;
            /**** Centre the checkbox + label horizontally beneath the From input. ****/
            justify-content: center;
        }
        .field-toggle input[type="checkbox"] {
            width: 14px;
            height: 14px;
            flex: none;
            margin: 0;
            -webkit-appearance: checkbox;
            appearance: checkbox;
        }

        .summary-bar {
            display: flex;
            gap: 24px;
            padding: 12px 16px;
            background: var(--panel);
            border-radius: 8px;
            margin-bottom: 20px;
            font-size: 13px;
            color: var(--text);
            flex-wrap: wrap;
        }

        .summary-bar span b { color: var(--text-bright); }

        .status {
            padding: 20px;
            text-align: center;
            color: var(--text-dim);
            font-size: 14px;
        }

        .error {
            padding: 16px;
            background: #7f1d1d;
            color: #fecaca;
            border-radius: 8px;
            font-size: 14px;
        }

        .error-retry {
            margin-left: 12px;
            padding: 6px 14px;
            background: #fecaca;
            color: #7f1d1d;
            border: none;
            border-radius: 6px;
            font-size: 13px;
            font-weight: 600;
            cursor: pointer;
        }

        .error-retry:hover {
            background: #fef2f2;
        }

        /***************************************************************************************/
        /**** Unified grid — a station-name column on the left, one candidate column per      ****/
        /**** train, stations ordered top (latest) to bottom (earliest).                       ****/
        /***************************************************************************************/

        .grid-wrapper {
            display: flex;
            gap: 0;
            overflow-x: scroll;
            padding: 16px 0;
            background: var(--panel);
            border-radius: 8px;
        }
        /**** Thin custom scrollbar — same 4px height as planes for visual consistency.   ****/
        /**** Must NOT coexist with `scrollbar-width` on the same element or Chrome falls ****/
        /**** back to the native ~15px native bar.                                         ****/
        .grid-wrapper::-webkit-scrollbar { height: 4px; width: 4px; }
        .grid-wrapper::-webkit-scrollbar-track { background: var(--bg); border-radius: 0 0 8px 8px; }
        .grid-wrapper::-webkit-scrollbar-thumb { background: var(--endpoint, #2560e6); border-radius: 2px; }
        .grid-wrapper::-webkit-scrollbar-thumb:hover { background: #2563eb; }

        .station-col {
            flex-shrink: 0;
            min-width: 200px;
            padding-right: 12px;
            border-right: 1px solid var(--panel-border);
            position: sticky;
            left: 0;
            z-index: 10;
            background: var(--panel);
        }

        .lane {
            flex-shrink: 0;
            min-width: 190px;
            padding: 0 14px;
            position: relative;
            border-right: 1px solid var(--panel-border);
            display: flex;
            flex-direction: column;
            cursor: pointer;
            transition: background 0.12s, box-shadow 0.12s;
        }

        .lane:hover { background: rgba(37, 99, 230, 0.06); }

        .lane.selected {
            background: rgba(37, 99, 230, 0.12);
            box-shadow: inset 0 0 0 2px var(--endpoint);
        }

        .lane.best {
            background: rgba(16, 185, 129, 0.06);
        }

        .lane.best.selected {
            background: rgba(16, 185, 129, 0.18);
            box-shadow: inset 0 0 0 2px #10b981;
        }

        /***************************************************************************************/
        /**** Continue bar — appears above the grid once candidates are rendered.              ****/
        /***************************************************************************************/

        .continue-bar {
            display: flex;
            flex-direction: column;
            align-items: stretch;
            gap: 10px;
            padding: 10px 14px;
            margin: 12px 0;
            background: var(--panel);
            border: 1px solid var(--panel-border);
            border-radius: 6px;
        }
        .continue-bar > * { width: 100%; }
        .continue-bar > button {
            width: 100%;
            height: 48px;
            font-size: 16px;
            padding: 0 16px;
        }
        .continue-bar .hint { font-size: 13px; color: var(--text-dim); }
        .continue-bar .sel  { font-size: 13px; color: var(--text-bright); font-weight: 600; display: block; }
        .continue-bar .sel .headline { display: block; font-size: 14px; margin-bottom: 3px; }
        .continue-bar .sel .legs {
            display: block;
            font-size: 12px;
            font-weight: 400;
            color: var(--text-dim);
            margin-top: 2px;
            line-height: 1.5;
        }
        .continue-bar .sel .legs b { color: var(--text-bright); font-weight: 600; }
        .continue-bar .sel .total  { color: var(--leg-1); }
        .continue-bar .sel .sep    { color: var(--text-dim); margin: 0 6px; }

        /***************************************************************************************/
        /**** Mini leg-track inside the Selected bar — each leg becomes a coloured pill       ****/
        /**** with the source → dest + per-leg price, mirroring the main track visual.        ****/
        /***************************************************************************************/

        .continue-bar .sel .mini-track {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 6px;
            margin-top: 6px;
            font-size: 12px;
            font-weight: 500;
            line-height: 1.3;
        }
        .continue-bar .sel .mini-leg {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 3px 8px;
            border-radius: 4px;
            border: 1px solid var(--track);
            background: rgba(255, 255, 255, 0.03);
            color: var(--text);
        }
        .continue-bar .sel .mini-leg .stations { color: var(--text-bright); font-weight: 600; }
        .continue-bar .sel .mini-leg .price    { font-weight: 700; font-family: "Liberation Mono", "SF Mono", "Cascadia Mono", "Cascadia Code", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", monospace; }
        .continue-bar .sel .mini-leg.leg-1 { border-color: var(--leg-1); }
        .continue-bar .sel .mini-leg.leg-1 .price { color: var(--leg-1); }
        .continue-bar .sel .mini-leg.leg-2 { border-color: var(--leg-2); }
        .continue-bar .sel .mini-leg.leg-2 .price { color: var(--leg-2); }
        .continue-bar .sel .mini-leg.leg-3 { border-color: var(--leg-3); }
        .continue-bar .sel .mini-leg.leg-3 .price { color: var(--leg-3); }
        .continue-bar .sel .mini-leg.leg-4 { border-color: var(--leg-4); }
        .continue-bar .sel .mini-leg.leg-4 .price { color: var(--leg-4); }
        .continue-bar .sel .mini-leg.leg-5 { border-color: var(--leg-5); }
        .continue-bar .sel .mini-leg.leg-5 .price { color: var(--leg-5); }

        /**** Change-point chevron between legs, shown as a small arrow with the word          */
        /**** "change" so the user can read the itinerary like a map strip.                     */
        .continue-bar .sel .mini-change {
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 0.4px;
            text-transform: uppercase;
            color: var(--text-dim);
            padding: 0 2px;
        }

        .continue-bar button[disabled] { background: var(--panel-border); color: var(--text-dim); cursor: not-allowed; }

        /***************************************************************************************/
        /**** Two-option choice cards inside the Continue bar.                                  */
        /****                                                                                   */
        /**** The user picks between (a) the cheapest direct journey we can find in the 2hr     */
        /**** window (the "standard" route a normal booking site would sell them) and (b) our   */
        /**** calculated split-ticket plan.  Continue books whichever is selected.               */
        /***************************************************************************************/

        .option-stack { display: flex; flex-direction: column; gap: 8px; flex: 1; min-width: 0; }
        .option-card {
            /**** Solo card now — Best standard journey was removed for beta testers, so the */
            /**** radio dot column is gone and the card no longer behaves as a selector.  Two   */
            /**** columns: title/detail then price.                                              */
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 12px;
            align-items: center;
            padding: 10px 14px;
            background: var(--bg);
            border: 1px solid var(--panel-border);
            border-radius: 6px;
            transition: border-color 0.12s, background 0.12s;
        }
        .option-card input[type="radio"] {
            /**** Hidden because the chooser is now single-option; JS still reads .checked    */
            /**** so the input stays in the DOM, just invisible.                                */
            display: none;
        }
        .option-card:has(input:checked) {
            border-color: var(--endpoint);
            background: rgba(59, 130, 246, 0.10);
        }
        .option-card .option-title {
            font-size: 12px;
            color: var(--text-dim);
            text-transform: uppercase;
            letter-spacing: 0.5px;
            margin-bottom: 3px;
        }
        .option-card:has(input:checked) .option-title { color: var(--endpoint); font-weight: 600; }
        .option-card .option-detail { font-size: 13px; color: var(--text-bright); line-height: 1.4; }
        /**** legs-line / mini-track — strip of leg pills (e.g. "EUS → CRE £10.50  →     ****/
        /**** CHANGE  CRE → LIV £15.20") shown under the optimised journey detail.  Flex   ****/
        /**** + gap on the inner .mini-track wrapper (which is the actual pill container)   ****/
        /**** gives breathing room between adjacent pills so "change" doesn't butt against   */
        /**** the next station name and station name doesn't butt against the price.            */
        .option-card .option-detail .legs-line {
            font-size: 12px;
            color: var(--text-dim);
            margin-top: 4px;
        }
        .option-card .option-detail .legs-line .mini-track {
            display: flex;
            flex-wrap: wrap;
            /**** Centre-align all pills vertically so the "→ CHANGE" arrow sits visually  */
            /**** mid-line between the leg pills, not dropped or raised.                     */
            align-items: center;
            gap: 4px 8px;
        }
        /**** Within each leg pill, give the station name + price a small gap so they don't  */
        /**** run together ("Crewe£10.50" → "Crewe £10.50").  inline-flex keeps the pill    */
        /**** itself laid out as one unit within the legs-line strip.                          */
        .option-card .option-detail .legs-line .mini-leg {
            display: inline-flex;
            align-items: baseline;
            gap: 4px;
        }
        .option-card .option-detail .legs-line .mini-leg .price {
            font-weight: 600;
            color: var(--text-bright);
        }
        .option-card .option-detail .legs-line .mini-change {
            font-weight: 600;
            color: var(--text-dim);
            text-transform: uppercase;
            font-size: 10px;
            letter-spacing: 0.4px;
        }
        .option-card .option-price {
            font-size: 18px;
            font-weight: 700;
            color: var(--leg-1);
            font-family: "Liberation Mono", "SF Mono", "Cascadia Mono", "Cascadia Code", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", monospace;
            white-space: nowrap;
        }
        .option-card .option-price.savings {
            font-size: 11px;
            font-weight: 500;
            color: var(--text-dim);
            display: block;
            margin-top: 2px;
        }
        .option-card.option-disabled { opacity: 0.4; cursor: default; }
        .option-card.option-disabled:hover { border-color: var(--panel-border); }

        /***************************************************************************************/
        /**** Return tab — return-direction time picker.  Tickets are already paid via the */
        /**** outbound Return product, so this is just "which train do you want to ride        */
        /**** back".  Cards are intentionally compact — no per-card price, no leg breakdown.    */
        /***************************************************************************************/

        .return-header { padding: 12px 0 16px 0; border-bottom: 1px solid var(--panel-border); margin-bottom: 12px; }
        .return-title  { font-size: 18px; font-weight: 600; color: var(--text-bright); }
        .return-subtitle { font-size: 13px; color: var(--text-dim); margin-top: 4px; line-height: 1.4; }
        .return-card-list {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
            gap: 8px;
        }
        .return-card {
            display: flex;
            flex-direction: column;
            padding: 10px 12px;
            border: 1px solid var(--panel-border);
            border-radius: 6px;
            cursor: pointer;
            transition: border-color 0.1s;
        }
        .return-card:hover { border-color: var(--text-dim); }
        .return-card input[type="radio"] {
            position: absolute;
            opacity: 0;
            pointer-events: none;
        }
        .return-card:has(input:checked) {
            border-color: var(--endpoint);
            background: rgba(255, 255, 255, 0.02);
        }
        .return-card-times { font-size: 16px; font-weight: 500; color: var(--text-bright); }
        .return-card-times .return-arrow { color: var(--text-dim); margin: 0 4px; }
        .return-card-times .return-dur   { color: var(--text-dim); font-size: 13px; font-weight: 400; }
        .return-card-trains { font-size: 12px; color: var(--text-dim); margin-top: 4px; }

        /***************************************************************************************/
        /**** Open-Return explainer rows — Validity / Time / Route / Best for.  Each row is   */
        /**** a label-value pair, two-column on desktop, stacked on narrow viewports.           */
        /***************************************************************************************/

        .return-terms {
            display: grid;
            grid-template-columns: 1fr;
            gap: 12px;
            margin-top: 8px;
        }
        .return-term {
            display: grid;
            grid-template-columns: 140px 1fr;
            gap: 16px;
            padding: 10px 12px;
            border: 1px solid var(--panel-border);
            border-radius: 4px;
            background: rgba(255, 255, 255, 0.02);
        }
        .return-term-label {
            font-size: 12px;
            font-weight: 600;
            color: var(--endpoint);
            text-transform: uppercase;
            letter-spacing: 0.4px;
            align-self: start;
        }
        .return-term-value {
            font-size: 14px;
            color: var(--text);
            line-height: 1.5;
        }

        /***************************************************************************************/
        /**** Inner sub-tabs for the journey grid (Standard / Optimised).  Same stations, ****/
        /**** different split treatment per tab.  Lanes inside each sub-grid update their     */
        /**** matching option card on click.                                                   */
        /***************************************************************************************/

        .grid-tab-bar {
            display: flex;
            gap: 4px;
            margin-bottom: 0;
            padding: 0 4px;
        }
        .grid-tab-button {
            background: transparent;
            color: var(--text-dim);
            padding: 6px 14px;
            height: auto;
            border: 1px solid var(--panel-border);
            border-bottom: none;
            border-radius: 6px 6px 0 0;
            font-size: 12px;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            cursor: pointer;
        }
        .grid-tab-button:hover { color: var(--text); background: rgba(59, 130, 246, 0.06); }
        .grid-tab-button.active {
            color: var(--text-bright);
            background: var(--panel);
            border-color: var(--endpoint);
            border-bottom: 1px solid var(--panel);
            position: relative;
            top: 1px;
        }
        .grid-sub-panel { display: none; }
        .grid-sub-panel.active { display: block; }

        /***************************************************************************************/
        /**** Journeys sub-tab — tabulated journey list (mirrors National Rail planner's   ****/
        /**** "Please select a journey" view for users who prefer rows over the lane grid). ****/
        /***************************************************************************************/

            /**** Outer table wrapper inside #grid-sub-journeys.  Two tables live side-by-     */
            /**** side: one for optimised candidates, one for standard.  The .journeys-hidden  */
            /**** class collapses the inactive one so only the matching radio's table shows.   */
            .journeys-panel-table {
                width: 100%;
                border-collapse: collapse;
                font-size: 14px;
                color: var(--text-bright);
                margin-top: 8px;
            }

            .journeys-panel-table.journeys-hidden,
            .journeys-hidden                       { display: none; }

            .journeys-panel-table th {
                text-align: left;
                font-weight: 600;
                padding: 10px 12px;
                border-bottom: 1px solid rgba(255, 255, 255, 0.08);
                color: var(--text-muted);
                font-size: 12px;
                text-transform: uppercase;
                letter-spacing: 0.5px;
            }

            /**** Sortable column headers — click to re-sort the table.  The .sort-arrow */
            /**** is dimmed when the column isn't the active sort, bright when it is.   */
            .journeys-panel-table th.sortable {
                cursor: pointer;
                user-select: none;
                transition: color 0.12s;
            }

            .journeys-panel-table th.sortable:hover {
                color: var(--text-bright);
            }

            .journeys-panel-table th.sortable .sort-arrow {
                display: inline-block;
                margin-left: 2px;
                opacity: 0.35;
                font-size: 11px;
                transition: opacity 0.12s;
            }

            .journeys-panel-table th.sortable.sort-active {
                color: var(--text-bright);
            }

            .journeys-panel-table th.sortable.sort-active .sort-arrow {
                opacity: 1;
            }

            .journeys-panel-table td {
                padding: 14px 12px;
                border-bottom: 1px solid rgba(255, 255, 255, 0.06);
                vertical-align: middle;
            }

            .journeys-panel-table tr.journey-row {
                cursor: pointer;
                transition: background 0.1s ease;
            }

            .journeys-panel-table tr.journey-row:hover {
                background: rgba(37, 99, 230, 0.06);
            }

            .journeys-panel-table tr.journey-row.selected {
                background: rgba(37, 99, 230, 0.12);
            }

            .journeys-panel-table .j-dep, .journeys-panel-table .j-arr {
                font-weight: 600;
                font-size: 16px;
            }

            .journeys-panel-table .j-station {
                color: var(--text-muted);
                font-size: 12px;
                margin-top: 2px;
            }

            .journeys-panel-table .j-arrow {
                color: var(--text-muted);
                width: 24px;
                text-align: center;
            }

            .journeys-panel-table .j-meta {
                color: var(--text-muted);
                font-size: 13px;
            }

            .journeys-panel-table .j-meta b {
                color: var(--text-bright);
                font-weight: 600;
            }

            .journeys-panel-table .j-price {
                font-weight: 700;
                font-size: 16px;
                color: var(--text-bright);
                text-align: right;
                white-space: nowrap;
            }

            .journeys-panel-table .j-price-from {
                color: var(--text-muted);
                font-size: 12px;
                font-weight: 400;
            }

            .journeys-panel-table .j-saves {
                color: #10b981;
                font-size: 12px;
                font-weight: 600;
                margin-top: 2px;
            }

            /**** (Per-row Select button removed 2026-05-15: the whole .journey-row is   ****/
            /**** already cursor:pointer and the click handler in Wire_Candidate_Selector ****/
            /**** treats a row tap exactly the same as a button click.  The button was a  ****/
            /**** redundant tap target that pushed the table over the 412 CSS-px Pixel 8  ****/
            /**** viewport.)                                                              ****/

            /**** Multi-leg via-hub badge — sits in the .j-meta cell next to the change ****/
            /**** count.  Communicates that this candidate is a synthesised cross-      ****/
            /**** network journey routed through a specific hub.                        ****/
            .journeys-panel-table .via-badge {
                display: inline-block;
                margin-top: 4px;
                padding: 2px 6px;
                border-radius: 4px;
                background: var(--surface-2, #1f2937);
                color: var(--text-muted, #9ca3af);
                font-size: 11px;
                font-weight: 600;
                letter-spacing: 0.02em;
            }

            /**** Expand toggle for multi-leg candidates.  Hidden by default for      ****/
            /**** direct candidates and revealed only when cand.multi_leg is set.     ****/
            .journeys-panel-table .journey-row-legs-toggle {
                display: inline-block;
                margin-top: 4px;
                margin-left: 6px;
                padding: 2px 6px;
                background: transparent;
                color: var(--accent, #60a5fa);
                border: 1px solid var(--accent, #60a5fa);
                border-radius: 4px;
                cursor: pointer;
                font-size: 11px;
                font-weight: 600;
            }

            .journeys-panel-table .journey-row-legs-toggle:hover {
                background: var(--accent, #60a5fa);
                color: #ffffff;
            }

            /**** Hidden-by-default expansion row.  Toggled visible by                ****/
            /**** journey-row-legs-toggle click handler.                              ****/
            .journeys-panel-table tr.journey-row-legs[hidden] {
                display: none;
            }

            .journeys-panel-table tr.journey-row-legs td {
                background: var(--surface-2, #1f2937);
                padding: 12px 16px;
                font-size: 13px;
            }

            .journeys-panel-table .leg-detail-list {
                display: flex;
                flex-direction: column;
                gap: 6px;
            }

            .journeys-panel-table .leg-detail-row {
                display: grid;
                grid-template-columns: 60px 1fr auto;
                column-gap: 12px;
                align-items: baseline;
            }

            .journeys-panel-table .leg-detail-row .leg-tag {
                color: var(--text-muted, #9ca3af);
                font-weight: 600;
                font-size: 11px;
                text-transform: uppercase;
                letter-spacing: 0.04em;
            }

            .journeys-panel-table .leg-detail-row .leg-route b {
                color: var(--text, #f3f4f6);
            }

            .journeys-panel-table .leg-detail-row .leg-times {
                color: var(--text-muted, #9ca3af);
                font-variant-numeric: tabular-nums;
            }

        /***************************************************************************************/
        /**** Phone-portrait fit — tighten the 5-column journeys table at ≤ 540px so it    ****/
        /**** fits Pixel 8 (412 CSS-px) and iPhone SE (375).  The Select button column was  ****/
        /**** removed entirely 2026-05-15 — the row itself is the tap target — so the      ****/
        /**** remaining compensations are modest:                                            ****/
        /****   - 10×6 cell padding (down from 14×12)                                       ****/
        /****   - .j-price allowed to wrap below "Single from" so its intrinsic width drops ****/
        /***************************************************************************************/
        @media (max-width: 540px) {

            .journeys-panel-table th { padding: 8px 6px; }

            .journeys-panel-table td { padding: 10px 6px; }

            .journeys-panel-table .j-price { white-space: normal; }

        }

        /**** Container for the SVG track + lane-cells below the header. ****/
        .lane-track-area {
            position: relative;
            flex: 1;
        }

        .lane-header {
            padding: 12px 0 14px;
            border-bottom: 1px solid var(--panel-border);
            margin-bottom: 8px;
            height: 210px;
        }

        .lane-header .dep-arr {
            font-size: 16px;
            font-weight: 600;
            color: var(--text-bright);
        }

        .lane-header .train-no {
            font-family: "Liberation Mono", "SF Mono", "Cascadia Mono", "Cascadia Code", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", monospace;
            font-size: 11px;
            color: var(--text-dim);
            margin-top: 2px;
        }

        /**** Optimised "best" price — large, bright, with optional savings badge.         */
        .lane-header .best-price {
            display: flex;
            align-items: baseline;
            justify-content: space-between;
            margin-top: 8px;
            font-size: 16px;
            font-weight: 700;
            color: var(--text-bright);
            font-family: "Liberation Mono", "SF Mono", "Cascadia Mono", "Cascadia Code", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", monospace;
        }
        .lane-header .best-price .saves {
            font-size: 10px;
            font-weight: 600;
            color: var(--leg-1);
            background: rgba(16, 185, 129, 0.15);
            padding: 1px 6px;
            border-radius: 3px;
            font-family: inherit;
        }
        /**** Direct price — secondary line beneath, dimmed.                                */
        .lane-header .direct-line {
            display: flex;
            justify-content: space-between;
            margin-top: 4px;
            font-size: 11px;
            color: var(--text-dim);
        }
        .lane-header .direct-line s { text-decoration: none; }   /* keep semantics, no strikethrough by default */

        /**** Walk-up "always-available" tier — same shape as direct-line, slightly       ****/
        /**** brighter accent on the label so the operator can spot the durable price at  ****/
        /**** a glance.  Sits between direct-line and the Advance-ladder.                 ****/
        .lane-header .walk-up-line {
            display: flex;
            justify-content: space-between;
            margin-top: 2px;
            font-size: 11px;
            color: var(--text-dim);
        }
        .lane-header .walk-up-line > :first-child {
            color: var(--accent, #7df0c8);
            font-weight: 600;
            letter-spacing: 0.02em;
        }

        .lane-header .prices {
            display: flex;
            justify-content: space-between;
            margin-top: 8px;
            font-size: 12px;
            color: var(--text-dim);
        }

        .lane-header .prices b { color: var(--text-bright); }

        .lane-header .savings {
            display: inline-block;
            margin-top: 6px;
            padding: 2px 8px;
            background: rgba(16, 185, 129, 0.15);
            color: var(--leg-1);
            font-weight: 600;
            font-size: 11px;
            border-radius: 3px;
        }

        .station-header {
            padding: 12px 12px 14px 16px;
            border-bottom: 1px solid var(--panel-border);
            margin-bottom: 8px;
            height: 210px;
            font-size: 12px;
            color: var(--text-dim);
            display: flex;
            flex-direction: column;
            justify-content: flex-end;
        }

        .ladder {
            margin-top: 10px;
            font-size: 11px;
            display: grid;
            grid-template-columns: auto auto auto;
            gap: 2px 6px;
            align-items: center;
        }

        .ladder .r { color: var(--text-dim); }
        .ladder .p { color: var(--text-bright); font-weight: 600; text-align: right; font-family: "Liberation Mono", "SF Mono", "Cascadia Mono", "Cascadia Code", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", monospace; }
        .ladder .t { color: var(--text-dim); font-size: 10px; }
        .ladder .r.best { color: var(--leg-1); }
        .ladder .p.best { color: var(--leg-1); }
        .ladder .r.sel, .ladder .p.sel {
            background: rgba(37, 99, 230, 0.16);
            border-radius: 3px;
            padding: 0 4px;
            color: var(--text-bright);
        }
        /**** Walk-up "always available" durable tiers — tint with the accent colour    ****/
        /**** so the operator can spot the structural-saving rows at a glance.  The ✓   ****/
        /**** prefix in the label reinforces "buy this, guaranteed."                    ****/
        .ladder .r.durable { color: var(--accent, #2da678); font-weight: 600; }
        .ladder .p.durable { color: var(--accent, #2da678); }

        .station-header .title {
            font-weight: 600;
            color: var(--text-bright);
            font-size: 14px;
        }

        /***************************************************************************************/
        /**** Row: station-name cell on the left, and a matching row on every lane on right.  ****/
        /***************************************************************************************/

        .row {
            height: 44px;
            display: flex;
            align-items: center;
            padding: 0 16px;
            font-size: 13px;
            color: var(--text);
        }

        .row .station-name { flex: 1; }
        .row .station-name .crs {
            color: var(--text-dim);
            font-size: 11px;
            margin-left: 4px;
        }

        /***************************************************************************************/
        /**** Lane cell — the SVG "dot + line" for a single stop on a candidate.              ****/
        /***************************************************************************************/

        .lane-cell {
            height: 44px;
            position: relative;
        }

        /**** The track line is drawn via a single SVG absolutely positioned behind the       ****/
        /**** dots, so we can paint each segment with a different colour for split legs.       ****/
        .lane-track {
            position: absolute;
            left: 0;
            top: 0;
            bottom: 0;
            width: 100%;
            pointer-events: none;
        }

        .stop-dot {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: var(--track);
            border: 2px solid var(--panel);
            box-sizing: content-box;
            z-index: 2;
        }

        .stop-dot.endpoint { background: var(--endpoint); }
        .stop-dot.boundary { width: 14px; height: 14px; box-shadow: 0 0 0 2px var(--panel); }
        .stop-dot.leg-1-boundary { background: var(--leg-1); }
        .stop-dot.leg-2-boundary { background: var(--leg-2); }
        .stop-dot.leg-3-boundary { background: var(--leg-3); }
        .stop-dot.leg-4-boundary { background: var(--leg-4); }
        .stop-dot.leg-5-boundary { background: var(--leg-5); }

        .stop-time {
            position: absolute;
            left: calc(50% + 12px);
            top: 50%;
            transform: translateY(-50%);
            font-family: "Liberation Mono", "SF Mono", "Cascadia Mono", "Cascadia Code", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", monospace;
            font-size: 11px;
            color: var(--text-dim);
            white-space: nowrap;
            z-index: 3;
        }

        /***************************************************************************************/
        /**** Floating leg price pills in the lane, absolutely positioned between boundaries. ****/
        /***************************************************************************************/

        .leg-price {
            position: absolute;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 11px;
            font-weight: 600;
            padding: 3px 10px;
            border-radius: 12px;
            white-space: nowrap;
            z-index: 5;
            background: var(--panel);
            border: 1px solid;
        }

        .lane.best .leg-price { background: #152b3c; }

        .leg-price.leg-1 { color: var(--leg-1); border-color: var(--leg-1); }
        .leg-price.leg-2 { color: var(--leg-2); border-color: var(--leg-2); }
        .leg-price.leg-3 { color: var(--leg-3); border-color: var(--leg-3); }
        .leg-price.leg-4 { color: var(--leg-4); border-color: var(--leg-4); }
        .leg-price.leg-5 { color: var(--leg-5); border-color: var(--leg-5); }

        /***************************************************************************************/
        /**** Tiny "change" tag that sits alongside each train-change boundary on the zigzag. ****/
        /***************************************************************************************/

        .change-tag {
            position: absolute;
            left: 2px;
            transform: translateY(-50%);
            font-size: 9px;
            font-weight: 600;
            color: var(--text-dim);
            text-transform: uppercase;
            letter-spacing: 0.4px;
            white-space: nowrap;
            pointer-events: none;
            z-index: 4;
        }

        /***************************************************************************************/
        /**** Walk badge — same shape as the change-tag but amber, sits at the midpoint of    */
        /**** a dashed walk segment so the user can read "walk 25m" right on the track itself. */
        /***************************************************************************************/

        .walk-tag {
            position: absolute;
            left: 2px;
            transform: translateY(-50%);
            font-size: 9px;
            font-weight: 600;
            color: #f59e0b;
            text-transform: uppercase;
            letter-spacing: 0.4px;
            white-space: nowrap;
            pointer-events: none;
            z-index: 4;
        }

        /***************************************************************************************/
        /**** Mobile breakpoint — form layout                                              ****/
        /****                                                                              ****/
        /**** Below 720px the desktop multi-column form-row collapses into a clean         ****/
        /**** stacked grid: From / To full-width, all paired selectors 50/50, Plan + Reset ****/
        /**** as 50/50 buttons.  Input height bumped to 44px and font-size to 16px so the  ****/
        /**** focus tap target meets WCAG 2.1 AA and iOS Safari does not auto-zoom on tap.  ****/
        /***************************************************************************************/

        /***************************************************************************************/
        /**** Tablet-and-down (≤1024px) breakpoint — rules that benefit phone AND tablet:    ****/
        /****   • Short station names in the lane grid (saves a lot of column width).        ****/
        /****   • Continue button stacks below the journey-choice cards rather than          ****/
        /****     floating to the right of them.                                              ****/
        /***************************************************************************************/

        @media (max-width: 1024px) {

            /**** Lane grid — swap to short station names. ****/
            .name-long  { display: none; }
            .name-short { display: inline; }

        }

        @media (max-width: 720px) {

            /***********************************************************************************/
            /**** Tighten the page-wide gutter so every section gets more horizontal real      ****/
            /**** estate.  Header, page body, and lane grid all share the same 12px gutter.    ****/
            /***********************************************************************************/

            .page-body { padding: 12px; }

            /***********************************************************************************/
            /**** Header — three-dot kebab replaces the inline nav links on narrow viewports. ****/
            /***********************************************************************************/

            .site-header { position: relative; }
            .site-header .site-header-inner { padding: 0 12px; position: relative; flex-wrap: nowrap; }
            .top-nav { right: 12px; }

            .menu-toggle {
                display: inline-flex;
                align-items: center;
                justify-content: flex-end;
                background: transparent;
                border: 0;
                padding: 8px 0 8px 10px;
                color: #fff;
                cursor: pointer;
                border-radius: 4px;
                height: 36px;
                min-width: 24px;
            }
            .menu-toggle:hover, .menu-toggle:focus { background: rgba(255, 255, 255, 0.08); outline: none; }
            .menu-toggle svg { display: block; }

            .top-nav {
                position: absolute;
                top: calc(100% + 6px);
                right: 24px;
                background: #1a1a3b;
                border: 1px solid #2a2a55;
                border-radius: 6px;
                padding: 6px 0;
                box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
                display: none;
                flex-direction: column;
                z-index: 100;
                width: max-content;
            }

            .site-header.open .top-nav { display: flex; }

            .top-nav a {
                margin-left: 0;
                padding: 10px 14px;
                display: block;
                font-size: 15px;
                opacity: 1;
                line-height: 1.2;
                white-space: nowrap;
            }
            .top-nav a:hover { background: rgba(255, 255, 255, 0.06); }
            .top-nav a.active { text-decoration: none; }
            .top-nav a.active::before { content: "\203A  "; color: var(--endpoint); }

            .form-row { gap: 10px; padding: 14px; }

            .form-row-break { display: none !important; }

            .search-field { flex: 0 0 calc(50% - 5px); min-width: 0; }
            .search-field--station  { flex: 0 0 100%; }
            .search-field--railcard { flex: 0 0 100%; }

            .from-stack {
                flex: 0 0 100% !important;
                width: 100%;
                min-width: 0;
                align-items: flex-start;
                padding: 0;
                margin: 0;
            }

            .from-stack .search-field--station { width: 100%; }

            .from-stack > * { align-self: flex-start; }

            .jitter-toggle.field-toggle {
                margin-left: 0;
                padding-left: 0;
                justify-content: flex-start;
                align-items: center;
            }

            /**** Custom-styled checkbox so its visible square sits flush with the From / To ****/
            /**** input box left edge — Linux Chrome's default checkbox has a 1-2px internal ****/
            /**** inset that makes it look slightly off-axis next to the input boxes above.  ****/
            .jitter-toggle input[type="checkbox"] {
                appearance: none;
                -webkit-appearance: none;
                width: 14px;
                height: 14px;
                margin: 0;
                padding: 0;
                flex-shrink: 0;
                background: var(--bg);
                border: 1px solid var(--panel-border);
                border-radius: 3px;
                cursor: pointer;
                position: relative;
            }

            .jitter-toggle input[type="checkbox"]:checked {
                background: var(--endpoint);
                border-color: var(--endpoint);
            }

            .jitter-toggle input[type="checkbox"]:checked::after {
                content: "";
                position: absolute;
                left: 4px;
                top: 1px;
                width: 4px;
                height: 8px;
                border: solid #fff;
                border-width: 0 2px 2px 0;
                transform: rotate(45deg);
            }

            .search-field input, .search-field select {
                width: 100% !important;
                min-width: 0 !important;
                max-width: 100% !important;
                height: 44px;
                font-size: 16px;
                padding: 10px 12px;
                box-sizing: border-box;
            }

            .search-field label, .form-row label {
                margin-left: 0 !important;
                padding-left: 0 !important;
                text-indent: 0 !important;
                font-size: 13px;
                letter-spacing: 0;
            }

            .search-field select { padding-right: 32px !important; }

            .search-field input[type="date"], .search-field input[type="time"] { padding: 10px 12px !important; }

            .search-field input[type="date"]::-webkit-datetime-edit,
            .search-field input[type="time"]::-webkit-datetime-edit { padding: 0; margin: 0; }

            .search-field input[type="date"]::-webkit-datetime-edit-fields-wrapper,
            .search-field input[type="time"]::-webkit-datetime-edit-fields-wrapper { padding: 0; margin: 0; }

            .form-row > button {
                flex: 1 1 calc(50% - 5px);
                height: 48px;
                font-size: 16px;
                padding: 0 16px;
                align-self: stretch;
            }

            .form-spacer { display: none; }

            /***********************************************************************************/
            /**** Lane grid — break the from→to header onto two lines (destination indented)  ****/
            /**** instead of letting it wrap arbitrarily across five lines.  Station names in ****/
            /**** the rows below stay readable at the desktop styling.                        ****/
            /***********************************************************************************/

            .station-header .title-to {
                display: block;
                padding-left: 12px;
            }

            /**** Universal ellipsis fallback — anything still too long for the column ****/
            /**** truncates with "…" while the 3-letter CRS code beside it stays as the ****/
            /**** unambiguous identifier.                                                ****/
            /**** Tighten the side padding on the column + lanes so the lane gets more   ****/
            /**** breathing room without crushing the station-name text.                 ****/
            .station-col {
                min-width: 160px !important;
                max-width: 160px;
                padding-right: 4px !important;
            }

            .lane {
                padding: 0 8px !important;
                min-width: 160px;
            }

            .station-col .row .station-name {
                display: flex;
                align-items: baseline;
                overflow: hidden;
                gap: 6px;
            }

            .station-col .row .name-short {
                flex: 0 1 auto;
                min-width: 0;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }

            .station-col .row .crs {
                flex-shrink: 0;
                margin-left: 0 !important;
            }

        }


/* Footer text links — inherit parent <small> colour instead of UA blue. */
.link-inherit { color: inherit; }


/* Stale-price banner — pinned to the top of /itinerary when the live planner price */
/* has overtaken the user's cached chosen_p.  Amber to read "warning, not error".   */
.cwt-stale-price-banner {
    background:    #fff1dd;
    color:         #805812;
    border:        1px solid #d4a449;
    border-radius: 8px;
    padding:       12px 16px;
    margin:        16px auto;
    max-width:     960px;
    font-size:     15px;
    line-height:   1.45;
}

.cwt-stale-price-banner a {
    color:           #805812;
    text-decoration: underline;
    font-weight:     600;
    margin-left:     6px;
}
