/* ==========================================================================
   Sabah Booking — brand product detail pages.
   carinfo, and the tourinfo / holidayinfo / hotelinfo / transferinfo /
   ticketinfo siblings that follow.

   Loaded AFTER brand-home.css (tokens + shell) and brand-page.css (prose,
   form fields, tables). Body carries "sb-home sb-page sb-detail".

   UNLIKE the other brand pages, detail pages DO load jQuery. Their booking
   widget — datepicker, option selection, live price calculation — is jQuery
   and is the revenue path; it is deliberately left running as-is and only its
   markup is restyled. The dependency is kept minimal: plain jquery-3.6.3 plus
   jquery-ui (for .datepicker(), the only plugin the booking JS calls), NOT
   the 437K assets3/js/common_scripts.js theme bundle, which would also boot
   mmenu / flexslider / sticky-sidebar against markup that no longer exists.

   Bootstrap is NOT loaded, so its utilities are gone: any `d-none` scratch
   element in the old markup must become the `hidden` attribute (brand-page.css
   carries `.sb-page [hidden]{display:none}`, which beats a declared display).

   No !important anywhere.
   ========================================================================== */

/* ---- detail hero -------------------------------------------------------- */
.sb-dhero{
  position:relative;background-size:cover;background-position:center;
  background-color:var(--sand-100);
  padding:44px var(--gutter) 40px;
}
.sb-dhero::before{
  content:"";position:absolute;inset:0;
  background:linear-gradient(100deg,
    rgba(255,255,255,.97) 0%,
    rgba(255,255,255,.92) 40%,
    rgba(255,255,255,.6) 70%,
    rgba(255,255,255,.2) 100%);
}
.sb-dhero__inner{position:relative;z-index:2;max-width:820px}
.sb-dhero h1{
  font:var(--text-h1);font-size:clamp(24px,3vw,36px);
  display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap;
}
.sb-dhero__meta{
  display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin-top:12px;
  font:var(--text-subtitle);color:var(--text-body-color);
}
.sb-dhero__meta a{color:var(--text-body-color)}
.sb-dhero__meta a:hover{color:var(--text-link)}
.sb-dhero__meta .sb-icon{width:16px;height:16px}
/* the wishlist heart sits inline with the title, not floated over the photo */
.sb-dhero .sb-wishbtn{
  display:inline-flex;align-items:center;justify-content:center;
  width:38px;height:38px;border-radius:50%;flex:none;
  background:var(--surface-card);box-shadow:var(--shadow-card);
  color:var(--gray-500);cursor:pointer;margin-top:4px;
}
.sb-dhero .sb-wishbtn:hover{color:var(--coral)}
/* wishlist.js injects its own `.sb-wishbtn:after{font-family:ElegantIcons}`
   heart into <head> at init. That icon font is part of the theme bundle brand
   pages no longer load, so the glyph would render as a tofu box next to the
   lucide heart the markup carries. `.liked` is the class wishlist.js paints on.

   The injected style tag lands in <head> at runtime, AFTER this linked
   stylesheet - so on a specificity TIE, cascade order hands it the win over
   a same-specificity override here. `.sb-dhero .sb-wishbtn::after` (0,2,1)
   silences the base glyph, but wishlist.js ships two MORE ::after rules at
   that same (0,2,1) - `:hover:after` and `.liked:after` (the box painted
   once an item is saved) - and the plain override can't out-rank either.
   Overriding both explicitly at (0,3,1) closes the gap without !important.
   Hovering a saved item is covered by the :hover override alone (0,3,1
   beats .liked's 0,2,1 regardless of state), but .liked on its own still
   needs its own line - that's the one that was reappearing on mouseout. */
.sb-dhero .sb-wishbtn::after,
.sb-dhero .sb-wishbtn:hover::after,
.sb-dhero .sb-wishbtn.liked::after{content:none}
.sb-dhero .sb-wishbtn.liked{color:var(--coral)}
.sb-dhero .sb-wishbtn.liked .sb-icon{fill:var(--coral)}

/* ---- sticky section nav ------------------------------------------------- */
.sb-dnav{
  position:sticky;top:62px;z-index:15;        /* clears the sticky brand header */
  background:var(--surface-page);
  border-bottom:1px solid var(--border-subtle);
  padding:0 var(--gutter);
}
.sb-dnav ul{display:flex;gap:26px;overflow-x:auto;scrollbar-width:none}
.sb-dnav ul::-webkit-scrollbar{display:none}
.sb-dnav a{
  display:block;padding:14px 0;white-space:nowrap;
  font:var(--text-subtitle);font-weight:600;color:var(--text-muted);
  border-bottom:2px solid transparent;
}
.sb-dnav a:hover{color:var(--text-heading)}
.sb-dnav a.is-active{color:var(--green-600);border-bottom-color:var(--green-500)}

