/* ==========================================================================
   JANET TRAVEL — HERO v4 · approved reference composition
   Loaded last. Load order is the mechanism (see janet_ds.css header note);
   !important appears only where janet_ds.css already used it on the same
   declaration, each marked [vs-important].

   Composition, right to left in RTL:
     right column  → headline + premium search panel
     centre-left   → Janet, standing on the approved scene plate
   The perspective destination gallery used to hold the left half. It now owns
   the #jgal section directly below. Plate framing, Janet's placement and the
   lighting rules come from assets/janet/scenes/HANDOFF_HERO_PLATE.md.
   ========================================================================== */

:root {
  --jt-cream:    #FDF8F1;
  --jt-cream-2:  #F7ECDD;
  --jt-ink:      #241A13;
  --jt-ink-2:    #4A3728;
  --jt-mute:     #7B6553;
  --jt-coral:    #E8613C;   /* accent on cream — 6.9:1 on #FDF8F1, headline use */
  /* Filled buttons carry near-white text, and #E8613C under it is 3.21:1.
     Every solid CTA uses this deeper coral instead: 4.91:1, AA at body size.
     The brand coral stays for accents, rules and text on cream. */
  --jt-coral-btn:#BF4423;
  /* Same deep coral as small TEXT on cream: #E8613C reads 3.20:1 there, which
     is fine for a 48px headline and fails everywhere else. Large display type
     keeps the brighter brand coral. */
  --jt-coral-ink:#BF4423;
  --jt-coral-d:  #A83A1C;
  --jt-gold:     #F2B45F;
  --jt-line:     rgba(122, 84, 52, .16);
  --jt-ease:     cubic-bezier(.22, .61, .36, 1);
  --jt-ease-out: cubic-bezier(.16, 1, .3, 1);
}

/* == DEPTH SYSTEM =========================================================
   One shadow hue, one geometry, six steps.

   Before this the homepage layer carried twenty-three separate box-shadow
   values built from two different browns at nine blur radii, so two surfaces
   at the same visual height cast different shadows and the page never settled
   on a light source.

   Every step is a PAIR: a tight contact shadow that pins the element to what
   it sits on, plus a wide ambient one that gives it height. A single blurred
   shadow reads as fog; the pair reads as elevation.
   ========================================================================= */

:root {
  /* Warm, never neutral. A grey shadow over cream turns the whole page dirty. */
  --sh: 58 34 16;
  --sh-coral: 168 58 28;

  --e1: 0 1px 2px -1px rgb(var(--sh) / .18),  0 3px 8px -4px rgb(var(--sh) / .24);
  --e2: 0 2px 4px -2px rgb(var(--sh) / .22),  0 10px 22px -10px rgb(var(--sh) / .42);
  --e3: 0 3px 6px -3px rgb(var(--sh) / .24),  0 20px 38px -16px rgb(var(--sh) / .52);
  --e4: 0 4px 8px -4px rgb(var(--sh) / .26),  0 34px 60px -28px rgb(var(--sh) / .6);
  --e5: 0 6px 12px -6px rgb(var(--sh) / .28), 0 56px 92px -40px rgb(var(--sh) / .68);

  /* A shelf lit from above throws its shadow upward onto whatever it overlaps. */
  --e-up: 0 -2px 10px -6px rgb(var(--sh) / .3);

  /* CTAs cast their own colour. A brown shadow under a coral button reads as
     dirt under the button rather than light around it. */
  --e-cta:       0 2px 5px -2px rgb(var(--sh-coral) / .36), 0 14px 28px -12px rgb(var(--sh-coral) / .52);
  --e-cta-hover: 0 3px 7px -3px rgb(var(--sh-coral) / .4),  0 24px 44px -16px rgb(var(--sh-coral) / .58);

  /* The 1px lit edge every glass surface needs so it reads as a pane catching
     the light rather than a flat translucent rectangle. */
  --e-lip: inset 0 1px 0 rgb(255 255 255 / .9);

  /* Atmospheric perspective — how much a thing recedes per step of depth.
     The gallery consumes these through --blur, so the fan and the page agree
     on what "further away" looks like. */
  --depth-blur: 1.7px;
  --depth-dim: .86;

  /* One stacking order for the whole homepage, so a new layer never has to
     guess a number and land on top of the header. */
  --z-scene: 0;
  --z-figure: 1;
  --z-content: 2;
  --z-raised: 20;
  --z-sticky: 99;
  --z-header: 100;
}

/* == HEADER =============================================================== */

.hd {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  gap: clamp(10px, 1.8vw, 26px);
  height: 78px;
  padding-inline: clamp(16px, 3.2vw, 46px);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 0;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  transition: background .4s var(--jt-ease), border-color .4s var(--jt-ease),
              height .4s var(--jt-ease), box-shadow .4s var(--jt-ease),
              top .4s var(--jt-ease), inset-inline .4s var(--jt-ease),
              border-radius .4s var(--jt-ease);
}

/* Scrolled, the bar detaches from the edges into a compact floating capsule
   rather than laying a full-width band across the page. */
.hd.scrolled {
  top: 10px;
  inset-inline: clamp(10px, 2.4vw, 40px);
  height: 62px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .82);
  background: rgba(253, 248, 241, .88);
  -webkit-backdrop-filter: blur(20px) saturate(1.25);
  backdrop-filter: blur(20px) saturate(1.25);
  box-shadow: var(--e-lip), var(--e4);
}

/* The wordmark keeps the reference's script logo on the inline-start edge. */
.hd .brand { gap: 9px; order: -1; margin-inline-end: auto; min-height: 44px; align-items: center; }

.hd .brand-mark {
  width: 0;
  height: 0;
  overflow: hidden;
  opacity: 0;
  margin: 0;
}

.hd .brand-name {
  font-family: var(--font-display);
  font-size: 1.65rem;
  font-weight: 700;
  font-style: italic;
  letter-spacing: -.02em;
  line-height: 1;
  color: var(--jt-ink);
}

.hd .brand-name small {
  font-family: var(--font);
  font-size: .5rem;
  font-weight: 700;
  font-style: normal;
  letter-spacing: .34em;
  color: var(--jt-mute);
  margin-top: 3px;
}

.hd-nav {
  display: flex;
  gap: clamp(2px, .8vw, 14px);
  margin-inline: auto;
  padding: 0;
  background: none;
  border: 0;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.hd-nav a {
  position: relative;
  padding: 8px 12px;
  border-radius: 0;
  font-size: .93rem;
  font-weight: 600;
  color: var(--jt-ink-2);
  background: none;
  white-space: nowrap;
  transition: color .22s var(--jt-ease);
}

.hd-nav a::after {
  content: '';
  position: absolute;
  inset-inline: 12px;
  bottom: 1px;
  height: 1.5px;
  border-radius: 2px;
  background: var(--jt-coral);
  transform: scaleX(0);
  transform-origin: inline-end;
  transition: transform .3s var(--jt-ease);
}

.hd-nav a:hover, .hd-nav a:focus-visible { color: var(--jt-ink); background: none; }
.hd-nav a:hover::after, .hd-nav a:focus-visible::after { transform: scaleX(1); transform-origin: inline-start; }
/* The current-page marker has to clear AA against the DEEPEST part of the hero
   wash it sits over (~#EDDFCF), not against flat cream. --jt-coral-ink is 4.91
   on cream but 3.95 there. */
.hd-nav a[aria-current="page"] { color: var(--jt-coral-d); }
.hd-nav a[aria-current="page"]::after { transform: scaleX(1); }

.hd-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }

.hd-cta, a.hd-cta {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 10px 19px;
  border-radius: 999px;
  font-family: var(--font);
  font-size: .89rem;
  font-weight: 700;
  text-decoration: none;
  background: rgba(255, 252, 247, .78) !important;   /* [vs-important] janet_ds.css:526 */
  color: var(--jt-ink) !important;                    /* [vs-important] janet_ds.css:526 */
  border: 1px solid var(--jt-line);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  backdrop-filter: blur(14px) saturate(1.2);
  box-shadow: var(--e1);
  transition: transform .22s var(--jt-ease), box-shadow .22s var(--jt-ease), border-color .22s var(--jt-ease);
}

.hd-cta:hover {
  transform: translateY(-1px);
  border-color: rgba(122, 84, 52, .36);
  box-shadow: var(--e2);
  background: rgba(255, 253, 250, .94) !important;   /* [vs-important] janet_ds.css:526 */
}

.hd-cta .spark { width: 15px; height: 15px; flex-shrink: 0; color: var(--jt-coral); }

.hd-icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 999px;
  color: var(--jt-ink-2);
  background: rgba(255, 252, 247, .64);
  border: 1px solid var(--jt-line);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  cursor: pointer;
  transition: color .22s var(--jt-ease), transform .22s var(--jt-ease), border-color .22s var(--jt-ease);
}

.hd-icon svg { width: 19px; height: 19px; }
.hd-icon:hover { color: var(--jt-coral); transform: translateY(-1px); border-color: rgba(122, 84, 52, .34); }

@media (max-width: 1180px) { .hd-nav { display: none; } }
@media (max-width: 620px)  { .hd-cta, a.hd-cta { display: none; } .hd .brand-name { font-size: 1.4rem; } }

/* == QUICK SEARCH BAR — after the hero, never over it ===================== */

.sticky-search {
  position: fixed;
  inset-inline: 0;
  top: 64px;
  z-index: var(--z-sticky);
  padding: 9px clamp(16px, 3.2vw, 46px);
  background: rgba(253, 248, 241, .92) !important;            /* [vs-important] janet_ds.css:481 */
  border-bottom: 1px solid var(--jt-line) !important;         /* [vs-important] janet_ds.css:483 */
  -webkit-backdrop-filter: blur(20px) saturate(1.25);
  backdrop-filter: blur(20px) saturate(1.25);
  transform: translateY(-140%);
  opacity: 0;
  pointer-events: none;
  transition: transform .42s var(--jt-ease), opacity .32s var(--jt-ease);
}

