/* ==========================================================================
   Sabah Booking — brand product listings.
   Shared by toursNew.jsp, holidaysNew.jsp, carsNew.jsp and transfersNew.jsp.

   Loaded AFTER css/brand-home.css, which supplies the tokens and the shared
   brand shell: header, footer, search bar, card / badge / rating / price,
   trust badge, buttons. This file adds ONLY what the listing pages need.

   The body carries "sb-home sb-listing": `sb-home` is the brand shell scope
   that brand-home.css resets are written against, `sb-listing` is this page.
   Note the body must NOT be given the class `sb-tours` — brand-home.css uses
   `.sb-tours` for the homepage's featured-tour grid.

   No !important anywhere: this page does not load the legacy stylesheets.
   ========================================================================== */

/* ---- listing hero ------------------------------------------------------ */
.sb-listhero{
  position:relative;background-size:cover;background-position:center;
  padding:34px var(--gutter) 76px;
}
.sb-listhero::before{                   /* white protection gradient, never a dark scrim */
  content:"";position:absolute;inset:0;
  background:linear-gradient(100deg,
    rgba(255,255,255,.97) 0%,
    rgba(255,255,255,.92) 38%,
    rgba(255,255,255,.55) 68%,
    rgba(255,255,255,.15) 100%);
}
.sb-listhero__inner{position:relative;z-index:2;max-width:620px}
.sb-crumb{font:var(--text-caption);color:var(--text-muted)}
.sb-crumb a{color:var(--text-muted)}
.sb-crumb a:hover{color:var(--text-link)}
.sb-listhero h1{font:var(--text-h1);font-size:clamp(28px,3.4vw,40px);margin-top:8px}
.sb-listhero__sub{font:var(--text-subtitle);font-size:15px;color:var(--text-muted);margin-top:8px}
.sb-listhero__trust{display:flex;gap:22px;margin-top:20px;flex-wrap:wrap}

/* the homepage search bar, floated over the hero */
.sb-searchwrap--flat{margin-top:-40px}

/* mascot leaning over the search bar - anchored to .sb-search-shell (not the
   hero) so it tracks the bar's own centered box at any viewport width,
   instead of a fixed offset from the full-bleed hero's edge. The PNG is
   already cropped right at the fingertips, so sitting it in front of
   (z-index above) the bar with the crop line dipping just inside the bar's
   top edge reads as "Boki pointing into the search field", not as a body
   cut off mid-torso. */
.sb-listhero__boki{
  position:absolute;right:130px;top:-165px;height:210px;width:auto;
  z-index:6;pointer-events:none;
}
@media (max-width:900px){
  /* the trust badges are the lowest-value line in the hero at this width -
     drop them so the mascot has somewhere to stand instead of overlapping
     the title, which runs nearly edge-to-edge once the hero stops being a
     two-column layout. The extra hero bottom padding gives the (now
     shorter) mascot clear air below the copy before it reaches the bar. */
  .sb-listhero{padding-bottom:116px}
  .sb-listhero__trust{display:none}
  .sb-listhero__boki{right:var(--gutter);top:-79px;height:100px}
}

/* ---- keyword live-suggest dropdown -------------------------------------
   Same row markup/classes as index.jsp's search modal (.sb-sres__*, from
   brand-home.css), just anchored under the bar instead of a fullscreen
   overlay. */
.sb-search-shell{position:relative;max-width:760px;margin:0 auto} /* match .sb-search's own centering so the panel below lines up with it */
.sb-search-suggest{
  position:absolute;top:100%;left:0;right:0;margin-top:6px;
  background:var(--surface-card);border:1px solid var(--border-subtle);
  border-radius:var(--radius-lg);box-shadow:var(--shadow-card);
  z-index:6;overflow:hidden;
}
.sb-search-suggest__body{max-height:60vh;overflow-y:auto;padding:8px}

