/* ============================================================
   Buckettrips — Trip search (/trips)
   Mobile-first. Uses CSS vars from base.html and the accent tokens
   from settings-fab.css.

   This file is the banner and the results. The control under the banner
   is `filter.css`, one component for the three pages that narrow a list;
   the only thing said about it here is what the banner does to it, which
   is give it a light ground of its own over the photograph.

   Results are a table on desktop and a stack of rows on mobile, from one
   set of elements: the header row hides below 701px and each cell labels
   itself from data-label instead.
   ============================================================ */

body.tl-page main > .container { margin-top:0 !important; padding-top:0; }
.tl { overflow-x:hidden; overflow-x:clip; max-width:100%; }

/* ════════════════════════════════════════════════════════════════
   HEAD — title, one line of promise, and the search box
   ════════════════════════════════════════════════════════════════ */
.tl-head { position:relative; padding:40px 0 28px; background:var(--bg-secondary); border-bottom:1px solid var(--border-color); }
.tl-head .container { max-width:1000px; position:relative; z-index:2; }

/* ── THE BANNER ──
   FIN, from `docs/FILTERING.md`, drawn in `app/static/mockups/final.html`: a
   photograph from the page's own material, a wash dark enough for white type,
   the crumb, the question, one sentence, and the field. Every number here is
   the mockup's `.mast.banner`.

   The photograph is chosen in the admin, so it may be anything: the wash is
   what keeps white type legible over a bright sky as well as a dark one, rather
   than trusting whichever image is set this month. Under it the ground is the
   near-black the type sits on anyway, so a picture that fails to load leaves a
   banner rather than a hole. */
/* No `overflow:hidden` here, however much the photograph looks like it wants
   one. The field's dropdown is absolutely positioned inside this box and hangs
   below it, so a clip on the head cuts the typeahead off at the banner's edge.
   `.tl-banner` clips the picture, which is the only thing that overflows. */
.tl-head.has-banner {
    padding:22px 0 20px;
    border-bottom:none;
    /* The brand's own pine, not `--text-primary`: that token is near-black in
       light mode and WHITE in dark, so the ground behind the photograph painted
       white under white type -- a flash while the hero loads, and a permanently
       unreadable head if it 404s. `--brand-bg` is dark in both modes by design,
       and a `-text` token was never a background anyway. */
    background:var(--brand-bg); color:var(--white);
}
.tl-banner { position:absolute; inset:0; overflow:hidden; }
/* object-fit and object-position come from adjust_style() on the element, so the
   rule sets only what the crop does not. */
.tl-head .tl-banner img { width:100%; height:100%; display:block; }
/* Left to right, not top to bottom: the type is in the left third and that is
   where the wash has to be heaviest. The right end stays light enough to see
   what the photograph is of. */
.tl-banner-veil {
    position:absolute; inset:0;
    background:linear-gradient(to right, rgba(0,0,0,.82) 0%, rgba(0,0,0,.62) 52%, rgba(0,0,0,.3) 100%);
}
/* Over the photograph the words go white; the field does not. It keeps its own
   light ground so it reads as a control rather than as part of the picture,
   which is the one rule FIN states about it. */
.tl-head.has-banner .tl-crumb,
.tl-head.has-banner .tl-crumb a,
.tl-head.has-banner .tl-lede { color:rgba(255,255,255,.86); }
.tl-head.has-banner .tl-title { color:var(--white); text-shadow:0 2px 20px rgba(0,0,0,.4); }
.tl-head.has-banner .bt-field { background:var(--bg-primary); border-color:transparent; }
.tl-head.has-banner .bt-tok.bt-add { color:var(--text-secondary); }