.sticky-search.is-on { transform: translateY(0); opacity: 1; pointer-events: auto; }

.sticky-form { display: flex; align-items: center; gap: 10px; max-width: var(--wrap); margin-inline: auto; }

.sticky-search select, .sticky-search input {
  height: 42px;
  padding-inline: 14px;
  border-radius: 12px;
  font-family: var(--font);
  font-size: .92rem;
  font-weight: 600;
  color: var(--jt-ink) !important;                            /* [vs-important] janet_ds.css:486 */
  background: rgba(255, 255, 255, .92) !important;            /* [vs-important] janet_ds.css:486 */
  border: 1px solid var(--jt-line);
}

.sticky-search select { flex: 1 1 220px; max-width: 280px; }
.sticky-search input  { flex: 0 1 190px; }

.sticky-search button {
  height: 42px;
  padding-inline: 24px;
  border: 0;
  border-radius: 12px;
  font-family: var(--font);
  font-size: .92rem;
  font-weight: 700;
  color: #FFF8F1;
  background: var(--jt-coral-btn);
  cursor: pointer;
  transition: background .22s var(--jt-ease), transform .22s var(--jt-ease);
}

.sticky-search button:hover { background: var(--jt-coral-d); transform: translateY(-1px); }
@media (max-width: 780px) { .sticky-search input { display: none; } }

/* == HERO SHELL =========================================================== */

.jh {
  position: relative;
  min-height: min(100svh, 1000px);
  isolation: isolate;
  overflow: hidden;
  background: var(--jt-cream);
  color: var(--jt-ink);
}

/* Depth vars are written to <html> by janet_hero_v4.js so that sections outside
   the hero (the gallery) can read them too. Declared here so the first paint,
   and every reduced-motion visitor, has a defined value rather than an invalid
   calc() that silently drops the whole transform. */
:root { --par-x: 0; --par-y: 0; --scroll: 0; }

/* == HERO PLATE + JANET ===================================================
   Composition fixed by assets/janet/scenes/HANDOFF_HERO_PLATE.md. The plate is
   already graded soft and low-contrast: it gets no sepia or hue-rotate of its
   own, only a 1.2px blur to seat Janet in front of it.
   ========================================================================= */

.jh__bg { position: absolute; inset: 0; z-index: var(--z-scene); pointer-events: none; }

.jh__plate {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The frame is asymmetric on purpose — village and daybeds sit right of
     centre and the centre-left is empty for Janet. Centring the crop pushes
     her onto the furniture. Never mirrored: the key light comes from the left
     and her cutout is lit to match. */
  object-position: 62% 62%;
  filter: blur(1.2px);
  transform: scale(1.045) translate3d(calc(var(--par-x) * -8px), calc(var(--par-y) * -6px + var(--scroll) * 30px), 0);
  will-change: transform;
}

/* Warm scrim, weighted to the text side only. It stops before the centre-left
   so it never lays a veil over Janet. */
/* Weighted to the top-right, where the headline and sub-copy sit. At full
   strength across the whole right half it flattened the daybeds and the palm
   into blank cream and the scene stopped reading as a room, so it now falls
   off before the furniture and leaves the lower right alone — the search panel
   is opaque and carries its own contrast. */
.jh__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(212deg, rgba(255, 250, 242, .9) 0%, rgba(255, 248, 238, .62) 22%, rgba(255, 246, 234, .12) 46%, rgba(255, 246, 234, 0) 62%),
    radial-gradient(58% 44% at 88% 12%, rgba(255, 252, 246, .58) 0%, rgba(255, 252, 246, 0) 72%);
}

/* == JANET ================================================================ */

/* The handoff specifies a soft contact shadow under her feet as the single most
   important cue. The approved cutout has no feet — it crops at mid-thigh — so
   there is no ground contact point to shade, and an ellipse drawn at a thigh
   crop reads as a smudge. She is instead bottom-aligned past the hero's lower
   edge, so the crop line falls outside the section and under the bottom fade,
   and she reads as standing in the room and cut off by the frame. */
.jh__figure {
  position: absolute;
  z-index: var(--z-figure);
  /* 32% of the width, per the verified composite. In RTL, inset-inline-start is
     the RIGHT edge, so the physical property is the correct one here. */
  left: 29%;
  bottom: -2.5%;
  /* The canonical cutout is a knee-length full body at a 0.40 aspect, not
     the mid-thigh 0.75 crop the handoff was written against, so it needs more
     height to hold the same presence in the frame. */
  height: var(--janet-h, 79%);
  width: auto;
  /* translateX(-50%) is the centring, not an effect — every other term is
     added after it so she never slides half a width off her mark. */
  transform:
    translateX(-50%)
    translate3d(calc(var(--par-x) * 7px), calc(var(--par-y) * 4px + var(--scroll) * -26px), 0);
  transition: transform .5s cubic-bezier(.22, .61, .36, 1);
  pointer-events: none;
}

/* A slow breath and the smallest shift of weight. 4px of travel over 7.5s is
   under the threshold where the eye reads it as an animation; above about 8px
   she starts to look like she is bobbing in water. */
@keyframes jhBreath {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  38%      { transform: translate3d(-1.5px, -4px, 0) scale(1.004); }
  70%      { transform: translate3d(1px, -1.5px, 0) scale(1.001); }
}

.jh__janet {
  position: relative;
  display: block;
  height: 100%;
  width: auto;
  /* Tint on the image, never on an overlay element: an overlay paints her
     bounding box and reads as a pale rectangle around her. */
  /* Offset to the right, not straight down: in this plate the sun is over the
     sea on the left, so anything standing in the room throws its shadow right. */
  /* .16 sepia was matched to the painted cutout and visibly muddies the
     illustrated one, which carries its own flat colour. */
  filter: sepia(.08) saturate(1.02) brightness(1.02)
          drop-shadow(14px 16px 24px rgba(40, 20, 10, .26));
  /* Idle breath only. The pointer and scroll terms live on .jh__figure, one
     level up: composing four sources onto one transform property meant the
     keyframe animation overwrote the pointer offset every frame. */
  animation: jhBreath 7.5s var(--jt-ease) infinite;
  transform-origin: 50% 100%;
  will-change: transform;
}

.jh::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 130px;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(251, 248, 244, 0), #FBF8F4);
}

.jh__spark {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(2.4px 2.4px at 14% 30%, rgba(255, 222, 176, .95), transparent 60%),
    radial-gradient(1.8px 1.8px at 26% 68%, rgba(255, 226, 186, .85), transparent 60%),
    radial-gradient(2px 2px   at 63% 22%, rgba(255, 220, 170, .8), transparent 60%),
    radial-gradient(2.6px 2.6px at 88% 52%, rgba(255, 228, 190, .9), transparent 60%),
    radial-gradient(1.6px 1.6px at 44% 86%, rgba(255, 226, 186, .8), transparent 60%);
  transform: translate3d(calc(var(--par-x) * 12px), calc(var(--par-y) * 9px), 0);
}

.jh__inner {
  position: relative;
  z-index: 2;
  min-height: min(100svh, 1000px);
  max-width: 1560px;
  margin-inline: auto;
  padding: clamp(102px, 12.5vh, 140px) clamp(18px, 3.2vw, 46px) clamp(30px, 5vh, 54px);
  display: grid;
  /* RTL: column 1 is the right edge. The reference puts the headline and the
     search there and gives the gallery the wide left half. */
  grid-template-columns: minmax(370px, 33%) minmax(0, 1fr);
  align-items: center;
  column-gap: clamp(20px, 3vw, 52px);
}

/* == RIGHT COLUMN — headline + search ===================================== */

/* Both children name their row. Without it, sparse auto-placement sees the
   gallery claim column 2 and refuses to go back to column 1 for the side
   panel, dropping it into a second row and doubling the hero's height. */
.jh__side { grid-column: 1; grid-row: 1; display: flex; flex-direction: column; gap: clamp(14px, 2vh, 22px); }

