

/* NO ZOOM WHEN A FIELD IS TAPPED ON A PHONE (Jacob 9/29: "I press on the agent, then I can change it ... it zooms in, and
   then it's unpleasant ... it happens in other places"): the phone zooms in on any field whose text is under 16px. On a
   touch screen every field is 16px at least, on every page. */
@media (pointer: coarse){
  input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select, textarea{font-size:16px !important}
}
/* ═══ TOKENS ═══════════════════════════════════════════════════════════════
   Jade Funding Group design system, alpha. Every value below comes from the spec; nothing here is
   invented. The rule the whole thing turns on: CONTENT OUTRANKS CHROME. Navigation, cards, borders
   and controls stay quiet so the numbers, the statuses and the next action are what the eye lands
   on. Jade is a signal, not a coat of paint.
   ═════════════════════════════════════════════════════════════════════════ */
:root{
  color-scheme:light;                     /* a browser in dark mode must not repaint this product */

  /* Brand — one action colour, used for interaction and genuine positive emphasis. Nothing else. */
  --primary:#0B7A5A; --primary-focus:#0F8F69; --primary-on-dark:#42C79A; --primary-soft:#EAF6F1;

  /* Text — four steps, so importance is legible without colour doing the work. */
  --ink:#171A1D; --body:#25292E; --body-muted:#687078;
  --ink-muted-80:#363B40; --ink-muted-48:#7D858C;

  /* Surfaces. Cards sit on the muted canvas; the dark tones are for selective emphasis only. */
  --canvas:#FFFFFF; --canvas-muted:#F6F7F8; --surface-soft:#FAFBFB;
  --surface-dark-1:#202427; --surface-dark-2:#252A2E; --surface-dark-3:#1B1F22;
  --surface-black:#111315; --surface-chip:#E8EBED;

  /* Hairlines. Quiet — prefer a surface change or space over boxing everything in. */
  --divider-soft:#EEF0F2; --hairline:#DDE1E5;

  /* Semantic only. A colour here means the state means it. */
  --success:#14865F; --success-soft:#E7F5EE;
  --warning:#A46A14; --warning-soft:#FFF5DF;
  --danger:#B44747;  --danger-soft:#FCEDED;
  --info:#3E6E91;    --info-soft:#EDF4F9;

  --rounded-xs:6px; --rounded-sm:8px; --rounded-md:12px; --rounded-lg:16px;
  --rounded-xl:20px; --rounded-pill:9999px;

  /* Shadow is for things that genuinely sit ABOVE other content. Cards get a border instead. */
  --shadow-floating:0 8px 30px rgba(20,27,31,.08);
  --ring:0 0 0 2px rgba(23,26,29,.28);          /* grey, never green (Jacob 9/25: no green, no box inside a box, anywhere) */

  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s6:24px; --s8:32px; --s12:48px; --s16:64px;

  --sans:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
  --ease:150ms cubic-bezier(.4,0,.2,1);
  --rail-w:248px; --rail-mini:68px; --top-h:64px; --page-max:1600px;

  /* ── The names the pages already speak, pointed at the system above. One stylesheet governs
        everything; the page CSS was aliased rather than rewritten in a single risky go. ── */
  --gray-25:var(--surface-soft); --gray-50:var(--canvas-muted); --gray-100:var(--divider-soft);
  --gray-200:var(--divider-soft); --gray-300:var(--hairline); --gray-400:var(--ink-muted-48);
  --gray-500:var(--body-muted); --gray-600:var(--ink-muted-80); --gray-700:var(--body);
  --gray-800:var(--ink); --gray-900:var(--ink);
  --brand-25:var(--primary-soft); --brand-50:var(--primary-soft); --brand-100:#CFE7DD;
  --brand-200:#A8D5C5; --brand-500:var(--primary-focus); --brand-600:var(--primary);
  --brand-700:#095F46; --brand-800:#074434;
  --warn-50:var(--warning-soft); --warn-100:#FBEBC8; --warn-200:#EBD9AE;
  --warn-600:var(--warning); --warn-700:#8A580F;
  --bad-50:var(--danger-soft); --bad-100:#F7DADA; --bad-200:#EFC4C4;
  --bad-600:var(--danger); --bad-700:#963A3A;
  --bg:var(--canvas-muted); --surface:var(--canvas); --surface-2:var(--surface-soft);
  --ink-2:var(--body-muted); --ink-3:var(--ink-muted-48);
  --line:var(--divider-soft); --line-strong:var(--hairline);
  --rail:var(--surface-black); --rail-ink:#FFFFFF; --rail-ink-2:#8D959B; --rail-line:#22262A;
  --r-xs:var(--rounded-xs); --r-sm:var(--rounded-sm); --r-md:var(--rounded-md);
  --r-lg:var(--rounded-lg); --r-xl:var(--rounded-xl); --r-full:var(--rounded-pill);
  --sh-xs:none; --sh-sm:none; --sh-md:var(--shadow-floating); --sh-lg:var(--shadow-floating);
  --raised:var(--surface-soft); --line-soft:var(--divider-soft); --shadow:none;
  --jade:var(--primary); --jade-ink:#095F46;
  --jade-soft:var(--primary-soft); --jade-line:#CFE7DD;
  --amber:var(--warning); --amber-soft:var(--warning-soft); --amber-line:#EBD9AE;
  --clay:var(--danger); --clay-soft:var(--danger-soft); --clay-line:#EFC4C4;
  --blue:var(--info); --green:var(--success); --red:var(--danger);
  --line-2:var(--divider-soft); --radius:var(--rounded-lg);
  --tint-green:var(--success-soft); --tint-amber:var(--warning-soft); --tint-red:var(--danger-soft);
}

/* ═══ BASE ═════════════════════════════════════════════════════════════════ */
*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0; background:var(--canvas-muted); color:var(--body);
  font:400 15px/1.5 var(--sans); letter-spacing:-.1px;
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}
/* 600, never 700/800. Display type here is controlled, not dramatic. */
h1,h2,h3,h4{margin:0; font-weight:600; color:var(--ink)}
h1{font-size:30px; line-height:1.2; letter-spacing:-.45px}
h2{font-size:22px; line-height:1.25; letter-spacing:-.25px}
a{color:var(--brand-600); text-decoration:none}
a:hover{color:var(--brand-700); text-decoration:underline}
button,input,select,textarea{font-family:inherit; color:inherit}
:focus-visible{outline:none; box-shadow:var(--ring); border-radius:var(--r-sm)}
/* No glow inside a box you type in (Jacob, 9/23: "why is there a green box inside of our box... anywhere, that green thing should not be there"). The box's own border shows where you are. */
input:focus-visible,textarea:focus-visible,select:focus-visible,[contenteditable]:focus-visible{box-shadow:none}
::selection{background:var(--brand-100)}

/* Money and counts have to line up in a column. */
.jf-num,.num{font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1; letter-spacing:-.014em}

/* ═══ TYPE RAMP ════════════════════════════════════════════════════════════
   Metric values carry tabular numerals so money in a column lines up on the decimal.
   ═════════════════════════════════════════════════════════════════════════ */
.jf-display{font-size:36px; line-height:1.12; font-weight:600; letter-spacing:-.6px; color:var(--ink)}
.jf-metric{font-size:32px; line-height:1.1; font-weight:600; letter-spacing:-.5px; color:var(--ink);
  font-variant-numeric:tabular-nums}
.jf-metric.md{font-size:24px; line-height:1.15; letter-spacing:-.3px}
.jf-h1{font-size:22px; line-height:1.25; font-weight:600; letter-spacing:-.25px; color:var(--ink)}
.jf-h2{font-size:15px; line-height:1.45; font-weight:600; letter-spacing:-.1px; color:var(--ink)}
.jf-small{font-size:13px; line-height:1.4; color:var(--body-muted)}
.jf-micro{font-size:13px; line-height:1.4; color:var(--body-muted)}
.jf-eyebrow{font-size:12px; line-height:1.2; font-weight:600; letter-spacing:.2px;
  text-transform:uppercase; color:var(--body-muted)}

