/* =========================================================
   Tour detail page (chi-tiet-tour.html)
   ========================================================= */
/* Shared hero gallery for every catalog type — one asymmetric layout (big
   main photo left, 1 wide side photo top-right, 2 half-width side photos
   bottom-right) for every type, with no prev/next arrows.
   `grid-template-areas` (rather than the old .tour-gallery-main's
   grid-column/row spans) is what lets a plain flat list of up to 4 <a> tiles
   (single-knt-shell.php) reproduce hotel's former 2-level nested grid in ONE
   level — needed so mobile's kntInitSwipeCarousel() (main.js), which treats
   every child of the viewport as a slide, still works on the exact same
   markup. */
.tour-gallery {
	display: grid;
	/* Proportions: main photo 60% width/100% height; the big top-right side
	   photo 40% width/50% height; the 2 small bottom-right side photos 20%
	   width each/50% height each. */
	grid-template-columns: 3fr 1fr 1fr;
	grid-template-rows: 1fr 1fr;
	grid-template-areas:
		"main top top"
		"main bl br";
	gap: 8px;
	height: 420px;
	border-radius: var(--radius-lg);
	overflow: hidden;
}
@media (max-width: 900px) {
	.tour-gallery {
		height: 320px;
	}
}
@media (max-width: 600px) {
	/* .tour-gallery is shared by every catalog type, hotel included (see
	   .tour-gallery's own desktop-rule comment further up this file) —
	   starts as up to 4 flat photos (main + 3 side) in the desktop
	   grid-template-areas layout, turned into a swipeable card row here
	   rather than collapsing to one static photo, since there is no longer
	   a single obvious "main" shot to fall back to on its own. Custom
	   drag/momentum carousel (main.js's kntInitSwipeCarousel()) turns
	   these 5 cells into draggable slides — .tour-gallery is now the
	   fixed-size VIEWPORT (overflow:hidden, never scrolls natively), and
	   main.js inserts a `.knt-swipe-track` child that actually holds the
	   slides and moves via `transform: translateX()`. Replaced native
	   `scroll-snap-type: mandatory` entirely: that lets the browser's own
	   momentum fling run first and only THEN evaluate/animate to the
	   nearest snap point, reading as a distinct 2-phase "swipe, then a
	   small correction jump" rather than one continuous motion. See
	   kntInitSwipeCarousel()'s own comment in main.js for the full
	   drag/velocity/easing design. */
	.tour-gallery {
		/* The desktop base rule is display:grid — reset to a plain block
		   here since .tour-gallery is now just a static overflow:hidden
		   viewport, no layout algorithm of its own; .knt-swipe-track (JS)
		   is its one real child and owns the actual flex/gap row. */
		display: block;
		grid-template-columns: none;
		grid-template-rows: none;
		grid-template-areas: none;
		/* .tour-gallery lives inside .container (20px side padding) in the
		   markup — full-bleed breakout so it spans the true viewport width
		   instead of sitting inset an extra 20px on top of the peek padding
		   below, which would make the photo itself render visibly smaller
		   and show blank page background in the gutter instead of a
		   peeking neighbor photo. Standard "escape a centered .container
		   without restructuring the
		   HTML" trick — safe at this breakpoint specifically since mobile
		   browsers don't reserve a scrollbar gutter the way desktop does. */
		width: 100vw;
		margin-left: calc(50% - 50vw);
		margin-right: calc(50% - 50vw);
		/* THE container itself must have no visible frame of its own — the
		   desktop grid rule above sets border-radius+overflow:hidden on
		   .tour-gallery (the whole strip, not each photo), which would
		   otherwise still be in effect here. That reads as one fixed
		   rounded "window" with photos dragged around inside it, rather
		   than each photo sliding past independently. Each card below
		   already has its OWN border-radius/overflow:hidden — the outer strip needs
		   none, so cards read as independent floating tiles sliding past
		   each other, not content masked inside one static frame. */
		border-radius: 0;
		/* overflow:hidden (not overflow-x:auto) — this is now a plain
		   viewport, JS drives all movement via the inner track's transform.
		   touch-action:pan-y lets a mostly-vertical touch still scroll the
		   PAGE normally; only a horizontal drag is captured by our own
		   pointer handlers (kntInitSwipeCarousel(), main.js). */
		overflow: hidden;
		touch-action: pan-y;
		height: 230px;
		position: relative;
	}
}

.tour-gallery-main {
	grid-area: main;
}

.tour-gallery-thumb-1 {
	grid-area: top;
}

.tour-gallery-thumb-2 {
	grid-area: bl;
}

.tour-gallery-thumb-3 {
	grid-area: br;
}

/* Mobile-only "extra" photo tiles (tiles 5-10, single-knt-shell.php) — real
   photos beyond desktop's fixed 4-slot grid, letting mobile's swipeable
   carousel show a minimum of 9 clear photos before a "Xem tất cả ảnh"
   overlay tile (client, 2026-09-16). Not a named grid-area, so it stays
   hidden outside the ≤600px block below or it would auto-place into an
   implicit grid row/column and break the desktop layout. */
.tour-gallery-extra {
	display: none;
}

/* Fewer than 4 total gallery photos leaves a named grid area (top-right/
   bottom-right/bottom-left) with no tile assigned to it — `grid-template-
   areas` still reserves that track's space regardless, so it just renders
   as a blank gap instead of "gracefully" using less of the grid (e.g. a
   ticket with just main+2 side photos would leave an empty bottom-right
   cell). :has() detects which trailing tile is actually the LAST one
   present and stretches IT to fill the remaining empty area instead. */
.tour-gallery:has(.tour-gallery-thumb-1):not(:has(.tour-gallery-thumb-2)) .tour-gallery-thumb-1 {
	grid-column: 2 / 4;
	grid-row: 1 / 3;
}

.tour-gallery:has(.tour-gallery-thumb-2):not(:has(.tour-gallery-thumb-3)) .tour-gallery-thumb-2 {
	grid-column: 2 / 4;
	grid-row: 2 / 3;
}

/* Only 1 total photo (no side tiles at all) — let the main photo take the
   full width too, instead of leaving the entire right side blank. */
.tour-gallery:not(:has(.tour-gallery-thumb-1)) .tour-gallery-main {
	grid-column: 1 / 4;
	grid-row: 1 / 3;
}