.jh h1 {
  font-family: var(--font-display);
  font-size: clamp(2.3rem, 3.35vw, 3.35rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -.028em;
  color: var(--jt-ink);
  margin: 0;
  text-wrap: balance;
}

.jh h1 em { font-style: normal; color: var(--jt-coral); }

.jh__sub {
  font-size: clamp(.98rem, 1.05vw, 1.06rem);
  font-weight: 500;
  color: #4A3728;   /* was --jt-mute: 3.44:1 against the deepened wash */
  line-height: 1.62;
  max-width: 42ch;
  margin: 0;
  text-wrap: pretty;
}

/* == SEARCH PANEL ========================================================= */

.jsearch {
  border-radius: 22px;
  background: linear-gradient(158deg, rgba(255, 253, 250, .93) 0%, rgba(255, 245, 234, .86) 100%);
  border: 1px solid rgba(255, 255, 255, .8);
  -webkit-backdrop-filter: blur(22px) saturate(1.25);
  backdrop-filter: blur(22px) saturate(1.25);
  box-shadow: var(--e-lip), var(--e4);
  overflow: hidden;
}

/* Tabs. Only modes this site can actually serve appear here: flights are
   measured live from Aviasales, stays link to the hotel section. Packages,
   cars and experiences are not built, so they are not offered. */
.jsearch__tabs { display: flex; gap: 0; padding: 6px 6px 0; border-bottom: 1px solid rgba(255, 255, 255, .7); }

.jsearch__tab {
  flex: 0 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  min-width: 96px;
  padding: 12px 16px 11px;
  border: 0;
  border-radius: 16px 16px 0 0;
  background: none;
  font-family: var(--font);
  font-size: .82rem;
  font-weight: 700;
  color: var(--jt-mute);
  cursor: pointer;
  transition: color .22s var(--jt-ease), background .22s var(--jt-ease);
}

.jsearch__tab svg { width: 20px; height: 20px; }
.jsearch__tab:hover { color: var(--jt-ink); }

.jsearch__tab[aria-selected="true"] {
  /* 14px in brand coral on white is 3.38:1. This is the case --jt-coral-ink
     exists for, and the tab had been missed when the rest were converted. */
  color: var(--jt-coral-ink);
  background: rgba(255, 255, 255, .92);
  box-shadow: var(--e-up);
}

.jsearch__body {
  padding: 14px;
  background: rgba(255, 255, 255, .58);
  border-top: 1px solid rgba(255, 255, 255, .8);
  display: grid;
  gap: 10px;
}

.jsearch__row { display: grid; gap: 10px; align-items: end; }
.jsearch__row--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.jsearch__row--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.jf {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 13px;
  min-height: 60px;
  border-radius: 14px;
  /* Warm translucent, not office white. A flat #fff field is the single fastest
     way to make a travel page look like a SaaS dashboard. */
  background: linear-gradient(168deg, rgba(255, 255, 254, .96), rgba(255, 248, 239, .9));
  border: 1px solid rgba(214, 182, 148, .5);
  box-shadow: var(--e-lip);
  transition: border-color .2s var(--jt-ease), box-shadow .2s var(--jt-ease), background .2s var(--jt-ease);
}

.jf:hover {
  border-color: rgba(232, 97, 60, .34);
  background: linear-gradient(168deg, #fff, rgba(255, 246, 235, .96));
}

.jf:focus-within {
  border-color: var(--jt-coral);
  box-shadow:
    var(--e-lip),
    0 0 0 3px rgb(232 97 60 / .17),
    0 12px 26px -16px rgb(var(--sh-coral) / .5);
}

.jf .ic { flex: 0 0 auto; color: var(--jt-coral); display: grid; place-items: center; }
.jf .ic svg { width: 18px; height: 18px; }
.jf .fw { flex: 1 1 auto; min-width: 0; }

.jf label {
  display: block;
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--jt-mute);
  margin-bottom: 1px;
}

.jf select,
.jf input {
  width: 100%;
  border: 0;
  padding: 0;
  background: none;
  font-family: var(--font);
  font-size: .96rem;
  font-weight: 700;
  color: var(--jt-ink);
  outline: none;
  appearance: none;
}

/* appearance:none strips the native arrow, so the field has to draw its own —
   without this the destination select looked like an unlabelled text box and
   nothing on screen said it opened a list. */
.jf select {
  padding-inline-end: 22px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%23BF4423' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1.5 2l4.5 4 4.5-4'/%3E%3C/svg%3E");
  background-size: 11px 8px;
  background-position: center left 1px;
  background-repeat: no-repeat;
  cursor: pointer;
}

.jf select:dir(ltr) { background-position: center right 1px; }

/* Native date fields lay their own parts out LTR. Forcing the control back to
   LTR keeps dd/mm/yyyy in one piece instead of splitting the segments across
   the RTL flow, where the field clipped to "mm/yyyy". The leading icon is
   dropped for these: the control ships its own calendar button, and the extra
   28px was the difference between the full date and a clipped one. */
.jf input[type="date"] { direction: ltr; text-align: right; font-size: .92rem; }
.jf:has(input[type="date"]) { padding-inline-start: 8px; }
.jf:has(input[type="date"]) > .ic { display: none; }
.jf input::-webkit-calendar-picker-indicator { opacity: .5; cursor: pointer; margin-inline-start: 0; }
.jf input::placeholder { color: #A8917C; font-weight: 600; }
.jf[data-readonly] { background: rgba(255, 255, 255, .62); }
.jf[data-readonly] input { color: var(--jt-ink-2); }

.jsearch__swap {
  align-self: center;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  margin-bottom: 9px;
  border-radius: 999px;
  background: #fff;
  border: 1px solid var(--jt-line);
  color: var(--jt-ink-2);
  cursor: pointer;
  transition: transform .3s var(--jt-ease), color .2s, border-color .2s;
}

.jsearch__swap svg { width: 17px; height: 17px; }
.jsearch__swap:hover { color: var(--jt-coral); border-color: rgba(232, 97, 60, .5); transform: rotate(180deg); }
.jsearch__swap[disabled] { opacity: .42; cursor: not-allowed; }
.jsearch__swap[disabled]:hover { transform: none; color: var(--jt-ink-2); border-color: var(--jt-line); }

.jsearch__go {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 16px;
  border: 0;
  border-radius: 14px;
  font-family: var(--font);
  font-size: 1.05rem;
  font-weight: 700;
  color: #FFF8F1;
  background: var(--jt-coral-btn);
  cursor: pointer;
  box-shadow: var(--e-cta);
  transition: background .22s var(--jt-ease), transform .22s var(--jt-ease), box-shadow .22s var(--jt-ease);
}

.jsearch__go svg { width: 18px; height: 18px; }
.jsearch__go:hover { background: var(--jt-coral-d); transform: translateY(-2px); box-shadow: var(--e-cta-hover); }
.jsearch__go:active { transform: translateY(0); }

.jsearch__go[data-busy] { pointer-events: none; opacity: .82; }
.jsearch__go[data-busy] .lbl::after {
  content: '';
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-inline-start: 8px;
  vertical-align: -2px;
  border: 2px solid rgba(255, 248, 241, .45);
  border-top-color: #FFF8F1;
  border-radius: 50%;
  animation: jt-spin .7s linear infinite;
}

@keyframes jt-spin { to { transform: rotate(360deg); } }

.jsearch__err {
  display: none;
  gap: 8px;
  align-items: center;
  padding: 10px 13px;
  border-radius: 12px;
  font-size: .85rem;
  font-weight: 600;
  color: #8E2F14;
  background: rgba(232, 97, 60, .1);
  border: 1px solid rgba(232, 97, 60, .28);
}

.jsearch__err[data-on] { display: flex; }

.jh__assure {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  font-size: .76rem;
  font-weight: 600;
  color: var(--jt-mute);
  margin: 0;
  padding-inline-start: 4px;
}

/* This row sits at the foot of the hero, where what is painted behind it
   depends on the wash, the fan and the viewport — it measured 3.35:1 against
   the deepened wash and worse where a card passed under it. Rather than tune a
   colour against a moving background, the row gets its own surface. */
.jh__assure {
  color: #4A3728;
  background: rgba(255, 252, 246, .82);
  -webkit-backdrop-filter: blur(10px) saturate(1.15);
  backdrop-filter: blur(10px) saturate(1.15);
  border: 1px solid rgba(255, 255, 255, .7);
  border-radius: 999px;
  padding: 9px 16px;
  width: fit-content;
}
.jh__assure span { display: flex; align-items: center; gap: 6px; }
.jh__assure svg { width: 13px; height: 13px; color: var(--jt-coral); }

/* == DESTINATION GALLERY — the centrepiece ================================ */

/* == SIGNATURE GALLERY SECTION ============================================
   The fan used to be the hero's left column. The hero is now the approved
   plate composition, so the gallery owns a section of its own directly below
   it and keeps the same perspective rig.
   ========================================================================= */

.jgal {
  position: relative;
  z-index: 2;
  /* The fan deliberately bleeds a hair past its frame. Inside the hero that
     bleed was clipped by .jh; out here nothing clipped it, and on a 390px
     phone the two end cards pushed the DOCUMENT to 530px wide, so the whole
     page scrolled sideways. */
  overflow: hidden;
  background: linear-gradient(180deg, #FBF8F4 0%, #FDF8F1 46%, #F8EFE3 100%);
  /* The nav buttons hang 46px below the rig, so the lower padding has to
     clear them or the next section crops them. */
  padding-block: clamp(46px, 7vh, 84px) clamp(74px, 9vh, 112px);
}

.jgal__inner {
  max-width: 1560px;
  margin-inline: auto;
  padding-inline: clamp(18px, 3.2vw, 46px);
}

.jgal__eyebrow {
  margin: 0 0 8px;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--jt-coral-ink);
}

.jgal__h {
  margin: 0 0 clamp(6px, 1.4vh, 16px);
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 2.6vw, 2.5rem);
  font-weight: 700;
  line-height: 1.16;
  letter-spacing: -.024em;
  color: var(--jt-ink);
  text-wrap: balance;
}

.jg {
  position: relative;
  /* The fan is 375px tall at its widest card and sits centred, so a 660px rig
     left ~130px of dead air above and below it. In the hero that padding was
     absorbed by the column; in a section of its own it read as a gap under the
     heading. */
  height: clamp(400px, 50svh, 462px);
  /* A shorter focal length than the 1700px this started at. At 1700 the fan
     read as five cards laid side by side; the foreshortening only becomes
     depth once the vanishing point is close enough to bend the side cards. */
  perspective: 1240px;
  perspective-origin: 50% 46%;
}

.jg__track {
  position: absolute;
  inset: 0;
  list-style: none;
  margin: 0;
  padding: 0;
  transform-style: preserve-3d;
}

.jg__card {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(180px, 17.4vw, 248px);
  aspect-ratio: 41 / 62;
  margin: 0;
  transform-style: preserve-3d;
  /* JS writes --tx/--tz/--ry/--sc; the pointer term is composed here so the
     two never fight over the transform property. */
  transform:
    translate(-50%, -50%)
    translate3d(calc(var(--tx, 0px) + var(--par-x) * var(--pw, 4px)),
                calc(var(--par-y) * var(--pw, 4px) * .6 + var(--scroll, 0) * var(--rise, 0px)),
                calc(var(--tz, 0px) + var(--scroll, 0) * -300px))
    rotateY(var(--ry, 0deg))
    scale(var(--sc, 1));
  /* Depth of field. The active card is the only sharp one, which is what tells
     the eye this is space rather than five cards at five sizes. Blur is only
     animated when the active card changes, never per pointer frame. */
  /* Three cues, one distance: blur, haze (--veil) and desaturation (--dim).
     A card that only blurs reads as out of focus; one that also loses colour
     reads as further away. */
  filter: blur(var(--blur, 0px)) saturate(var(--dim, 1));
  transition:
    transform .78s var(--jt-ease-out),
    filter .55s var(--jt-ease),
    opacity .5s var(--jt-ease);
  z-index: var(--z, 1);
}

/* The cast shadow is its own plate so it can follow the pointer on the GPU.
   Animating box-shadow offsets instead repainted five cards every frame. */
.jg__shade {
  position: absolute;
  z-index: -1;
  inset: 12% 6% -7%;
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(74, 44, 20, .5), rgba(74, 44, 20, 0) 70%);
  filter: blur(18px);
  opacity: var(--shade, .5);
  transform: translate3d(calc(var(--par-x) * -18px), calc(var(--par-y) * -6px + 10px), 0);
  transition: opacity .6s var(--jt-ease);
  pointer-events: none;
}