/* ---- two-column detail layout ------------------------------------------- */
.sb-dlayout{
  display:grid;grid-template-columns:minmax(0,1fr) 360px;
  gap:40px;align-items:start;
  padding:32px var(--gutter) 48px;
  max-width:1320px;margin:0 auto;
}
.sb-dmain{min-width:0}
/* The rail can only stick if its container is taller than it is. `align-items:
   start` sizes the aside to its content, which leaves zero travel and makes
   position:sticky a no-op — so this one item stretches to the row height. */
.sb-dlayout > aside{align-self:stretch;min-width:0}

/* ---- content section ---------------------------------------------------- */
.sb-dsec{padding:28px 0;border-top:1px solid var(--border-subtle)}
.sb-dsec:first-child{border-top:0;padding-top:0}
.sb-dsec > h2{font:var(--text-h2);font-size:22px;margin-bottom:16px}
.sb-dsec > h3{font:var(--text-h3);margin-bottom:12px}

/* Boki waving at the end of the description, standing right on the closing
   rule rather than guessing where the next section's own border-top lands.
   The next section's own border-top is switched off so the two lines don't
   double up.

   .sb-dsec--bokiclose (not #description) does that switch-off: the
   "description" id is reused for other sections on some detail pages
   (carinfo's #description is actually Terms & Conditions, with the real
   description section above it carrying no id at all), so a class the
   description section itself opts into is the one thing consistent across
   tourinfo/holidayinfo/transferinfo/ticketinfo/carinfo.

   .sb-dsec__bokirule reserves real box-model height for Boki (height:
   mascot-height + the gap wanted above him), with the actual 10px hairline
   drawn at its bottom edge via ::before. Earlier this box was just the 10px
   line itself and Boki overlapped upward from it via position:absolute,
   which added no space of his own — every time his height changed, the gap
   to the prose text above silently changed with it (and last had him
   overlapping the description text by ~130px instead of clearing it).
   Reserving his height here means the prose and Boki never fight for the
   same space regardless of how big he is drawn. */
.sb-dsec--bokiclose + .sb-dsec{border-top:0}
.sb-dsec__bokirule{
  position:relative;height:138px;
}
.sb-dsec__bokirule::before{
  content:"";position:absolute;left:0;right:0;bottom:0;height:10px;
  background:var(--border-subtle);
}
.sb-dsec__boki{
  position:absolute;right:0;bottom:0;z-index:1;
  height:188px;width:auto;
  pointer-events:none;
}
@media (max-width:767px){
  .sb-dsec__bokirule{height:200px} /* 150px mascot + 50px clearance */
  .sb-dsec__boki{height:150px}
}

/* ---- collapsible prose ---------------------------------------------------
   Long CMS descriptions are clamped to half a viewport and faded out, with
   prose-clamp.js adding the See More / See Less button underneath.

   The clamp class is applied by JS and never by the markup, so a description
   that already fits gets no button and no fade at all — those pages look
   exactly as they did before. Nothing is removed from the DOM either: the copy
   stays readable to crawlers and to in-page find.

   Only the .sb-prose block is wrapped, never the whole section: transferinfo's
   description section used to also carry the exclusive-charter availability
   calendar, which must not disappear behind a fade (it now has a section of
   its own, but the rule still holds — the wrapper is the prose). */
.sb-clamp{position:relative}
.sb-clamp--on{max-height:50vh;overflow:hidden}
.sb-clamp--on::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:96px;
  background:linear-gradient(to bottom,transparent,var(--surface-page));
  pointer-events:none;
}
/* display:flex, not the .sb-btn default inline-flex — `margin:auto` centres a
   block-level box and is a no-op on an inline-level one. */
.sb-clamp__btn{display:flex;margin:14px auto 0}
/* One chevron that rotates, not two icons swapped: lucide.createIcons() runs
   once at load and replaces the <i> with an inline <svg>, so re-writing
   data-lucide after that point renders nothing. */
.sb-clamp__btn .sb-icon{transition:transform var(--dur-fast) var(--ease-brand)}
.sb-clamp__btn[aria-expanded="true"] .sb-icon{transform:rotate(180deg)}

/* ---- spec list ----------------------------------------------------------- */
.sb-spec{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));
  gap:14px;
}
.sb-spec__item{display:flex;align-items:center;gap:10px}
.sb-spec__disc{
  width:36px;height:36px;flex:none;border-radius:50%;background:var(--green-100);
  display:inline-flex;align-items:center;justify-content:center;
}
.sb-spec__disc .sb-icon{width:18px;height:18px;color:var(--green-600)}
.sb-spec__label{font:var(--text-caption);color:var(--text-muted);display:block}
.sb-spec__value{font:var(--text-subtitle);font-weight:600;color:var(--text-heading)}