.tour-gallery-main,
.tour-gallery-thumb {
	display: block;
	position: relative;
	/* A grid item's default min-width/min-height:auto lets its own <img>'s
	   intrinsic size (whatever the source photo's real dimensions are, e.g.
	   900x500) refuse to shrink below that — silently overriding the
	   60/40/20/20 fr-ratio column split and the 50/50 row split above,
	   which is what makes a tile not fit its slot. object-fit:cover on the
	   <img> itself (below) is what then crops the now-correctly-shrunk box's
	   content, not the source image's own aspect ratio. */
	min-width: 0;
	min-height: 0;
}
@media (max-width: 600px) {
	/* .knt-swipe-track (main.js) is the actual flex row that gets dragged —
	   gap here, not on .tour-gallery, since that's now just the fixed-size
	   viewport wrapping it. Each slide's own width is set inline by JS
	   (kntInitSwipeCarousel()'s measure()), not via a CSS calc() peek
	   formula — the track's own width is auto/content-based (sum of every
	   slide), so a percentage-based child width has nothing stable to
	   resolve against. border-radius/overflow:hidden stay on the slides
	   themselves (unaffected by this restructuring). */
	.tour-gallery-main,
	.tour-gallery-thumb,
	.tour-gallery-extra {
		border-radius: var(--radius);
		overflow: hidden;
	}
	/* .tour-gallery-extra is display:none by default (desktop) — this is
	   what actually makes it a real slide in the mobile swipe carousel
	   (kntInitSwipeCarousel() moves every DOM child into the track
	   regardless of its CSS display, but it still needs to render). */
	.tour-gallery-extra {
		display: block;
		position: relative;
		min-width: 0;
		min-height: 0;
	}
	/* Desktop's own "more" overlay (tile 3 of 4, single-knt-shell.php) must
	   stay hidden on mobile — mobile shows 5 more real, unobstructed photos
	   past this same tile instead (see .tour-gallery-extra above and tile 9's
	   own overlay below), so this tile needs to read as a plain clear photo
	   there, not the "view all" trigger. Compound selector (both classes,
	   not just the modifier alone) is required here: .tour-gallery-more-label
	   itself (unconditional `display: flex`, further down this file) has the
	   SAME specificity as a single-class `.tour-gallery-more-label--desktop-
	   only` selector and comes AFTER it in source order, so a single-class
	   override here would silently lose to that later rule even inside this
	   ≤600px block — confirmed live 2026-09-16 (deployed, but tile 3 still
	   showed the overlay on mobile). Two classes on one selector outranks it
	   regardless of source order. */
	.tour-gallery-more-label.tour-gallery-more-label--desktop-only {
		display: none;
	}
}

.tour-gallery-main .ph-thumb,
.tour-gallery-thumb .ph-thumb,
.tour-gallery-extra .ph-thumb {
	height: 100%;
}

/* Real product photos render as a plain <img> (wp_get_attachment_image),
   not the .ph-thumb placeholder markup above — without this, the <img>
   only takes its own intrinsic aspect-ratio height instead of filling the
   grid cell, leaving a visible gap in the row below/above it. */
.tour-gallery-main img,
.tour-gallery-thumb img,
.tour-gallery-extra img,
.tour-gallery-main video,
.tour-gallery-thumb video,
.tour-gallery-extra video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Play-icon overlay for a video gallery tile ("_knt_gallery" now accepts
   video, see inc/fields.php's knt_store_render_gallery_media()) — CSS-drawn
   triangle in a circle, same "no emoji icons" reasoning as every other icon
   in this theme. Shared by every hero-gallery layout (.tour-gallery AND
   .hotel-gallery below), not scoped to one. */
.knt-gallery-video-icon {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 48px;
	height: 48px;
	border-radius: 999px;
	background: rgba(9, 20, 23, 0.55);
	pointer-events: none;
}

.knt-gallery-video-icon::before {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: 0;
	height: 0;
	border-top: 9px solid transparent;
	border-bottom: 9px solid transparent;
	border-left: 14px solid #fff;
	transform: translate(-35%, -50%);
}

.tour-gallery-more-label {
	position: absolute;
	inset: 0;
	background: rgba(9, 20, 23, 0.55);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 700;
	font-size: 0.9375rem;
}

/* text-wrap:balance evens out line lengths across a wrapped heading
   instead of greedily filling each line, avoiding a lone orphaned word on
   its own line at smaller font sizes — no fallback needed beyond the
   default wrap behavior older engines already give it. */
.tour-title {
	font-size: 1.875rem;
	margin: 10px 0 0;
	text-wrap: balance;
}
@media (max-width: 782px) {
	.tour-title {
		font-size: 1.375rem;
	}
}
@media (max-width: 782px) {
	.tour-title {
		margin-top: 4px;
	}
}

/* Spacing each block apart with its own margin (12px/8px/0px — collapsing
   further where adjacent margins overlap) lands on 3 visibly different
   gaps stacked on top of each other. A single flex-column gap on their
   shared wrapper (only child of
   .tour-header-top — no new class needed) makes every gap between them
   the same, regardless of how many of the optional rows are present. */
.tour-header-top > div {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.tour-rating-row {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 10px;
	margin: 0;
}

/* .star-rating's base .icon-star (main.css ~line 545) is only 13px —
   fine on a dense archive card, but reads as visibly smaller/misaligned
   next to the 28px-tall review-score-badge and the 16px location pin icon
   sharing this same line. Scoped bump, same idea as .hotel-list-stars-row's
   own 15px override elsewhere. */
.tour-rating-row .star-rating {
	display: inline-flex;
	align-items: center;
}

.tour-rating-row .star-rating .icon-star {
	width: 18px;
	height: 18px;
}

/* .review-score-row's own margin: 2px 0 8px (main.css ~line 4057) is tuned
   for its ORIGINAL stacked-card usage (spacing before/after other block
   content) — that asymmetric top/bottom margin shifts its flexbox
   align-items:center position ~3px off from its siblings here (measured via
   getBoundingClientRect()), even though all three look like they "should"
   already be centered. Zero it out in this inline-row context only. */
.tour-rating-row .review-score-row {
	margin: 0;
}

/* .tour-meta-row-item inherits .tour-meta-row's 0.9375rem (main.css ~2780)
   — visibly bigger than review-score-label/-count (0.8125rem/0.75rem)
   right next to it on this merged line, reading as inconsistent text
   sizing. Match the review text's size here; .tour-meta-row itself (tour/
   vé/xe's own separate, non-merged row) is untouched. */
.tour-rating-row .tour-meta-row-item {
	font-size: 0.8125rem;
}

/* "Rất tốt · 677 đánh giá" on ONE line (matches the mockup) instead of
   .review-score-info's default stacked-two-line look — scoped to this
   header row only, so the archive/listing cards (hotel-list-item etc.)
   using the exact same knt_store_render_review_badge() markup keep their
   own stacked layout, which fits their narrower card width better. */
.tour-rating-row .review-score-info {
	flex-direction: row;
	align-items: center;
	gap: 4px;
}

.tour-rating-row .review-score-count::before {
	content: "·";
	margin-right: 4px;
	color: var(--color-text-light);
}

.tour-meta-row {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 20px;
	color: var(--color-text-light);
	font-size: 0.9375rem;
}

.tour-meta-row-item {
	display: flex;
	align-items: center;
	gap: 8px;
}

.tour-meta-row-item .icon {
	width: 16px;
	height: 16px;
	flex-shrink: 0;
}

/* Anchor sub-nav — sticky under the site header while scrolling the detail
   sections. top uses the live-measured --header-height (see main.js) rather
   than a hardcoded px guess — this is the bar that sits directly under the
   header, so any drift here is the most visible ("hở" gap) of all of them. */
.tour-subnav {
	display: flex;
	gap: 4px;
	overflow-x: auto;
	border-bottom: 1px solid var(--color-border);
	margin: 12px 0 32px;
	position: sticky;
	top: var(--header-height, 117px);
	background: var(--color-bg);
	z-index: 5;
	/* Hide the horizontal scrollbar track on mobile swipe — same technique
	   as the other horizontal-scroll rows in this file (e.g. #chon-phong
	   .ticket-package-list). */
	scrollbar-width: none;
}

.tour-subnav::-webkit-scrollbar {
	display: none;
}

.tour-subnav a {
	padding: 14px 18px;
	font-weight: 600;
	font-size: 0.9375rem;
	color: var(--color-text-light);
	border-bottom: 2px solid transparent;
	white-space: nowrap;
}

.tour-subnav a:hover {
	color: var(--color-primary);
}

/* Scroll-spy active state (main.js toggles this as the visitor scrolls
   past each .tour-section). Reuses the `border-bottom: 2px solid
   transparent` already reserved on every link above, just swapping the
   color in in rather than adding new box-model properties (so nothing
   shifts size when a tab becomes active). */
.tour-subnav a.is-active {
	color: var(--color-primary);
	border-bottom-color: var(--color-primary);
}

/* =========================================================
   Hotel detail page (OTA-style rebuild) — the badge/caption/counter
   overlay classes below started life as khách sạn-only (is_hotel branch,
   template-parts/single-knt-shell.php) but are now used inside every
   catalog type's shared .tour-gallery-main/-thumb tiles (see .tour-gallery's
   own comment further up this file) — nested directly inside the tile <a>
   now (not a separate wrapper <div>),
   which is also why the old prev/next arrow <button>s are gone: a <button>
   can't nest inside an <a> (invalid HTML), and main.css/single-knt-
   shell.php no longer need a non-<a> wrapper now nothing else has to sit
   beside the <a> as a sibling.
   ========================================================= */
.hotel-gallery-badge {
	position: absolute;
	top: 12px;
	left: 12px;
	z-index: 2;
}

/* Real WordPress attachment "Caption" (Media Library's own field, no new
   admin UI) shown as a small overlay label — e.g. "Hồ bơi ngoài trời" —
   only when the admin actually filled it in for that photo. Bottom-fade
   gradient so it stays readable over any photo without needing a solid bar. */
.hotel-gallery-caption {
	position: absolute;
	inset: auto 0 0 0;
	z-index: 1;
	padding: 16px 10px 8px;
	background: linear-gradient(to top, rgba(9, 20, 23, 0.65), transparent);
	color: #fff;
	font-size: 0.75rem;
	font-weight: 600;
}

.hotel-gallery-counter {
	position: absolute;
	bottom: 12px;
	left: 12px;
	z-index: 2;
	padding: 4px 10px;
	border-radius: 999px;
	background: rgba(9, 20, 23, 0.6);
	color: #fff;
	font-size: 0.75rem;
	font-weight: 600;
}

/* "Hạng Phòng" (#chon-phong) — a horizontal-scroll row of vertical photo
   cards, while the underlying .ticket-package-radio/.ticket-tiers-qty
   booking-price engine (main.js) is completely untouched: only CSS layout
   changes here, same DOM/data-* attributes as before.
   "Gói Vé"/"Gói Tour" (#goi-ve) get the same horizontal-scroll-card
   treatment below but with their OWN card-head rules, not a shared
   selector with #chon-phong — #goi-ve's cards have no photo
   thumb, so the radio can stay a normal in-flow element instead of
   #chon-phong's absolute-positioned overlay-on-photo trick. */
#chon-phong .ticket-package-list {
	flex-direction: row;
	overflow-x: auto;
	scrollbar-width: none;
}

#chon-phong .ticket-package-list::-webkit-scrollbar {
	display: none;
}