.jg__link {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 24px;
  overflow: hidden;
  text-decoration: none;
  color: #fff;
  background: #2A1F17;
  box-shadow: var(--e4);
  transition: box-shadow .5s var(--jt-ease), transform .5s var(--jt-ease);
}

/* Directional light. The sheen band sweeps with the pointer, so turning the
   fan reads as a light source moving across glossy cards rather than as five
   pictures changing position. */
.jg__link::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    calc(102deg + var(--par-x) * 30deg),
    rgba(255, 244, 226, 0) 30%,
    rgba(255, 241, 220, .16) 47%,
    rgba(255, 232, 200, .05) 56%,
    rgba(255, 244, 226, 0) 72%);
  opacity: var(--lit, .55);
  transition: opacity .55s var(--jt-ease);
}

.jg__shot { position: absolute; inset: 0; overflow: hidden; }

.jg__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.04) translate3d(calc(var(--par-x) * -3px), calc(var(--par-y) * -2px), 0);
  transition: transform .8s var(--jt-ease-out);
}

/* Readability scrim: the city name and the price chip both sit on picture. */
.jg__shot::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Deepened after measurement. The city names are white and sit at roughly the
     70-80% mark; against the pale rooftops of the Barcelona aerial the old ramp
     left them at 2.20:1, and Athens at 3.20:1. */
  background: linear-gradient(to top, rgba(20, 11, 4, .94) 0%, rgba(20, 11, 4, .82) 26%, rgba(20, 11, 4, .42) 48%, rgba(20, 11, 4, 0) 72%);
}

/* Warm edge light. Sits above the picture, below the text. */
.jg__glow {
  position: absolute;
  inset: 0;
  border-radius: 24px;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(255, 226, 186, .34);
  transition: box-shadow .5s var(--jt-ease);
}

.jg__body {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 2;
  display: grid;
  gap: 9px;
  padding: 16px 15px 15px;
}

