/* ==========================================================================
   BOOKALLO — bkx_routes, THE SHELL EVERYWHERE ELSE
   The homepage's visual system, reaching /search, /hotels, hotel detail,
   /plan, the destination pages and every editorial page.

   WHY THIS IS AN ADAPTER AND THE HOMEPAGE WAS A REWRITE
   -----------------------------------------------------
   The homepage's markup is written in HTML files, so it could be replaced.
   These pages are not. A flight result card, a hotel card, a hotel detail
   panel, a planner day and a comparison row are all emitted at RUNTIME by
   janet_live_search.js (178 KB), janet_hotel_render.js (96 KB),
   janet_hotel_card.css's markup contract and janet_plan.js (68 KB). Those four
   files own the money path: the price semantics, the seller attribution, the
   affiliate deep links, the availability truth labels and the booking handoff.

   Rewriting their output to change how it LOOKS would mean re-deriving all of
   that, and the first field missed is a revenue path that fails silently. So
   the renderers keep emitting exactly what they emit, and this sheet dresses
   it. The visual system is the same one; only the mechanism differs, and it
   differs because the risk profile does.

   IT LOADS LAST, ON PURPOSE. These pages carry janet_live_search.css,
   janet_hotels.css, janet_plan.css, janet_hero_v4.css and janet_hotel_card.css
   — the previous system, still holding the layout its renderers assume. This
   sheet is injected after all of them by scripts/build_dist.js and re-points
   what a reader SEES without moving what a renderer READS.

   01 the shell everywhere   04 hotel detail
   02 search + results       05 the planner
   03 hotels                 06 destination + editorial
   ========================================================================== */


/* == 01 · THE SHELL, ON EVERY PAGE ======================================== */

/*
 * The ground first. These pages set their own body background in three
 * different sheets, and a page that is midnight at the top and #0E1116 in the
 * middle is two products with one header.
 */