/* ---- Fast Search ---------------------------------------------------------
   Was full-bleed (the .sb-fastform it sits in has no width of its own), so
   next to the 760px-wide search pill above it, it read as a huge, flat,
   near-invisible strip — same white as the page behind it. Matching the
   search bar's own width makes it read as one unit, and the icon disc +
   tinted bar give it the visual weight the plain white/border version
   didn't have. */
.sb-fastform{display:block}
.sb-fast{
  max-width:760px;margin:12px auto 0;
  background:var(--green-100);border:1px solid var(--green-300);
  border-radius:var(--radius-lg);box-shadow:var(--shadow-card);
  overflow:hidden;
}
.sb-fast__bar{
  display:flex;align-items:center;gap:12px;padding:14px 18px;cursor:pointer;
  font:var(--text-h4);color:var(--text-heading);list-style:none;
  transition:background var(--dur-fast) var(--ease-brand);
}
.sb-fast__bar::-webkit-details-marker{display:none}
.sb-fast__bar:hover{background:rgb(255 255 255 / .4)}
.sb-fast__bar__disc{
  width:32px;height:32px;border-radius:var(--radius-pill);background:var(--surface-card);
  display:inline-flex;align-items:center;justify-content:center;flex:none;
}
.sb-fast__bar .sb-icon{width:18px;height:18px;color:var(--green-600)}
.sb-fast__title{flex:1}
.sb-fast__count{
  min-width:22px;height:22px;padding:0 7px;border-radius:var(--radius-pill);
  background:var(--green-500);color:var(--text-inverse);
  font:var(--text-small);display:inline-flex;align-items:center;justify-content:center;
}
.sb-fast__count[hidden]{display:none}
.sb-fast__chev{color:var(--gray-500);transition:transform var(--dur-fast) var(--ease-brand)}
.sb-fast[open] .sb-fast__chev{transform:rotate(180deg)}
.sb-fast__body{padding:4px 18px 18px;border-top:1px solid var(--gray-100)}
.sb-cats{display:flex;flex-wrap:wrap;gap:10px;padding-top:14px}
.sb-fast__foot{display:flex;gap:10px;align-items:center;margin-top:16px;flex-wrap:wrap}
/* .sb-home prefix only to stay ahead of brand-home.css's `.sb-home a.sb-btn--ghost`
   (0,2,1), which restores charcoal on anchor-buttons. "Clear settings" is a real
   <a> and is meant to stay muted. */
.sb-home .sb-fast__foot .sb-btn--ghost{color:var(--text-muted)}

/* a category chip is a checkbox wearing a chip */
.sb-chip--cat{position:relative;user-select:none}
.sb-chip--cat input{position:absolute;opacity:0;width:0;height:0}
.sb-chip--cat .sb-icon{color:var(--gray-500)}
.sb-chip--cat:has(input:checked){
  background:var(--green-500);border-color:var(--green-500);color:var(--text-inverse);
}
.sb-chip--cat:has(input:checked) .sb-icon{color:var(--text-inverse)}
.sb-chip--cat:has(input:focus-visible){outline:2px solid var(--focus-ring);outline-offset:2px}

/* ---- toolbar: client-side filter chips + server-side sort ----------------
   Sand band, so the row reads as a distinct controls strip instead of chips
   floating loose between the search card and the results grid.

   The width matches the card grid's content box exactly: .sb-list is
   max-width:1400px with a gutter of padding on each side, so the band is
   that box minus two gutters. A plain max-width:1400px here (what the
   transparent version used, where it only fed padding) would overhang the
   cards by a gutter on each side once it had a background to show for it. */
.sb-toolbar{
  width:calc(100% - var(--gutter) * 2);
  max-width:calc(1400px - var(--gutter) * 2);
  margin:28px auto 10px;
  display:flex;justify-content:space-between;align-items:center;
  gap:18px;flex-wrap:nowrap;
  padding:14px 18px;
  background:var(--sand-100);border-radius:var(--radius-lg);
}
/* the sort group keeps its intrinsic width and stays pinned right; only the
   filter cluster gives way. nowrap above is what makes that happen - a
   wrapping flex container splits lines by hypothetical size BEFORE it ever
   shrinks an item, so with wrap the sort group jumped to its own line while
   .sb-fgroups sat there with slack it never used. */