/* ---- photo gallery -------------------------------------------------------
   The legacy page filled `#photos` by AJAX and relied on magnificPopup from
   the theme bundle for the lightbox. That bundle is gone, so the lightbox is
   the small vanilla one below — navigating away to the raw .jpg is not an
   acceptable substitute for a gallery. */
.sb-gallery{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));
  gap:10px;
}
.sb-gallery a{display:block;position:relative;cursor:zoom-in;border-radius:var(--radius-md);overflow:hidden}
.sb-gallery img{
  width:100%;height:120px;object-fit:cover;display:block;
  transition:transform var(--dur-med) var(--ease-brand);
}
.sb-gallery a:hover img{transform:scale(1.05)}
.sb-gallery[hidden]{display:none}

/* ---- lightbox ------------------------------------------------------------
   Vanilla replacement for magnificPopup. Opens over the page, arrow/escape
   keys work, and the backdrop closes it. */
.sb-lightbox{position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center}
.sb-lightbox[hidden]{display:none}
.sb-lightbox__backdrop{position:absolute;inset:0;background:rgba(17,17,17,.9)}
.sb-lightbox__fig{
  position:relative;z-index:1;max-width:min(1100px,92vw);max-height:88vh;
  display:flex;flex-direction:column;align-items:center;gap:12px;
}
.sb-lightbox__img{
  max-width:100%;max-height:78vh;width:auto;height:auto;
  border-radius:var(--radius-md);box-shadow:var(--shadow-float);
}
.sb-lightbox__count{font:var(--text-caption);color:rgba(255,255,255,.75)}
.sb-lightbox__btn{
  position:absolute;top:50%;transform:translateY(-50%);z-index:2;
  width:46px;height:46px;border:0;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.92);color:var(--charcoal);
  display:flex;align-items:center;justify-content:center;
  transition:background var(--dur-fast) var(--ease-brand);
}
.sb-lightbox__btn:hover{background:#fff}
.sb-lightbox__btn--prev{left:max(12px,calc(50vw - 620px))}
.sb-lightbox__btn--next{right:max(12px,calc(50vw - 620px))}
.sb-lightbox__close{
  position:absolute;top:18px;right:18px;z-index:2;
  width:40px;height:40px;border:0;border-radius:50%;cursor:pointer;
  background:rgba(255,255,255,.92);color:var(--charcoal);
  display:flex;align-items:center;justify-content:center;
}
.sb-lightbox__close:hover{background:#fff}

/* ---- sidebar rail --------------------------------------------------------
   The whole rail sticks, not just the booking card. Sticking the card alone
   makes it translate down over whatever follows it in the aside (the share
   buttons) once you scroll — sticky keeps its original space in flow but
   paints at the offset position, so it overlaps its own later siblings. */
/* A rail TALLER than the viewport cannot use a positive `top`: the head of the
   card pins and the running total and Book Now button sit permanently below
   the fold, unreachable because the card never moves.

   `bottom` does NOT fix it — sticky-bottom only engages while scrolling UP
   (it stops an element dropping too low); scrolling down it does nothing.

   The working pattern is a NEGATIVE top of (viewportHeight - railHeight - gap):
   scrolling down, the rail pins at that offset, which puts its BOTTOM edge just
   inside the viewport — so the total and the button are what stay on screen.
   brandFooter.jsp computes it, since it depends on both heights, and re-computes
   as the card grows with pax rows. The declaration here is the short-rail
   default and the fallback if the script does not run. */
.sb-drail{position:sticky;top:120px}

/* ---- booking box (sidebar) ----------------------------------------------- */
.sb-bookbox{
  background:var(--surface-card);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-card);border:1px solid var(--border-subtle);
  padding:22px;
}
.sb-bookbox__price{
  display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;
  padding-bottom:16px;margin-bottom:16px;border-bottom:1px solid var(--border-subtle);
}
.sb-bookbox__amount{font:600 26px/1 var(--font-display);color:var(--text-heading)}
.sb-bookbox__cur{font:var(--text-subtitle);font-weight:600;color:var(--text-heading)}
.sb-bookbox__unit{font:var(--text-caption);color:var(--text-muted)}
.sb-bookbox__was{font:var(--text-caption);color:var(--text-muted);text-decoration:line-through}
/* Struck original per-pax rate, sitting immediately before .sb-bookbox__amount.
   Same muted/line-through treatment the rate breakdown rows use, one step
   smaller than the live price so the eye still lands on the discounted one. */
.sb-bookbox__ori{font:600 17px/1 var(--font-display);color:var(--text-muted);text-decoration:line-through}
/* Jump chip to the group price ladder. margin-left:auto parks it at the right
   end of the baseline row; the row wraps, so on a narrow rail it drops onto its
   own line rather than squeezing the price.

   Qualified with .sb-home for the same reason .sb-home a.sb-btn--cta is:
   `.sb-home a{color:var(--text-link)}` in brand-home.css is (0,2,0) and would
   otherwise beat a bare-class colour here regardless of load order. */
.sb-home .sb-bookbox__gd{
  margin-left:auto;display:inline-flex;align-items:center;gap:5px;
  padding:4px 10px;border-radius:var(--radius-pill);
  background:var(--green-100);border:1px solid var(--green-300);
  font:var(--text-small);color:var(--green-800);
  text-decoration:none;cursor:pointer;
  transition:background var(--dur-fast) var(--ease-brand),
             border-color var(--dur-fast) var(--ease-brand);
}
.sb-home .sb-bookbox__gd:hover,
.sb-home .sb-bookbox__gd:focus-visible{background:var(--green-300);border-color:var(--green-500);color:var(--green-800)}
.sb-bookbox__gd .sb-icon{width:14px;height:14px;flex:none}
.sb-bookbox__promo{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  width:100%;margin-top:10px;padding:8px 12px;
  background:var(--coral-100);border-radius:var(--radius-sm);
  font:var(--text-small);color:var(--coral-800);
}
.sb-bookbox__promo strong{font:600 13px/1 var(--font-display)}
.sb-bookbox .sb-field + .sb-field{margin-top:14px}
.sb-bookbox__grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:14px}
/* Two selectors, not one: `.sb-bookbox__grid .sb-field` alone (0,2,0) loses
   to `.sb-bookbox .sb-field + .sb-field` above (0,3,0) for the SECOND field
   in each row (Time/the return Time select), which also matches that
   sibling rule - so it kept its 14px margin-top and sat 14px lower than the
   Date/Return Date field beside it, out of line despite both being the same
   height. Matching that selector's specificity here is what actually beats it. */