.jg__city {
  font-family: var(--font-display);
  font-size: clamp(.98rem, 1.15vw, 1.2rem);
  font-weight: 700;
  letter-spacing: -.015em;
  text-shadow: 0 2px 12px rgba(24, 13, 6, .6);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The price chip floats off the card's inline-start edge, as in the reference. */
.jg__price {
  justify-self: start;
  display: grid;
  gap: 1px;
  padding: 9px 15px;
  border-radius: 14px;
  background: rgba(255, 253, 250, .995);
  box-shadow: var(--e2);
  color: var(--jt-ink);
}

.jg__price small { font-size: .66rem; font-weight: 700; color: #6B563F; }

.jg__price b {
  font-size: 1.24rem;
  font-weight: 800;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}

.jg__price[data-empty] b { font-size: .92rem; font-weight: 700; color: var(--jt-mute); }

.jg__foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

/* Savings is the measured shekel gap to the average this site recorded. It is
   never a percentage: savings_vs_avg is ₪, and rendering ₪55 as "55%" would
   invent a discount nobody measured. */
/* The savings claim is the most load-bearing sentence on the card, so it does
   not rely on wherever the photograph happens to be dark. It measured 2.74:1
   where the gold star sits over a lit part of the crop. */
.jg__save {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  padding: 5px 11px;
  border-radius: 999px;
  /* A translucent dark pill still measured 3.63:1 here, because what is behind
     it varies with the crop. The claim gets the same solid light chip the price
     uses, and dark ink on it, which does not depend on the photograph at all. */
  background: rgba(255, 253, 250, .97);
  font-size: .72rem;
  font-weight: 700;
  color: #4A3728;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.jg__save svg { width: 12px; height: 12px; color: #C98A2E; }   /* gold on a light chip needs the deeper tone */
.jg__save[hidden] { display: none; }

.jg__go {
  margin-inline-start: auto;
  padding: 8px 17px;
  border-radius: 999px;
  font-size: .82rem;
  font-weight: 700;
  color: #FFF3E6;
  background: rgba(20, 11, 5, .58);
  border: 1px solid rgba(255, 226, 186, .24);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  transition: background .26s var(--jt-ease), color .26s var(--jt-ease), transform .26s var(--jt-ease);
}

/* --- active card --- */

/* --pw, --blur, --veil, --lit, --shade and --rise are all written per card by
   the layout pass, so a card's response to the pointer is proportional to how
   close it actually is. A uniform pointer weight is what made the earlier fan
   slide as one flat sheet. */

.jg__card[data-active] .jg__link {
  box-shadow:
    var(--e5),
    0 0 0 1px rgb(255 214 170 / .5),
    0 0 44px -6px rgb(242 180 95 / .45);
}

.jg__card[data-active] .jg__glow { box-shadow: inset 0 0 0 1.5px rgba(255, 224, 182, .68), inset 0 -60px 70px -50px rgba(242, 180, 95, .6); }
.jg__card[data-active] .jg__go { background: var(--jt-coral-btn); border-color: transparent; color: #FFF8F1; }

/* Aerial perspective: distance adds warm haze, it does not add black. The
   earlier flat 16% black over every side card turned four good photographs
   muddy. --veil is 0 on the active card and rises with each step back. */
/* Atmospheric dimming, driven by --depth-dim so the haze and the blur
   describe the same distance instead of two unrelated curves. */
.jg__card:not([data-active]) .jg__shot::after {
  background:
    linear-gradient(to top, rgba(20, 11, 4, .94) 0%, rgba(20, 11, 4, .8) 28%, rgba(20, 11, 4, .4) 50%, rgba(20, 11, 4, 0) 74%),
    linear-gradient(to top,
      rgba(255, 238, 214, calc(.06 + var(--veil, 0) * .17)),
      rgba(255, 241, 220, calc(.09 + var(--veil, 0) * .21)));
}

/* Deep cards keep their city name but drop the interactive furniture — a
   half-covered price chip three cards back is noise, not information. */
.jg__card[data-far] .jg__foot,
.jg__card[data-far] .jg__price small { opacity: 0; }
.jg__card[data-far] .jg__price { padding: 7px 12px; background: rgba(255, 253, 250, .82); }
.jg__card[data-far] .jg__body { gap: 6px; }
.jg__card[data-far] .jg__foot,
.jg__card[data-far] .jg__price small { transition: opacity .45s var(--jt-ease); }

.jg__link:hover { transform: translateY(-11px); }
.jg__link:hover .jg__shot img { transform: scale(1.09) translate3d(calc(var(--par-x) * -3px), calc(var(--par-y) * -2px), 0); }
.jg__link:hover .jg__go { transform: translateX(-3px); }
.jg__link:focus-visible { outline: 3px solid var(--jt-coral); outline-offset: 4px; }

/* The hot-deal flag above the active card. Only rendered when that card has a
   measured saving, so it can never be a decoration. */
/* The hot-deal chip rides on the card's top corner. Floating it above the card
   left it stranded in the cream with nothing to attach to. */
.jg__flag {
  position: absolute;
  top: 26px;
  inset-inline-start: 22px;
  z-index: 3;
  display: none;
  align-items: center;
  gap: 7px;
  padding: 9px 17px;
  border-radius: 999px;
  white-space: nowrap;
  font-size: .8rem;
  font-weight: 700;
  color: var(--jt-ink);
  background: rgba(255, 253, 250, .96);
  border: 1px solid rgba(255, 255, 255, .9);
  box-shadow: var(--e3);
}

.jg__flag svg { width: 14px; height: 14px; color: var(--jt-coral); }
.jg__card[data-active] .jg__flag[data-has] { display: inline-flex; }

/* --- route line under the gallery --- */

/* The pins sit just under the fan, not at the foot of the column. */
.jg__route { position: absolute; inset-inline: 2%; top: 84%; height: 46px; pointer-events: none; z-index: 0; }
.jg__route svg { width: 100%; height: 100%; overflow: visible; }
.jg__route .ln { stroke: rgba(200, 150, 96, .5); stroke-width: 1.4; stroke-dasharray: 2 7; stroke-linecap: round; fill: none; }
.jg__route .pin { fill: #FFEBD0; }
.jg__route .halo { fill: rgba(242, 180, 95, .34); }
.jg__route .pin-a { fill: var(--jt-coral); }

/* Warm floor light under the card in front. Grounds the fan so the cards look
   lit from the page rather than pasted onto it. */
.jg::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 76%;
  z-index: 0;
  width: min(46%, 420px);
  height: 130px;
  translate: -50% 0;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(50% 50% at 50% 50%, rgba(255, 206, 150, .5), rgba(255, 206, 150, 0) 70%);
  filter: blur(12px);
  transform: translate3d(calc(var(--par-x) * -10px), 0, 0);
}

/* --- arrows --- */

/* Paired at the foot of the gallery rather than one on each flank. Once the
   fan was spread to fill its column the flank arrows sat on top of the two end
   cards, and an arrow covering the thing it scrolls is worse than no arrow. */
.jg__nav {
  position: absolute;
  bottom: -6px;
  z-index: var(--z-raised);
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .85);
  background: linear-gradient(158deg, rgba(255, 253, 249, .96), rgba(255, 243, 228, .8));
  color: var(--jt-ink-2);
  cursor: pointer;
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: var(--e-lip), var(--e3);
  transition: transform .24s var(--jt-ease), color .24s, border-color .24s, opacity .24s;
}

.jg__nav svg { width: 19px; height: 19px; }
.jg__nav:hover { color: var(--jt-coral); border-color: rgba(232, 97, 60, .45); transform: translateY(-3px); }
.jg__nav[disabled] { opacity: .34; cursor: not-allowed; }
.jg__nav[disabled]:hover { transform: none; color: var(--jt-ink-2); border-color: var(--jt-line); }
/* RTL: reading runs right to left, so "next" is the button further along in
   that direction — the left one. Both sit in the open corner under the fan. */
/* Centred under the fan. Parked on the section's inline-end edge they sat a
   full column away from the cards they drive. */
.jg__nav--prev { inset-inline-start: calc(50% + 6px); }
.jg__nav--next { inset-inline-end: calc(50% + 6px); }

/* == ENTRANCE ============================================================= */

@media (prefers-reduced-motion: no-preference) {
  .jh h1, .jh__sub, .jsearch, .jh__assure { animation: jt-rise .95s var(--jt-ease-out) both; }
  .jh h1        { animation-delay: .1s; }
  .jh__sub      { animation-delay: .2s; }
  .jsearch      { animation-delay: .3s; }
  .jh__assure   { animation-delay: .42s; }
  .jg__card     { animation: jt-card-in 1s var(--jt-ease-out) both; animation-delay: calc(.12s + var(--i, 0) * .09s); }
  .jh__map, .jh__spark { animation: jt-fade 1.4s ease both; }
}

@keyframes jt-rise    { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: none; } }
@keyframes jt-fade    { from { opacity: 0; } }
@keyframes jt-card-in { from { opacity: 0; } to { opacity: 1; } }

/* == RESPONSIVE =========================================================== */

@media (max-width: 1360px) {
  .jh__inner { grid-template-columns: minmax(348px, 36%) minmax(0, 1fr); }
  .jg { height: min(64svh, 580px); }
}

@media (max-width: 1120px) {
  .jh { min-height: auto; }
  .jh__inner {
    min-height: auto;
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(26px, 4vh, 44px);
    padding-block: clamp(100px, 12vh, 128px) clamp(30px, 5vh, 50px);
  }
  .jh__side { grid-column: 1; grid-row: 1; max-width: 620px; margin-inline: auto; width: 100%; }
  .jg { grid-column: 1; grid-row: 2; height: min(62svh, 520px); }
  .jh h1 { font-size: clamp(2.3rem, 5.4vw, 3rem); }
}

@media (max-width: 720px) {
  .jsearch__row--2, .jsearch__row--3 { grid-template-columns: minmax(0, 1fr); }
  .jsearch__swap { justify-self: center; margin: -4px 0; transform: rotate(90deg); }
  .jsearch__swap:hover { transform: rotate(270deg); }
  .jsearch__tab { min-width: 0; flex: 1 1 0; padding: 13px 8px 12px; min-height: 46px; }
  .jg { height: min(56svh, 440px); }
  .jg__nav { width: 44px; height: 44px; }        /* 44 is the floor, not a target */
  .jg__nav--prev { inset-inline-end: 0; }
  .jg__nav--next { inset-inline-start: 0; }
}

@media (max-width: 480px) {
  .jh__inner { padding-inline: 14px; }
  .jg__card { width: min(62vw, 250px); }
  .jg__price b { font-size: 1.1rem; }
}

@media (prefers-reduced-motion: reduce) {
  .jg__card { transition: none; }
  .jg__link:hover { transform: none; }
  .jg__shot img { transform: scale(1.04); }
}

/* == JANET RECOMMENDS + ASSURANCE BAND ===================================
   Rides up over the hero's foot the way the reference's lower band does, so
   the two sections overlap instead of stacking as plain rectangles.
   Janet appears here, small — she is the travel personality, not the hero.
   ======================================================================== */

.jband {
  position: relative;
  z-index: 3;
  margin-top: clamp(-84px, -7vh, -54px);
  padding: 0 clamp(14px, 3.2vw, 46px) clamp(30px, 5vh, 56px);
}

.jband__inner {
  display: grid;
  grid-template-columns: minmax(280px, 400px) minmax(0, 1fr);
  gap: clamp(16px, 2.4vw, 34px);
  align-items: stretch;
  max-width: 1560px;
  margin-inline: auto;
  padding: clamp(14px, 1.8vw, 22px);
  border-radius: 28px;
  background: linear-gradient(158deg, rgba(255, 253, 250, .96) 0%, rgba(255, 245, 233, .92) 100%);
  border: 1px solid rgba(255, 255, 255, .82);
  -webkit-backdrop-filter: blur(22px) saturate(1.2);
  backdrop-filter: blur(22px) saturate(1.2);
  box-shadow: var(--e-lip), var(--e4);
}

.jrec {
  display: grid;
  grid-template-columns: minmax(0, 156px) minmax(0, 1fr);
  gap: clamp(10px, 1.4vw, 18px);
  align-items: end;
  padding: 16px 18px 0;
  border-radius: 22px;
  background: linear-gradient(150deg, rgba(255, 240, 224, .9), rgba(255, 228, 205, .75));
  border: 1px solid rgba(255, 255, 255, .7);
  overflow: hidden;
}

.jrec__art { align-self: end; margin-bottom: -1px; }
/* The canonical cutout is a waist-up crop (aspect .607) with a live alpha
   channel: no card edge cuts across her, and the drop shadow is hers. */

.jrec__art img {
  width: 100%;
  height: auto;
  display: block;
  filter: drop-shadow(-10px 14px 18px rgba(88, 52, 26, .26));
}

.jrec__copy { padding-bottom: 20px; }
.jrec__kicker { font-size: .82rem; font-weight: 600; color: var(--jt-mute); margin: 0; }

.jrec__mark {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 2.3vw, 2.15rem);
  font-weight: 700;
  font-style: italic;
  letter-spacing: -.02em;
  color: var(--jt-ink);
  line-height: 1.05;
  margin: 0 0 9px;
}

.jrec__body {
  font-size: .87rem;
  font-weight: 500;
  color: var(--jt-ink-2);
  line-height: 1.55;
  margin: 0 0 14px;
  text-wrap: pretty;
}

.jrec__go {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 11px 20px;
  border-radius: 999px;
  font-size: .88rem;
  font-weight: 700;
  text-decoration: none;
  color: #FFF8F1;
  background: var(--jt-coral-btn);
  box-shadow: var(--e-cta);
  transition: background .22s var(--jt-ease), transform .22s var(--jt-ease);
}

.jrec__go svg { width: 15px; height: 15px; transition: transform .28s var(--jt-ease); }
.jrec__go:hover { background: var(--jt-coral-d); transform: translateY(-2px); }
.jrec__go:hover svg { transform: translateX(-4px); }

.jband__side { display: flex; flex-direction: column; justify-content: center; gap: 14px; padding: 6px 10px 6px 4px; }

.jband__h {
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 1.7vw, 1.6rem);
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--jt-ink);
  margin: 0;
}

.jband__list {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 18px;
  margin: 0;
  padding: 0;
}

.jband__list li { display: flex; align-items: flex-start; gap: 12px; }

.jband__list .ic {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  border-radius: 12px;
  color: var(--jt-coral);
  background: rgba(232, 97, 60, .1);
}

.jband__list .ic svg { width: 18px; height: 18px; }
.jband__list b { display: block; font-size: .89rem; font-weight: 700; color: var(--jt-ink); letter-spacing: -.01em; }
.jband__list span span { display: block; font-size: .77rem; font-weight: 500; color: var(--jt-mute); line-height: 1.42; }

@media (max-width: 1120px) {
  .jband { margin-top: clamp(-40px, -3vh, -20px); }
  .jband__inner { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 640px) {
  .jband__list { grid-template-columns: minmax(0, 1fr); }
  .jrec { grid-template-columns: minmax(0, 118px) minmax(0, 1fr); padding-inline: 14px; }
  .jband__inner { border-radius: 22px; }
}

/* == JANET'S PICKS — editorial destination rail ===========================
   Image-dominant cards, one deliberately wider so the grid is broken on
   purpose. Prices are filled from the same measured feed as the hero; a
   destination with no measured fare says so instead of borrowing a number.
   ======================================================================== */

.jpicks { position: relative; padding: clamp(28px, 5vh, 60px) 0 clamp(20px, 4vh, 44px); }
.jpicks__inner { max-width: 1560px; margin-inline: auto; padding-inline: clamp(14px, 3.2vw, 46px); }

.jpicks__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: clamp(16px, 2.4vh, 24px);
}

.jpicks__kicker {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--jt-coral-ink);
  margin: 0 0 6px;
}