.sb-toolbar > .sb-fgroup{flex:none}
.sb-toolbar__icon{flex:none;display:inline-flex;color:var(--green-600)}
.sb-toolbar__icon .sb-icon{width:18px;height:18px}
/* flex:1 + min-width:0 lets the filter groups wrap among THEMSELVES when the
   row runs out of width, instead of shoving the whole sort group onto a line
   of its own. The band's own padding costs ~36px, which is just enough to
   break the one-line fit the transparent toolbar used to get at >=1400px. */
.sb-fgroups{display:flex;gap:22px;flex-wrap:wrap;align-items:center;flex:1 1 auto;min-width:0}
.sb-fgroup{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.sb-fgroup__label{
  font:var(--text-small);color:var(--text-muted);
  text-transform:uppercase;letter-spacing:.5px;
}
.sb-chip{
  display:inline-flex;align-items:center;gap:6px;
  font:var(--text-subtitle);color:var(--text-heading);
  background:var(--sand-100);border:1px solid var(--border-subtle);
  border-radius:var(--radius-pill);padding:7px 14px;cursor:pointer;
  transition:background var(--dur-fast) var(--ease-brand),
             color var(--dur-fast) var(--ease-brand);
}
.sb-chip:hover{background:var(--sand-300)}
.sb-chip .sb-icon{width:15px;height:15px}
/* green = the page's own filter selection */
.sb-chip.is-on{background:var(--green-500);border-color:var(--green-500);color:var(--text-inverse)}
.sb-chip.is-on:hover{background:var(--green-600);border-color:var(--green-600)}
/* blue = selected state carried by the URL (sort, area) — both are anchors
   that reload the page, as against the green chips above, which are this
   page's own client-side filtering of the rows already loaded. */
.sb-chip--sort.is-on,
.sb-chip--area.is-on{background:var(--blue-100);border-color:var(--blue-300);color:var(--blue-800)}
.sb-chip--sort.is-on:hover,
.sb-chip--area.is-on:hover{background:var(--blue-100)}
/* Toolbar chips sit ON the sand band, where the default sand-100 chip would
   vanish into it - invert them to white. :not(.is-on) rather than relying on
   source order, so the green/blue selected states above keep winning even if
   these rules are ever moved. */
.sb-toolbar .sb-chip:not(.is-on){background:var(--surface-card)}
.sb-toolbar .sb-chip:not(.is-on):hover{background:var(--sand-300)}

/* ---- results grid --------------------------------------------------------
   Capped at 1400px: this grid had no width limit of its own, so on a wide
   monitor 3 columns stretched the cards (and the photos inside them, past
   their native resolution) into big, soft-looking tiles. */
.sb-list{max-width:1400px;margin:0 auto;padding:20px var(--gutter) 0}
.sb-list__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:22px;align-content:start}
.sb-list__grid .sb-card{position:relative}
.sb-list__grid .sb-card__media img{height:206px}
.sb-card__link{display:block;color:inherit}
.sb-card.is-filtered{display:none}

/* slow zoom on the photo, the same gesture the homepage interest tiles use */
.sb-card__media{overflow:hidden}
.sb-card__media img{transition:transform var(--dur-med) var(--ease-brand)}
.sb-card:hover .sb-card__media img{transform:scale(1.05)}

/* two-line clamps keep every card in a row the same height */
.sb-card__title--clamp,.sb-card__sub--clamp{
  display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;
}
.sb-card__title--clamp{-webkit-line-clamp:2;min-height:2.5em}
.sb-card__sub--clamp{-webkit-line-clamp:2;min-height:2.7em}

/* duration / accessibility line */
.sb-card__meta{
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:10px;
  font:var(--text-caption);color:var(--text-muted);
}
.sb-card__meta span{display:inline-flex;align-items:center;gap:5px}
.sb-card__meta .sb-icon{width:14px;height:14px}