html, body { background: var(--x-void, #04070C); }
body { color: var(--x-ink, #F5F1E8); }

/* --- the header ---------------------------------------------------------
 * `.hd` is the shared site header on every non-homepage route. It becomes the
 * shell's nav in place: the brand, the links and the actions keep their
 * classes, their hrefs and their order.
 */
.hd {
  position: sticky;
  inset-block-start: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  gap: clamp(14px, 2.4vw, 36px);
  padding: clamp(10px, 1.2vw, 16px) clamp(20px, 4vw, 64px);
  background: rgba(4, 7, 12, .88);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
  backdrop-filter: blur(22px) saturate(150%);
  border-block-end: 1px solid var(--x-line, rgba(240, 236, 226, .10));
  box-shadow: 0 18px 40px -34px rgba(0, 0, 0, .9);
}
.hd .brand, .jp-bar__logo, .jp-bar__home {
  display: flex;
  align-items: center;
  gap: .6rem;
  min-block-size: 44px;
  text-decoration: none;
  color: var(--x-ink, #F5F1E8);
}
.hd .brand-mark {
  display: grid;
  place-items: center;
  inline-size: 34px; block-size: 34px;
  border-radius: 10px;
  background: linear-gradient(150deg, var(--x-gold, #E3BE79), var(--x-gold-deep, #C9A24A));
  color: var(--on-sky, #10131A);
  font-weight: 700;
  box-shadow: 0 18px 50px -20px rgba(227, 190, 121, .4);
}
.hd .brand-name { letter-spacing: .18em; font-weight: 600; }

.hd-nav { display: flex; align-items: center; gap: clamp(12px, 1.8vw, 28px); margin-inline-end: auto; }
.hd-nav a {
  position: relative;
  min-block-size: 44px;
  display: inline-flex;
  align-items: center;
  font-size: .93rem;
  color: var(--x-ink-2, #C3C6CC);
  text-decoration: none;
  transition: color .25s var(--x-ease, cubic-bezier(.22,1,.36,1));
}
.hd-nav a::after {
  content: "";
  position: absolute;
  inset-block-end: 8px;
  inset-inline-start: 0;
  inline-size: 0;
  block-size: 1px;
  background: var(--x-gold, #E3BE79);
  transition: inline-size .3s var(--x-ease, cubic-bezier(.22,1,.36,1));
}
.hd-nav a:hover { color: var(--x-ink, #F5F1E8); }
.hd-nav a:hover::after { inline-size: 100%; }

.hd-actions { display: flex; align-items: center; gap: 10px; }
.hd-el, .hd-lang {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  min-block-size: 44px;
  padding-inline: .8rem;
  border-radius: 999px;
  border: 1px solid var(--x-line-2, rgba(240, 236, 226, .18));
  background: rgba(255, 255, 255, .03);
  color: var(--x-ink-2, #C3C6CC);
  text-decoration: none;
  font-size: .86rem;
  transition: border-color .25s var(--x-ease), color .25s var(--x-ease);
}
.hd-el:hover, .hd-lang:hover { border-color: var(--x-gold-edge, rgba(227,190,121,.42)); color: var(--x-gold, #E3BE79); }
.hd-el__av { inline-size: 26px; block-size: 26px; border-radius: 50%; }

/* --- section rhythm and headings ---------------------------------------- */
.jn-sec { padding-block: clamp(48px, 6.5vh, 96px); position: relative; }
.jn-sec + .jn-sec { border-block-start: 1px solid var(--x-line, rgba(240,236,226,.10)); }
.jn-sec h2, .ls-head h1, .jh-hero h1, .jp-intro h1, .dhero h1,
.jn-head h2, .jhd__name {
  font-family: "Frank Ruhl Libre", Georgia, serif;
  font-weight: 500;
  letter-spacing: -.018em;
  line-height: 1.06;
}

/* --- the footer ---------------------------------------------------------- */
.ft {
  border-block-start: 1px solid var(--x-line, rgba(240,236,226,.10));
  background: var(--x-deep, #070B13);
  padding-block: clamp(32px, 5vh, 60px);
}
.ft a { color: var(--x-ink-2, #C3C6CC); }
.ft a:hover { color: var(--x-gold, #E3BE79); }

/* --- one material for every raised surface on the site -------------------
 * Every card, panel and popover on these routes, in one declaration. The
 * previous system gave each renderer its own ground and its own hairline, so
 * the product read as five components that happened to share a header.
 */
.ls-card, .jhc, .jp-card, .jdh-trip, .ls-prefs, .jh-facets, .ls-cal,
.jhd__book, .jp-cmp, .jp-day, .ls-dh, .jn-guide, .jn-why__i {
  border-radius: var(--x-r, 16px);
  border: 1px solid var(--x-line, rgba(240, 236, 226, .10));
  background: linear-gradient(150deg, rgba(16, 22, 32, .72), rgba(7, 11, 18, .84));
  box-shadow: 0 18px 44px -20px rgba(0, 0, 0, .86);
  color: var(--x-ink, #F5F1E8);
}

/* Every primary action, in one champagne. */
/* `.dhero__cta` is the WRAPPER around a destination hero’s two buttons, not a
   button — painting it champagne produced a full-width gold bar with the real
   button sitting inside it. The buttons are `.btn` and `.ghost`. */
.ls-btn, .ls-card__cta, .jhc__cta, .jh-go, .jp-btn, .jn-cta,
.dhero__cta > .btn, .ghero__cta > .btn,
.jhd__book .ls-btn, #jh-go, #ls-go {
  border-radius: 999px;
  background: linear-gradient(160deg, var(--x-gold, #E3BE79), var(--x-gold-deep, #C9A24A));
  color: var(--on-sky, #10131A);
  border: 0;
  font-weight: 700;
  min-block-size: 46px;
  box-shadow: 0 18px 50px -20px rgba(227, 190, 121, .4);
  transition: transform .2s var(--x-ease), box-shadow .2s var(--x-ease);
}
.ls-btn:hover, .ls-card__cta:hover, .jh-go:hover, .jp-btn:hover,
.jn-cta:hover, .dhero__cta > .btn:hover { transform: translateY(-2px); }
.ls-btn:active, .jh-go:active, .jp-btn:active { transform: translateY(1px) scale(.99); }

/* Every price, in one champagne, in tabular figures so a column of them
   lines up rather than shimmering. */
.ls-card__price, .jhc__amount, .jhd__book-ppn, .jp-cmp__price,
.ls-dh__price, .jp-ledger__v, .jp-meter__head b {
  color: var(--x-gold, #E3BE79);
  font-variant-numeric: tabular-nums;
}

/* Focus, once, for the whole site, and never removed. */
:focus-visible { outline: 2px solid var(--x-gold, #E3BE79); outline-offset: 3px; }


/*
 * THE GLASS LIST, RE-AIMED.
 *
 * janet_premium.css publishes a cream glass for the surfaces that float over
 * content — `.jp-glass, .hd.scrolled, .sticky-search, .ls-bar, .bottom-nav,
 * .mnav` — and, separately, `body:has(.dhero__img) .hd`, added deliberately
 * because a transparent header measured failing over a destination hero.
 *
 * Both were right for a cream page and both outrank a bare `.hd`: (0,2,0) and
 * (0,2,1) against (0,1,0). Measured on /tisot-zolot-bud, the header rendered
 * rgba(251,250,248,.82) — a cream bar above a midnight page, on all eleven
 * destination pages, while every other route had the dark one.
 *
 * Matched here at the same specificity rather than with !important, so the
 * reason it wins is the cascade and not a hammer.
 */
body:has(.dhero__img) .hd,
.hd.scrolled,
.jp-glass, .sticky-search, .bottom-nav, .mnav {
  background: rgba(4, 7, 12, .88);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
  backdrop-filter: blur(22px) saturate(150%);
  border-block-end-color: var(--x-line, rgba(240, 236, 226, .10));
  border-color: var(--x-line, rgba(240, 236, 226, .10));
}
@supports not (backdrop-filter: blur(1px)) {
  body:has(.dhero__img) .hd, .hd.scrolled { background: rgba(4, 7, 12, .97); }
}

/* == 01b · THE PALETTES THE :root RE-POINT CANNOT REACH ===================
 *
 * THE MECHANISM janet-visual-system.md DESCRIBES HAS ONE BLIND SPOT, and it
 * cost fifty-two cards on /hotels.
 *
 * The rule is: a component declares its palette at `:where()` — zero
 * specificity — and the theme re-points it at `:root`. janet_hotel_card.css
 * obeys that rule exactly. It still lost, because ZERO SPECIFICITY IS NOT THE
 * SAME AS NOT BEING THERE: the declaration sits on `.jhc` itself, and a custom
 * property declared on an element always beats the same property INHERITED
 * from :root, whatever the specificity of either selector. Specificity only
 * decides between declarations on the SAME element.
 *
 * Measured on the live page: `--jhc-sand` read #111316 at :root and #F7F0E7
 * inside every card. So the theme was correct, the component was correct, and
 * the page was cream anyway.
 *
 * The fix is to re-point on the component's own element, which is what this
 * block does. It is deliberately a MAPPING onto the shell's `--x-*` tokens
 * rather than a set of literals, so there is still exactly one place where the
 * midnight and the champagne are defined.
 *
 * Only palettes that declare COLOUR this way are listed. `.ls`, `.jp` and
 * `.jb` declare geometry and semantic status colours locally and read their
 * surfaces from the base tokens, which the :root re-point does reach.
 */
.jhc, .jhf, .jhd {
  --jhc-ink:        var(--x-ink, #F5F1E8);
  --jhc-ink-2:      var(--x-ink-2, #C3C6CC);
  --jhc-mute:       var(--x-ink-3, #8E939C);
  --jhc-line:       var(--x-line, rgba(240,236,226,.10));
  --jhc-line-2:     var(--x-line, rgba(240,236,226,.10));
  --jhc-card:       var(--x-raised, #0C1119);
  --jhc-coral:      var(--x-gold, #E3BE79);
  --jhc-coral-ink:  var(--x-gold, #E3BE79);
  --jhc-coral-soft: var(--x-gold-wash, rgba(227,190,121,.12));
  /*
   * --jhc-navy and --jhc-teal each do TWO jobs in janet_hotel_card.css: they
   * paint a chip BEHIND white text (.jhc__score-n, .jhd__score b, .jhc__flag)
   * and they colour TEXT (.jhc__type, .jhc__zone, .jhd__fac li::before).
   * One value cannot serve both once the theme inverts — mapped to a light
   * ink they made #fff invisible on the review score, which is the number
   * every reader looks at first. They stay DARK SURFACES here, and the three
   * text usages are re-coloured below.
   */
  --jhc-navy:       #22303F;
  --jhc-gold:       var(--x-gold, #E3BE79);
  --jhc-sand:       var(--x-deep, #070B13);
  /* The two verdict colours stay GREEN and stay distinguishable. A palette
     sweep that painted "free cancellation" champagne would have removed the
     one signal on the card that is not decoration. */
  --jhc-teal:       #1F6B4A;
  --jhc-good:       #57C08A;
  --jhc-good-bg:    rgba(87, 192, 138, .12);
}
.ed {
  --ed-cream:   var(--x-deep, #070B13);
  --ed-sand:    var(--x-raised, #0C1119);
  --ed-ink:     var(--x-ink, #F5F1E8);
  --ed-ink-2:   var(--x-ink-2, #C3C6CC);
  --ed-mute:    var(--x-ink-3, #8E939C);
  --ed-coral:   var(--x-gold, #E3BE79);
  --ed-coral-t: var(--x-gold-deep, #C9A24A);
  --ed-line:    var(--x-line, rgba(240,236,226,.10));
}
/*
 * Two more component-scoped palettes with BARE LITERALS, found by the new
 * assertion in test_one_visual_system.js rather than by eye. Both are status
 * colours picked for a cream page: an error at #a11b1b and a budget bar at
 * #2E6F4E are legible on paper and nearly invisible on midnight. They are text
 * and small fills, so the conformance sweep — which looks for light SURFACES
 * and white-on-white — could not have caught them.
 *
 * The ROLE survives the re-point: green still means within budget, amber still
 * means over, red still means wrong. Only the lightness changes, because the
 * ground did.
 */
.jb {
  --jb-warn: #E0A44A;
  --jb-bad:  #F0776A;
  --jb-good: #57C08A;
}
.jp {
  --jp-ok:   #57C08A;
  --jp-warn: #E08A4A;
}
.ls {
  /* Status washes, not surfaces: the light backgrounds behind "cheapest" and
     "price is older" are pale panels designed for a cream page. */
  --ls-cheap:    #57C08A;
  --ls-cheap-bg: rgba(87, 192, 138, .12);
  --ls-warn:     #E0A44A;
  --ls-warn-bg:  rgba(224, 164, 74, .12);
  --ls-stale:    #C08F5A;
}


/* == 02 · SEARCH — SPATIAL FLIGHT RESULTS ================================= */

/*
 * The reference's flight page is a lit aviation environment with the results
 * layered in front of it. The environment is a fixed, very dark gradient scene
 * rather than a photograph: this page can hold sixty result cards, and a
 * full-bleed image behind a long scrolling list is a repaint on every frame of
 * that scroll for a picture nobody can see through the results anyway.
 */
.ls-main, body.ls-page { position: relative; }
.bkx-sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(120% 80% at 50% -10%, rgba(58, 96, 150, .30), transparent 62%),
    radial-gradient(90% 60% at 80% 8%, rgba(227, 190, 121, .12), transparent 60%),
    linear-gradient(180deg, #071019 0%, #04070C 58%);
}
/* One slow band of light across the top of the frame, the horizon. */
.bkx-sky::after {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 62vh;
  background: linear-gradient(102deg, transparent 42%, rgba(255, 246, 226, .07) 50%, transparent 58%);
  background-size: 280% 100%;
}
html.js .bkx-sky::after { animation: bkx-sheen 24s linear infinite; }

/* --- the search bar becomes the console --------------------------------- */
.ls-bar {
  position: sticky;
  inset-block-start: var(--x-nav-h, 62px);
  z-index: 40;
  border-radius: 0 0 var(--x-r-lg, 22px) var(--x-r-lg, 22px);
  border: 1px solid var(--x-line-2, rgba(240,236,226,.18));
  border-block-start: 0;
  background: linear-gradient(180deg, rgba(31, 34, 40, .90), rgba(13, 15, 18, .95));
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  backdrop-filter: blur(20px) saturate(150%);
  box-shadow: 0 26px 60px -30px rgba(0, 0, 0, .95), 0 0 0 1px rgba(227, 190, 121, .08);
}
.ls-bar::before {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline: 14%;
  block-size: 1px;
  background: linear-gradient(90deg, transparent, rgba(227, 190, 121, .7), transparent);
  pointer-events: none;
}
.ls-field { transition: background-color .28s var(--x-ease), box-shadow .28s var(--x-ease); }
.ls-field:focus-within { background: rgba(227, 190, 121, .05); box-shadow: inset 0 -2px 0 var(--x-gold, #E3BE79); }
.ls-field input, .ls-field select { min-block-size: 46px; box-sizing: border-box; }

/* --- the results, in depth ----------------------------------------------
 * `--i` is the card's index, written once by bkx_routes.js. Each card enters
 * from a little further back, so the list ARRIVES rather than appearing, and
 * the stagger is capped so the twentieth card is not still dealing itself out
 * while the reader is reading the third.
 */
.ls-list { perspective: 1400px; }
.ls-card {
  position: relative;
  transform-origin: 50% 60%;
  transition:
    transform .42s var(--x-ease, cubic-bezier(.22,1,.36,1)),
    box-shadow .42s var(--x-ease),
    border-color .3s var(--x-ease);
}
html.js .ls-list.bkx-in .ls-card {
  animation: bkx-card-in .72s var(--x-ease-2, cubic-bezier(.16,1,.3,1)) both;
  animation-delay: calc(min(var(--i, 0) * 55ms, 520ms));
}
@keyframes bkx-card-in {
  from { opacity: 0; transform: translate3d(0, 26px, -120px) rotateX(6deg); }
  to   { opacity: 1; transform: none; }
}
.ls-card:hover {
  transform: translate3d(0, -3px, 26px);
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  box-shadow: 0 30px 66px -26px rgba(0, 0, 0, .95);
}
/* The chosen offer comes FORWARD, and it is the only card that does. */
.ls-card[data-bkx-picked] {
  z-index: 3;
  transform: translate3d(0, -6px, 44px);
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  box-shadow:
    0 34px 76px -26px rgba(0, 0, 0, .98),
    0 0 0 1px rgba(227, 190, 121, .34),
    0 18px 50px -20px rgba(227, 190, 121, .35);
}

/* --- the route line ------------------------------------------------------
 * Drawn between the two airport codes the card already prints. It is a
 * DECORATION over data the card owns: no time, no distance, no stop count is
 * invented here, and a card with one stop still says so in its own words.
 */
.ls-card__route { position: relative; }
.bkx-route {
  position: relative;
  block-size: 1px;
  margin-block: .5rem;
  background: linear-gradient(90deg,
    rgba(227, 190, 121, 0), var(--x-gold-edge, rgba(227,190,121,.42)) 18%,
    var(--x-gold-edge, rgba(227,190,121,.42)) 82%, rgba(227, 190, 121, 0));
  transform-origin: center;
  scale: 0 1;
  transition: scale .9s var(--x-ease-2, cubic-bezier(.16,1,.3,1));
}
html.js .ls-list.bkx-in .bkx-route { scale: 1 1; }
.bkx-route::after {
  content: "";
  position: absolute;
  inset-block-start: -3px;
  inset-inline-start: 0;
  inline-size: 7px; block-size: 7px;
  border-radius: 50%;
  background: var(--x-gold, #E3BE79);
  box-shadow: 0 0 10px rgba(227, 190, 121, .8);
  opacity: 0;
}
@keyframes bkx-route-fly {
  from { transform: translateX(0); opacity: 0; }
  14%  { opacity: 1; }
  86%  { opacity: 1; }
  to   { transform: translateX(var(--bkx-route-w, 200px)); opacity: 0; }
}
html.js .ls-list.bkx-in .ls-card:hover .bkx-route::after {
  animation: bkx-route-fly 1.4s var(--x-ease, cubic-bezier(.22,1,.36,1)) 1;
}

/* --- filters, in the same material -------------------------------------- */
.ls-prefs, .jdh-filters, .jh-facets {
  border-radius: var(--x-r, 16px);
  overflow: hidden;
}
.ls-prefs__body, .jh-facets__body {
  transition: max-block-size .45s var(--x-ease-2, cubic-bezier(.16,1,.3,1)), opacity .3s linear;
}
.ls-chip, .jh-chip, .jp-chip, .ls-prefs__chips span {
  border-radius: 999px;
  border: 1px solid var(--x-line-2, rgba(240,236,226,.18));
  background: rgba(255, 255, 255, .03);
  transition: border-color .25s var(--x-ease), background-color .25s var(--x-ease), color .25s var(--x-ease);
}
.ls-chip:hover, .jh-chip:hover, .jp-chip:hover {
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  color: var(--x-gold, #E3BE79);
}
.ls-chip[aria-pressed="true"], .jp-chip[aria-pressed="true"], .jp-chip.is-on {
  background: var(--x-gold-wash, rgba(227,190,121,.12));
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  color: var(--x-gold, #E3BE79);
}

/* Sorting and filtering must not FLASH. The list fades and settles rather
   than blanking, which is what makes a re-sort read as the same list moving. */
.ls-list.bkx-settling { opacity: .35; transition: opacity .16s linear; }
.ls-list { transition: opacity .32s var(--x-ease, cubic-bezier(.22,1,.36,1)); }



/* == 02b · SEARCH — THE SURFACE THE READER ACTUALLY LOOKS AT ==============
 *
 * MEASURED, not assumed: on /search the full seventeen-card `.ls-list` sits
 * inside `#jdh-all-flights`, which ships `hidden` behind a toggle. What the
 * page actually shows is `janet_deal_hunter.js`'s three chosen flights, three
 * chosen hotels and three packages — the `.jdh-*` markup. The first version of
 * this sheet dressed only `.ls-card`, so every rule in it applied to elements
 * with a zero-height box and the page a reader sees was left untouched.
 *
 * So this is the /search scene. The `.ls-*` rules above stay what dress the
 * full list once the reader opens it.
 */

/* --- each block of three is a SCENE, not a row of boxes ------------------ */
.jdh { position: relative; margin-block: clamp(3rem, 7vw, 6rem); }
.jdh__head { margin-block-end: clamp(1.2rem, 2.4vw, 2rem); }
.jdh__h {
  font-family: var(--x-display, "Frank Ruhl Libre", Georgia, serif);
  font-size: clamp(1.55rem, 3vw, 2.5rem);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: -.01em;
  color: var(--x-ink, #F5F1E8);
}
.jdh__sub { color: var(--x-ink-3, #8E939C); max-inline-size: 62ch; }

/*
 * THE THREE CARDS STAND IN SPACE. `--i` is the card's position, written by
 * bkx_routes.js, and the depth each one enters from is its index — so they
 * land front to back rather than all at once. The stagger is capped because
 * three is the normal case and a fourth must not feel late.
 */
.jdh__grid { perspective: 1500px; perspective-origin: 50% 34%; }
.jdh-card {
  position: relative;
  border-radius: var(--x-r-lg, 22px);
  border: 1px solid var(--x-line, rgba(240,236,226,.10));
  background: linear-gradient(158deg, rgba(16, 22, 32, .78), rgba(7, 11, 18, .90));
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  backdrop-filter: blur(14px) saturate(140%);
  box-shadow: 0 30px 68px -32px rgba(0, 0, 0, .95);
  transform-origin: 50% 62%;
  transition:
    transform .44s var(--x-ease, cubic-bezier(.22,1,.36,1)),
    box-shadow .44s var(--x-ease),
    border-color .3s var(--x-ease);
}
html.js .jdh__grid .jdh-card { opacity: 0; }
html.js .jdh__grid.bkx-in .jdh-card {
  animation: bkx-card-in .8s var(--x-ease-2, cubic-bezier(.16,1,.3,1)) both;
  animation-delay: calc(min(var(--i, 0) * 120ms, 600ms));
}
.jdh-card:hover {
  transform: translate3d(0, -4px, 30px);
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  box-shadow: 0 38px 82px -28px rgba(0, 0, 0, .98);
}

/*
 * TWO DIFFERENT KINDS OF FORWARD, and they must not be confused.
 *
 * `--pick` is BOOKALLO's choice — a standing editorial claim, so it carries a
 * champagne edge and a small permanent lift.
 * `[data-jdh-on]` is the READER's choice, set by janet_deal_hunter.js when they
 * press "בחר את הטיסה הזו". That is the one that comes furthest forward. It is
 * the product's own selection state; nothing here invents a second one.
 */
.jdh__grid > .jdh-card { block-size: 100%; }
.jdh-card--pick {
  border-color: var(--x-gold, #E3BE79);
  transform: translateZ(18px) scale(1.015);
  box-shadow:
    0 36px 80px -28px rgba(0, 0, 0, .97),
    0 0 0 1px rgba(227, 190, 121, .26),
    0 16px 46px -22px rgba(227, 190, 121, .28);
}
.jdh-card--pick::before {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline: 18%;
  block-size: 1px;
  background: linear-gradient(90deg, transparent, rgba(227, 190, 121, .85), transparent);
  pointer-events: none;
}
.jdh-card[data-jdh-on] {
  z-index: 3;
  transform: translate3d(0, -8px, 52px);
  border-color: var(--x-gold, #E3BE79);
  box-shadow:
    0 40px 90px -26px rgba(0, 0, 0, .98),
    0 0 0 1px rgba(227, 190, 121, .40),
    0 20px 56px -22px rgba(227, 190, 121, .34);
}

/* --- the badges, the facts, the price ----------------------------------- */
.jdh-role, .jdh-chip, .jdh-fresh, .jdh-rate, .jdh-stars {
  border-radius: 999px;
  border: 1px solid var(--x-line-2, rgba(240,236,226,.18));
  background: rgba(255, 255, 255, .03);
  color: var(--x-ink-2, #C3C6CC);
}
.jdh-role--pick {
  background: linear-gradient(160deg, var(--x-gold, #E3BE79), var(--x-gold-deep, #C9A24A));
  border-color: transparent;
  color: var(--x-on-gold, #10131A);
  font-weight: 700;
}
.jdh-fresh--live { color: var(--x-gold, #E3BE79); border-color: var(--x-gold-edge, rgba(227,190,121,.42)); }
.jdh-card__air { color: var(--x-ink, #F5F1E8); font-weight: 600; }
.jdh-leg__t { color: var(--x-ink, #F5F1E8); font-variant-numeric: tabular-nums; }
.jdh-leg__tag, .jdh-leg__d, .jdh-facts, .jdh-price__note { color: var(--x-ink-3, #8E939C); }
.jdh-price__total, .jdh-pkg__total {
  color: var(--x-gold, #E3BE79);
  font-family: var(--x-display, "Frank Ruhl Libre", Georgia, serif);
  font-size: clamp(1.6rem, 2.6vw, 2.15rem);
  font-variant-numeric: tabular-nums;
  line-height: 1.06;
}
/* An unknown fact stays visibly unknown rather than borrowing the confident
   colour of the ones beside it. */
.jdh-unk { color: var(--x-ink-3, #8E939C); font-style: italic; }
.jdh-why { color: var(--x-ink-2, #C3C6CC); }
.jdh-why strong { color: var(--x-ink, #F5F1E8); }

/* --- the two actions ----------------------------------------------------- */
.jdh-cta {
  border-radius: 999px;
  background: linear-gradient(160deg, var(--x-gold, #E3BE79), var(--x-gold-deep, #C9A24A));
  color: var(--x-on-gold, #10131A);
  font-weight: 700;
  border: 0;
  transition: transform .22s var(--x-ease), box-shadow .22s var(--x-ease);
}
.jdh-cta:hover { transform: translateY(-2px); box-shadow: 0 16px 34px -14px rgba(227,190,121,.6); }
.jdh-select {
  border-radius: 999px;
  border: 1px solid var(--x-line-2, rgba(240,236,226,.18));
  background: transparent;
  color: var(--x-ink-2, #C3C6CC);
  transition: border-color .25s var(--x-ease), color .25s var(--x-ease), background-color .25s var(--x-ease);
}
.jdh-select:hover { border-color: var(--x-gold-edge, rgba(227,190,121,.42)); color: var(--x-gold, #E3BE79); }
.jdh-select[data-jdh-on] {
  background: var(--x-gold-wash, rgba(227,190,121,.12));
  border-color: var(--x-gold, #E3BE79);
  color: var(--x-gold, #E3BE79);
}
.jdh-cta--off { opacity: .55; }

/*
 * THE PHOTO WELL — and the case where there is no photograph.
 *
 * Hotel pictures come from the provider's CDN and some of them do not load.
 * An empty dark rectangle where a photograph should be reads as a broken page;
 * "we have no picture of this property" and "this page is broken" must not look
 * the same. So the well carries a designed plate UNDER the image: when the
 * image loads it covers the plate completely, and when it fails bkx_routes.js
 * removes the <img> and the plate is what the reader sees.
 */
.jdh-card__ph {
  position: relative;
  overflow: hidden;
  border-radius: var(--x-r, 16px) var(--x-r, 16px) 0 0;
  background:
    radial-gradient(120% 90% at 30% 0%, rgba(58, 96, 150, .30), transparent 62%),
    linear-gradient(160deg, #0D1420, #070B13);
}
.jdh-card__ph::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 42%, rgba(4, 7, 12, .72));
  pointer-events: none;
}
.jdh-card__ph[data-bkx-nophoto]::before {
  content: "";
  position: absolute;
  inset-block-end: 14px;
  inset-inline-start: 14px;
  inline-size: 26px;
  block-size: 1px;
  background: var(--x-gold-edge, rgba(227,190,121,.42));
}
.jdh-card__ph img {
  position: relative;
  z-index: 1;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: transform 1.2s var(--x-ease, cubic-bezier(.22,1,.36,1));
}
.jdh-card:hover .jdh-card__ph img { transform: scale(1.04); }
.ls-hotel-card__gal { z-index: 2; }

/* --- the trip the reader is assembling, as a floating control surface ---- */
.jdh-trip {
  position: sticky;
  inset-block-end: 12px;
  z-index: 45;
  border-radius: var(--x-r-lg, 22px);
  border: 1px solid var(--x-line-2, rgba(240,236,226,.18));
  background: linear-gradient(180deg, rgba(31, 34, 40, .92), rgba(13, 15, 18, .96));
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  backdrop-filter: blur(20px) saturate(150%);
  box-shadow: 0 34px 74px -30px rgba(0, 0, 0, .96), 0 0 0 1px rgba(227, 190, 121, .10);
}
.jdh-trip__total { color: var(--x-gold, #E3BE79); font-variant-numeric: tabular-nums; }

/* --- the top deal panel -------------------------------------------------- */
.ls-deal-hero, .ls-dh {
  border-radius: var(--x-r-lg, 22px);
  border: 1px solid var(--x-gold-edge, rgba(227,190,121,.42));
  background: linear-gradient(158deg, rgba(20, 27, 39, .82), rgba(7, 11, 18, .92));
  box-shadow: 0 36px 82px -30px rgba(0, 0, 0, .96);
}
.ls-dh__total-price {
  font-family: var(--x-display, "Frank Ruhl Libre", Georgia, serif);
  font-size: clamp(2rem, 4.4vw, 3.4rem);
  line-height: 1.02;
}

/* --- the panels below, in one material ---------------------------------- */
.ls-sum, .ls-save, .ls-trust, .ls-sellers, .ls-cal, .ls-flex, .ls-janet, .jdh-break {
  border-radius: var(--x-r, 16px);
  border: 1px solid var(--x-line, rgba(240,236,226,.10));
  background: linear-gradient(158deg, rgba(16, 22, 32, .62), rgba(7, 11, 18, .80));
}
.ls-sum__v, .ls-trust__v { color: var(--x-ink, #F5F1E8); }
.ls-sum__k, .ls-trust__k, .ls-trust__note { color: var(--x-ink-3, #8E939C); }

@media (max-width: 860px) {
  /* Three cards side by side become three you swipe between. The perspective
     goes with them: a rotated card at 380px costs legibility for an effect
     nobody can see at that size. */
  .jdh__grid { perspective: none; }
  .jdh-card--pick, .jdh-card[data-jdh-on] { transform: none; }
  .jdh-card[data-jdh-on] { border-color: var(--x-gold, #E3BE79); }
}


/* == 03 · HOTELS — DEPTH, NOT A CAROUSEL ================================= */

/*
 * THIS WAS A RAIL, AND THE RAIL WAS WRONG. Kept as a note rather than quietly
 * replaced, because the reasoning is the part worth not repeating.
 *
 * The homepage's destination strip is a curated handful you browse, so a
 * horizontal rail with the cards turned in space is right there. `/hotels`
 * returned FIFTY properties for one Budapest search — a comparison surface
 * somebody filters, sorts and scans. Forcing that into a one-card-at-a-time
 * carousel hides forty-nine results behind a swipe, and the screenshot showed
 * the second cost too: `.jhc` lays its photograph and its facts out side by
 * side, so at a fixed 340px the text column collapsed to about one character
 * per line and every card became an unreadable vertical ribbon.
 *
 * So the grid stays a grid, and the depth comes from the axis a grid has going
 * spare: cards arrive from behind the screen, the one under the pointer comes
 * forward, and the photograph moves inside its own frame. Visibly spatial;
 * still fifty results you can compare.
 */
#ls-hotel-grid, .jh-grid { perspective: 1600px; perspective-origin: 50% 30%; }

.jhc {
  position: relative;
  border-radius: var(--x-r-lg, 22px);
  border: 1px solid var(--x-line, rgba(240,236,226,.10));
  background: linear-gradient(158deg, rgba(16, 22, 32, .74), rgba(7, 11, 18, .88));
  box-shadow: 0 26px 60px -32px rgba(0, 0, 0, .94);
  transform-origin: 50% 64%;
  transition:
    transform .42s var(--x-ease, cubic-bezier(.22,1,.36,1)),
    box-shadow .42s var(--x-ease),
    border-color .3s var(--x-ease);
}
.jhc:hover, .jhc:focus-within {
  transform: translate3d(0, -5px, 40px);
  z-index: 3;
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  box-shadow:
    0 38px 84px -28px rgba(0, 0, 0, .98),
    0 0 0 1px rgba(227, 190, 121, .22);
}

/*
 * The arrival. `--i` is written by bkx_routes.js and capped hard: fifty cards
 * at even 40ms each is two seconds of dealing, and the reader is looking at
 * the first row the whole time. Past the twelfth card they simply appear —
 * they are below the fold, and nobody watches an animation they cannot see.
 */
html.js #ls-hotel-grid.bkx-in .jhc,
html.js .jh-grid.bkx-in .jhc {
  animation: bkx-card-in .7s var(--x-ease-2, cubic-bezier(.16,1,.3,1)) both;
  animation-delay: calc(min(var(--i, 0) * 45ms, 540ms));
}

/* The photograph lives in its own frame and moves inside it. */
.jhc__media {
  position: relative;
  overflow: hidden;
  border-radius: var(--x-r, 16px) var(--x-r, 16px) 0 0;
  background: linear-gradient(160deg, #0D1420, #070B13);
}
.jhc__media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  cursor: zoom-in;              /* the photo opens the hotel's gallery panel */
  transition: transform .8s var(--x-ease, cubic-bezier(.22,1,.36,1));
}
.jhc:hover .jhc__media img { transform: scale(1.05); }
/* Same rule as the picks: a supplier photograph that never arrives leaves a
   designed plate, not a hole. */
.jhc__media[data-bkx-nophoto]::before {
  content: "";
  position: absolute;
  inset-block-end: 14px;
  inset-inline-start: 14px;
  inline-size: 26px;
  block-size: 1px;
  background: var(--x-gold-edge, rgba(227,190,121,.42));
}

/* The hotels hero keeps its photograph and takes the shell's grade. */
.jh-hero { position: relative; isolation: isolate; }
.jh-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(4,7,12,.72) 0%, rgba(4,7,12,.18) 30%, rgba(4,7,12,.92) 100%),
    radial-gradient(110% 70% at 50% 40%, transparent 24%, rgba(4,7,12,.72) 100%);
}
.jh-hero > * { position: relative; z-index: 2; }

/* The bands and the facet rail in the shell's material. */
.jh-band--sand, .jh-band--soft { background: transparent; }
.jh-aside, .jh-sortbar, .jh-summary, .jh-promise, .jh-guides, .jh-notices {
  border-radius: var(--x-r, 16px);
  border: 1px solid var(--x-line, rgba(240,236,226,.10));
  background: linear-gradient(158deg, rgba(16, 22, 32, .60), rgba(7, 11, 18, .78));
}
.jh-rhead__h { font-family: var(--x-display, "Frank Ruhl Libre", Georgia, serif); }

@media (max-width: 860px) {
  #ls-hotel-grid, .jh-grid { perspective: none; }
  .jhc:hover, .jhc:focus-within { transform: none; }
}



/* --- FOUR LIGHT-THEME SURVIVORS, MEASURED ON THE LIVE PAGE ---------------
 *
 * The `--jhc-*` palette IS re-pointed by janet_premium.css and resolves
 * correctly here (`--jhc-sand` reads #111316). These four are the two ways a
 * component still resists a theme, both of which janet-visual-system.md names,
 * and all four were live on 52 cards at once:
 *
 *  1. A LITERAL no stylesheet can reach.
 *     `.jhc__media--none` paints #FFF6EC → #EFE3D4 → the "no photograph from
 *     the supplier" plate was a cream card on a midnight page. It is the most
 *     common state in this result set, so the page's dominant surface was the
 *     one surface the theme could not touch.
 *     `.jhc__details` and `.jhf__g` are the same defect: `background: #fff`.
 *
 *  2. A TOKEN THAT INVERTED.
 *     `.jhc .ls-card__cta--check` sets `background: var(--jhc-ink)` with
 *     `color: #FFF6EA`. Under the light theme --jhc-ink is #241C16 and that is
 *     near-white on near-black. Re-pointed, --jhc-ink IS the light text colour
 *     (#F5F3EF) — so the button became #FFF6EA on #F5F3EF, which is text you
 *     cannot see, on the control that checks whether a room is available.
 *
 * Repaired here rather than in janet_hotel_card.css because that sheet is
 * still the light-theme source of truth for anything that renders it without
 * the shell, and because the shell is what has to be true everywhere.
 */
.jhc__media--none, .ls-hotel-card__ph--none {
  background:
    radial-gradient(120% 90% at 30% 0%, rgba(58, 96, 150, .28), transparent 62%),
    linear-gradient(160deg, #0D1420, #070B13);
  color: var(--x-ink-3, #8E939C);
}
.jhc__media-fb {
  background: linear-gradient(160deg, var(--x-gold, #E3BE79), var(--x-gold-deep, #C9A24A));
  color: var(--x-on-gold, #10131A);
}
.jhc__details, .jhf__g {
  background: linear-gradient(158deg, rgba(16, 22, 32, .70), rgba(7, 11, 18, .86));
}
.jhc__details {
  border-color: var(--x-line-2, rgba(240,236,226,.18));
  color: var(--x-ink-2, #C3C6CC);
  transition: border-color .22s var(--x-ease), color .22s var(--x-ease), background-color .22s var(--x-ease);
}
.jhc__details:hover {
  background: var(--x-gold-wash, rgba(227,190,121,.12));
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  color: var(--x-gold, #E3BE79);
}
/*
 * "Check availability" answers in place; "book" hands the reader off. The two
 * must not look identical — janet_hotel_card.css says so and it is right — so
 * this stays the SECONDARY of the pair: an outline against the champagne fill
 * the booking action carries.
 */
.jhc .ls-card__cta--check {
  background: transparent;
  border: 1px solid var(--x-line-2, rgba(240,236,226,.18));
  color: var(--x-ink-2, #C3C6CC);
}
.jhc .ls-card__cta--check:hover {
  background: var(--x-gold-wash, rgba(227,190,121,.12));
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  color: var(--x-gold, #E3BE79);
}
/*
 * The price rail — the fifth literal, and the one the background-COLOUR sweep
 * missed because it is painted with a gradient. `linear-gradient(#FFFDFB,
 * #FDF6EF)` is a cream column beside a midnight card, and it holds the price,
 * which makes it the most looked-at surface on the page.
 *
 * Specificity here is `.jhc .jhc__rail` because the sheet declares it that
 * way; a single-class rule loses to it even loading later.
 */
.jhc .jhc__rail {
  background: linear-gradient(180deg, rgba(20, 27, 39, .70), rgba(7, 11, 18, .86));
  border-color: var(--x-line, rgba(240,236,226,.10));
}
.jhc__ppn, .jhc__conf, .ls-hotel-card__conf { color: var(--x-ink-3, #8E939C); }
/* The three places those two tokens are TEXT rather than a surface. */
.jhc__type { color: var(--x-ink-2, #C3C6CC); background: rgba(240, 236, 226, .06); }
.jhc__zone { color: #57C08A; background: rgba(87, 192, 138, .12); }
.jhd__fac li::before { color: #57C08A; }

/* The detail panel and its cards. Same two failure modes as the result cards,
   on the surface a reader reaches just before booking. */
.jhd { background: var(--x-deep, #070B13); color: var(--x-ink, #F5F1E8); }
.jhd__s, .jhr {
  background: linear-gradient(158deg, rgba(16, 22, 32, .70), rgba(7, 11, 18, .86));
  border-color: var(--x-line, rgba(240,236,226,.10));
}
.jhd__x, .jhd__nav {
  background: rgba(13, 15, 18, .82);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border: 1px solid var(--x-line-2, rgba(240,236,226,.18));
  color: var(--x-ink, #F5F1E8);
}
.jhd__x:hover, .jhd__nav:hover {
  background: rgba(20, 27, 39, .92);
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  color: var(--x-gold, #E3BE79);
}
.jhd__cap {
  border-color: var(--x-line-2, rgba(240,236,226,.18));
  background: radial-gradient(120% 100% at 50% 0%, rgba(58,96,150,.22) 0%, var(--x-deep, #070B13) 70%);
}
/*
 * On the detail panel this button is the PRIMARY action, not the secondary one
 * it is on a result card, and janet_hotel_card.css says so with its own
 * higher-specificity rule: `background: var(--jhc-ink); color: #FFF6EA`.
 * Re-pointed, --jhc-ink is the light text colour, so it painted #FFF6EA on
 * #F5F1E8 — the availability check, unreadable. Champagne, like every other
 * primary action in the shell.
 */
.jhd__book .ls-card__cta--check {
  background: linear-gradient(160deg, var(--x-gold, #E3BE79), var(--x-gold-deep, #C9A24A));
  color: var(--x-on-gold, #10131A);
  border: 0;
  font-weight: 700;
}
.jhd__book .ls-card__cta--check:hover {
  background: linear-gradient(160deg, #EFCE8E, var(--x-gold, #E3BE79));
  color: var(--x-on-gold, #10131A);
}
.jhd__nofoto-m {
  background: linear-gradient(160deg, var(--x-gold, #E3BE79), var(--x-gold-deep, #C9A24A));
  color: var(--x-on-gold, #10131A);
}

/*
 * THE FOUR PROVENANCE BADGES — live, cached, indicative, sandbox.
 *
 * janet_live_search.css paints them as pale pills with dark text: #e7f6ec on
 * #1c6b38 and three more like it. Correct on a cream page; on midnight they
 * were 48 pastel stickers scattered over dark cards, which the repetition rule
 * in shell_conformance.js reported the moment it started visiting /hotels WITH
 * a search rather than on its empty form.
 *
 * They are INVERTED rather than recoloured, and the four hues are kept apart,
 * because these are the labels that say where a price came from. Making them
 * one champagne would be tidier and would erase the distinction between a
 * price measured a moment ago and one read out of a cache.
 */
.ls-badge--live       { background: rgba(87, 192, 138, .14); color: #6FD3A0; }
.ls-badge--cached     { background: rgba(224, 164, 74, .14); color: #E0A44A; }
.ls-badge--indicative { background: rgba(150, 140, 220, .14); color: #A79CE8; }
.ls-badge--sandbox    { background: rgba(240, 119, 106, .14); color: #F0776A; }
.ls-tag { background: rgba(240, 236, 226, .06); color: var(--x-ink-2, #C3C6CC); }

/*
 * The favourite button. `--jt-cream-2` is a cream literal from the base
 * search sheet, so this rendered as a white disc in the corner of every card —
 * fifty-one of them on one page. It slipped past the conformance sweep because
 * a 38px circle is 1,444 square pixels and the sweep ignores anything under
 * 12,000, a floor that exists so a badge or a chip is not reported as a
 * surface. One white pill is a badge; fifty-one are a design, so the sweep now
 * counts repetition as well as area.
 */
.ls-card .jfav, .ls-hotel-card .jfav, .jhc .jfav {
  background: rgba(13, 15, 18, .78);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-color: var(--x-line-2, rgba(240,236,226,.18));
  color: var(--x-ink-2, #C3C6CC);
}
.ls-card .jfav:hover, .ls-hotel-card .jfav:hover, .jhc .jfav:hover {
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  color: var(--x-gold, #E3BE79);
}
.jfav.jfav--on { color: var(--x-gold, #E3BE79); }

.jhf__l { color: var(--x-ink, #F5F1E8); }
.jhf__opt { color: var(--x-ink-2, #C3C6CC); }
.jhf__opt input { accent-color: var(--x-gold, #E3BE79); }


/* == 04 · HOTEL DETAIL — THE GALLERY IN DEPTH ============================= */

/*
 * The reference's hotel detail is a depth stack: one image forward, the others
 * behind it and angled. `.jhd__stage` is the large frame the renderer already
 * builds and `.jhd__thumbs` its strip; the strip becomes the stack.
 */
.jhd { perspective: 1600px; }
.jhd__stage {
  position: relative;
  border-radius: var(--x-r-lg, 22px);
  overflow: hidden;
  box-shadow: 0 40px 90px -34px rgba(0, 0, 0, .96);
  transform: translate3d(0, 0, 0);
}
.jhd__stage img { transition: transform .8s var(--x-ease-2, cubic-bezier(.16,1,.3,1)); }

.jhd__thumbs {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  scrollbar-width: none;
  perspective: 1200px;
  padding-block: 14px;
}
.jhd__thumbs::-webkit-scrollbar { display: none; }
.jhd__thumb {
  --d: 0; --ad: 0;
  flex: 0 0 auto;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--x-line, rgba(240,236,226,.10));
  transform-origin: 50% 60%;
  transform:
    translate3d(0, calc(var(--ad) * 6px), calc(var(--ad) * -90px))
    rotateY(calc(var(--d) * -22deg));
  transition: transform .18s linear, border-color .3s var(--x-ease), filter .3s var(--x-ease);
  filter: brightness(calc(1 - var(--ad) * .32));
  cursor: pointer;
}
.jhd__thumb[aria-current="true"], .jhd__thumb.is-on {
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  filter: none;
  box-shadow: 0 0 0 1px rgba(227, 190, 121, .3), 0 18px 44px -20px rgba(0, 0, 0, .9);
}

/* The booking panel floats in front of the scene and stays with the reader. */
.jhd__book {
  position: sticky;
  inset-block-start: calc(var(--x-nav-h, 62px) + 16px);
  border-radius: var(--x-r-lg, 22px);
  border: 1px solid var(--x-line-2, rgba(240,236,226,.18));
  background: linear-gradient(157deg, rgba(18, 24, 34, .90), rgba(8, 11, 18, .95));
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  backdrop-filter: blur(20px) saturate(150%);
  box-shadow: 0 40px 90px -34px rgba(0, 0, 0, .96), 0 0 0 1px rgba(227, 190, 121, .10);
}
.jhd__book::before {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline: 16%;
  block-size: 1px;
  background: linear-gradient(90deg, transparent, rgba(227, 190, 121, .7), transparent);
}
/* Room rows arrive from depth, once. */
html.js .jhd__rows.bkx-in .jhd__row {
  animation: bkx-card-in .6s var(--x-ease-2, cubic-bezier(.16,1,.3,1)) both;
  animation-delay: calc(min(var(--i, 0) * 60ms, 420ms));
}


/* == 05 · THE PLANNER — THE TRIP ASSEMBLES =============================== */

/*
 * The result is not a page of boxes: the destination lands, then the flight,
 * then the hotel, then the days, then the budget. Each stage is a class the
 * runtime adds when the previous one has settled, so the reader watches the
 * trip being built rather than meeting it already finished.
 *
 * EVERY FIGURE IS THE PLANNER'S OWN. This sheet moves what janet_plan.js
 * wrote and never writes a number; the budget meter is filled from a width the
 * runtime reads off the planner's own rendered total.
 */
.jp-intro, .jp-result { position: relative; }
.jp-bar { position: sticky; inset-block-start: 0; z-index: 90; }

.jp-card, .jp-day, .jp-cmp {
  transform-origin: 50% 60%;
}
html.js .jp-result .jp-card,
html.js .jp-result .jp-day,
html.js .jp-result .jp-cmp { opacity: 0; }
html.js .jp-result.bkx-built .jp-card,
html.js .jp-result.bkx-built .jp-day,
html.js .jp-result.bkx-built .jp-cmp {
  animation: bkx-assemble .78s var(--x-ease-2, cubic-bezier(.16,1,.3,1)) both;
  animation-delay: calc(160ms + min(var(--i, 0) * 210ms, 2400ms));
}
@keyframes bkx-assemble {
  from { opacity: 0; transform: translate3d(0, 30px, -140px) rotateX(7deg); }
  to   { opacity: 1; transform: none; }
}

/* The route that connects the days. It draws itself down the itinerary as the
   days land, which is the one line in this scene that is a picture of the plan
   rather than a piece of it. */
.jp-days { position: relative; }
.jp-days::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 18px;
  inline-size: 2px;
  block-size: 100%;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--x-gold-edge, rgba(227,190,121,.42)), rgba(227,190,121,.06));
  transform-origin: top;
  scale: 1 0;
  transition: scale 1.6s var(--x-ease-2, cubic-bezier(.16,1,.3,1)) .5s;
}
html.js .jp-result.bkx-built .jp-days::before { scale: 1 1; }
.jp-day { position: relative; }
.jp-day__n {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  inline-size: 38px; block-size: 38px;
  border-radius: 50%;
  background: linear-gradient(160deg, var(--x-gold, #E3BE79), var(--x-gold-deep, #C9A24A));
  color: var(--on-sky, #10131A);
  font-weight: 700;
}

/*
 * THE BUDGET METER IS janet_plan.js's OWN, dressed — not a second one.
 *
 * budget_meter.js decides WITHIN / OVER / UNKNOWN and janet_plan.js paints the
 * fill with an inline scaleX from the figure it actually holds. Nothing here
 * computes a width; `data-status` is read only to choose a colour, and the
 * UNKNOWN state is deliberately NOT gold — a withheld total must not look like
 * a measured one wearing a confident bar.
 */
.jp-meter { position: relative; }
.jp-meter__track {
  block-size: 5px;
  border-radius: 3px;
  background: var(--x-line, rgba(240,236,226,.10));
  overflow: hidden;
}
.jp-meter__fill {
  display: block;
  block-size: 100%;
  transform-origin: inset-inline-start center;
  background: linear-gradient(90deg, var(--x-gold-deep, #C9A24A), var(--x-gold, #E3BE79));
  transition: transform 1.3s var(--x-ease-2, cubic-bezier(.16,1,.3,1)) .4s;
}
.jp-meter[data-status="OVER"] .jp-meter__fill {
  background: linear-gradient(90deg, #B4552F, #E08A4A);
}
.jp-meter[data-status="UNKNOWN"] .jp-meter__fill {
  background: repeating-linear-gradient(135deg,
    rgba(240,236,226,.20) 0 6px, rgba(240,236,226,.06) 6px 12px);
}
.jp-meter__line { color: var(--x-ink-2, #C3C6CC); }

/* The five questions get the console's material. */
.jp-fieldset, .jp-grid > .jp-field {
  border-radius: var(--x-r, 16px);
  border: 1px solid var(--x-line, rgba(240,236,226,.10));
  background: linear-gradient(150deg, rgba(16, 22, 32, .68), rgba(7, 11, 18, .80));
}
.jp-nav__rail { display: flex; gap: 8px; }

/*
 * A URL IS AN LTR RUN, and this one is inside an RTL document.
 *
 * The share link renders as a scrambled string under `direction: rtl` —
 * the bidi algorithm reorders the neutral characters in the query string and
 * the reader is shown a link that is not the link. Same class of defect as the
 * reversed date range on the homepage, and the same shape of fix: state the
 * direction of the run rather than of the page.
 */
.jp-share input, .jp-share code, .jp-share textarea {
  direction: ltr;
  text-align: start;
  unicode-bidi: isolate;
  font-variant-numeric: tabular-nums;
}
.jp-share input {
  border-radius: 999px;
  border: 1px solid var(--x-line, rgba(240,236,226,.10));
  background: rgba(255, 255, 255, .03);
  color: var(--x-ink-2, #C3C6CC);
}


/* == 06 · DESTINATION + EDITORIAL ======================================== */

/*
 * A destination page opens on its own environment. The hero photograph is
 * already full-width in the generated markup; this gives it the shell's grade,
 * a slow breathing scale, and depth between the picture and the type.
 */
.dhero, .ghero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  min-block-size: min(72vh, 680px);
  display: grid;
  align-content: center;
}
.dhero__img, .ghero__img, .dhero img, .ghero img {
  position: absolute;
  inset: -4%;
  inline-size: 108%;
  block-size: 108%;
  object-fit: cover;
  z-index: -2;
}
@keyframes bkx-breathe { 0%,100% { scale: 1; } 50% { scale: 1.045; } }
html.js .dhero__img, html.js .ghero__img { animation: bkx-breathe 34s ease-in-out infinite; }
.dhero::after, .ghero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* The headline and its subhead sit in the vertical CENTRE of this hero
     (align-content: center), which is exactly where the old ramp was thinnest
     — .22 at 26%, a transparent radial core — so over a bright sky (Budapest,
     Bangkok) the h1 measured 2.45:1 and the subhead 2.30:1 at 1440 (release
     gate, 2026-09-08). The band the type occupies now holds .60–.72; the top
     edge and the corners are as they were. */
  background:
    linear-gradient(180deg, rgba(4,7,12,.80) 0%, rgba(4,7,12,.42) 22%, rgba(4,7,12,.62) 44%, rgba(4,7,12,.74) 62%, rgba(4,7,12,.94) 100%),
    radial-gradient(112% 74% at 50% 44%, rgba(4,7,12,.10) 10%, rgba(4,7,12,.78) 100%);
}
.dhero h1, .ghero h1 {
  font-size: clamp(2.1rem, 3.6vw + .5rem, 4rem);
  text-shadow: 0 4px 40px rgba(0, 0, 0, .7);
}
/* #C3C6CC over the Bangkok hero photograph measured 4.48:1 at 1920 — two
   hundredths short. One step lighter; the hierarchy against the headline
   (cream, 700) is unchanged. */
.dhero__sub, .ghero__sub { color: #D6D9DF; max-inline-size: 54ch; }

/* Editorial imagery gets depth rather than sitting flat in the column. */
.jn-guide, .ed-fig, .jd-card {
  border-radius: var(--x-r-lg, 22px);
  overflow: hidden;
  transition: transform .4s var(--x-ease), box-shadow .4s var(--x-ease), border-color .3s var(--x-ease);
}
.jn-guide:hover, .jd-card:hover {
  transform: translateY(-6px);
  border-color: var(--x-gold-edge, rgba(227,190,121,.42));
  box-shadow: 0 30px 66px -26px rgba(0, 0, 0, .95);
}


/* == SHARED · reveals on every route ====================================== */

html.js .bkx-rise-r {
  opacity: 0;
  transform: translate3d(0, 24px, 0);
  transition: opacity .8s var(--x-ease-2, cubic-bezier(.16,1,.3,1)),
              transform .8s var(--x-ease-2, cubic-bezier(.16,1,.3,1));
}
html.js .bkx-rise-r.bkx-in { opacity: 1; transform: none; }


/* == RESPONSIVE =========================================================== */

@media (max-width: 900px) {
  .hd { padding-inline: clamp(14px, 4vw, 22px); gap: 4px 12px; flex-wrap: wrap; }
  /*
   * THE NAV STAYS ON A PHONE (2026-09-12). It was `display: none` here, and the
   * inner pages carry no burger — measured on 10 of 12 routes at 390px: the
   * header offered the logo and a language link and nothing else, so a reader
   * on /tisot-zolot-roma could not reach hotels or the planner without going
   * home first. It becomes its own swipeable row under the brand; the header is
   * sticky, not fixed, so the taller header pushes content rather than covering it.
   */
  .hd-nav {
    order: 3;
    flex: 1 0 100%;
    margin-inline-end: 0;
    gap: 18px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .hd-nav::-webkit-scrollbar { display: none; }
  .hd-nav a { flex: 0 0 auto; white-space: nowrap; min-inline-size: 44px; justify-content: center; }
  .hd-nav a::after { inset-block-end: 6px; }
  .jhd__book { position: static; }
}
@media (max-width: 720px) {
  .jhd__thumb { transform: none; filter: none; }
}

@media (min-width: 2200px) {
  .hd { padding-inline: clamp(48px, 4vw, 96px); }
}


/* == REDUCED MOTION ======================================================= */

@media (prefers-reduced-motion: reduce) {
  html.js .ls-list.bkx-in .ls-card,
  html.js .jp-result.bkx-built .jp-card,
  html.js .jp-result.bkx-built .jp-day,
  html.js .jp-result.bkx-built .jp-cmp,
  html.js .jhd__rows.bkx-in .jhd__row { animation: none !important; opacity: 1 !important; transform: none !important; }
  html.js .jp-result .jp-card,
  html.js .jp-result .jp-day,
  html.js .jp-result .jp-cmp { opacity: 1; }
  html.js .bkx-rise-r { opacity: 1 !important; transform: none !important; transition: none !important; }
  html.js .dhero__img, html.js .ghero__img, html.js .bkx-sky::after { animation: none !important; }
  html.js #ls-hotel-grid.bkx-in .jhc,
  html.js .jh-grid.bkx-in .jhc,
  html.js .jdh__grid.bkx-in .jdh-card { animation: none !important; opacity: 1 !important; transform: none !important; }
  html.js .jdh__grid .jdh-card { opacity: 1; }
  .jhc:hover, .jhc:focus-within, .jdh-card:hover, .jdh-card--pick,
  .jdh-card[data-jdh-on], .jhd__thumb { transform: none !important; filter: none !important; transition: none !important; }
  .ls-card:hover, .ls-card[data-bkx-picked] { transform: none; }
  .jp-days::before, .bkx-route { scale: 1 1 !important; transition: none !important; }
  .jp-meter__fill { transition: none !important; }
  .jhd__thumbs { scroll-behavior: auto; }
}

/* == THE COVERFLOW ========================================================
 *
 * The engine writes transform, opacity, z-index and `--cf-a` (the card's own
 * distance from the front, 0 at centre). Everything here is the LOOK, and
 * nothing here animates transform — bkx_coverflow.js is the only animation
 * authority for position, and a CSS transition on top of a spring is two
 * animations arguing about one property.
 *
 * WHAT THIS SHEET MUST NOT DO, learned by doing it:
 * a first version set `position: relative; z-index: 4` on every direct child
 * of a card so the text would sit above the picture. The deals renderer paints
 * its photograph with `<span class="jn-deal__img" style="background-image:…">`
 * positioned absolutely — so the blanket rule pulled the picture back into
 * flow and every card went black. The deck rendered, moved and centred
 * perfectly, with no images in it.
 *
 * So the picture is named explicitly, and nothing here touches "every child".
 */
.bkx-cf {
  position: relative;
  perspective: 1500px;
  perspective-origin: 50% 46%;
  touch-action: pan-y;               /* vertical page scroll still belongs to the page */
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  block-size: var(--cf-h, clamp(430px, 56vh, 560px));
  overflow: hidden;
}
.bkx-cf.is-dragging { cursor: grabbing; }
.bkx-cf:focus-visible { outline: 2px solid var(--x-gold, #E3BE79); outline-offset: 6px; border-radius: var(--x-r-lg, 22px); }

/* The track is only a positioning context: the cards are placed absolutely
   from the centre, because a flex row would fight the transforms for the left
   edge of every card. */
/*
 * SPECIFICITY, and why these selectors are three classes deep.
 *
 * The renderers style their own cards at TWO classes — `.bkx .jn-deal`
 * gives a fare card `position: relative`, `flex: 0 0 auto` and a fixed
 * 288px, and `.bkx .jn-deals__track` makes its container a flex scroller.
 * A single-class `.bkx-cf__item` loses to both, so the engine was writing
 * transforms onto cards that were still being LAID OUT by a flex row: the
 * deck moved, turned and lit correctly and was centred 431px off, because
 * `inset-inline-start: 50%` was resolving against a 288px card rather than
 * the 1440px frame.
 *
 * Three classes beats two whatever order the sheets load in, which matters
 * because this block is appended and a later edit could move it.
 */
.bkx-cf .bkx-cf__track {
  /*
   * THE TRACK MUST NOT CATCH THE POINTER, and this is not a nicety.
   *
   * Measured: elementFromPoint returned the TRACK at all fifteen points
   * across a side card's own box. Not one pixel of any card except the
   * centre one was hittable, so the headline interaction — hover a side
   * card, the deck turns — was dead for every reader, and so were the links
   * on those cards.
   *
   * The cause is `preserve-3d`: inside it, z-index does not decide stacking,
   * 3D POSITION does. The side cards are pushed back by translateZ, which
   * puts them behind their own parent's plane at z = 0 — and the parent
   * fills the frame, so it wins the hit test everywhere. Raising z-index on
   * the cards cannot fix it; the engine was already writing one.
   */
  pointer-events: none;
  position: absolute;
  display: block !important;
  overflow: visible;
  padding: 0;
  gap: 0;
  inset: 0;
  display: block;
  transform-style: preserve-3d;
}
.bkx-cf .bkx-cf__track .bkx-cf__item {
  /* …and the cards take it back. */
  pointer-events: auto;
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  margin-block-start: calc(var(--cf-card-h, 400px) / -2);
  margin-inline-start: calc(var(--cf-card-w, 300px) / -2);
  inline-size: var(--cf-card-w, 300px);
  block-size: var(--cf-card-h, 400px);
  transform-origin: 50% 58%;
  will-change: transform, opacity;
  border-radius: var(--x-r-lg, 22px);
  overflow: hidden;
  border: 1px solid var(--x-line, rgba(240,236,226,.10));
  background: var(--x-raised, #0C1119);
  box-shadow: 0 34px 76px -30px rgba(0, 0, 0, .96);
  text-decoration: none;
  /* NOT transform — see above. These two are the only properties the
     stylesheet is allowed to ease. */
  transition: box-shadow .34s var(--x-ease, cubic-bezier(.22,1,.36,1)),
              border-color .34s var(--x-ease, cubic-bezier(.22,1,.36,1));
}

/*
 * THE PICTURE, in whichever of the three shapes a renderer emits it: a real
 * <img>, the deals strip's background-image <span>, or the hotel row's.
 * Named rather than matched by position, so a renderer that adds a wrapper
 * does not silently lose its photograph.
 */
.bkx-cf__item > img,
.bkx-cf__item > .jn-deal__img,
.bkx-cf__item > .bkx-card__pic,
.bkx-cf__item > .jn-hot__img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  background-size: cover;
  background-position: center;
  z-index: 0;
  filter: saturate(calc(1 - var(--cf-a, 0) * .20)) brightness(calc(1 - var(--cf-a, 0) * .08));
}

/* The dimming is driven by the SAME number the geometry uses, so a card half
   way to the front is half way lit. */
.bkx-cf__item::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(4,7,12,.04) 0%, rgba(4,7,12,.06) 38%, rgba(4,7,12,.94) 100%),
    linear-gradient(rgba(4, 7, 12, calc(var(--cf-a, 0) * 0.24)), rgba(4, 7, 12, calc(var(--cf-a, 0) * 0.24)));
  transition: background-color .2s linear;
}
/* The card's own text sits above the veil. Named classes again, never `> *`. */
.bkx-cf__item > .jn-deal__body,
.bkx-cf__item > .bkx-card__body,
.bkx-cf__item > .jn-deal__where,
.bkx-cf__item > .jn-deal__meta,
.bkx-cf__item > .jn-deal__price,
.bkx-cf__item > .jn-deal__note,
.bkx-cf__item > .jn-deal__tag,
.bkx-cf__item > figcaption,
.bkx-cf__item > h3,
.bkx-cf__item > p,
.bkx-cf__item > span:not(.jn-deal__img):not(.bkx-card__pic):not(.jn-hot__img) { position: relative; z-index: 2; }

/* The front card is the primary object: champagne edge, its own light, no
   extra veil at all. */
.bkx-cf__item[data-cf-centre] {
  border-color: var(--x-gold, #E3BE79);
  box-shadow:
    0 44px 96px -28px rgba(0, 0, 0, .98),
    0 0 0 1px rgba(227, 190, 121, .34),
    0 22px 60px -22px rgba(227, 190, 121, .34);
}

/* The floor the deck stands on. */
.bkx-cf__floor {
  position: absolute;
  inset-block-end: 3%;
  left: 50%;                     /* physical, like the translate (RTL centring, 2026-09-12) */
  translate: -50% 0;
  inline-size: min(1180px, 92%);
  block-size: 30%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(56% 54% at 50% 100%,
    rgba(227, 190, 121, .20), rgba(227, 190, 121, .04) 46%, transparent 74%);
}

@media (max-width: 860px) {
  .bkx-cf { --cf-h: clamp(340px, 56vh, 460px); }
}
@media (prefers-reduced-motion: reduce) {
  .bkx-cf__item { transition: none !important; }
  .bkx-cf__item > img, .bkx-cf__item > .jn-deal__img { filter: none !important; }
  /*
   * THE DECK IS NOT BLANKED, IT IS FLATTENED.
   *
   * The old rail sat in flow, so `transform: none !important` was the right
   * reduced-motion answer for it and the suite pins that. A coverflow card is
   * positioned absolutely at the centre of its frame and placed by transform,
   * so the same rule would stack all eight cards on one spot — a page that
   * respects the preference by destroying the content.
   *
   * The flattening happens where the transform is WRITTEN instead:
   * bkx_coverflow.js sets rotateY and translateZ to zero under the same media
   * query, so a reader who asked for less motion gets a flat, still, readable
   * row and keeps every card. The spring is skipped there too — the deck moves
   * to its target in one frame rather than easing.
   *
   * The old guarantee still holds for every .bkx-card that is NOT in a deck.
   */
  .bkx-card:not(.bkx-cf__item) { transform: none !important; }
}

/* The picks deck sits in a taller frame than the homepage's destination
   cards: a hotel card carries a photograph, a name, a rating, amenities, a
   price and two actions, and squeezing that into a 400px portrait would make
   the deck unreadable at exactly the moment it is meant to be admired. */
.bkx-cf--picks {
  --cf-h: clamp(500px, 58vh, 620px);
  --cf-card-w: min(400px, 76vw);
  --cf-card-h: min(600px, 62vh);
  margin-block: clamp(10px, 2vh, 26px);
}
/*
 * The card keeps its OWN layout. A first version forced
 * `display:flex; flex-direction:column` on it, which collapsed the hotel
 * photograph into a 30px filmstrip: the renderer lays that card out as a grid
 * with a sized media row, and re-declaring the display threw the row sizing
 * away. The deck supplies a frame; what goes inside it is the renderer's.
 */
.bkx-cf--picks .jdh-card { overflow: auto; overscroll-behavior: contain; }
/* The front card is the only one a pointer may reach into: a card turned away
   is decoration, and letting a click land on it is how a reader books the
   wrong hotel. */
.bkx-cf--picks .jdh-card:not([data-cf-centre]) { pointer-events: none; }
.bkx-cf--picks .jdh-card[data-cf-centre] { pointer-events: auto; }
@media (max-width: 860px) {
  .bkx-cf--picks { --cf-h: clamp(500px, 74vh, 640px); --cf-card-w: min(320px, 84vw); }
}

/* == COVERFLOW · PASS 3 — A DECK, NOT A ROW =============================
 *
 * The note was "it reads as a row of portrait cards", and it did. The engine's
 * geometry is fixed in bkx_coverflow.js; these are the three things the
 * stylesheet owns that were also missing.
 */

/* 1. THE PERSPECTIVE WAS TOO FAR AWAY. At 1400px a 58-degree turn foreshortens
 *    by a few percent and reads as a slightly narrower card. At 900 the same
 *    turn is unmistakable, and the receding cards genuinely converge. */
.bkx-cf {
  perspective: 900px;
  perspective-origin: 50% 42%;
}

/* 2. SHADING THAT LEANS. A turned card in the world is lit unevenly: the edge
 *    swinging away from you goes dark, the near edge catches the light. `--cf-s`
 *    is the sign of the card's offset, so the gradient can point at whichever
 *    side is actually the far one. Without this, a rotated card is just a
 *    narrower rectangle — this is what makes the turn legible. */
.bkx-cf .bkx-cf__track .bkx-cf__item::after {
  background:
    linear-gradient(180deg, rgba(4,7,12,.04) 0%, rgba(4,7,12,.06) 38%, rgba(4,7,12,.94) 100%),
    linear-gradient(
      calc(90deg + var(--cf-s, 1) * 180deg),
      /* --cf-a runs to 2.6, so a 0.62 coefficient clamped the far edge of the
         outer cards to solid black and the deck lost its ends. These are priced
         against the RANGE, not against 1. */
      rgba(2, 4, 8, calc(var(--cf-a, 0) * 0.27)) 0%,
      rgba(2, 4, 8, calc(var(--cf-a, 0) * 0.07)) 48%,
      rgba(255, 246, 232, calc(var(--cf-a, 0) * 0.035)) 100%
    );
}

/* 3. THE FLOOR. The reference deck stands on something — there is a soft
 *    reflection and a pool of light under it. A deck floating on flat black is
 *    the other half of why it read as a row of tiles. */
.bkx-cf::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline: 8%;
  block-size: 24%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(46% 100% at 50% 0%, rgba(227, 190, 121, .16), transparent 70%),
    radial-gradient(70% 100% at 50% 0%, rgba(58, 96, 150, .10), transparent 72%);
  filter: blur(6px);
}

/* The front card carries a real cast shadow now, not just a glow — a lit object
   above a floor throws one, and its absence is subtle but reads as a sticker. */
.bkx-cf .bkx-cf__track .bkx-cf__item[data-cf-centre] {
  box-shadow:
    0 54px 90px -30px rgba(0, 0, 0, .98),
    0 0 0 1px rgba(227, 190, 121, .50),
    0 0 52px -8px rgba(227, 190, 121, .34),
    0 30px 76px -22px rgba(227, 190, 121, .30);
}


/* == MOBILE PASS · 2026-09-16 =============================================
 * Inner routes, measured live at 320-430px. Declared last on purpose.
 */

/* The browser's 8px body margin survived on /plan and /journal: header and
   content inset, journal cards lopsided (24px one side, 64px the other). */
body { margin: 0; }

@media (max-width: 900px) {
  /* The two-row phone header kept /search and /hotels' fixed 78px height, so
     the nav row spilled 21-25px below the bar and over #ls-form-chip. */
  .hd, .hd.scrolled { height: auto; min-block-size: 56px; }
  .hd.scrolled { inset-block-start: 0; inset-inline: 0; border-radius: 0; }
  .hd-nav { gap: 4px; }
  /* one first row everywhere: the brand at the start, the actions at the end
     (they sat beside the logo on most routes and at the far edge on two) */
  .hd .brand { margin-inline-end: auto; }

  /* Sections injected outside any padded wrapper met the screen edge. */
  .ft-in { padding-inline: clamp(16px, 4vw, 22px); }
  section.wrap[aria-labelledby="hpract-h"] { box-sizing: border-box; padding-inline: clamp(16px, 4vw, 22px); }
  .tip { margin-inline: clamp(16px, 4vw, 22px); }
}

/* /plan: the legend sat in the border notch and inputs 1px from the border
   once the console material added a border to a zero-padding fieldset. */
.jp-fieldset { padding: 14px; }
.jp-fieldset > .jp-legend { float: inline-start; inline-size: 100%; margin-block-end: 12px; }
.jp-fieldset > .jp-legend + * { clear: both; }

/* Journal index: the list kept the default 40px ul padding, and a 290px track
   minimum is wider than a 320px phone after gutters. The whole card is the
   link's target, not only its 21px-tall title. */
.jr-grid { padding-inline: 0; grid-template-columns: repeat(auto-fill, minmax(min(290px, 100%), 1fr)); }
.jr-card { position: relative; }
.jr-card h2 a::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.jr-card h2 a:focus-visible { outline: none; }
.jr-card:has(h2 a:focus-visible) { outline: 2px solid var(--x-gold, #E3BE79); outline-offset: 2px; }
.jr-links a { min-block-size: 44px; box-sizing: border-box; display: inline-flex; align-items: center; }

/* /hotels results: the full-width CTA was content-box, 35px wider than its
   rail, and its end was cut by the card on all 69 cards at 320-390. */
.ls-hotel-card__price .ls-card__cta, .ls-hotel-card__price .ls-card__cta--off,
.jhc__rail .ls-card__cta { box-sizing: border-box; max-inline-size: 100%; }
.ls-hotel-card__gal { min-block-size: 44px; }

/* Price calendar: 54px buttons in 42.9px columns overlapped in 39 pairs. */
.ls-cal__day { inline-size: 100%; padding-inline: 0; box-sizing: border-box; }

/* /search arriving with a search, on a phone: before the results exist the
   page is short, so the footer and the disclosure painted inside the first
   screen and were pushed out when the progress card appeared (CLS 0.131 on a
   4x-throttled phone, production 2026-09-16). The results area holds a screen
   of height from the start; search.html's fold script sets the attribute. */
html[data-ls-prefold] .ls-main { min-block-size: 100svh; }

/* /search on a phone: the folded-form chip wrapped to a second line once the
   real summary replaced its placeholder, moving the results 20-31px (measured
   CLS 0.06-0.17 after the fold-before-paint fix). One line, ellipsised, and off
   the screen edge. */
@media (max-width: 767px) {
  .ls-form-chip {
    /* An explicit width: a <button> does not stretch to its container the way
       a block does, so with nowrap and inline-size:auto the chip grew to its
       text and ran up to 74px off the screen edge at 320-390 (measured on
       production after rel_20260916183232, 2026-09-16). */
    --chip-gut: clamp(12px, 4vw, 16px);
    box-sizing: border-box;
    inline-size: calc(100% - 2 * var(--chip-gut));
    max-inline-size: calc(100% - 2 * var(--chip-gut));
    margin-inline: var(--chip-gut);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

/* The phone nav row: five links fit at 390px once the inherited 12px side
   padding (janet_hero_v4.css, /search and /hotels only) is trimmed, instead of
   the last one reading "מחיר" at the screen edge. Where they still do not fit
   (320-360) the row scrolls and its far edge fades, so a cut word reads as
   "more this way" and not as a broken label. */
@media (max-width: 900px) {
  .hd-nav { justify-content: space-between; }
  .hd-nav a { padding-inline: 6px; }
}
@media (max-width: 380px) {
  /*
   * THE WORDMARK FITS BESIDE THE LANGUAGE LINK IN BOTH FONTS (2026-09-17).
   *
   * Measured on production at 320px: the brand link is 214px wide in the system
   * font and 196px in Assistant. With the language link (65px) and the row gap
   * it exceeded the 288px of content box in the system font ONLY, so the header
   * wrapped to a third row until the brand font arrived and then unwrapped —
   * 48px of header height, taking the whole page with it: CLS 0.276 on
   * /en/hotels at 320px, intermittent (it depends on the font beating the first
   * paint). The Hebrew wordmark is shorter and never wrapped.
   *
   * Slightly less tracking and size on the wordmark at this width — and only at
   * this width — keeps the first row one row in both fonts, with room to spare
   * for a wider fallback on another platform. Nothing else about the mark
   * changes; measured after: 181px in the system font, 169px in Assistant.
   */
  .hd .brand-name { font-size: .92rem; letter-spacing: .12em; }
  .hd-nav {
    justify-content: flex-start;
    -webkit-mask-image: linear-gradient(to left, #000 85%, transparent);
            mask-image: linear-gradient(to left, #000 85%, transparent);
  }
  .hd-nav:dir(ltr) {
    -webkit-mask-image: linear-gradient(to right, #000 85%, transparent);
            mask-image: linear-gradient(to right, #000 85%, transparent);
  }
}
