.hub-cs-search {
    /* Overridden inline (see hub-cruise-search.php) only when the agent explicitly
       picks a color in Cruise Hub → Wygląd. Otherwise every value below is a live
       var() reference into the host theme's own button tokens — never a color
       captured/copied at build time — so it tracks whatever the agent's theme
       actually renders, including if they change themes or repaint their palette
       later. --theme-button-* is Blocksy's (the test site's theme) own semantic
       hook for "what a button looks like"; --wp--preset--color-- is the generic
       block-theme (theme.json) equivalent. Text pairs with its own background
       (button text-on-accent isn't always white — Blocksy's is black), and hover
       is its own separate pair since a theme's hover state is very often not just
       a lighter/darker tint of the base color. */
    --hub-cs-accent: var(--theme-button-background-initial-color, var(--wp--preset--color--primary, #0a6b58));
    --hub-cs-accent-text: var(--theme-button-text-initial-color, #fff);
    --hub-cs-accent-hover: var(--theme-button-background-hover-color, var(--hub-cs-accent));
    --hub-cs-accent-text-hover: var(--theme-button-text-hover-color, var(--hub-cs-accent-text));
    /* Every font-size below is calc(var(--hub-cs-base-font) * ratio) instead of a
       flat rem value — rem is ALWAYS relative to <html>, but Blocksy's own font-size
       customizer never touches <html>, only `body` + its own selector list (via
       --theme-font-size, regenerated live in blocksy-dynamic-global-css.php whenever
       the agent changes the setting). rem therefore silently ignored the customizer
       entirely; this tracks the theme's real, current value instead. Defined once
       here (not per-declaration) so nested elements don't compound multiple relative
       steps into each other the way plain em would. */
    --hub-cs-base-font: var(--theme-font-size, 1rem);
    font-family: inherit;
}

.hub-cs-layout {
    display: flex;
    align-items: flex-start;
    gap: 24px;
}

.hub-cs-sidebar {
    /* +50px per request — .hub-cs-main is flex:1, so widening this automatically
       narrows the listing rows by the same 50px, no separate change needed there. */
    flex: 0 0 290px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 16px;
    background: #f7f7f7;
    border-radius: 8px;
}

/* Not sticky: with the expanded filter set the sidebar can be taller than the
   viewport, and a stuck (position: sticky) sidebar pins its top edge to the
   viewport while its own bottom portion — last filter options, the Termin
   popup's confirm buttons — scrolls off-screen with no way to reach it. The
   sidebar instead just scrolls up together with the listing cards, like the
   stacked mobile layout below (900px breakpoint) already does. */

.hub-cs-main {
    flex: 1 1 0%;
    min-width: 0;
}

.hub-cs-filter {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: calc(var(--hub-cs-base-font) * 0.8125);
}

.hub-cs-filter label {
    font-weight: 600;
    color: #444;
}

.hub-cs-filter-icon {
    height: 1em;
    width: 1em;
    object-fit: contain;
    vertical-align: -0.15em;
    margin-right: 0.4em;
}

.hub-cs-filter input,
.hub-cs-filter select,
.hub-cs-filter textarea {
    padding: 8px 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    font-size: calc(var(--hub-cs-base-font) * 0.875);
    width: 100%;
}

/* ---- custom <select> dropdown (see enhanceSelect() in search.js) ----
   The real <select> (.hub-cs-select-native) stays in the DOM as the actual
   form field, just not rendered — its own native option-list popup is the
   browser/OS's own UI and can't be restyled via CSS in any browser, which is
   what made every filter dropdown look like a stock OS control dropped into
   an otherwise custom-designed page. This trigger/popup pair is deliberately
   styled to match .hub-cs-daterange-toggle/-popup exactly (same padding,
   border, radius, shadow) — the Termin field already established this visual
   language for "a custom dropdown in this sidebar", so a plain select dropdown
   reads as the same kind of control, not a third, different-looking one. */
.hub-cs-select {
    position: relative;
}

.hub-cs-select-native {
    display: none;
}

.hub-cs-select-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    background: #fff;
    font-family: inherit;
    font-size: calc(var(--hub-cs-base-font) * 0.875);
    text-align: left;
    color: inherit;
    cursor: pointer;
}

.hub-cs-select-btn span {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.hub-cs-select-btn::after {
    /* A real CSS-drawn triangle instead of the Unicode "▾" glyph — a font
       character's rendered size can't be pinned to an exact pixel width via
       font-size (it also varies by font/OS), which is why the old glyph read
       as barely visible regardless of how its font-size was tuned. Border-
       triangle width is exact and independent of any font, and picks up the
       real theme accent instead of a fixed grey. */
    content: '';
    flex: 0 0 auto;
    width: 0;
    height: 0;
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-top: 6px solid var(--hub-cs-accent);
    transition: transform 0.15s ease;
}

.hub-cs-select-btn.is-open::after {
    transform: rotate(180deg);
}

.hub-cs-select-popup {
    position: absolute;
    z-index: 20;
    top: calc(100% + 4px);
    left: 0;
    width: 100%;
    max-height: 280px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid #ccc;
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
    padding: 4px;
}

.hub-cs-select-option {
    padding: 7px 10px;
    border-radius: 5px;
    font-size: calc(var(--hub-cs-base-font) * 0.875);
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.hub-cs-select-option:hover,
.hub-cs-select-option.is-active {
    /* Same tint used for the Termin calendar's own day-hover state — one
       consistent "this is the hovered/focused thing in a dropdown" language
       across both custom dropdowns in this sidebar. */
    background: color-mix(in srgb, var(--hub-cs-accent) 25%, #fff);
}

.hub-cs-select-option.is-selected {
    font-weight: 700;
}

.hub-cs-select-option.is-selected::before {
    content: '✓ ';
    color: var(--hub-cs-accent);
}

.hub-cs-filter-operator { position: relative; }
.hub-cs-filter-operator-popup { width: min(620px, calc(100vw - 40px)); max-height: none; overflow: visible; padding: 12px; }
.hub-cs-filter-operator-popup .hub-cs-operator-columns { display: flex; gap: 16px; }
.hub-cs-filter-operator-popup .hub-cs-operator-column { display: flex; max-height: 300px; min-width: 0; flex: 1 1 50%; flex-direction: column; gap: 2px; overflow-y: auto; }
.hub-cs-filter-operator-popup .hub-cs-operator-column-ships { padding-left: 16px; border-left: 1px solid #eee; }
.hub-cs-filter-operator-popup .hub-cs-operator-option { display: block; width: 100%; padding: 7px 8px; border: 0; border-radius: 5px; color: inherit; background: none; font: inherit; text-align: left; cursor: pointer; }
.hub-cs-filter-operator-popup .hub-cs-operator-option:hover { background: color-mix(in srgb, var(--hub-cs-accent) 15%, #fff); }
.hub-cs-filter-operator-popup .hub-cs-operator-option.is-selected { color: var(--hub-cs-accent-text); background: var(--hub-cs-accent); }
.hub-cs-filter-operator-popup .hub-cs-ship-option { display: flex; align-items: center; gap: 8px; padding: 7px 8px; border-radius: 5px; cursor: pointer; }
.hub-cs-filter-operator-popup .hub-cs-ship-option.is-checked { background: color-mix(in srgb, var(--hub-cs-accent) 30%, #fff); font-weight: 600; }
.hub-cs-filter-operator-popup .hub-cs-ship-option input { width: auto; accent-color: var(--hub-cs-accent); }
.hub-cs-filter-operator-popup .hub-cs-operator-ships-empty { margin: 0; padding: 7px 8px; color: #777; }
.hub-cs-filter-operator-popup .hub-cs-operator-search { display: block; width: 100%; box-sizing: border-box; margin: 0 0 10px; padding: 8px 10px; border: 1px solid #ccc; border-radius: 5px; font: inherit; color: inherit; background: #fff; }
.hub-cs-filter-operator-popup .hub-cs-operator-search-results { display: flex; max-height: 300px; flex-direction: column; gap: 2px; overflow-y: auto; }
.hub-cs-filter-operator-popup .hub-cs-operator-search-option { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; width: 100%; padding: 7px 8px; border: 0; border-radius: 5px; color: inherit; background: none; font: inherit; text-align: left; cursor: pointer; }
.hub-cs-filter-operator-popup .hub-cs-operator-search-option:hover { background: color-mix(in srgb, var(--hub-cs-accent) 15%, #fff); }
.hub-cs-filter-operator-popup .hub-cs-operator-search-operator { flex-shrink: 0; font-size: .85em; color: #777; }
/* The columns and the search results swap places; both are display:flex above,
   which would otherwise beat the [hidden] attribute. */
.hub-cs-filter-operator-popup .hub-cs-operator-columns[hidden],
.hub-cs-filter-operator-popup .hub-cs-operator-search-results[hidden] { display: none; }
.hub-cs-filter-operator-popup .hub-cs-daterange-actions { margin-top: 12px; }

@media (max-width: 700px) {
    .hub-cs-filter-operator-popup { position: static; width: 100%; margin-top: 6px; }
    .hub-cs-filter-operator-popup .hub-cs-operator-columns { flex-direction: row; }
}

/* ---- Port wypłynięcia / Port na trasie type-to-filter input (see
   initTypeahead() in search.js) — the suggestion list reuses
   .hub-cs-select-popup/-option's own look (same border/shadow/hover tint) so
   it reads as the same "dropdown suggestions" language as the select-based
   filters above, just triggered by typing instead of a click. */
.hub-cs-typeahead {
    position: relative;
}

/* Styled explicitly to match .hub-cs-daterange-toggle/.hub-cs-select-btn's own
   look (same padding/border/radius/font). Qualified with both the parent
   .hub-cs-typeahead wrapper AND the `input` type (specificity 0-2-1) rather
   than the class alone (0-1-0) — Blocksy's own generic text-input rule is
   `input:is([type=text], [type=email], ...)`, which resolves to 0-1-1 (an
   attribute selector inside :is() counts same as a class), so a bare single
   class here lost to it outright: found live, border/border-radius/padding/
   background were all still the theme's own var(--theme-form-field-*)
   defaults (a much rounder, greyed-out pill) despite this rule's values
   being individually correct. Same fix already used elsewhere in this
   codebase for the identical Blocksy-beats-a-single-class-selector shape
   of bug (see the row-title/row-region margin fix). */
.hub-cs-typeahead input.hub-cs-typeahead-input {
    display: block;
    box-sizing: border-box;
    /* Blocksy's own input:is([type=text], ...) rule (the one this whole
       selector exists to beat, see the comment above) also sets a fixed
       height: var(--theme-form-field-height, 40px) — a property this rule
       never used to touch at all, so it kept applying even after border/
       radius/padding/background here started winning. The result: same
       padding value on paper, but a shorter, more cramped-looking box than
       every sibling field here, all of which are <button>s the theme's
       input-only rule never touches and so size themselves purely from
       their own padding + line-height (found live: still visibly shorter
       after the first specificity fix, next to Kierunek/Termin/Armator). */
    height: auto;
    width: 100%;
    padding: 8px 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    font-family: inherit;
    font-size: calc(var(--hub-cs-base-font) * 0.875);
    background: #fff;
    color: inherit;
}

.hub-cs-typeahead-suggestions {
    position: absolute;
    z-index: 20;
    top: calc(100% + 4px);
    left: 0;
    width: 100%;
    max-height: 240px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid #ccc;
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
    padding: 4px;
}

.hub-cs-typeahead-option {
    display: block;
    width: 100%;
    padding: 7px 10px;
    border: 0;
    border-radius: 5px;
    background: none;
    font-family: inherit;
    font-size: calc(var(--hub-cs-base-font) * 0.875);
    text-align: left;
    color: inherit;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.hub-cs-typeahead-option:hover {
    background: color-mix(in srgb, var(--hub-cs-accent) 25%, #fff);
}

/* Port typeaheads: a country row ("Włochy · 12 portów") above the ports, the
   port's own country in grey on the right, and a heading over one country's
   list (assets/port-countries.js). */
.hub-cs-typeahead-option:has(.hub-cs-typeahead-option-meta) {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

.hub-cs-typeahead-option-name {
    overflow: hidden;
    text-overflow: ellipsis;
}

.hub-cs-typeahead-option-meta {
    flex-shrink: 0;
    font-size: .85em;
    color: #777;
}

.hub-cs-typeahead-country {
    font-weight: 600;
}

.hub-cs-typeahead-country::before {
    content: "";
    display: inline-block;
    width: 1em;
    height: 1em;
    margin-right: 6px;
    vertical-align: -0.15em;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18'/%3E%3C/svg%3E") center / contain no-repeat;
}

.hub-cs-typeahead-country + .hub-cs-typeahead-option:not(.hub-cs-typeahead-country) {
    margin-top: 4px;
    border-top: 1px solid #eee;
    border-radius: 0 0 5px 5px;
}

.hub-cs-typeahead-all {
    font-weight: 600;
    color: var(--hub-cs-accent);
    border-bottom: 1px solid #eee;
    border-radius: 5px 5px 0 0;
    margin-bottom: 4px;
}

.hub-cs-typeahead-heading {
    padding: 6px 10px 4px;
    font-size: calc(var(--hub-cs-base-font) * 0.75);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: #777;
}

/* Multi-port refinement used only by "Port na trasie". Selected ports remain
   directly under the input as compact removable chips, so the current AND
   criteria are visible without reopening anything. */
.hub-cs-route-port-multi .hub-cs-typeahead-suggestions {
    position: relative;
    top: auto;
    margin-top: 4px;
}

.hub-cs-modal-open {
    overflow: hidden;
}

.hub-cs-route-port-backdrop {
    position: fixed;
    z-index: 999998;
    inset: 0;
    background: rgba(15, 31, 48, .46);
}

.hub-cs-route-port-modal {
    position: fixed;
    z-index: 999999;
    top: 50%;
    left: 50%;
    width: min(820px, calc(100vw - 32px));
    max-height: min(680px, calc(100vh - 32px));
    overflow: auto;
    transform: translate(-50%, -50%);
    border: 1px solid var(--theme-border-color, #d8dee5);
    border-radius: 16px;
    background: #fff;
    box-shadow: 0 24px 70px rgba(15, 31, 48, .24);
    color: var(--theme-text-color, inherit);
}

.hub-cs-route-port-backdrop[hidden],
.hub-cs-route-port-modal[hidden] {
    display: none !important;
}

.hub-cs-route-port-modal-head,
.hub-cs-route-port-modal-actions,
.hub-cs-route-port-selected-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

.hub-cs-route-port-modal-head {
    padding: 20px 22px;
    border-bottom: 1px solid var(--theme-border-color, #d8dee5);
}

.hub-cs-route-port-modal-head strong {
    display: block;
    color: var(--theme-heading-color, inherit);
    font-size: 1.2em;
}

.hub-cs-route-port-modal-head span {
    display: block;
    margin-top: 3px;
    opacity: .72;
    font-size: .82em;
}

.hub-cs-route-port-modal-head > button {
    flex: 0 0 44px;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--theme-palette-color-6, #f2f5f7);
    color: inherit;
    font: inherit;
    font-size: 24px;
    cursor: pointer;
}

.hub-cs-route-port-modal-columns {
    display: grid;
    grid-template-columns: minmax(0, 1.08fr) minmax(260px, .92fr);
    min-height: 350px;
}

.hub-cs-route-port-modal-column {
    padding: 22px;
}

.hub-cs-route-port-modal-column + .hub-cs-route-port-modal-column {
    border-left: 1px solid var(--theme-border-color, #d8dee5);
    background: var(--theme-palette-color-7, #fafbfc);
}

.hub-cs-route-port-modal-selected > div:last-child {
    display: grid;
    gap: 7px;
    margin-top: 12px;
}

.hub-cs-route-port-selected-head span {
    opacity: .7;
    font-size: .78em;
}

.hub-cs-route-port-selected-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-height: 40px;
    padding: 3px 5px 3px 11px;
    border: 1px solid var(--theme-border-color, #d8dee5);
    border-radius: 8px;
    background: #fff;
    font-size: .84em;
}

.hub-cs-route-port-modal-actions {
    padding: 15px 22px;
    border-top: 1px solid var(--theme-border-color, #d8dee5);
}

@media (max-width: 640px) {
    .hub-cs-route-port-modal {
        width: calc(100vw - 20px);
        max-height: calc(100vh - 20px);
        border-radius: 12px;
    }

    .hub-cs-route-port-modal-head,
    .hub-cs-route-port-modal-column,
    .hub-cs-route-port-modal-actions {
        padding: 16px;
    }

    .hub-cs-route-port-modal-columns {
        grid-template-columns: 1fr;
        min-height: 0;
    }

    .hub-cs-route-port-modal-column + .hub-cs-route-port-modal-column {
        border-top: 1px solid var(--theme-border-color, #d8dee5);
        border-left: 0;
    }
}

.hub-cs-route-port-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 7px;
}

.hub-cs-route-port-chips[hidden],
.hub-cs-route-port-limit[hidden] {
    display: none !important;
}

.hub-cs-route-port-chip {
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    min-height: 30px;
    padding: 2px 3px 2px 9px;
    border: 1px solid color-mix(in srgb, var(--hub-cs-accent) 42%, #d8dee5);
    border-radius: 999px;
    background: color-mix(in srgb, var(--hub-cs-accent) 10%, #fff);
    font-size: calc(var(--hub-cs-base-font) * 0.75);
    line-height: 1.25;
}

.hub-cs-route-port-chip-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.hub-cs-route-port-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 26px;
    width: 26px;
    height: 26px;
    margin-left: 3px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
}

.hub-cs-route-port-remove:hover,
.hub-cs-route-port-remove:focus-visible {
    background: color-mix(in srgb, var(--hub-cs-accent) 24%, #fff);
    outline: none;
}

.hub-cs-route-port-limit {
    margin: 6px 0 0;
    color: #b42318;
    font-size: calc(var(--hub-cs-base-font) * 0.75);
}

.hub-cs-filter-checkbox label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-weight: 400;
    color: #333;
    cursor: pointer;
}

.hub-cs-filter-checkbox input[type="checkbox"] {
    width: auto;
    padding: 0;
}

/* Subregion "sorter" — see initSubregionSorter() in search.js. Options list
   has the same look as the polski pilot checkbox right below it (plain
   label/input pairs, no boxed input), on purpose: a radio list here reads as
   a lightweight in-page refinement, not another dropdown-style filter
   competing with Kierunek. */
.hub-cs-subregion {
    /* The divider belongs to this whole block (not just the options list) so
       it sits right before the sidebar's own 14px gap to whichever filter
       comes next — reading as "end of the subregion group", not a line stuck
       mid-way through it. */
    padding-bottom: 14px;
    border-bottom: 2px solid color-mix(in srgb, var(--hub-cs-accent) 55%, transparent);
}

/* A plain `display` declaration on a class always beats the UA stylesheet's
   `[hidden] { display: none }` (author origin wins ties regardless of
   specificity) — .hub-cs-subregion also carries .hub-cs-filter, whose own
   unconditional `display: flex` was winning that tie, so the chip + divider
   above kept rendering (empty label, but still a solid-colored pill and
   line) even with the `hidden` attribute set and no region chosen. Same bug,
   same fix, as .hub-cs-daterange-popup[hidden] above. */
.hub-cs-subregion[hidden] {
    display: none;
}

.hub-cs-subregion .hub-cs-subregion-label {
    /* Same pill treatment as the region badge on listing cards
       (.hub-cs-row-region) — rounded, solid theme-accent background, real
       button-pair colors, not invented ones. Two-class selector on purpose:
       .hub-cs-filter label (0,1,1) otherwise beats a single-class
       .hub-cs-subregion-label (0,1,0) regardless of source order and keeps
       its own plain color:#444. */
    display: inline-flex;
    align-items: center;
    margin: 0 0 8px;
    padding: 3px 12px;
    border-radius: 999px;
    background: var(--hub-cs-accent);
    color: var(--hub-cs-accent-text);
    font-size: calc(var(--hub-cs-base-font) * 0.75);
    font-weight: 600;
}

.hub-cs-subregion-options {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.hub-cs-subregion-option {
    display: flex;
    align-items: center;
    gap: 6px;
    /* Same ratio as .hub-cs-row-ship (the ship-name line on listing cards), per
       request. Set explicitly rather than left to inherit from .hub-cs-filter's
       own font-size — a <label> wrapping a form control doesn't reliably
       inherit font-size the way plain text does in every browser, which is
       what actually caused this to fall back to a UA default here despite
       .hub-cs-filter already declaring the same ratio one level up.  */
    font-size: calc(var(--hub-cs-base-font) * 0.8125);
    font-weight: 400;
    color: #333;
    cursor: pointer;
}

.hub-cs-subregion-option input[type="radio"] {
    width: auto;
}

.hub-cs-filter-actions {
    display: flex;
    gap: 8px;
    margin-top: 4px;
}

/* Mobile/tablet filter drawer chrome — all of this is a no-op above 900px
   (see the max-width: 900px block near the bottom for the actual drawer
   behavior): the sidebar is never off-canvas at desktop, so its close
   button/backdrop/the Filtruj pill have nothing to do there. Hidden here at
   the base level (not just left unstyled) so they can never flash visible
   before the mobile rules load, and so nothing here has to also be undone
   in a min-width: 901px override. */
.hub-cs-filter-backdrop,
.hub-cs-sidebar-close,
.hub-cs-pill-btn {
    display: none;
}

.hub-cs-daterange {
    position: relative;
}

.hub-cs-daterange-toggle {
    display: block;
    width: 100%;
    padding: 8px 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    font-size: calc(var(--hub-cs-base-font) * 0.875);
    text-align: left;
    background: #fff;
    cursor: pointer;
    color: inherit;
}

.hub-cs-daterange-popup {
    position: absolute;
    z-index: 20;
    top: 0;
    /* Opens rightward into the results column rather than downward over the narrow
       240px sidebar — a two-month calendar needs more width than the sidebar has. */
    left: 100%;
    margin-left: 10px;
    width: max-content;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 14px;
    background: #fff;
    border: 1px solid #ccc;
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
}

/* A plain `display` declaration on a class always beats the UA stylesheet's
   `[hidden] { display: none }` (author origin wins ties regardless of specificity),
   so toggling the `hidden` attribute from JS silently did nothing on its own — this
   rule's higher specificity (class + attribute) is what actually makes it take
   effect. Found live: the popup stayed visible after Zastosuj despite `hidden` being
   set, because nothing was overriding the unconditional `display: flex` above. */
.hub-cs-daterange-popup[hidden] {
    display: none;
}

.hub-cs-daterange-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 2px;
}

.hub-cs-calendar-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.hub-cs-calendar-nav {
    width: 26px;
    height: 26px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background: #fff;
    cursor: pointer;
    font-size: calc(var(--hub-cs-base-font) * 0.875);
    line-height: 1;
    padding: 0;
}

.hub-cs-calendar-months {
    display: flex;
    gap: 18px;
}

.hub-cs-calendar-month {
    width: 200px;
}

.hub-cs-calendar-month-title {
    text-align: center;
    font-weight: 600;
    font-size: calc(var(--hub-cs-base-font) * 0.8125);
    margin-bottom: 6px;
    text-transform: capitalize;
}

.hub-cs-calendar-weekdays {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    font-size: calc(var(--hub-cs-base-font) * 0.6875);
    color: #888;
    text-align: center;
    margin-bottom: 4px;
}

.hub-cs-calendar-days {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
}

.hub-cs-calendar-day {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: calc(var(--hub-cs-base-font) * 0.75);
    border: none;
    border-radius: 4px;
    background: none;
    color: inherit;
    cursor: pointer;
    padding: 0;
}

.hub-cs-calendar-day.is-empty {
    visibility: hidden;
    cursor: default;
}

.hub-cs-calendar-day:disabled {
    color: #ccc;
    cursor: default;
}

.hub-cs-calendar-day:hover:not(:disabled):not(.is-selected) {
    background: color-mix(in srgb, var(--hub-cs-accent) 25%, #fff);
}

.hub-cs-calendar-day.is-selected {
    background: var(--hub-cs-accent);
    color: var(--hub-cs-accent-text);
    font-weight: 700;
}

.hub-cs-calendar-day.is-in-range {
    background: color-mix(in srgb, var(--hub-cs-accent) 15%, #fff);
    border-radius: 0;
}

@media (max-width: 600px) {
    .hub-cs-daterange-popup {
        left: 0;
        top: calc(100% + 6px);
        margin-left: 0;
        width: 260px;
    }

    .hub-cs-calendar-months {
        flex-direction: column;
        gap: 14px;
    }

    .hub-cs-calendar-month {
        width: 100%;
    }
}

.hub-cs-btn {
    padding: 9px 18px;
    border-radius: 5px;
    border: 1px solid #ccc;
    background: #fff;
    cursor: pointer;
    /* Same reasoning as --hub-cs-accent: a real theme button's own font-size,
       not a guess. Found live (not by design): a plain <button> stayed at the
       0.875rem fallback while <button type="submit"> escaped to Blocksy's real
       --theme-button-font-size, purely because [type="submit"] happened to win a
       CSS specificity tie — i.e. the "wrong" one was accidentally the properly
       inherited one. Made deliberate and consistent for every button here instead
       of leaving it up to a selector-specificity coincidence. */
    font-size: var(--theme-button-font-size, 0.875rem);
}

.hub-cs-btn-primary {
    background: var(--hub-cs-accent);
    border-color: var(--hub-cs-accent);
    color: var(--hub-cs-accent-text);
}

.hub-cs-btn-primary:hover {
    background: var(--hub-cs-accent-hover);
    border-color: var(--hub-cs-accent-hover);
    color: var(--hub-cs-accent-text-hover);
}

.hub-cs-toolbar {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 14px;
    margin-bottom: 16px;
}

/* display: contents at desktop — a transparent wrapper so Sortuj (its only
   child there, .hub-cs-pill-btn stays display: none) lays out exactly as if
   this div weren't there at all. Mobile/tablet override this to a real flex
   row of two matched pill buttons — see the max-width: 900px block. */
.hub-cs-toolbar-actions {
    display: contents;
}

.hub-cs-sort {
    flex: 0 0 220px;
}

.hub-cs-status {
    color: #555;
    font-size: calc(var(--hub-cs-base-font) * 0.875);
}

.hub-cs-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.hub-cs-row {
    display: flex;
    align-items: stretch;
    /* Back to the original 250px — 270px was a guess made without live
       verification and turned out unnecessary once the image sizing below
       stopped ballooning its own width (see that rule's comment). */
    height: 250px;
    border: 1px solid #e2e2e2;
    border-radius: 8px;
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    background: #fff;
    transition: box-shadow 0.15s ease;
}

.hub-cs-row:hover {
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
}

.hub-cs-row-image {
    /* Fixed WIDTH now (was flex: 0 0 auto, letting aspect-ratio derive width
       from the row's stretched height) — at 16/9 that meant a much wider
       image than 4/3 used to produce at the same 250px height (~444px vs
       333px), which starved .hub-cs-row-body's own width badly enough that
       its title/route/fact-boxes rendered cramped (found live via
       screenshot: "everything looks tiny"). A fixed width sidesteps that —
       .hub-cs-row's own align-items: stretch still stretches this element's
       HEIGHT to the row's 250px regardless (flex-basis only ever set WIDTH
       in a row-direction flex container), and object-fit: cover on the real
       <img> below crops to fill whatever box results, so aspect-ratio is
       still doing real work here — just for MOBILE, where .hub-cs-row
       switches to flex-direction: column and flex-basis: auto (see that
       media query) flips this into controlling HEIGHT instead, at which
       point aspect-ratio is what makes a full-width mobile thumbnail read
       as 16:9 rather than some arbitrary intrinsic size. */
    position: relative;
    flex: 0 0 380px;
    aspect-ratio: 16 / 9;
    background: #eee;
    overflow: hidden;
}

.hub-cs-row-image img:not(.hub-cs-row-image-logo) {
    /* :not() excludes the logo overlay — it's also a direct <img> child of
       .hub-cs-row-image, so a plain `.hub-cs-row-image img` selector matched both
       and this rule (declared first, same specificity) was winning the tie,
       stretching the logo to fill the whole photo box instead of a small corner
       badge — found live via screenshot, not obvious from source. */
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.hub-cs-row-image-logo {
    position: absolute;
    top: 3px;
    left: 3px;
    width: 25%;
    height: auto;
    border-radius: 3px;
}

.hub-cs-row-body {
    flex: 1 1 0%;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 14px 18px;
    overflow: hidden;
}

/* +10% of the date line's own font-size — still used by the admin picker's
   own compact date line (renderPickerCard()'s buildDateLine() call), even
   though this listing row moved to fact boxes below and no longer needs it
   itself. */
.hub-cs-date-icon {
    height: 1.1em;
    width: auto;
    vertical-align: -0.15em;
    margin-right: 0.35em;
}

.hub-cs-row-body .hub-cs-row-title {
    /* No font-size/color here on purpose — it's a real <h3>. A class-scoped rule
       here would have HIGHER specificity than the theme's own bare `h3 {...}`
       rule and would beat it, silently overriding the theme's real heading
       size/color (found live, back when this was h2: a 1.25x-ratio "fix" here
       was itself still blocking real inheritance, just with a friendlier-
       looking wrong value instead of an obviously wrong one). Leaving these
       unset lets the theme's own heading CSS win outright. */
    flex-shrink: 0;
    margin: 0;
    /* Blocksy sets margin-block-end (a logical property, not margin-bottom) on
       every heading via a selector whose real specificity ties this rule's own —
       same bug found on .hub-cs-row-region-chip, same fix: qualify with the
       actual parent to break the tie, and target the exact same logical
       property name. */
    margin-block-end: 0;
    line-height: 1.25;
    /* Reserves the full 2-line height regardless of whether THIS title actually
       wraps to 2 lines — without it, a 1-line title left the fact boxes below
       sitting higher than in a neighboring mobile-slider card whose title
       wrapped further (same alignment bug already found and fixed on
       [hub_boxy]'s card). */
    min-height: 2.5em;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.hub-cs-row-route {
    /* Sized to match .hub-cs-row-ship's font (per request) rather than the real
       theme <p> size — confirmed live that the real size DOES genuinely inherit
       correctly (identical font-size/line-height/color to a real theme paragraph),
       this is a deliberate visual-consistency choice, not a fallback for a broken
       inheritance. */
    font-size: calc(var(--hub-cs-base-font) * 0.8125);
    line-height: 1.4;
    flex-shrink: 0;
    margin: 0;
    /* Same reservation as the title above, same reason. */
    min-height: 2.8em;
    display: -webkit-box;
    /* Reduced from 4 to 2 lines — the redesigned card shows a region chip and
       three fact boxes below this, which a 4-line route no longer leaves room
       for at this row's height; a long route now trims visually the same way
       [hub_boxy]'s card's does (2-line clamp, ellipsis only past that). */
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.hub-cs-route-port {
    /* No color anywhere here on purpose (per request: bold, no accent) — stays
       whatever color this <p> already has, only weight/scale move. inline-block
       + transform (not a dot marker) is what actually moved smoothly live — the
       earlier absolutely-positioned dot needed live position measurement and
       came out misaligned/jumpy instead of gliding under the text. */
    display: inline-block;
    transform-origin: center bottom;
    transition: transform 0.35s ease, opacity 0.35s ease;
}

.hub-cs-route-port.is-active {
    font-weight: 700;
    transform: scale(1.12);
}

.hub-cs-route-port.is-trail {
    /* Fades via opacity rather than a color mix, so it stays accent-free too. */
    opacity: 0.55;
}

@media (prefers-reduced-motion: reduce) {
    .hub-cs-route-port {
        transition: none;
    }
}

.hub-cs-row-region-chip {
    /* A category-pill badge (matching the theme's own blog category pill
       style: rounded, solid background) reads as a tag/label, not body-of-
       text content — unlike the plain-text real headings elsewhere in this
       plugin, a badge is expected to have its own compact size, so unlike
       those this one DOES set font-size deliberately. Colors still aren't
       invented: background/text are the same var(--hub-cs-accent)/
       var(--hub-cs-accent-text) pair the theme's own buttons resolve to. A
       <span>, not a heading, so none of the Blocksy margin-tie footguns
       documented on .hub-cs-row-title/.hub-cs-row-price-value apply here. */
    align-self: flex-start;
    flex-shrink: 0;
    padding: 3px 12px;
    border-radius: 999px;
    background: var(--hub-cs-accent);
    color: var(--hub-cs-accent-text);
    font-size: calc(var(--hub-cs-base-font) * 0.75);
    line-height: 1.4;
    font-weight: 600;
}

/* Region + transport chips in one line, same pill style for both. */
.hub-cs-row-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    flex-shrink: 0;
}

/* Same equal-width-columns approach as [hub_boxy]'s own fact boxes
   (Hub_CS_Offer_Card_Renderer/boxes.css) — CSS Grid with minmax(0, 1fr) on
   the OD/DO tracks, not flex, because a flex item's default min-width: auto
   would otherwise size each date box off its OWN text ("23.09.2026" wider
   than "5.09.2026") instead of matching its sibling. Duplicated rather than
   shared across search.css/boxes.css since those are independently loaded
   stylesheets for two different shortcodes (same reasoning as every other
   search.js/frontsearch.js duplication in this codebase). */
.hub-cs-row-facts {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr);
    gap: 8px;
    flex-shrink: 0;
    /* .hub-cs-row-body has no justify-content, so any height the fixed
       250px row leaves unused past title+route+chip's own natural size
       was just collecting as dead space AFTER this, the last child (found
       live via screenshot: a big gap under the fact boxes, and — same
       symptom, same cause — too little visual separation between the
       region chip and this row, since 6px was true everywhere). margin-top:
       auto claims all of that leftover space for itself instead, pushing
       this row flush to the body's bottom edge and turning the slack into
       breathing room right above it. */
    margin-top: auto;
}

.hub-cs-row-fact {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 4px 8px;
    border: 1.5px solid var(--hub-cs-accent);
    border-radius: 8px;
    text-align: center;
    min-width: 0;
    /* Safety net: minmax(0, 1fr) above deliberately allows this box to
       shrink narrower than its own date text's natural width (that's what
       makes the two date boxes match each other) — this clips instead of
       letting the text spill past the border on a narrow mobile-slider card. */
    overflow: hidden;
}

.hub-cs-row-fact span {
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #888;
}

.hub-cs-row-fact b {
    font-size: 0.8125rem;
    white-space: nowrap;
}

.hub-cs-row-fact-nights {
    min-width: 50px;
}

.hub-cs-row-fact-nights b {
    font-size: 1.125rem;
    line-height: 1.1;
}

.hub-cs-row-side {
    flex: 0 0 190px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 14px 18px;
    border-left: 1px solid #eee;
    text-align: center;
}

/* Icon above the name (same treatment as [hub_boxy]'s own card), centered —
   align-self: stretch on the name span below is what still gives it a
   definite width to ellipsize against despite the column's align-items:
   center (which sizes the icon to its own content, exactly as intended). */
.hub-cs-row-ship {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    font-size: calc(var(--hub-cs-base-font) * 0.8125);
}

.hub-cs-row-ship img {
    height: 2.2em;
    width: auto;
}

.hub-cs-row-ship-name {
    align-self: stretch;
    min-width: 0;
    font-weight: 700;
    /* Explicit, not theme-derived — see the matching, live-verified comment on
       [hub_boxy]'s own .hub-cs-box-card-ship-name in boxes.css: this site's
       real --theme-link-initial-color resolves to black, not blue. */
    color: #004aad;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.hub-cs-row-price {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
}

.hub-cs-row-price .hub-cs-row-price-value {
    /* No font-size here — real h3, theme's own size wins outright, full stop.
       margin-block-start/end set explicitly (not just margin:0) and the selector
       qualified with its parent for extra specificity — same fix as
       .hub-cs-row-title: Blocksy's own heading rule sets these as logical
       properties on every h1-h6, which tie in specificity with a bare margin:0
       here and (loaded later) win, padding the price out with extra space
       above/below. */
    margin-block-start: 0;
    margin-block-end: 0;
    margin: 0;
}

.hub-cs-row-price-eur {
    font-size: 0.8125rem;
    color: #767676;
}

.hub-cs-row-cta {
    /* Can't be a real <button>/<a> — it already sits inside .hub-cs-row's own <a>,
       and nesting interactive content inside interactive content is invalid HTML.
       A real button-sized/-shaped block now (per the reference card design),
       not the small text link this used to be. */
    padding: 10px 22px;
    border-radius: 8px;
    font-weight: 700;
    font-size: calc(var(--hub-cs-base-font) * 0.8125);
    color: var(--hub-cs-accent-text);
    background: var(--hub-cs-accent);
    white-space: nowrap;
}

.hub-cs-row:hover .hub-cs-row-cta {
    background: var(--hub-cs-accent-hover);
    color: var(--hub-cs-accent-text-hover);
}

.hub-cs-pagination {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 20px;
}

.hub-cs-page-btn {
    padding: 6px 12px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background: #fff;
    cursor: pointer;
    font-size: var(--theme-button-font-size, 0.875rem);
}

.hub-cs-page-btn.is-active {
    background: var(--hub-cs-accent);
    border-color: var(--hub-cs-accent);
    color: var(--hub-cs-accent-text);
}

.hub-cs-page-btn:disabled {
    cursor: default;
    opacity: .4;
}

.hub-cs-page-ellipsis {
    padding: 6px 4px;
    color: #999;
}

@media (max-width: 900px) {
    body.hub-cs-filter-drawer-open { overflow: hidden; }
    .hub-cs-layout {
        flex-direction: column;
    }

    /* .hub-cs-layout has align-items: flex-start (correct at desktop, where
       the cross axis is height and the sidebar/results column genuinely
       shouldn't stretch to match each other's height) — flex-direction:
       column above flips the cross axis to WIDTH, and flex-start on that
       axis means "don't stretch," so without an explicit width here this
       element sized itself off its own content's natural/preferred width
       instead of the viewport (confirmed live via an on-page measurement:
       3400px rendered width at a 402px viewport — the .hub-cs-boxes-grid
       slider's own width: 100% inside it was correctly computed, just
       against this wrong, unconstrained 3400px basis). .hub-cs-sidebar
       right below already has its own explicit width: 100% for the exact
       same reason — this was the one part of that fix that got missed. */
    .hub-cs-main {
        width: 100%;
    }

    /* Sidebar becomes an off-canvas drawer, not an inline wrapped row of
       filters (that was this rule's previous behavior) — opened via the
       Filtruj pill below, closed via the X inside it
       (data-hub-cs-filter-close), the backdrop, or submitting the form (see
       search.js). translateX (not display: none) so the slide transition
       has something to animate — width itself is set per-breakpoint further
       down (100% phone, 50% tablet, per request), not here. */
    .hub-cs-sidebar {
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        z-index: 2147483000;
        flex-basis: auto;
        flex-wrap: nowrap;
        overflow-y: auto;
        border-radius: 0;
        transform: translateX(-100%);
        transition: transform 0.25s ease;
        box-shadow: 4px 0 24px rgba(0, 0, 0, 0.18);
    }

    .hub-cs-search.is-filter-open .hub-cs-sidebar {
        transform: translateX(0);
    }

    .hub-cs-sidebar-close {
        display: flex;
        align-self: flex-end;
        align-items: center;
        justify-content: center;
        width: 32px;
        height: 32px;
        margin-bottom: 4px;
        border: none;
        border-radius: 999px;
        background: #ececec;
        color: inherit;
        font-size: 20px;
        line-height: 1;
        cursor: pointer;
    }

    .hub-cs-search.is-filter-open .hub-cs-filter-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 2147482999;
        background: rgba(0, 0, 0, 0.4);
    }

    /* Sortuj's own trigger (built by enhanceSelect(), see search.js) restyled
       to match the Filtruj pill right next to it — a leading icon via
       ::before (this select's own ::after triangle is hidden, redundant
       next to a fixed "Sortuj" label that never shows the live value, see
       updateLabel()'s data-hub-cs-static-label branch) rather than the
       left-aligned dropdown-with-current-value look this same class has at
       every OTHER field. */
    .hub-cs-toolbar-actions {
        display: flex;
        gap: 10px;
        width: 100%;
    }

    .hub-cs-pill-btn,
    .hub-cs-sort {
        flex: 1 1 0%;
    }

    .hub-cs-pill-btn {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
        padding: 9px 14px;
        border: 1px solid #ccc;
        border-radius: 999px;
        background: #fff;
        font-family: inherit;
        font-size: calc(var(--hub-cs-base-font) * 0.875);
        font-weight: 600;
        color: inherit;
        cursor: pointer;
    }

    .hub-cs-sort label {
        display: none;
    }

    .hub-cs-sort .hub-cs-select-btn {
        justify-content: center;
        border-radius: 999px;
        font-weight: 600;
    }

    .hub-cs-sort .hub-cs-select-btn::before {
        content: '⇅';
    }

    .hub-cs-sort .hub-cs-select-btn::after {
        display: none;
    }

    /* No .hub-cs-row/.hub-cs-list mobile rules here anymore — <=900px never
       renders either of those anymore. search.js's renderResults() switches
       to renderBoxStyleCard() and the results container's class to
       .hub-cs-boxes-grid at this same breakpoint (mobileCardsMql), so the
       mobile slider comes entirely from boxes.css's own .hub-cs-boxes-grid
       rules — see that file, and renderBoxStyleCard()'s comment in
       search.js for why a reflow of THIS shape was never going to produce
       [hub_boxy]'s card. */
}

/* Tablet: drawer opens to half the screen width. */
@media (min-width: 601px) and (max-width: 900px) {
    .hub-cs-sidebar {
        width: 50%;
    }
}

@media (max-width: 600px) {
    .hub-cs-toolbar {
        flex-direction: column;
        align-items: stretch;
    }

    /* Phone: drawer opens to the full screen width. */
    .hub-cs-sidebar {
        width: 100%;
    }
}

/* "Szukaj opisowo" (natural-language search) — a fixed tab at the right edge of the
   viewport, visible at EVERY breakpoint. Deliberately not .hub-cs-pill-btn (that
   class is display:none above 900px, base rule near the top of this file, because
   its target — the sidebar — is already shown inline on desktop and the pill has
   nothing to toggle there); this drawer has no "always visible inline" desktop
   equivalent, so its trigger stays visible everywhere. The drawer itself reuses the
   same off-canvas translateX() slide technique as .hub-cs-sidebar's own mobile
   drawer (max-width: 900px block above), just mirrored to the right. */
html.hub-cs-modal-open {
    overflow: hidden;
}

.hub-cs-nlp-tab {
    position: fixed;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2147481000;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 14px 8px;
    border: none;
    border-radius: 8px 0 0 8px;
    background: var(--hub-cs-accent);
    color: var(--hub-cs-accent-text);
    cursor: pointer;
    box-shadow: -2px 2px 10px rgba(0, 0, 0, 0.18);
    writing-mode: vertical-rl;
    text-orientation: mixed;
    font: inherit;
    font-size: calc(var(--hub-cs-base-font) * 0.85);
}

.hub-cs-nlp-tab:hover {
    background: var(--hub-cs-accent-hover);
    color: var(--hub-cs-accent-text-hover);
}

.hub-cs-nlp-tab svg {
    transform: rotate(90deg);
}

.hub-cs-nlp-drawer {
    position: fixed;
    inset: 0;
    z-index: 2147483000;
}

.hub-cs-nlp-drawer[hidden] {
    display: none;
}

.hub-cs-nlp-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
}

.hub-cs-nlp-panel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(420px, 100%);
    display: flex;
    flex-direction: column;
    background: #fff;
    box-shadow: -4px 0 24px rgba(0, 0, 0, 0.18);
    outline: none;
    transform: translateX(100%);
    transition: transform 0.25s ease;
}

/* .is-open (not [hidden] alone — see nlp-chat.js's openDrawer()) is what actually
   drives the slide-in: deferred to the next animation frame so the transition has
   something to animate from. */
.hub-cs-nlp-drawer.is-open .hub-cs-nlp-panel {
    transform: translateX(0);
}

.hub-cs-nlp-close {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 32px;
    height: 32px;
    border: none;
    border-radius: 999px;
    background: #ececec;
    color: inherit;
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    z-index: 1;
}

.hub-cs-nlp-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 20px 48px 14px 20px;
    border-bottom: 1px solid #eee;
    flex: 0 0 auto;
}

.hub-cs-nlp-header strong {
    font-size: calc(var(--hub-cs-base-font) * 1.05);
}

.hub-cs-nlp-header-avatar,
.hub-cs-nlp-msg-avatar {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border-radius: 999px;
    overflow: hidden;
    background: color-mix(in srgb, var(--hub-cs-accent) 15%, #fff);
    color: var(--hub-cs-accent);
    display: flex;
    align-items: center;
    justify-content: center;
}

.hub-cs-nlp-header-avatar img,
.hub-cs-nlp-msg-avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.hub-cs-nlp-header-avatar svg,
.hub-cs-nlp-msg-avatar svg {
    width: 60%;
    height: 60%;
}

.hub-cs-nlp-messages {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 16px 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.hub-cs-nlp-msg {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    max-width: 88%;
}

.hub-cs-nlp-msg.is-user {
    align-self: flex-end;
    flex-direction: row-reverse;
}

.hub-cs-nlp-msg.is-assistant {
    align-self: flex-start;
}

.hub-cs-nlp-bubble {
    padding: 9px 14px;
    border-radius: 16px;
    font-size: calc(var(--hub-cs-base-font) * 0.9);
    line-height: 1.4;
    white-space: pre-wrap;
}

.hub-cs-nlp-msg.is-user .hub-cs-nlp-bubble {
    background: var(--hub-cs-accent);
    color: var(--hub-cs-accent-text);
    border-bottom-right-radius: 4px;
}

.hub-cs-nlp-msg.is-assistant .hub-cs-nlp-bubble {
    background: #f0f0f0;
    color: #222;
    border-bottom-left-radius: 4px;
}

.hub-cs-nlp-composer {
    flex: 0 0 auto;
    display: flex;
    gap: 8px;
    padding: 14px 20px;
    border-top: 1px solid #eee;
}

.hub-cs-nlp-input {
    flex: 1 1 auto;
    padding: 9px 12px;
    border: 1px solid #ccc;
    border-radius: 999px;
    font: inherit;
    box-sizing: border-box;
}

.hub-cs-nlp-submit {
    margin: 0 20px 16px;
}

@media (max-width: 600px) {
    .hub-cs-nlp-panel {
        width: 100%;
    }
}

/* Grouped listing: how many fare variants this one card stands for. */
.hub-cs-row-variants,
.hub-cs-box-card-variants {
    display: inline-block;
    margin-top: 4px;
    font-size: 0.8125rem;
    font-weight: 600;
    color: #0b6b3a;
}

.hub-cs-box-card-variants {
    margin: 0 0 6px;
}