/* rating placeholder keeps the price line aligned across cards */
.sb-rating--none{color:var(--text-muted);font-weight:400}

/* discounted price: struck original + coral "moment of action" */
.sb-price__was{font:var(--text-caption);color:var(--text-muted);text-decoration:line-through}
.sb-price__amount--deal{color:var(--coral-600)}

/* ---- listing card composition -------------------------------------------
   The listing card used to be five equally-weighted rows stacked down a wide
   tile — title, blurb, rating, meta, price — which read as a list, not a
   card: no focal point, and a lot of dead space on a 400px-wide tile.

   Three moves, all scoped to .sb-list__grid so the homepage's 5-across
   .sb-tours cards and searchResults' minimal .sb-hot__grid cards (same
   .sb-card classes, much less content) keep their compact proportions:
     1. duration moves onto the photo (.sb-card__dur), which was carrying
        only a badge and the heart;
     2. rating + attribute tags share one row (.sb-card__facts);
     3. price becomes a footer under a hairline (.sb-card__foot), pinned to
        the bottom so footers line up across a row whatever the title wraps to.
   Net: five loose rows become three plus a conclusion. */
.sb-list__grid .sb-card{display:flex;flex-direction:column}
.sb-list__grid .sb-card__link{display:flex;flex-direction:column;flex:1}
.sb-list__grid .sb-card__body{display:flex;flex-direction:column;flex:1;padding:16px}

/* fact pills along the bottom of the photo (duration, place) — they balance
   the badge above them and keep these one-glance facts out of the body rows.
   White at .92 rather than a dark scrim, per the brand's "white protection,
   never a dark scrim" rule for type over photography.

   A flex row, not individually-positioned pills: the place names differ a lot
   in length ("Kota Kinabalu" vs "Tawau (Semporna)" vs the zh forms), so they
   need to wrap as a group rather than overlap at narrow card widths. */
.sb-card__pills{
  position:absolute;left:10px;right:10px;bottom:10px;
  display:flex;flex-wrap:wrap;gap:6px;
}
.sb-card__pill{
  display:inline-flex;align-items:center;gap:5px;min-width:0;
  font:var(--text-small);color:var(--text-heading);
  background:rgba(255,255,255,.92);
  border-radius:var(--radius-pill);padding:5px 10px;
  box-shadow:0 1px 3px rgb(34 34 34 / .12);
}
.sb-card__pill .sb-icon{width:13px;height:13px;flex:none;color:var(--green-600)}

/* rating + tags on one line. min-height reserves the row on cards that have
   neither, so the footers below still align across the grid. */
.sb-card__facts{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  margin-top:10px;min-height:22px;
}
.sb-card__facts .sb-rating{margin-top:0}

/* neutral attribute chip (Accessible). Deliberately sand, not green: green
   chips are the savings signal below, and two green pills in one card muddies
   which one is the offer. */
.sb-tag{
  display:inline-flex;align-items:center;gap:5px;
  font:var(--text-small);color:var(--text-body-color);
  background:var(--sand-100);border:1px solid var(--border-subtle);
  border-radius:var(--radius-pill);padding:3px 9px;
}
.sb-tag .sb-icon{width:13px;height:13px;color:var(--green-600)}

/* price footer: margin-top:auto pins it to the card's bottom edge */
.sb-card__foot{
  display:flex;align-items:flex-end;justify-content:space-between;gap:10px;
  margin-top:auto;padding-top:12px;border-top:1px solid var(--gray-100);
}
.sb-card__foot .sb-price{margin-top:12px;flex-wrap:wrap}

/* savings pill. Green (the design system's tag colours), not coral — the
   discounted amount beside it is already the card's one coral accent, and
   the brand caps coral at ~3% of a screen. */
.sb-save{
  flex:none;font:var(--text-small);font-weight:600;
  color:var(--green-800);background:var(--green-100);
  border-radius:var(--radius-pill);padding:4px 9px;
}