/* ═══ SIDEBAR ══════════════════════════════════════════════════════════════ */
/* ONE PAGE TO THE NEXT WITHOUT A FLASH (9/24): the pages cross-fade and the rail holds still */
@view-transition{navigation:auto}
.rail{view-transition-name:jf-rail}
::view-transition-old(root),::view-transition-new(root){animation-duration:.14s}
@media (prefers-reduced-motion:reduce){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none !important}}
.jf-app{display:flex; min-height:100vh}
.rail{width:var(--rail-w); flex:none; background:var(--rail); color:var(--rail-ink);
  display:flex; flex-direction:column; position:sticky; top:0; height:100vh}
.rail-brand{display:flex; align-items:center; padding:var(--s6) 18px var(--s4)}
.rail-logo{width:148px; height:auto; display:block}
.rail-mark{width:28px; height:28px; border-radius:var(--rounded-sm); background:var(--primary);
  flex:none; display:flex; align-items:center; justify-content:center}
.rail-name{font-size:14px; font-weight:600; line-height:1.2; letter-spacing:-.012em}
.rail-sub{font-size:11px; letter-spacing:.07em; text-transform:uppercase; font-weight:500;
  color:var(--rail-ink-2); margin-top:1px}
.rail-group{padding:var(--s6) 18px var(--s2); font-size:12px; letter-spacing:.2px;
  text-transform:uppercase; font-weight:600; color:#6E767C}
.rail-nav{display:flex; flex-direction:column; gap:2px; padding:0 10px}
.rail-item{display:flex; align-items:center; gap:10px; padding:0 12px; height:42px;
  border-radius:var(--rounded-sm); color:#A9B1B7; font-size:14px; font-weight:500;
  letter-spacing:-.05px; text-decoration:none;
  transition:background var(--ease), color var(--ease); position:relative}
.rail-item:hover{background:rgba(255,255,255,.05); color:var(--rail-ink); text-decoration:none}
.rail-back{display:flex; align-items:center; gap:8px; margin:0 12px 4px; padding:0 12px; height:36px; border:1px solid rgba(255,255,255,.14); border-radius:10px; color:var(--rail-ink); font-size:13px; font-weight:500; text-decoration:none; white-space:nowrap; overflow:hidden}
.rail-back span{overflow:hidden; text-overflow:ellipsis}
.rail-back:hover{background:rgba(255,255,255,.06); color:var(--rail-ink); text-decoration:none}
.rail-back[hidden]{display:none}
/* the name beside an icon while the menu is closed (the menu cuts off anything wider than itself, so it lives on the page) */
.rail-tip{position:fixed; z-index:500; padding:6px 10px; border-radius:8px; background:#1F2529; color:#FFFFFF; font:500 13px/18px Inter,system-ui,sans-serif;
  white-space:nowrap; pointer-events:none; box-shadow:0 6px 18px rgba(10,14,18,.28); transform:translateY(-50%)}
.rail-tip[hidden]{display:none}
@media (max-width:960px){ .rail-toggle{display:none} .rail-tip{display:none} }
/* THE DEAL CARD OVER ANY PAGE (window.openDealCard below) */
.dealov{position:fixed; inset:0; z-index:255; background:rgba(20,27,31,.16)}   /* over the chat button (250: it sat on the application editor's Save, 10/5), under a ringing call (260) */
.dealov.parked{display:none}   /* a closed card kept for the next one (10/22, window.openDealCard): out of sight, catching nothing */
.dealov iframe{position:absolute; inset:0; width:100%; height:100%; border:0; background:transparent}
/* THE OVERLAYS ARRIVE AND LEAVE SOFTLY (Jacob 10/14: "the confirmations, the notify and everything are not smooth ... how can
   the animations be smoother and nicer?"): the scrim fades in, the card appears with a short fade once it has loaded instead
   of popping in half drawn, and it fades out on close. One on its way out has lost its id and catches no click. */
.dealov,.textsov{animation:jf-fade .18s ease-out}
.dealov{transition:background .2s ease}
.dealov.ready{background:transparent}
.dealov iframe,.textsov iframe{opacity:0; transition:opacity .2s cubic-bezier(.2,.8,.2,1)}
.dealov.ready iframe,.textsov.ready iframe{opacity:1}
.dealov.out,.textsov.out{opacity:0; transition:opacity .16s ease; pointer-events:none}
/* THE DIALER'S CONVERSATION OVER ANY PAGE (window.openTexts below): the Dialer draws its own panel and scrim inside */
html.ov-texts #jfgChat .jc-launch{display:none}   /* the team chat button never sits on Send */
.textsov{position:fixed; left:0; right:0; top:0; height:100vh; height:100dvh; z-index:256}   /* over the deal card (255) so a deal's texts can open on top of it, under a ringing call (260). Was 205, under the card since it rose on 10/5 (10/14) */
.textsov iframe{position:absolute; inset:0; width:100%; height:100%; border:0; background:transparent}
.dealov .wait,.textsov .wait{position:absolute; top:24px; right:24px; background:#fff; border:1px solid #DDE1E5; border-radius:999px; padding:8px 14px; font:13px Inter,system-ui,sans-serif; color:#687078}
/* the "Opening" pill waits a beat before fading in, so a card that opens at once never flashes it, and fades away on load */
.dealov .wait,.textsov .wait{animation:jf-fade .2s ease-out .15s backwards; transition:opacity .16s ease}
.dealov .wait.gone,.textsov .wait.gone{animation:none; opacity:0}
@keyframes jf-fade{from{opacity:0}to{opacity:1}}
@keyframes jf-rise{from{opacity:0; transform:translateY(6px)}to{opacity:1; transform:none}}
@keyframes jf-drop{from{opacity:0; transform:translateY(-4px)}to{opacity:1; transform:none}}
@keyframes jf-lift{from{opacity:0; transform:translateY(4px)}to{opacity:1; transform:none}}
/* Selected is a quiet lift plus a 2px jade mark — not a coloured slab. */
.rail-item.on{background:rgba(255,255,255,.07); color:#FFFFFF; font-weight:600}
.rail-item.on::before{content:""; position:absolute; left:-10px; top:9px; bottom:9px; width:2px;
  border-radius:0 2px 2px 0; background:var(--primary-on-dark)}
.rail-item svg{flex:none; stroke:currentColor; opacity:.8}
.rail-item.on svg{opacity:1}
.rail-item svg.ic-gold{stroke:#F2C14E; opacity:1}
.rail-item svg.ic-pink{stroke:#F48FB8; opacity:1}
.rail-item svg.ic-green{stroke:#4FD1A0; opacity:1}   /* green is "call" across the dashboard (Open in the Dialer) */
.rail-item svg.ic-blue{stroke:#6AA8FF; opacity:1}
.rail-item svg.ic-purple{stroke:#B69CFF; opacity:1}
/* THE MENU OPENS AND CLOSES FROM ITS OWN BUTTON (Jacob 10/14: "an open and close button for the sidebar ... I don't need it
   open ... I know what every stage is based on the icon ... when I hover over an icon, it can show me in text what it is").
   Closed it is the strip of icons and stays so under the pointer; each icon says its name in a label beside it. Open, it
   takes its full width and the page moves over for it. The choice is remembered in this browser. (Was: opens on hover, 10/11.)
   A NARROW RAIL (Jacob 10/11: "minimize, not completely"). On a
   computer the rail is a strip of icons; the pointer over it (or a key moving into it) opens it over the page, never pushing
   the page aside, and it folds back when the pointer leaves. Everything keeps its place in both widths: only the words fade,
   so nothing jumps. The counts sit on the icons while it is narrow. The diamond alone stands for the logo. Phones keep their
   drawer (the 960px rules below). */
@media (min-width:961px){
  .jf-app{padding-left:var(--rail-mini); transition:padding-left .2s cubic-bezier(.2,.8,.2,1)}
  html.rail-open .jf-app{padding-left:var(--rail-w)}
  .rail-toggle{cursor:pointer; background:transparent; font-family:inherit; text-align:left}
  .rail-toggle svg{flex:none; transition:transform .2s}
  .rail:not(.open) .rail-toggle svg{transform:rotate(180deg)}
  .rail-toggle:focus-visible{outline:2px solid rgba(255,255,255,.5); outline-offset:2px}
  /* closed, every highlight is a square inside the strip, never a bar cut off at its edge (Jacob 10/14) */
  .rail-item,.rail-back,.rail-me{transition:width .2s cubic-bezier(.2,.8,.2,1), padding .2s, background var(--ease), color var(--ease)}
  .rail:not(.open):not(.keep) .rail-item{width:48px; padding:0 15px}
  .rail:not(.open):not(.keep) .rail-back{width:44px; padding:0 13px}
  .rail:not(.open):not(.keep) .rail-me{width:48px; padding:0 8px; overflow:hidden}
  .rail-quota{transition:width .2s cubic-bezier(.2,.8,.2,1), padding .2s}
  .rail:not(.open):not(.keep) .rail-quota{width:48px; padding:6px 10px 8px}
  .rail{position:fixed; left:0; top:0; bottom:0; height:auto; width:var(--rail-mini); z-index:80; overflow-x:hidden; overflow-y:auto; scrollbar-width:none;
    transition:width .2s cubic-bezier(.2,.8,.2,1), box-shadow .2s}
  .rail.open{width:var(--rail-w)}
  .rail.keep:not(.open){width:var(--rail-w); box-shadow:10px 0 32px rgba(10,14,18,.28)}
  .rail-nav,.rail-group,.rail-brand,.rail-foot{width:var(--rail-w); box-sizing:border-box; flex:none}
  .rail-back{width:calc(var(--rail-w) - 24px); box-sizing:border-box; flex:none}
  .rail-item,.rail-back,.rail-me{white-space:nowrap}
  .rail-item > span:not(.rail-badge),.rail-group,.rail-back span,.rail-me .who,.rail-me > svg:last-child,.rail-quota .rq-t{transition:opacity .14s}
  /* opening, the words fade in a moment after the menu starts to widen, so they never show cut off at its edge (10/14) */
  .rail.open :is(.rail-item > span:not(.rail-badge),.rail-group,.rail-back span,.rail-me .who,.rail-me > svg:last-child,.rail-quota .rq-t){transition:opacity .18s ease .06s}
  .rail:not(.open):not(.keep) :is(.rail-item > span:not(.rail-badge),.rail-group,.rail-back span,.rail-me .who,.rail-me > svg:last-child,.rail-quota .rq-t){opacity:0}
  .rail:not(.open):not(.keep) .rail-badge{position:absolute; left:28px; top:5px; min-width:16px; height:16px; padding:0 4px; font-size:10px; line-height:16px}
  .rail-logo{transition:clip-path .2s, transform .2s}
  .rail:not(.open):not(.keep) .rail-logo{clip-path:inset(40% 35% 0 35%); transform:translate(-58px,-12px)}
}
.rail-foot{margin-top:auto; padding:10px; border-top:1px solid var(--rail-line); position:relative}
.rail-badge{margin-left:auto; min-width:18px; height:18px; padding:0 6px; border-radius:999px; background:#42C79A; color:#0F1412; font:700 11px/18px Inter,sans-serif; text-align:center}
.rail-badge[hidden]{display:none}
/* KIXIE RECYCLE MOVES LEFT THIS WEEK, IN THE MENU'S FOOT (Jacob 10/14: "I want guys to know what the limit is, with a visual to
   see how many recycle triggers they still have left"). Agents only: owners have no limit. A thin bar in the stage's own
   colour shows what is left; amber at 20 or fewer, red-ish at none. Closed, only the bar stays and the words come back in
   the label beside it. */
.rail-quota{display:block; width:100%; margin:0 0 8px; padding:6px 8px 8px; border-radius:var(--r-sm); color:var(--rail-ink-2); font-size:12px; line-height:16px; box-sizing:border-box; overflow:hidden}
.rail-quota[hidden]{display:none}
.rail-quota .rq-t{display:flex; align-items:baseline; justify-content:space-between; gap:8px; white-space:nowrap}
.rail-quota .rq-l{min-width:0; overflow:hidden; text-overflow:ellipsis}
.rail-quota .rq-n{flex:none; color:var(--rail-ink); font-weight:600; font-variant-numeric:tabular-nums}
.rail-quota .rq-bar{display:block; height:3px; margin-top:6px; border-radius:2px; background:rgba(255,255,255,.12); overflow:hidden}
.rail-quota .rq-bar i{display:block; width:0; height:100%; border-radius:2px; background:#1F95BD; transition:width .4s cubic-bezier(.2,.8,.2,1)}
.rail-quota.low .rq-n{color:#F2C14E}
.rail-quota.low .rq-bar i{background:#E0A93B}
.rail-quota.out .rq-n{color:#F29B9B}
.jf-sh{position:fixed; z-index:400; background:#fff; border-radius:0 0 24px 24px; box-shadow:0 4px 8px 1px rgba(60,64,67,.24); clip-path:inset(0 -24px -24px -24px); padding:0 0 10px; font:400 15px Inter,system-ui,sans-serif; color:#202124; max-height:440px; overflow:auto}
.jf-sh::before{content:""; display:block; height:1px; background:#E8EAED; margin:0 20px 8px}
.jf-sh[hidden]{display:none}
.jf-sh:not([hidden]){animation:jf-drop .16s cubic-bezier(.2,.8,.2,1)}   /* it settles out of the search box with a soft fade (10/14) */
.jf-sh-on{background:#fff !important; border-color:transparent !important; box-shadow:0 1px 8px 1px rgba(60,64,67,.24) !important; border-bottom-left-radius:0 !important; border-bottom-right-radius:0 !important; position:relative; z-index:401}
.jf-sh-r{display:flex; align-items:center}
.jf-sh-r:hover{background:#F1F3F4}
.jf-sh-go{flex:1; min-width:0; display:flex; align-items:center; gap:20px; text-align:left; border:0; background:none; padding:10px 8px 10px 24px; font:inherit; color:inherit; cursor:pointer}
.jf-sh-go span{min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.jf-sh-ic{flex:none; color:#5F6368}
.jf-sh-st{flex:none; margin-left:auto; font-style:normal; font:600 11px/20px Inter,system-ui,sans-serif; padding:0 8px; border-radius:4px; white-space:nowrap; max-width:45%; overflow:hidden; text-overflow:ellipsis; text-transform:uppercase; letter-spacing:.02em}
.jf-sh-x{flex:none; width:36px; height:36px; margin-right:12px; border:0; background:none; border-radius:999px; color:#5F6368; cursor:pointer; display:inline-flex; align-items:center; justify-content:center}
.jf-sh-x:hover{background:#E3E5E8; color:#202124}
.jf-sh-m{display:flex; flex-direction:column; min-width:0}
.jf-sh-m b,.jf-sh-m small{white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.jf-sh-m b{font-weight:500; color:#202124}
.jf-sh-m small{font-size:13px; color:#5F6368}
.jf-sh-d{flex:none; margin-left:auto; padding-right:16px; font-style:normal; font-size:12.5px; color:#5F6368}
.jf-sh-f{display:flex; justify-content:flex-end; padding:2px 16px 0}
.jf-sh-clear{border:0; background:none; color:#5F6368; font:500 12.5px Inter,system-ui,sans-serif; cursor:pointer; padding:6px 8px; border-radius:6px}
.jf-sh-clear:hover{background:#F1F3F4; color:#202124}
.rail-me{display:flex; align-items:center; gap:10px; width:100%; height:44px; padding:0 8px;
  border-radius:var(--r-sm); background:none; border:0; color:var(--rail-ink); cursor:pointer;
  text-align:left; transition:background var(--ease)}
.rail-me:hover{background:rgba(255,255,255,.05)}
.rail-avatar{width:28px; height:28px; border-radius:var(--rounded-pill); flex:none;
  background:var(--primary); color:#fff; display:flex; align-items:center;
  justify-content:center; font-weight:600; font-size:12px}
.rail-menu{position:absolute; left:10px; right:10px; bottom:60px; background:var(--canvas);
  border:1px solid var(--divider-soft); border-radius:var(--rounded-md);
  box-shadow:var(--shadow-floating);
  overflow:hidden; display:block; z-index:40;
  /* fades and rises 4px out of the button (10/14); shut, it is invisible to the pointer and the keyboard at once */
  opacity:0; visibility:hidden; transform:translateY(4px); pointer-events:none;
  transition:opacity .16s ease, transform .16s cubic-bezier(.2,.8,.2,1), visibility 0s linear .16s}
.rail-menu.open{opacity:1; visibility:visible; transform:none; pointer-events:auto; transition:opacity .18s ease, transform .18s cubic-bezier(.2,.8,.2,1), visibility 0s}
.rail-menu a{display:flex; align-items:center; gap:10px; padding:10px 14px; color:var(--ink);
  font-size:14px; font-weight:500; text-decoration:none}
.rail-menu a:hover{background:var(--gray-50); text-decoration:none}

/* ═══ MOBILE HEADER AND DRAWER ═════════════════════════════════════════════ */
.jf-mobilebar{display:none; align-items:center; gap:var(--s3); height:56px; padding:0 var(--s2) 0 var(--s1);
  background:var(--rail); position:sticky; top:0; z-index:50; flex:none}
.jf-burger{width:46px; height:46px; border:0; background:none; color:#fff; cursor:pointer;
  display:flex; align-items:center; justify-content:center; border-radius:var(--r-sm);
  transition:background var(--ease)}
.jf-burger:active{background:rgba(255,255,255,.1)}
.jf-mobilelogo{height:24px; width:auto; display:block}
.jf-scrim{display:none; position:fixed; inset:0; background:rgba(16,24,40,.55); z-index:60;
  opacity:0; pointer-events:none; transition:opacity .22s ease}
.jf-scrim.open{opacity:1; pointer-events:auto}
body.jf-locked{overflow:hidden}

/* ═══ PAGE FRAME ═══════════════════════════════════════════════════════════ */
.jf-main{flex:1; min-width:0; display:flex; flex-direction:column}
.jf-topbar{display:flex; align-items:center; gap:var(--s4); min-height:var(--top-h);
  /* white, one hairline, nothing else. The page title leads; search stays secondary. */
  padding:var(--s3) var(--s8); background:var(--surface); border-bottom:1px solid var(--line);
  position:sticky; top:0; z-index:20; flex-wrap:wrap}
.jf-topbar h1{font-size:18px; font-weight:600; letter-spacing:-.018em}
.jf-lede{font-size:13px; color:var(--ink-2)}
.jf-topbar .right{margin-left:auto; display:flex; align-items:center; gap:var(--s2); flex-wrap:wrap}
.jf-page{padding:var(--s6) var(--s6) var(--s12); width:100%; max-width:var(--page-max)}
.jf-section{margin-top:var(--s8)}
.jf-section-head{display:flex; align-items:baseline; gap:var(--s3); margin-bottom:var(--s3);
  flex-wrap:wrap}
.jf-section-head h2{font-size:15px; font-weight:600}
.jf-section-head .note{font-size:13px; color:var(--ink-3)}

/* ═══ BUTTONS ══════════════════════════════════════════════════════════════ */
.jf-btn{display:inline-flex; align-items:center; justify-content:center; gap:var(--s2);
  height:40px; padding:0 18px; border-radius:var(--rounded-pill); border:1px solid var(--hairline);
  background:var(--canvas); color:var(--ink); font-size:14px; font-weight:500; line-height:1.2;
  letter-spacing:-.05px; cursor:pointer; text-decoration:none; white-space:nowrap;
  transition:background var(--ease), border-color var(--ease), color var(--ease),
             transform var(--ease)}
.jf-btn[hidden]{display:none}   /* a button marked hidden stays hidden (10/4: Shop a Deal's empty "Taken off" showed with nothing taken off, and opened an empty box) */
/* THE DASHBOARD'S OWN QUESTION BOX (Jacob 10/4: "I want messages to be internal messages, not like a Chrome pop-up"):
   window.jfConfirm and window.jfAlert, below, instead of the browser's confirm() and alert() */
.jf-dlg,.jf-dlg-out{position:fixed; inset:0; z-index:1000; background:rgba(23,26,29,.38); display:flex; align-items:center; justify-content:center; padding:16px}
/* it fades in with a slight rise and fades out before it goes (Jacob 10/14: "the confirmations ... are not smooth"); the one
   leaving is .jf-dlg-out, so a page asking whether a question is open (.jf-dlg) already hears no, and it catches no click */
.jf-dlg{animation:jf-fade .18s ease-out}
.jf-dlg .jf-dlg-box{animation:jf-rise .18s cubic-bezier(.2,.8,.2,1)}
.jf-dlg-out{opacity:0; pointer-events:none; transition:opacity .14s ease}
.jf-dlg-out .jf-dlg-box{transform:translateY(4px); transition:transform .14s ease}   /* in the middle of the screen (Jacob 10/5: it sat near the top, like Chrome's own) */
/* never taller than the screen: a long list scrolls inside, the title and the buttons stay in view (Jacob 10/5: Clean up on
   Vu Development listed so many copies that its buttons were off the screen) */
.jf-dlg-box{width:min(440px, 100%); max-height:calc(100vh - 32px); max-height:calc(100dvh - 32px); background:var(--canvas, #fff); border:1px solid var(--hairline, #E3E6EA); border-radius:16px; box-shadow:0 18px 48px rgba(23,26,29,.18); padding:22px 22px 18px; display:flex; flex-direction:column; gap:18px}
.jf-dlg-h{font-size:16px; font-weight:600; color:var(--ink, #1F2328); margin:0; flex:none}
.jf-dlg-t{font-size:14.5px; line-height:1.55; color:var(--body, #363B40); white-space:pre-line; overflow-wrap:anywhere; min-height:0; overflow-y:auto; overscroll-behavior:contain}
.jf-dlg-b{display:flex; justify-content:flex-end; gap:10px; flex-wrap:wrap; flex:none}
.jf-dlg-b .jf-btn.primary.danger{background:var(--danger); border-color:var(--danger); color:#fff}
.jf-dlg-b .jf-btn:focus-visible{outline:2px solid #9AA1A8; outline-offset:2px}
@media (max-width:760px){ .jf-dlg-b .jf-btn{flex:1} }
/* THE DASHBOARD'S OWN TOAST (Jacob 10/14 sweep: "I flip Paid and nothing tells me it saved"): window.jfToast below; the Deals page .toast look, above the question box (1000) and the deal card (255/256) */
.jf-toast{position:fixed; left:50%; bottom:22px; transform:translateX(-50%); z-index:1100; background:#1F2328; color:#fff; padding:10px 18px 12px; border-radius:999px; font-size:13px; font-weight:500; line-height:18px; box-shadow:0 8px 30px rgba(0,0,0,.2); max-width:min(90vw,560px); overflow:hidden; overflow-wrap:anywhere; text-align:center; pointer-events:none;
  display:flex; align-items:center; justify-content:center; gap:12px}
.jf-toast[hidden]{display:none}
/* window.jfToast(text, { tone: 'ok' | 'bad', ms, undo }) (Jacob 10/14: "when I hit AI underwriting there's this bar that goes
   by. It is smooth compared to other things"): it slides up 8px and fades in, a thin line along its foot shrinks over its
   time like the underwriting bar, and it fades out. Only a toast with Undo takes the pointer, and never while it fades. */
.jf-toast.in{animation:jf-toast-in .24s cubic-bezier(.2,.8,.2,1)}
@keyframes jf-toast-in{from{opacity:0; transform:translate(-50%,8px)}to{opacity:1; transform:translate(-50%,0)}}
.jf-toast.out{opacity:0; transform:translate(-50%,4px); transition:opacity .18s ease, transform .18s ease}
.jf-toast.has-undo{pointer-events:auto}
.jf-toast.out,.jf-toast.out *{pointer-events:none !important}
.jf-toast .jf-toast-t{min-width:0}
.jf-toast .jf-toast-u{flex:none; border:0; background:transparent; color:#42C79A; font:600 13px/18px var(--sans); padding:4px 8px; margin:-4px -8px -4px 0; border-radius:999px; cursor:pointer}
.jf-toast .jf-toast-u:hover{background:rgba(255,255,255,.08)}
.jf-toast .jf-toast-u:focus-visible{outline:2px solid rgba(255,255,255,.5); outline-offset:1px; box-shadow:none}
.jf-toast .jf-toast-p{position:absolute; left:18px; right:18px; bottom:4px; height:2px; border-radius:2px; background:rgba(255,255,255,.45); transform-origin:left center; animation:jf-toast-p linear forwards}
.jf-toast.ok .jf-toast-p{background:#42C79A}
.jf-toast.bad .jf-toast-p{background:#F29B9B}
.jf-toast.bad{background:#2A1F21}
@keyframes jf-toast-p{from{transform:scaleX(1)}to{transform:scaleX(0)}}
html.embed .jf-toast{visibility:visible}
/* EMOJIS IN ANY TEXT BOX (Jacob 10/19: "add emojis to both the dialer and anywhere we can insert text"), window.jfEmoji in
   SHELL_JS: a small card of common emojis, grouped, over the page beside the smiley button. Fixed to the screen and kept
   inside it (a 375px phone too), above the chat panel and the deal card, under the question box (1000). */
.jf-emo{position:fixed; z-index:990; box-sizing:border-box; background:var(--canvas, #fff); border:1px solid var(--hairline, #E3E6EA); border-radius:14px; box-shadow:0 12px 34px rgba(23,26,29,.16); padding:4px 10px 10px; overflow-y:auto; overscroll-behavior:contain; font-family:Inter,system-ui,sans-serif; animation:jf-fade .14s ease-out; visibility:visible}
.jf-emo[hidden]{display:none}
.jf-emo .jf-emo-h{margin:8px 2px 4px; font-size:11px; font-weight:600; line-height:14px; letter-spacing:.04em; text-transform:uppercase; color:var(--ink-3, #687078)}
.jf-emo .jf-emo-g{display:grid; grid-template-columns:repeat(auto-fill, minmax(36px, 1fr)); gap:2px}
.jf-emo .jf-emo-b{height:36px; min-width:0; width:auto; margin:0; padding:0; border:0; border-radius:8px; background:transparent; box-shadow:none; font:400 21px/1 "Apple Color Emoji","Segoe UI Emoji","Noto Color Emoji",sans-serif; cursor:pointer; display:flex; align-items:center; justify-content:center}
.jf-emo .jf-emo-b:hover{background:#F1F3F4}
.jf-emo .jf-emo-b:focus-visible{outline:2px solid #9AA1A8; outline-offset:-2px}
@media (max-width:760px){ .jf-emo .jf-emo-g{grid-template-columns:repeat(auto-fill, minmax(40px, 1fr))} .jf-emo .jf-emo-b{height:42px; font-size:24px} }
@media (prefers-reduced-motion:reduce){ .jf-emo{animation:none} }
/* A BUTTON AT WORK (Jacob 10/15), window.jfBusy in SHELL_JS: the button keeps its size and says what it is doing on one
   line. Two looks, both picked by Jacob from the preview (docs/button-loading-options.html): the LIVING ORB, a small glowing
   drop that turns and breathes beside the words, on every button; the LIQUID FILL, colour flowing through the whole button,
   on the main actions (Shop it, Send). Both fade in after a moment, so an answer that comes at once never flashes. */
.jf-busy{overflow:hidden !important; cursor:progress !important}
.jf-busy:disabled,.jf-busy[disabled],.jf-busy[aria-disabled=true]{opacity:1 !important}
a.jf-busy{pointer-events:none}
/* the words are not clipped by their own line (that cut the orb's glow into a square); the button's rounded edge clips */
.jf-busy > .jf-busy-t{display:block; min-width:0; max-width:100%; margin:0 auto; text-align:center; white-space:nowrap; position:relative; z-index:1}
.jf-busy-orb{display:inline-block; width:14px; height:14px; margin-right:8px; vertical-align:-2px; border-radius:50%; opacity:0;
  background:radial-gradient(circle at 34% 32%, rgba(255,255,255,.95) 0 9%, transparent 11%), conic-gradient(from 0deg, #7C5CF5, #36B5D8, #5F78DA, #7C5CF5);
  box-shadow:0 0 10px rgba(124,92,245,.45); animation:jf-fade .3s ease-out .12s forwards, jf-spin 1.6s linear infinite; position:relative}
/* the glow breathes by fading a second, brighter glow in and out (10/22): a moving box-shadow is painted again on every frame,
   60 times a second for the whole of a 90-second reading; a fade is done by the graphics chip alone */
.jf-busy-orb::after{content:""; position:absolute; inset:0; border-radius:50%; box-shadow:0 0 16px rgba(54,181,216,.75); opacity:0; pointer-events:none; animation:jf-breathe 1.6s ease-in-out infinite}
.jf-busy-t.only > .jf-busy-orb{margin-right:0}
@keyframes jf-spin{to{transform:rotate(1turn)}}
@keyframes jf-breathe{50%{opacity:1}}
.jf-busy-fill{isolation:isolate; animation:jf-ink .35s ease-out .12s forwards}
/* the colour is a square at least 1.6 times the button's width, centred and turning, so it covers every corner of a wide
   button too (a phone-wide Send) and no white shows at the rounded ends */
.jf-busy-fill::before{content:""; position:absolute; left:50%; top:50%; width:max(320px, 160%); aspect-ratio:1; translate:-50% -50%; z-index:0; opacity:0; pointer-events:none;
  background:conic-gradient(from 0deg, #5F78DA, #7C5CF5, #36B5D8, #0B7A5A, #5F78DA); filter:blur(14px); animation:jf-fade .35s ease-out .12s forwards, jf-spin 2.4s linear infinite}
.jf-busy-fill > *{position:relative; z-index:1}
.jf-busy-fill > .jf-busy-t{text-shadow:0 1px 2px rgba(20,24,60,.22)}
/* the fill is clipped inside the border, so the button's own colour stops there too (background-clip): a jade 1px ring
   showed round a wide Send */
.jf-busy-fill{background-clip:padding-box !important}
@keyframes jf-ink{to{color:#fff; border-color:transparent; box-shadow:0 4px 16px -6px rgba(95,120,218,.6)}}
/* A BUTTON THAT ANSWERS (Jacob 10/15: "once something is done, it confirms it in a nice way as well, not something that you
   have to search for"): window.jfBusy's done.ok('4 named') and done.bad('Not saved') in SHELL_JS. The button the person pressed
   turns, at the same size, into a soft jade state with a check that draws itself (red with a cross for a failure), holds a
   moment (1.6 s, a failure 2.4 s) and eases back to its own label. Played once; the toast still carries the longer words. */
.jf-said{pointer-events:none; overflow:hidden !important; isolation:isolate;
  transition:background-color .35s cubic-bezier(.2,.8,.2,1), border-color .35s ease, color .35s ease, box-shadow .35s ease !important}
.jf-said:disabled,.jf-said[disabled]{opacity:1 !important}
.jf-said-ok{background:#E8F5EF !important; border-color:rgba(11,122,90,.30) !important; color:#0A6B4F !important; box-shadow:0 0 0 3px rgba(11,122,90,.10), 0 4px 14px -6px rgba(11,122,90,.45) !important}
.jf-said-bad{background:#FCEEEC !important; border-color:rgba(192,57,43,.30) !important; color:#B0352A !important; box-shadow:0 0 0 3px rgba(192,57,43,.09), 0 4px 14px -6px rgba(192,57,43,.40) !important}
.jf-said > .jf-said-t{display:flex; align-items:center; gap:7px; min-width:0; max-width:100%; margin:0 auto; white-space:nowrap; position:relative; z-index:1;
  font-weight:600; animation:jf-said-in .32s cubic-bezier(.2,.8,.2,1) both; transition:opacity .18s ease, transform .18s ease}
/* centred by auto margins, not justify-content, so words too long overflow to the right only and are measured truly */
.jf-said > .jf-said-t > span{flex:none}
.jf-said > .jf-said-t > :first-child{margin-left:auto} .jf-said > .jf-said-t > :last-child{margin-right:auto}
.jf-said > .jf-said-t.out{opacity:0; transform:translateY(-2px)}
.jf-said-i{flex:none; display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; border-radius:50%; background:#0B7A5A; font-style:normal;
  animation:jf-said-pop .42s cubic-bezier(.2,.9,.3,1.25) both, jf-said-ring .9s ease-out .16s}
.jf-said-bad .jf-said-i{background:#C0392B; animation:jf-said-pop .42s cubic-bezier(.2,.9,.3,1.25) both, jf-said-ring-bad .9s ease-out .16s}
.jf-said-i svg{display:block; width:12px; height:12px; fill:none; stroke:#fff; stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; overflow:visible}
.jf-said-i path{stroke-dasharray:1; stroke-dashoffset:1; animation:jf-said-draw .4s cubic-bezier(.65,0,.35,1) .16s forwards}
.jf-said-fill::before{content:""; position:absolute; left:50%; top:50%; width:max(320px, 160%); aspect-ratio:1; translate:-50% -50%; z-index:0; pointer-events:none;
  background:conic-gradient(from 0deg, #5F78DA, #7C5CF5, #36B5D8, #0B7A5A, #5F78DA); filter:blur(14px); animation:jf-said-melt .55s ease-out forwards}
.jf-said-back{transition:background-color .4s ease, border-color .4s ease, color .4s ease, box-shadow .4s ease !important; animation:jf-said-back .4s ease}
@keyframes jf-said-in{from{opacity:0; transform:translateY(3px)}to{opacity:1; transform:none}}
@keyframes jf-said-pop{from{opacity:0; transform:scale(.4)}to{opacity:1; transform:none}}
@keyframes jf-said-ring{from{box-shadow:0 0 0 0 rgba(11,122,90,.38)}to{box-shadow:0 0 0 7px rgba(11,122,90,0)}}
@keyframes jf-said-ring-bad{from{box-shadow:0 0 0 0 rgba(192,57,43,.34)}to{box-shadow:0 0 0 7px rgba(192,57,43,0)}}
@keyframes jf-said-draw{to{stroke-dashoffset:0}}
@keyframes jf-said-melt{from{opacity:1}to{opacity:0; transform:rotate(.2turn)}}
@keyframes jf-said-back{from{color:transparent}}
.jf-btn:hover{background:var(--surface-soft); text-decoration:none}
/* Pressed state is a hair of scale, not a bounce. This is money, not a game. */
.jf-btn:active{transform:scale(.98)}
.jf-btn.primary{background:var(--primary); border-color:var(--primary); color:#fff}
.jf-btn.primary:hover{background:var(--primary-focus); border-color:var(--primary-focus); color:#fff}
.jf-btn.ghost{border-color:transparent; background:transparent; color:var(--primary); padding:0 14px}
.jf-btn.ghost:hover{background:var(--primary-soft)}
.jf-btn.danger{color:var(--danger)}
.jf-btn.danger:hover{background:var(--danger-soft); border-color:var(--danger); color:var(--danger)}
.jf-btn.sm{height:32px; padding:0 14px; font-size:13px}
/* 40px circle for an icon that needs no label: overflow, close, a row action. */
.jf-btn.icon{width:40px; height:40px; padding:0; border-radius:var(--rounded-pill);
  background:var(--surface-chip); border-color:transparent; color:var(--ink)}
.jf-btn.icon:hover{background:var(--hairline)}
.jf-btn[disabled],.jf-btn.is-disabled{opacity:.5; cursor:not-allowed}
.jf-btn[disabled]:hover{background:var(--canvas); transform:none}

/* ═══ FORM CONTROLS ════════════════════════════════════════════════════════ */
.jf-field{display:flex; align-items:center; gap:var(--s2); height:40px; padding:0 16px;
  border-radius:var(--rounded-pill); border:1px solid var(--hairline); background:var(--canvas);
  color:var(--ink);
  transition:border-color var(--ease), box-shadow var(--ease); position:relative}
.jf-field:focus-within{border-color:#9AA1A7; box-shadow:none}
.jf-field input,.jf-field select{border:0; background:none; outline:none; min-width:0; width:100%;
  font-size:14px; height:100%}
.jf-field select{cursor:pointer; appearance:none; padding-right:18px}
.jf-field.select::after{content:""; position:absolute; right:12px; width:6px; height:6px;
  border-right:1.5px solid var(--ink-3); border-bottom:1.5px solid var(--ink-3);
  transform:translateY(-2px) rotate(45deg); pointer-events:none}
.jf-field input::placeholder{color:var(--ink-3)}
.jf-label{display:block; font-size:12px; font-weight:600; letter-spacing:.2px;
  color:var(--ink-muted-80); margin-bottom:6px}

/* ═══ STATUS — one vocabulary for the whole product ════════════════════════ */
.jf-badge{display:inline-flex; align-items:center; gap:6px; height:24px; padding:0 9px;
  border-radius:var(--rounded-pill); font-size:13px; font-weight:600; line-height:1.35;
  white-space:nowrap; background:var(--surface-chip); color:var(--ink-muted-80);
  border:1px solid transparent}
.jf-badge::before{content:""; width:6px; height:6px; border-radius:var(--r-full);
  background:currentColor; opacity:.85; flex:none}
.jf-badge.bare::before{display:none}
.jf-badge.success{background:var(--success-soft); color:var(--success)}
.jf-badge.pending{background:var(--surface-chip); color:var(--ink-muted-80)}
.jf-badge.attention{background:var(--warning-soft); color:var(--warning)}
/* Declined is the commonest answer in this business, so it is soft — but it is still a NO, and a
   board where every answer is the same grey tells the eye nothing. */
.jf-badge.declined{background:var(--danger-soft); color:var(--danger)}
.jf-badge.error{background:var(--danger-soft); color:var(--danger)}
.jf-badge.info{background:var(--info-soft); color:var(--info)}

/* ═══ SURFACES ═════════════════════════════════════════════════════════════ */
/* The hairline, not the soft divider. On an almost-white page a #EEF0F2 edge disappears and every
   card dissolves into the background — which is most of why this looked lifeless. */
.jf-card{background:var(--canvas); border:1px solid var(--hairline);
  border-radius:var(--rounded-lg)}
.jf-card-head{display:flex; align-items:center; gap:var(--s3); padding:var(--s4) var(--s6);
  border-bottom:1px solid var(--divider-soft)}
.jf-card-body{padding:var(--s6)}

/* KPI — emphasis by type and space, never by a coloured slab */
.jf-kpis{display:grid; gap:var(--s6); grid-template-columns:repeat(auto-fit,minmax(232px,1fr))}
.jf-kpi{background:var(--canvas); border:1px solid var(--hairline);
  border-radius:var(--rounded-lg); padding:var(--s6) 20px; min-width:0}
.jf-kpi .label{font-size:12px; font-weight:600; letter-spacing:.2px; color:var(--body-muted)}
.jf-kpi .value{font-size:32px; font-weight:600; letter-spacing:-.5px; line-height:1.1;
  margin-top:10px; color:var(--ink); font-variant-numeric:tabular-nums}
/* One primary metric and one supporting line. Colour only where the number MEANS good or bad. */
.jf-kpi .value.brand{color:var(--primary)}
.jf-kpi .value.warn{color:var(--warning)}
.jf-kpi .value.muted{color:var(--ink-muted-48)}
.jf-kpi .foot{font-size:13px; color:var(--body-muted); margin-top:8px}
.jf-kpi .foot b{font-weight:600; color:var(--body)}
/* The one dark card. Sparingly: a single number worth stopping at, never a whole row of them. */
.jf-kpi.lead{background:var(--surface-dark-1); border-color:transparent; padding:var(--s6)}
.jf-kpi.lead .label{color:#9AA3A9}
.jf-kpi.lead .value{color:#fff}
.jf-kpi.lead .value.brand{color:var(--primary-on-dark)}
.jf-kpi.lead .foot,.jf-kpi.lead .foot b{color:#B9C0C5}

/* ═══ TABLES AND ROWS ══════════════════════════════════════════════════════ */
.jf-table{width:100%; border-collapse:separate; border-spacing:0}
.jf-table th{text-align:left; font-size:12px; font-weight:600; letter-spacing:.2px;
  color:var(--body-muted); padding:12px var(--s4); background:var(--surface-soft);
  border-bottom:1px solid var(--divider-soft); white-space:nowrap}
.jf-table th.right,.jf-table td.right{text-align:right}
.jf-table td{padding:14px var(--s4); border-bottom:1px solid var(--divider-soft); font-size:14px;
  line-height:1.4; color:var(--body); vertical-align:middle; height:48px}
.jf-table tbody tr:last-child td{border-bottom:0}
.jf-table tbody tr:hover td{background:var(--surface-soft)}
.jf-table tr.on td{background:var(--primary-soft)}
/* Money reads down the column, so it is right-aligned and tabular whatever the row height. */
.jf-table td.money,.jf-table td.right{font-variant-numeric:tabular-nums}
.jf-table td.money{font-weight:600; color:var(--ink); text-align:right}

.jf-list-row{display:flex; align-items:center; gap:var(--s4); padding:12px var(--s6);
  border-bottom:1px solid var(--divider-soft); min-height:56px; transition:background var(--ease)}
.jf-list-row:last-child{border-bottom:0}
.jf-list-row:hover{background:var(--surface-soft)}

/* ═══ MASTER–DETAIL ════════════════════════════════════════════════════════ */
/* A section's worth of air above the master–detail. It had none, so a heading sat on the border of
   whatever block came before it. */
.jf-split{display:grid; grid-template-columns:360px minmax(0,1fr); gap:var(--s6); align-items:start;
  margin-top:var(--s8)}
.jf-pick{display:flex; align-items:flex-start; gap:var(--s3); width:100%; text-align:left;
  cursor:pointer; background:var(--canvas); border:1px solid var(--hairline);
  border-radius:var(--rounded-md); padding:14px; color:inherit;
  position:relative; overflow:hidden; min-height:56px;
  transition:border-color var(--ease), background var(--ease)}
.jf-pick:hover{background:var(--surface-soft); border-color:var(--hairline)}
/* Selected: the soft jade surface the spec reserves for a chosen row, plus a jade edge. */
.jf-pick.on{border-color:var(--primary); background:var(--primary-soft)}
.jf-pick.on::before{content:""; position:absolute; left:0; top:10px; bottom:10px; width:3px;
  border-radius:0 3px 3px 0; background:var(--primary)}
.jf-panel{background:var(--canvas); border:1px solid var(--hairline);
  border-radius:var(--rounded-lg); overflow:hidden; position:sticky;
  top:calc(var(--top-h) + var(--s6)); max-height:calc(100vh - var(--top-h) - var(--s8));
  display:flex; flex-direction:column}
.jf-panel-head{padding:var(--s6) var(--s6) var(--s4); border-bottom:1px solid var(--line);
  display:flex; align-items:flex-start; gap:var(--s4); flex-wrap:wrap}
.jf-panel-body{overflow-y:auto; padding:var(--s2) 0}
.jf-panel-foot{border-top:1px solid var(--divider-soft); padding:12px var(--s6);
  background:var(--surface-soft);
  display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap}

/* THE LIST HOLDS ITS PLACE, LIKE THE PANEL BESIDE IT.
   The answers panel already stood still; the list of deals did not, so every deal shopped made the
   page taller. At fourteen deals, reading a funder's reply and then picking the next merchant meant
   travelling back up the whole page. Now the column is as tall as the screen and no taller, and the
   deals scroll INSIDE it — the page stops growing however good the day is.
   The 2px of padding is for the selected deal's shadow and its green edge, which a plain overflow
   would shave off. */
.jf-picklist{position:sticky; top:calc(var(--top-h) + var(--s6));
  max-height:calc(100vh - var(--top-h) - var(--s8));
  display:flex; flex-direction:column; min-height:0}
.jf-picklist > .jf-scroll{overflow-y:auto; min-height:0; flex:1 1 auto; scrollbar-gutter:stable;
  padding:2px var(--s2) var(--s2) 3px; margin:-2px calc(var(--s2) * -1) 0 -3px}
/* A column flex box with a ceiling SQUASHES its children to fit — which crushed every deal card
   down over its own second line. Nothing in this list may shrink; it scrolls instead. */
.jf-picklist > .jf-scroll > *{flex:0 0 auto}
.jf-picklist > .jf-section-head,.jf-picklist > .jf-find,.jf-picklist > .hint{flex:0 0 auto}
.jf-find{margin-bottom:var(--s3)}

/* A card that IS the exception, not a card with an exception inside it. */
.jf-card.attention{background:var(--warning-soft); border-color:#E6D2A6}
.jf-card.attention .jf-card-head{border-bottom-color:#EBDDBC}
.jf-card.attention .jf-h2{color:#7A4F0F}

/* ═══ ALERTS ═══════════════════════════════════════════════════════════════ */
.jf-alert{display:flex; gap:var(--s3); padding:14px var(--s4); border-radius:var(--rounded-md);
  border:1px solid var(--divider-soft); background:var(--canvas); font-size:14px; color:var(--body)}
.jf-alert svg{flex:none; margin-top:1px}
.jf-alert.attention{background:var(--warning-soft); border-color:#EBD9AE; color:var(--warning)}
.jf-alert.error{background:var(--bad-50); border-color:var(--bad-200); color:var(--bad-700)}

/* ═══ EMPTY AND LOADING ════════════════════════════════════════════════════ */
.jf-empty{padding:var(--s12) var(--s6); text-align:center; background:var(--surface);
  border:1px solid var(--line); border-radius:var(--r-lg)}
.jf-empty h3{font-size:15px; font-weight:600; margin-bottom:4px}
.jf-empty p{margin:0; font-size:14px; color:var(--ink-2)}
.jf-skel{background:linear-gradient(90deg,var(--gray-100) 25%,var(--gray-200) 37%,var(--gray-100) 63%);
  background-size:400% 100%; animation:jf-shimmer 1.4s ease-in-out infinite;
  border-radius:var(--r-xs); height:12px}
@keyframes jf-shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}
.jf-live{display:inline-flex; align-items:center; gap:6px; height:28px; padding:0 10px;
  border-radius:var(--r-full); background:var(--gray-100); color:var(--ink-2);
  font-size:12px; font-weight:500; white-space:nowrap}
.jf-live i{width:6px; height:6px; border-radius:var(--r-full); background:var(--brand-500);
  display:inline-block}
.jf-live.checking i{background:var(--warn-600); animation:jf-blip 1s ease-in-out infinite}
@keyframes jf-blip{0%,100%{opacity:1}50%{opacity:.25}}

/* ═══ NARROWER SCREENS ═════════════════════════════════════════════════════ */
@media(max-width:1280px){
  .jf-topbar{padding:var(--s3) var(--s6)}
  .jf-split{grid-template-columns:312px minmax(0,1fr)}
}
@media(max-width:960px){
  /* THUMBS, NOT CURSORS. Two things break a phone: a tap target smaller than a fingertip, and a
     text field under 16px — iOS silently zooms the whole page in when you tap one of those, and the
     layout never comes back. Every field is 16px here and every control is at least 44px tall. */
  input,select,textarea,.jf-field input,.jf-field select{font-size:16px}
  .jf-btn,.jf-field{min-height:44px}
  .jf-btn.sm{min-height:44px}
  input[type=checkbox],input[type=radio]{width:20px;height:20px}
  .rail-item{height:44px}
  .rail-me{height:48px}
  .rail-menu a{min-height:46px}
  .jf-list-row,.jf-pick{min-height:56px}

  /* ── MOBILE: the work has to be on the first screen ───────────────────
     Measured before this: 216px of controls (they wrapped onto 3.4 rows) plus 472px of stacked
     stat boxes meant 969px — more than a full screen — of scrolling before the first deal appeared.
     The controls become ONE horizontally-scrolling row, and the stats become compact tiles two
     across. Same information, roughly a third of the height. */
  .jf-topbar{padding:10px var(--s4) 10px; gap:var(--s2); row-gap:var(--s2)}
  .jf-topbar h1{font-size:18px}
  .jf-lede{font-size:12px}
  .jf-topbar .right{
    flex:1 1 240px; min-width:0; margin-left:auto; gap:6px; flex-wrap:nowrap; overflow-x:auto;
    scrollbar-width:none; -webkit-overflow-scrolling:touch; padding-bottom:2px;
  }
  .jf-topbar .right::-webkit-scrollbar{display:none}
  .jf-topbar .right > *{flex:0 0 auto}
  /* The one thing you came to do stays pinned at the left edge, never scrolled away. */
  .jf-topbar .right .jf-btn.primary{order:-1}
  /* Freshness is desktop trivia; on a phone it is a row of pixels better spent on a deal. */
  .jf-live{display:none}
  .jf-hide-sm{display:none}

  /* Stat tiles: two across, compact, with the label doing the work type should do. */
  .jf-kpis{grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--s2)}
  .jf-kpi{padding:11px 12px 12px; border-radius:var(--r-md)}
  .jf-kpi .label{font-size:11px; font-weight:600; letter-spacing:.04em; text-transform:uppercase;
    color:var(--ink-3); line-height:1.3}
  .jf-kpi .value{font-size:23px; margin-top:2px; line-height:1.1}
  .jf-kpi .foot{font-size:11px; margin-top:2px; line-height:1.35; color:var(--ink-3);
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden}
  /* The one tile that is a job rather than a number carries the amber edge. */
  .jf-kpi.act{border-color:var(--warn-200); background:var(--warn-50)}
  .jf-kpi.act .label{color:var(--warn-700)}

  .jf-section{margin-top:var(--s6)}
  .jf-page{padding-top:var(--s4)}
  .jf-page h2:first-of-type{margin-top:0}
  .jf-page > #notices:empty{display:none}

  /* ── A DRAWER, NOT A SIDEWAYS STRIP ────────────────────────────────────
     The rail used to lie down as a horizontal scroller across the top, so reaching Incoming Leads
     meant swiping the nav sideways — nobody discovers that, and it is not how a phone app behaves.
     On a phone the rail becomes a proper drawer behind a menu button: every section listed in full,
     one tap, and it slides away again. */
  .jf-app{flex-direction:column}
  .jf-mobilebar{display:flex}
  .jf-scrim{display:block}

  .rail{
    position:fixed; top:0; left:0; width:274px; max-width:86vw; height:100dvh; z-index:70;
    flex-direction:column; overflow-y:auto; padding:0;
    transform:translateX(-100%); transition:transform .24s cubic-bezier(.2,.8,.2,1);
    box-shadow:none;
  }
  .rail.open{transform:translateX(0); box-shadow:0 0 0 100vmax rgba(0,0,0,0)}
  .rail-brand{padding:var(--s6) 18px var(--s4)}
  .rail-logo{width:150px}
  .rail-group{display:block}
  .rail-nav{flex-direction:column; padding:0 10px; gap:2px}
  .rail-item{height:48px; padding:0 12px; white-space:normal}
  .rail-item.on::before{display:block}
  .rail-foot{margin-top:auto; padding:12px; border-top:1px solid var(--rail-line)}
  .rail-me{width:100%; height:52px; padding:0 8px}
  .rail-me .who{display:block}
  .rail-menu{left:12px; right:12px; bottom:66px; top:auto; width:auto}

  .jf-split{grid-template-columns:minmax(0,1fr)}
  .jf-panel{position:static; max-height:none}
  /* One column on a phone: a box that scrolls inside another that scrolls is a trap for a thumb. */
  .jf-picklist{position:static; max-height:none; display:block}
  .jf-picklist > .jf-scroll{overflow:visible; max-height:none; padding:0; margin:0}
  .jf-page{padding:var(--s4) var(--s4) var(--s8)}
  .jf-topbar{padding:var(--s3) var(--s4)}
}
/* ASKED FOR LESS MOTION (the computer's own setting): nothing slides or rises, things only fade, and quickly (10/14) */
@media (prefers-reduced-motion:reduce){
  .jf-dlg .jf-dlg-box,.jf-toast.in{animation:jf-fade .12s linear}
  .jf-sh:not([hidden]),.dealov,.textsov{animation-duration:.12s; animation-name:jf-fade}
  .jf-dlg-out .jf-dlg-box{transform:none}
  .jf-toast.out{transform:translateX(-50%)}
  .jf-toast .jf-toast-p{display:none}
  .jf-busy-orb,.jf-busy-fill::before{animation-duration:.3s, 4s, 4s}
  .jf-busy-orb::after{animation-duration:4s}
  .jf-said > .jf-said-t,.jf-said-i,.jf-said-bad .jf-said-i,.jf-said-i path,.jf-said-back{animation:none}
  .jf-said-fill::before{display:none}
  .jf-said-i path{stroke-dashoffset:0}
  .jf-said,.jf-said-back,.jf-said > .jf-said-t{transition-duration:.12s !important}
  .jf-skel{animation:none}
  .rail-menu,.rail-menu.open{transform:none}
  .jf-app,.rail,.rail-item,.rail-back,.rail-me,.rail-quota,.rail-quota .rq-bar i,.rail-logo,.rail-toggle svg{transition-duration:0s !important}
}