#chon-phong .ticket-package-card {
	flex: 0 0 240px;
	/* Same "equal-height card, actions pinned to the bottom regardless of
	   description length" fix as #goi-ve's package cards below — a flex
	   column plus .ticket-package-card-actions's own margin-top:auto
	   (shared rule, further down this file). */
	display: flex;
	flex-direction: column;
}

#chon-phong .ticket-package-card-head {
	position: relative;
	flex-direction: column;
	align-items: stretch;
	padding: 0 0 14px;
	gap: 10px;
}

#chon-phong .ticket-package-card-actions {
	margin-top: auto;
}

#chon-phong .ticket-package-head-main,
#chon-phong .ticket-package-price {
	padding: 0 14px;
}

#chon-phong .ticket-package-price {
	margin-left: 0;
	padding-left: 14px;
	padding-top: 2px;
	font-size: 1rem;
}

/* The radio sits over the photo's top-right corner (same "overlay control on
   a photo" convention as .wishlist-btn elsewhere) instead of inline before
   the name — a card the user picks by clicking anywhere in it (native
   <label> behavior), not a list row read left-to-right. Restyled as a
   circular checkmark badge — same technique as #goi-ve's photo-package cards below
   (.ticket-package-card:has(.ticket-package-thumb) .ticket-package-radio),
   just with no :has() gate since a room card's photo slot (real photo or
   the .ph-thumb placeholder) is always present, unlike a "Gói Vé" package's
   optional one. */
#chon-phong .ticket-package-radio {
	appearance: none;
	position: absolute;
	top: 10px;
	right: 10px;
	z-index: 2;
	width: 24px;
	height: 24px;
	margin: 0;
	border: 2px solid #fff;
	border-radius: 50%;
	background-color: rgba(9, 20, 23, 0.25);
	box-shadow: 0 1px 4px rgba(9, 20, 23, 0.2);
	cursor: pointer;
}

#chon-phong .ticket-package-radio:checked {
	background-color: var(--color-primary);
	border-color: var(--color-primary);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5l5.5 5.5L20 7'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: center;
	background-size: 13px 13px;
}

/* "Gói Vé"/"Gói Tour" (#goi-ve) — same horizontal-scroll-row idea as
   #chon-phong above, own rules since there's no photo thumb here: the
   radio stays a normal in-flow element (first row), name (second row),
   then the per-"Loại khách" price breakdown (.ticket-package-price-tiers,
   below the head), so the collapsed card shows real prices up front, not
   only after opening "Xem chi tiết"/"Chọn". */
#goi-ve .ticket-package-list {
	flex-direction: row;
	overflow-x: auto;
	scrollbar-width: none;
}

#goi-ve .ticket-package-list::-webkit-scrollbar {
	display: none;
}

/* Center the single card when a product has only ONE package — with
   flex-direction:row + overflow-x:auto above, the default
   justify-content:flex-start otherwise left-aligns a lone fixed-width
   card, leaving a large empty gap on its right. :has()-scoped so 2+
   packages keep flex-start,
   which the horizontal swipe needs to stay scrollable from the first
   card — centering a multi-card row would clip the start card off-screen
   with no way to scroll back to it in some browsers. */
#goi-ve .ticket-package-list:has(.ticket-package-card:only-child) {
	justify-content: center;
}

#goi-ve .ticket-package-card {
	flex: 0 0 260px;
	/* .ticket-package-card is a plain block, not a flex column — with cards
	   already stretched to equal height by the row's default align-items,
	   a shorter package description just leaves "Xem chi tiết" sitting
	   wherever that shorter content happens to end, well above the card's
	   actual (equal-height) bottom edge — the card LOOKS shorter even
	   though its border is the same height as its neighbor. Making the
	   card itself a flex column
	   and pinning .ticket-package-card-actions to the bottom below fixes
	   this regardless of description length. */
	display: flex;
	flex-direction: column;
	/* Positioning context for the corner-checkmark radio below — only
	   matters for cards that actually have a photo (:has(), already used
	   elsewhere in this file for the same shared .ticket-package-* classes,
	   e.g. .ticket-package-card:has(.ticket-package-radio:checked)). */
	position: relative;
}

/* Radio restyled as a corner checkmark badge opposite the "Nhãn" badge —
   only when a package actually has a photo (no photo = no image corner to
   anchor it against, so it stays the plain inline radio right before the
   name instead, unchanged). */