/* wishlist heart — wishlist.js toggles `.liked` on the button */
.sb-wish.liked .sb-icon{color:var(--coral);fill:var(--coral)}

/* ---- empty state ------------------------------------------------------- */
/* Boki carries the bad news - the design system gives the mascot the help and
   dead-end moments rather than an apologetic icon. */
.sb-empty{
  grid-column:1/-1;text-align:center;background:var(--sand-100);
  border-radius:var(--radius-lg);padding:40px 24px 44px;
}
.sb-empty img{height:110px;width:auto;margin:0 auto}
.sb-empty p{font:var(--text-subtitle);font-size:15px;color:var(--text-muted);margin-top:12px}

/* .sb-empty__art: the dressed-up variant (tours.jsp's no-results state).
   A soft glow disc behind Boki, and the mascot itself cropped to bust-height
   — the art is a bigger, better-looking presence this way than a small
   full-body shot floating on flat sand. The wrapper's fixed height + hidden
   overflow does the cropping: the <img> is taller than the box and simply
   runs off the bottom, no object-fit/positioning trickery needed. */
.sb-empty__art{
  position:relative;width:210px;height:180px;margin:0 auto;overflow:hidden;
}
.sb-empty__art::before{
  content:"";position:absolute;inset:0;
  background:radial-gradient(circle at 50% 38%, var(--green-100) 0%, transparent 70%);
}
.sb-empty__art img{
  /* explicit width AND height (not width:auto), matching the source's own
     360:500 ratio — width:auto here would inherit the shared `.sb-home
     img{max-width:100%}` rule, which caps the width to the wrapper's own
     width while the explicit height stays uncapped, squashing Boki
     horizontally instead of just cropping his legs off. */
  position:relative;width:210px;height:292px;max-width:none;margin:0 auto;
  display:block;
  filter:drop-shadow(0 10px 14px rgb(34 34 34 / .12));
}
.sb-empty__art + p{margin-top:4px}

/* .sb-empty--search: the cross-product search dead end (searchResults.jsp),
   which unlike the filter empty state has to explain itself — a headline
   naming the query, a line of advice, and four ways back into the catalogue.

   Every override here is written at (0,2,0) on purpose: the base
   `.sb-empty p` above is (0,1,1), so a lone `.sb-empty__title` (0,1,0) would
   silently lose to it and inherit muted 15px body type. */
.sb-empty--search p{max-width:520px;margin-left:auto;margin-right:auto}
.sb-empty .sb-empty__title{
  font:var(--text-h3);color:var(--text-heading);margin-top:16px;
}
.sb-empty .sb-empty__browse{
  margin-top:22px;font:var(--text-small);color:var(--text-muted);
  text-transform:uppercase;letter-spacing:.5px;
}
.sb-empty__links{
  display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:12px;
}
.sb-empty__links .sb-btn{padding:10px 16px}
/* .sb-btn--ghost is transparent with border:0 — invisible against the sand
   card. Give the three secondary routes a real surface so they read as
   buttons, while green still marks the primary one. */
.sb-empty__links .sb-btn--ghost{
  background:var(--surface-card);border:1px solid var(--border-subtle);
}
.sb-empty__links .sb-btn--ghost:hover{background:var(--sand-300)}

/* ---- pagination -------------------------------------------------------- */
.sb-pager{display:flex;justify-content:center;gap:8px;flex-wrap:wrap;padding:32px var(--gutter) 0}
.sb-pager a,.sb-pager span{
  min-width:38px;height:38px;padding:0 12px;
  display:inline-flex;align-items:center;justify-content:center;
  font:var(--text-subtitle);font-weight:600;color:var(--text-heading);
  background:var(--surface-card);border:1px solid var(--border-subtle);
  border-radius:var(--radius-md);
  transition:background var(--dur-fast) var(--ease-brand);
}
.sb-pager a:hover{background:var(--sand-100)}
.sb-pager .is-current{background:var(--blue-500);border-color:var(--blue-500);color:var(--text-inverse)}