.sb-bookbox__grid .sb-field,
.sb-bookbox__grid .sb-field + .sb-field{margin-top:0}
/* running total above the submit button */
.sb-bookbox__total{
  display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  margin-top:18px;padding-top:16px;border-top:1px solid var(--border-subtle);
}
.sb-bookbox__totallabel{font:var(--text-subtitle);font-weight:600;color:var(--text-heading)}
.sb-bookbox__totalamount{font:600 22px/1 var(--font-display);color:var(--green-800)}
.sb-bookbox__submit{width:100%;justify-content:center;margin-top:16px;padding:12px 18px}
.sb-bookbox__note{font:var(--text-caption);color:var(--text-muted);margin-top:10px;text-align:center}
.sb-bookbox__extras{
  display:flex;flex-direction:column;gap:10px;margin-top:16px;
  padding-top:16px;border-top:1px solid var(--border-subtle);
}
.sb-check{display:flex;align-items:flex-start;gap:10px;cursor:pointer}
.sb-check input{margin:2px 0 0;flex:none;accent-color:var(--green-500);width:16px;height:16px}
.sb-check span{font:var(--text-body);color:var(--text-body-color)}
.sb-check b{color:var(--text-heading)}

/* ---- payment options ------------------------------------------------------ */
.sb-payopt{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
.sb-payopt__card{
  background:var(--sand-100);border-radius:var(--radius-md);
  padding:18px 16px;text-align:center;
}
.sb-payopt__card h3{font:var(--text-h4);margin-bottom:6px}
.sb-payopt__card p{font:var(--text-caption);color:var(--text-muted)}
.sb-payopt__card--off h3{text-decoration:line-through;color:var(--text-muted)}
.sb-payopt__disc{
  width:40px;height:40px;margin:0 auto 10px;border-radius:50%;
  background:var(--surface-card);display:flex;align-items:center;justify-content:center;
}
.sb-payopt__disc .sb-icon{width:19px;height:19px;color:var(--green-600)}

/* ---- good to know --------------------------------------------------------
   Replaces the page-local <style> block the legacy page carried inline. */
.sb-gtk{display:flex;flex-direction:column}
.sb-gtk__row{
  display:flex;align-items:flex-start;gap:14px;
  padding:14px 4px;border-bottom:1px solid var(--gray-100);
}
.sb-gtk__row:last-child{border-bottom:0}
.sb-gtk__disc{
  flex:none;width:36px;height:36px;border-radius:50%;background:var(--green-100);
  display:flex;align-items:center;justify-content:center;
}
.sb-gtk__disc .sb-icon{width:18px;height:18px;color:var(--green-600)}
.sb-gtk__text{font:var(--text-body);color:var(--text-body-color)}
.sb-gtk__text b{color:var(--text-heading)}

/* ---- facility grid (hotelinfo) -------------------------------------------
   The facility icons are one 30px-cell sprite; the page picks a cell per
   amenity with an inline background-position, which is why those inline
   styles are left alone and only the box around them is restyled here.
   Replaces `.facilityLI` from plugins/misc/custom.css, which brand pages do
   not load (it also forced a 200px min-height on the whole block). */
.sb-facil{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));
  gap:10px;list-style:none;
}
.sb-facil__item{display:flex;align-items:center;gap:10px}
.sb-facil__ico{
  flex:none;width:30px;height:30px;border-radius:var(--radius-sm);
  background-image:url("https://d2puken2sfiwoz.cloudfront.net/resources/common/img/facilities.jpg");
  background-repeat:no-repeat;
}
.sb-facil__item span + span{font:var(--text-body);color:var(--text-body-color)}