#goi-ve .ticket-package-card:has(.ticket-package-thumb) .ticket-package-radio {
	appearance: none;
	position: absolute;
	top: 10px;
	right: 10px;
	z-index: 2;
	width: 24px;
	height: 24px;
	margin: 0;
	border: 2px solid #fff;
	border-radius: 50%;
	background-color: rgba(9, 20, 23, 0.25);
	box-shadow: 0 1px 4px rgba(9, 20, 23, 0.2);
	cursor: pointer;
}

#goi-ve .ticket-package-card:has(.ticket-package-thumb) .ticket-package-radio:checked {
	background-color: var(--color-primary);
	border-color: var(--color-primary);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5l5.5 5.5L20 7'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: center;
	background-size: 13px 13px;
}

#goi-ve .ticket-package-card-actions {
	margin-top: auto;
}

#goi-ve .ticket-package-card-head {
	flex-direction: column;
	align-items: stretch;
	padding: 14px;
	gap: 8px;
}

#goi-ve .ticket-package-name {
	font-size: 1rem;
	text-align: center;
	/* Reserve a fixed 2-line box so the price/desc/bullets below start at
	   the same Y across every card in the row regardless of title length —
	   a 1-line title would otherwise leave its price sitting noticeably
	   higher than a neighboring 2-line title's price. A title longer than
	   2 lines gets clamped with an ellipsis instead of pushing a 3rd line,
	   which would re-break this same alignment the other way.
	   -webkit-line-clamp is prefixed but supported
	   in every current major browser engine, not just WebKit. */
	line-height: 1.3;
	min-height: 2.6em;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

#goi-ve .ticket-package-price-tiers {
	margin-top: 2px;
}

/* Optional per-package "Ảnh gói"/"Nhãn". Full-bleed at the top of the card —
   its flex-item cross-axis stretch (default on #goi-ve .ticket-package-card,
   a flex column) already makes it fill the card's width without an
   explicit width:100% here. Genuinely optional: a package with no photo
   entered renders exactly as before (no empty placeholder box forced in). */
.ticket-package-thumb {
	position: relative;
	aspect-ratio: 4 / 3;
	overflow: hidden;
}

/* The img sits inside a plain <span class="ticket-package-thumb-inner">
   (client, 2026-09-16: a package's photo is a static preview, deliberately
   NOT the <a class="hotel-gallery-photo"> lightbox link .room-card-thumb a
   below uses) — this fills that span to the thumb's own size. */
.ticket-package-thumb-inner {
	display: block;
	width: 100%;
	height: 100%;
	position: relative;
}

.ticket-package-thumb img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.ticket-package-badge {
	position: absolute;
	top: 10px;
	left: 10px;
	padding: 4px 10px;
	border-radius: 999px;
	background: var(--color-primary);
	color: #fff;
	font-size: 0.6875rem;
	font-weight: 800;
}

/* Fallback for a badge entered with no photo — can't overlay a photo that
   doesn't exist, so render as a plain inline pill above the name instead
   of silently dropping it (see the PHP-side elseif branch). */
.ticket-package-badge-inline {
	position: static;
	display: inline-block;
	margin: 14px 14px 0;
}

/* Mobile-only "coverflow" swipe — center card full-size, side cards
   visibly smaller. Desktop's plain multi-card horizontal-scroll row above
   is explicitly untouched, mobile-only by design. Custom drag/momentum
   carousel (main.js's kntInitSwipeCarousel()) replaces native
   `scroll-snap-type: mandatory` entirely, same as .tour-gallery's own
   version further down — native scroll-snap has a two-phase "fling, then
   a separate correction snap" feel rather than one continuous motion.
   `.ticket-package-list` is now the fixed-size VIEWPORT (overflow:hidden,
   never scrolls natively); main.js inserts a `.knt-swipe-track` child that
   actually holds the cards and moves via `transform: translateX()`. */
@media (max-width: 600px) {
	#goi-ve .ticket-package-list,
	#chon-phong .ticket-package-list {
		/* The shared base rule (top of this file) is display:flex — reset
		   to a plain block since .ticket-package-list is now just a static
		   overflow:hidden viewport, no layout algorithm of its own;
		   .knt-swipe-track (JS) is its one real child and owns the actual
		   flex/gap row. Also keeps the desktop-only :has(:only-child)
		   centering rule further up this file inert here (justify-content
		   only does anything on a flex/grid container) instead of fighting
		   main.js's own transform-based centering math. */
		display: block;
		overflow: hidden;
		touch-action: pan-y;
		position: relative;
	}

	/* .knt-swipe-track (main.js) is the flex row that gets dragged — gap
	   here, not on .ticket-package-list, since that's now just the
	   fixed-size viewport wrapping it. Each card's own width is set
	   inline by JS (kntInitSwipeCarousel()'s measure()), not via a CSS
	   calc() peek formula — the track's own width is auto/content-based
	   (sum of every card), so a percentage-based child width has nothing
	   stable to resolve against. #chon-phong shares this same reason
	   (client, 2026-09-16 — same treatment as #goi-ve, right above). */
	#goi-ve .ticket-package-card,
	#chon-phong .ticket-package-card {
		flex-shrink: 0;
	}
}

.room-card-thumb {
	position: relative;
	width: 100%;
	height: 140px;
	overflow: hidden;
	flex-shrink: 0;
}

/* When a room has real photo(s), the img sits inside an <a class="hotel-
   gallery-photo"> (opens the shared lightbox) instead of directly inside
   .room-card-thumb — this fills that link to the same size. */
.room-card-thumb a {
	display: block;
	width: 100%;
	height: 100%;
	position: relative;
}

.room-card-thumb img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.room-card-photo-count {
	position: absolute;
	bottom: 6px;
	right: 6px;
	padding: 2px 7px;
	border-radius: 999px;
	background: rgba(9, 20, 23, 0.7);
	color: #fff;
	font-size: 0.6875rem;
	font-weight: 700;
}

/* All of a room's photos beyond the first (shown as the card thumb) — each
   tagged .hotel-gallery-photo so they open in the same shared lightbox as
   the main hotel gallery (main.js), not a separate per-room viewer. */
.room-card-photo-strip {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-top: 10px;
}

.room-card-photo-strip a {
	display: block;
	width: 52px;
	height: 52px;
	border-radius: 6px;
	overflow: hidden;
	flex-shrink: 0;
}

.room-card-photo-strip img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

/* .ph-thumb (dev/no-photo placeholder) isn't a replaced element — override
   its own aspect-ratio:4/3 (main.css's shared .ph-thumb rule) instead of
   object-fit, which does nothing on a <span>. */
.room-card-thumb .ph-thumb {
	width: 100%;
	height: 100%;
	aspect-ratio: auto;
}

/* "Khoảng cách tới điểm mốc" — optional, real-data-only list next to the
   Vị Trí map (see _knt_nearby_places, inc/fields.php). Absent entirely from
   the DOM when empty, no CSS empty-state needed. */
.hotel-nearby-list {
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: 10px;
	margin-top: 16px;
	padding: 16px;
	background: var(--color-bg-alt);
	border-radius: var(--radius);
}

.hotel-nearby-item {
	display: flex;
	align-items: center;
	gap: 10px;
	font-size: 0.9375rem;
	color: var(--color-text);
}

.hotel-nearby-item .icon {
	width: 16px;
	height: 16px;
	color: var(--color-primary);
	flex-shrink: 0;
}

