:root{
  --bg:#f7f2e9; --card:#fffdf9; --ink:#33271f; --muted:#74675f;
  --gold:#b8862f; --gold2:#d9b56d; --line:#e6d8c4; --danger:#a33b32; --ok:#2e6b4b;
}
*{box-sizing:border-box}
body{margin:0;font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;background:var(--bg);color:var(--ink);font-size:14px}
header{position:sticky;top:0;z-index:20;background:rgba(247,242,233,.96);backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
.wrap{max-width:1180px;margin:auto;padding:18px}
.brand{display:flex;align-items:center;gap:14px}
.mark{width:46px;height:46px;border:1.5px solid var(--gold);border-radius:50%;display:grid;place-items:center;font-family:Georgia,serif;font-size:20px;color:var(--gold);background:#fffaf0}
h1{font-size:21px;margin:0;font-family:Georgia,serif;letter-spacing:.04em}
.brand small{color:var(--muted)}
nav{display:flex;gap:8px;margin-top:14px;flex-wrap:wrap}
nav button,.seg button,.btn{border:1px solid var(--line);background:#fffaf3;color:var(--ink);padding:8px 16px;border-radius:8px;cursor:pointer;font-weight:600;font-size:14px}
nav button.active,.seg button.active,.btn.primary{background:var(--gold);color:white;border-color:var(--gold)}
.btn.dark{background:var(--ink);color:white;border-color:var(--ink)}
.btn.ghost{background:transparent}
.btn.danger{background:#fff6f5;color:var(--danger);border-color:#e8bbb7}
main{max-width:1180px;margin:auto;padding:20px 18px 48px}
.tab{display:none}.tab.active{display:block}
.grid{display:grid;grid-template-columns:1.05fr .95fr;gap:18px}
@media(max-width:900px){.grid{grid-template-columns:1fr}.sticky{position:static!important}}
.card{background:var(--card);border:1px solid var(--line);border-radius:18px;padding:18px;box-shadow:0 8px 26px rgba(92,64,25,.05);margin-bottom:16px}
.card h2{font-family:Georgia,serif;font-size:18px;margin:0 0 14px;color:#5b3e1f}
.card h3{font-size:15px;margin:18px 0 8px}
.fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
@media(max-width:640px){.fields{grid-template-columns:1fr}}
.filterGrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}
.pageBar{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-top:14px}
.pageBar .actions{align-items:center}
.pageBar select{width:auto}
.pageBar .btn.ghost{min-width:44px;min-height:44px;padding:6px 12px;font-size:18px;line-height:1;text-align:center}
label{display:block;font-size:12px;color:var(--muted);font-weight:700;margin-bottom:6px}
input,select,textarea{width:100%;padding:8px 12px;border:1px solid #d9cab7;border-radius:8px;background:white;color:var(--ink);font:inherit;font-size:14px}
/* <select> and type=date render ~2px taller than a plain text input even with identical
   padding/border — each browser adds its own native chrome (dropdown arrow, calendar icon)
   that padding alone doesn't account for. An explicit height (box-sizing:border-box is set
   globally above) is the only reliable way to line every field up exactly; min-height on
   textarea (below) still overrides it since min-height wins when larger. td input/td select
   keep their own smaller sizing (declared later, higher specificity) for the compact table. */
input,select{height:37px}
/* The global input rule above (width:100%, height:37px, padding:8px 12px) is sized for text
   fields and swallows a checkbox into a huge full-width box with no visible tick — reset it
   back to its native size. */
input[type="checkbox"]{width:16px;height:16px;padding:0;flex:none;margin:0}
/* A text-input-plus-checkbox filter row: label+input stack on the left (so the row's natural
   height is the label line + the 37px input), the checkbox sits to the right. flex-end lines
   the checkbox up with the INPUT's baseline, not the "Username" label above it, which is what
   a top-aligned grid would do since the checkbox has no label-row counterpart to match. */
.syslog-filters{display:flex;align-items:flex-end;gap:20px;flex-wrap:wrap}
.syslog-filter-username{width:100%;max-width:280px}
/* 44x44 minimum touch target (this app runs on mobile at exhibitions) even though the visible
   box is 16px — padding extends the clickable area without growing what's drawn on screen. */
.checkbox-field{display:flex;align-items:center;gap:8px;min-height:44px;padding:10px 4px;margin:0;font-weight:400;font-size:14px;color:var(--ink);text-transform:none;cursor:pointer}
input:disabled,select:disabled,textarea:disabled{background:var(--line);color:var(--muted);cursor:not-allowed;border-style:dashed}
textarea{min-height:92px;resize:vertical}
.seg{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.seg.two{grid-template-columns:repeat(2,1fr)}
.badge{display:inline-flex;padding:5px 9px;border-radius:999px;background:#f4ead9;color:#6a4a22;font-size:12px;font-weight:700}
.summary{position:relative;overflow:hidden}
.summary:before{content:"";position:absolute;right:-90px;top:-90px;width:230px;height:230px;border:1px solid #ead8b7;border-radius:50%}
.price{font-size:34px;font-family:Georgia,serif;font-weight:700;color:#7c531f;margin:4px 0 2px}
.strike{color:var(--muted);text-decoration:line-through}
.discount{color:var(--ok);font-weight:750}
.kv{display:grid;grid-template-columns:145px 1fr;gap:7px 12px;font-size:14px}
.kv div:nth-child(odd){color:var(--muted)}
.hr{height:1px;background:var(--line);margin:16px 0}
.mini{font-size:12px;color:var(--muted)}
.note{background:#fff8e8;border:1px solid #ead6a6;padding:11px;border-radius:10px;font-size:13px}
.lock-hint{display:block;font-size:11px;color:#a15c00;margin-top:4px}
.details{display:grid;grid-template-columns:1fr 1fr;gap:12px}
@media(max-width:640px){.details{grid-template-columns:1fr}}
.detailbox{border:1px solid var(--line);border-radius:12px;padding:12px;background:#fff}
.detailbox h4{margin:0 0 8px;color:#7a5425}
.detailbox ul{margin:0;padding-left:18px;font-size:13px;line-height:1.55}
.actions{display:flex;gap:8px;flex-wrap:wrap}
table{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}
th,td{border-bottom:1px solid var(--line);padding:8px 10px;text-align:left;vertical-align:middle}
thead{position:sticky;top:0}
th{color:#755327;background:#fbf5eb;position:sticky;top:0}
/* Sticky needs to be set on <thead> itself, not just its <th> cells — Chromium/WebKit only
   reliably stick the whole header row when the row-group element also carries position:sticky. */
/* border-collapse:collapse silently breaks position:sticky on <th> in Chromium/WebKit — the
   sticky th just scrolls away instead of pinning. border-spacing:0 keeps the same visual
   result as collapse did (only border-bottom is set anywhere, so there's nothing to double). */
td input,td select{padding:5px 8px;font-size:13px;border-radius:6px;width:auto;min-width:100px}
td .btn{padding:5px 10px;font-size:12px;border-radius:6px}
.scroll{overflow:auto;max-height:520px}
/* Leads table: pagination already caps rows per page (25/50/100), so the extra inner
   scrollbox that made sense for a once-unbounded list just hides most of the current page
   behind a tiny 520px window. Keep horizontal scroll for narrow viewports, drop the vertical
   cap so the page scrolls naturally instead. */
/* overflow-x:auto + overflow-y:visible isn't a real combination the CSS spec allows — when one
   axis is non-visible, the other's computed value is forced from visible back to auto (Overflow
   spec §"computed value" rule), so a partial override here silently stayed a scroll container.
   Drop both axes so this wrapper isn't a scroll container at all: the table flows with the page
   and the document becomes the nearest scrolling ancestor, which is what its th sticky (below)
   needs to reference the page header instead of a local box that never actually scrolls. */
.scroll-noVCap{max-height:none;overflow:visible}
/* The Leads table now shares the page's own scroll (no local scrollbox), same context as the
   sticky page header — so its th top:0 would stick right underneath the opaque header instead
   of below it. Offset by the header's real height (measured in leads.js, since it varies with
   nav wrapping on narrow widths) so both stay visible while scrolling: navbar on top, column
   header row pinned just under it. */
#tab-leads .scroll-noVCap thead,
#tab-leads .scroll-noVCap thead th{top:var(--header-h,131px);z-index:15}
.right{text-align:right}
.status{font-size:11px;font-weight:800;padding:4px 7px;border-radius:999px;background:#eee}
.status.hot{background:#fde6dc;color:#a34224}.status.warm{background:#fff1bf;color:#8a641b}.status.cold{background:#e9eef1;color:#53626c}
.status.cancelled{background:#fbdada;color:#8a1f1f}
tr.lead-cancelled,.leadCard.lead-cancelled{opacity:.55}
tr.lead-cancelled td:nth-child(3) b,.leadCard.lead-cancelled .leadCard-name{text-decoration:line-through}
.statgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
@media(max-width:720px){.statgrid{grid-template-columns:repeat(2,1fr)}}
.stat{background:#fff;border:1px solid var(--line);border-radius:14px;padding:14px}
.stat b{font-size:23px;font-family:Georgia,serif;color:#7b5629;display:block}

.dashgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
@media(max-width:960px){.dashgrid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.dashgrid{grid-template-columns:1fr}}
.dashcard{background:#fff;border:1px solid var(--line);border-radius:14px;padding:14px}
.dashcard h4{margin:0 0 10px;font-size:12px;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.03em}
.dashcard .dashempty{font-size:12px;color:var(--muted);padding:10px 0}
.dashdonut-wrap{display:flex;align-items:center;gap:14px}
.dashlegend{display:flex;flex-direction:column;gap:6px;font-size:12px;flex:1;min-width:0}
.dashlegend-row{display:flex;align-items:center;gap:6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dashlegend-dot{width:9px;height:9px;border-radius:50%;flex:none}
.dashlegend-label{color:var(--ink);font-weight:600;overflow:hidden;text-overflow:ellipsis}
.dashlegend-value{margin-left:auto;color:var(--muted);flex:none}
.dashbars{display:flex;flex-direction:column;gap:8px}
.dashbar-row{display:grid;grid-template-columns:72px 1fr auto;align-items:center;gap:8px;font-size:12px}
.dashbar-label{color:var(--ink);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dashbar-track{background:#f1e9db;border-radius:999px;height:8px;overflow:hidden}
.dashbar-fill{height:100%;border-radius:999px}
.dashbar-value{color:var(--muted);font-weight:700;white-space:nowrap}
.dashstat{padding:10px 0 4px;text-align:center}
.dashstat-value{font-size:36px;font-family:Georgia,serif;font-weight:700;color:#7b5629}
.dashstat-delta{font-size:12px;font-weight:700;margin-top:6px}
.hidden{display:none!important}
.footer-note{text-align:center;color:var(--muted);font-size:12px;margin-top:22px}
.modal-overlay{position:fixed;inset:0;background:rgba(51,39,31,.45);display:grid;place-items:center;z-index:100}
.modal-box{background:var(--card);border:1px solid var(--line);border-radius:18px;padding:28px 26px;box-shadow:0 20px 50px rgba(92,64,25,.18);max-width:360px;width:90%;text-align:center}
.modal-box h3{font-family:Georgia,serif;font-size:18px;margin:0 0 10px;color:#5b3e1f}
.modal-box .modal-quotation{font-size:22px;font-weight:700;color:#7c531f;font-family:Georgia,serif;margin:10px 0 18px}
.modal-box>.btn{width:100%}
.modal-box .actions .btn{flex:1}
.modal-box .mini{margin-top:4px}
.modal-icon{width:46px;height:46px;border-radius:50%;display:grid;place-items:center;margin:0 auto 12px;font-family:Georgia,serif;font-size:22px;font-weight:700;line-height:1}
.modal-box.tone-warning .modal-icon{background:#f6ead2;color:var(--gold)}
.modal-box.tone-warning h3{color:#8a5a12}
.modal-box.tone-error .modal-icon{background:#f7e2df;color:var(--danger)}
.modal-box.tone-error h3{color:var(--danger)}
.modal-box.tone-success .modal-icon{background:#dcefe4;color:var(--ok)}
.modal-box.tone-success h3{color:var(--ok)}
#appAlertMessage{white-space:pre-line}

/* ==========================================================================
   MOBILE & PWA  (v1.3)
   Everything below is additive and gated behind max-width media queries or
   opt-in classes, so the desktop layout above is untouched. Breakpoint is
   768px: wide enough to keep tablets on the desktop table, narrow enough to
   catch every phone in portrait.
   ========================================================================== */

/* Fields stay at the same 14px as desktop on mobile too. Normally iOS Safari force-
   zooms any input under 16px on focus and never zooms back out — that's why the
   viewport meta on app.html now carries user-scalable=no, maximum-scale=1: it disables
   the auto-zoom (and pinch-zoom generally) so 14px is safe here. login.html's viewport
   wasn't changed, so its two fields still need to stay >=16px or they'd zoom on focus
   there. Padding stays trimmed from the earlier 16px pass — smaller text no longer
   needs as much air around it. */
@media(max-width:768px){
  input,select,textarea,td input,td select{font-size:14px}
  input,textarea,td input{padding:5px 12px}
  /* iOS Safari renders type=date with native picker chrome that ignores width/padding/
     text-align (worst on the disabled #dataDate field, where the dashed disabled-state
     border made it more pronounced) — appearance:none forces it back onto the same box
     model as every other field. Scoped to mobile only: desktop Chrome/Firefox/Safari
     already render type=date consistently with sibling fields, and appearance:none here
     was making it 5px taller than everything else once tried at 1440px. */
  input[type="date"]{-webkit-appearance:none;appearance:none;text-align:left;line-height:1.4}
  input[type="date"]:disabled{border-style:solid}
  /* WebKit collapses an *empty* type=date almost to nothing once appearance:none is set —
     its internal placeholder segments (the "mm/dd/yyyy" WebKit would otherwise draw) don't
     establish a line box the way real text does, so the field shrinks to just its border
     until a date is actually picked. min-height floors it at the same size as every other
     field (Tanggal Dari, right next to it, only looked fine here because it already had a
     value). Harmless no-op on engines that don't have this quirk. */
  input[type="date"]{min-height:34px}
}

/* Notch / home-indicator clearance. Harmless (env() resolves to 0px) in a
   browser tab; only pays off once the app is installed and runs full-bleed. */
@media(max-width:768px){
  header .wrap{padding-top:calc(10px + env(safe-area-inset-top))}
  .wrap,main{padding-left:calc(14px + env(safe-area-inset-left));
             padding-right:calc(14px + env(safe-area-inset-right))}
}

/* --- Compact header ------------------------------------------------------
   The desktop header stacks a 46px mark, a 21px serif title, a subtitle and a
   6-button nav that wrapped to three rows on a 390px phone — roughly 190px of
   a 667px viewport gone before any content. Shrink the brand row and turn the
   nav into one horizontally-scrollable strip of pills. */
@media(max-width:768px){
  .wrap{padding-top:10px;padding-bottom:10px}
  .brand{gap:10px}
  .mark{width:34px;height:34px;font-size:15px;flex:none}
  /* .brand's two children are flex rows with an inline style (no shrink by default:
     flex items floor at their content's natural width, so text-overflow never kicked
     in and the two groups summed wider than the viewport). Force both to actually
     shrink, then the ellipsis rules below can do their job. */
  .brand>div{min-width:0}
  .brand>div:first-child{flex:1 1 auto}
  .brand>div:first-child>div{min-width:0}
  .brand>div:last-child{flex:none}
  h1{font-size:15px;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
  .brand small{font-size:10.5px;line-height:1.3;display:block;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
  #currentUserInfo{max-width:80px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

  nav{flex-wrap:nowrap;overflow-x:auto;gap:6px;margin-top:10px;
      scrollbar-width:none;-webkit-overflow-scrolling:touch;
      scroll-snap-type:x proximity;
      /* bleed the strip to the screen edges so pills scroll out of view
         cleanly instead of stopping short inside the wrap's padding */
      margin-left:-14px;margin-right:-14px;padding:2px 14px}
  nav::-webkit-scrollbar{display:none}
  nav button{flex:none;white-space:nowrap;scroll-snap-align:start;border-radius:999px}
}

/* --- 44px minimum tap targets -------------------------------------------
   Apple HIG and Material both put the floor at 44px; nav/.btn computed to
   about 33px, which is a coin-flip tap on a phone. nav is excluded from the
   44px floor below (it gets its own smaller sizing right after) — it's a
   frequent, low-stakes switch between six items, not a one-shot commit like
   Save/PDF, so a slightly smaller, denser strip reads better than six
   full-height pills eating the top of the screen. */
@media(max-width:768px){
  .btn,.seg button{
    min-height:44px;display:inline-flex;align-items:center;justify-content:center}
  .seg button{padding:8px 6px;font-size:13px;line-height:1.2;text-align:center}
  nav button{min-height:36px;padding:6px 14px;font-size:12.5px;
    display:inline-flex;align-items:center;justify-content:center}
  label{font-size:12.5px}
  main{padding-top:14px;padding-bottom:32px}
  .card{padding:14px;border-radius:14px}
  .kv{grid-template-columns:minmax(0,auto) minmax(0,1fr)}
  .kv div:nth-child(even){text-align:right}
  .pageBar{gap:8px}
  .pageBar .actions{width:100%;justify-content:center}
}

/* --- Leads: table on desktop, card list on phones ------------------------
   The Leads table carries 10 columns plus inline-editable cells; on a 390px
   screen that is pure horizontal scrolling. Below 768px the table is replaced
   by #leadCards (rendered in parallel by leads.js) — same data, same actions,
   stacked vertically. The table markup stays in the DOM and untouched, so the
   desktop view and its sticky header are entirely unaffected. */
.leadCards{display:none}
@media(max-width:768px){
  #tab-leads .scroll-noVCap{display:none}
  .leadCards{display:flex;flex-direction:column;gap:10px;margin-top:12px}
}
.leadCard{background:#fff;border:1px solid var(--line);border-radius:14px;padding:13px 14px;
          box-shadow:0 2px 8px rgba(92,64,25,.04)}
.leadCard-top{display:flex;justify-content:space-between;align-items:flex-start;gap:10px}
.leadCard-name{font-family:Georgia,serif;font-size:16px;font-weight:700;color:#5b3e1f;
               line-height:1.3;margin:0;text-align:left;word-break:break-word}
button.leadCard-name{background:none;border:0;padding:0;cursor:pointer;
               text-decoration:underline;text-decoration-color:var(--gold2);text-underline-offset:3px}
.leadCard-quo{font-size:12px;color:var(--muted);font-weight:600;margin-top:3px}
.leadCard-price{font-family:Georgia,serif;font-size:21px;font-weight:700;color:#7c531f;margin:11px 0 0}
.leadCard-disc{font-size:12px;color:var(--ok);font-weight:700}
.leadCard-kv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:5px 12px;font-size:13px;margin-top:10px}
.leadCard-kv div:nth-child(odd){color:var(--muted)}
.leadCard-kv div:nth-child(even){text-align:right;word-break:break-word}
.leadCard .actions{margin-top:12px;padding-top:11px;border-top:1px solid var(--line);flex-wrap:nowrap}
.leadCard .actions .btn{flex:1;min-height:44px;padding:8px 10px}
.leadCard-empty{text-align:center;color:var(--muted);font-size:13px;padding:26px 10px}

/* --- Sticky quotation bar (New Lead tab) ---------------------------------
   .sticky is switched off below 900px, which pushed the quotation total below
   the entire ~30-field form — the one number a salesperson is there to show a
   client sat behind a long scroll. Pin the total to the bottom edge instead,
   with a button that jumps to the full summary card. */
.mobilePriceBar{display:none;position:fixed;left:0;right:0;bottom:0;z-index:60;
  align-items:center;gap:12px;
  padding:10px 16px calc(10px + env(safe-area-inset-bottom));
  background:rgba(255,253,249,.97);backdrop-filter:blur(10px);
  border-top:1px solid var(--line);box-shadow:0 -6px 22px rgba(92,64,25,.10)}
.mobilePriceBar-info{flex:1;min-width:0}
.mobilePriceBar-label{font-size:10.5px;color:var(--muted);font-weight:800;
  text-transform:uppercase;letter-spacing:.04em}
.mobilePriceBar-value{font-family:Georgia,serif;font-size:20px;font-weight:700;color:#7c531f;
  line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mobilePriceBar .btn{flex:none;min-height:44px}
@media(max-width:768px){
  .mobilePriceBar.show{display:flex}
  /* keep the last form field reachable above the fixed bar */
  body.hasPriceBar main{padding-bottom:calc(84px + env(safe-area-inset-bottom))}
}

/* --- Collapsible form sections (New Lead tab) ----------------------------
   Four stacked cards on a phone read as one ~30-field scroll. mobile.js tags
   them .collapsible and collapses all but the first; the h2 becomes the
   toggle. Desktop never sees any of this. */
@media(max-width:768px){
  .form-side .card.collapsible>h2{
    display:flex;align-items:center;justify-content:space-between;gap:10px;
    margin:0;min-height:44px;cursor:pointer;user-select:none}
  .form-side .card.collapsible>h2:after{
    content:"";width:9px;height:9px;flex:none;margin-right:3px;
    border-right:2px solid var(--gold);border-bottom:2px solid var(--gold);
    transform:translateY(-3px) rotate(45deg);transition:transform .18s ease}
  .form-side .card.collapsed>h2:after{transform:translateY(2px) rotate(-135deg)}
  .form-side .card.collapsed>:not(h2){display:none}
  .form-side .card.collapsible:not(.collapsed)>h2{margin-bottom:14px}
}

@media print{
  header,.no-print,#tab-leads,#tab-admin{display:none!important}
  body{background:white}
  main{padding:0}
  #tab-new{display:block!important}
  .grid{display:block}
  .form-side{display:none}
  .summary{box-shadow:none;border:none}
  .card{box-shadow:none}
  .summary:before{display:none}
}