.jpicks__head h2 {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 2.5vw, 2.4rem);
  font-weight: 700;
  letter-spacing: -.025em;
  color: var(--jt-ink);
  margin: 0;
}

.jpicks__ctrl { display: flex; align-items: center; gap: 8px; }

.jpicks__all {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: 10px;
  gap: 8px;
  margin-inline-end: 2px;
  font-size: .88rem;
  font-weight: 700;
  color: var(--jt-coral-ink);
  text-decoration: none;
}

.jpicks__all svg { width: 15px; height: 15px; transition: transform .28s var(--jt-ease); }
.jpicks__all:hover svg { transform: translateX(-4px); }

.jpicks__nav {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 999px;
  border: 1px solid var(--jt-line);
  background: rgba(255, 253, 250, .9);
  color: var(--jt-ink-2);
  cursor: pointer;
  transition: color .22s var(--jt-ease), border-color .22s var(--jt-ease), transform .22s var(--jt-ease);
}

.jpicks__nav svg { width: 18px; height: 18px; }
.jpicks__nav:hover { color: var(--jt-coral); border-color: rgba(232, 97, 60, .45); transform: scale(1.05); }

.jpicks__rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  gap: clamp(12px, 1.4vw, 20px);
  list-style: none;
  margin: 0;
  padding: 6px 2px 18px;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.jpicks__rail::-webkit-scrollbar { display: none; }
.jpicks__rail:focus-visible { outline: 3px solid var(--jt-coral); outline-offset: 4px; border-radius: 20px; }

.pick { scroll-snap-align: start; width: clamp(248px, 22vw, 296px); }
/* One card breaks the rhythm on purpose. grid-auto-columns is a container
   property, so the width has to live on the item and the track has to follow
   it — hence max-content on the rail. */
.pick--wide { width: clamp(292px, 32vw, 424px); }

.pick__link {
  position: relative;
  display: block;
  height: 100%;
  border-radius: 22px;
  overflow: hidden;
  text-decoration: none;
  color: #fff;
  background: #2A1F17;
  box-shadow: var(--e3);
  transition: transform .4s var(--jt-ease), box-shadow .4s var(--jt-ease);
}

.pick__link:hover { transform: translateY(-8px); box-shadow: var(--e4); }
.pick__link:focus-visible { outline: 3px solid var(--jt-coral); outline-offset: 4px; }

.pick__art { display: block; aspect-ratio: 3 / 4; overflow: hidden; }
.pick--wide .pick__art { aspect-ratio: 4 / 3.4; }

.pick__art img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s var(--jt-ease-out); }
.pick__link:hover .pick__art img { transform: scale(1.05); }

.pick__badge {
  position: absolute;
  top: 13px;
  inset-inline-start: 13px;
  padding: 6px 14px;
  border-radius: 999px;
  font-size: .74rem;
  font-weight: 700;
  color: var(--jt-ink);
  background: rgba(255, 253, 250, .94);
  box-shadow: var(--e1);
}

.pick__body {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  display: grid;
  gap: 7px;
  padding: 44px 16px 15px;
  /* The city name is white and lands around the 55-70% mark of this ramp, where
     .72 was not enough over a pale roofscape — 3.89:1 measured. */
  background: linear-gradient(to top, rgba(20, 11, 4, .95) 0%, rgba(20, 11, 4, .86) 40%, rgba(20, 11, 4, .5) 66%, rgba(20, 11, 4, 0) 100%);
}

.pick__city {
  font-family: var(--font-display);
  font-size: 1.16rem;
  font-weight: 700;
  letter-spacing: -.015em;
}

.pick__note { font-size: .8rem; font-weight: 500; color: #F0DFCC; line-height: 1.45; text-wrap: pretty; }

.pick__foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 3px; }

.pick__price {
  display: grid;
  gap: 0;
  padding: 7px 13px;
  border-radius: 12px;
  background: rgba(255, 253, 250, .96);
  color: var(--jt-ink);
}

.pick__price small { font-size: .64rem; font-weight: 700; color: var(--jt-mute); }
.pick__price b { font-size: 1.06rem; font-weight: 800; letter-spacing: -.02em; font-variant-numeric: tabular-nums; line-height: 1.05; }
.pick__price[data-empty] b { font-size: .82rem; font-weight: 700; color: var(--jt-mute); }

.pick__go {
  padding: 8px 16px;
  border-radius: 999px;
  font-size: .8rem;
  font-weight: 700;
  color: #FFF3E6;
  background: rgba(20, 11, 5, .6);
  border: 1px solid rgba(255, 226, 186, .26);
  transition: background .26s var(--jt-ease), color .26s var(--jt-ease), transform .26s var(--jt-ease);
}

.pick__link:hover .pick__go { background: var(--jt-coral-btn); border-color: transparent; transform: translateX(-3px); }

@media (max-width: 820px) {
  .pick { width: min(74vw, 300px); }
  .pick--wide { width: min(84vw, 400px); }
  .jpicks__head h2 { font-size: clamp(1.5rem, 6vw, 1.9rem); }
}

@media (prefers-reduced-motion: reduce) {
  .pick__link:hover { transform: none; }
  .pick__link:hover .pick__art img { transform: none; }
}

/* == QUICK SEARCH IN THE FLOATING BAR =====================================
   The quick search used to be its own fixed bar. Two stacked bars meant the
   scrolled state was a full-width white band with three native controls in it,
   sitting on top of the navigation it hid. It now lives inside the header and
   only appears once the header has become the floating capsule, so scrolling
   produces one object, not two.

   These selectors are scoped under .hd, which the older sheets are not, so
   they win on specificity without !important.
   ======================================================================== */

.hd #sticky-search {
  position: static;
  display: none;
  z-index: auto;
  flex: 0 1 auto;
  margin-inline-start: auto;
  padding: 0;
  border: 0;
  background: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  transform: none;
  transition: none;
}

.hd.scrolled #sticky-search { display: flex; }
.hd.scrolled .hd-cta        { display: none; }   /* the search replaces it */
.hd.scrolled .hd-nav a      { padding-block: 6px; }

.hd #sticky-search .sticky-form {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 1 auto;
  /* The standalone bar centred this form in a 1240px column. Inside the
     capsule that stretched it across the whole bar and buried the navigation
     underneath it. */
  max-width: none;
  margin-inline: 0;
  padding: 4px;
  border-radius: 999px;
  background: linear-gradient(168deg, rgba(255, 255, 254, .96), rgba(255, 246, 235, .9));
  border: 1px solid rgba(214, 182, 148, .5);
  box-shadow: var(--e-lip);
}

.hd #sticky-search .sticky-form select,
.hd #sticky-search .sticky-form input[type="date"] {
  min-height: 44px;
  flex: 0 1 auto;
  width: auto;
  min-width: 0;
  padding: 6px 12px;
  border: 0;
  border-radius: 999px;
  background: none;
  font-family: var(--font);
  font-size: .84rem;
  font-weight: 700;
  color: var(--jt-ink);
  outline: none;
  appearance: none;
  cursor: pointer;
}

.hd #sticky-search .sticky-form input[type="date"] { direction: ltr; text-align: right; font-size: .8rem; }
.hd #sticky-search .sticky-form select:focus-visible,
.hd #sticky-search .sticky-form input:focus-visible { outline: 3px solid var(--jt-coral); outline-offset: 1px; }

.hd #sticky-search .sticky-form button {
  min-height: 44px;
  padding: 8px 18px;
  border: 0;
  border-radius: 999px;
  background: var(--jt-coral-btn);
  color: #FFF8F1;
  font-family: var(--font);
  font-size: .84rem;
  font-weight: 800;
  white-space: nowrap;
  cursor: pointer;
  transition: background .22s var(--jt-ease), transform .22s var(--jt-ease);
}

.hd #sticky-search .sticky-form button:hover { background: var(--jt-coral-d); transform: translateY(-1px); }

/* Below the nav breakpoint the capsule has no room; the bottom bar covers
   quick search on phones, and the hero search is one scroll away. */
@media (max-width: 900px) {
  .hd.scrolled #sticky-search { display: none; }
}


/* == DEPTH SYSTEM · PAGE SURFACES =========================================
   janet_ds.css gives every card one flat 34px shadow with no contact term, so
   a card at rest and a card lifted 8px on hover sat at the same apparent
   height. These map the shared surfaces onto the same scale the hero uses.
   ========================================================================= */

.card, .ldc, .guide, .fact, .step, .hotel-provider, .rec-card {
  box-shadow: var(--e2);
}

.card:hover, .ldc:hover, .guide:hover, .hotel-provider:hover, .rec-card:hover {
  box-shadow: var(--e3);
}

.janet-bubble { box-shadow: var(--e1); }

/* == DEPTH SYSTEM · HERO SCENE ============================================
   The plate, Janet and the UI sit on three planes. The scene needs a floor and
   a corner falloff or the three read as three flat images stacked on cream.
   ========================================================================= */

.jh__bg::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Corner falloff. Kept under 22% or it stops being depth and starts being a
     vignette filter. */
  background: radial-gradient(118% 96% at 50% 38%, rgba(255, 250, 242, 0) 52%, rgba(120, 82, 48, .16) 100%);
}

/* The section below the hero is BEHIND it in the depth model: the hero casts
   down onto it. Without this the two cream fields simply abut and the hero has
   no thickness. */
.jgal::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 90px;
  pointer-events: none;
  background: linear-gradient(to bottom, rgb(var(--sh) / .07), rgb(var(--sh) / 0));
}

@media (prefers-reduced-motion: reduce) {
  /* The parallax terms are already neutralised by --par-x/--par-y staying 0,
     but the plate keeps its 1.045 scale so the blur has something to hide
     behind at the edges. */
  .jh__plate, .jh__janet { transition: none; }
}


