/* ==========================================================================
   BOOKALLO — חיפוש חי · Live price intelligence UI
   Built on the janet_hero_v4 token set (cream / coral / warm elevation).
   Nothing here redefines a brand token; it consumes them, so a change to the
   palette moves this page with the rest of the site.
   ========================================================================== */

/* One content-box element anywhere in this tree adds its padding OUTSIDE its
   width and pushes the whole RTL page sideways. The search inputs did exactly
   that (a 371px field in a 343px column); rather than chase the next one, the
   whole subtree is border-box. */
.ls, .ls *, .ls *::before, .ls *::after { box-sizing: border-box; }

/*
 * :where() — ZERO specificity, deliberately.
 *
 * These are the component palette DECLARATIONS. Declared on a bare class they
 * sit at (0,1,0) on the element itself, which BEATS the theme re-pointing the
 * same tokens on :root — so janet_premium.css converted the whole product and
 * every card here kept its own coral.
 *
 * Measured on production 2026-08-30, /hotels results: --jhc-coral resolved to
 * the sea accent at documentElement while 44 price elements rendered #B8401F
 * and 19 form controls #E8613C. The theme was correct; it simply never reached
 * the element. janet_book.css already used :where() for exactly this reason.
 *
 * :where() keeps the values as defaults for anything the theme does not
 * re-point, and lets the theme win everywhere it does.
 */
:where(.ls) {
  --ls-gap: clamp(.75rem, 2vw, 1.25rem);
  --ls-r: 18px;
  --ls-r-sm: 12px;
  --ls-cheap: #1E7A4B;          /* 4.9:1 on cream — used for savings, never for coral's job */
  --ls-cheap-bg: #E8F5ED;
  --ls-warn: #9A5B12;
  --ls-warn-bg: #FDF1DF;
  --ls-stale: #8A5A2B;
  color: var(--jt-ink);
}

/* ---------- search bar ---------- */