/* ---- trust panel ---------------------------------------------------------
   The transparency block. This is the brand's positioning — "the most
   transparent way to visit Sabah" — and the reason a traveller books here
   rather than direct, so it is deliberately NOT the generic .sb-feat card
   grid it started as: that read as three oversized, low-density tiles.

   It is built to look like a credential, not a marketing panel: a green
   header band, verified assurances with filled check discs, and the licence
   numbers as a compact footer strip. Everything is real, checkable fact —
   the MOTAC licence links out to the government register — which is what
   makes the claim worth making. */
.sb-trustpanel{
  border:2px solid var(--green-500);border-radius:var(--radius-lg);
  overflow:hidden;background:var(--surface-card);
  box-shadow:0 4px 16px rgba(108,194,67,.14),0 12px 32px rgba(34,34,34,.06);
}
/* Solid green header, not a tint: this is the one block on the page that
   should read as a stamp rather than another content card. */
.sb-trustpanel__head{
  display:flex;align-items:center;gap:13px;
  padding:17px 22px;
  background:var(--green-600);color:var(--text-inverse);
}
.sb-trustpanel__head .sb-icon{width:26px;height:26px;flex:none;color:var(--text-inverse)}
.sb-trustpanel__head h2{
  font:var(--text-h3);font-size:19px;color:var(--text-inverse);margin:0;
}
.sb-trustpanel__head p{
  font:var(--text-caption);color:rgba(255,255,255,.82);margin-top:2px;
}

.sb-trustpanel__body{padding:6px 20px 14px}
.sb-trustpanel__row{display:flex;gap:12px;align-items:flex-start;padding:12px 0}
.sb-trustpanel__row + .sb-trustpanel__row{border-top:1px solid var(--gray-100)}
.sb-trustpanel__check{
  width:22px;height:22px;flex:none;border-radius:50%;
  background:var(--green-500);color:var(--text-inverse);
  display:flex;align-items:center;justify-content:center;margin-top:1px;
}
.sb-trustpanel__check .sb-icon{width:13px;height:13px}
.sb-trustpanel__row b{
  display:block;font:var(--text-subtitle);font-weight:600;color:var(--text-heading);
}
.sb-trustpanel__row p{font:var(--text-caption);color:var(--text-muted);margin-top:3px}

/* credentials strip — the 1px grid gap is the divider */
.sb-trustpanel__creds{
  display:grid;grid-template-columns:repeat(3,1fr);gap:1px;
  background:var(--border-subtle);border-top:1px solid var(--border-subtle);
}
.sb-trustpanel__cred{
  background:var(--sand-100);padding:14px 16px;
  display:flex;gap:10px;align-items:flex-start;
}
.sb-trustpanel__cred .sb-icon{width:17px;height:17px;flex:none;color:var(--green-600);margin-top:1px}
.sb-trustpanel__cred b{
  display:block;font:var(--text-small);font-weight:600;color:var(--text-heading);
}
.sb-trustpanel__cred span{display:block;font:var(--text-caption);color:var(--text-muted);margin-top:2px}
.sb-trustpanel__cred a{font:var(--text-caption);color:var(--text-link);text-decoration:underline}

/* Boki standing beside the panel. The PNG already carries its own transparent
   margin on the left and top, so he needs no cropping or negative offsets —
   setting him flush against the card is enough for the two to read as one
   unit. align-items:flex-end puts his feet on the card's bottom edge whatever
   his rendered height turns out to be. He is decorative (alt=""), so hiding
   him on phones costs nothing and gives the card the full width back. */
.sb-trustpanel-wrap{display:flex;align-items:flex-end}
.sb-trustpanel-wrap .sb-trustpanel-wrap__mascot{display:block;flex:none}
/* squared off where Boki docks against the card; the hide breakpoints below
   restore the rounded corner since nothing sits there once he's gone */
.sb-trustpanel-wrap .sb-trustpanel{flex:1;min-width:0;border-bottom-left-radius:0}

/* ---- review summary ------------------------------------------------------
   The individual review cards reuse .sb-review* from brand-home.css; this is
   just the average + per-star distribution block above them. */