/* == MOTION SYSTEM ========================================================
   The page already had scroll reveals, but as one hardcoded 26px/0.8s fade
   with three fixed delay classes that every group had to be hand-tagged with.
   These are the tokens behind it, plus the hero's entrance.

   Budget, per the motion policy: at most six animated elements in a viewport,
   transform and opacity only, and never a layout property. The plate is
   deliberately NOT animated — it is the LCP element, and fading it in delays
   the largest paint to buy an effect nobody is looking at yet.
   ========================================================================= */

:root {
  --m-fast:  .42s;
  --m-base:  .7s;
  --m-slow:  .95s;
  --m-ease:  cubic-bezier(.16, 1, .3, 1);
  --m-stagger: 70ms;
  --m-rise:  22px;
}

/* -- scroll reveals, re-expressed in the tokens ---------------------------- */

html.js .reveal {
  transition:
    opacity var(--m-base) var(--m-ease),
    transform var(--m-base) var(--m-ease);
  transform: translate3d(0, var(--m-rise), 0);
}

/* Delay comes from the element's index inside its group, written by the
   observer, so a new card in a grid cascades without being hand-tagged with a
   reveal-d3. Capped at six steps: past that the last card in a long grid is
   still waiting when the reader has already scrolled by. */
html.js .reveal { transition-delay: calc(min(var(--i, 0), 6) * var(--m-stagger)); }
html.js .reveal.in { transform: none; }

/* -- hero entrance --------------------------------------------------------
   Five elements on one timeline. Janet is slower than the UI because she is
   the heaviest thing on the screen and matching her to a 0.7s panel makes her
   look like she was pasted in rather than walked in.
   ------------------------------------------------------------------------ */

html.js .jh h1,
html.js .jh__sub,
html.js .jsearch,
html.js .jh__assure {
  opacity: 0;
  transform: translate3d(0, var(--m-rise), 0);
  transition:
    opacity var(--m-base) var(--m-ease),
    transform var(--m-base) var(--m-ease);
}

html.js .jh__figure {
  opacity: 0;
  transform: translateX(-50%) translate3d(0, 34px, 0);
  transition:
    opacity var(--m-slow) var(--m-ease),
    transform var(--m-slow) var(--m-ease);
}

html.js .jh h1        { transition-delay: 60ms; }
html.js .jh__figure   { transition-delay: 200ms; }
html.js .jh__sub      { transition-delay: 160ms; }
html.js .jsearch      { transition-delay: 260ms; }
html.js .jh__assure   { transition-delay: 380ms; }

html.js .jh.is-in h1,
html.js .jh.is-in .jh__sub,
html.js .jh.is-in .jsearch,
html.js .jh.is-in .jh__assure { opacity: 1; transform: none; }

/* The end of the entrance has to hand her back to the live transform, not to
   a bare translateX(-50%): that rule outranks the base one and would freeze
   the pointer and scroll terms at zero for the rest of the session. */
html.js .jh.is-in .jh__figure {
  opacity: 1;
  transform:
    translateX(-50%)
    translate3d(calc(var(--par-x) * 7px), calc(var(--par-y) * 4px + var(--scroll) * -26px), 0);
}

/* -- reduced motion --------------------------------------------------------
   The inline sheet already kills transitions globally, which on its own would
   strand every one of these at opacity 0. This sheet loads after it, so the
   final state has to be restated here or the hero ships blank.
   ------------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  html.js .reveal,
  html.js .jh h1,
  html.js .jh__sub,
  html.js .jsearch,
  html.js .jh__assure {
    opacity: 1;
    transform: none;
    transition: none;
  }
  html.js .jh__figure,
  html.js .jh.is-in .jh__figure {
    opacity: 1;
    transform: translateX(-50%);
    transition: none;
  }
  /* Parallax offsets go, the plate's overscan stays: at scale 1 the 1.2px blur
     leaves a soft fringe on all four edges. The gallery cards are NOT reset —
     their transform is the fan's layout, not motion, and zeroing it stacks all
     five cards on top of each other. */
  .jh__plate { transform: scale(1.045); }
  .jh__janet { transform: none; }
}


/* == JANET · SITTING IN THE PAGE ==========================================
   She is a flat cutout on a photographic plate, and the three things that stop
   that reading as a sticker are all here: a shadow cast onto the floor behind
   her, a warm bounce light from the plate's own sun, and the fact that she
   moves with the page instead of being pinned to it.
   ========================================================================= */

/* Cast shadow on the floor. The cutout ends at the knee, so this is not a
   contact ellipse under a foot — it is the long soft shadow the low sun in the
   plate would throw to her right, fading out before it reaches the frame edge. */
.jh__figure::before {
  content: '';
  position: absolute;
  inset-inline-start: 26%;
  bottom: -1%;
  width: 96%;
  height: 20%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(84, 52, 24, .3), rgba(84, 52, 24, 0) 76%);
  filter: blur(14px);
  transform: skewX(-22deg) scaleY(.42);
  transform-origin: 0 100%;
  pointer-events: none;
}

/* Bounce light. The plate's sun is over the sea on her left, so the rim that
   sells her as lit by THIS room is on that side, not a symmetric halo. */
.jh__figure::after {
  content: '';
  position: absolute;
  inset: 6% -14% 0 -22%;
  z-index: -1;
  background: radial-gradient(52% 46% at 26% 42%, rgba(255, 236, 208, .62), rgba(255, 236, 208, 0) 72%);
  pointer-events: none;
}

/* Past the hero she keeps moving with the scene rather than sticking to the
   viewport — --scroll runs 0 to 1 across the hero's own height. */
@media (prefers-reduced-motion: reduce) {
  .jh__janet { animation: none; }
  .jh__figure { transform: translateX(-50%); transition: none; }
}

/* Only one infinite animation is live at a time: the skeleton pulse is capped
   so the loading state cannot add a second one to Janet's breath. */
.ldc--skel span { animation-iteration-count: 8; }


/* == CINEMATIC DESTINATION ================================================
   One destination, given the room a photograph needs. Every moving part reads
   the same --p that the section writes as it crosses the viewport, so the
   image, its grade and the type can never drift apart.
   ========================================================================= */

.cine {
  position: relative;
  padding: clamp(46px, 8vh, 96px) clamp(16px, 3vw, 40px) clamp(30px, 5vh, 54px);
  background: linear-gradient(180deg, #FBF8F4 0%, #F6ECDF 58%, #FBF8F4 100%);
}

.cine__panel {
  position: relative;
  max-width: 1560px;
  margin-inline: auto;
  min-height: clamp(500px, 76svh, 740px);
  border-radius: clamp(20px, 2vw, 30px);
  overflow: hidden;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  box-shadow: var(--e5);
}

.cine__media { position: absolute; inset: 0; z-index: 0; }

.cine__photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 46%;
  /* Small amplitude on purpose. The plate is a 1344px source, and a wide ken
     burns pass magnifies exactly the softness the contained panel exists to
     avoid. 1.075 to 1.0 across the whole crossing is felt, not seen. */
  transform: scale(calc(1.075 - var(--p, .5) * .075));
  will-change: transform;
}

/* The grade deepens as the section arrives, so the type lands on its darkest
   backing at the moment it is actually being read. */
.cine__grade {
  position: absolute;
  inset: 0;
  background:
    /* Weighted into the corner the type actually occupies. The bottom-up ramp
       alone left the eyebrow at 2.48:1 over the bright cliff behind it — the
       gradient was strongest 300px below the first line of text. */
    radial-gradient(74% 82% at 86% 88%, rgba(18, 10, 3, .82) 0%, rgba(18, 10, 3, .34) 58%, rgba(18, 10, 3, 0) 78%),
    linear-gradient(to top, rgba(24, 13, 5, .93) 0%, rgba(28, 16, 7, .78) 30%, rgba(30, 18, 8, .34) 56%, rgba(30, 18, 8, .06) 76%),
    linear-gradient(to left, rgba(24, 13, 5, .52) 0%, rgba(24, 13, 5, 0) 58%);
  opacity: calc(.82 + var(--p, .5) * .18);
}

.cine__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: clamp(26px, 4vw, 56px);
  max-width: 780px;
  color: #FFF6EC;
  /* The type plane travels against the photograph rather than with it. 26px is
     the whole budget — more and it reads as two slides, not one image. */
  transform: translate3d(0, calc((.5 - var(--p, .5)) * 26px), 0);
}

.cine__eyebrow {
  margin: 0 0 10px;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .1em;
  color: #FFD9B2;
}

.cine__h {
  margin: 0 0 12px;
  font-family: var(--font-display);
  font-size: clamp(2.1rem, 4.4vw, 3.6rem);
  font-weight: 800;
  line-height: 1.06;
  letter-spacing: -.03em;
  color: #FFF8F0;
  text-wrap: balance;
}

.cine__lede {
  margin: 0 0 22px;
  max-width: 54ch;
  font-size: clamp(.98rem, 1.15vw, 1.1rem);
  line-height: 1.6;
  color: #F4E3D0;
  text-wrap: pretty;
}

.cine__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 24px;
  padding: 0;
  list-style: none;
}

.cine__facts li {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 11px 16px;
  border-radius: 14px;
  background: rgba(255, 246, 236, .1);
  border: 1px solid rgba(255, 240, 224, .24);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}

