/* ============================================================
   Wilsprop — design system (warm-light). Single source of truth.
   Linked on EVERY page AFTER the page's inline <style>, so these
   :root tokens override page-local drift (source order wins).

   Palette: ink (text) · bone (bg) · teal (primary) · gold (accent).
   Type: Fraunces (display) · Inter (body) · JetBrains Mono (numbers).
   Editing this file? Bump the ?v= querystring on every page.
   ============================================================ */
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;600&display=swap');

:root{
  --bg:#f4f1ea;        /* bone — warm off-white page */
  --panel:#ffffff;     /* card surface */
  --panel2:#efe7d6;    /* secondary surface */
  --line:#d9cfba;      /* default hairline — quiet, warm */
  --line-strong:#cdbd97; /* emphasis borders only (featured / active) */
  --text:#272219;      /* ink */
  --muted:#5f5648;     /* warm grey — passes AA on the ivory bg */
  --brand:#0e3633;     /* deep teal — dark bands, wordmark */
  --brand-ink:#042e2c; /* text on --accent fills */
  --accent:#0a8c88;    /* teal (success / primary / verified) */
  --accent-soft:#e6f4f3; --accent-edge:#bfe1df;
  --accent2:#2f6db0;   /* blue (links / info / AI) */
  --accent2-soft:#eaf1fb; --accent2-edge:#cdddf3;
  --gold:#b9882c;      /* gold accent */
  --warn:#b87914;
  --bad:#c2410c;
  --good:#0a8c88;
  --red:#c0392b;       /* readable red on light surfaces */
  --purple:#7c4dcf;    /* readable purple on light surfaces */
  --chip:#ffffff;      /* chips / ghost buttons */

  /* type */
  --font-display:'Fraunces',Georgia,'Times New Roman',serif;
  --font-ui:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  --font-mono:'JetBrains Mono',ui-monospace,'SFMono-Regular',monospace;

  /* radius scale */
  --r-sm:8px; --r-md:12px; --r-lg:16px; --r-xl:20px; --r-pill:999px;

  /* elevation: one shadow voice (warm ink), three tiers */
  --shadow-rest:0 1px 2px rgba(39,34,25,.05),0 4px 14px rgba(39,34,25,.06);
  --shadow-lift:0 12px 28px rgba(39,34,25,.10);
  --shadow-pop:0 24px 60px rgba(39,34,25,.16);
}