.sb-revsum{display:grid;grid-template-columns:180px minmax(0,1fr);gap:28px;align-items:center}
.sb-revsum__avg{text-align:center}
.sb-revsum__score{font:600 44px/1 var(--font-display);color:var(--text-heading)}
.sb-revsum__stars{color:var(--star);font-size:16px;margin-top:6px}
.sb-revsum__count{font:var(--text-caption);color:var(--text-muted);margin-top:6px}
.sb-revbars{display:flex;flex-direction:column;gap:7px}
.sb-revbar{display:flex;align-items:center;gap:10px}
.sb-revbar__label{
  font:var(--text-caption);color:var(--text-muted);
  white-space:nowrap;min-width:34px;text-align:right;
}
/* Both of these are <span>s. An inline box ignores height, so the track would
   collapse and the fill would never paint — they must be blockified. The track
   gets it from being a flex item; the fill needs it declared. */
.sb-revbar__track{
  flex:1;display:block;height:7px;border-radius:var(--radius-pill);
  background:var(--gray-100);overflow:hidden;
}
.sb-revbar__fill{display:block;height:100%;background:var(--star);border-radius:var(--radius-pill)}
.sb-revbar__n{font:var(--text-caption);color:var(--text-muted);min-width:26px}

/* ==========================================================================
   tourinfo — the components carinfo did not need.

   A tour page carries an itinerary (day tabs + a per-day timeline), a group
   price ladder, a per-pax stepper and a mobile price bar. Everything below is
   additive; nothing here is referenced by carinfo.

   Three sets of class names below are NOT brand names and are deliberate:
   `qtyButtons / qtyInput / qtyTotal / qtyInc / qtyDec` and the `rotate-x`
   keyframe are the contract the page's own inline stepper JS builds against
   (it injects .qtyInc/.qtyDec as siblings of `.qtyButtons input` and reads
   `.qtyTotal`). Renaming them would mean rewriting booking JS, which the
   conversion does not touch.
   ========================================================================== */

/* ---- two content columns (highlights beside what's included) ------------- */
.sb-dcols{display:grid;grid-template-columns:1fr 1fr;gap:32px}
.sb-dcols h3{font:var(--text-h3);margin-bottom:12px}

/* ---- video embed ---------------------------------------------------------
   Replaces bootstrap's `.ratio.ratio-16x9`. */
.sb-video{
  position:relative;width:100%;padding-top:56.25%;
  border-radius:var(--radius-lg);overflow:hidden;background:var(--gray-100);
}
.sb-video iframe{position:absolute;inset:0;width:100%;height:100%;border:0}

/* ---- group price ladder --------------------------------------------------
   #grouppriceBody is filled by the page's setGrouppriceBar(); the blocks are
   emitted by that JS, so these are the only selectors that can reach them.
   Each block is also a .sb-tip trigger whose bubble pops up above it, so the
   box can't carry a permanent `overflow:hidden` — pairing overflow-x:hidden
   with overflow-y:visible doesn't help either, since the spec silently
   turns that "visible" into "auto", which still clips a bubble poking out
   above the box's own top edge (auto only adds a scrollbar over it, it
   doesn't stop the clip). Instead the JS adds .sb-pricebar--sliding only for
   the few hundred ms the slide-in transition is actually running, so
   hover/focus tooltips stay unclipped the rest of the time. Blocks flex to
   equal widths — the inline `width:n%` the JS writes is what the legacy bar
   chart used and flex-basis supersedes it. */
.sb-pricebar{border-radius:var(--radius-md)}
.sb-pricebar--sliding{overflow-x:hidden}
.sb-pricebar__inner{display:flex;gap:6px}
.sb-pricebar__block{
  flex:1;min-width:0;padding:14px 6px;text-align:center;
  background:var(--sand-100);border-radius:var(--radius-sm);
  font:var(--text-caption);color:var(--text-muted);
}
.sb-pricebar__block strong{
  display:block;font:var(--text-subtitle);font-weight:600;color:var(--text-heading);
}
.sb-pricebar__value{display:block;margin-top:6px;font:var(--text-body);color:var(--green-800)}

/* ---- itinerary tabs ------------------------------------------------------
   Native buttons + a five-line switcher, not bootstrap's tab plugin. */
.sb-tabs__nav{
  display:flex;gap:6px;flex-wrap:wrap;
  border-bottom:1px solid var(--border-subtle);margin-bottom:20px;
}
.sb-tabs__tab{
  background:none;border:0;cursor:pointer;padding:10px 14px;
  font:var(--text-subtitle);font-weight:600;color:var(--text-muted);
  border-bottom:2px solid transparent;
}
.sb-tabs__tab:hover{color:var(--text-heading)}
.sb-tabs__tab.is-active{color:var(--green-600);border-bottom-color:var(--green-500)}
.sb-tabs__panel{display:none}
.sb-tabs__panel.is-active{display:block}

/* ---- itinerary timeline --------------------------------------------------
   brand-home.css strips list markers globally, so only the connector rule
   below is doing any work. */