/* The crumb, the question, one sentence, and then the field. */
.tl-crumb {
    font-family:var(--font-mono); font-size:.62rem; letter-spacing:.14em;
    text-transform:uppercase; color:var(--text-muted); opacity:.72;
}
.tl-crumb a, .tl-crumb a:hover, .tl-crumb a:focus-visible { color:inherit; text-decoration:none; }
.tl-crumb a:hover { text-decoration:underline; }
.tl-crumb span { opacity:.5; margin:0 4px; }
.tl-title {
    /* `anywhere`, because the head is inside a clipped box: a long word that
       cannot break is a word that gets cut rather than one that scrolls. */
    overflow-wrap:anywhere;
    margin:6px 0 0;
    font-size:1.5rem; font-weight:800; letter-spacing:-.036em; line-height:1.05;
    max-width:18ch;
}
.tl-lede {
    margin:9px 0 16px;
    font-size:.82rem; line-height:1.5; color:var(--text-secondary);
    opacity:.82; max-width:56ch;
}
/* The field stops well short of the full width: a search box the width of a
   desktop page reads as a banner, not as a thing to type in. */
.tl-head .bt-field { max-width:640px; }

/* ════════════════════════════════════════════════════════════════
   RESULTS — a table on desktop, a stack on mobile
   ════════════════════════════════════════════════════════════════ */
.tl-results { padding:24px 0 72px; }
.tl-results .container { max-width:1000px; }
.tl-count {
    display:flex; align-items:baseline; justify-content:space-between; gap:16px; flex-wrap:wrap;
    font-size:.85rem; color:var(--text-secondary);
    margin-bottom:14px;
}
.tl-count strong { color:var(--text-primary); font-weight:700; }

/* ── SORT ──
   Beside the results it orders, which is where a reader looks for it, rather
   than buried among the filters that decide what the results are. One button
   naming the order the list is in; the other five behind it. Six links in a row
   wrapped to two lines at any width worth having and read as navigation rather
   than as one setting with one value. */
.tl-sort { position:relative; }
.tl-sort > summary {
    display:inline-flex; align-items:center; gap:7px;
    padding:5px 11px; border-radius:50px;
    border:1px solid var(--border-color); background:var(--bg-primary);
    font-size:.76rem; color:var(--text-secondary);
    cursor:pointer; list-style:none; white-space:nowrap;
}
.tl-sort > summary::-webkit-details-marker { display:none; }
.tl-sort > summary:hover { border-color:var(--accent-color); }
.tl-sort > summary:focus-visible { outline:2px solid var(--accent-color); outline-offset:2px; }
.tl-sort > summary b { color:var(--text-primary); font-weight:600; }
.tl-sort-label {
    font-size:.6rem; font-weight:700; text-transform:uppercase; letter-spacing:.1em;
    color:var(--text-muted); font-family:var(--font-mono);
}
.tl-sort-car {
    width:11px; height:11px; opacity:.6;
    fill:none; stroke:currentColor; stroke-width:2.4;
    stroke-linecap:round; stroke-linejoin:round;
}
.tl-sort[open] > summary .tl-sort-car { transform:rotate(180deg); }

.tl-sort-menu {
    position:absolute; top:calc(100% + 6px); right:0; z-index:40;
    min-width:210px; padding:5px;
    background:var(--bg-elevated); border:1px solid var(--border-color);
    border-radius:12px; box-shadow:0 16px 48px var(--card-shadow);
    display:flex; flex-direction:column;
}
.tl-sort-opt {
    font-size:.82rem; font-weight:500;
    padding:8px 10px; border-radius:8px;
    text-decoration:none; white-space:nowrap;
}
.tl-sort-opt, .tl-sort-opt:hover, .tl-sort-opt:focus-visible { color:var(--text-primary); }
.tl-sort-opt:hover { background:var(--bg-secondary); }
.tl-sort-opt:focus-visible { outline:2px solid var(--accent-color); outline-offset:-2px; }
.tl-sort-opt.is-on, .tl-sort-opt.is-on:hover { color:var(--accent-color); font-weight:600; }

/* The admin grid's card (.dg-table-wrap), on the same tokens, so a change
   to them reaches both. */
.tl-list {
    border:1px solid var(--card-border); border-radius:var(--radius-xl);
    background:var(--card-bg);
    box-shadow:var(--shadow-card);
    overflow:hidden;
}