.tour-layout {
	display: grid;
	grid-template-columns: 1fr 360px;
	gap: 48px;
	align-items: start;
	padding-bottom: 60px;
}
@media (max-width: 900px) {
	.tour-layout {
		grid-template-columns: 1fr;
	}
}

/* Grid items refuse to shrink below their content's min-content width by
   default — without this the main column can force itself wider than its
   1fr share and squeeze/overflow .tour-sidebar off the edge. */
.tour-main {
	min-width: 0;
}

.tour-sidebar {
	position: sticky;
	/* Below both the sticky header AND the sticky .tour-subnav — both heights
	   are live-measured (main.js), not guessed, same reasoning as .tour-subnav
	   above. */
	top: calc(var(--header-height, 117px) + var(--tour-subnav-height, 78px));
}
@media (max-width: 900px) {
	.tour-sidebar {
		position: static;
	}
}

/* Dedicated checkout page (template-dat-tour.php) — same
   grid-with-sticky-sidebar idea as .tour-layout above, just its own class
   since the two columns mean something different here (contact form +
   order-review card, not content + booking widget). .checkout-summary also
   carries the plain ".booking-card" class in markup for its box chrome
   (border/radius/shadow, no positioning of its own — safe to combine) AND
   so main.js's existing ".booking-card"-scoped stepper/total wiring
   activates with no JS changes; same min-width:0 reasoning as .tour-main
   above. */
.checkout-layout {
	display: grid;
	grid-template-columns: 1fr 380px;
	gap: 48px;
	align-items: start;
	padding: 40px 0 60px;
}
@media (max-width: 900px) {
	.checkout-layout {
		grid-template-columns: 1fr;
		gap: 24px;
	}
}

.checkout-main {
	min-width: 0;
}

.checkout-section-title {
	margin-bottom: 20px;
}

.checkout-summary {
	position: sticky;
	top: calc(var(--header-height, 117px) + 20px);
}
@media (max-width: 900px) {
	.checkout-summary {
		position: static;
		/* Order-review card reads before the contact form on mobile — the
		   visitor should see what they're booking before filling anything
		   in, same reasoning a real checkout puts the order summary first
		   on a narrow screen even though desktop shows it as a side card. */
		order: -1;
	}
}

.checkout-summary-item {
	display: flex;
	align-items: center;
	gap: 12px;
	margin-bottom: 18px;
	padding-bottom: 18px;
	border-bottom: 1px solid var(--color-border);
}

.checkout-summary-photo {
	flex-shrink: 0;
	width: 64px;
	height: 64px;
	border-radius: var(--radius);
	overflow: hidden;
}

.checkout-summary-photo img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.checkout-summary-item-body {
	display: flex;
	flex-direction: column;
	gap: 4px;
	min-width: 0;
}

.checkout-summary-item-title {
	font-weight: 700;
	color: var(--color-text);
}

.checkout-selected-package {
	font-size: 0.875rem;
	color: var(--color-text-light);
}

.checkout-summary-price {
	font-size: 1.375rem;
	font-weight: 800;
	color: var(--color-text);
	margin-bottom: 4px;
}

.checkout-summary-call {
	width: 100%;
	justify-content: center;
	margin-top: 18px;
}

.checkout-submit-btn {
	width: 100%;
	justify-content: center;
}

.tour-section {
	scroll-margin-top: calc(var(--header-height, 117px) + var(--tour-subnav-height, 78px));
	padding-bottom: 40px;
	margin-bottom: 40px;
	border-bottom: 1px solid var(--color-border);
}

.tour-section:last-child {
	border-bottom: none;
	margin-bottom: 0;
}

.tour-section-title {
	font-size: 1.375rem;
	margin-bottom: 1em;
}

.tour-highlights {
	/* Single column, stacked — a 2-col grid makes long items read
	   left-to-right-then-down in an unnatural order and wastes space when
	   items wrap to very different heights. */
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.tour-highlights li,
.tour-includes-list li,
.tour-excludes-list li {
	display: flex;
	align-items: flex-start;
	gap: 10px;
}

.tour-highlights .icon {
	width: 18px;
	height: 18px;
	margin-top: 2px;
	flex-shrink: 0;
	color: var(--color-primary);
}

/* "Xem tất cả N điểm nổi bật" trigger — opens the .knt-modal below with the
   full list (a real popup, not an inline-expanding list). Plain <button>,
   so the site-wide button:active tap-feedback rule (top of this file)
   already covers it. */
.tour-highlights-more-btn {
	display: inline-block;
	margin-top: 10px;
	border: none;
	background: none;
	padding: 0;
	font-family: inherit;
	cursor: pointer;
	font-weight: 600;
	font-size: 0.8125rem;
	color: var(--color-primary-dark);
}

.tour-highlights-more-btn:hover {
	text-decoration: underline;
}

.tour-includes-grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 16px 32px;
}
@media (max-width: 900px) {
	.tour-includes-grid {
		grid-template-columns: 1fr;
	}
}

/* Tour-only "Menu Ăn Trưa", sits right below the Bao Gồm/Không Bao Gồm grid
   inside the same section. 2-column grid on desktop to save vertical space
   since a lunch menu can easily run to 8-9 dishes — collapses to 1 column
   at the same 900px breakpoint .tour-includes-grid already uses, so a long
   dish name has room to wrap instead of being squeezed into a narrow
   phone-width column. Uses .icon-bowl (a dish icon, not .icon-check) since
   these are menu items, not "included" checklist items. */
.tour-lunch-menu {
	margin-top: 24px;
	padding-top: 24px;
	border-top: 1px solid var(--color-border);
}

.tour-lunch-menu-list {
	list-style: none;
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 10px 24px;
}
@media (max-width: 900px) {
	.tour-lunch-menu-list {
		grid-template-columns: 1fr;
	}
}

.tour-lunch-menu-list li {
	display: flex;
	align-items: flex-start;
	gap: 8px;
}

.tour-lunch-menu-list .icon {
	width: 18px;
	height: 18px;
	margin-top: 1px;
	flex-shrink: 0;
}

/* Fixed allergy/vegetarian disclaimer under the dish list. */
.tour-lunch-menu-note {
	margin: 14px 0 0;
	/* Matches the base body text size for readability (still set apart via
	   italic + muted color, not size). */
	font-size: 1rem;
	font-style: italic;
	color: var(--color-text-light);
}

.tour-includes-list,
.tour-excludes-list {
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.tour-includes-list .icon,
.tour-excludes-list .icon {
	width: 18px;
	height: 18px;
	margin-top: 2px;
	flex-shrink: 0;
}

.tour-includes-list .icon {
	color: #059669;
}

.tour-excludes-list .icon {
	color: var(--color-danger);
}

/* "Trải Nghiệm" — alternating photo/text story rows for a ticket/tour whose
   appeal is a sequence of moments rather than logistics (a show, a scenic
   route) — distinct from "Hành Trình"/.policy-step, which is the practical
   step-by-step (ga nào, mấy phút, làm gì). Alternation uses an explicit
   .experience-row-reverse modifier, not :nth-child(even), since adding or
   removing a row would silently break alternation keyed off sibling order.
   Static-dev prototype only so far (chi-tiet-ve.html) — not yet ported to
   a real WP template. */
.experience-story {
	display: flex;
	flex-direction: column;
	gap: 32px;
}

.experience-row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 24px;
	align-items: center;
}
@media (max-width: 900px) {
	.experience-row {
		grid-template-columns: 1fr;
	}
}

