/* ==== assets/tokens/colors.css ==== */
/* =============================================================================
   KamerFind — colour tokens. Navy and gold.

   This is a RE-PIGMENTATION of NomadKit, not a replacement of it. Every role
   name, every ramp stop and every component selector is unchanged; only the
   values behind them move. That works because NomadKit's role contract already
   had the polarity navy/gold needs:

     --color-primary   was sand-500  with DARK ink on it  -> gold takes that slot
     --color-secondary was ocean-500 with WHITE ink on it -> navy takes that slot

   So nomadkit.css and app.css keep every rule. Nothing reads a raw ramp except
   where a specific tint is wanted.

   Every ratio quoted below is measured. AA small text needs 4.50, AA large
   (>=18.66px bold or >=24px) needs 3.00.
   ============================================================================= */

:root{

/* ---- Navy: the logo's blue, at the logo's saturation ---------------------
   The ramp used to say it was the logo's blue walked down in lightness, and it
   was not: it sat at 63% saturation against the mark's 87%, which is what made
   the chrome read as grey rather than blue. Every stop now carries the mark's
   212 degree hue at the chroma the luminance allows.

   The LUMINANCE of each stop is unchanged, deliberately. Every ratio this file
   documents was measured against those values, and moving hue without moving
   lightness keeps all eleven of them true: white on the chrome is 15.85,
   neutral-350 on the footer 6.94, info ink on its tint 10.53. Re-pigmenting is
   safe precisely because it is not a re-lightening. */
--navy-25:#EFF6FF;  --navy-50:#DEEDFF;  --navy-100:#BDDCFF; --navy-200:#82BCFF;
--navy-300:#2E8FFF; --navy-400:#0062D3; --navy-500:#003D85; --navy-600:#00326D;
--navy-700:#002753; --navy-800:#002249; --navy-900:#001631; --navy-950:#000F1F;

/* ---- Gold: the logo's orange with the red pulled out --------------------- */
--gold-50:#FCF6E6;  --gold-100:#F8E7BE; --gold-200:#F2D183; --gold-300:#EDBF4F;
--gold-400:#E3A81F; --gold-500:#C88A10; --gold-600:#B0790D; --gold-700:#7A5207;

/* ---- Neutrals: the same twelve stops, hue rotated from sand to navy ------ */
--neutral-0:#FFFFFF;   --neutral-25:#F7F9FC;  --neutral-50:#EEF2F8;
--neutral-100:#E3E9F2; --neutral-200:#D3DDEB; --neutral-300:#B3C2D6;
--neutral-350:#8FA2BA; --neutral-400:#7B8FA9; --neutral-450:#5E748E;
--neutral-500:#51647C; --neutral-600:#41536B; --neutral-700:#2E3F55;
--neutral-800:#1C2B3E; --neutral-900:#0A1628;
/* 350 and 450 are new stops, and both exist because a ratio demanded one.
   350 is the only value clearing AA as tertiary text on BOTH the navy-800
   chrome and the navy-900 footer; 450 is the only one clearing AA as a
   placeholder while still reading lighter than text the user has typed. */

/* ---- Semantic ramps: kept, retinted to sit on a cool ground -------------- */
--forest-50:#E4F3E9;  --forest-100:#BEE0C8; --forest-500:#146B3A;
--forest-600:#0F542D; --forest-700:#0A3C20;
--warning-50:#FBEDD2; --warning-100:#F6DCA8; --warning-500:#8A5606; --warning-600:#7A4C05;
--error-50:#FCE7E6;   --error-100:#F8CFCD;  --error-500:#C62A22;   --error-600:#B4231C;

/* ---- Surfaces ------------------------------------------------------------ */
--color-bg:var(--neutral-25);
--color-surface:var(--neutral-0);
--color-surface-sunken:var(--neutral-50);
--color-page:var(--neutral-100);        /* the desktop frame's ground */
--color-chrome:var(--navy-800);         /* header and tab bar field */
--color-chrome-deep:var(--navy-900);    /* footer and toast field */

/* ---- Borders ------------------------------------------------------------- */
--color-border:var(--neutral-200);         /* decorative: card edge, divider */
--color-border-strong:var(--neutral-300);  /* decorative emphasis */
--color-border-control:var(--neutral-400); /* real control edges — 3.31:1 */

/* ---- Text on light grounds ----------------------------------------------- */
--text-primary:var(--neutral-900);      /* 18.13 on white */
--text-secondary:var(--neutral-600);    /*  7.85 on white */
--text-muted:var(--neutral-500);        /*  6.06 on white, 4.97 on page */
--text-placeholder:var(--neutral-450);  /*  4.81 on white */
--text-on-navy:#FFFFFF;                 /* 10.40 on navy-500 */
--text-on-gold:var(--neutral-900);      /*  8.54 on gold-400 */
--text-on-forest:#FFFFFF;               /*  6.44 on forest-500 */

/* ---- Roles: gold carries action, navy carries structure ------------------ */
--color-primary:var(--gold-400);
--color-primary-hover:var(--gold-500);
--color-primary-active:var(--gold-600);
/* NEVER #FFFFFF. White on gold-400 is 2.13:1 — it fails at any size, and it is
   the single easiest way to wreck this palette. */
--color-primary-text:var(--neutral-900);
--color-primary-ink:var(--gold-700);    /* gold as TEXT, on a light tint only */

--color-secondary:var(--navy-500);
--color-secondary-hover:var(--navy-600);
--color-secondary-active:var(--navy-700);
--color-secondary-text:#FFFFFF;

--color-link:var(--navy-400);           /* 5.69 on white */

--color-success:var(--forest-500);  --color-success-bg:var(--forest-50);  --color-success-ink:var(--forest-600);
--color-warning:var(--warning-500); --color-warning-bg:var(--warning-50); --color-warning-ink:var(--warning-600);
--color-error:var(--error-500);     --color-error-bg:var(--error-50);     --color-error-ink:var(--error-600);
--color-info:var(--navy-500);       --color-info-bg:var(--navy-50);       --color-info-ink:var(--navy-600);

--focus-ring:0 0 0 3px rgba(0,98,211,.38);
--scrim:rgba(0,15,31,.52);
--photo-scrim:linear-gradient(to top,rgba(0,15,31,.80),rgba(0,15,31,0));

/* The mark's own gradient, for the two places it is allowed to appear: the
   splash and the tour progress rail. */
--brand-gradient:linear-gradient(100deg,#003D85 0%,#0062D3 38%,#E3A81F 82%,#C88A10 100%);
}

/* =============================================================================
   INVERSE SURFACE

   Put data-surface="navy" on the header, tab bar or footer and every existing
   component follows — .nk-btn--ghost, .nk-tag, .kf-lang, .kf-link, .kf-iconbtn.
   No new variant classes, because the components read role tokens and this
   scope only changes what those roles mean inside it.
   ============================================================================= */
[data-surface="navy"]{
  --color-surface:var(--navy-800);
  --color-surface-sunken:rgba(255,255,255,.07);
  --color-border:rgba(255,255,255,.13);
  --color-border-strong:rgba(255,255,255,.24);
  --color-border-control:rgba(255,255,255,.34);
  --text-primary:#FFFFFF;
  --text-secondary:var(--neutral-300);
  --text-muted:var(--neutral-350);
  --color-link:var(--gold-300);
  --color-secondary:rgba(255,255,255,.12);
  --color-secondary-hover:rgba(255,255,255,.18);
  --color-secondary-text:#FFFFFF;
  /* The light focus ring is invisible on navy; gold is the only ring that
     survives both grounds. */
  --focus-ring:0 0 0 3px rgba(237,191,79,.55);
  color:#FFFFFF;
}

/* ==== assets/tokens/typography.css ==== */
/* The design system ships this file with a bare @import of Lora + Work Sans at
   the top. A CSS @import is render-blocking and serialises behind this
   stylesheet, which on a 3G handset holds first paint for a second or more. The
   fonts are loaded from the HTML instead — preconnect + a non-blocking link with
   font-display:swap — so the import is deliberately absent here. Re-exporting
   this file from the design system will put it back; delete it again. */
:root{
--font-display:'Lora',Georgia,'Times New Roman',serif;
--font-body:'Work Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
--font-mono:'SFMono-Regular',Consolas,'Liberation Mono',monospace;
--text-xs:12px;--text-sm:14px;--text-base:16px;--text-lg:18px;--text-xl:20px;--text-2xl:24px;--text-3xl:30px;--text-4xl:36px;--text-5xl:48px;--text-6xl:60px;
--leading-tight:1.1;--leading-snug:1.25;--leading-normal:1.5;--leading-relaxed:1.65;
--weight-regular:400;--weight-medium:500;--weight-semibold:600;--weight-bold:700;
}

/* ==== assets/tokens/spacing.css ==== */
:root{
--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-8:32px;--space-10:40px;--space-12:48px;--space-16:64px;--space-20:80px;--space-24:96px;--space-32:128px;
--container-max:1200px;
}

/* ==== assets/tokens/effects.css ==== */
:root{
--radius-sm:6px;--radius-md:10px;--radius-lg:16px;--radius-xl:24px;--radius-full:999px;
--shadow-sm:0 1px 2px rgba(0,22,49,.06);
--shadow-md:0 4px 12px rgba(0,22,49,.10);
--shadow-lg:0 12px 32px rgba(0,22,49,.18);
--ease-standard:cubic-bezier(0.4,0,0.2,1);
/* Material's standard curve is fine for a colour change and too polite for
   anything that enters: it eases in, so the first frames barely move and the
   interface reads as slow at exactly the moment somebody is watching hardest.
   These two are for entrances and for things that travel. */
--ease-out:cubic-bezier(0.23,1,0.32,1);
--ease-sheet:cubic-bezier(0.32,0.72,0,1);
--duration-fast:120ms;
--duration-base:200ms;
}
*{box-sizing:border-box}
body{margin:0;background:var(--color-bg);color:var(--text-primary);font-family:var(--font-body)}

/* ==== assets/nomadkit.css ==== */
/* NomadKit component layer — authored from the design system's readme rules:
   hover = one step darker in the same hue, active = one step darker + scale(0.98),
   borders over shadows, 6px inputs / 10px buttons+cards / 16px surfaces / pill tags. */

/* ---------- Icon (Heroicons outline, CSS-mask recolored to currentColor) ---------- */
.nk-icon{
  display:inline-block;flex:none;width:24px;height:24px;
  background-color:currentColor;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain;
}

/* ---------- Button ---------- */
.nk-btn{
  --nk-btn-bg:var(--color-primary);
  --nk-btn-bg-hover:var(--color-primary-hover);
  --nk-btn-bg-active:var(--color-primary-active);
  --nk-btn-fg:var(--color-primary-text);
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  font-family:var(--font-body);font-size:14px;font-weight:var(--weight-semibold);
  line-height:1;white-space:nowrap;
  padding:0 18px;height:44px;
  border:1px solid transparent;border-radius:var(--radius-md);
  background:var(--nk-btn-bg);color:var(--nk-btn-fg);
  cursor:pointer;text-decoration:none;
  transition:background var(--duration-fast) var(--ease-standard),
             transform var(--duration-fast) var(--ease-standard);
}
.nk-btn:hover{background:var(--nk-btn-bg-hover);text-decoration:none;color:var(--nk-btn-fg)}
.nk-btn:active{background:var(--nk-btn-bg-active);transform:scale(.98)}
.nk-btn:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.nk-btn[disabled]{opacity:.45;cursor:not-allowed;pointer-events:none}
.nk-btn--sm{height:34px;padding:0 13px;font-size:13px}
.nk-btn--lg{height:50px;padding:0 22px;font-size:15px}
.nk-btn--block{width:100%}
.nk-btn--secondary{
  --nk-btn-bg:var(--color-secondary);--nk-btn-bg-hover:var(--color-secondary-hover);
  --nk-btn-bg-active:var(--color-secondary-active);--nk-btn-fg:var(--text-on-navy);
}
.nk-btn--ghost{
  --nk-btn-bg:transparent;--nk-btn-bg-hover:var(--color-surface-sunken);
  --nk-btn-bg-active:var(--neutral-100);--nk-btn-fg:var(--text-primary);
  border-color:var(--color-border);
}
.nk-btn--success{
  --nk-btn-bg:var(--color-success);--nk-btn-bg-hover:var(--forest-600);
  --nk-btn-bg-active:var(--forest-700);--nk-btn-fg:var(--text-on-forest);
}

/* ---------- Field (Input / Select) ---------- */
.nk-field{display:flex;flex-direction:column;gap:6px}
.nk-field__label{font-size:12px;font-weight:var(--weight-semibold);color:var(--text-secondary)}
.nk-input,.nk-select{
  font-family:var(--font-body);font-size:14px;color:var(--text-primary);
  height:42px;padding:0 13px;width:100%;
  background:var(--color-surface);
  border:1px solid var(--color-border);border-radius:var(--radius-sm);
  outline:none;
  transition:border-color var(--duration-fast) var(--ease-standard);
}
.nk-input::placeholder{color:var(--neutral-400)}
.nk-input:hover,.nk-select:hover{border-color:var(--color-border-strong)}
.nk-input:focus,.nk-select:focus{border-color:var(--color-secondary);box-shadow:var(--focus-ring)}
.nk-select{
  appearance:none;padding-right:36px;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%23695A48' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m19.5 8.25-7.5 7.5-7.5-7.5'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 11px center;background-size:16px;
}

/* ---------- Checkbox ---------- */
.nk-check{
  appearance:none;flex:none;width:20px;height:20px;margin:0;cursor:pointer;
  border:1px solid var(--color-border-strong);border-radius:var(--radius-sm);
  background:var(--color-surface);
  transition:background var(--duration-fast) var(--ease-standard),border-color var(--duration-fast) var(--ease-standard);
}
.nk-check:checked{
  background:var(--color-secondary);border-color:var(--color-secondary);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%23fff' stroke-width='3'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m4.5 12.75 6 6 9-13.5'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:center;background-size:14px;
}
.nk-check:focus-visible{outline:none;box-shadow:var(--focus-ring)}

/* ---------- Switch ---------- */
.nk-switch{
  appearance:none;position:relative;flex:none;width:40px;height:24px;margin:0;cursor:pointer;
  border:1px solid var(--color-border-strong);border-radius:var(--radius-full);
  background:var(--color-surface-sunken);
  transition:background var(--duration-fast) var(--ease-standard),border-color var(--duration-fast) var(--ease-standard);
}
.nk-switch::after{
  content:'';position:absolute;top:2px;left:2px;width:18px;height:18px;
  border-radius:var(--radius-full);background:var(--color-surface);box-shadow:var(--shadow-sm);
  transition:transform var(--duration-fast) var(--ease-standard);
}
.nk-switch:checked{background:var(--color-secondary);border-color:var(--color-secondary)}
.nk-switch:checked::after{transform:translateX(16px)}
.nk-switch:focus-visible{outline:none;box-shadow:var(--focus-ring)}

/* ---------- Tag / Badge ---------- */
.nk-tag{
  display:inline-flex;align-items:center;height:32px;padding:0 14px;
  font-size:13px;font-weight:var(--weight-semibold);
  border:1px solid var(--color-border);border-radius:var(--radius-full);
  background:var(--color-surface);color:var(--text-secondary);
  cursor:pointer;white-space:nowrap;
  transition:background var(--duration-fast) var(--ease-standard),
             border-color var(--duration-fast) var(--ease-standard),
             transform var(--duration-fast) var(--ease-standard);
}
.nk-tag:hover{background:var(--color-surface-sunken)}
.nk-tag:active{transform:scale(.98)}
.nk-tag[aria-pressed="true"]{
  background:var(--color-primary);border-color:var(--color-primary);color:var(--neutral-900);
}
.nk-tag[aria-pressed="true"]:hover{background:var(--color-primary-hover);border-color:var(--color-primary-hover)}
.nk-badge{
  display:inline-flex;align-items:center;
  font-size:10px;font-weight:var(--weight-semibold);
  padding:4px 9px;border-radius:var(--radius-full);
  background:var(--color-surface-sunken);color:var(--text-secondary);
}
.nk-badge--success{background:var(--forest-50);color:var(--color-success)}
.nk-badge--error{background:var(--color-error);color:#fff}
.nk-badge--solid-success{background:var(--color-success);color:#fff}

/* ==== assets/app.css ==== */
/* ================= KamerFind app shell ================= */
html,body{height:100%}
body{background:var(--neutral-100);overscroll-behavior-y:none}
a{color:var(--color-secondary);text-decoration:none}
a:hover{color:var(--color-secondary-hover);text-decoration:underline}
button{font:inherit}
:focus-visible{outline:none;box-shadow:var(--focus-ring)}

@keyframes kfUp{from{transform:translateY(100%)}to{transform:translateY(0)}}
@keyframes kfFade{from{opacity:0}to{opacity:1}}
@keyframes kfSpin{to{transform:rotate(360deg)}}
@keyframes kfRise{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
@keyframes kfShimmer{from{background-position:-380px 0}to{background-position:380px 0}}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
}

/* --- Outer page: bare viewport on phones, framed device on wide screens --- */
.kf-page{min-height:100%;display:flex;flex-direction:column;align-items:center;background:var(--neutral-100)}

.kf-device{
  position:relative;width:100%;
  height:100dvh;background:var(--color-bg);overflow:hidden;flex:none;

  /* The bottom bar's height was written out in five separate places that all
     had to agree — the bar, the scrollport's padding, its sticky variant, the
     sticky price bar and the toast. Naming it once means a short-viewport
     override is one line instead of five chances to disagree. */
  --kf-tabbar:68px;

  /* The header's height, named for the same reason the tab bar's is: the bar
     and the scrollport underneath it have to agree, and on the desktop frame a
     mock status bar pushes both down by 46px. One variable, two readers. */
  --kf-appbar:54px;
  --kf-chrome-top:env(safe-area-inset-top,0px);

  /* In landscape the notch moves from the top edge to the leading edge, and
     nothing here read left/right — while both entry points opt into drawing
     under the cutout with viewport-fit=cover. max() makes these exact no-ops
     wherever the inset is 0, which is every device in portrait. */
  --kf-gutter-l:max(20px, env(safe-area-inset-left,0px));
  --kf-gutter-r:max(20px, env(safe-area-inset-right,0px));
}

.kf-scrollport{
  position:absolute;top:calc(var(--kf-chrome-top) + var(--kf-appbar));bottom:0;left:0;right:0;
  overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;
  padding-bottom:var(--kf-tabbar);
}
.kf-scrollport[data-sticky="true"]{padding-bottom:calc(var(--kf-tabbar) * 2)}

.kf-footnote{display:none;width:390px;font-size:11px;color:var(--text-muted);line-height:1.6;text-wrap:pretty}

/* A stray closing brace sat here. At the top level a CSS parser folds it into
   the next selector, which silently dropped the rule after it — the one that
   hides scrollbars on every sideways rail. */


.kf-scroll{scrollbar-width:none}
.kf-scroll::-webkit-scrollbar{width:0;height:0}

.kf-screen{animation:kfFade .18s ease}
.kf-pad{padding:6px 20px 26px;display:flex;flex-direction:column;gap:22px}
.kf-pad--tight{gap:16px}
.kf-stack{display:flex;flex-direction:column;gap:12px}
.kf-stack--sm{gap:8px}
.kf-row{display:flex;align-items:center;justify-content:space-between;gap:10px}
.kf-row--baseline{align-items:baseline}
.kf-h1{font-family:var(--font-display);font-size:28px;font-weight:var(--weight-semibold);color:var(--text-primary);line-height:1.15;text-wrap:pretty;margin:0}
.kf-h2{font-family:var(--font-display);font-size:20px;font-weight:var(--weight-semibold);color:var(--text-primary);margin:0}
.kf-h3{font-size:13px;font-weight:var(--weight-semibold);color:var(--text-primary);margin:0}
.kf-sub{font-size:13px;color:var(--text-secondary);line-height:1.5}
.kf-muted{font-size:12px;color:var(--text-muted)}
.kf-link{cursor:pointer;font-size:13px;font-weight:var(--weight-semibold);color:var(--color-secondary);background:none;border:0;padding:0}
.kf-iconbtn{background:none;border:0;padding:0;cursor:pointer;display:inline-flex;color:inherit}

/* --- Language toggle --- */
/* overflow:hidden is what rounds the two segments, but it also clipped the
   focus ring off the EN/FR toggle entirely. The ring is redrawn as an outline,
   which is not clipped by an ancestor. */
.kf-lang{display:flex;border:1px solid var(--color-border);border-radius:var(--radius-full);overflow:hidden}
.kf-lang button:focus-visible{outline:2px solid var(--color-secondary);outline-offset:-2px;box-shadow:none}
.kf-lang button{
  cursor:pointer;padding:5px 12px;font-size:12px;font-weight:var(--weight-semibold);
  background:transparent;color:var(--text-secondary);border:0;
}
.kf-lang button[aria-pressed="true"]{background:var(--color-primary);color:var(--neutral-900)}
.kf-lang--lg button{padding:6px 14px}
/* On navy the light outline is invisible, and gold is the only ring that reads
   on both grounds. The rest of the toggle needs no override: it already draws
   from role tokens the inverse scope redefines. */
[data-surface="navy"] .kf-lang button:focus-visible{outline-color:var(--gold-300)}
[data-surface="navy"] .kf-lang button{color:var(--neutral-300)}

/* --- Media slot: real photo when configured, labelled placeholder when not --- */
.kf-media{
  position:relative;width:100%;height:100%;
  background:var(--color-surface-sunken);overflow:hidden;
  display:flex;align-items:center;justify-content:center;text-align:center;
}
.kf-media__img{width:100%;height:100%;object-fit:cover;display:block}
.kf-media__label{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;
  padding:12px;font-size:10px;line-height:1.35;color:var(--text-muted);
}
.kf-media__label .nk-icon{color:var(--neutral-400)}
.kf-media--lg .kf-media__label{font-size:12px}

/* --- Hero --- */
.kf-hero{border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;background:var(--color-surface)}
.kf-hero__slider{position:relative;height:158px;border-bottom:1px solid var(--color-border);background:var(--color-surface-sunken);overflow:hidden}
.kf-hero__slide{position:absolute;inset:0;opacity:0;transition:opacity 700ms var(--ease-standard)}
.kf-hero__slide[data-active="true"]{opacity:1}
.kf-hero__caption{
  /* The bottom padding keeps the caption's text clear of the dots that now sit
     along the bottom edge; the scrim still runs to the bottom behind them, so
     white dots stay legible over a bright photograph. */
  position:absolute;left:0;right:0;bottom:0;padding:20px 14px 26px;z-index:2;
  background:linear-gradient(to top,rgba(0,15,31,.80),rgba(0,15,31,0));
  color:#fff;font-size:11px;font-weight:var(--weight-semibold);text-align:left;letter-spacing:.02em;
}
/* Bottom-centre, where a thumb already rests and where every photo carousel on
   a phone puts them. Top-right put the only control on the hero in the corner
   furthest from the hand holding the device. */
.kf-hero__dots{
  position:absolute;left:50%;transform:translateX(-50%);bottom:12px;
  display:flex;gap:5px;z-index:3;
}
.kf-hero__dot{width:14px;height:6px;border-radius:var(--radius-full);border:0;padding:0;cursor:pointer}
/* The pill itself is drawn by ::before and scaled — see the craft pass at the
   end of this file. Animating width here relaid out the row on every slide. */
.kf-hero__slider[data-photos="true"] .kf-hero__dot{background:rgba(255,255,255,.55)}
.kf-hero__slider[data-photos="true"] .kf-hero__dot[data-active="true"]{background:#fff}
.kf-hero__body{padding:18px 16px 16px;display:flex;flex-direction:column;gap:14px}
.kf-hero__tagline{font-family:var(--font-display);font-size:26px;font-weight:var(--weight-semibold);line-height:1.15;color:var(--text-primary);text-wrap:pretty}

/* --- Cards --- */
.kf-card{
  border:1px solid var(--color-border);border-radius:var(--radius-md);
  background:var(--color-surface);overflow:hidden;
  transition:box-shadow var(--duration-fast) var(--ease-standard);
  text-align:left;padding:0;width:100%;font:inherit;color:inherit;display:block;
}
.kf-card:hover{box-shadow:var(--shadow-md)}
button.kf-card{cursor:pointer}

.kf-railrow{display:flex;gap:12px;overflow-x:auto;margin:0 -20px;padding:0 20px 4px}
.kf-region{flex:none;width:148px;cursor:pointer}
.kf-region__media{height:82px}
.kf-region__body{padding:10px 12px 12px}
.kf-region__name{font-size:14px;font-weight:var(--weight-semibold);color:var(--text-primary)}
.kf-region__city{font-size:12px;color:var(--text-secondary)}
.kf-region__count{font-size:11px;color:var(--text-muted);margin-top:4px}
.kf-regiongrid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.kf-regiongrid .kf-region{width:auto}
.kf-regiongrid .kf-region__media{height:76px}

.kf-mini{cursor:pointer;display:flex;gap:12px;padding:10px}
.kf-mini__media{width:98px;height:82px;flex:none;border-radius:var(--radius-sm)}
.kf-mini__body{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1}
.kf-mini__title{font-size:14px;font-weight:var(--weight-semibold);color:var(--text-primary);line-height:1.3}
.kf-mini__price{margin-top:auto;font-size:14px;font-weight:var(--weight-bold);color:var(--text-primary)}

.kf-promo{
  cursor:pointer;width:100%;text-align:left;font:inherit;
  border:1px solid var(--gold-200);background:var(--gold-50);border-radius:var(--radius-md);
  padding:16px;display:flex;align-items:center;gap:12px;
}
.kf-promo__title{font-size:14px;font-weight:var(--weight-semibold);color:var(--neutral-900)}
.kf-promo__sub{font-size:12px;color:var(--text-secondary);line-height:1.4}
.kf-promo .nk-icon{color:var(--color-primary-ink)}

/* --- Browse --- */
.kf-browsehead{
  padding:6px 20px 14px;display:flex;flex-direction:column;gap:12px;
  background:var(--color-surface);border-bottom:1px solid var(--color-border);
}
.kf-browsehead__title{font-family:var(--font-display);font-size:21px;font-weight:var(--weight-semibold);color:var(--text-primary);line-height:1.15}
.kf-catrail{display:flex;gap:8px;overflow-x:auto;margin:0 -20px;padding:2px 20px 4px}
.kf-catrail>*{flex:none}
.kf-results{padding:16px 20px 26px;display:flex;flex-direction:column;gap:14px}

.kf-listing__media{position:relative;height:154px;cursor:pointer;width:100%;border:0;padding:0}
.kf-listing__flags{position:absolute;left:10px;top:10px;display:flex;gap:6px;z-index:2}
.kf-listing__count{
  position:absolute;right:10px;bottom:10px;z-index:2;
  background:rgba(0,15,31,.72);color:#fff;font-size:10px;padding:3px 9px;border-radius:var(--radius-full);
}
.kf-listing__body{padding:12px 14px 14px;display:flex;flex-direction:column;gap:6px}
.kf-listing__top{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
/* A link, but the headline of a card — not a run of body text with an
   underline through it. It reads as the title it is and behaves as the link it
   has become: the whole word-wrapped block is the target, and the underline
   appears on hover where a pointer can ask for it. */
.kf-listing__title{
  font-size:15px;font-weight:var(--weight-semibold);color:var(--text-primary);line-height:1.3;
  text-decoration:none;
}
.kf-listing__title:hover{text-decoration:underline;text-underline-offset:2px}
.kf-listing__price{font-size:16px;font-weight:var(--weight-bold);color:var(--text-primary)}
.kf-listing__foot{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:6px}

.kf-service{cursor:pointer;padding:14px;display:flex;gap:12px;text-align:left}
.kf-avatar{
  width:60px;height:60px;flex:none;border-radius:var(--radius-full);
  background:var(--color-surface-sunken);display:flex;align-items:center;justify-content:center;
  font-size:17px;font-weight:var(--weight-bold);color:var(--text-muted);overflow:hidden;
}
.kf-avatar img{width:100%;height:100%;object-fit:cover}
.kf-service__body{display:flex;flex-direction:column;gap:4px;min-width:0;flex:1}
.kf-service__name{font-size:15px;font-weight:var(--weight-semibold);color:var(--text-primary)}
.kf-service__idline{display:flex;align-items:center;gap:6px;flex-wrap:wrap}

.kf-empty{
  border:1px dashed var(--color-border-strong);border-radius:var(--radius-md);
  padding:30px 20px;text-align:center;font-size:14px;color:var(--text-secondary);line-height:1.55;
}
.kf-empty--left{text-align:left;padding:22px 18px;font-size:13px}

/* --- Detail --- */
.kf-gallery{position:relative;height:276px;touch-action:pan-y}
.kf-gallery__frame{position:absolute;inset:0}
.kf-gallery__nav{position:absolute;top:14px;left:14px;right:14px;display:flex;align-items:center;justify-content:space-between;z-index:3}
.kf-round{
  width:36px;height:36px;border:0;padding:0;cursor:pointer;border-radius:var(--radius-full);
  background:rgba(255,255,255,.96);box-shadow:var(--shadow-sm);
  display:flex;align-items:center;justify-content:center;color:var(--text-primary);
}
/* Arrows sit vertically centred over the photo, out of the way of the
   back/favourite/share row along the top. Hidden from pointer-less touch
   devices would be wrong — a touch user who cannot swipe still needs them. */
.kf-gallery__arrow{
  position:absolute;top:50%;transform:translateY(-50%);z-index:3;
  background:rgba(255,253,247,.92);
}
.kf-gallery__arrow--prev{left:14px}
.kf-gallery__arrow--next{right:14px}
.kf-gallery__count{
  position:absolute;right:14px;bottom:14px;z-index:3;
  background:rgba(0,15,31,.72);color:#fff;font-size:11px;padding:4px 10px;border-radius:var(--radius-full);
}
.kf-detail{padding:18px 20px 24px;display:flex;flex-direction:column;gap:20px}
.kf-detail__title{font-family:var(--font-display);font-size:25px;font-weight:var(--weight-semibold);line-height:1.15;color:var(--text-primary);text-wrap:pretty}
.kf-chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px}
.kf-chip{font-size:11px;font-weight:var(--weight-semibold);padding:5px 11px;border-radius:var(--radius-full);background:var(--color-surface-sunken);color:var(--text-secondary)}
.kf-chip--success{background:var(--forest-50);color:var(--color-success)}
.kf-chip--error{background:var(--color-error-bg);color:var(--error-600)}
/* The feature grid is now grouped: Water & electricity together, Security
   together. Twenty tiles in one undifferentiated run is a list nobody reads to
   the end; four short labelled runs is four quick answers. */
.kf-highlights{display:flex;flex-direction:column;gap:16px}
.kf-featgroup{display:flex;flex-direction:column;gap:8px}
.kf-featgroup .kf-h3{color:var(--text-muted);font-size:11px;letter-spacing:.06em;text-transform:uppercase}
.kf-feats{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px}

/* Counts and measurements are facts about the property, not things it has, so
   they get a line of their own above the features rather than a tile among
   them. */
.kf-numbers{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:4px}
.kf-number{
  font-size:13px;font-weight:var(--weight-semibold);color:var(--text-primary);
  background:var(--color-surface-sunken);border:1px solid var(--color-border);
  border-radius:var(--radius-full);padding:6px 13px;
}

/* Whatever the fixed lists could not hold, as the rep typed it. */
.kf-extras{margin:14px 0 0;padding-left:20px;display:flex;flex-direction:column;gap:5px}
.kf-extras li{font-size:13px;line-height:1.5;color:var(--text-secondary);text-wrap:pretty}

@media (max-width:400px){
  .kf-feats{grid-template-columns:1fr 1fr}
}
.kf-highlight{
  border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);
  padding:12px 8px;display:flex;flex-direction:column;align-items:center;gap:7px;text-align:center;
}
.kf-highlight .nk-icon{color:var(--color-secondary)}
.kf-highlight span{font-size:11px;font-weight:var(--weight-medium);color:var(--text-primary);line-height:1.3}
.kf-prose{font-size:14px;line-height:1.6;color:var(--text-secondary);text-wrap:pretty}

.kf-locked{
  border:1px dashed var(--color-border-strong);border-radius:var(--radius-md);
  padding:16px;background:var(--color-surface-sunken);display:flex;flex-direction:column;gap:8px;
}
.kf-locked__num{font-size:20px;font-weight:var(--weight-bold);letter-spacing:.12em;color:var(--neutral-400)}
.kf-unlocked{
  border:1px solid var(--forest-100);border-radius:var(--radius-md);padding:16px;background:var(--forest-50);
  display:flex;flex-direction:column;gap:12px;animation:kfRise .2s var(--ease-standard);
}
.kf-unlocked__note{font-size:12px;color:var(--color-success);font-weight:var(--weight-semibold)}
.kf-unlocked__num{font-size:22px;font-weight:var(--weight-bold);letter-spacing:.03em;color:var(--text-primary)}
.kf-actions{display:flex;gap:8px}
.kf-actions>*{flex:1}
.kf-safety{border-top:1px solid var(--color-border);padding-top:16px;display:flex;align-items:center;gap:10px}
.kf-safety .nk-icon{color:var(--text-muted)}
.kf-safety span{font-size:12px;color:var(--text-muted);line-height:1.5}

/* --- Provider --- */
.kf-provhead{padding:8px 20px 18px;background:var(--color-surface);border-bottom:1px solid var(--color-border);display:flex;flex-direction:column;gap:14px}
.kf-provhead__id{display:flex;gap:14px;align-items:center}
.kf-provhead__id .kf-avatar{width:72px;height:72px;font-size:21px}
.kf-provhead__name{font-family:var(--font-display);font-size:23px;font-weight:var(--weight-semibold);color:var(--text-primary);line-height:1.15}
.kf-galrail{display:flex;gap:10px;overflow-x:auto;margin:0 -20px;padding:0 20px 4px}
.kf-galrail .kf-media{flex:none;width:132px;height:96px;border-radius:var(--radius-md);border:1px solid var(--color-border)}
.kf-review{border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);padding:13px 14px;display:flex;flex-direction:column;gap:5px}
.kf-review__who{font-size:13px;font-weight:var(--weight-semibold);color:var(--text-primary)}
.kf-review__stars{font-size:12px;color:var(--color-primary-ink)}
.kf-review__text{font-size:13px;color:var(--text-secondary);line-height:1.5}

/* --- Profile --- */
.kf-profile__id{display:flex;align-items:center;gap:14px}
.kf-profile__id .kf-avatar{width:64px;height:64px;font-size:19px}
.kf-profile__name{font-family:var(--font-display);font-size:22px;font-weight:var(--weight-semibold);color:var(--text-primary);line-height:1.15}
.kf-ledger{border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);padding:13px 14px;display:flex;flex-direction:column;gap:8px}
.kf-ledger__top{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
.kf-ledger__date{flex:none;font-size:10px;font-weight:var(--weight-semibold);color:var(--text-muted)}
.kf-ledger__num{font-size:17px;font-weight:var(--weight-bold);letter-spacing:.03em;color:var(--text-primary)}
.kf-links{display:flex;flex-direction:column;gap:2px}
.kf-links button{
  cursor:pointer;display:flex;align-items:center;gap:12px;padding:15px 4px;width:100%;
  border:0;border-bottom:1px solid var(--color-border);background:none;text-align:left;color:inherit;
}
.kf-links button>span{flex:1;font-size:14px;color:var(--text-primary)}
.kf-links .nk-icon{color:var(--text-secondary)}
.kf-links .nk-icon--chev{color:var(--text-muted)}

/* --- List-with-us form --- */
.kf-consent{
  border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface-sunken);
  padding:14px;display:flex;gap:11px;align-items:flex-start;cursor:pointer;
}
.kf-consent span{font-size:12px;color:var(--text-secondary);line-height:1.5}
.kf-sent{display:flex;flex-direction:column;gap:14px;align-items:flex-start;padding-top:20px;animation:kfRise .2s var(--ease-standard)}
.kf-sent .nk-icon{color:var(--color-success)}
.kf-sent h2{font-family:var(--font-display);font-size:25px;font-weight:var(--weight-semibold);color:var(--text-primary);line-height:1.2;margin:0}

/* --- Support chat --- */
.kf-chat{display:flex;flex-direction:column;min-height:100%}
/* The header names this screen, so the head is only the live status line. */
.kf-chat__head{padding:9px 20px;background:var(--color-surface);border-bottom:1px solid var(--color-border);display:flex;align-items:center;gap:12px}
.kf-chat__status{font-size:12px;color:var(--color-success)}
.kf-chat__log{flex:1;padding:16px 20px;display:flex;flex-direction:column;gap:10px}
.kf-bubble{max-width:78%;padding:11px 14px;border-radius:var(--radius-lg);font-size:14px;line-height:1.5}
.kf-bubble--them{align-self:flex-start;background:var(--color-surface);color:var(--text-primary);border:1px solid var(--color-border)}
.kf-bubble--me{align-self:flex-end;background:var(--color-secondary);color:#fff}
.kf-chat__compose{
  position:sticky;bottom:0;background:var(--color-surface);border-top:1px solid var(--color-border);
  padding:12px 16px;display:flex;gap:10px;align-items:center;
}
.kf-chat__compose .nk-input{border-radius:var(--radius-full);background:var(--color-bg)}

/* --- Sticky bar + tab bar --- */
.kf-sticky{
  position:absolute;left:0;right:0;bottom:var(--kf-tabbar);z-index:4;
  background:var(--color-surface);border-top:1px solid var(--color-border);
  padding:12px 18px;display:flex;align-items:center;justify-content:space-between;gap:12px;
  box-shadow:0 -4px 14px rgba(0,22,49,.06);
}
.kf-sticky>div:first-child{flex:1 1 auto;min-width:0}
.kf-sticky .nk-btn{flex:0 0 auto;font-size:13px;padding:0 14px}
.kf-sticky__caption{font-size:11px;color:var(--text-muted)}
.kf-sticky__price{font-size:17px;font-weight:var(--weight-bold);color:var(--text-primary);white-space:nowrap}

.kf-tabs{
  position:absolute;left:0;right:0;bottom:0;height:var(--kf-tabbar);z-index:5;
  padding-bottom:env(safe-area-inset-bottom,0px);
  background:var(--color-chrome);
  border-top:1px solid rgba(255,255,255,.10);
  box-shadow:0 -1px 0 rgba(255,255,255,.06) inset;
  display:grid;grid-template-columns:repeat(5,1fr);align-items:stretch;
}
.kf-tabs button{
  /* Stretch to the full 68px bar rather than shrink-wrapping to the icon and
     label — content height was 41px, under the 44px touch floor. */
  cursor:pointer;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:4px;
  min-height:44px;border:0;background:none;color:var(--neutral-350);
}
.kf-tabs button[aria-current="page"]{color:var(--gold-300)}
.kf-tabs span{
  font-size:10px;font-weight:var(--weight-semibold);color:currentColor;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:0 2px;
}

/* --- Payment sheet --- */
.kf-scrim{position:absolute;inset:0;z-index:8;background:var(--scrim);border:0;padding:0;animation:kfFade .16s ease}
.kf-sheet{
  position:absolute;left:0;right:0;bottom:0;z-index:9;max-height:92%;overflow-y:auto;
  background:var(--color-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
  padding:10px 22px 26px;box-shadow:var(--shadow-lg);animation:kfUp .22s var(--ease-standard);
}
.kf-sheet__grip{width:40px;height:4px;border-radius:var(--radius-full);background:var(--color-border-strong);margin:0 auto 18px}
.kf-sheet__title{font-family:var(--font-display);font-size:22px;font-weight:var(--weight-semibold);color:var(--text-primary);line-height:1.2}
.kf-sheet__body{display:flex;flex-direction:column;gap:16px}
.kf-amount{
  display:flex;align-items:center;justify-content:space-between;padding:14px 16px;
  border-radius:var(--radius-md);background:var(--color-surface-sunken);border:1px solid var(--color-border);
}
.kf-amount span{font-size:13px;color:var(--text-secondary)}
.kf-amount strong{font-size:20px;font-weight:var(--weight-bold);color:var(--text-primary)}
.kf-methods{display:flex;flex-direction:column;gap:9px}
.kf-method{
  cursor:pointer;display:flex;align-items:center;gap:12px;padding:14px;width:100%;text-align:left;font:inherit;
  border-radius:var(--radius-md);border:1px solid var(--color-border);background:var(--color-surface);
}
.kf-method[aria-pressed="true"]{border-color:var(--color-secondary);background:var(--navy-50)}
.kf-method__swatch{
  width:38px;height:38px;flex:none;border-radius:var(--radius-sm);
  display:flex;align-items:center;justify-content:center;
  font-size:10px;font-weight:var(--weight-bold);color:var(--neutral-900);
}
.kf-method__body{flex:1;min-width:0}
.kf-method__name{font-size:14px;font-weight:var(--weight-semibold);color:var(--text-primary)}
.kf-method__hint{font-size:11px;color:var(--text-muted)}
.kf-method .nk-icon{color:var(--color-success)}
.kf-spinner{
  width:44px;height:44px;border-radius:var(--radius-full);
  border:3px solid var(--color-border);border-top-color:var(--color-secondary);
  animation:kfSpin .9s linear infinite;
}
.kf-pending{display:flex;flex-direction:column;align-items:center;gap:16px;padding:26px 10px 34px;text-align:center}
.kf-pending h2{font-family:var(--font-display);font-size:20px;font-weight:var(--weight-semibold);color:var(--text-primary);margin:0}
.kf-pending p{font-size:13px;color:var(--text-secondary);line-height:1.6;max-width:280px;margin:0}
.kf-payerr{
  border:1px solid var(--error-100);background:var(--color-error-bg);color:var(--error-600);
  border-radius:var(--radius-md);padding:12px 14px;font-size:13px;line-height:1.5;
}
/* Quiet, centred, and the same shape as the gate's "Not now", because it is the
   same decision: leaving without doing the thing. It carries the 44px hit area
   .kf-link already provides, so it is reachable by thumb without competing with
   the pay button above it. */
/* Both classes, because the generic .kf-link is declared further down the file
   and would otherwise win on source order and paint this gold — the same colour
   as the Pay button immediately above it. Two gold controls stacked, one of
   which abandons the purchase, is the wrong hierarchy on a payment screen: the
   way out should be findable, not competitive. */
.kf-link.kf-paycancel{
  align-self:center;padding:8px 12px;margin-top:-2px;
  color:var(--text-secondary);font-weight:var(--weight-medium);
}
/* Inside the pending sheet the note has to out-rank `.kf-pending p`, which is
   the styling for the instruction above it. It is the quieter of the two: the
   instruction is what to do now, this is only what happens if you leave. */
.kf-pending .kf-payfoot{font-size:11px;color:var(--text-muted);max-width:300px;margin-top:-6px}
.kf-pending .kf-paycancel{margin-top:0}
.kf-payfoot{font-size:11px;color:var(--text-muted);text-align:center;line-height:1.5}

/* ---- Find one listing by its code -----------------------------------------
   A visitor who arrives from TikTok already knows which room they want, so this
   sits above the region rail and is the first thing they can act on. Deliberately
   quieter than the hero: it is a shortcut for the few who hold a code, not a
   second search for everyone else. */
.kf-codefind{
  display:flex;flex-direction:column;gap:8px;
  padding:14px;border-radius:var(--radius-lg);
  background:var(--color-surface-sunken);border:1px solid var(--color-border);
}
.kf-codefind__label{font-size:13px;font-weight:var(--weight-semibold);color:var(--text-primary)}
.kf-codefind__row{display:flex;gap:8px}
/* Monospace and wide tracking: these six characters are read off a moving video
   and retyped, so every digit has to be unmistakable. */
.kf-codefind__input{
  flex:1;min-width:0;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  letter-spacing:.14em;text-transform:lowercase;
}
.kf-codefind__go{flex:none}
.kf-codefind__hint{font-size:11px;color:var(--text-muted);margin:0;line-height:1.5}
.kf-codefind__err{font-size:12px;color:var(--color-error);margin:0;line-height:1.5}

/* ---- The Mobile Money number ----------------------------------------------
   +237 sits inside the field's border rather than beside it, so the whole thing
   reads as one number — "+237 650 14 34 78" — and there is only one place to
   type. The wrapper draws the frame and takes the focus ring, because the frame
   is what a person sees as the field; the input inside gives up its own border
   so there is no box within a box. */
.kf-tel{
  display:flex;align-items:center;gap:9px;padding-left:13px;
  background:var(--color-surface);
  border:1px solid var(--color-border);border-radius:var(--radius-sm);
  transition:border-color var(--duration-fast) var(--ease-standard);
}
.kf-tel:hover{border-color:var(--color-border-strong)}
.kf-tel:focus-within{border-color:var(--color-secondary);box-shadow:var(--focus-ring)}
.kf-tel__cc{
  flex:none;font-size:14px;color:var(--text-secondary);
  font-variant-numeric:tabular-nums;-webkit-user-select:none;user-select:none;
  padding-right:9px;border-right:1px solid var(--color-border);
}
.kf-tel__input{
  border:0;background:transparent;padding-left:0;border-radius:0;min-width:0;
  font-variant-numeric:tabular-nums;letter-spacing:.02em;
}
.kf-tel__input:hover,.kf-tel__input:focus{border-color:transparent;box-shadow:none}

/* --- Toast --- */
.kf-toast{
  position:absolute;left:18px;right:18px;bottom:calc(var(--kf-tabbar) + var(--space-4));z-index:10;
  background:var(--neutral-900);color:var(--neutral-25);border-radius:var(--radius-md);
  padding:13px 16px;font-size:13px;line-height:1.45;box-shadow:var(--shadow-lg);
  animation:kfRise .2s var(--ease-standard);
}
.kf-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* =============================== Header ==================================
   A context bar, not a third way to navigate. The tab bar owns destinations
   and the back control owns hierarchy; this says whose app it is, where you
   are, and holds the one setting that has to reach every screen.
   ========================================================================= */
/* The BAND spans the window. */
.kf-appbar{
  position:absolute;top:var(--kf-chrome-top);left:0;right:0;z-index:5;
  height:var(--kf-appbar);display:flex;align-items:center;
  padding-left:var(--kf-gutter-l);padding-right:var(--kf-gutter-r);
  background:var(--color-chrome);
  border-bottom:1px solid rgba(255,255,255,.10);
}
/* Its CONTENTS are the row, and on wide screens they take the same measure as
   the content below — so the mark sits directly above the first card instead of
   out at the window's edge. This is the flex container at every size; putting
   the layout on the band itself broke the moment a wrapper appeared between
   them. */
.kf-appbar__inner{
  display:flex;align-items:center;gap:10px;width:100%;min-width:0;
}
@media (min-width:700px){
  .kf-appbar__inner{max-width:var(--kf-maxw,720px);margin-inline:auto;gap:12px}
}

/* Desktop navigation, in the header where the pointer is. Hidden until the tab
   bar goes away, so the two never both claim to be the navigation. */
.kf-navlinks{display:none;align-items:center;gap:2px;margin-left:14px}
.kf-navlinks button{
  background:none;border:0;cursor:pointer;font:inherit;font-size:13px;
  padding:8px 12px;border-radius:var(--radius-sm);color:var(--neutral-300);
}
.kf-navlinks button:hover{background:rgba(255,255,255,.08);color:#FFFFFF}
.kf-navlinks button[aria-current="page"]{
  background:rgba(237,191,79,.14);color:var(--gold-300);font-weight:var(--weight-semibold);
}
.kf-appbar__gap{flex:1 1 auto;min-width:8px}

.kf-brand{
  display:inline-flex;align-items:center;gap:9px;min-width:0;
  background:none;border:0;padding:0;cursor:pointer;color:inherit;
}
.kf-brand__mark{
  border-radius:7px;display:block;flex:none;
  /* The mark is a photograph-like gradient on a navy field; a hairline stops
     its darkest corner from dissolving into the header. */
  box-shadow:0 0 0 1px rgba(255,255,255,.16);
}
.kf-brand__word{
  font-family:var(--font-display);font-size:16px;font-weight:var(--weight-bold);
  color:#FFFFFF;letter-spacing:-.01em;white-space:nowrap;
}
.kf-brand__word b{color:var(--gold-300);font-weight:inherit}

.kf-appbar__back{color:#FFFFFF;flex:none}
.kf-appbar__title{
  font-size:15px;font-weight:var(--weight-semibold);color:#FFFFFF;
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* Truncates rather than wraps: a two-line header would shift every screen
   under it by 20px the moment a region with a long name is selected. */
.kf-appbar__place{
  display:inline-flex;align-items:center;gap:4px;min-width:0;max-width:38%;
  font-size:12px;color:var(--neutral-300);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.kf-appbar__place .nk-icon{flex:none;color:var(--gold-300)}
.kf-appbar .kf-lang button{padding:4px 9px;font-size:11px}

/* Save and share moved to the trailing edge when back left the photograph. */
.kf-gallery__nav--end{justify-content:flex-end}

/* =============================== Footer ==================================
   Not fixed and not everywhere. A phone already spends 68px on the tab bar,
   and repeating legal links under every listing would push the price below
   the fold. It appears where a footer is actually read.
   ========================================================================= */
.kf-foot{
  background:var(--color-chrome-deep);
  padding:26px var(--kf-gutter-l) 24px var(--kf-gutter-r);
  display:flex;flex-direction:column;gap:18px;
}
.kf-foot__brand{display:flex;align-items:center;gap:11px}
.kf-foot__brand>span{display:flex;flex-direction:column;gap:3px;min-width:0}
.kf-foot__word{
  font-family:var(--font-display);font-size:18px;font-weight:var(--weight-bold);
  color:#FFFFFF;letter-spacing:-.01em;
}
.kf-foot__word b{color:var(--gold-300);font-weight:inherit}
.kf-foot__tag{font-size:12px;color:var(--neutral-300);line-height:1.45;text-wrap:pretty}

.kf-foot__note{
  margin:0;font-size:12px;line-height:1.55;color:var(--neutral-350);
  text-wrap:pretty;
  border-left:2px solid var(--gold-400);padding-left:11px;
}

.kf-foot__cols{display:grid;grid-template-columns:1fr 1fr;gap:18px 14px}
.kf-foot__col{display:flex;flex-direction:column;align-items:flex-start;gap:7px;min-width:0}
.kf-foot__col h2{
  margin:0 0 1px;font-family:var(--font-body);
  font-size:11px;font-weight:var(--weight-semibold);letter-spacing:.07em;
  text-transform:uppercase;color:var(--neutral-350);
}
.kf-foot__link{
  background:none;border:0;padding:0;cursor:pointer;text-align:left;
  font-size:13px;line-height:1.35;color:var(--neutral-100);
}
.kf-foot__link:hover{color:var(--gold-300);text-decoration:underline}
.kf-foot__link:focus-visible{outline:2px solid var(--gold-300);outline-offset:3px;box-shadow:none}

.kf-foot__base{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;
  padding-top:16px;border-top:1px solid rgba(255,255,255,.13);
  font-size:11px;line-height:1.5;color:var(--neutral-350);
}

/* Inside the Profile screen the footer has to escape .kf-pad's 20px gutter,
   because a footer inset from the edges reads as another card. */
.kf-foot--inscreen{
  margin:4px calc(var(--kf-gutter-l) * -1) -26px calc(var(--kf-gutter-r) * -1);
  border-radius:var(--radius-lg) var(--radius-lg) 0 0;
}
/* Profile already carries a labelled Language row a few pixels above this. Two
   toggles that do the same thing, one under the other, is a question the user
   has to answer twice. */
.kf-foot--inscreen .kf-lang{display:none}

/* Under the desktop device frame, matched to its width. */
/* Hidden by default, which on a phone means always. It is switched on at the
   tablet breakpoint above — a footer under a bottom tab bar on a 640px-tall
   screen is a third band of chrome competing for the same job. */
.kf-foot--page{display:none;width:100%}
.kf-foot--page > *{width:100%;max-width:var(--kf-maxw,720px);margin-inline:auto}

/* =============================== Legal =================================== */
.kf-legal{display:flex;flex-direction:column;gap:18px}
.kf-legal__sec{display:flex;flex-direction:column;gap:5px}
.kf-note{
  margin:0;padding:11px 13px;border-radius:var(--radius-md);
  font-size:12px;line-height:1.55;text-wrap:pretty;
}
.kf-note--warn{
  background:var(--color-warning-bg);color:var(--color-warning-ink);
  border:1px solid var(--warning-100);
}

/* ========================= Short viewports ===============================
   A 360x640 Android and any phone in landscape spend a fixed 122px on chrome
   before a single listing is drawn. Below 640px of height that is a fifth of
   the screen, so the header and the tab bar each give back what they can
   without dropping under the 44px touch floor: the bar's buttons stay 44px
   tall, the bar itself just stops padding around them.
   ========================================================================= */
@media (max-height:640px){
  .kf-device{--kf-appbar:48px;--kf-tabbar:56px}
  .kf-tabs button{gap:2px}
  .kf-tabs button span{font-size:10px}
  .kf-brand__word{font-size:15px}
  .kf-appbar__place{font-size:11px}
}

/* Landscape on a phone is the same squeeze with less to give: the tab bar's
   labels are what goes, because five icons in a row are already learnable and
   the alternative is a listing card with no room to be a card. */
@media (max-height:460px){
  .kf-device{--kf-appbar:44px;--kf-tabbar:46px}
  .kf-tabs button span{display:none}
  .kf-tabs button{gap:0}
}

/* =============================================================================
   WIDE SCREENS

   This used to draw a 390px phone, bezel and all, with a painted-on
   "9:41 · MTN CM · 4G" status bar, centred in whatever space the browser had.
   That is a picture of the product rather than the product: on a laptop it
   threw away the entire screen to show a mockup, and it told every visitor
   arriving from a search result that they were looking at a demo.

   So the phone is gone and the layout is real. There are three sizes and each
   one earns its differences:

     phone   the fixed app shell, unchanged. A scrollport between a fixed
             header and a fixed tab bar, and NO footer — a screen that already
             spends 122px on chrome cannot also spend a footer, and the tab bar
             is where navigation lives.

     tablet  the DOCUMENT scrolls instead of a box inside it. Header sticks,
             tab bar stays fixed to the viewport because a tablet is still a
             touch device, and the footer arrives at the end of the scroll.

     desktop no bottom bar. Navigation moves into the header, where a pointer
             already is, and the content takes a readable measure instead of
             stretching a phone column across 1600px.
   ============================================================================= */

/* --------------------------------------------------------- tablet and up --- */
@media (min-width:700px){
  .kf-page{align-items:stretch}

  /* The shell stops being a fixed box. Everything below follows from this. */
  .kf-device{
    height:auto;min-height:100dvh;overflow:visible;
    display:flex;flex-direction:column;
    --kf-gutter-l:max(28px, env(safe-area-inset-left,0px));
    --kf-gutter-r:max(28px, env(safe-area-inset-right,0px));
  }

  .kf-appbar{position:sticky;top:0}

  /* Static flow, so the page scrolls and a footer can exist after the content.
     The bottom padding still clears the fixed tab bar. */
  .kf-scrollport{
    position:static;overflow:visible;flex:1 0 auto;
    padding-bottom:calc(var(--kf-tabbar) + 8px);
  }
  .kf-scrollport[data-sticky="true"]{padding-bottom:calc(var(--kf-tabbar) * 2 + 8px)}

  /* Fixed to the VIEWPORT now, not absolute inside a box that no longer has a
     bottom edge in view. */
  .kf-tabs{position:fixed}
  .kf-sticky{position:fixed}

  /* A phone column stretched to 1600px is unreadable, so the content takes a
     measure and centres. The gutters stay on the outside so the header and the
     footer still span the full width. */
  .kf-screen{width:100%;max-width:var(--kf-maxw,720px);margin-inline:auto}
  .kf-appbar,.kf-foot{--kf-inner:var(--kf-maxw,720px)}

  /* The gutters stay. max-width caps the measure and margin:auto centres it,
     but neither of those puts space between the text and the window edge when
     the window is narrower than the measure — which is every width from 1024px
     to 1120px. box-sizing is border-box, so this padding is inside the cap
     rather than added to it. */
  .kf-pad{padding:18px var(--kf-gutter-r) 34px var(--kf-gutter-l);gap:26px}

  /* Two things the phone layout scrolls sideways can simply be laid out. */
  .kf-railrow{
    display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
    gap:14px;overflow:visible;margin:0;padding:0;
  }
  .kf-railrow .kf-card{width:auto}
  .kf-regiongrid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:14px}
  .kf-regiongrid .kf-region__media{height:110px}
  .kf-catrail{margin:0;padding:2px 0 4px;flex-wrap:wrap;overflow:visible}

  .kf-list--grid{
    display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
    gap:14px;align-items:start;
  }

  /* 280px under a 664px measure is a letterbox strip, not a photograph. */
  .kf-hero__slider{height:320px}
  .kf-hero__body{padding:22px 22px 20px}
  .kf-hero__tagline{font-size:32px}

  /* The footer is a tablet-and-up thing. On a phone it would be a third band of
     chrome under a tab bar that already does the job. */
  .kf-foot--page{display:flex}
  /* The tab bar is fixed to the viewport here, so the last thing on the page
     needs to clear it or the copyright line sits underneath it. */
  .kf-footnote{
    display:block;width:100%;max-width:var(--kf-maxw,720px);margin:0 auto;
    padding:18px 0 calc(var(--kf-tabbar) + 24px);
  }
}

/* ------------------------------------------------------------- desktop --- */
@media (min-width:1024px){
  .kf-device{--kf-maxw:1120px;--kf-gutter-l:32px;--kf-gutter-r:32px}

  /* No bottom bar. A fixed thumb-reach strip across a 27-inch monitor is a
     phone habit, and the pointer is already at the top of the window. */
  .kf-tabs{display:none}
  .kf-scrollport{padding-bottom:40px}
  .kf-scrollport[data-sticky="true"]{padding-bottom:96px}
  /* Nothing is fixed at the bottom any more, so the clearance goes away too. */
  .kf-footnote{padding-bottom:40px}

  /* The header grows the navigation the tab bar used to carry. */
  .kf-appbar{height:64px}
  .kf-navlinks{display:flex}

  .kf-hero{display:grid;grid-template-columns:1.15fr .85fr}
  .kf-hero__slider{height:100%;min-height:340px;border-bottom:0;border-right:1px solid var(--color-border)}
  .kf-hero__body{padding:32px 30px;justify-content:center}
  .kf-hero__tagline{font-size:36px}

  .kf-railrow{grid-template-columns:repeat(auto-fill,minmax(220px,1fr))}
  .kf-regiongrid{grid-template-columns:repeat(auto-fill,minmax(230px,1fr))}
  .kf-regiongrid .kf-region__media{height:130px}

  /* The listing and provider lists become a grid rather than one long column of
     full-width cards with 900px of empty space beside them. */
  .kf-list--grid{
    display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));
    gap:14px;align-items:start;
  }
}

/* Very wide: stop growing the measure, start growing the margins. */
@media (min-width:1440px){
  .kf-device{--kf-maxw:1240px}
}

/* =============================== Account =================================
   Sign in and register, as one card with one submit. They differ by a single
   field, and presenting them as separate screens sends somebody who mistyped
   their email in the sign-in box looking for a different page.
   ========================================================================= */
.kf-auth{align-items:center}
.kf-authcard{
  width:100%;max-width:420px;margin:8px auto 0;
  background:var(--color-surface);border:1px solid var(--color-border);
  border-radius:var(--radius-lg);box-shadow:var(--shadow-md);
  padding:20px 18px 22px;display:flex;flex-direction:column;gap:13px;
}

/* A segmented control, not two links: the two modes are one choice. */
.kf-authtabs{
  display:grid;grid-template-columns:1fr 1fr;gap:4px;padding:4px;
  background:var(--color-surface-sunken);border-radius:var(--radius-full);
}
.kf-authtab{
  border:0;cursor:pointer;font:inherit;font-size:14px;font-weight:var(--weight-semibold);
  padding:10px 12px;border-radius:var(--radius-full);
  background:transparent;color:var(--text-secondary);
}
.kf-authtab[aria-current="true"]{background:var(--color-secondary);color:#FFFFFF}
.kf-authtab:focus-visible{outline:2px solid var(--color-secondary);outline-offset:2px;box-shadow:none}

/* The reveal sits inside the field rather than beside it, so the field keeps
   its full width and the control stays where the eye already is. */
.kf-pw{position:relative;display:block}
.kf-pw .nk-input{padding-right:44px}
.kf-pw__eye{
  position:absolute;right:6px;top:50%;transform:translateY(-50%);
  background:none;border:0;cursor:pointer;padding:7px;border-radius:var(--radius-sm);
  color:var(--text-muted);display:inline-flex;
}
.kf-pw__eye:hover{color:var(--text-primary);background:var(--color-surface-sunken)}

.kf-authrow{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
.kf-remember{display:inline-flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;color:var(--text-secondary)}
.kf-link{
  background:none;border:0;padding:0;cursor:pointer;font:inherit;font-size:13px;
  font-weight:var(--weight-semibold);color:var(--color-primary-ink);
}
.kf-link:hover{text-decoration:underline}

/* A rule with a word on it, drawn with a border rather than two absolutely
   positioned lines that need their widths kept in sync. */
.kf-or{display:flex;align-items:center;gap:12px;color:var(--text-muted);font-size:12px}
.kf-or::before,.kf-or::after{content:'';flex:1;height:1px;background:var(--color-border)}

.kf-authcard .nk-btn--ghost{gap:10px}
.kf-authswap{margin:2px 0 0;text-align:center;font-size:13px;color:var(--text-secondary)}
.kf-hint{font-size:12px;color:var(--text-muted);line-height:1.5;margin:0;text-wrap:pretty}
.kf-hint--center{text-align:center}

.kf-trustrow{
  display:flex;justify-content:center;gap:18px;flex-wrap:wrap;
  margin-top:16px;font-size:12px;color:var(--text-muted);
}
.kf-trustrow span{display:inline-flex;align-items:center;gap:5px}
.kf-trustrow .nk-icon{color:var(--text-muted)}

.kf-note{margin:0;padding:10px 12px;border-radius:var(--radius-md);font-size:13px;line-height:1.5;text-wrap:pretty}
.kf-note--error{background:var(--color-error-bg);color:var(--color-error-ink);border:1px solid var(--error-100)}

/* ============================ Services hero ==============================
   A navy band that says what the directory is for, lets someone search it,
   and gives the supply side a door. A directory that only ever addresses the
   people searching it runs out of people to find.
   ========================================================================= */
.kf-screen--wide{padding:0}
.kf-svchero{background:var(--color-chrome);position:relative;overflow:hidden}
/* One soft disc of light, so the band is not a flat rectangle. Drawn rather
   than fetched — it is a gradient, not an image. */
.kf-svchero::after{
  content:'';position:absolute;right:-90px;top:-120px;width:340px;height:340px;
  border-radius:50%;background:rgba(255,255,255,.045);pointer-events:none;
}
.kf-svchero__inner{
  position:relative;z-index:1;
  padding:26px var(--kf-gutter-r) 30px var(--kf-gutter-l);
  display:flex;flex-direction:column;gap:14px;
}
.kf-svchero__eyebrow{
  display:inline-flex;align-items:center;gap:10px;
  font-size:12px;letter-spacing:.10em;text-transform:uppercase;
  font-weight:var(--weight-semibold);color:var(--gold-300);
}
.kf-svchero__badge{
  width:40px;height:40px;border-radius:var(--radius-md);flex:none;
  display:inline-flex;align-items:center;justify-content:center;
  background:rgba(255,255,255,.10);color:var(--gold-300);
}
.kf-svchero__title{
  margin:0;font-family:var(--font-display);font-size:29px;line-height:1.12;
  font-weight:var(--weight-bold);color:#FFFFFF;letter-spacing:-.015em;text-wrap:balance;
}
.kf-svchero__sub{
  margin:0;font-size:14px;line-height:1.55;color:var(--neutral-300);
  max-width:52ch;text-wrap:pretty;
}
.kf-svchero__find{display:flex;gap:10px;flex-wrap:wrap;margin-top:2px}
.kf-svcsearch{position:relative;display:block;flex:1 1 230px;min-width:0}
.kf-svcsearch .nk-icon{
  position:absolute;left:13px;top:50%;transform:translateY(-50%);
  color:var(--neutral-350);pointer-events:none;
}
/* The controls sit ON the navy, so they take the inverse surface's field
   treatment rather than the white-page one. */
.kf-svcsearch .nk-input,.kf-svcwhere .nk-select{
  height:46px;background:rgba(255,255,255,.09);
  border-color:rgba(255,255,255,.20);color:#FFFFFF;
}
.kf-svcsearch .nk-input{padding-left:40px}
.kf-svcsearch .nk-input::placeholder{color:var(--neutral-350)}
.kf-svcsearch .nk-input:focus,.kf-svcwhere .nk-select:focus{
  border-color:var(--gold-400);background:rgba(255,255,255,.14);
}
.kf-svcwhere{flex:0 1 190px;min-width:150px}
.kf-svcwhere .nk-select{
  /* The default chevron is a dark SVG that vanishes on navy. */
  background-image:linear-gradient(45deg,transparent 50%,var(--neutral-300) 50%),
                   linear-gradient(135deg,var(--neutral-300) 50%,transparent 50%);
  background-position:calc(100% - 19px) 50%,calc(100% - 13px) 50%;
  background-size:6px 6px,6px 6px;background-repeat:no-repeat;
}
.kf-svchero__cta{align-self:flex-start;gap:8px;margin-top:4px}

.kf-svcbody{padding:16px var(--kf-gutter-r) 26px var(--kf-gutter-l);display:flex;flex-direction:column;gap:14px}
.kf-svcbody .kf-catrail{margin:0;padding:2px 0 4px}
.kf-svcbody .kf-results{padding:0}

@media (min-width:700px){
  .kf-screen--wide{max-width:none}
  .kf-svchero__inner,.kf-svcbody{
    max-width:var(--kf-maxw,720px);margin-inline:auto;
  }
  .kf-svchero__inner{padding-top:40px;padding-bottom:44px}
  .kf-svchero__title{font-size:38px}
  .kf-svchero__sub{font-size:15px}
}
@media (min-width:1024px){
  .kf-svchero__title{font-size:44px;max-width:16ch}
  .kf-svchero__find{max-width:640px}
}

/* ============================ Loading and failure ========================
   A screen that has not loaded says so. Rendering an empty list for both
   "still fetching" and "could not fetch" lets the reader conclude the country
   has no houses in it.
   ========================================================================= */
.kf-blank{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:12px;text-align:center;padding:56px 20px;min-height:44vh;
  color:var(--text-secondary);
}
.kf-blank .nk-icon{color:var(--color-warning)}
.kf-blank .kf-prose{max-width:38ch;margin:0}
.kf-blank .kf-h2{margin:0}

.kf-spinner{
  width:26px;height:26px;border-radius:50%;
  border:2.5px solid var(--color-border);
  border-top-color:var(--color-secondary);
  animation:kfSpin .7s linear infinite;
}
/* Under reduced motion the global rule clamps the animation to nothing, which
   would leave a static broken ring sitting there looking stuck. A pulse of
   opacity carries the same "working" signal without spinning anything. */
@media (prefers-reduced-motion:reduce){
  .kf-spinner{border-top-color:var(--color-border);animation:none;opacity:.6}
}

/* =========================== Feature filters ============================
   Only the features the chosen category actually has. One universal list
   would put a "swimming pool" filter on single rooms that always returns
   nothing, and leave shops without "road frontage", which is the thing people
   searching for a shop care about most.
   ========================================================================= */
.kf-filterchips{
  display:flex;gap:7px;overflow-x:auto;
  margin:0 calc(var(--kf-gutter-l) * -1) 0 calc(var(--kf-gutter-r) * -1);
  padding:2px var(--kf-gutter-r) 6px var(--kf-gutter-l);
}
.kf-filterchip{
  flex:none;cursor:pointer;font:inherit;font-size:12.5px;
  padding:7px 13px;border-radius:var(--radius-full);
  border:1px solid var(--color-border);background:var(--color-surface);
  color:var(--text-secondary);white-space:nowrap;
  transition:background var(--duration-fast) var(--ease-standard),
             border-color var(--duration-fast) var(--ease-standard);
}
.kf-filterchip:hover{border-color:var(--color-border-control);color:var(--text-primary)}
.kf-filterchip[aria-pressed="true"]{
  background:var(--color-secondary);border-color:var(--color-secondary);
  color:#FFFFFF;font-weight:var(--weight-semibold);
}
.kf-filterchip--more{color:var(--color-primary-ink);font-weight:var(--weight-semibold)}
.kf-filterchip--clear{color:var(--color-error);border-color:var(--error-100)}

@media (min-width:700px){
  .kf-filterchips{flex-wrap:wrap;overflow:visible;margin:0;padding:2px 0 6px}
}

/* =============================================================================
   GLASS

   Translucent, lit-from-above surfaces in the manner of Apple's current
   interfaces — used where they carry meaning, not everywhere. Chrome that floats
   over content (the header, the tab bar, the price bar, sheets) is glass so the
   page reads as one continuous surface moving underneath it. Controls that sit
   on photographs are glass so they stay legible without a white box punched out
   of the picture. Ordinary content stays opaque, because text on glass over
   other text is the one thing this material cannot do.

   Two budgets govern it:

   CONTRAST. Every tint below was chosen against the worst case behind it — the
   header's navy is 84% opaque because at that level gold nav text over white
   content still clears 4.5:1; lighter looked better and failed.

   COST. backdrop-filter repaints what is behind it on every scroll frame. It is
   used on a handful of fixed elements and on small pills only where a device
   can hover (a laptop), and never on cards that scroll or on the moving review
   wall, which get the same look from gradients instead.
   ============================================================================= */

:root{
  --glass-navy:rgba(0,34,73,.84);
  --glass-light:rgba(255,255,255,.66);
  --glass-light-strong:rgba(255,255,255,.84);
  --glass-edge-light:rgba(255,255,255,.78);
  --glass-edge-dark:rgba(255,255,255,.14);
  --glass-hi:inset 0 1px 0 rgba(255,255,255,.85);
  --glass-hi-dark:inset 0 1px 0 rgba(255,255,255,.14);
  --glass-shadow:0 10px 30px rgba(0,22,49,.12),0 1px 3px rgba(0,22,49,.06);
  --glass-shadow-lg:0 18px 44px rgba(0,22,49,.20),0 2px 6px rgba(0,22,49,.08);
  --glass-blur:blur(22px) saturate(180%);
  --gold-gloss:linear-gradient(180deg,#F3CB66 0%,#E3A81F 100%);
  --navy-gloss:linear-gradient(180deg,#0A6FE8 0%,#003D85 100%);
  --radius-glass:22px;
}

/* A faint wash of the brand's two colours behind everything, so a translucent
   surface has something to be translucent over. On a phone the shell is a fixed
   box and the page scrolls across this; on wider screens it sits at the top. */
.kf-device{
  background:
    radial-gradient(900px 520px at -10% -8%,rgba(58,102,172,.13),transparent 70%),
    radial-gradient(760px 520px at 110% 12%,rgba(227,168,31,.11),transparent 70%),
    var(--color-bg);
}

/* ---------------------------------------------------------------- header --- */
/* The header now floats: content scrolls up under it rather than stopping at
   its edge. It also covers the notch strip, which was a transparent band. */
.kf-appbar{
  top:0;height:calc(var(--kf-chrome-top) + var(--kf-appbar));
  padding-top:var(--kf-chrome-top);
  background:var(--glass-navy);
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  border-bottom:1px solid rgba(255,255,255,.10);
  box-shadow:var(--glass-hi-dark),0 8px 24px rgba(0,15,31,.14);
}
.kf-scrollport{
  top:0;
  padding-top:calc(var(--kf-chrome-top) + var(--kf-appbar));
  --kf-bottom:calc(var(--kf-tabbar) + 8px + env(safe-area-inset-bottom,0px));
  padding-bottom:calc(var(--kf-bottom) + 14px);
}
.kf-scrollport[data-sticky="true"]{padding-bottom:calc(var(--kf-bottom) * 2 + 22px)}

.kf-appbar .kf-lang{
  padding:2px;gap:2px;border-color:rgba(255,255,255,.16);
  background:rgba(255,255,255,.07);box-shadow:var(--glass-hi-dark);
}
.kf-lang button{border-radius:var(--radius-full)}
.kf-lang button[aria-pressed="true"]{
  background:var(--gold-gloss);color:var(--neutral-900);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.55),0 2px 6px rgba(200,138,16,.30);
}
.kf-navlinks button{border-radius:var(--radius-full)}
.kf-navlinks button[aria-current="page"]{
  background:rgba(255,255,255,.12);box-shadow:var(--glass-hi-dark);
}

/* --------------------------------------------------------------- tab bar --- */
/* A floating capsule rather than a slab welded to the bottom edge. */
.kf-device{--kf-tabbar:62px}
.kf-tabs{
  left:max(12px, env(safe-area-inset-left,0px));
  right:max(12px, env(safe-area-inset-right,0px));
  bottom:calc(8px + env(safe-area-inset-bottom,0px));
  height:var(--kf-tabbar);padding:0 6px;
  border-radius:var(--radius-full);
  background:var(--glass-navy);
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  border:1px solid rgba(255,255,255,.14);
  box-shadow:var(--glass-hi-dark),0 14px 34px rgba(0,15,31,.30);
}
.kf-tabs button{position:relative;isolation:isolate;min-height:44px}
.kf-tabs button[aria-current="page"]::before{
  content:'';position:absolute;inset:7px 3px;z-index:-1;border-radius:var(--radius-full);
  background:rgba(255,255,255,.12);box-shadow:var(--glass-hi-dark);
}
@media (max-height:640px){ .kf-device{--kf-tabbar:56px} }
@media (max-height:460px){ .kf-device{--kf-tabbar:48px} }

/* ------------------------------------------------------------ price bar --- */
.kf-sticky{
  left:10px;right:10px;
  bottom:calc(var(--kf-tabbar) + 16px + env(safe-area-inset-bottom,0px));
  border-radius:var(--radius-glass);padding:10px 10px 10px 18px;
  background:var(--glass-light-strong);
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  border:1px solid var(--glass-edge-light);
  box-shadow:var(--glass-hi),var(--glass-shadow-lg);
}
.kf-toast{
  bottom:calc(var(--kf-tabbar) + 24px + env(safe-area-inset-bottom,0px));
  border-radius:18px;
  background:rgba(0,22,49,.88);
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur);
  box-shadow:var(--glass-hi-dark),var(--glass-shadow-lg);
}

/* --------------------------------------------------------------- buttons --- */
/* Pills with light caught on the top edge. The fill stays the design system's
   role colour, so hover and pressed states still come from nomadkit.css; the
   gloss is a highlight painted on top of it. */
.kf-device .nk-btn{
  border-radius:var(--radius-full);
  background-image:linear-gradient(180deg,rgba(255,255,255,.26) 0%,rgba(255,255,255,0) 58%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.50),inset 0 -1px 0 rgba(0,22,49,.10),
             0 4px 14px rgba(0,22,49,.12);
  transition:background-color var(--duration-fast) var(--ease-standard),
             box-shadow var(--duration-base) var(--ease-standard),
             transform 100ms ease-out;
}
.kf-device .nk-btn:active{transform:scale(.97)}
.kf-device .nk-btn:focus-visible{box-shadow:var(--focus-ring),inset 0 1px 0 rgba(255,255,255,.5)}
.kf-device .nk-btn[disabled]{box-shadow:none}
.kf-device .nk-btn:not(.nk-btn--ghost):not(.nk-btn--secondary){
  box-shadow:inset 0 1px 0 rgba(255,255,255,.55),inset 0 -1px 0 rgba(122,82,7,.18),
             0 6px 18px rgba(200,138,16,.26);
}
.kf-device .nk-btn--ghost{
  background-color:var(--glass-light);
  border-color:var(--glass-edge-light);
  box-shadow:var(--glass-hi),0 1px 2px rgba(0,22,49,.05),0 6px 18px rgba(0,22,49,.07);
}
.kf-device .nk-btn--ghost:hover{background-color:rgba(255,255,255,.86)}
[data-surface="navy"] .nk-btn--ghost,
.kf-device [data-surface="navy"] .nk-btn--ghost{
  background-color:rgba(255,255,255,.08);border-color:rgba(255,255,255,.20);
  box-shadow:var(--glass-hi-dark);
}
.kf-device [data-surface="navy"] .nk-btn--ghost:hover{background-color:rgba(255,255,255,.16)}
@media (hover:hover){
  .kf-device .nk-btn--ghost{-webkit-backdrop-filter:blur(14px) saturate(170%);backdrop-filter:blur(14px) saturate(170%)}
}

/* Round controls over photographs: frosted discs. */
.kf-round,.kf-gallery__arrow{
  width:40px;height:40px;
  background:rgba(255,255,255,.64);
  -webkit-backdrop-filter:blur(14px) saturate(180%);backdrop-filter:blur(14px) saturate(180%);
  border:1px solid rgba(255,255,255,.72);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9),0 4px 14px rgba(0,15,31,.18);
  transition:transform 100ms ease-out,background-color var(--duration-fast) var(--ease-standard);
}
.kf-round:hover{background:rgba(255,255,255,.86)}
.kf-round:active{transform:scale(.94)}
.kf-gallery__arrow:active{transform:translateY(-50%) scale(.94)}
.kf-round--sm{width:34px;height:34px}
.kf-gallery__count,.kf-listing__count{
  background:rgba(0,15,31,.46);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
  border:1px solid rgba(255,255,255,.18);
}

/* Chips: lit capsules; the chosen one takes the gold. */
.kf-device .nk-tag,.kf-filterchip{
  background:rgba(255,255,255,.74);border-color:rgba(0,22,49,.08);
  box-shadow:var(--glass-hi),0 1px 2px rgba(0,22,49,.05);
}
.kf-device .nk-tag[aria-pressed="true"],.kf-filterchip[aria-pressed="true"]{
  background:var(--gold-gloss);border-color:rgba(200,138,16,.45);color:var(--neutral-900);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.55),0 3px 10px rgba(200,138,16,.24);
}

/* ----------------------------------------------------------------- cards --- */
.kf-card{
  border-radius:18px;border-color:rgba(0,22,49,.07);
  box-shadow:0 1px 2px rgba(0,22,49,.04),0 8px 22px rgba(0,22,49,.06);
}
.kf-card:hover{box-shadow:0 2px 4px rgba(0,22,49,.05),0 14px 32px rgba(0,22,49,.10)}
.kf-mini__media{border-radius:12px}
.kf-hero{
  border-radius:26px;border-color:var(--glass-edge-light);
  background:var(--glass-light-strong);box-shadow:var(--glass-hi),var(--glass-shadow);
}
.kf-promo{border-radius:20px;box-shadow:var(--glass-hi),0 6px 18px rgba(200,138,16,.10)}
.kf-authcard{border-radius:26px;border-color:var(--glass-edge-light);box-shadow:var(--glass-hi),var(--glass-shadow)}
.kf-locked,.kf-unlocked,.kf-ledger,.kf-review{border-radius:18px}
.kf-highlight{border-radius:16px}

/* ------------------------------------------------------ whole photographs --- */
/* A listing photo is shown entire. What it does not cover is a blurred,
   darkened copy of itself rather than grey bars, so a portrait photo on a wide
   frame still reads as one picture. */
.kf-media--fit{background:var(--navy-900)}
.kf-media--fit .kf-media__img{position:relative;z-index:1;object-fit:contain}
.kf-media__backdrop{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transform:scale(1.25);filter:blur(20px) saturate(1.3) brightness(.78);
  z-index:0;pointer-events:none;
}

.kf-listing__media{display:block;position:relative;height:auto;aspect-ratio:4 / 3;overflow:hidden}
.kf-listing__media > .kf-media{position:absolute;inset:0}

.kf-gallery{height:auto;aspect-ratio:1 / 1;max-height:72vh;background:var(--navy-900);overflow:hidden}
.kf-gallery__frame .kf-media__label{color:var(--neutral-300)}

/* ----------------------------------------------------------- region cards --- */
.kf-rrail__ctl{display:flex;align-items:center;gap:8px}
.kf-rrail__arrow{display:none}

.kf-rcards{
  display:flex;gap:14px;overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;scroll-padding-inline:var(--kf-gutter-l);
  margin:0 calc(var(--kf-gutter-r) * -1) 0 calc(var(--kf-gutter-l) * -1);
  padding:6px var(--kf-gutter-r) 16px var(--kf-gutter-l);
}
.kf-rcards > .kf-rcard{flex:none;width:clamp(168px, 47vw, 250px);scroll-snap-align:start}

.kf-rcard{
  position:relative;display:block;width:100%;aspect-ratio:4 / 5;
  border:0;padding:0;margin:0;cursor:pointer;overflow:hidden;isolation:isolate;
  border-radius:26px;color:#FFFFFF;font:inherit;text-align:center;
  background:linear-gradient(160deg,var(--navy-400),var(--navy-800));
  box-shadow:0 12px 28px rgba(0,22,49,.18),0 2px 6px rgba(0,22,49,.10);
  container-type:inline-size;
  transition:transform 140ms ease-out,box-shadow var(--duration-base) var(--ease-standard);
  -webkit-tap-highlight-color:transparent;
}
.kf-rcard__img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;
  transition:transform 700ms cubic-bezier(.2,.7,.2,1);
}
.kf-rcard__img--none{
  background:
    radial-gradient(120% 80% at 30% 20%,rgba(237,191,79,.35),transparent 60%),
    linear-gradient(160deg,var(--navy-400),var(--navy-800));
}
.kf-rcard__shade{
  position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(0,15,31,.14) 0%,rgba(0,15,31,.30) 48%,rgba(0,15,31,.50) 100%);
}
.kf-rcard__body{
  position:absolute;inset:0;z-index:1;padding:14px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;
}
.kf-rcard__city{
  font-family:var(--font-body);font-weight:var(--weight-bold);
  font-size:26px;font-size:clamp(19px, 13.5cqi, 40px);
  line-height:1.04;letter-spacing:-.025em;text-wrap:balance;overflow-wrap:anywhere;
  text-shadow:0 2px 18px rgba(0,0,0,.38);
}
.kf-rcard__pill{
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:12.5px;font-weight:var(--weight-semibold);letter-spacing:.01em;
  padding:7px 14px;border-radius:var(--radius-full);
  background:rgba(255,255,255,.20);border:1px solid rgba(255,255,255,.34);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.38),0 4px 14px rgba(0,15,31,.18);
  text-shadow:0 1px 6px rgba(0,0,0,.30);
}
@media (hover:hover){
  .kf-rcard__pill{-webkit-backdrop-filter:blur(12px) saturate(160%);backdrop-filter:blur(12px) saturate(160%)}
  @media (hover: hover) and (pointer: fine){
    /* A tap on a phone counts as a hover and never un-hovers, so the zoom
       sticks on whichever card was last touched. Pointer devices only. */
    .kf-rcard:hover .kf-rcard__img{transform:scale(1.06)}
  }
  .kf-rcard:hover{box-shadow:0 18px 40px rgba(0,22,49,.24),0 2px 6px rgba(0,22,49,.10)}
}
.kf-rcard:active{transform:scale(.97)}
.kf-rcard:focus-visible{outline:3px solid var(--gold-400);outline-offset:3px;box-shadow:none}

.kf-rgrid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.kf-rgrid .kf-rcard{border-radius:22px}

/* ----------------------------------------------------------- review wall --- */
.kf-reviews{
  position:relative;overflow:hidden;border-radius:28px;
  padding:22px 0 18px;display:flex;flex-direction:column;gap:16px;
  background:
    radial-gradient(520px 280px at 8% -10%,rgba(58,102,172,.60),transparent 70%),
    radial-gradient(460px 280px at 105% 110%,rgba(227,168,31,.30),transparent 70%),
    var(--navy-800);
  box-shadow:var(--glass-hi-dark),var(--glass-shadow-lg);
}
.kf-reviews__head{
  padding:0 20px;display:flex;flex-wrap:wrap;align-items:flex-end;
  justify-content:space-between;gap:14px;
}
.kf-eyebrow{
  margin:0 0 5px;font-size:11px;letter-spacing:.14em;text-transform:uppercase;
  font-weight:var(--weight-semibold);color:var(--gold-300);
}
.kf-reviews .kf-h2{color:#FFFFFF;text-wrap:balance}
.kf-reviews__meta{margin:5px 0 0;font-size:13px;color:var(--neutral-300)}
.kf-reviews__cta{gap:8px;flex:none}
.kf-reviews__empty{margin:0;padding:0 20px 4px;font-size:14px;color:var(--neutral-300)}
.kf-reviews__foot{margin:0;padding:0 20px;font-size:11.5px;line-height:1.5;color:var(--neutral-350)}

.kf-marquee{
  overflow-x:auto;overscroll-behavior-x:contain;outline:none;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 24px,#000 calc(100% - 24px),transparent 100%);
          mask-image:linear-gradient(90deg,transparent 0,#000 24px,#000 calc(100% - 24px),transparent 100%);
}
.kf-marquee:focus-visible{box-shadow:inset 0 0 0 2px var(--gold-300)}
.kf-marquee__track{display:flex;gap:12px;width:max-content;padding:4px 20px 8px}

.kf-rv{
  flex:none;width:276px;border-radius:22px;padding:16px 16px 14px;
  display:flex;flex-direction:column;gap:10px;
  background:linear-gradient(180deg,rgba(255,255,255,.15) 0%,rgba(255,255,255,.06) 100%);
  border:1px solid rgba(255,255,255,.16);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.20),0 10px 26px rgba(0,0,0,.20);
}
.kf-rv__top{display:flex;align-items:center;justify-content:space-between;gap:8px}
.kf-rv__stars{color:var(--gold-300);font-size:15px;letter-spacing:.08em}
.kf-rv__sample{
  font-size:10px;font-weight:var(--weight-semibold);letter-spacing:.08em;text-transform:uppercase;
  padding:3px 9px;border-radius:var(--radius-full);color:var(--neutral-100);
  background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.20);
}
.kf-rv__body{
  flex:1;margin:0;font-size:14px;line-height:1.55;color:#F2F5FA;text-wrap:pretty;
  display:-webkit-box;-webkit-line-clamp:5;-webkit-box-orient:vertical;overflow:hidden;
}
.kf-rv__who{display:flex;align-items:center;gap:10px}
.kf-rv__avatar{
  width:36px;height:36px;flex:none;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  font-size:12px;font-weight:var(--weight-bold);color:var(--neutral-900);
  background:var(--gold-gloss);box-shadow:inset 0 1px 0 rgba(255,255,255,.55);
}
.kf-rv__id{display:flex;flex-direction:column;gap:1px;min-width:0}
.kf-rv__id b{font-size:13px;color:#FFFFFF}
.kf-rv__id span{font-size:12px;color:var(--neutral-300);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* --------------------------------------------------------------- contact --- */
.kf-contact{
  border-radius:28px;padding:22px 20px;display:flex;flex-direction:column;gap:16px;
  background:var(--glass-light-strong);border:1px solid var(--glass-edge-light);
  box-shadow:var(--glass-hi),var(--glass-shadow);
}
.kf-contact__actions{display:grid;gap:10px}
.kf-device .kf-contactbtn{
  height:auto;min-height:58px;justify-content:flex-start;gap:12px;
  padding:9px 20px;text-align:left;white-space:normal;
}
.kf-contactbtn > span{display:flex;flex-direction:column;gap:3px;line-height:1.15}
.kf-contactbtn small{font-size:12.5px;font-weight:var(--weight-medium);opacity:.86;overflow-wrap:anywhere}
.kf-contactbtn--wa{
  --nk-btn-bg:#0E7C42;--nk-btn-bg-hover:#0B6A38;--nk-btn-bg-active:#08582E;--nk-btn-fg:#FFFFFF;
}
.kf-device .kf-contactbtn--wa:not(.nk-btn--ghost){
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35),inset 0 -1px 0 rgba(0,0,0,.18),0 6px 18px rgba(14,124,66,.30);
}
.kf-wa{flex:none}

/* ------------------------------------------------------ sign-in required --- */
.kf-gate{gap:14px}
.kf-gate__icon{
  width:54px;height:54px;border-radius:17px;flex:none;
  display:flex;align-items:center;justify-content:center;
  color:var(--neutral-900);background:var(--gold-gloss);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.6),0 8px 20px rgba(200,138,16,.30);
}
.kf-gate__icon--info{color:#FFFFFF;background:var(--navy-gloss);box-shadow:inset 0 1px 0 rgba(255,255,255,.3),0 8px 20px rgba(27,63,118,.30)}
.kf-gate__perks{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:9px}
.kf-gate__perks li{display:flex;align-items:center;gap:10px;font-size:13.5px;color:var(--text-secondary)}
.kf-gate__perks .nk-icon{color:var(--color-success)}
.kf-gate__later{align-self:center;padding:8px 12px;color:var(--text-secondary)}
.kf-gatecard{
  width:100%;max-width:440px;margin:10px auto 0;padding:24px 20px 22px;
  display:flex;flex-direction:column;gap:14px;border-radius:26px;
  background:var(--glass-light-strong);border:1px solid var(--glass-edge-light);
  box-shadow:var(--glass-hi),var(--glass-shadow);
}
.kf-locked__cta{align-self:flex-start;gap:8px;margin-top:4px}
.kf-note--info{background:var(--navy-50);color:var(--color-info-ink);border:1px solid var(--navy-100)}
.kf-note--ok{background:var(--color-success-bg);color:var(--color-success-ink);border:1px solid var(--forest-100)}
.kf-sticky .nk-btn{gap:6px}

/* ---------------------------------------------------------------- sheets --- */
.kf-sheet{
  border-radius:28px 28px 0 0;
  background:rgba(255,255,255,.90);
  -webkit-backdrop-filter:blur(30px) saturate(180%);backdrop-filter:blur(30px) saturate(180%);
  border-top:1px solid rgba(255,255,255,.95);
  box-shadow:var(--glass-hi),0 -12px 44px rgba(0,15,31,.24);
  padding-bottom:calc(26px + env(safe-area-inset-bottom,0px));
}

/* ---------------------------------------------------------- review form --- */
.kf-stars{display:flex;gap:8px;flex-wrap:wrap}
.kf-star{
  width:50px;height:50px;border-radius:16px;cursor:pointer;
  font-size:26px;line-height:1;color:var(--neutral-300);
  background:rgba(255,255,255,.8);border:1px solid rgba(0,22,49,.10);
  box-shadow:var(--glass-hi),0 1px 2px rgba(0,22,49,.05);
  transition:transform 100ms ease-out,color var(--duration-fast) var(--ease-standard);
}
.kf-star[data-on="true"]{color:var(--gold-400);background:var(--gold-50);border-color:var(--gold-200)}
.kf-star:active{transform:scale(.92)}
.kf-star:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.kf-textarea{height:auto;min-height:132px;padding:11px 13px;line-height:1.5;resize:vertical;font-family:var(--font-body)}
.kf-count{font-size:12px;color:var(--text-muted);text-align:right}

/* -------------------------------------------------------- fallbacks ------ */
/* No backdrop-filter, or a reader who asked for less transparency: every glass
   surface becomes its solid equivalent. Nothing loses contrast; it just stops
   being see-through. */
@supports not ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){
  .kf-appbar,.kf-tabs{background:var(--color-chrome)}
  .kf-sticky,.kf-sheet,.kf-contact,.kf-gatecard{background:#FFFFFF}
  .kf-round,.kf-gallery__arrow{background:rgba(255,255,255,.95)}
  .kf-rcard__pill{background:rgba(0,15,31,.42)}
}
@media (prefers-reduced-transparency:reduce){
  .kf-appbar,.kf-tabs{background:var(--color-chrome);-webkit-backdrop-filter:none;backdrop-filter:none}
  .kf-sticky,.kf-sheet,.kf-contact,.kf-gatecard,.kf-hero{background:#FFFFFF;-webkit-backdrop-filter:none;backdrop-filter:none}
  .kf-round,.kf-gallery__arrow{background:#FFFFFF;-webkit-backdrop-filter:none;backdrop-filter:none}
  .kf-rcard__pill{background:rgba(0,15,31,.55);-webkit-backdrop-filter:none;backdrop-filter:none}
}
@media (prefers-contrast:more){
  .kf-appbar,.kf-tabs{background:var(--navy-900)}
  .kf-rcard__shade{background:rgba(0,15,31,.55)}
  .kf-rv{background:var(--navy-900);border-color:rgba(255,255,255,.45)}
}

/* ---------------------------------------------------------- tablet and up --- */
@media (min-width:700px){
  /* Static layout: the header is sticky in the flow, so nothing needs padding
     out from under it any more. */
  .kf-appbar{position:sticky;top:0;height:calc(var(--kf-appbar) + var(--kf-chrome-top))}
  .kf-scrollport{padding-top:0;padding-bottom:calc(var(--kf-tabbar) + 36px)}
  .kf-scrollport[data-sticky="true"]{padding-bottom:calc(var(--kf-tabbar) * 2 + 60px)}

  .kf-tabs{
    left:50%;right:auto;transform:translateX(-50%);
    width:min(560px, calc(100% - 32px));
    bottom:calc(14px + env(safe-area-inset-bottom,0px));
  }
  .kf-sticky{
    left:50%;right:auto;transform:translateX(-50%);
    width:min(720px, calc(100% - 32px));
    bottom:calc(var(--kf-tabbar) + 26px + env(safe-area-inset-bottom,0px));
  }
  .kf-toast{
    position:fixed;left:50%;right:auto;transform:translateX(-50%);
    width:min(460px, calc(100% - 32px));
    bottom:calc(var(--kf-tabbar) + 30px);
  }

  /* A sheet on a tablet is a floating card near the bottom of the WINDOW. It
     was absolutely positioned in a shell that is now as tall as the whole page,
     so the payment sheet opened at the bottom of the document, off screen. */
  .kf-scrim{position:fixed}
  .kf-sheet{
    position:fixed;left:50%;right:auto;bottom:24px;
    width:min(480px, calc(100% - 32px));max-height:calc(100dvh - 48px);
    border-radius:28px;border:1px solid rgba(255,255,255,.95);
    padding-bottom:26px;
    transform:translateX(-50%);animation:kfSheetIn .24s var(--ease-standard);
  }

  .kf-rrail__arrow{display:flex}
  .kf-rcards{margin:0;padding:6px 2px 18px;scroll-padding-inline:2px}
  .kf-rcards > .kf-rcard{width:220px}
  .kf-rgrid{grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:16px}

  .kf-gallery{
    aspect-ratio:auto;max-height:none;height:clamp(380px, 62vh, 640px);
    margin:18px var(--kf-gutter-r) 0 var(--kf-gutter-l);
    border-radius:28px;box-shadow:var(--glass-shadow-lg);
  }

  .kf-reviews{padding:28px 0 22px}
  .kf-reviews__head,.kf-reviews__foot,.kf-reviews__empty{padding-left:28px;padding-right:28px}
  .kf-marquee__track{padding:4px 28px 10px;gap:14px}
  .kf-marquee{
    -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 48px,#000 calc(100% - 48px),transparent 100%);
            mask-image:linear-gradient(90deg,transparent 0,#000 48px,#000 calc(100% - 48px),transparent 100%);
  }
  .kf-rv{width:320px}

  .kf-contact{flex-direction:row;align-items:center;justify-content:space-between;padding:26px 28px;gap:24px}
  .kf-contact__copy{max-width:46ch}
  .kf-contact__actions{grid-template-columns:auto auto;flex:none}
}
@keyframes kfSheetIn{
  from{opacity:0;transform:translate(-50%,18px) scale(.98)}
  to{opacity:1;transform:translate(-50%,0) scale(1)}
}

/* ---------------------------------------------------------------- desktop --- */
@media (min-width:1024px){
  .kf-appbar{height:64px}
  .kf-scrollport{padding-bottom:40px}
  .kf-scrollport[data-sticky="true"]{padding-bottom:110px}
  .kf-sticky{bottom:20px;width:min(760px, calc(100% - 48px))}
  .kf-toast{bottom:28px}
  .kf-rcards > .kf-rcard{width:236px}
  .kf-rgrid{grid-template-columns:repeat(5,1fr)}
  .kf-gallery{height:clamp(420px, 66vh, 680px);margin-top:22px}
}
@media (min-width:700px) and (max-width:1023px){
  .kf-rgrid{grid-template-columns:repeat(3,1fr)}
}

/* ------------------------------------------------ late layout corrections --- */
/* A narrow card (the two-column grid on a phone) stacks the region over the
   count rather than cutting both off. */
@container (max-width: 200px){
  .kf-rcard__pill{
    display:flex;flex-direction:column;align-items:center;gap:1px;
    white-space:normal;padding:6px 12px;border-radius:16px;line-height:1.2;
  }
  .kf-rcard__pill .kf-rcard__dot{display:none}
  .kf-rcard__pill > span:last-child{font-weight:var(--weight-medium);font-size:11.5px;opacity:.92}
}

@media (min-width:700px){
  .kf-foot__cols{grid-template-columns:repeat(3,minmax(0,1fr))}
}
/* The contact card goes side by side only where the copy keeps a readable
   width beside two buttons; on a tablet it stacks. */
@media (min-width:700px) and (max-width:959px){
  .kf-contact{flex-direction:column;align-items:stretch}
  .kf-contact__copy{max-width:none}
  .kf-contact__actions{grid-template-columns:1fr 1fr}
}

/* ============================== Units ====================================
   A property that sells more than one price: a hotel's standard room and its
   suite, a block's studio and its two-bedroom. One row each, so the page can
   say what the card's range is made of.
   ========================================================================= */
.kf-units{display:flex;flex-direction:column;gap:8px}
.kf-unit{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:13px 15px;border-radius:16px;
  background:var(--color-surface);border:1px solid rgba(0,22,49,.07);
  box-shadow:var(--glass-hi),0 1px 2px rgba(0,22,49,.04);
}
.kf-unit__id{display:flex;flex-direction:column;gap:3px;min-width:0}
.kf-unit__name{font-size:14px;font-weight:var(--weight-semibold);color:var(--text-primary)}
.kf-unit__meta{font-size:12px;color:var(--text-secondary);text-wrap:pretty}
.kf-unit__price{
  flex:none;text-align:right;font-size:15px;font-weight:var(--weight-bold);
  color:var(--text-primary);white-space:nowrap;
}
.kf-unit__gone{display:block;font-size:11px;font-weight:var(--weight-medium);color:var(--color-error)}
.kf-unit--gone{opacity:.62}
.kf-unit--gone .kf-unit__price{color:var(--text-muted)}
.kf-number--term{background:var(--gold-50);border-color:var(--gold-200);color:var(--neutral-900)}
.kf-videolink{
  display:inline-flex;align-items:center;gap:9px;align-self:flex-start;
  font-size:13.5px;font-weight:var(--weight-semibold);
  padding:10px 16px;border-radius:var(--radius-full);
  background:var(--color-surface);border:1px solid var(--color-border);
  color:var(--color-secondary);box-shadow:var(--glass-hi),0 1px 2px rgba(0,22,49,.05);
}
.kf-videolink:hover{text-decoration:none;border-color:var(--color-border-control)}
.kf-idline{display:flex;flex-direction:column;gap:2px}
.kf-idline span{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--color-success)}
.kf-idline b{font-size:15px;color:var(--text-primary);font-weight:var(--weight-semibold)}
.kf-chip--warn{background:var(--color-warning-bg);color:var(--color-warning-ink)}
.nk-badge--warn{background:var(--color-warning);color:#FFFFFF}

/* =============================================================================
   CRAFT PASS

   Findings from auditing every screen at 320, 375, 768 and 1280, fixed as one
   batch. Each block says what it is repairing.
   ============================================================================= */

/* ---- Contrast: the tab bar's resting labels -------------------------------
   neutral-350 on the glass navy composites to 3.74:1 at 10px, under AA. The
   next stop up clears 5.4:1 and still sits behind the gold of the current tab. */
.kf-tabs button{color:var(--neutral-300)}

/* ---- Touch targets --------------------------------------------------------
   Apple's floor is 44pt and a dozen controls sat under it: the back arrow at
   22px, the language toggle at 21, the hero dots at 6, the filter chips at 31.
   Rather than inflate every control until the chrome is all padding, the small
   ones keep their drawn size and carry an invisible 44px hit area. */
.kf-hit{position:relative}
.kf-hit::after{
  content:'';position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:max(100%,44px);height:max(100%,44px);
}
/* `position:relative` here only exists to give the ::after below something to
   anchor to. The gallery arrows are excluded because they are already
   positioned — `absolute`, against the photograph — and this rule sits later in
   the file at equal specificity, so it was overriding them. The effect on a
   phone was that `left:14px` nudged the prev arrow sideways from its static
   spot and `right:14px` pushed the next arrow 14px off the left edge, both
   staggered down the column: two arrows on the same side, one half
   off-screen. An absolutely positioned element is already a containing block,
   so the hit area works without this. */
.kf-appbar__back,.kf-brand,.kf-lang button,.kf-hero__dot,.kf-link,
.kf-filterchip,.kf-device .nk-tag,.kf-round:not(.kf-gallery__arrow),
.kf-authtab,.kf-star,.kf-iconbtn{position:relative}
.kf-appbar__back::after,.kf-brand::after,.kf-lang button::after,.kf-hero__dot::after,
.kf-link::after,.kf-pw__eye::after,.kf-filterchip::after,.kf-device .nk-tag::after,
.kf-round::after,.kf-authtab::after,.kf-star::after,.kf-iconbtn::after{
  content:'';position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:max(100%,44px);height:max(100%,44px);
}
/* The toggle clipped its children, which also clipped their hit areas; the
   rounded look now comes from the buttons themselves. */
.kf-lang{overflow:visible}
.kf-appbar .kf-lang button{padding:7px 12px;font-size:11.5px}
.kf-authtab{min-height:44px}
.nk-input,.nk-select{height:44px}
.kf-pw .nk-input{padding-right:48px}
.kf-pw__eye{width:40px;height:40px;justify-content:center;align-items:center}
.kf-device .nk-tag,.kf-filterchip{min-height:36px}
.kf-chat__compose .nk-btn--sm{height:40px}

/* ---- Measure --------------------------------------------------------------
   On a laptop the description ran 154 characters to the line; the eye loses
   its place returning to the left margin past about 75. */
.kf-prose{max-width:58ch}
.kf-legal .kf-prose,.kf-blank .kf-prose{max-width:54ch}

/* ---- Numbers --------------------------------------------------------------
   Prices are a column of figures people compare down the page. Proportional
   digits make each row a different width, so the column wobbles. */
.kf-listing__price,.kf-mini__price,.kf-sticky__price,.kf-unit__price,
.kf-number,.kf-amount strong,.kf-locked__num,.kf-unlocked__num,.kf-ledger__num{
  font-variant-numeric:tabular-nums;
}

/* ---- The surfaces the browser draws for us --------------------------------
   Selection, the caret, the native control tint and the scrollbar all ship
   with defaults that belong to no design system. */
::selection{background:var(--gold-200);color:var(--neutral-900)}
[data-surface="navy"] ::selection,.kf-reviews ::selection{background:var(--gold-300);color:var(--navy-900)}
:root{accent-color:var(--color-secondary)}
.nk-input,.nk-select,.kf-textarea{caret-color:var(--color-secondary)}
html{scrollbar-color:var(--neutral-300) transparent;scrollbar-width:thin}
.kf-prose a,.kf-sub a{text-underline-offset:3px;text-decoration-thickness:1px}

/* ---- Type scale on a laptop ----------------------------------------------- */
@media (min-width:1024px){
  .kf-detail__title{font-size:34px;letter-spacing:-.02em}
  .kf-h1{font-size:34px}
}

/* ---- Reduced motion -------------------------------------------------------
   The old rule clamped every animation and transition on the page to 0.01ms,
   which also killed the feedback that tells somebody their tap registered.
   What people who ask for less motion need is the travel gone, not the
   information: colour and opacity stay, movement goes. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important}
  .kf-device .nk-btn,.kf-round,.kf-rcard,.nk-tag,.kf-filterchip,.kf-card,
  .nk-input,.nk-select,.kf-star,.kf-unit{
    transition-duration:120ms!important;transition-property:background-color,color,border-color,box-shadow,opacity!important;
  }
  .kf-device .nk-btn:active,.kf-round:active,.kf-rcard:active,.kf-star:active{transform:none}
  .kf-hero__slide{transition:opacity 180ms linear!important}
  .kf-screen{animation:none}
  html{scroll-behavior:auto}
}

/* Hit areas must not steal each other's taps. A 44px box around a 6px dot with
   a 5px gap would swallow its neighbours, so closely spaced controls grow in
   height only, and grow in width by what the gap allows. */
/* Anchored to the dot's underside rather than centred on it. Centred, the 44px
   box hung 7px below the slider, which clips its overflow — so the dots lost
   part of the very target this rule exists to give them once they moved to the
   bottom edge. Growing upward keeps all 44px inside the photograph. */
.kf-hero__dot::after{
  width:max(100%,20px);height:44px;
  top:auto;bottom:-8px;transform:translateX(-50%);
}
.kf-appbar .kf-lang button::after{width:100%;height:44px}
.kf-lang--lg button::after{width:100%;height:44px}

/* The hero's dots grew by animating width, which relayouts the row on every
   slide. The pill is drawn by a child instead and scaled, so the work happens
   on the compositor and the row's geometry never moves. The scale lives on the
   child precisely so it does not also shrink the parent's 44px hit area. */
.kf-hero__dot{width:14px;height:6px;background:transparent;transition:none}
.kf-hero__dot::before{
  content:'';position:absolute;inset:0;border-radius:var(--radius-full);
  background:rgba(0,22,49,.30);transform:scaleX(.43);transform-origin:center;
  transition:transform var(--duration-base) var(--ease-standard),
             background-color var(--duration-fast) var(--ease-standard);
}
.kf-hero__dot[data-active="true"]::before{transform:scaleX(1);background:var(--neutral-900)}
.kf-hero__slider[data-photos="true"] .kf-hero__dot{background:transparent}
.kf-hero__slider[data-photos="true"] .kf-hero__dot::before{background:rgba(255,255,255,.55)}
.kf-hero__slider[data-photos="true"] .kf-hero__dot[data-active="true"]::before{background:#FFFFFF}

/* A region chip carries how many people are in it, so an empty region reads as
   "nobody yet" rather than as a region KamerFind does not cover. */
.nk-tag__n{
  margin-left:7px;font-size:11px;font-weight:var(--weight-semibold);
  padding:1px 7px;border-radius:var(--radius-full);
  background:rgba(0,22,49,.08);color:var(--text-secondary);
}
.kf-device .nk-tag[aria-pressed="true"] .nk-tag__n{background:rgba(0,22,49,.16);color:var(--neutral-900)}
.kf-device .nk-tag--empty{color:var(--text-muted)}
.kf-empty--invite{display:flex;flex-direction:column;align-items:center;gap:14px}
.kf-empty--invite p{margin:0;max-width:40ch}

/* ============================ An empty country ===========================
   A directory on its first day is a directory looking for its first landlord.
   What belongs on the screen is the way to become one, not an apology.
   ========================================================================= */
.kf-firstrun{
  display:flex;flex-direction:column;align-items:flex-start;gap:12px;
  padding:26px 22px;border-radius:26px;
  background:var(--glass-light-strong);border:1px solid var(--glass-edge-light);
  box-shadow:var(--glass-hi),var(--glass-shadow);
}
.kf-firstrun__icon{
  width:50px;height:50px;border-radius:16px;display:flex;align-items:center;justify-content:center;
  color:var(--neutral-900);background:var(--gold-gloss);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.6),0 8px 20px rgba(200,138,16,.28);
}
.kf-firstrun__acts{display:flex;gap:10px;flex-wrap:wrap;margin-top:4px}
.kf-firstrun .kf-prose{max-width:46ch}

/* A region nobody has listed in yet is not a browsable region dressed as one. */
.kf-rcard--empty .kf-rcard__img{filter:saturate(.55) brightness(.82)}
.kf-rcard--empty .kf-rcard__pill{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.22)}

/* The place chip needs a block to ellipsise; as a bare text node in a flex row
   it clipped mid-word ("Tout le Camero"). */
.kf-appbar__placename{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* A control that does not work yet should not look like the fastest way in. */
.kf-authcard .nk-btn[aria-disabled="true"]{opacity:.55;cursor:not-allowed}

/* Cards are links now, so they can be copied and shared. They must not inherit
   the link colour or the hover underline that belongs to prose. */
a.kf-card,a.kf-listing__media,.kf-card a.nk-btn{color:inherit;text-decoration:none}
a.kf-card:hover,a.kf-listing__media:hover,.kf-card a.nk-btn:hover{text-decoration:none}
a.kf-listing__media{display:block}
a.kf-mini,a.kf-service{cursor:pointer}

/* ===================== Measured accessibility repairs ====================
   From an instrumented pass that composites the real paint stack rather than
   guessing at the first opaque ancestor. Every number below was measured.
   ========================================================================= */

/* The current desktop nav link was gold-300 on the translucent pill over navy:
   4.19:1 at 13px semibold, under the 4.5 floor. One stop lighter clears it at
   4.9:1 and still reads as the same gold. */
.kf-navlinks button[aria-current="page"]{color:var(--gold-200)}

/* The focus ring was a 34%-alpha wash: 1.62:1 against the page it sits on, and
   invisible over a photograph. A white halo inside a solid navy ring survives
   both, which is what the components that already overrode it were doing by
   hand. */
:root{--focus-ring:0 0 0 2px #FFFFFF, 0 0 0 5px rgba(16,40,71,.92)}
[data-surface="navy"]{--focus-ring:0 0 0 2px rgba(0,22,49,.9), 0 0 0 5px var(--gold-300)}

/* Footer links were 17.6px tall with no hit area at all — under even the 24px
   floor. Given room to be 40px, with the column gap tightened so neighbouring
   links cannot swallow each other's taps. */
.kf-foot__col{gap:2px}
.kf-foot__link{min-height:40px;display:flex;align-items:center}

/* A tick-box is 20px and a switch is 24px; the label beside them is the thing
   a thumb actually lands on, so the label is what gets the height. */
.kf-remember,.kf-consent,.kf-results label.kf-sub{min-height:44px}
.kf-results label.kf-sub{align-items:center}

/* Small buttons keep their drawn size and take a 44px hit area in height only;
   they sit beside other controls, and width expansion would steal their taps. */
.kf-device .nk-btn--sm{position:relative}
.kf-device .nk-btn--sm::after{
  content:'';position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:100%;height:max(100%,44px);
}

/* Skip link: visible only when focused, which is the one moment it matters. */
.kf-skip{
  position:absolute;left:-9999px;top:0;z-index:20;
  padding:12px 18px;border-radius:0 0 var(--radius-md) 0;
  background:var(--color-chrome);color:#FFFFFF;font-weight:var(--weight-semibold);
}
.kf-skip:focus{left:0;text-decoration:none}
.kf-scrollport:focus{outline:none}

@keyframes kfPulse{0%,100%{opacity:.35}50%{opacity:.9}}
@media (prefers-reduced-motion:reduce){
  /* "Working" still has to read as working. A spinner frozen at 60% opacity is
     indistinguishable from a broken one, so it breathes instead of spinning —
     a change in brightness, not in position. */
  .kf-spinner{animation:kfPulse 1.8s ease-in-out infinite!important;animation-duration:1.8s!important}
}

/* ---- Provider trust signals -----------------------------------------------
   A badge here is a claim made to somebody deciding whether to let a stranger
   into their house, so these are styled to be read rather than admired: the
   availability dot is the only thing that moves, and it only exists while the
   provider has actually said they are free. */
.kf-chips--tight{gap:5px;margin:2px 0 0}
.kf-service__from{
  font-size:12px;font-weight:var(--weight-semibold);color:var(--color-primary-ink);
  font-variant-numeric:tabular-nums;
}
.kf-dot{
  display:inline-flex;align-items:center;gap:5px;flex:none;
  font-size:11px;font-weight:var(--weight-semibold);color:var(--color-success);
}
.kf-dot__mark{
  width:7px;height:7px;border-radius:var(--radius-full);flex:none;
  background:var(--color-success);
  /* The halo is the live signal. It is the one animated thing on the card, so
     it reads as "now" rather than as decoration. */
  box-shadow:0 0 0 0 color-mix(in srgb, var(--color-success) 55%, transparent);
  animation:kf-pulse 2.4s var(--ease-standard) infinite;
}
@keyframes kf-pulse{
  0%   { box-shadow:0 0 0 0 color-mix(in srgb, var(--color-success) 55%, transparent); }
  70%  { box-shadow:0 0 0 6px color-mix(in srgb, var(--color-success) 0%, transparent); }
  100% { box-shadow:0 0 0 0 color-mix(in srgb, var(--color-success) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce){
  /* Still visible, just still. */
  .kf-dot__mark{animation:none}
}


/* ---- Press feedback --------------------------------------------------------
   The primary button only had this inside the desktop frame, so the control
   people actually press — on a phone, with a thumb — gave nothing back. A
   button that does not acknowledge the press reads as a button that did not
   hear it, and the user presses again. */
.nk-btn{transition:transform 160ms var(--ease-out),
        background-color var(--duration-fast) var(--ease-standard)}
.nk-btn:active:not(:disabled){transform:scale(.97)}
.kf-card:active{transform:scale(.995)}

/* ---- The rail changing under you -------------------------------------------
   Every twelve seconds the three cards are replaced. Swapped instantly it
   reads as a glitch — the one case where the absence of animation is the bug.
   The old set leaves by softening rather than sliding, because nothing is
   moving anywhere: it is being replaced. Blur does the work a crossfade alone
   cannot, blending two overlapping sets into one change instead of two objects
   swapping. */
.kf-foryou__cards{transition:opacity 220ms var(--ease-out),filter 220ms var(--ease-out)}
.kf-foryou__cards[data-leaving="true"]{opacity:0;filter:blur(6px)}
.kf-foryou__cards[data-entering="true"] > *{
  opacity:0;transform:translateY(8px);
  animation:kf-rise 320ms var(--ease-out) forwards;
}
/* Short stagger: long enough to read as a cascade, short enough that the last
   card is not still arriving when somebody reaches for the first. */
.kf-foryou__cards[data-entering="true"] > *:nth-child(1){animation-delay:0ms}
.kf-foryou__cards[data-entering="true"] > *:nth-child(2){animation-delay:55ms}
.kf-foryou__cards[data-entering="true"] > *:nth-child(3){animation-delay:110ms}
.kf-foryou__cards[data-entering="true"] > *:nth-child(4){animation-delay:165ms}
.kf-foryou__cards[data-entering="true"] > *:nth-child(5){animation-delay:220ms}
.kf-foryou__cards[data-entering="true"] > *:nth-child(6){animation-delay:275ms}
.kf-foryou__cards[data-entering="true"] > *:nth-child(n+7){animation-delay:330ms}
@keyframes kf-rise{to{opacity:1;transform:none}}

@media (prefers-reduced-motion: reduce){
  /* The content still changes; it just stops moving and blurring to do it. */
  .kf-foryou__cards,
  .kf-foryou__cards[data-leaving="true"]{transition:none;filter:none;opacity:1}
  .kf-foryou__cards[data-entering="true"] > *{animation:none;opacity:1;transform:none}
  .nk-btn:active:not(:disabled),.kf-card:active{transform:none}
}

/* ---- The home rail's card -------------------------------------------------
   A shop window, not a list. The photograph leads and holds a 4:3 frame at
   every width, so what grows with the screen is the picture rather than the
   gap between two columns of text. miniCard stays as it is for Saved, which is
   a list you scan by name and where a row is the right shape.

   Phone: two rows that scroll sideways, so what you see is a 2x2 window and a
   flick moves to the next pair. A phone column you drag through forever buries
   the fourth listing; a sideways window keeps four in reach and the rest one
   gesture away.
   Tablet: three across. Laptop: four, so the whole shortlist is one glance,
   which is the point of a shortlist. */
.kf-foryou__cards{
  /* Two across, two down, and that is the whole rail. It used to scroll
     sideways with a third card sliced in half at the edge, which hid half the
     shortlist behind a gesture and moved the card under a thumb already on its
     way to it. Four whole cards, no scroller, nothing cut off. */
  display:grid;gap:12px;
  grid-template-columns:repeat(2,minmax(0,1fr));
}

.kf-hcard{
  display:flex;flex-direction:column;min-width:0;
  background:var(--color-surface);border:1px solid var(--color-border);
  border-radius:var(--radius-lg);overflow:hidden;
  box-shadow:var(--shadow-sm);
  transition:transform 160ms var(--ease-out),box-shadow 160ms var(--ease-out);
}
.kf-hcard:active{transform:scale(.985)}
@media (hover: hover) and (pointer: fine){
  .kf-hcard:hover{box-shadow:var(--shadow-md)}
}
/* A fixed ratio rather than a fixed height: a row of cards then lines up
   whatever shape the photographs are, which pixels never manage the moment one
   landlord sends a portrait photo. */
.kf-hcard__shot{position:relative;display:block;width:100%;aspect-ratio:4 / 3;background:var(--color-surface-sunken)}
.kf-hcard__media{position:absolute;inset:0;width:100%;height:100%}
.kf-hcard__flag{
  position:absolute;left:8px;top:8px;z-index:2;
  font-size:10px;font-weight:var(--weight-semibold);letter-spacing:.02em;
  padding:3px 7px;border-radius:var(--radius-full);
  background:var(--color-success);color:#FFFFFF;
}
.kf-hcard__body{display:flex;flex-direction:column;gap:2px;padding:10px 11px 12px;min-width:0}
/* Price first: it decides whether the rest is worth reading. */
.kf-hcard__price{font-size:15px;font-weight:var(--weight-bold);color:var(--text-primary);font-variant-numeric:tabular-nums}
.kf-hcard__title{
  font-size:13px;font-weight:var(--weight-semibold);color:var(--text-primary);line-height:1.3;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.kf-hcard__where{font-size:12px;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

@media (min-width: 700px){
  /* Pointer sizes get a plain grid: no hidden sideways scroll on a device with
     no thumb to flick it. */
  /* The resets that used to undo the sideways scroller are gone with it; all
     that changes per width now is how many columns. */
  .kf-foryou__cards{grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}
}
@media (min-width: 1000px){
  .kf-foryou__cards{grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}
  .kf-hcard__title{font-size:14px}
}
@media (prefers-reduced-motion: reduce){
  .kf-hcard:active{transform:none}
}

/* ==== assets/preapp.css ==== */
/* =============================================================================
   KamerFind — pre-app flow (splash, tour, city, auth, permissions, profile).

   Loaded on top of tokens/* + nomadkit.css + app.css. The pre-app and the app
   stand on the same paper: same ground (#FFFDF7), same Lora/Work Sans, same
   radii, same 120/200ms motion. The ONLY thing that changes is which two role
   tokens carry action and accent — and those transition back to sand/ocean at
   the moment of handoff.

   Brand blue/orange is a MARK, not a palette. Blue carries actions; orange
   carries progress and the mark, and appears in exactly four places: the logo,
   the tour progress rail, the OTP active underline, and the inspected shield.
   ============================================================================= */

/* The brand-token swap that used to live here is gone.
   It existed to reconcile two palettes: NomadKit's warm sand/ocean in the app
   against the logo's blue/orange in the pre-app, with an @property transition
   animating between them at the handoff. Now that the whole product is navy and
   gold there is no second palette, nothing to swap, and nothing to animate —
   the handoff is continuous because the two surfaces are genuinely the same.

   --brand-gradient survives in tokens/colors.css for the two places the mark's
   own gradient is still allowed: the splash and the tour progress rail. */

/* French runs 15-20% longer than English. Without this the tour headlines wrap
   to a fourth line and push the progress rail off the fold on a short phone. */
:lang(fr) .pa-slide__text h1{font-size:25px}
:lang(fr) .pa-sheet h2{font-size:19px}

/* ---------------------------------------------------------------- shell --- */
.pa-screen,.pa-tour,.pa-splash{
  position:absolute;inset:0;display:flex;flex-direction:column;
  background:var(--color-bg);overflow:hidden;
}
/* --kf-chrome-top is the device's own top inset — the notch on a phone, zero
   everywhere else. It used to also carry the height of a painted-on carrier
   bar that the desktop mockup drew; the mockup is gone and so is that. */
.pa-screen,.pa-tour,.pa-splash{top:var(--kf-chrome-top)}
.pa-scroll{flex:1;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch}
/* The sticky confirm bar is painted over the scroller, so the last row of
   region cards needs room to clear it. */
.pa-scroll[data-sticky="true"]{padding-bottom:84px}

/* One compositing layer per transition: the incoming screen fades and slides,
   the outgoing one only fades. Retreat is faster than advance — going back
   should feel cheap. */
@keyframes paIn  {from{opacity:0;transform:translateX(16px)} to{opacity:1;transform:none}}
@keyframes paBack{from{opacity:0;transform:translateX(-16px)}to{opacity:1;transform:none}}
@keyframes paLift{from{opacity:0;transform:translateY(10px)} to{opacity:1;transform:none}}
.pa-screen[data-dir="fwd"]{animation:paIn 200ms var(--ease-standard) both}
.pa-screen[data-dir="back"]{animation:paBack 160ms var(--ease-standard) both}

.pa-head{
  display:flex;align-items:center;gap:10px;
  padding:calc(env(safe-area-inset-top,0px) + 12px) 20px 4px;min-height:56px;flex:none;
}
.pa-head__spacer{flex:1}
/* 44px minimum on every header control. The visual size stays small; the hit
   area is negative-margined outward so the layout does not grow. */
.pa-head .kf-iconbtn{
  width:44px;height:44px;margin-left:-11px;
  align-items:center;justify-content:center;border-radius:var(--radius-full);
}
.pa-head .kf-iconbtn[hidden]{display:inline-flex!important;visibility:hidden}  /* hold the slot */
.pa-skip{
  background:none;border:0;cursor:pointer;padding:0 10px;min-height:44px;min-width:44px;
  margin-right:-10px;
  font-size:13px;font-weight:var(--weight-semibold);color:var(--text-secondary);
}
.pa-skip:hover{color:var(--text-primary)}

.pa-body{padding:0 20px;display:flex;flex-direction:column;gap:10px}
.pa-foot{
  flex:none;padding:16px 20px calc(env(safe-area-inset-bottom,0px) + 24px);
  display:flex;flex-direction:column;gap:14px;
}

/* Desktop-frame chrome only (hidden under 760px). --text-muted sits at 3.71:1
   on the neutral-100 page ground, so this takes --text-secondary. */
.kf-footnote{color:var(--text-secondary)}

/* ---------------------------------------------------------------- splash --- */
/* position/inset/flex come from the shared root rule above — redeclaring inset
   here would undo the 46px status-bar offset on the desktop frame. */
.pa-splash{
  align-items:center;justify-content:center;gap:0;
  padding:0 32px 8vh;text-align:center;
}
.pa-splash__mark{
  width:96px;height:96px;border-radius:var(--radius-lg);
  box-shadow:var(--shadow-md);animation:paLift 320ms var(--ease-standard) both;
}
.pa-splash__word{
  margin:22px 0 0;font-family:var(--font-display);font-size:28px;
  font-weight:var(--weight-semibold);color:var(--text-primary);letter-spacing:-.01em;
}
.pa-splash__word b{color:var(--navy-600);font-weight:inherit}
.pa-splash__tag{
  margin:10px 0 0;font-size:13px;line-height:var(--leading-relaxed);
  color:var(--text-secondary);max-width:260px;text-wrap:pretty;
}
/* Determinate, not a spinner — a spinner on a slow phone reads as failure. */
.pa-splash__bar{
  margin-top:40px;width:64px;height:2px;border-radius:var(--radius-full);
  background:var(--neutral-200);overflow:hidden;
}
.pa-splash__bar i{
  display:block;height:100%;width:100%;border-radius:inherit;
  background:var(--brand-gradient);background-size:64px 100%;
  /* Revealed by clipping, not by growing. Animating width relayouts on every
     frame, and this one runs for three seconds during boot — precisely when the
     main thread is busy parsing the app on the low-end Android this targets.
     clip-path stays on the compositor. scaleX would be wrong here: the gradient
     is anchored by background-size, so scaling squashes it instead of
     uncovering it. */
  clip-path:inset(0 100% 0 0);
  /* Duration is overwritten inline at boot from SPLASH_MS, so the bar reaches
     full exactly as the splash ends. This value is only the fallback. */
  transition:clip-path 3000ms linear;
}
.pa-splash__bar i[data-full="true"]{clip-path:inset(0 0 0 0)}

/* ------------------------------------------------------------------ tour --- */
.pa-tour__stage{position:relative;flex:1;overflow:hidden}
.pa-slide{
  position:absolute;inset:0;display:flex;flex-direction:column;
  opacity:0;pointer-events:none;transition:opacity 200ms var(--ease-standard);
}
.pa-slide[data-active="true"]{opacity:1;pointer-events:auto}

/* The one real photograph in the flow. Type never crosses it — the legibility
   problem is solved by not creating it. */
.pa-slide__media{
  position:relative;height:300px;flex:none;overflow:hidden;
  background:var(--neutral-200) center/cover no-repeat;
}
.pa-slide__media img{
  width:100%;height:100%;object-fit:cover;object-position:50% 62%;display:block;
}
.pa-slide__media::after{
  content:'';position:absolute;left:0;right:0;bottom:0;height:96px;
  background:linear-gradient(to top,var(--color-bg),rgba(255,253,247,0));
}
/* Slides 2 and 3 spend zero bytes: the base64 LQIP already in the manifest,
   blurred into an abstract colour field. scale(1.1) hides the blur's edge. */
.pa-slide__field{
  position:relative;height:300px;flex:none;overflow:hidden;background:var(--neutral-200);
}
.pa-slide__field i{
  position:absolute;inset:-20px;display:block;background-size:cover;background-position:center;
  filter:blur(18px) saturate(1.15);transform:scale(1.1);
}
.pa-slide__field::after{
  content:'';position:absolute;left:0;right:0;bottom:0;height:120px;
  background:linear-gradient(to top,var(--color-bg),rgba(255,253,247,0));
}
/* A sharp, real UI fragment over the blur, so it reads as depth of field
   rather than a broken image. */
.pa-float{
  position:absolute;left:50%;transform:translateX(-50%);top:104px;width:86%;z-index:2;
  box-shadow:var(--shadow-lg);border-radius:var(--radius-md);
  background:var(--color-surface);border:1px solid var(--color-border);overflow:hidden;
}
.pa-float__row{display:flex;gap:11px;padding:11px}
.pa-float__thumb{
  width:76px;height:64px;flex:none;border-radius:var(--radius-sm);
  background-size:cover;background-position:center;
}
.pa-float__body{min-width:0;display:flex;flex-direction:column;gap:3px}
.pa-float__title{font-size:13px;font-weight:var(--weight-semibold);line-height:1.3;color:var(--text-primary)}
.pa-float__meta{font-size:11px;color:var(--text-secondary)}
.pa-float__price{font-size:13px;font-weight:var(--weight-bold);color:var(--text-primary);margin-top:auto}
.pa-shield{
  display:inline-flex;align-items:center;gap:4px;align-self:flex-start;
  font-size:10px;font-weight:var(--weight-semibold);
  padding:3px 8px;border-radius:var(--radius-full);
  background:var(--gold-50);color:var(--color-primary-ink);
}
.pa-shield .nk-icon{color:var(--color-primary-ink)}
/* the real .kf-amount row, lifted from the Mobile Money sheet */
.pa-float__pay{padding:13px 14px;display:flex;flex-direction:column;gap:10px}
.pa-float__amount{display:flex;align-items:center;justify-content:space-between}
.pa-float__amount span{font-size:12px;color:var(--text-secondary)}
.pa-float__amount strong{font-size:19px;font-weight:var(--weight-bold);color:var(--text-primary)}
.pa-float__methods{display:flex;gap:8px}
.pa-momo{
  flex:1;display:flex;align-items:center;gap:7px;padding:7px 9px;
  border:1px solid var(--color-border);border-radius:var(--radius-sm);
}
.pa-momo b{
  width:26px;height:26px;flex:none;border-radius:4px;display:flex;
  align-items:center;justify-content:center;font-size:8px;color:var(--neutral-900);
}
.pa-momo span{font-size:10px;color:var(--text-secondary);line-height:1.2}

.pa-slide__text{padding:22px 20px 0;display:flex;flex-direction:column;gap:10px}
.pa-slide__text h1{
  margin:0;font-family:var(--font-display);font-size:28px;line-height:1.15;
  font-weight:var(--weight-semibold);color:var(--text-primary);text-wrap:pretty;
}
.pa-slide__text p{margin:0;font-size:15px;line-height:var(--leading-relaxed);color:var(--text-secondary);max-width:32ch;text-wrap:pretty}
.pa-chips{display:flex;gap:7px;flex-wrap:wrap;margin-top:2px}
.pa-chips .nk-tag{cursor:default;height:28px;font-size:12px;padding:0 11px}

/* Filling the rail reveals a slice of the logo gradient: progress through
   onboarding literally paints the mark. */
.pa-rail{display:flex;gap:4px;justify-content:center;align-items:center}
.pa-rail button{
  width:44px;height:44px;padding:0;border:0;background:none;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
}
.pa-rail i{
  display:block;width:28px;height:3px;border-radius:var(--radius-full);
  background:var(--neutral-200);overflow:hidden;position:relative;
}
.pa-rail i::after{
  /* Same reveal, same reason as the splash bar: each segment uncovers its own
     slice of the logo gradient, so the fill is clipped rather than grown. */
  content:'';position:absolute;inset:0;border-radius:inherit;
  background:var(--brand-gradient);background-size:104px 100%;
  clip-path:inset(0 100% 0 0);
  transition:clip-path 200ms var(--ease-standard);
}
.pa-rail button:nth-child(1) i::after{background-position:0 0}
.pa-rail button:nth-child(2) i::after{background-position:-38px 0}
.pa-rail button:nth-child(3) i::after{background-position:-76px 0}
.pa-rail button[aria-current="step"] i::after,
.pa-rail button[data-done="true"] i::after{clip-path:inset(0 0 0 0)}

/* ----------------------------------------------------------------- where --- */
.pa-where__grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;padding-bottom:8px}
.pa-region{
  position:relative;text-align:left;cursor:pointer;padding:0;font:inherit;color:inherit;
  border:1px solid var(--color-border);border-radius:var(--radius-md);
  background:var(--color-surface);overflow:hidden;
  transition:border-color var(--duration-fast) var(--ease-standard),
             box-shadow var(--duration-fast) var(--ease-standard);
}
.pa-region:hover{box-shadow:var(--shadow-md)}
.pa-region[aria-pressed="true"]{border:2px solid var(--color-secondary)}
/* Ten flat swatches sampled from the ten region photographs. Ten real photos
   would be ~600KB to answer a question the user answers in one tap. */
/* The swatch carries the CITY, not the region's initials: "South" and
   "South-West" both abbreviate to SO, "North" and "North-West" both to NO, and
   the city is the label people actually search by. */
.pa-region__swatch{
  height:76px;display:flex;align-items:center;justify-content:center;
  padding:0 8px;text-align:center;text-wrap:balance;
  font-family:var(--font-display);font-size:15px;line-height:1.2;
  font-weight:var(--weight-semibold);
  text-shadow:0 1px 3px rgba(33,28,23,.4);
}
.pa-region__check{
  position:absolute;top:8px;right:8px;width:22px;height:22px;border-radius:var(--radius-full);
  background:var(--color-surface);display:none;align-items:center;justify-content:center;
  box-shadow:var(--shadow-sm);color:var(--color-secondary);
}
.pa-region[aria-pressed="true"] .pa-region__check{display:flex}
.pa-region__body{padding:10px 12px 12px}
.pa-region__name,.pa-region__city,.pa-region__count{display:block}
.pa-region__name{font-size:14px;font-weight:var(--weight-semibold);color:var(--text-primary);line-height:1.3}
.pa-region__city{font-size:12px;color:var(--text-secondary);line-height:1.35}
/* --text-muted is 4.43:1 on paper, just under AA at this size, so the counts
   use --text-secondary (7.4:1) instead. */
.pa-region__count{font-size:11px;color:var(--text-secondary);margin-top:4px}
.pa-locate{
  width:100%;justify-content:flex-start;gap:10px;height:50px;
}
.pa-locate .nk-icon:last-child{margin-left:auto;color:var(--text-muted)}
.pa-searchwrap{position:relative}
.pa-searchwrap .nk-icon{
  position:absolute;left:12px;top:50%;transform:translateY(-50%);
  color:var(--text-muted);pointer-events:none;
}
.pa-searchwrap .nk-input{padding-left:38px}
.pa-everything{
  align-self:center;margin:4px 0 8px;background:none;border:0;cursor:pointer;
  font-size:13px;font-weight:var(--weight-semibold);color:var(--color-secondary);
  padding:0 12px;min-height:44px;
}
/* The EN|FR pill is 24px tall by design in the app; inside the pre-app header
   it is one of only three controls on screen, so it gets a real hit area. */
.pa-head .kf-lang button{min-height:44px;padding:0 14px}

/* ---------------------------------------------------------------- sheets --- */
/* Deliberately the same component and the same physics as the Mobile Money
   sheet the user meets later, so auth arrives looking already-trusted. */
.pa-sheet{
  position:absolute;left:0;right:0;bottom:0;z-index:9;max-height:94%;overflow-y:auto;
  background:var(--color-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
  padding:10px 22px calc(env(safe-area-inset-bottom,0px) + 26px);
  box-shadow:var(--shadow-lg);animation:kfUp 200ms var(--ease-standard);
}
.pa-sheet__grip{width:40px;height:4px;border-radius:var(--radius-full);background:var(--color-border-strong);margin:0 auto 14px}
.pa-sheet__head{display:flex;align-items:center;gap:8px;margin-bottom:10px;min-height:28px}
/* --text-muted is 4.43:1 on the sheet's white ground — just under AA at 12px. */
.pa-sheet__ctx{font-size:12px;color:var(--text-secondary);font-weight:var(--weight-semibold)}
.pa-sheet h2{
  margin:0;font-family:var(--font-display);font-size:20px;line-height:1.2;
  font-weight:var(--weight-semibold);color:var(--text-primary);
}
.pa-sheet p{margin:5px 0 0;font-size:13px;line-height:var(--leading-relaxed);color:var(--text-secondary)}
.pa-sheet__body{display:flex;flex-direction:column;gap:14px;margin-top:14px}
.pa-or{display:flex;align-items:center;gap:10px;color:var(--text-muted);font-size:12px}
.pa-or::before,.pa-or::after{content:'';flex:1;height:1px;background:var(--color-border)}
.pa-legal{font-size:11px;line-height:1.6;color:var(--text-muted);text-align:center;margin:2px 0 0}
.pa-legal a{color:var(--text-secondary);text-decoration:underline}
.pa-social{justify-content:flex-start;gap:10px}
.pa-social svg{width:18px;height:18px;flex:none}
.pa-social span{flex:1;text-align:center;margin-left:-28px}

/* Error lines are reserved at a fixed height so nothing reflows when one
   appears — a form that jumps under your thumb feels broken. */
.pa-help{min-height:17px;font-size:12px;line-height:1.4;color:var(--text-muted)}
.pa-help[data-error="true"]{color:var(--color-error)}

.pa-phone{display:flex;align-items:stretch;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-surface);overflow:hidden}
.pa-phone:focus-within{border-color:var(--color-secondary);box-shadow:var(--focus-ring)}
.pa-phone__cc{
  display:flex;align-items:center;gap:6px;padding:0 12px;flex:none;
  background:var(--color-surface-sunken);border-right:1px solid var(--color-border);
  font-size:14px;font-weight:var(--weight-semibold);color:var(--text-secondary);
}
.pa-phone input{
  flex:1;min-width:0;border:0;outline:none;background:none;padding:0 13px;height:48px;
  font-family:var(--font-body);font-size:20px;letter-spacing:.04em;
  font-variant-numeric:tabular-nums;color:var(--text-primary);
}
.pa-phone input::placeholder{color:var(--neutral-300);letter-spacing:.04em}

/* Six painted boxes under ONE transparent input, so Android SMS Retriever
   autofill and paste both work. Six real inputs break both. */
.pa-otp{position:relative}
.pa-otp__boxes{display:flex;gap:8px;justify-content:center}
.pa-otp__boxes i{
  width:46px;height:54px;border-radius:var(--radius-sm);
  border:1px solid var(--color-border);background:var(--color-surface);
  display:flex;align-items:center;justify-content:center;
  font-size:24px;font-weight:var(--weight-semibold);font-variant-numeric:tabular-nums;
  font-style:normal;color:var(--text-primary);
}
.pa-otp__boxes i:nth-child(3){margin-right:10px}   /* 3 + 3 grouping */
.pa-otp__boxes i[data-active="true"]{border-color:var(--color-border-strong);box-shadow:inset 0 -2px 0 var(--gold-500)}
.pa-otp__boxes i[data-filled="true"]{border-color:var(--color-border-strong)}
.pa-otp input{
  position:absolute;inset:0;width:100%;height:100%;opacity:0;
  border:0;background:none;font-size:16px;letter-spacing:2em;
}
.pa-otp[data-state="bad"] .pa-otp__boxes{animation:paShake 270ms var(--ease-standard)}
.pa-otp[data-state="bad"] .pa-otp__boxes i{border-color:var(--color-error)}
.pa-otp[data-state="ok"] .pa-otp__boxes i{border-color:var(--color-success);box-shadow:inset 0 -2px 0 var(--color-success)}
@keyframes paShake{0%{transform:translateX(0)}30%{transform:translateX(-4px)}65%{transform:translateX(4px)}100%{transform:translateX(0)}}
@media (prefers-reduced-motion:reduce){ .pa-otp[data-state="bad"] .pa-otp__boxes{animation:none} }

.pa-resend{display:flex;align-items:center;justify-content:center;gap:6px;font-size:13px;color:var(--text-secondary)}
.pa-inline{background:none;border:0;padding:0;cursor:pointer;font:inherit;font-weight:var(--weight-semibold);color:var(--color-secondary)}
.pa-inline[disabled]{color:var(--text-muted);cursor:default;font-weight:var(--weight-regular)}
.pa-center{text-align:center}
/* Standalone link-buttons ("Use email instead", "Back to sign in", Skip) are
   actions, not inline text, so they take a real 44px target. The Terms and
   Privacy links stay inline inside their sentence, where WCAG exempts them. */
.pa-sheet .kf-link{min-height:44px;width:100%}
/* Absolutely positioned, so the 44px hit area costs no layout. */
.pa-reveal{
  position:absolute;right:4px;top:50%;transform:translateY(-50%);
  min-height:44px;min-width:44px;padding:0 8px;
  display:flex;align-items:center;justify-content:center;
  background:none;border:0;cursor:pointer;
  font-size:12px;font-weight:var(--weight-semibold);color:var(--color-secondary);
}
.pa-fieldwrap{position:relative}
.pa-fieldwrap .nk-input{padding-right:66px}

.pa-icon-badge{
  width:52px;height:52px;border-radius:var(--radius-full);flex:none;
  display:flex;align-items:center;justify-content:center;margin-bottom:2px;
}
.pa-icon-badge--blue{background:var(--navy-50);color:var(--navy-600)}
.pa-icon-badge--orange{background:var(--gold-50);color:var(--color-primary-ink)}
.pa-icon-badge--green{background:var(--forest-50);color:var(--color-success)}

/* Declining is the same size as accepting — unequal fill, equal footprint. */
.pa-choice{display:flex;flex-direction:column;gap:9px}

.pa-tagrow{display:flex;flex-wrap:wrap;gap:8px}
.pa-verified{display:flex;align-items:center;gap:7px}

/* --------------------------------------------------------- screen index --- */
.pa-index{padding:20px;display:flex;flex-direction:column;gap:10px}
.pa-index a{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:12px 14px;border:1px solid var(--color-border);border-radius:var(--radius-md);
  background:var(--color-surface);font-size:14px;color:var(--text-primary);text-decoration:none;
}
.pa-index a:hover{box-shadow:var(--shadow-md);text-decoration:none;color:var(--text-primary)}
.pa-index code{font-size:11px;color:var(--text-muted);font-family:var(--font-mono)}