/* The column key. Hidden on mobile, where each cell labels itself instead. */
.tl-list-head { display:none; }

.tl-row {
    display:grid; grid-template-columns:1fr auto; gap:4px 14px;
    align-items:center;
    padding:14px 16px;
    text-decoration:none;
    border-top:1px solid var(--border-color);
    transition:background .12s ease;
}
.tl-row:first-of-type { border-top:none; }
/* base.html sets `a:hover { color: var(--text-primary) }`, which outranks a
   single class, so every link-shaped thing here restates its own color. */
.tl-row, .tl-row:hover, .tl-row:focus-visible { color:var(--text-primary); }
.tl-row:hover { background:var(--bg-secondary); }
.tl-row:focus-visible { outline:2px solid var(--accent-color); outline-offset:-2px; }

.tl-row-route { display:flex; align-items:center; gap:12px; min-width:0; grid-column:1 / -1; }
.tl-row-thumb {
    flex-shrink:0; width:52px; height:52px; border-radius:8px;
    background:var(--bg-tertiary); overflow:hidden;
    display:flex; align-items:center; justify-content:center;
    color:var(--text-muted);
}
/* Scoped through the row: a bare `.tl-row-thumb img` is one class and one type,
   which ties any other single-class image rule and then loses on source order.
   That collision is what made a 20px flag fill a whole card. */
.tl-row .tl-row-thumb img { width:100%; height:100%; object-fit:cover; display:block; }