/* ---- scheduled shuttle strip (transfers only) -------------------------- */
/* These routes have no photography, so the card leads with an icon disc. */
.sb-shuttle{padding:26px var(--gutter) 0}
.sb-shuttle__grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;margin-top:16px}
.sb-shuttle__card{
  background:var(--surface-card);border:1px solid var(--border-subtle);
  border-radius:var(--radius-lg);padding:18px;display:block;color:inherit;
  transition:transform var(--dur-med) var(--ease-brand),
             box-shadow var(--dur-med) var(--ease-brand);
}
.sb-shuttle__card:hover{transform:translateY(-2px);box-shadow:var(--shadow-card)}
.sb-shuttle__disc{
  width:38px;height:38px;border-radius:50%;background:var(--blue-100);
  display:inline-flex;align-items:center;justify-content:center;
}
.sb-shuttle__disc .sb-icon{width:20px;height:20px;color:var(--blue-600)}
.sb-shuttle__card h3{font:var(--text-h4);margin-top:12px}
.sb-shuttle__times{font:var(--text-caption);color:var(--text-muted);margin-top:6px}
.sb-shuttle__price{
  font:600 16px/1 var(--font-display);color:var(--text-heading);
  margin-top:12px;display:flex;align-items:baseline;gap:4px;
}

/* ---- hot tours strip --------------------------------------------------- */
.sb-hot{padding:52px var(--gutter) 0}
.sb-hot__grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;margin-top:16px}
.sb-hot .sb-card__media img{height:150px}
.sb-hot .sb-card__title{font:var(--text-subtitle);font-weight:600;color:var(--text-heading)}
/* the why band that follows brings its own top margin */
.sb-listing .sb-why{margin-top:56px}

/* ==========================================================================
   Tier B additions — the secondary listings (category pages, hotels,
   cross-product search, travel-companion board).
   ========================================================================== */

/* 4-up variant. The holiday category listing runs a shorter card (no rating,
   no per-pax unit) and reads as too sparse at the default 3-up. */
.sb-list__grid--four{grid-template-columns:repeat(4,1fr)}
.sb-list__grid--four .sb-card__media img{height:170px}

/* area caption under a title — cross-product search has no price or rating
   to anchor the card, so the place is the only distinguishing line. */
.sb-card__place{
  display:flex;align-items:center;gap:6px;margin-top:6px;
  font:var(--text-caption);color:var(--text-muted);
}
.sb-card__place .sb-icon{width:14px;height:14px;flex:none;color:var(--green-600)}

/* result count beside a section heading */
.sb-count{
  font:var(--text-small);color:var(--text-muted);font-weight:500;
  background:var(--gray-100);border-radius:var(--radius-pill);padding:2px 10px;
  margin-left:8px;vertical-align:middle;
}

/* ---- hotel star class ---------------------------------------------------
   NOT the same thing as .sb-rating: that is a review average (4.5 from 12
   reviews), this is the property's licensed star class. A hotel card can
   legitimately carry both, so they must stay separate components. */
.sb-stars{display:inline-flex;gap:2px;align-items:center}
.sb-stars .sb-icon{width:14px;height:14px;color:var(--star);fill:var(--star)}
.sb-stars__label{
  font:var(--text-caption);color:var(--text-muted);margin-left:4px;
}

/* ---- accepted payment methods ------------------------------------------ */
.sb-pay{display:flex;gap:10px;align-items:center;margin-top:8px}
.sb-pay__item{
  display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;border-radius:var(--radius-sm);
  background:var(--blue-100);
}
.sb-pay__item .sb-icon{width:15px;height:15px;color:var(--blue-600)}

/* ---- card-level call to action -----------------------------------------
   .sb-card__link wraps the whole tile in an anchor, so a nested button would
   be invalid. Like .sb-wish, this sits as a SIBLING of the link inside the
   card, not a child of it. */
.sb-card__cta{
  display:block;margin:0 14px 14px;text-align:center;
}
.sb-card__cta .sb-btn{width:100%;justify-content:center}