.experience-row-media {
	border-radius: var(--radius);
	overflow: hidden;
}

.experience-row-media .ph-thumb {
	align-items: center;
	justify-content: center;
	text-align: center;
}

.experience-row-reverse .experience-row-media {
	order: 2;
}
@media (max-width: 900px) {
	.experience-row-reverse .experience-row-media {
		order: 0;
	}
}

.experience-row-num {
	font-size: 0.75rem;
	font-weight: 800;
	letter-spacing: 0.08em;
	color: var(--color-primary);
	text-transform: uppercase;
}

.experience-row-body h3 {
	margin: 6px 0 8px;
	font-size: 1.125rem;
}

.experience-row-body p {
	margin: 0;
	color: var(--color-text-light);
}

/* "Hướng Dẫn Sử Dụng Vé" — a compact 4-step "how it works" grid for e-ticket
   items, kept deliberately TRUTHFUL to this site's real booking flow (a lead
   form + phone/Zalo follow-up, inc/bookings.php — no automated e-voucher/QR
   delivery system exists), not the generic "receive your e-ticket by email"
   copy a booking-site template would default to. */
.ticket-steps {
	list-style: none;
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 16px;
}
@media (max-width: 900px) {
	.ticket-steps {
		grid-template-columns: 1fr 1fr;
	}
}
@media (max-width: 480px) {
	.ticket-steps {
		grid-template-columns: 1fr;
	}
}

.ticket-step {
	padding: 18px 16px;
	border: 1px solid var(--color-border);
	border-radius: var(--radius);
	background: var(--color-bg-alt);
	text-align: center;
}

.ticket-step-num {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	margin-bottom: 10px;
	border-radius: 50%;
	background: var(--color-primary);
	color: #fff;
	font-weight: 800;
	font-size: 0.875rem;
}

.ticket-step-title {
	display: block;
	font-weight: 700;
	font-size: 0.9375rem;
	margin-bottom: 4px;
}

.ticket-step-desc {
	display: block;
	font-size: 0.8125rem;
	color: var(--color-text-light);
}

/* Khách Sạn "Tiện Nghi" — icon grid (first 8 amenities), any rest tucked
   behind a "Xem tất cả" <details> (.tour-amenities-more) rather than one
   long single-column list. Deliberately its own class, not a reuse of
   .tour-includes-list — that class is also 1-column-per-half inside
   .tour-includes-grid for tour/ticket "Bao Gồm"/"Không Bao Gồm", and a grid
   here would have collided with that layout on those other pages. */
.tour-amenities-grid {
	list-style: none;
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
	gap: 12px 20px;
}

.tour-amenities-grid li {
	display: flex;
	align-items: flex-start;
	gap: 10px;
}

.tour-amenities-grid .icon {
	width: 18px;
	height: 18px;
	margin-top: 2px;
	flex-shrink: 0;
	color: var(--color-primary);
}

.tour-amenities-more {
	margin-top: 14px;
}

.tour-amenities-more summary {
	cursor: pointer;
	font-weight: 600;
	font-size: 0.8125rem;
	color: var(--color-primary-dark);
}

.tour-amenities-more .tour-amenities-grid {
	margin-top: 14px;
}

/* Row of outline amenity pills under a listing's intro paragraph
   (chi-tiet-khach-san.html "Tổng Quan") — a quick-scan summary, not a
   replacement for the full .tour-amenities-grid further down the page. */
.tour-amenity-pills {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin: 16px 0 4px;
}

.tour-amenity-pill {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	padding: 8px 14px;
	border: 1px solid var(--color-border);
	border-radius: 999px;
	font-size: 0.8125rem;
	font-weight: 600;
	color: var(--color-text);
}

.tour-amenity-pill .icon {
	width: 15px;
	height: 15px;
	flex-shrink: 0;
	color: var(--color-primary);
}

/* Khách Sạn "Loại Phòng" — photo cards (chi-tiet-khach-san.html), not the
   .policy-step numbered-block reuse. This is a static-prototype-only
   presentation choice; the live WP template (single-knt_hotel.php) still
   renders room types as a
   selectable booking picker with a quantity stepper, since it's actually
   wired to a running total there — this static page has no such booking
   logic. Same "Liên hệ" price convention as everywhere else on the site
   (no invented VNĐ figures per content-guidelines.md), so the per-room
   price is text, not a number, unlike the reference's specific amounts. */
.room-type-grid {
	list-style: none;
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 20px;
}
@media (max-width: 900px) {
	.room-type-grid {
		grid-template-columns: 1fr;
	}
}

.room-type-card {
	display: flex;
	flex-direction: column;
	border: 1px solid var(--color-border);
	border-radius: var(--radius);
	overflow: hidden;
}

.room-type-card-thumb {
	height: 160px;
}

.room-type-card-thumb .ph-thumb {
	height: 100%;
}

.room-type-card-body {
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 14px 16px 16px;
	flex: 1;
}

.room-type-card-name {
	font-weight: 800;
	font-size: 1rem;
	color: var(--color-text);
}

.room-type-card-meta {
	font-size: 0.8125rem;
	color: var(--color-text-light);
}

.room-type-card-price-row {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 10px;
	margin-top: auto;
	padding-top: 12px;
}

.room-type-card-price-row .btn {
	width: 100%;
	justify-content: center;
}

.room-type-card-price {
	font-weight: 800;
	font-size: 0.9375rem;
	color: var(--color-primary);
}

.room-type-card-price-label {
	display: block;
	font-size: 0.6875rem;
	font-weight: 600;
	color: var(--color-text-light);
}

.tour-map-placeholder {
	aspect-ratio: 16 / 6;
	border-radius: var(--radius);
	background: var(--color-bg-alt);
	border: 1px dashed var(--color-border);
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 6px;
	color: var(--color-text-light);
	text-align: center;
	padding: 20px;
}

/* Real Google Maps embed (see knt_store_get_map_embed_url() in
   inc/fields.php) — same 16/6 footprint as .tour-map-placeholder above so
   swapping between "no map set yet" and "map set" never shifts the rest of
   the page's layout. */
.tour-map-embed {
	aspect-ratio: 16 / 6;
	border-radius: var(--radius);
	overflow: hidden;
}

.tour-map-embed iframe {
	width: 100%;
	height: 100%;
	border: 0;
	display: block;
}

.tour-map-placeholder .icon {
	width: 28px;
	height: 28px;
	color: var(--color-text-light);
}

.tour-faq-item {
	border-bottom: 1px solid var(--color-border);
	padding: 16px 0;
}

.tour-faq-item summary {
	cursor: pointer;
	font-weight: 700;
	list-style: none;
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 16px;
}

.tour-faq-item summary::-webkit-details-marker {
	display: none;
}

.tour-faq-item summary::after {
	content: "+";
	font-size: 1.25rem;
	font-weight: 700;
	color: var(--color-primary);
	flex-shrink: 0;
}

.tour-faq-item[open] summary::after {
	content: "\2212";
}

.tour-faq-item p {
	margin-top: 10px;
	color: var(--color-text-light);
}

/* Lịch Trình/Hành Trình step — a real <details>/<summary>, but a vertical
   timeline (dot + connecting line, ::before/::after on .itinerary-step
   itself — no extra markup needed) instead of .tour-faq-item's flat
   "+"/"−" accordion look, so the two don't read as too similar to each
   other. Chevron rotates open/closed
   instead of swapping "+"/"−" text, another visual break from the FAQ
   pattern. Not exclusive (no shared `name`, unlike the FAQ accordion) —
   opening several steps at once to compare is useful for an itinerary, not
   clutter the way it would be for FAQ answers. */