.tl-row-words { min-width:0; display:flex; flex-direction:column; gap:3px; }
.tl-row-name {
    display:flex; align-items:center; gap:7px;
    font-size:.95rem; font-weight:600; letter-spacing:-.01em;
    min-width:0;
}
.tl-row-flags { display:inline-flex; gap:3px; flex-shrink:0; }
.tl-row .tl-row-flags img {
    width:15px; height:15px; border-radius:50%;
    display:block; object-fit:cover;
}
.tl-row-cities {
    font-family:var(--font-mono);
    font-size:.71rem; color:var(--text-secondary);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

.tl-row-days, .tl-row-next, .tl-row-price { font-size:.8rem; }
.tl-row-days { color:var(--text-secondary); }
.tl-row-next { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }
.tl-row-date { font-family:var(--font-mono); font-size:.78rem; }
.tl-row-more { font-size:.68rem; color:var(--text-muted); }
.tl-row-soon { color:var(--text-muted); font-style:italic; }
.tl-row-full { font-size:.66rem; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:#f59e0b; }
/* Two lines' worth of price in one cell, so the cell is laid out rather than
   set as a sentence: on mobile the two sit side by side, on desktop the old
   price goes above the new one. */
.tl-row-price {
    font-weight:700; font-size:.95rem; text-align:right;
    display:flex; align-items:baseline; gap:7px; min-width:0;
}
.tl-row-now { white-space:nowrap; }
/* What it was. Muted and struck: the price that counts is the one beside it,
   and a struck number competing for weight reads as two prices. */
.tl-row-was { font-weight:400; font-size:.8rem; line-height:1.3; color:var(--text-muted);
              text-decoration:line-through; white-space:nowrap; }
.tl-row-pp { font-size:.68rem; font-weight:500; color:var(--text-muted); }

/* Mobile: the three facts sit in a row under the name, each wearing its label. */
.tl-row-days, .tl-row-next { grid-column:1 / -1; }
.tl-row-days::before, .tl-row-next::before {
    content:attr(data-label) ": ";
    color:var(--text-muted); font-size:.7rem;
}
.tl-row-price { grid-column:1 / -1; text-align:left; justify-content:flex-start; }

/* ── EMPTY ── */
.tl-empty {
    text-align:center; padding:52px 20px;
    background:var(--card-bg); border:1px dashed var(--card-border); border-radius:12px;
}
.tl-empty-icon { color:var(--text-muted); margin-bottom:12px; }
.tl-empty-title { font-size:1.1rem; font-weight:700; letter-spacing:-.01em; margin:0 0 8px; }
.tl-empty-sub { font-size:.88rem; color:var(--text-secondary); margin:0 auto 20px; max-width:44ch; }

/* ── THE WAY OUT OF AN EMPTY PAGE ──
   The only action a zero-result page offers. Its rules went with the filter
   wall they happened to sit beside, leaving it as bare accent-colored text. */
.tl-apply {
    display:inline-flex; align-items:center;
    padding:10px 20px; border-radius:10px;
    background:var(--accent-gradient); border:0;
    font-size:.85rem; font-weight:600;
    text-decoration:none;
    transition:filter .15s ease;
}
.tl-apply, .tl-apply:hover, .tl-apply:focus-visible { color:var(--accent-text); }
.tl-apply:hover { filter:brightness(1.08); }
.tl-apply:focus-visible { outline:2px solid var(--accent-color); outline-offset:2px; }

/* ── PAGER ── */
.tl-pager { display:flex; align-items:center; justify-content:center; gap:12px; margin-top:24px; }
.tl-pager-link {
    font-size:.82rem; font-weight:600;
    padding:9px 18px; border-radius:10px;
    background:var(--bg-tertiary); border:1px solid var(--border-color);
    text-decoration:none;
    transition:border-color .15s ease;
}
.tl-pager-link, .tl-pager-link:hover, .tl-pager-link:focus-visible { color:var(--text-primary); }
.tl-pager-link:hover { border-color:var(--accent-color); }
.tl-pager-link:focus-visible { outline:2px solid var(--accent-color); outline-offset:2px; }
.tl-pager-where { font-size:.8rem; color:var(--text-muted); }

/* 701, not 768. The mobile block above stops at 700 and this used to start at
   768, so a window between the two got neither: the rows stacked as if on a
   phone while the sort row and the head laid themselves out for a desktop, and
   the head overflowed the clip on `.tl`. A breakpoint pair has to meet. */
/* 700.02 rather than 701: see the note in filter.css. */
@media (min-width:700.02px) {
    .tl-head { padding:48px 0 28px; }
    /* The banner and the type both step up, as the mockup's desktop drawing
       has them. Nothing else about the head changes with the width. */
    .tl-head.has-banner { padding:30px 0 26px; }
    .tl-title { font-size:2rem; }
    .tl-lede { font-size:.88rem; }
    .tl-results { padding:28px 0 100px; }

    /* The four columns, shared by the header and every row. */
    .tl-list-head, .tl-row {
        display:grid;
        grid-template-columns:minmax(0,1fr) 96px 180px 136px;
        gap:16px; align-items:center;
    }
    /* The column key is the card's own head, drawn as the admin's grid draws
       one: the card's ground with a rule under it, and the labels in the same
       small mono capitals. On the page's gray it read as labels floating above
       a table that began at its first row. */
    .tl-list-head {
        padding:10px 16px;
        background:var(--card-bg);
        border-bottom:1px solid var(--border-color);
        font-family:var(--font-mono); font-size:.64rem; font-weight:700;
        text-transform:uppercase; letter-spacing:.12em;
        color:var(--text-muted);
    }
    .tl-list-head span:last-child { text-align:right; }
    .tl-row-route, .tl-row-days, .tl-row-next, .tl-row-price { grid-column:auto; }
    .tl-row-next { flex-direction:column; align-items:flex-start; gap:2px; }
    /* Stacked, right-aligned: the struck price above, the one that counts
       below with its "pp". Set on one line the cell needed both numbers plus
       the suffix side by side, which is more than any sane column width and is
       why the price used to hang off the edge of the table. */
    .tl-row-price { text-align:right; flex-direction:column; align-items:flex-end; gap:1px; }
    .tl-row-days::before, .tl-row-next::before { content:none; }
}


@media (prefers-reduced-motion: reduce) {
    .tl-row, .tl-apply { transition:none; }
}