/* ---- promo / CTA strip -------------------------------------------------- */
.sb-ctabanner{
  display:flex;align-items:center;justify-content:space-between;gap:20px;
  flex-wrap:wrap;
  background:var(--blue-100);border-radius:var(--radius-lg);
  padding:22px 26px;margin:0 var(--gutter);
}
.sb-ctabanner__copy h2{font:var(--text-h3)}
.sb-ctabanner__copy p{font:var(--text-body);color:var(--text-body-color);margin-top:4px}

/* ---- sortable table header ---------------------------------------------
   Pairs with .sb-table from brand-page.css on the travel-companion board. */
.sb-th--sort{cursor:pointer;user-select:none;white-space:nowrap}
.sb-th--sort:hover{color:var(--green-600)}
.sb-th--sort .sb-icon{
  width:13px;height:13px;margin-left:4px;vertical-align:-2px;color:var(--text-muted);
}
.sb-th--sort[aria-sort] .sb-icon{color:var(--green-600)}

/* board rows carry user-authored titles; keep them from blowing the column.
   Qualified with .sb-home a because brand-home.css sets `.sb-home a{color:...}`
   at specificity (0,1,1) — a bare .sb-posttitle (0,1,0) loses to it and the
   colour never applies. */
.sb-home a.sb-posttitle{font:var(--text-subtitle);font-weight:600;color:var(--text-heading)}
.sb-home a.sb-posttitle:hover{color:var(--green-600)}
.sb-postmeta{font:var(--text-caption);color:var(--text-muted)}
.sb-flag--open{
  display:inline-block;margin-left:6px;padding:1px 8px;border-radius:var(--radius-pill);
  background:var(--green-100);color:var(--green-800);
  font:var(--text-small);font-weight:600;
}

/* ---- responsive -------------------------------------------------------- */
@media (max-width:1100px){
  .sb-list__grid{grid-template-columns:repeat(2,1fr)}
  .sb-list__grid--four{grid-template-columns:repeat(3,1fr)}
  .sb-hot__grid,.sb-shuttle__grid{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:900px){
  .sb-searchwrap--flat{margin-top:-24px}
  /* back to wrapping here: there is no longer room to keep sort on the same
     line as the filters, so it drops below them instead of squeezing. */
  .sb-toolbar{align-items:flex-start;flex-wrap:wrap}
}
@media (max-width:767px){
  .sb-listhero{padding:22px var(--gutter) 96px}
  .sb-listhero::before{             /* horizontal fade leaves no photo at this width */
    background:linear-gradient(180deg,
      rgba(255,255,255,.96) 0%,
      rgba(255,255,255,.86) 55%,
      rgba(255,255,255,.4) 100%);
  }
  .sb-listhero__inner{max-width:none}
  .sb-list__grid{grid-template-columns:1fr}
  .sb-list__grid--four{grid-template-columns:repeat(2,1fr)}
  .sb-list__grid .sb-card__media img{height:190px}
  .sb-ctabanner{margin:0 var(--gutter);padding:18px}
  /* each filter group keeps to one swipeable line instead of wrapping into
     three - otherwise the toolbar pushes the first card off the screen.
     min-width:0 on both flex items is required for that: a flex item's
     default min-width is auto (= its content's min-content size), which
     wins over width:100%/overflow-x:auto and silently stretches the whole
     page sideways instead of scrolling inside the chip row. */
  .sb-toolbar{padding:12px 14px;gap:12px}
  .sb-fgroups{gap:10px;min-width:0}
  .sb-fgroup{
    width:100%;min-width:0;flex-wrap:nowrap;overflow-x:auto;padding-bottom:2px;
    scrollbar-width:none;
  }
  .sb-fgroup::-webkit-scrollbar{display:none}
  .sb-fgroup__label,.sb-fgroup .sb-chip,.sb-toolbar__icon{flex:none}
  .sb-fast__body{padding:4px 14px 16px}
  .sb-cats{gap:8px}
  .sb-chip{padding:6px 12px;font-size:13px}
}