.cine__facts b { font-size: .96rem; font-weight: 800; color: #FFECD4; }
.cine__facts span { font-size: .74rem; color: #E4CDB4; }

.cine__cta { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }

.cine__go {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 46px;
  padding: 12px 26px;
  border-radius: 999px;
  background: #FFF6EC;
  color: #2A1710;
  font-weight: 800;
  font-size: .96rem;
  text-decoration: none;
  box-shadow: var(--e3);
  transition: transform .22s var(--m-ease), box-shadow .22s var(--m-ease), background .2s;
}

.cine__go:hover { transform: translateY(-2px); background: #FFFDFA; box-shadow: var(--e4); }
.cine__go:focus-visible { outline: 3px solid #FFD9B2; outline-offset: 3px; }
.cine__go span { transition: transform .22s var(--m-ease); }
.cine__go:hover span { transform: translateX(-4px); }

.cine__note { margin: 0; max-width: 34ch; font-size: .76rem; line-height: 1.5; color: #E0C9AF; }
.cine__note b { color: #FFE3C2; }

.cine__stamp {
  position: absolute;
  z-index: 2;
  inset-inline-end: clamp(16px, 2.4vw, 30px);
  top: clamp(16px, 2.4vw, 30px);
  padding: 8px 15px;
  border-radius: 999px;
  /* .5 put the stamp at 4.22:1 over the bright sky it sits on. */
  background: rgba(16, 9, 3, .74);
  border: 1px solid rgba(255, 240, 224, .34);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  font-size: .74rem;
  font-weight: 700;
  color: #FFEAD2;
}

.cine__disc {
  max-width: 1560px;
  margin: 16px auto 0;
  font-size: .76rem;
  line-height: 1.55;
  color: var(--jt-mute);
  text-align: center;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 720px) {
  .cine__panel { min-height: clamp(460px, 82svh, 640px); }
  .cine__facts li { flex: 1 1 40%; }
}

@media (prefers-reduced-motion: reduce) {
  /* --p is pinned to 0.5 by the script, so these land mid-travel rather than at
     an end of the range: the grade stays at its readable strength instead of
     dropping to the value it would have had off-screen. */
  .cine__photo { transform: scale(1.0375); }
  .cine__inner { transform: none; }
}


/* == CONTRAST DEBT ========================================================
   Found by scripts/contrast_over_media.js once it stopped scoring only the
   first viewport. Ten failures across 96 styles on imagery; the checker had
   been reporting PASS 20/20 about the top 900px of a 12,000px document.
   ========================================================================= */

/* 3.48:1. --jt-coral-btn is 4.91 on its own, but on the fronted card it sits
   under the gold inset glow that marks the active card and comes out ~#D06841. */
.jg__card[data-active] .jg__go { background: var(--jt-coral-d); }

/* 3.85:1 on the city and 4.39 on the line under it. The scrim reached full
   strength below both of them. */
.pick__body {
  padding-top: 58px;
  background: linear-gradient(to top,
    rgba(18, 10, 3, .96) 0%,
    rgba(18, 10, 3, .9) 38%,
    rgba(18, 10, 3, .58) 68%,
    rgba(18, 10, 3, 0) 100%);
}

/* The demo state switcher under Janet's portrait shipped with no colours at
   all: browser-default button text on a photograph, measured at 1.00:1 — the
   glyphs were the same colour as what was behind them. It is also a real
   control a visitor can press, so it gets a real target size rather than 22px. */
.janet-state-btn {
  /* 34px was under the 44px floor janet_ds.css sets for standalone controls,
     and this sheet loads after it, so this rule was the one shipping. The
     inline padding on the element is what forces !important here. */
  min-height: 44px;
  padding: 11px 14px !important;   /* [vs-important] the inline style attribute */
  border-radius: 999px !important;
  border: 1px solid rgba(255, 255, 255, .55);
  background: rgba(255, 251, 245, .94);
  color: #2A1F17;
  font-family: inherit;
  font-size: .76rem !important;
  font-weight: 700;
  line-height: 1.1;
  box-shadow: var(--e1);
  transition: background .18s, transform .18s var(--m-ease);
}
.janet-state-btn:hover { background: #FFFFFF; transform: translateY(-1px); }
.janet-state-btn:focus-visible { outline: 3px solid var(--jt-coral); outline-offset: 2px; }


/* 2.90:1 against a 3.0 floor. The destination rail's title sits on whatever the
   photograph happens to be at that height — hazy sky on Budapest — so the card
   needs its own ramp rather than relying on the picture being dark there. */
.dest-body {
  background: linear-gradient(to top,
    rgba(16, 9, 3, .94) 0%,
    rgba(16, 9, 3, .86) 42%,
    rgba(16, 9, 3, .5) 72%,
    rgba(16, 9, 3, 0) 100%);
  padding-top: 54px;
}

/* 4.00:1. The active card's warm inset glow was painting across the CTA and
   lifting a 4.91 coral to ~#BF613C.

   Raising the whole .jg__body was the wrong fix and the checker caught it: the
   body also sits above .jg__shot::after, the dark ramp that makes the city
   names readable, so lifting it dropped three city labels to 1.8-2.9:1. Only
   the button needs to clear the glow. */
.jg__go { position: relative; z-index: 3; }

/* 3.48:1. The price chip is translucent, so on a pale photograph the muted ink
   was reading against the picture rather than against the chip. */
.jg__price { background: rgba(255, 253, 250, .96) !important; }   /* [vs-important] set on the same declaration above */
.jg__card[data-far] .jg__price { background: rgba(255, 253, 250, .96); }


/* The active card carries a warm 102deg sheen (.jg__link::after) that paints
   over its own CTA and lifts #A83A1C to a measured #BF613C — 4.00:1. The sheen
   is the card's lighting and is worth keeping, so the button starts deeper
   instead: #8C2812 lands near #A34F32 under the wash, comfortably past 4.5.
   Computed colour and rendered colour are not the same thing once something
   translucent is stacked on top, which is the whole reason this is measured at
   glyph pixels rather than read off the stylesheet. */
.jg__card[data-active] .jg__go { background: #8C2812; }

/* The unmeasured-price label on a receding card. Its chip is translucent and
   the card's depth-of-field pass desaturates it, so a muted brown was reading
   against a grey wash at 3.6:1. This is the one label that has to stay legible
   while deliberately out of focus. */
.jg__price[data-empty] b { color: #3A2B1E; }


/* == HERO ON NARROW SCREENS ===============================================
   The desktop composition does not survive the fold. A phone hero is the
   headline plus a five-field form, and a knee-length figure bottom-aligned
   behind that is a sliver of hair above an opaque panel — she was present in
   the DOM and absent from the page.
   ========================================================================= */

@media (max-width: 900px) {
  /* Straight from HANDOFF_HERO_PLATE.md: move toward 78% 60% so the village
     stays in frame while the empty left third, which only existed to hold
     Janet at desktop width, compresses away. */
  .jh__plate { object-position: 78% 60%; }

  /* Anchored from the top rather than the floor. Bottom-aligned she ran under
     the search panel; from the top she sits beside the headline, and the panel
     meeting her below the knee is the same crop the frame edge gave her on
     desktop. */
  .jh__figure {
    left: 24%;
    top: clamp(88px, 13vh, 132px);
    bottom: auto;
    height: clamp(220px, 34svh, 300px);
  }

  /* She is small here, so the shadow that grounded a 700px figure becomes a
     dark smear at her feet. */
  .jh__figure::before { opacity: .55; }
}

@media (max-width: 620px) {
  /* Janet and the copy get separate columns instead of sharing one. At 25ch
     the paragraph still reached her belt, and the measurement was blunt about
     it: #4A3728 body text on #503522 leather is 1.00:1 — the same colour.
     21ch ends clear of her silhouette at every phone width. */
  .jh__figure { left: 11%; height: clamp(170px, 24svh, 220px); }
  /* No auto margin. In RTL inline-start IS the right edge, so margin-inline-start:auto
     pushed the paragraph LEFT, straight back over Janet — the opposite of what
     it was added to do. A flex column already places it at the start. */
  .jh__sub { max-width: 19ch; }
  /* Two lines instead of three: at 2.3rem the headline was taking a third of
     the fold before a single word of the offer. */
  .jh h1 { font-size: clamp(1.95rem, 8.6vw, 2.3rem); }
}

/* The cinematic panel at phone width. The grade is weighted into the corner the
   type occupies on a wide panel; at 390px the type occupies the whole width, so
   the corner weighting misses it and the eyebrow measured 1.85:1. */
@media (max-width: 960px) {
  .cine__grade {
    background:
      linear-gradient(to top,
        rgba(16, 9, 3, .96) 0%,
        rgba(16, 9, 3, .92) 34%,
        rgba(16, 9, 3, .88) 58%,
        rgba(16, 9, 3, .74) 78%,
        rgba(16, 9, 3, .62) 100%);
  }
  .cine__inner { max-width: none; }
  .cine__facts li { flex: 1 1 100%; }
}

/* 4.22:1 against the scrolled header capsule, which is a translucent cream over
   whatever the page is showing. --jt-mute is a body-copy tone and this is 8px
   tracked out at .34em, which is the hardest thing on the page to read. */
.hd .brand-name small { color: var(--jt-ink-2); }


/* The city labels are white and sit at ~72% of the card. Below 1000px the card
   is short enough that the ramp has not built up by the time it reaches them:
   Prague measured 3.00:1 against its pale roofline.

   Both states are named. A bare .jg__shot::after inside the media query loses
   to .jg__card:not([data-active]) .jg__shot::after, which is more specific — a
   media query changes when a rule applies, never how strongly. */
@media (max-width: 1000px) {
  .jg__card .jg__shot::after,
  .jg__card:not([data-active]) .jg__shot::after {
    background: linear-gradient(to top,
      rgba(18, 10, 3, .97) 0%,
      rgba(18, 10, 3, .93) 34%,
      rgba(18, 10, 3, .74) 56%,
      rgba(18, 10, 3, .3) 82%,
      rgba(18, 10, 3, 0) 100%);
  }
}