.itinerary-step {
	position: relative;
	padding: 0 0 24px 28px;
}

.itinerary-step::before {
	/* Connecting line down to the next step's dot — omitted on the last
	   step below since there's nothing to connect to. */
	content: "";
	position: absolute;
	top: 14px;
	bottom: 0;
	left: 5px;
	width: 2px;
	background: var(--color-border);
}

.itinerary-step:last-of-type::before {
	/* :last-of-type (matches by tag — <details> — not :last-child) so this
	   still correctly targets the final step even if a future non-details
	   element (e.g. a trailing note) gets added after the steps in this
	   section. */
	display: none;
}

.itinerary-step::after {
	/* The dot itself — filled once expanded, hollow while collapsed, so an
	   already-viewed/open step reads differently from the rest at a glance. */
	content: "";
	position: absolute;
	top: 4px;
	left: 0;
	width: 12px;
	height: 12px;
	border-radius: 50%;
	background: var(--color-bg);
	border: 3px solid var(--color-primary);
	box-sizing: border-box;
}

.itinerary-step[open]::after {
	background: var(--color-primary);
}

.itinerary-step-heading {
	margin: 0;
	list-style: none;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	cursor: pointer;
	font-size: 1.0625rem;
	font-weight: 700;
	color: var(--color-text);
}

.itinerary-step-heading::-webkit-details-marker {
	display: none;
}

.itinerary-step-heading::after {
	/* CSS-drawn chevron (border trick, not a glyph) — rotates in place
	   instead of swapping "+"/"−" text. */
	content: "";
	flex-shrink: 0;
	width: 7px;
	height: 7px;
	border-right: 2px solid var(--color-text-light);
	border-bottom: 2px solid var(--color-text-light);
	transform: rotate(45deg);
	transition: transform 0.2s ease;
}

.itinerary-step[open] .itinerary-step-heading::after {
	transform: rotate(-135deg);
}

.itinerary-step-body {
	margin: 8px 0 0;
	color: var(--color-text-light);
}

.itinerary-step-photos {
	/* Full-width stacked photos, one per row — not a grid of small
	   thumbnails. */
	display: flex;
	flex-direction: column;
	gap: 16px;
	margin-top: 12px;
}

.itinerary-step-photos a {
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	border-radius: var(--radius);
	overflow: hidden;
}

.itinerary-step-photos img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.booking-card {
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	padding: 24px;
	box-shadow: var(--shadow-md);
}

.booking-card-price-label {
	font-size: 0.8125rem;
	font-weight: 600;
	color: var(--color-text-light);
	display: block;
	margin-bottom: 2px;
}

.booking-card-price {
	font-size: 1.5rem;
	font-weight: 800;
	color: var(--color-text);
	margin-bottom: 18px;
}

.booking-card-price del {
	color: var(--color-text-light);
	font-weight: 400;
	font-size: 1rem;
	margin-right: 8px;
}

.booking-card-price ins {
	text-decoration: none;
}

/* "Gói Vé & Giá" package cards — CONTENT column, #goi-ve section (see
   knt_store_get_active_price_packages() in inc/fields.php). One card per
   package; "Xem chi tiết"/"Chọn" open a bottom-sheet <dialog>
   (.knt-package-sheet) with the per-"Loại khách" qty steppers + running
   total — that per-tier pricing still only lives in the sheet, but the
   card itself now also shows a "Từ [rẻ nhất]" summary price
   (.ticket-package-price, same class Khách Sạn's room-type cards already
   used) so a visitor isn't blind to price before opening anything.
   Horizontal-scroll-card layout for #goi-ve is scoped further
   down (near #chon-phong's own version) — this base rule (plain vertical
   list) is the fallback for any future context reusing .ticket-package-*
   without that scope. */
.ticket-package-list {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.ticket-package-card {
	border: 1px solid var(--color-border);
	border-radius: var(--radius);
	overflow: hidden;
}

.ticket-package-card-head {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 12px 14px;
	cursor: pointer;
	transition: background 0.2s ease;
}

/* :has() covers modern browsers; main.js also toggles .is-selected as a
   fallback for anything older that doesn't support it. */
.ticket-package-card:has(.ticket-package-radio:checked) .ticket-package-card-head,
.ticket-package-card-head.is-selected {
	background: var(--color-bg-alt);
}

/* #goi-ve: a colored border around the WHOLE card instead of the grey
   fill above, which reads as "darkened" once the card also has a photo on
   it — the fill still shows through here since it targets
   .ticket-package-card-head specifically, only the text/price area, not
   the photo, which makes the darkening look like a stray patch rather
   than a deliberate selected state. Higher specificity than the generic
   rule above (id + descendant vs. plain class), so this wins here without
   touching every OTHER .ticket-package-card use sitewide. #chon-phong's
   room-type cards get the same treatment — they carry a photo (or the
   .ph-thumb placeholder) too, so the same "darkened patch" issue applies
   equally. */
#goi-ve .ticket-package-card:has(.ticket-package-radio:checked) .ticket-package-card-head,
#goi-ve .ticket-package-card-head.is-selected,
#chon-phong .ticket-package-card:has(.ticket-package-radio:checked) .ticket-package-card-head,
#chon-phong .ticket-package-card-head.is-selected {
	background: none;
}

#goi-ve .ticket-package-card:has(.ticket-package-radio:checked),
#chon-phong .ticket-package-card:has(.ticket-package-radio:checked) {
	/* A box-shadow ring gets clipped at the corners by this same card's own
	   overflow:hidden (needed to keep the photo's corners rounded) — a real
	   border isn't affected by overflow the way a box-shadow ring is, since
	   it's part of the box model itself rather than painted outside the
	   border edge. border-box sizing (global reset, top of this
	   file) means this 1px growth is absorbed by shrinking the content area
	   a hair, not by growing the card's own outer width/flex-basis. */
	border-color: var(--color-primary);
	border-width: 2px;
}

.ticket-package-radio {
	flex-shrink: 0;
	accent-color: var(--color-primary);
}

.ticket-package-name {
	font-weight: 700;
	font-size: 0.9375rem;
}

/* Khách Sạn's room-type cards reuse this exact card head — .ticket-package-name
   + a secondary "2 khách · 1 giường đôi" line, wrapped together so the price
   on the right (below) can sit at the row's vertical center regardless of
   whether the meta line is present ("Gói Vé" cards never render either of
   these two, so this is a no-op for them). */