.sb-timeline{position:relative;margin:0;padding:0}
.sb-timeline__item{display:flex;gap:14px;padding-bottom:20px;position:relative}
.sb-timeline__item::before{
  content:"";position:absolute;left:15px;top:34px;bottom:0;width:2px;
  background:var(--gray-100);
}
.sb-timeline__item:last-child::before{display:none}
.sb-timeline__num{
  flex:none;width:32px;height:32px;border-radius:50%;position:relative;z-index:1;
  background:var(--green-100);color:var(--green-800);
  display:flex;align-items:center;justify-content:center;
  font:var(--text-small);font-weight:600;
}
.sb-timeline__body{padding-top:3px;min-width:0}
.sb-timeline__time{
  display:block;font:var(--text-caption);font-weight:600;color:var(--coral-600);
}
.sb-timeline__body p{font:var(--text-body);color:var(--text-body-color);margin-top:2px}

/* ---- hotel zone locator --------------------------------------------------
   `.is-hit` is painted on the matching <tr> by the page's #checkhotelBtn
   handler after ./hotels answers. */
.sb-locate{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;margin-top:18px}
.sb-locate .sb-field{flex:1;min-width:220px}
.sb-table tr.is-hit td{background:var(--green-100);color:var(--green-800)}
@keyframes sb-spin{to{transform:rotate(360deg)}}
.sb-spin{animation:sb-spin 1s linear infinite}

/* ---- passenger stepper (booking box) ------------------------------------- */
.sb-qty{margin-top:16px;padding-top:16px;border-top:1px solid var(--border-subtle)}
.sb-qty__head{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  font:var(--text-subtitle);font-weight:600;color:var(--text-heading);margin-bottom:12px;
}
.sb-qty .qtyTotal{
  min-width:28px;height:28px;padding:0 9px;border-radius:var(--radius-pill);
  background:var(--green-100);color:var(--green-800);
  display:inline-flex;align-items:center;justify-content:center;
  font:var(--text-small);font-weight:600;
}
@keyframes rotate-x{from{transform:rotateX(0)}to{transform:rotateX(360deg)}}
.sb-qty .qtyTotal.rotate-x{animation:rotate-x var(--dur-med) var(--ease-brand)}
.sb-qty .qtyButtons{display:flex;align-items:center;gap:8px}
.sb-qty .qtyButtons + .qtyButtons{margin-top:10px}
.sb-qty .qtyButtons label{
  flex:1;min-width:0;display:flex;align-items:center;gap:6px;
  font:var(--text-body);color:var(--text-body-color);
}
.sb-qty .qtyButtons label .sb-icon{width:14px;height:14px;color:var(--coral-600)}
.sb-qty .qtyInput{
  width:48px;flex:none;text-align:center;padding:6px 4px;
  border:1px solid var(--gray-300);border-radius:var(--radius-sm);
  font:var(--text-body);color:var(--text-heading);background:var(--surface-page);
}
.sb-qty .qtyInput:disabled{background:var(--gray-100);color:var(--text-muted)}
/* injected by the stepper JS as bare divs — the glyphs are drawn, not typed */
.sb-qty .qtyInc,.sb-qty .qtyDec{
  width:28px;height:28px;flex:none;border-radius:50%;cursor:pointer;position:relative;
  background:var(--gray-100);color:var(--text-heading);
  transition:background var(--dur-fast) var(--ease-brand);
}
.sb-qty .qtyInc:hover,.sb-qty .qtyDec:hover{background:var(--green-100)}
.sb-qty .qtyInc::before,.sb-qty .qtyDec::before,.sb-qty .qtyInc::after{
  content:"";position:absolute;left:50%;top:50%;
  transform:translate(-50%,-50%);background:currentColor;
}
.sb-qty .qtyInc::before,.sb-qty .qtyDec::before{width:10px;height:2px}
.sb-qty .qtyInc::after{width:2px;height:10px}

/* ---- booking price breakdown --------------------------------------------- */
.sb-prows{margin-top:16px;padding-top:16px;border-top:1px solid var(--border-subtle)}
.sb-prow{
  display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1fr) minmax(48px,auto);
  gap:8px;align-items:baseline;padding:9px 0;
  border-bottom:1px solid var(--border-subtle);
  font:var(--text-body);font-size:14px;color:var(--text-body-color);
}
.sb-prow > :last-child{text-align:right}
.sb-prow b{color:var(--text-heading)}
.sb-prow--head{font:var(--text-caption);font-weight:600;color:var(--text-muted)}
.sb-prow--disc b{color:var(--coral-600)}
.sb-promorow{
  display:flex;align-items:flex-start;justify-content:space-between;gap:12px;
  padding:12px 0;border-bottom:1px solid var(--border-subtle);
}
.sb-promorow__note{font:var(--text-caption);color:var(--coral-600)}