/* global finish */
::selection{background:#cfe9e7;color:var(--brand)}
:focus-visible{outline:2px solid var(--accent2);outline-offset:2px;border-radius:4px}

/* stroke-icon tints (icons inherit currentColor; these set intent colours) */
.selfrent .sr-ic{color:var(--accent);line-height:0}

body{
  font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  background:var(--bg);
  color:var(--text);
  -webkit-font-smoothing:antialiased;
}

/* Premium display type for headings & brand */
h1,h2,h3,h4,.logo,.nm,.hero h1{
  font-family:'Fraunces',Georgia,'Times New Roman',serif;
  letter-spacing:-.012em;
  font-weight:600;
}
.hero h1{font-weight:700;}

/* Numbers feel deliberate in mono */
.stars,.statline b{font-family:'JetBrains Mono',ui-monospace,'SFMono-Regular',monospace;}

/* secondary-page header subtitle — hide on phones so the nav buttons don't cramp */
@media(max-width:620px){.hdr-sub,.mkt-sub{display:none}}

/* ---- Site-wide bottom tab bar (mobile primary nav) -------------------------
   Shown on phones for every page. Pages that don't ship their own tabbar get
   one injected by premise-data.js (ensureTabbar). body padding is applied only
   when a tabbar is actually present, so denied pages get no empty gap. -------- */
.tabbar{display:none}
@media(max-width:760px){
  .tabbar{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:1500;background:var(--panel);border-top:1px solid var(--line);padding:4px 2px;padding-bottom:calc(4px + env(safe-area-inset-bottom))}
  .tabbar .tb{flex:1;display:flex;flex-direction:column;align-items:center;gap:2px;font-size:10.5px;color:var(--muted);background:none;border:none;padding:6px 2px;font-family:inherit;text-decoration:none;cursor:pointer;position:relative}
  .tabbar .tb .ti{font-size:18px;line-height:1}
  .tabbar .tb.on{color:var(--accent);font-weight:700}
  body:has(nav.tabbar){padding-bottom:58px}
}

/* Current page highlighted in the header nav (orientation cue). */
header nav a.on,header nav a[aria-current="page"]{color:var(--accent);font-weight:700}

/* Logo wired to home by premise-data.js gets a pointer affordance. */
.logo[role="link"]{cursor:pointer}

/* Mobile: keep the header clear of the status bar / notch in the app shell.
   No-op in normal browsers (inset = 0); only adds space on notched/edge-to-edge. */
@supports(top:env(safe-area-inset-top)){
  @media(max-width:760px){ header{padding-top:calc(env(safe-area-inset-top) + 12px)} }
}

/* ── Fit-ranking line on search cards (buy.html / rent.html) ──
   The one-liner that explains WHY a listing fits the seeker's stated
   criteria + the main trade-off. Injected by cardHTML when Premise.fit
   is active. Colour band = strong / good / partial. */
.card .fitline{font-size:11.5px;line-height:1.45;margin:3px 0 1px;color:var(--muted);display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.card .fitline b{font-weight:800;font-size:10.5px;padding:1.5px 7px;border-radius:7px;white-space:nowrap;letter-spacing:.2px}
.card .fitline .fx{flex:1;min-width:0}
.card .fitline.strong b{background:#e6f4f3;color:#0a7d79;border:1px solid #bfe1df}
.card .fitline.good b{background:#eef4fb;color:var(--accent2);border:1px solid #cdddf3}
.card .fitline.partial b{background:#f6efdd;color:var(--warn);border:1px solid #e3d3a5}
/* fit-score ring — draws itself when the card scrolls into view (JS sets stroke-dashoffset) */
.card .fitline .fitring{width:27px;height:27px;flex-shrink:0;transform:rotate(-90deg)}
.fitring .rbg{fill:none;stroke:var(--panel2);stroke-width:2.8}
.fitring .rval{fill:none;stroke:currentColor;stroke-width:2.8;stroke-linecap:round;stroke-dasharray:78.54;stroke-dashoffset:78.54;transition:stroke-dashoffset .9s cubic-bezier(.2,.7,.3,1) .15s}
.fitring text{transform:rotate(90deg);transform-origin:center;fill:var(--text);font-size:10px;font-weight:800;text-anchor:middle;dominant-baseline:central;font-family:var(--font-ui)}
.fitline.strong .fitring{color:#0a7d79}
.fitline.good .fitring{color:var(--accent2)}
.fitline.partial .fitring{color:var(--warn)}
@media(prefers-reduced-motion:reduce){.fitring .rval{transition:none}}
/* "ranked by fit" pill in the results sub-header */
.fitpill{display:inline-flex;align-items:center;gap:4px;background:#e6f4f3;border:1px solid #bfe1df;color:#0a7d79;border-radius:9px;padding:2px 8px;font-size:11px;font-weight:700}

/* ── results grid: staggered card entrance (delay set inline per card) ── */
@keyframes cardin{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.grid>.card{animation:cardin .38s cubic-bezier(.2,.7,.3,1) both}
@media(prefers-reduced-motion:reduce){.grid>.card{animation:none}}

/* ── photo scrim: quiet bottom gradient so price/dots sit on any photo ── */
.card .ph::after{content:"";position:absolute;left:0;right:0;bottom:0;height:64px;background:linear-gradient(180deg,rgba(14,54,51,0),rgba(14,54,51,.45));pointer-events:none;z-index:2}

/* ── compass loading spinner: wrap a [data-i="compass"] icon in .wspin ── */
@keyframes wspin{to{transform:rotate(360deg)}}
.wspin{display:inline-flex;align-items:center;gap:8px}
.wspin [data-i] svg{animation:wspin 1.5s cubic-bezier(.55,.15,.45,.85) infinite;transform-origin:center}
@media(prefers-reduced-motion:reduce){.wspin [data-i] svg{animation:none}}

/* ── save heart: spring pop on save (class added by toggleSave) ── */
@keyframes heartpop{0%{transform:scale(1)}40%{transform:scale(1.45)}70%{transform:scale(.9)}100%{transform:scale(1)}}
.savebtn.pop{animation:heartpop .45s cubic-bezier(.2,.7,.3,1.4)}
@media(prefers-reduced-motion:reduce){.savebtn.pop{animation:none}}

/* ── ⌘K command palette (markup injected by premise-data.js ensurePalette) ── */
#pkOv{position:fixed;inset:0;background:rgba(15,20,18,.45);z-index:99998;display:none;align-items:flex-start;justify-content:center;padding:12vh 16px 16px;backdrop-filter:blur(2px)}
#pkOv.on{display:flex}
#pkCard{width:min(560px,100%);background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-pop);overflow:hidden;font-family:var(--font-ui);animation:pkin .18s cubic-bezier(.2,.7,.3,1) both}
@keyframes pkin{from{opacity:0;transform:translateY(-8px) scale(.985)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){#pkCard{animation:none}}
#pkHead{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line);color:var(--muted)}
#pkHead input{flex:1;border:none;outline:none;background:none;font-family:inherit;font-size:16px;color:var(--text);min-width:0}
#pkHead input::placeholder{color:var(--muted);opacity:.75}
#pkCard kbd{background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-size:10.5px;color:var(--muted);font-family:var(--font-ui)}
#pkList{max-height:min(340px,46vh);overflow-y:auto;padding:6px}
.pkit{display:flex;align-items:center;gap:11px;padding:10px 11px;border-radius:10px;cursor:pointer;color:var(--muted)}
.pkit .pkt{flex:1;font-size:14px;font-weight:600;color:var(--text);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pkit .pkt span{display:block;font-size:11.5px;font-weight:500;color:var(--muted);margin-top:1px}
.pkit.on,.pkit:hover{background:var(--accent-soft);color:var(--accent)}
.pkempty{padding:18px 14px;color:var(--muted);font-size:13px;text-align:center}
#pkFoot{display:flex;gap:14px;padding:9px 16px;border-top:1px solid var(--line);color:var(--muted);font-size:11px;background:var(--panel2)}
@media(max-width:560px){#pkOv{padding-top:8vh}#pkFoot{display:none}}