.ticket-package-head-main {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.ticket-package-meta {
	font-size: 0.8125rem;
	color: var(--color-text-light);
}

/* Room price, right-aligned via margin-left:auto rather than justify-content
   on the shared .ticket-package-card-head — that flex row only has 2
   children on "Gói Vé" cards (radio + name), and space-between would blow
   them apart; margin-left:auto on this THIRD, hotel-only child pushes just
   itself to the edge without touching the ticket layout at all. */
.ticket-package-price {
	margin-left: auto;
	padding-left: 10px;
	font-weight: 700;
	font-size: 0.9375rem;
	/* Matches the site's standard price color everywhere else
	   (.product-card-price on catalog cards). */
	color: var(--color-accent);
	white-space: nowrap;
}

/* Per-"Loại khách" price breakdown shown directly on the collapsed #goi-ve
   package card, one row per tier instead of a single "Từ [rẻ nhất]"
   summary — same knt_store_format_tier_price_html() formatting (0 price →
   "Miễn phí", compare_price → strikethrough) already used inside the
   "Lựa chọn" sheet's qty rows, styled the same way as that sheet's own
   .ticket-tier-qty-row/-name/-price so a tier reads identically in both
   places. */
.ticket-package-price-tiers {
	list-style: none;
	margin: 0;
	padding: 0 14px;
	display: flex;
	flex-direction: column;
	gap: 4px;
}

.ticket-package-price-tiers li {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	font-size: 0.8125rem;
}

.ticket-package-price-tier-name {
	color: var(--color-text-light);
}

.ticket-package-price-tier-value {
	font-weight: 700;
	color: var(--color-accent);
	white-space: nowrap;
}

.ticket-package-price-tier-value del {
	color: var(--color-text-light);
	font-weight: 400;
}

.ticket-package-price-tier-value ins {
	text-decoration: none;
}

/* Sidebar quantity-per-"Loại khách" (booking card) — only the panel whose
   data-package-index matches the currently-checked .ticket-package-radio
   (in the CONTENT column above) is visible; main.js toggles [hidden] and
   recomputes .ticket-tiers-total on every +/- and package change. */
.ticket-tiers-qty {
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: 12px;
	border: 1px solid var(--color-border);
	border-radius: var(--radius);
	margin-bottom: 14px;
}

/* [hidden] and .ticket-tiers-qty are equal specificity (0,1,0) — without
   this, source order alone decides, and this rule's own `display: flex`
   above (author stylesheet, applied after the UA stylesheet's plain
   `[hidden]{display:none}`) would silently win, showing every package's
   panel at once instead of just the selected one. main.js only ever
   toggles the `hidden` IDL property, never inline `style`, so this is the
   one place that has to win. */
.ticket-tiers-qty[hidden] {
	display: none;
}

.ticket-tier-qty-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
}

.ticket-tier-qty-info {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.ticket-tier-qty-name {
	font-weight: 600;
	font-size: 0.875rem;
}

.ticket-tier-qty-price {
	font-size: 0.8125rem;
	color: var(--color-text-light);
}

.ticket-tier-qty-price del {
	color: var(--color-text-light);
	font-weight: 400;
}

.ticket-tier-qty-price ins {
	text-decoration: none;
	font-weight: 600;
	color: var(--color-text);
}

.ticket-tiers-total {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 10px 12px;
	background: var(--color-bg-alt);
	border-radius: var(--radius);
	font-weight: 700;
	margin-bottom: 18px;
}

.ticket-tiers-total strong {
	font-size: 1.125rem;
	color: var(--color-primary-dark);
}

.booking-card-field {
	margin-bottom: 14px;
}

/* Small cross-sell block under the booking card's trust row — e.g. a ticket
   page linking to Xe Đưa Đón. Static-dev prototype only so far. */
.booking-card-upsell {
	margin-top: 16px;
	padding: 14px;
	border-radius: var(--radius);
	background: var(--color-bg-alt);
	border: 1px solid var(--color-border);
}

.booking-card-upsell-title {
	display: block;
	font-size: 0.875rem;
	font-weight: 700;
	margin-bottom: 4px;
}

.booking-card-upsell-text {
	margin: 0 0 10px;
	font-size: 0.8125rem;
	color: var(--color-text-light);
}

/* Shared "Đặt Ngay" lead-capture modal (#knt-booking-modal,
   template-parts/single-knt-shell.php) — every per-product/-package
   "Đặt Ngay"/"Chọn" CTA on a detail page opens this ONE modal
   (.js-knt-booking-trigger, main.js) instead of being its own dead
   href="#". Reuses .booking-card-field for its inputs (same label/input
   look, just extended below to also cover <textarea>) and .knt-modal for
   the popup chrome itself — only the success/error banner and the
   selection-preview line are new. */
.booking-card-field textarea {
	width: 100%;
	padding: 10px 14px;
	border: 1px solid var(--color-border);
	border-radius: var(--radius);
	font: inherit;
	color: var(--color-text);
	resize: vertical;
}

.knt-booking-form .btn {
	width: 100%;
	justify-content: center;
}

.knt-booking-summary-preview {
	background: var(--color-bg-alt);
	border-radius: var(--radius);
	padding: 10px 14px;
	margin-bottom: 16px;
	font-size: 0.8125rem;
	color: var(--color-text-light);
}

.knt-booking-success,
.knt-booking-error {
	border-radius: var(--radius);
	padding: 12px 14px;
	font-size: 0.9375rem;
	font-weight: 600;
}

.knt-booking-success {
	background: rgba(14, 124, 134, 0.1);
	color: var(--color-primary-dark);
}

.knt-booking-error {
	background: rgba(217, 45, 32, 0.1);
	color: var(--color-danger);
	margin-bottom: 16px;
}

/* =========================================================
   Mobile-only pinned "Đặt Ngay" CTA bar (product detail pages —
   template-parts/single-knt-shell.php) — the generic .mobile-bottom-nav
   above is disabled, so there's nothing else pinned to the bottom of the
   screen to conflict with. Hidden on desktop; the sidebar/package sheet
   already handle booking there.
   ========================================================= */
.mobile-sticky-cta {
	display: none;
}

@media (max-width: 900px) {
	.mobile-sticky-cta {
		display: block;
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 50;
		padding: 12px 20px;
		background: var(--color-bg);
		border-top: 1px solid var(--color-border);
		box-shadow: 0 -4px 16px rgba(9, 20, 23, 0.08);
	}

	.mobile-sticky-cta-btn {
		width: 100%;
		justify-content: center;
	}

	/* Hidden once the "related products" carousel (.knt-related-section)
	   scrolls into view — main.js toggles this via IntersectionObserver so
	   the CTA doesn't sit pinned over unrelated products further down the
	   page. */
	.mobile-sticky-cta.is-hidden {
		display: none;
	}

	/* Reserves room so the fixed bar never covers the footer's own last
	   line of content — :has() rather than a PHP-added body class, since
	   this component is entirely self-contained in one template file.
	   Must exclude the .is-hidden state too, or the footer keeps this
	   70px gap reserved even after the bar itself is scrolled away past
	   the "related products" section. */
	body:has(.mobile-sticky-cta:not(.is-hidden)) {
		padding-bottom: 70px;
	}

	/* .back-to-top's default 24px bottom offset sits right on top of the
	   pinned CTA bar (~70px tall: 24px own padding + ~47px button) once
	   that bar is visible — same :not(.is-hidden) scoping as the footer
	   padding reservation above, so the offset clears the moment the bar
	   itself hides. */
	body:has(.mobile-sticky-cta:not(.is-hidden)) .back-to-top {
		bottom: calc(94px + env(safe-area-inset-bottom, 0px));
	}

	/* Same reasoning as .back-to-top directly above — the chat FAB sits at
	   the mirrored bottom-left spot and needs the identical clearance. */
	body:has(.mobile-sticky-cta:not(.is-hidden)) .knt-chat-fab {
		bottom: calc(94px + env(safe-area-inset-bottom, 0px));
	}

	.knt-chat-fab {
		left: 16px;
		bottom: calc(16px + env(safe-area-inset-bottom, 0px));
	}

	.knt-chat-toast {
		bottom: calc(16px + env(safe-area-inset-bottom, 0px));
	}
}