.ls-bar {
  position: sticky; top: 0; z-index: 40;
  background: rgb(253 248 241 / .88);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  border-block-end: 1px solid var(--jt-line);
  box-shadow: var(--e1);
}
.ls-bar__in {
  max-width: 1180px; margin-inline: auto;
  padding: clamp(.7rem, 2vw, 1rem) clamp(1rem, 4vw, 2rem);
  display: grid; gap: .7rem;
  grid-template-columns: 1.1fr 1.1fr .9fr .9fr .7fr auto;
  align-items: end;
}
@media (max-width: 900px) { .ls-bar__in { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .ls-bar__in { grid-template-columns: 1fr; } }

.ls-field { display: grid; gap: .25rem; min-width: 0; position: relative; }
/* Field-level validation. Reuses the warn pair so the surface carries its own
   ink — see .ls-trust__note. Sits below the input, never over the suggestions. */
.ls-field__err {
  font: 600 .78rem/1.4 var(--font);
  color: var(--ls-warn); background: var(--ls-warn-bg);
  border-radius: 8px; padding: .35rem .6rem; margin: 0;
}
.ls-field input[aria-invalid="true"] { border-color: var(--ls-warn); }
.ls-field > label {
  font: 600 .72rem/1.2 var(--font, "Assistant", system-ui, sans-serif);
  color: var(--jt-mute); letter-spacing: .01em;
}
.ls-field input, .ls-field select {
  /* Without border-box, `inline-size: 100%` is the CONTENT width and the
     padding and border are added on top — each field rendered 371px inside a
     343px column and pushed the whole RTL page 12px wider than the viewport. */
  box-sizing: border-box;
  inline-size: 100%; min-height: 44px;
  padding: .55rem .8rem;
  font: 500 .95rem/1.3 var(--font, "Assistant", system-ui, sans-serif);
  color: var(--jt-ink);
  background: var(--jp-raised, #fff);
  border: 1px solid var(--jt-line); border-radius: var(--ls-r-sm);
  transition: border-color .18s var(--jt-ease), box-shadow .18s var(--jt-ease);
}
.ls-field input:focus-visible, .ls-field select:focus-visible,
.ls-btn:focus-visible, .ls-chip:focus-visible, .ls-card__cta:focus-visible,
.ls-refresh:focus-visible, .ls-cal__day:focus-visible, .ls-sellers__t:focus-visible {
  outline: 3px solid var(--jt-coral-btn); outline-offset: 2px;
}
.ls-field input:focus { border-color: var(--jt-coral-btn); box-shadow: 0 0 0 3px rgb(191 68 35 / .12); }

.ls-btn {
  min-height: 44px; padding: .6rem 1.4rem;
  font: 700 .95rem/1 var(--font, "Assistant", system-ui, sans-serif);
  color: var(--on-sky, #fff); background: var(--jt-coral-btn);
  border: 0; border-radius: var(--ls-r-sm); cursor: pointer;
  box-shadow: var(--e-cta);
  transition: transform .18s var(--jt-ease), box-shadow .18s var(--jt-ease), background .18s;
  display: inline-flex; align-items: center; gap: .45rem; justify-content: center;
}
.ls-btn:hover { background: var(--jt-coral-d); box-shadow: var(--e-cta-hover); transform: translateY(-1px); }
.ls-btn[disabled] { opacity: .55; cursor: progress; transform: none; }

/* typeahead */
.ls-ac {
  position: absolute; inset-block-start: 100%; inset-inline: 0; z-index: 60;
  margin-block-start: 4px; padding: .3rem;
  background: var(--jp-overlay, #fff); border: 1px solid var(--jt-line); border-radius: var(--ls-r-sm);
  box-shadow: var(--e3); max-block-size: 300px; overflow-y: auto;
}
.ls-ac[hidden] { display: none; }
.ls-ac__i {
  display: flex; justify-content: space-between; gap: .6rem; align-items: baseline;
  inline-size: 100%; padding: .6rem .6rem; min-height: 44px;
  background: none; border: 0; border-radius: 8px; cursor: pointer; text-align: start;
  font: 500 .9rem/1.3 var(--font, "Assistant", system-ui, sans-serif); color: var(--jt-ink);
}
.ls-ac__i:hover, .ls-ac__i[aria-selected="true"] { background: var(--jt-cream-2); }
.ls-ac__c { font: 700 .78rem/1 var(--font); color: var(--jt-mute); letter-spacing: .04em; }

/* ---------- page shell ---------- */

.ls-main { max-width: 1180px; margin-inline: auto; padding: clamp(1.2rem, 3vw, 2rem) clamp(1rem, 4vw, 2rem) 5rem; }
.ls-head { display: flex; flex-wrap: wrap; gap: .75rem 1.25rem; align-items: baseline; margin-block-end: 1.25rem; }
.ls-head h1 {
  font: 800 clamp(1.4rem, 3.4vw, 2.1rem)/1.15 var(--font-display, "Frank Ruhl Libre", Georgia, serif);
  color: var(--jt-ink); margin: 0;
}
.ls-head__meta { font: 500 .85rem/1.4 var(--font); color: var(--jt-mute); }

/* ---------- state blocks ---------- */

.ls-state { display: none; }
.ls-state[data-on] { display: block; }

.ls-empty, .ls-error {
  padding: clamp(1.5rem, 4vw, 2.5rem);
  background: var(--jp-raised, #fff); border: 1px solid var(--jt-line); border-radius: var(--ls-r);
  box-shadow: var(--e1); text-align: center;
}
.ls-error { border-color: rgb(154 91 18 / .3); background: var(--ls-warn-bg); }
.ls-empty h2, .ls-error h2 { font: 700 1.15rem/1.3 var(--font-display); margin: 0 0 .5rem; }
.ls-empty p, .ls-error p { font: 400 .95rem/1.6 var(--font); color: var(--jt-ink-2); margin: 0 auto; max-width: 52ch; }

/* progress — real stages, never a fake percentage */
.ls-prog { display: grid; gap: .9rem; padding: clamp(1.2rem, 3vw, 2rem) 0; }
.ls-prog__row { display: flex; align-items: center; gap: .6rem; font: 500 .95rem/1.4 var(--font); color: var(--jt-mute); }
.ls-prog__row[data-done] { color: var(--jt-ink); }
.ls-prog__dot {
  inline-size: 10px; block-size: 10px; border-radius: 50%;
  background: var(--jt-line); flex: 0 0 auto;
}
.ls-prog__row[data-active] .ls-prog__dot { background: var(--jt-coral-btn); animation: ls-pulse 1.1s var(--jt-ease) infinite; }
.ls-prog__row[data-done] .ls-prog__dot { background: var(--ls-cheap); }
@keyframes ls-pulse { 0%,100% { transform: scale(1); opacity: 1 } 50% { transform: scale(1.5); opacity: .55 } }

.ls-skel { display: grid; gap: .75rem; }
.ls-skel__c {
  block-size: 104px; border-radius: var(--ls-r);
  background: linear-gradient(100deg, var(--jt-cream-2) 30%, #fff 50%, var(--jt-cream-2) 70%);
  background-size: 220% 100%; animation: ls-shim 1.5s linear infinite;
}
@keyframes ls-shim { to { background-position: -220% 0 } }

/* ---------- summary strip ---------- */

.ls-sum {
  display: grid; gap: var(--ls-gap);
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  margin-block-end: 1.5rem;
}
.ls-sum__c {
  padding: .95rem 1.1rem; background: var(--jp-raised, #fff);
  border: 1px solid var(--jt-line); border-radius: var(--ls-r); box-shadow: var(--e1);
}
.ls-sum__c[data-hero] { border-color: rgb(191 68 35 / .3); background: linear-gradient(180deg, var(--jp-raised, #fff), var(--jp-elevated, #FFF7F3)); }
.ls-sum__k { font: 600 .74rem/1.2 var(--font); color: var(--jt-mute); text-transform: none; }
.ls-sum__v { font: 800 1.5rem/1.15 var(--font-display); color: var(--jt-ink); margin-block-start: .3rem; }
.ls-sum__n { font: 500 .78rem/1.4 var(--font); color: var(--jt-mute); margin-block-start: .2rem; }

/* ---------- Janet's pick ---------- */

.ls-janet {
  display: flex; gap: 1rem; align-items: flex-start;
  padding: 1.1rem 1.25rem; margin-block-end: 1.5rem;
  background: linear-gradient(180deg, var(--jp-raised, #FFF9F5), var(--jp-elevated, #FDF2EA));
  border: 1px solid rgb(191 68 35 / .22); border-radius: var(--ls-r); box-shadow: var(--e2);
}
.ls-janet__av {
  inline-size: 46px; block-size: 46px; border-radius: 50%; flex: 0 0 auto;
  object-fit: cover; border: 2px solid #fff; box-shadow: var(--e1); background: var(--jt-cream-2);
}
.ls-janet__t { font: 700 .8rem/1.2 var(--font); color: var(--jt-coral-ink); margin: 0 0 .3rem; }
.ls-janet__q { font: 500 1rem/1.55 var(--font); color: var(--jt-ink); margin: 0; }
.ls-janet__why { font: 400 .78rem/1.5 var(--font); color: var(--jt-mute); margin: .4rem 0 0; }

/* ---------- result cards ---------- */

.ls-sort {
  display: flex; align-items: center; gap: .5rem; margin-bottom: .75rem;
  flex-wrap: wrap;
}
.ls-sort__label { font: 600 .82rem/1 var(--font); color: var(--jt-mute); }
.ls-sort__btn {
  /* 36px was under the 44px tap target on every phone. These only exist once
     results have rendered, so a page-load sweep never sees them — it was the
     live search on a 375px viewport that caught it. */
  min-height: 44px; padding: .4rem .75rem;
  font: 500 .82rem/1 var(--font); color: var(--jt-ink-2);
  background: transparent; border: 1px solid var(--jt-line); border-radius: 999px;
  cursor: pointer; transition: background .15s, border-color .15s, color .15s;
}
.ls-sort__btn:hover { background: var(--jt-cream-2); }
.ls-sort__btn[data-active] {
  background: var(--jt-coral, #E8613C); color: var(--on-sky, #fff);
  border-color: var(--jt-coral, #E8613C);
}

.ls-list { display: grid; gap: .85rem; }

.ls-card {
  display: grid; gap: 1rem;
  grid-template-columns: 1fr auto;
  padding: 1rem 1.15rem;
  background: var(--jp-raised, #fff); border: 1px solid var(--jt-line); border-radius: var(--ls-r);
  box-shadow: var(--e1);
  transition: box-shadow .22s var(--jt-ease), transform .22s var(--jt-ease), border-color .22s;
}
.ls-card:hover { box-shadow: var(--e3); transform: translateY(-2px); border-color: rgb(122 84 52 / .26); }
.ls-card[data-best] { border-color: rgb(30 122 75 / .4); box-shadow: 0 0 0 1px rgb(30 122 75 / .18), var(--e2); }
@media (max-width: 640px) { .ls-card { grid-template-columns: 1fr; } }

.ls-card__main { min-width: 0; }
.ls-card__top { display: flex; flex-wrap: wrap; gap: .4rem; margin-block-end: .55rem; }

.ls-tag {
  font: 700 .72rem/1 var(--font); padding: .32rem .55rem; border-radius: 999px;
  background: var(--jt-cream-2); color: var(--jt-ink-2); white-space: nowrap;
}
.ls-tag[data-t="cheapest"] { background: var(--ls-cheap-bg); color: var(--ls-cheap); }
.ls-tag[data-t="direct"] { background: #E7F0FB; color: #1B4F8A; }
.ls-tag[data-t="fastest"] { background: #F3EBFA; color: #5B3A87; }
.ls-tag[data-t="best_value"] { background: #FDF1DF; color: var(--ls-warn); }

.ls-card__route { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; }
.ls-card__time { font: 800 1.15rem/1.2 var(--font-display); color: var(--jt-ink); font-variant-numeric: tabular-nums; }
.ls-card__arrow { color: var(--jt-mute); }
.ls-card__air { font: 500 .88rem/1.4 var(--font); color: var(--jt-ink-2); }
.ls-card__facts { display: flex; flex-wrap: wrap; gap: .35rem .9rem; margin-block-start: .45rem; font: 500 .82rem/1.5 var(--font); color: var(--jt-mute); }
.ls-card__facts b { font-weight: 700; color: var(--jt-ink-2); }

.ls-card__side { display: grid; gap: .5rem; justify-items: end; align-content: start; min-width: 140px; }
@media (max-width: 640px) { .ls-card__side { justify-items: stretch; } }
.ls-card__price { font: 800 1.7rem/1 var(--font-display); color: var(--jt-ink); font-variant-numeric: tabular-nums; }
.ls-card__per { font: 500 .72rem/1.3 var(--font); color: var(--jt-mute); }
.ls-card__cta {
  min-height: 44px; padding: .55rem 1.1rem;
  font: 700 .88rem/1 var(--font); color: var(--on-sky, #fff); background: var(--jt-coral-btn);
  border: 0; border-radius: var(--ls-r-sm); cursor: pointer; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  box-shadow: var(--e-cta); transition: background .18s, box-shadow .18s, transform .18s;
}
.ls-card__cta:hover { background: var(--jt-coral-d); box-shadow: var(--e-cta-hover); transform: translateY(-1px); }
/* The commercial disclosure — "this price is from earlier data, the click runs a
   fresh search and the price may change" — was set at .65rem (10.4px) inside a
   180px column: present, but the smallest text on the card. A statement that
   protects the traveler should not be the hardest thing to read on it. Contrast
   was already fine (6.7:1); this is size and measure only. */
.ls-card__disclaimer { font: 400 .74rem/1.45 var(--font); color: var(--jt-mute); text-align: end; max-width: 240px; margin-top: .4rem; }

/* freshness — the label is data, so it always renders */
.ls-fresh { display: inline-flex; align-items: center; gap: .3rem; font: 500 .74rem/1.3 var(--font); color: var(--jt-mute); }
.ls-fresh__d { inline-size: 7px; block-size: 7px; border-radius: 50%; background: var(--ls-cheap); flex: 0 0 auto; }
.ls-fresh[data-s="recent"] .ls-fresh__d { background: var(--jt-gold); }
.ls-fresh[data-s="stale"] { color: var(--ls-stale); }
.ls-fresh[data-s="stale"] .ls-fresh__d { background: var(--ls-stale); }
.ls-fresh[data-s="unknown_age"] .ls-fresh__d { background: var(--jt-line); }

/* seller comparison — only rendered when >1 seller genuinely quoted */
.ls-sellers { margin-block-start: .6rem; border-block-start: 1px dashed var(--jt-line); padding-block-start: .55rem; }
.ls-sellers__t {
  font: 600 .8rem/1.3 var(--font); color: var(--jt-coral-ink);
  background: none; border: 0; cursor: pointer; padding: .5rem 0; min-height: 44px;
  display: inline-flex; align-items: center; gap: .3rem;
}
.ls-sellers__l { display: none; margin-block-start: .5rem; }
.ls-sellers[data-open] .ls-sellers__l { display: grid; gap: .3rem; }
.ls-sellers__r {
  display: flex; justify-content: space-between; gap: .75rem; align-items: baseline;
  padding: .4rem .6rem; border-radius: 8px; background: var(--jt-cream);
  font: 500 .85rem/1.3 var(--font);
}
.ls-sellers__r[data-cheapest] { background: var(--ls-cheap-bg); color: var(--ls-cheap); font-weight: 700; }
.ls-sellers__p { font-variant-numeric: tabular-nums; font-weight: 700; }

/* ---------- flexible dates ---------- */

.ls-sect { margin-block-start: 2.25rem; }
.ls-sect > h2 { font: 700 1.15rem/1.3 var(--font-display); color: var(--jt-ink); margin: 0 0 .35rem; }
.ls-sect > .ls-sub { font: 400 .85rem/1.55 var(--font); color: var(--jt-mute); margin: 0 0 1rem; max-width: 62ch; }

.ls-flex { display: grid; gap: .7rem; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.ls-flex__c {
  padding: .9rem 1rem; background: var(--jp-raised, #fff); border: 1px solid var(--jt-line);
  border-radius: var(--ls-r); box-shadow: var(--e1);
}
.ls-flex__c[data-save] { border-color: rgb(30 122 75 / .35); background: linear-gradient(180deg, var(--jp-raised, #fff), var(--jp-elevated, #F4FBF7)); }
.ls-flex__w { font: 700 .78rem/1.2 var(--font); color: var(--jt-mute); }
.ls-flex__m { font: 500 .92rem/1.55 var(--font); color: var(--jt-ink); margin-block-start: .35rem; }
.ls-flex__s { font: 800 1.1rem/1.2 var(--font-display); color: var(--ls-cheap); margin-block-start: .4rem; }
/* What the figure above is a price OF. Never optional: the provider quotes per
   passenger and the card leads with the party total. */
.ls-flex__b { font: 600 .74rem/1.35 var(--font); color: var(--jt-mute); margin-block-start: .15rem; }
/* Two actions on a nearby-date card: staying on Janet with these dates leads,
   leaving for the seller is secondary. Both are 44px so the choice between them
   is not decided by which one is easier to hit. */
.ls-flex__acts { display: flex; flex-wrap: wrap; gap: .45rem; margin-block-start: .6rem; }
.ls-flex__go {
  flex: 1 1 auto; min-height: 44px; padding: .5rem .9rem; cursor: pointer;
  border: 0; border-radius: 10px; background: var(--jt-coral-btn); color: var(--on-sky, #fff);
  font: 700 .85rem/1 var(--font);
}
.ls-flex__go:hover { background: var(--jt-coral-d); }
.ls-flex__go:focus-visible,
.ls-flex__ext:focus-visible { outline: 3px solid var(--jt-coral-btn); outline-offset: 2px; }
.ls-flex__ext {
  flex: 0 1 auto; min-height: 44px; display: inline-flex; align-items: center;
  padding: .5rem .8rem; border-radius: 10px; border: 1px solid var(--jt-line);
  /* No fill of its own: the card is already white, so a second white block
     beside the coral primary adds a surface without adding meaning — and any
     light fill here would need its own pinned ink, because var(--jt-ink) is
     #241A13 in the light scale but re-points to var(--jp-text) under
     janet_premium.css. The ink is stated literally for the same reason. */
  background: transparent; color: #241A13; font: 600 .82rem/1 var(--font); text-decoration: none;
}
.ls-flex__ext:hover { border-color: var(--jt-coral-btn); }

/* ---------- price calendar ---------- */

.ls-cal { background: var(--jp-raised, #fff); border: 1px solid var(--jt-line); border-radius: var(--ls-r); box-shadow: var(--e1); padding: 1rem; }
.ls-cal__grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
.ls-cal__dow { font: 700 .7rem/1 var(--font); color: var(--jt-mute); text-align: center; padding-block: .35rem; }
.ls-cal__day {
  /* min-height keeps the 44px tap target; without min-inline-size:0 the
     aspect-ratio transfers that height back into a ~54px minimum WIDTH, and
     seven of those plus gaps need ~400px — 25px more than a 375px phone. The
     cell keeps its proportions wherever there is room and simply gets shorter
     where there is not, instead of widening the page. */
  aspect-ratio: 1 / .82; min-height: 44px; min-inline-size: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  border: 1px solid transparent; border-radius: 8px; cursor: pointer;
  background: var(--jt-cream); font: 500 .72rem/1.1 var(--font); color: var(--jt-ink-2);
  transition: transform .15s var(--jt-ease), box-shadow .15s;
}
.ls-cal__day:hover:not([disabled]) { transform: translateY(-2px); box-shadow: var(--e2); }
.ls-cal__day[disabled] { cursor: default; opacity: .45; }
.ls-cal__n { font-weight: 700; font-size: .78rem; }
.ls-cal__p { font-variant-numeric: tabular-nums; font-size: .7rem; }
.ls-cal__day[data-b="cheap"] { background: var(--ls-cheap-bg); color: var(--ls-cheap); border-color: rgb(30 122 75 / .3); }
.ls-cal__day[data-b="cheap"] .ls-cal__p { font-weight: 800; }
.ls-cal__day[data-b="average"] { background: var(--jt-cream-2); }
.ls-cal__day[data-b="expensive"] { background: #FBEEE9; color: #8A3A1E; }
/* A day with no measurement says so. It is never coloured as if it had a price. */
.ls-cal__day[data-b="no_data"] { background: repeating-linear-gradient(45deg, #FAF6F0, #FAF6F0 4px, #F2ECE3 4px, #F2ECE3 8px); color: var(--jt-mute); }
.ls-cal__day[data-sel] { outline: 3px solid var(--jt-coral-btn); outline-offset: 1px; }
.ls-cal__legend { display: flex; flex-wrap: wrap; gap: .5rem 1rem; margin-block-start: .85rem; font: 500 .76rem/1.3 var(--font); color: var(--jt-mute); }
.ls-cal__lg { display: inline-flex; align-items: center; gap: .35rem; }
.ls-cal__sw { inline-size: 12px; block-size: 12px; border-radius: 3px; }

/* ---------- trust panel ---------- */

.ls-trust {
  margin-block-start: 2.5rem; padding: 1.25rem 1.4rem;
  background: var(--jt-cream-2); border: 1px solid var(--jt-line); border-radius: var(--ls-r);
}
.ls-trust h2 { font: 700 1rem/1.3 var(--font-display); margin: 0 0 .5rem; color: var(--jt-ink); }
.ls-trust p { font: 400 .86rem/1.6 var(--font); color: var(--jt-ink-2); margin: 0 0 .75rem; max-width: 68ch; }
.ls-trust__g { display: grid; gap: .6rem 1.5rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.ls-trust__k { font: 600 .74rem/1.2 var(--font); color: var(--jt-mute); }
.ls-trust__v { font: 600 .88rem/1.4 var(--font); color: var(--jt-ink); }
.ls-trust ul { margin: .5rem 0 0; padding-inline-start: 1.1rem; }
.ls-trust li { font: 400 .82rem/1.65 var(--font); color: var(--jt-ink-2); }
.ls-trust__note {
  font: 600 .82rem/1.4 var(--font); color: var(--ls-warn);
  background: var(--ls-warn-bg); border-radius: 8px;
  padding: .45rem .75rem; display: inline-block; margin-bottom: .6rem;
}

/* ---------- refresh / price-changed ---------- */

.ls-refresh {
  min-height: 44px; padding: .6rem .9rem;
  font: 600 .82rem/1 var(--font); color: var(--jt-coral-ink);
  background: var(--jp-raised, #fff); border: 1px solid rgb(191 68 35 / .35); border-radius: 999px; cursor: pointer;
  display: inline-flex; align-items: center; gap: .35rem;
  transition: background .18s, border-color .18s;
}
.ls-refresh:hover { background: var(--jp-elevated, #FFF3EE); }
.ls-refresh[data-busy] { opacity: .6; cursor: progress; }

.ls-changed {
  margin-block-start: .5rem; padding: .6rem .8rem; border-radius: var(--ls-r-sm);
  font: 600 .85rem/1.45 var(--font);
  background: var(--ls-warn-bg); color: var(--ls-warn); border: 1px solid rgb(154 91 18 / .28);
}
.ls-changed[data-dir="down"] { background: var(--ls-cheap-bg); color: var(--ls-cheap); border-color: rgb(30 122 75 / .3); }

.ls-stale-note {
  display: flex; flex-wrap: wrap; gap: .6rem; align-items: center;
  padding: .7rem .9rem; margin-block-end: 1rem;
  background: var(--ls-warn-bg); border: 1px solid rgb(154 91 18 / .28); border-radius: var(--ls-r-sm);
  font: 500 .86rem/1.45 var(--font); color: var(--ls-warn);
}

/* ---------- hotels ---------- */

.ls-hotels__none {
  padding: 1rem 1.15rem; background: var(--jp-raised, #fff); border: 1px dashed var(--jt-line);
  border-radius: var(--ls-r); font: 400 .88rem/1.6 var(--font); color: var(--jt-ink-2);
}
.ls-hotels__links { display: flex; flex-wrap: wrap; gap: .5rem; margin-block-start: .8rem; }
.ls-hotels__l {
  min-height: 44px; padding: .6rem .9rem; border-radius: 999px;
  background: var(--jt-cream-2); border: 1px solid var(--jt-line);
  font: 600 .84rem/1 var(--font); color: var(--jt-ink); text-decoration: none;
  display: inline-flex; align-items: center; gap: .35rem;
}
.ls-hotels__l:hover { background: var(--jp-elevated, #fff); box-shadow: var(--e1); }

.ls-hotel-grid { display: grid; gap: .7rem; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.ls-hotel-card {
  background: var(--jp-raised, #fff); border: 1px solid var(--jt-line); border-radius: var(--ls-r);
  box-shadow: var(--e1); padding: 1rem 1.15rem;
  display: flex; flex-direction: column; gap: .6rem;
}
.ls-hotel-card__name { font: 700 .95rem/1.35 var(--font-display); color: var(--jt-ink); }
.ls-hotel-card__meta { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-block-start: .2rem; }
.ls-hotel-card__stars { color: #D4A017; font-size: .85rem; letter-spacing: 1px; }
.ls-hotel-card__seller { font: 500 .78rem/1 var(--font); color: var(--jt-mute); }
.ls-hotel-card__price { margin-block-start: auto; padding-block-start: .5rem; border-block-start: 1px solid var(--jt-line); }
.ls-hotel-card__total { font: 800 1.15rem/1.2 var(--font-display); color: var(--jt-ink); }
.ls-hotel-card__ppn { font: 400 .78rem/1.4 var(--font); color: var(--jt-mute); margin-block-start: .15rem; }
.ls-hotel-card .ls-card__cta { margin-block-start: .55rem; }
.ls-hotel-note { font: 400 .8rem/1.55 var(--font); color: var(--jt-mute); margin-block-start: .7rem; max-width: 62ch; }

/* ---------- trip cost ---------- */

.ls-trip { background: var(--jp-raised, #fff); border: 1px solid var(--jt-line); border-radius: var(--ls-r); box-shadow: var(--e1); padding: 1.1rem 1.25rem; }
.ls-trip__r { display: flex; justify-content: space-between; gap: 1rem; padding-block: .45rem; font: 500 .92rem/1.4 var(--font); border-block-end: 1px solid var(--jt-line); }
.ls-trip__r:last-of-type { border-block-end: 0; }
.ls-trip__r[data-unknown] { color: var(--jt-mute); font-style: italic; }
.ls-trip__t { display: flex; justify-content: space-between; gap: 1rem; margin-block-start: .6rem; padding-block-start: .6rem; border-block-start: 2px solid var(--jt-ink); font: 800 1.1rem/1.3 var(--font-display); }
.ls-trip__note { font: 400 .8rem/1.55 var(--font); color: var(--jt-mute); margin-block-start: .7rem; }

/* ---------- deal hero ---------- */

.ls-deal-hero { margin-block-end: 1.5rem; }
.ls-dh { background: linear-gradient(135deg, var(--jp-raised, #fff) 60%, var(--jp-elevated, #FFF8F2)); border: 2px solid var(--jt-coral, #E8613C); border-radius: var(--ls-r); box-shadow: 0 4px 20px rgba(14, 90, 82, .12); padding: 1.5rem 1.75rem; position: relative; }
.ls-dh__badge { display: inline-block; background: var(--jt-coral, #E8613C); color: var(--on-sky, #fff); font: 700 .82rem/1 var(--font-display); padding: .35rem .75rem; border-radius: 999px; margin-block-end: .75rem; }
.ls-dh__dest { font: 800 1.5rem/1.2 var(--font-display); color: var(--jt-ink); }
.ls-dh__dates { font: 500 .9rem/1.4 var(--font); color: var(--jt-mute); margin-block-end: 1rem; }
.ls-dh__lines { display: flex; flex-direction: column; gap: .5rem; margin-block-end: 1rem; }
.ls-dh__line { display: flex; align-items: center; gap: .5rem; font: 500 .95rem/1.4 var(--font); }
.ls-dh__line--na { color: var(--jt-mute); font-style: italic; }
.ls-dh__icon { font-size: 1.1rem; flex-shrink: 0; width: 1.5rem; text-align: center; }
.ls-dh__price { margin-inline-start: auto; font-weight: 700; color: var(--jt-ink); }
.ls-dh__total { text-align: center; border-block-start: 2px solid var(--jt-ink); padding-block-start: 1rem; margin-block-start: .5rem; }
.ls-dh__total-label { font: 600 .85rem/1 var(--font); color: var(--jt-mute); margin-block-end: .3rem; }
.ls-dh__total-price { font: 900 2rem/1.1 var(--font-display); color: var(--jt-ink); }
.ls-dh__per-person { font: 500 .85rem/1 var(--font); color: var(--jt-mute); margin-block-start: .25rem; }
.ls-dh__reason { display: flex; align-items: flex-start; gap: .6rem; margin-block-start: 1rem; padding: .9rem 1rem; background: var(--jt-cream-2, #FAF6F0); border-radius: calc(var(--ls-r) - 4px); font: 500 .9rem/1.5 var(--font); color: var(--jt-ink); }
.ls-dh__reason img { border-radius: 50%; flex-shrink: 0; }
.ls-dh__reason p { margin: 0; }
.ls-dh__ctas { display: flex; gap: .6rem; justify-content: center; margin-block-start: 1rem; flex-wrap: wrap; }
.ls-dh__ctas .ls-card__cta { flex: 1; min-width: 140px; text-align: center; }
.ls-card__cta--hotel { background: var(--jt-ink, #2C1810); }
.ls-card__cta--hotel:hover { background: #1a0e09; }
.ls-dh__sep { text-align: center; font: 400 .78rem/1.5 var(--font); color: var(--jt-mute); margin-block-start: .6rem; }
.ls-dh__meta { text-align: center; font: 400 .75rem/1.4 var(--font); color: var(--jt-mute); margin-block-start: .5rem; }

/* ---------- activity highlights ---------- */

.ls-act-highlights { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .75rem; margin-block-start: 1rem; }
.ls-act-hl { background: var(--jp-raised, #fff); border: 1px solid var(--jt-line); border-radius: var(--ls-r); padding: .85rem 1rem; }
.ls-act-hl strong { display: block; font: 700 .95rem/1.3 var(--font-display); margin-block-end: .45rem; }

@media (prefers-reduced-motion: reduce) {
  .ls-card, .ls-btn, .ls-card__cta, .ls-cal__day { transition: none; }
  .ls-card:hover, .ls-btn:hover, .ls-card__cta:hover, .ls-cal__day:hover { transform: none; }
  .ls-prog__row[data-active] .ls-prog__dot { animation: none; }
  .ls-skel__c { animation: none; }
}

/* ---------------------------------------------------------------------------
   Hotel cards, package cards, classification badges, skeletons.
   Every price on screen sits next to a badge saying where it came from — the
   badge is not decoration, it is the thing that keeps the product honest.
   --------------------------------------------------------------------------- */

.ls-badge {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .2rem .5rem; border-radius: 999px;
  font: 700 .68rem/1.3 var(--font); white-space: nowrap;
}
.ls-badge--live       { background: #e7f6ec; color: #1c6b38; }
.ls-badge--cached     { background: #fdf2e0; color: #8a5a12; }
.ls-badge--indicative { background: #f0eef8; color: #4b3f86; }
.ls-badge--sandbox    { background: #fde9e9; color: #8d2020; }

.ls-tag {
  display: inline-flex; align-items: center;
  padding: .2rem .5rem; border-radius: 6px;
  background: var(--jt-cream); color: var(--jt-ink-2);
  font: 600 .68rem/1.3 var(--font); white-space: nowrap;
}
.ls-tag--good { background: #e7f6ec; color: #1c6b38; }

.ls-hotel-head { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-block-end: .7rem; }
.ls-hotel-head__note { font: 500 .78rem/1.4 var(--font); color: var(--jt-mute); }

.ls-hotel-ctrl {
  display: flex; flex-wrap: wrap; gap: .6rem 1rem;
  align-items: center; margin-block-end: .9rem;
}
.ls-hotel-ctrl__f { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.ls-hotel-ctrl__count { font: 500 .74rem/1 var(--font); color: var(--jt-mute); margin-inline-start: .3rem; }
.ls-sort__lbl { font: 600 .78rem/1 var(--font); color: var(--jt-mute); }

.ls-chip {
  min-height: 44px; padding: .4rem .8rem;
  border: 1px solid var(--jt-line); border-radius: 999px;
  background: #fff; color: var(--jt-ink-2); cursor: pointer;
  font: 600 .78rem/1 var(--font);
  transition: background .15s, border-color .15s, color .15s;
}
.ls-chip:hover { background: var(--jt-cream-2); }
.ls-chip[data-active] { background: var(--jt-coral-btn); border-color: var(--jt-coral-btn); color: var(--on-sky, #fff); }

.ls-hotel-grid { display: grid; gap: .85rem; }

.ls-hotel-card {
  display: grid; gap: .8rem;
  grid-template-columns: 132px minmax(0, 1fr) auto;
  align-items: start;
  background: var(--jp-raised, #fff); border: 1px solid var(--jt-line); border-radius: var(--ls-r);
  box-shadow: var(--e1); padding: .85rem;
}
.ls-hotel-card:not(:has(.ls-hotel-card__ph)) { grid-template-columns: minmax(0, 1fr) auto; }
.ls-hotel-card__ph { inline-size: 132px; block-size: 104px; border-radius: 10px; overflow: hidden; background: var(--jt-cream); }
.ls-hotel-card__ph img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.ls-hotel-card__info { min-inline-size: 0; }
.ls-hotel-card__name { margin: 0 0 .3rem; font: 700 1rem/1.3 var(--font); color: var(--jt-ink); }
.ls-hotel-card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; margin-block-end: .3rem; }
.ls-hotel__stars { color: #d29b2a; font-size: .8rem; letter-spacing: .04em; }
.ls-hotel-card__rate { font: 700 .82rem/1 var(--font); color: var(--jt-ink); }
.ls-hotel-card__rate small { font-weight: 500; color: var(--jt-mute); }
.ls-hotel-card__city { font: 500 .76rem/1 var(--font); color: var(--jt-mute); }
.ls-hotel-card__room { font: 500 .82rem/1.4 var(--font); color: var(--jt-ink-2); margin-block-end: .35rem; }
.ls-hotel-card__tags { display: flex; flex-wrap: wrap; gap: .3rem; }
.ls-hotel-card__price { text-align: end; display: grid; gap: .25rem; justify-items: end; }
.ls-hotel-card__total { font: 800 1.15rem/1.1 var(--font); color: var(--jt-ink); font-variant-numeric: tabular-nums; }
.ls-hotel-card__ppn { font: 500 .74rem/1.3 var(--font); color: var(--jt-mute); }
.ls-hotel-card__tax { font: 600 .72rem/1.3 var(--font); color: #8a5a12; }
.ls-card__cta--off {
  background: var(--jt-cream); color: var(--jt-mute);
  cursor: not-allowed; box-shadow: none; border: 1px dashed var(--jt-line);
}

/* packages */
.ls-pkg-grid { display: grid; gap: .85rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.ls-pkg {
  background: var(--jp-raised, #fff); border: 1px solid var(--jt-line); border-radius: var(--ls-r);
  box-shadow: var(--e1); padding: 1rem; display: grid; gap: .5rem; align-content: start;
}
.ls-pkg--value { border-color: #b9a6f0; }
.ls-pkg--janet { border-color: var(--jt-coral-btn); }
.ls-pkg__tag { font: 800 .75rem/1 var(--font); letter-spacing: .02em; color: var(--jt-mute); text-transform: none; }
.ls-pkg--cheap .ls-pkg__tag { color: #1c6b38; }
.ls-pkg--value .ls-pkg__tag { color: #4b3f86; }
.ls-pkg--janet .ls-pkg__tag { color: var(--jt-coral-btn); }
.ls-pkg__total { font: 800 1.6rem/1 var(--font); color: var(--jt-ink); font-variant-numeric: tabular-nums; }
.ls-pkg__pp { font: 500 .78rem/1 var(--font); color: var(--jt-mute); }
.ls-pkg__rows { display: grid; gap: .3rem; margin-block-start: .3rem; }
.ls-pkg__row { display: flex; justify-content: space-between; gap: .6rem; font: 500 .82rem/1.4 var(--font); color: var(--jt-ink-2); }
.ls-pkg__row b { font-variant-numeric: tabular-nums; white-space: nowrap; }
.ls-pkg__why { margin: .4rem 0 0; font: 500 .8rem/1.5 var(--font); color: var(--jt-ink-2); }
.ls-pkg__sep { margin: .35rem 0 0; font: 400 .72rem/1.4 var(--font); color: var(--jt-ink-3, var(--jt-ink-2)); opacity: .85; }

/* ---------- hotel photo gallery ---------- */
.ls-hotel-card__ph { position: relative; }
.ls-hotel-card__ph--none {
  display: flex; align-items: center; justify-content: center;
  min-height: 120px; background: var(--jt-surface-2, #f2f0ec);
  color: var(--jt-ink-3, #8a857c); font: 500 .75rem/1 var(--font); border-radius: 10px;
}
.ls-hotel-card__gal {
  position: absolute; bottom: .5rem; inset-inline-start: .5rem;
  border: 0; border-radius: 999px; padding: .35rem .7rem; cursor: pointer;
  font: 600 .72rem/1 var(--font); color: #fff;
  background: rgba(12, 16, 28, .62); backdrop-filter: blur(6px);
  transition: background .18s ease;
  /* Tap target: the pill rendered at 23px, under the WCAG 2.5.8 (AA) 24px
     floor. inline-flex + min-height lifts the hit area to 32px without
     bloating the overlay into a full button. */
  display: inline-flex; align-items: center; min-height: 32px;
}
.ls-hotel-card__gal:hover, .ls-hotel-card__gal:focus-visible { background: rgba(12, 16, 28, .85); outline: 2px solid #fff3; }
.ls-gal {
  position: fixed; inset: 0; z-index: 1200; display: flex; align-items: center; justify-content: center;
  background: rgba(8, 10, 18, .82); backdrop-filter: blur(10px);
  animation: lsGalIn .22s ease;
}
@keyframes lsGalIn { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .ls-gal { animation: none; } }
.ls-gal__panel { width: min(920px, 94vw); max-height: 92vh; display: flex; flex-direction: column; gap: .5rem; }
.ls-gal__head { display: flex; align-items: center; justify-content: space-between; color: #fff; font: 600 .95rem/1.3 var(--font); }
/* 44px minimum touch targets (WCAG 2.5.8) — 2.75rem = 44px at 16px root. */
.ls-gal__close { border: 0; background: rgba(255,255,255,.12); color: #fff; border-radius: 999px; width: 2.75rem; height: 2.75rem; cursor: pointer; font-size: 1rem; }
.ls-gal__close:focus-visible { outline: 2px solid #fff; }
.ls-gal__stage { position: relative; border-radius: 14px; overflow: hidden; background: #101420; min-height: min(58vh, 480px); display: flex; align-items: center; justify-content: center; }
.ls-gal__img { width: 100%; height: min(58vh, 480px); object-fit: cover; display: block; }
.ls-gal__cap { position: absolute; bottom: 0; inset-inline: 0; padding: .55rem .9rem; color: #fff; font: 500 .78rem/1.4 var(--font); background: linear-gradient(transparent, rgba(8,10,18,.78)); }
.ls-gal__loading { color: #cfd3de; font: 500 .85rem/1.5 var(--font); padding: 2rem; text-align: center; }
.ls-gal__nav { display: flex; align-items: center; justify-content: center; gap: 1rem; color: #fff; font: 600 .8rem/1 var(--font); }
.ls-gal__btn { border: 0; background: rgba(255,255,255,.14); color: #fff; border-radius: 999px; width: 2.75rem; height: 2.75rem; font-size: 1.2rem; cursor: pointer; }
.ls-gal__btn:hover, .ls-gal__btn:focus-visible { background: rgba(255,255,255,.28); outline: 2px solid #fff5; }
@media (max-width: 480px) {
  .ls-gal__panel { width: 100vw; max-height: 100vh; }
  .ls-gal__stage, .ls-gal__img { height: 56vh; border-radius: 0; }
}

/* skeletons */
.ls-hskel-card {
  display: grid; grid-template-columns: 132px minmax(0, 1fr) auto; gap: .8rem;
  background: #fff; border: 1px solid var(--jt-line); border-radius: var(--ls-r); padding: .85rem;
}
.ls-hskel { background: linear-gradient(90deg, var(--jt-cream) 25%, var(--jt-cream-2) 37%, var(--jt-cream) 63%);
  background-size: 400% 100%; animation: ls-shimmer 1.4s ease infinite; border-radius: 8px; }
.ls-hskel--ph { inline-size: 132px; block-size: 104px; }
.ls-hskel-lines { display: grid; gap: .5rem; align-content: start; }
.ls-hskel--t { block-size: 18px; inline-size: 60%; }
.ls-hskel--s { block-size: 12px; inline-size: 40%; }
.ls-hskel--p { block-size: 28px; inline-size: 90px; justify-self: end; }
@keyframes ls-shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .ls-hskel { animation: none; } }

@media (max-width: 620px) {
  /* One column on a phone: a 132px photo beside text leaves the price nowhere
     to go, and the card was breaking rather than stacking. */
  .ls-hotel-card, .ls-hskel-card { grid-template-columns: minmax(0, 1fr); }
  .ls-hotel-card__ph, .ls-hskel--ph { inline-size: 100%; block-size: 150px; }
  .ls-hotel-card__price { text-align: start; justify-items: stretch; }
  .ls-hotel-card__price .ls-card__cta, .ls-hotel-card__price .ls-card__cta--off { inline-size: 100%; text-align: center; }
  .ls-hskel--p { justify-self: stretch; inline-size: 100%; }
}

/* Labelled date items — see labelledRange() in janet_live_search.js.
   A bare "A – B" range is unreadable on an RTL page: bidi paints the first
   item on the right, so the range reads backwards to anyone scanning
   left-to-right. The label travels with the value instead. */
.ls-dh__dates, .ls-flex__w { display: flex; flex-wrap: wrap; gap: 2px 14px; align-items: baseline; }
.ls-dt { white-space: nowrap; }
.ls-dt b {
  font-weight: 700;
  font-size: .74em;
  letter-spacing: .04em;
  opacity: .72;
  margin-inline-end: 4px;
}
.ls-pax { white-space: nowrap; opacity: .8; }

/* ── Recent searches (local-first chips under the form) ───────────────────
   Token-based like the rest of the ls UI, so the chips follow whichever scale
   (cream or dark champagne) the page is wearing — the dark-scale guard rejects
   a hardcoded light surface here, and rightly. */
.ls-recents{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:12px}
.ls-recents__label{font-size:.8rem;font-weight:600;color:var(--jt-mute)}
.ls-recents__chip{
  display:inline-flex;align-items:center;min-height:44px;padding:6px 14px;
  border-radius:999px;border:1px solid var(--jt-line);background:var(--jt-cream-2);
  color:var(--jt-ink);font-size:.85rem;font-weight:600;text-decoration:none;
  transition:border-color .15s,background .15s
}
.ls-recents__chip:hover{border-color:var(--jt-coral-btn)}
.ls-recents__chip:focus-visible{outline:3px solid var(--jt-coral-btn);outline-offset:2px}
.ls-recents__clear{
  border:0;background:none;color:var(--jt-mute);font-size:.78rem;font-weight:600;
  cursor:pointer;min-height:44px;padding:6px 8px
}
.ls-recents__clear:hover{color:var(--jt-coral-btn);text-decoration:underline}

/* ── Save-this-search (results, /api/saved-search) ──────────────────────── */
.ls-save{
  display:flex;align-items:center;flex-wrap:wrap;gap:10px 14px;
  padding:12px 16px;margin-block-end:1.25rem;
  background:var(--jt-cream-2);border:1px solid var(--jt-line);border-radius:var(--ls-r-sm)
}
.ls-save__t{font:700 .92rem/1.3 var(--font);color:var(--jt-ink)}
.ls-save__l{display:inline-flex;align-items:center;gap:6px;font:600 .85rem/1 var(--font);color:var(--jt-ink-2)}
.ls-save__l input{
  width:90px;min-height:44px;padding:.4rem .6rem;border:1px solid var(--jt-line);
  border-radius:10px;font:600 .9rem/1 var(--font);color:var(--jt-ink);background:var(--jp-raised, #fff)
}
.ls-save__btn{
  min-height:44px;padding:.45rem 1.1rem;border:0;border-radius:10px;cursor:pointer;
  background:var(--jt-coral-btn);color:var(--on-sky, #fff);font:700 .88rem/1 var(--font)
}
.ls-save__btn:hover{background:var(--jt-coral-d)}
.ls-save__btn:focus-visible,.ls-save__x:focus-visible{outline:3px solid var(--jt-coral-btn);outline-offset:2px}
.ls-save__btn[disabled]{opacity:.55;cursor:progress}
.ls-save__x{
  min-height:44px;padding:.45rem .8rem;border:0;background:none;cursor:pointer;
  color:var(--jt-mute);font:600 .82rem/1 var(--font);text-decoration:underline
}
.ls-save__note{font:500 .76rem/1.4 var(--font);color:var(--jt-mute);flex-basis:100%}
.ls-save__res{font:600 .85rem/1.4 var(--font);color:var(--jt-ink);flex-basis:100%}

/* ── Favorites (hearts + view) ──────────────────────────────────────────── */
.ls-card{position:relative}
.ls-card .jfav,.ls-hotel-card .jfav{
  position:absolute;top:10px;inset-inline-end:10px;z-index:2;
  width:38px;height:38px;border-radius:50%;border:1px solid var(--jt-line);
  background:var(--jt-cream-2);color:var(--jt-mute);font-size:1.1rem;line-height:1;
  cursor:pointer;display:grid;place-items:center;transition:transform .15s,color .15s
}
.ls-card .jfav:hover,.ls-hotel-card .jfav:hover{transform:scale(1.08)}
.ls-card .jfav:focus-visible,.ls-hotel-card .jfav:focus-visible{outline:3px solid var(--jt-coral-btn);outline-offset:2px}
.jfav.jfav--on{color:var(--jt-coral-btn)}
.jfav__h{font:700 1.05rem/1.3 var(--font-display);color:var(--jt-ink);margin:1.6rem 0 .7rem}
.jfav__list{list-style:none;margin:0;padding:0;display:grid;gap:8px}
.jfav__row{
  display:flex;align-items:center;flex-wrap:wrap;gap:8px 12px;
  padding:10px 14px;background:#fff;border:1px solid var(--jt-line);border-radius:var(--ls-r-sm);
  color:var(--jt-ink)
}
.jfav__type{font:700 .72rem/1 var(--font);color:var(--jt-mute);background:var(--jt-cream-2);border-radius:6px;padding:4px 8px}
.jfav__title{font:600 .9rem/1.35 var(--font);flex:1;min-width:140px}
.jfav__price{font:600 .82rem/1 var(--font);color:var(--jt-ink-2)}
/* "בזמן השמירה" qualifies the price as the one captured when the favourite was
   saved, not today's. It rendered at 10.9px on mobile — a qualifier nobody can
   read is a price with no qualifier at all. */
.jfav__price small{color:var(--jt-mute);font-weight:500;font-size:.75rem}
.jfav__go{font:700 .85rem/1 var(--font);color:var(--jt-coral-btn);text-decoration:none;min-height:44px;display:inline-flex;align-items:center}
.jfav__go:hover{text-decoration:underline}
.jfav__rm{border:0;background:none;color:var(--jt-mute);font:600 .78rem/1 var(--font);cursor:pointer;min-height:44px;padding:4px 8px}
.jfav__rm:hover{color:var(--jt-coral-btn);text-decoration:underline}

/* autocomplete secondary line (country · airport) */
.ls-ac__sub{display:block;font:500 .74rem/1.3 var(--font);color:var(--jt-mute)}

/* Hotel wait narration — the live-rate call is the slow one (11-14s cold).
   States measured fact only; never a fabricated progress bar. */
.ls-hotel-wait{
  margin:0 0 .8rem;font:500 .88rem/1.5 var(--font);color:var(--jt-mute);
  display:flex;align-items:center;gap:.5rem
}
.ls-hotel-wait::before{
  content:"";inline-size:12px;block-size:12px;border-radius:50%;flex:0 0 auto;
  border:2px solid var(--jt-line);border-block-start-color:var(--jt-coral-btn);
  animation:ls-hotel-spin .9s linear infinite
}
@keyframes ls-hotel-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){
  .ls-hotel-wait::before{animation:none;border-block-start-color:var(--jt-line)}
}

/* External hand-off note on the hotel card: the traveler is leaving Janet for
   a different seller, and the final price is set there. Quiet, but present. */
.ls-hotel-card__handoff{
  margin:.5rem 0 0;font:500 .72rem/1.45 var(--font);color:var(--jt-mute);
  max-inline-size:34ch
}
.ls-hotel-card__handoff-x{display:block;font-weight:600;color:var(--jt-ink-2)}

/* Flight filters — factual toggles only, no ranking. */
.ls-fflt{display:flex;flex-wrap:wrap;gap:8px;margin:.5rem 0 1rem}
.ls-fflt small{opacity:.65;font-weight:600;margin-inline-start:.25rem}
.ls-fflt__none{
  margin:1rem 0;padding:1.1rem 1.25rem;border-radius:var(--ls-r-sm);
  background:var(--jt-cream-2);border:1px solid var(--jt-line);
  color:var(--jt-ink);font:500 .95rem/1.5 var(--font);text-align:center
}

/* ---- price truth (2026-08-19) --------------------------------------------
   The headline is the amount payable, prefixed "from" because the engine asks
   for one representative (lowest) rate per hotel. The supplier's own total and
   the confidence wording sit underneath as the breakdown, never as a promise. */
.ls-hotel-card__from { font: 600 .72rem/1 var(--font); color: var(--jt-mute); }
.ls-hotel-card__basis { display: block; font: 500 .7rem/1.3 var(--font); color: var(--jt-mute); font-weight: 500; }
.ls-hotel-card__conf { font: 600 .7rem/1.3 var(--font); color: var(--jt-mute); margin-block-start: .2rem; }
.ls-hotel-card__tax--unknown { color: var(--jt-mute); }

/* ============ HOTEL PREFERENCES ============================================
   Behind a disclosure because the always-visible row is what a first-time
   visitor needs and this is what someone who knows what they want opens.
   Eight more controls on that row would be a wall.

   The three groups are not cosmetic grouping — they are the three support
   levels from live_search/core/hotel_preferences.js, and the sub-headings say
   which is which. A customer ticking "pool" is entitled to know it reorders
   rather than filters, and a customer setting a budget is entitled to know it
   removes.
   ========================================================================= */

.ls-prefs { grid-column: 1 / -1; margin-block-start: .35rem; }

.ls-prefs__toggle {
  display: inline-flex; align-items: center; gap: .5rem;
  /* 44px is the project floor and this sat 4px under it. */
  min-height: 44px; padding: .4rem .85rem;
  border: 1px solid var(--jt-line, rgba(244,241,234,.12));
  border-radius: 999px;
  background: rgba(255, 255, 255, .04);
  color: var(--jt-ink, #F4F1EA);
  font: 600 .88rem/1 var(--font);
  cursor: pointer;
  transition: background .18s ease, border-color .18s ease;
}
.ls-prefs__toggle:hover { background: rgba(232, 201, 137, .12); border-color: rgba(232, 201, 137, .35); }
.ls-prefs__chev { transition: transform .22s ease; }
.ls-prefs__toggle[aria-expanded="true"] .ls-prefs__chev { transform: rotate(180deg); }

/* How many preferences are active, so a collapsed panel is never a silent one. */
.ls-prefs__count {
  display: inline-grid; place-items: center; min-width: 20px; height: 20px;
  padding-inline: .35rem; border-radius: 999px;
  background: var(--jt-coral, #E8C989); color: #0B0F16;
  font: 800 .72rem/1 var(--font);
}
.ls-prefs__count[hidden] { display: none; }

.ls-prefs__body {
  margin-block-start: .7rem; padding: 1rem 1.1rem;
  border: 1px solid var(--jt-line, rgba(244,241,234,.1));
  border-radius: 16px;
  background: rgba(255, 255, 255, .03);
  display: grid; gap: 1.1rem;
}

.ls-prefs__gh {
  margin: 0 0 .55rem;
  font: 700 .8rem/1.3 var(--font);
  color: var(--jt-ink, #F4F1EA);
  letter-spacing: .01em;
}
/* The support level, stated rather than implied. */
.ls-prefs__gh small {
  display: block; margin-block-start: .15rem;
  font: 500 .74rem/1.4 var(--font);
  color: var(--jt-mute, #949CAB);
  letter-spacing: 0;
}

.ls-prefs__row { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: .7rem; }

.ls-pf { display: grid; gap: .3rem; }
.ls-pf > span { font: 600 .76rem/1.2 var(--font); color: var(--jt-mute, #949CAB); }
.ls-pf input, .ls-pf select {
  min-height: 42px; padding: .45rem .7rem;
  border: 1px solid var(--jt-line, rgba(244,241,234,.12));
  border-radius: 10px;
  background: rgba(255, 255, 255, .05);
  color: var(--jt-ink, #F4F1EA);
  font: 600 .9rem/1 var(--font);
}

.ls-prefs__chips { display: flex; flex-wrap: wrap; gap: .45rem; }

/* 44px tall so a thumb can hit it; the checkbox itself is far smaller. */
.ls-chip {
  display: inline-flex; align-items: center; gap: .45rem;
  min-height: 44px; padding: .35rem .8rem;
  border: 1px solid var(--jt-line, rgba(244,241,234,.12));
  border-radius: 999px;
  background: rgba(255, 255, 255, .03);
  font: 600 .85rem/1 var(--font);
  color: var(--jt-ink, #F4F1EA);
  cursor: pointer;
  transition: background .16s ease, border-color .16s ease;
}
.ls-chip:hover { border-color: rgba(232, 201, 137, .38); }
.ls-chip input { width: 16px; height: 16px; accent-color: var(--jt-coral, #E8C989); cursor: pointer; }
.ls-chip:has(input:checked) { background: rgba(232, 201, 137, .14); border-color: rgba(232, 201, 137, .5); }
.ls-chip:has(input:focus-visible) { outline: 2px solid var(--jt-coral, #E8C989); outline-offset: 2px; }

/* What nobody supplies. Rendered from the API's own `capabilities.unavailable`
   rather than hard-coded, so this line cannot claim a limitation that has
   since been lifted — as happened to the amenities on 2026-08-21. */
.ls-prefs__none {
  margin: 0; font: 500 .74rem/1.5 var(--font); color: var(--jt-mute, #949CAB);
}
.ls-prefs__none:empty { display: none; }

.ls-prefs__foot { display: flex; justify-content: flex-end; }
.ls-prefs__clear {
  min-height: 44px; padding: .35rem .9rem;
  border: 1px solid var(--jt-line, rgba(244,241,234,.12)); border-radius: 999px;
  background: none; color: var(--jt-mute, #949CAB);
  font: 600 .82rem/1 var(--font); cursor: pointer;
}
.ls-prefs__clear:hover { color: var(--jt-ink, #F4F1EA); border-color: rgba(232, 201, 137, .35); }

@media (max-width: 620px) {
  .ls-prefs__body { padding: .85rem .8rem; }
  .ls-prefs__row { grid-template-columns: 1fr; }
}

/* The way out of a zero-result search. Cards, not prose: the empty state has
   always ADVISED trying a nearby destination and could never offer one. */
.ls-empty__h { margin: 1.4rem 0 .3rem; font: 700 1rem/1.3 var(--font-display, var(--font)); }
.ls-empty__sub { margin: 0 0 .8rem; }
.ls-empty__card { text-decoration: none; color: inherit; display: block; transition: border-color .18s ease, background .18s ease; }
.ls-empty__card:hover { border-color: rgba(232, 201, 137, .45); background: rgba(232, 201, 137, .08); }
.ls-empty__card:focus-visible { outline: 2px solid var(--jt-coral, #E8C989); outline-offset: 2px; }

/* --- the answered form collapses on a phone ------------------------------ */
/*
 * Measured 2026-08-22 at 390x844: the first clickable CTA sat at 1228px,
 * because eight stacked fields and a submit button fill more than one screen.
 * A customer arriving on a URL that already carries a search had to scroll one
 * and a half screens to reach anything clickable.
 *
 * The form is HIDDEN, not removed, and only on a narrow screen and only once
 * results exist. The chip below it puts it back.
 */
@media (max-width: 767px) {
  .ls-bar__in[data-collapsed] { display: none; }
}

.ls-form-chip {
  display: none;
  width: 100%;
  text-align: start;
  font: inherit;
  font-size: 0.95rem;
  padding: 0.7rem 0.9rem;
  border-radius: 0.7rem;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.04);
  color: inherit;
  cursor: pointer;
  /* A real tap target, for the same reason the CTAs are checked for one. */
  min-height: 44px;
}
@media (max-width: 767px) {
  .ls-bar__in[data-collapsed] + .ls-form-chip { display: block; }
}
.ls-form-chip:hover { background: rgba(255, 255, 255, 0.08); }
.ls-form-chip:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }

/* ==========================================================================
   MOBILE: A STICKY BAR MAY NOT BE TALLER THAN THE VIEWPORT
   ==========================================================================
   `.ls-bar` was `position: sticky; top: 0` at EVERY width, and below 520px
   `.ls-bar__in` collapses to a single column: eight fields plus a submit
   button, stacked. Measured 2026-09-02 at 320x568 and 390x844 — the bar is
   taller than a short phone viewport, so a sticky element pinned to the top
   pushes its own submit button off the bottom of the screen, and the page
   below it cannot be scrolled to bring the button back because the bar is
   what is pinned.

   That is worst on the FIRST visit, before any result exists: there is no
   chip to collapse into and nothing else on the screen worth sticking.

   So the bar is STATIC below 768px, and becomes sticky again only once the
   form has collapsed to the one-line summary chip — which is short, and which
   is exactly the state where keeping the search reachable is worth a pinned
   element. A browser without :has() simply keeps the static bar, which is the
   safe direction to fail.

   Above 768px nothing changes: the fields sit on one or two rows and the bar
   is a normal sticky header. */
@media (max-width: 767px) {
  .ls-bar { position: static; }
  .ls-bar:has(.ls-bar__in[data-collapsed]) { position: sticky; inset-block-start: 0; }
}

/* ==========================================================================
   THE PRICE BLOCK — a number is never published without its unit
   ==========================================================================
   Display-size tabular numerals so a column of prices aligns digit for digit,
   then the unit, the passengers and the dates, then the measurement stamp in
   fog grey. The order is deliberate: the figure is what draws the eye, and
   everything that qualifies it sits directly under it rather than beside it,
   where a scan can miss it. */
.ls-card__price {
  font: 800 clamp(1.55rem, 4.4vw, 1.9rem)/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
  color: var(--jt-ink);
}
.ls-card[data-best] .ls-card__price { color: var(--jp-gold, var(--jt-coral)); }
.ls-card__per {
  font: 500 .74rem/1.35 var(--font);
  color: var(--jp-text-3, var(--jt-mute));
  text-align: end;
  max-inline-size: 15rem;
}
@media (max-width: 640px) { .ls-card__per { text-align: start; } }

/* ==========================================================================
   HAIRLINES, NOT SHADOWS
   ==========================================================================
   A drop shadow is light scattered by an object above a surface. On a
   near-black ground there is nothing for it to scatter against, so it renders
   as a smear that makes the card edge LESS legible, not more. Depth on this
   surface ladder is carried by the border and by the step in background
   lightness, which is what the ladder is for.

   Scoped to the dark theme by a token probe rather than a media query, so it
   follows the theme rather than the operating system. */
@supports (color: color-mix(in srgb, red, blue)) {
  :root:has(.ls) .ls-card,
  :root:has(.ls) .ls-sum__c,
  :root:has(.ls) .ls-flex__c { box-shadow: none; }
}
.ls-card { box-shadow: none; border-color: var(--jt-line); }
.ls-card:hover {
  box-shadow: none;
  transform: none;
  border-color: color-mix(in srgb, var(--jp-gold, #C9A24A) 42%, transparent);
}
.ls-card[data-best] {
  border-color: color-mix(in srgb, var(--jp-gold, #C9A24A) 55%, transparent);
  box-shadow: none;
}

/* ==========================================================================
   NO LINK IS A STATE, NOT A BROKEN BUTTON
   ==========================================================================
   Rendered when the booking contract has no usable https URL. It must not
   look like something that can be pressed: an inert control that looks live
   is the one failure a customer will report as "the site is broken" and we
   will not be able to reproduce. */
.ls-card__cta--off {
  min-height: 44px; padding: .55rem 1.1rem;
  font: 600 .82rem/1.25 var(--font);
  color: var(--jp-text-3, var(--jt-mute));
  background: none;
  border: 1px dashed var(--jt-line);
  border-radius: var(--ls-r-sm);
  display: inline-flex; align-items: center; justify-content: center; text-align: center;
  cursor: default;
}

/* ==========================================================================
   PARTIAL RESULTS
   ==========================================================================
   Reuses #ls-stale, which is already the strip that says something about
   these prices. `data-partial` is the stronger case: the results on the screen
   are real and the search behind them did not finish. Accent hairline, no
   fill, because the results below it are the thing to read. */
.ls-stale-note[data-partial] {
  border: 1px solid color-mix(in srgb, var(--jp-gold, #C9A24A) 45%, transparent);
  background: color-mix(in srgb, var(--jp-gold, #C9A24A) 8%, transparent);
  color: var(--jt-ink-2);
  border-radius: var(--ls-r-sm);
  padding: .7rem .9rem;
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .7rem;
}
.ls-stale-note[data-partial] b { color: var(--jt-ink); }

/* ==========================================================================
   THE RECOVERY SEARCH
   ========================================================================== */
.ls-recovery__ins {
  margin: .9rem 0 0; padding: 0; list-style: none;
  display: grid; gap: .35rem;
  font: 500 .85rem/1.5 var(--font); color: var(--jt-ink-2);
}
.ls-recovery__ins li { padding-inline-start: 1rem; position: relative; }
.ls-recovery__ins li::before {
  content: ""; position: absolute; inset-inline-start: 0; inset-block-start: .55em;
  inline-size: 5px; block-size: 5px; border-radius: 50%;
  background: var(--jp-gold, var(--jt-coral));
}
/* The alternative's own basis line: what the total covers, for how many
   people. Fog grey, under the figure, never beside it. */
.ls-flex__b { font: 500 .74rem/1.4 var(--font); color: var(--jp-text-3, var(--jt-mute)); }

/* ==========================================================================
   ZERO HORIZONTAL OVERFLOW AT 320
   ==========================================================================
   The narrowest phone still in real use. `min-width` on a grid child defaults
   to `auto`, which is the child's CONTENT width, so one long unbreakable
   string — an airline name, a URL, a 6-digit price — makes the whole RTL page
   scroll sideways. These are the three columns that carry provider text. */
@media (max-width: 430px) {
  .ls-card { grid-template-columns: minmax(0, 1fr); }
  .ls-card__main, .ls-card__side { min-inline-size: 0; }
  .ls-card__side { min-width: 0; }
  .ls-card__facts { gap: .3rem .7rem; }
  .ls-sellers__r { gap: .5rem; }
  .ls-flex { grid-template-columns: minmax(0, 1fr); }
  .ls-flex__c { min-inline-size: 0; overflow-wrap: anywhere; }
  .ls-card__disclaimer { max-width: none; text-align: start; }
}

/* ==========================================================================
   ONE ACCENT, ON THE PRICE AND ON THE ACTION
   ==========================================================================
   Measured in a browser 2026-09-02, 390x844, dark theme:

     .ls-card__cta   linear-gradient champagne, --on-sky ink   the BOOKING action
     .ls-refresh     flat #C9A24A,             --on-sky ink   "check the price again"

   Two full-width champagne slabs stacked in one 140px column, and the louder
   of the two is a utility that re-queries a price. `:root .ls-refresh` in
   janet_premium.css is a sensible global default for a control that elsewhere
   stands alone; on this card it competes with the only action that matters.

   So it is demoted HERE, scoped to the flight card, and nowhere else. The
   accent survives as the border and the label, which is what the control needs
   to be findable without being the loudest thing on the card. Specificity is
   (0,3,0) against the theme's (0,1,1) — a class chain, not !important, so a
   later theme change can still win by saying so. */
.ls .ls-card__side .ls-refresh,
.ls .ls-flex__c .ls-refresh {
  background: none;
  color: var(--jp-gold, var(--jt-coral-btn));
  border: 1px solid color-mix(in srgb, var(--jp-gold, #C9A24A) 38%, transparent);
}
.ls .ls-card__side .ls-refresh:hover,
.ls .ls-flex__c .ls-refresh:hover {
  background: color-mix(in srgb, var(--jp-gold, #C9A24A) 12%, transparent);
  border-color: color-mix(in srgb, var(--jp-gold, #C9A24A) 60%, transparent);
}

/* ==========================================================================
   THE LABEL CHIPS WERE A LIGHT-MODE ISLAND
   ==========================================================================
   Measured on the dark theme: `.ls-tag[data-t="cheapest"]` rendered
   #1E7A4B on #E8F5ED — a pale mint block sitting on a #16181C card, at the
   top of every result. `direct` (#E7F0FB), `fastest` (#F3EBFA) and
   `best_value` (#FDF1DF) are the same shape.

   This is the "component palette the theme has never heard of" defect from
   .claude/rules/janet-visual-system.md, one level down: --ls-cheap-bg and
   --ls-warn-bg are declared in THIS sheet's :where(.ls) block, and the two
   blue and purple washes are not tokens at all, so nothing the theme does can
   reach any of them.

   Re-pointed here rather than in the theme, because these tokens belong to
   this component and the theme cannot be edited from this seam. Each chip
   keeps its hue as INK and takes a transparent tint of the same hue as its
   wash, so it reads as a label on the surface it is actually on and the
   category colouring survives. All four measured against --jp-raised:
   9.54, 9.14, 8.42 and 9.43 to one. */
.ls-tag {
  background: color-mix(in srgb, var(--jt-line, #878C95) 30%, transparent);
  color: var(--jt-ink-2);
  border: 1px solid transparent;
}
.ls-tag[data-t="cheapest"] {
  color: #5FD39B;
  background: color-mix(in srgb, #5FD39B 12%, transparent);
  border-color: color-mix(in srgb, #5FD39B 26%, transparent);
}
.ls-tag[data-t="direct"] {
  color: #8CBEF5;
  background: color-mix(in srgb, #8CBEF5 12%, transparent);
  border-color: color-mix(in srgb, #8CBEF5 26%, transparent);
}
.ls-tag[data-t="fastest"] {
  color: #C0A6F0;
  background: color-mix(in srgb, #C0A6F0 12%, transparent);
  border-color: color-mix(in srgb, #C0A6F0 26%, transparent);
}
.ls-tag[data-t="best_value"] {
  color: #E3B664;
  background: color-mix(in srgb, #E3B664 12%, transparent);
  border-color: color-mix(in srgb, #E3B664 26%, transparent);
}
/* The same two washes reach the seller list and the freshness dot. */
.ls-sellers__r { background: color-mix(in srgb, var(--jt-line, #878C95) 22%, transparent); }
.ls-sellers__r[data-cheapest] {
  background: color-mix(in srgb, #5FD39B 12%, transparent);
  color: #5FD39B;
}
.ls-fresh[data-s="stale"] { color: #D6A46B; }
.ls-fresh[data-s="stale"] .ls-fresh__d { background: #D6A46B; }
.ls-fresh__d { background: #5FD39B; }

/* ==========================================================================
   THE FOUR REMAINING LIGHT-SCALE INKS — MEASURED, NOT ESTIMATED
   ==========================================================================
   Computed against --jp-raised (#16181C), the surface these actually sit on:

     .ls-flex__ext   #241A13   1.04:1   the secondary CTA on every nearby-date
                                        and recovery card — INVISIBLE
     --ls-cheap      #1E7A4B   3.34:1   below AA
     --ls-warn       #9A5B12   3.28:1   below AA
     --ls-stale      #8A5A2B   3.03:1   below AA

   All four were correct when this sheet was written for cream, and all four
   are declared in a palette janet_premium.css does not re-point — so the
   conversion to the dark ladder reached the surfaces and never reached these.

   `.ls-flex__ext` is the one that mattered: its ink was pinned LITERALLY, and
   its own comment explains why — `--jt-ink` was #241A13 on the light scale and
   the token could not be trusted. The theme re-points --jt-ink now, so the
   reason has expired and only the workaround was left. A guard that outlived
   its policy.

   The light values stay as the declared defaults above; these are the values
   the page actually renders, and the theme is loaded unconditionally on every
   reader page (asserted by test_one_visual_system). Zero specificity, so the
   theme can still take these over if it ever learns about them. */
/*
 * NOT re-pointed as --ls-cheap / --ls-warn. janet_premium.css re-points those
 * on :root (0,1,0) and a :where() declaration here (0,0,0) loses to it, so a
 * token override at this seam would have been dead CSS that measured correct
 * in the file and rendered the old value in the browser. Verified in a browser
 * before writing this: .ls-field__err computed rgb(138,90,18), the theme's
 * value, not the one this sheet asked for.
 *
 * The theme's own semantic pair is below AA on the surface the theme itself
 * establishes — measured against --jp-raised (#16181C):
 *
 *     --ls-cheap  #1F6B4A   3.06:1
 *     --ls-warn   #8A5A12   3.01:1
 *
 * That belongs to janet_premium.css and is reported rather than reached into
 * from here. What this sheet owns is the TEXT that renders in them, so each of
 * those is given a measured ink of its own and the theme keeps the washes.
 */
.ls-field__err {
  color: #E8B366;                                                  /* 9.37:1 */
  background: color-mix(in srgb, #E8B366 10%, transparent);
}
.ls-field input[aria-invalid="true"] { border-color: #E8B366; }
.ls-trust__note, .ls-error {
  color: #E8B366;
  background: color-mix(in srgb, #E8B366 10%, transparent);
  border-color: color-mix(in srgb, #E8B366 28%, transparent);
}
.ls-cal__day[data-b="cheap"] {
  color: #5FD39B;                                                  /* 9.54:1 */
  background: color-mix(in srgb, #5FD39B 12%, transparent);
  border-color: color-mix(in srgb, #5FD39B 30%, transparent);
}
.ls-prog__row[data-done] .ls-prog__dot { background: #5FD39B; }

.ls-flex__ext {
  /* The token, not the literal. See above. */
  color: var(--jt-ink);
  border-color: color-mix(in srgb, var(--jp-gold, #C9A24A) 30%, transparent);
}
.ls-flex__ext:hover {
  border-color: color-mix(in srgb, var(--jp-gold, #C9A24A) 60%, transparent);
  background: color-mix(in srgb, var(--jp-gold, #C9A24A) 10%, transparent);
}

/*
 * The alternative's price takes the accent, not the savings green.
 *
 * `--ls-cheap` is the colour this product uses for a MEASURED SAVING, and the
 * recovery and date-alternate cards claim no saving — they publish a total for
 * different dates. Printing it in the savings colour is a claim made in
 * pigment, which is the hardest kind to notice and the hardest to retract.
 */
.ls-flex__s {
  color: var(--jp-gold, var(--jt-coral));
  font-variant-numeric: tabular-nums;
}
/* Except where the API genuinely measured one: [data-save] is set only when
   the server published a saving_ils. */
.ls-flex__c[data-save] .ls-flex__s { color: var(--ls-cheap); }
.ls-flex__c[data-save] {
  border-color: color-mix(in srgb, var(--ls-cheap) 35%, transparent);
  background: var(--jp-raised, #fff);
}