/* ---- tour guide card ------------------------------------------------------
   holidayinfo only. tourGuide.jsp was a whole stand-alone <html> document
   (own <head>, own <style>, font-awesome off a CDN) static-included into the
   middle of the page body; the brand markup is inlined in holidayinfo.jsp and
   styled here instead. */
.sb-guide{background:var(--sand-100);border-radius:var(--radius-lg);padding:24px}
.sb-guide__head{display:flex;align-items:center;gap:18px;margin-bottom:18px}
.sb-guide__photo{
  width:96px;height:96px;flex:none;border-radius:50%;object-fit:cover;
  border:3px solid var(--surface-card);
}
.sb-guide__name{font:var(--text-h3);color:var(--text-heading)}
.sb-guide__meta{
  font:var(--text-caption);color:var(--text-muted);
  display:flex;gap:6px;flex-wrap:wrap;margin-top:4px;
}
.sb-guide__pills{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px}
.sb-guide__pill{
  background:var(--blue-100);color:var(--blue-800);
  padding:4px 12px;border-radius:var(--radius-pill);
  font:var(--text-caption);font-weight:500;
}

/* ---- responsive ------------------------------------------------------------ */
@media (max-width:1100px){
  .sb-dlayout{grid-template-columns:minmax(0,1fr) 320px;gap:28px}
}
/* Boki stands down through one awkward band: the rail is still beside the
   main column here, but the creds strip does not collapse to one column until
   900px, so a 200px mascot squeezes three credentials into ~112px each and
   doubles the strip's height. Below 900 the rail drops away and he fits
   again, so this is a band, not a floor. */
/* Both hide rules are scoped through the wrap on purpose: brand-home.css sets
   `.sb-home img{display:block}` (0,1,1), which outranks a bare class (0,1,0),
   so an unscoped `display:none` here is silently ignored. */
@media (max-width:1100px) and (min-width:901px){
  .sb-trustpanel-wrap .sb-trustpanel-wrap__mascot{display:none}
  .sb-trustpanel-wrap .sb-trustpanel{border-bottom-left-radius:var(--radius-lg)}
}
@media (max-width:900px){
  /* the booking box stops being a rail and becomes the last block on the page */
  .sb-dlayout{grid-template-columns:1fr}
  .sb-drail{position:static}
  .sb-payopt{grid-template-columns:1fr}
  .sb-revsum{grid-template-columns:1fr;gap:18px}
  .sb-lightbox__btn--prev{left:12px}
  .sb-lightbox__btn--next{right:12px}
  .sb-trustpanel__creds{grid-template-columns:1fr}
  .sb-dcols{grid-template-columns:1fr;gap:24px}
  /* the price ladder gets its own horizontal scroll rather than squeezing
     five tiers into a phone width */
  .sb-pricebar{overflow-x:auto}
  .sb-pricebar__block{flex:0 0 108px}
}
@media (max-width:767px){
  .sb-dhero{padding:30px var(--gutter) 28px}
  .sb-dhero::before{
    background:linear-gradient(180deg,
      rgba(255,255,255,.95) 0%,
      rgba(255,255,255,.85) 45%,
      rgba(255,255,255,.45) 100%);
  }
  /* .sb-dnav becomes the only sticky bar this narrow: .sb-header is scoped
     off sticky (it and .sb-dnav both declare top:0, and two independent
     sticky siblings don't auto-stack, so without this they overlap — found
     2026-08-16 measuring the section-nav smooth-scroll offset, see the
     .sb-dnav script in tourinfo.jsp and siblings). Scoped to body.sb-detail
     so the homepage/listing/content pages, which also use .sb-header, keep
     their sticky header at every width.

     relative, NOT static: .sb-burger's mobile dropdown (.sb-nav, brand-home.css)
     is `position:absolute;top:100%` and relies on .sb-header being a
     positioned element to anchor against. static drops that, so .sb-nav's
     containing block became the document root and `top:100%` landed near the
     bottom of the whole page instead of just under the header — found
     2026-08-16 opening the hamburger menu on tourinfo.jsp. relative has the
     same "not sticky/fixed" effect the stickyBottom() helper checks for
     (so the smooth-scroll offset calc is untouched) while still being a
     positioned element, fixing both bugs at once. */
  body.sb-detail .sb-header{position:relative}
  .sb-dnav{top:0}
  .sb-dlayout{padding:24px var(--gutter) 36px}
  .sb-bookbox__grid{grid-template-columns:1fr}
  .sb-gallery img{height:100px}
  .sb-guide__head{flex-direction:column;align-items:flex-start;gap:12px}
  .sb-trustpanel-wrap .sb-trustpanel-wrap__mascot{display:none}
  .sb-trustpanel-wrap .sb-trustpanel{border-bottom-left-radius:var(--radius-lg)}
}
