:root{
  --bg:#0d1117; --panel:#151b24; --panel2:#1b222d; --line:#252d3a;
  --ink:#e6edf6; --dim:#8a97ab; --faint:#5d6b80;
  --go:#2ea043; --go-hi:#3fb950; --stop:#d1364a; --stop-hi:#e5566a;
  --accent:#4a9eff; --warn:#d9a13b;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
}
*{box-sizing:border-box}
/* dvh, not vh. On a phone 100vh is the height the viewport would have with the browser
   chrome retracted, so a 100vh app is taller than the screen the moment the URL bar is
   showing — the footer and whatever sits above it are simply below the fold until you
   scroll, which is the one thing this layout must never require. dvh tracks the chrome as
   it moves. The vh line stays as the fallback for anything that has not caught up. */
html,body{height:100vh; height:100dvh}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font:15px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  display:flex; flex-direction:column; overscroll-behavior:none; overflow:hidden;
}

/* ---------- top bar ---------- */
.bar{
  display:flex; align-items:center; gap:14px; padding:10px 16px;
  background:var(--panel); border-bottom:1px solid var(--line); flex:none;
  padding-top:calc(10px + env(safe-area-inset-top));
}
.brand{font-weight:700; letter-spacing:.3px}
.spacer{flex:1}
.from{color:var(--dim); font-size:13px}
.from b{color:var(--ink); font-family:var(--mono)}
.counter{color:var(--dim); font-size:13px}
.pill{font-size:12px; padding:3px 10px; border-radius:999px; font-weight:600}
.pill-wait{background:#3a2f14; color:var(--warn)}
.pill-ok{background:#10301a; color:var(--go-hi)}
.pill-bad{background:#3a1620; color:var(--stop-hi)}

/* ---------- layout ---------- */
.grid{flex:1; display:grid; grid-template-columns:minmax(320px,1fr) minmax(380px,1.1fr); min-height:0; min-width:0}
/* min-width:0 here is load-bearing, not tidying. A grid item defaults to min-width:auto, so a
   1fr column refuses to shrink below its content's min-content width and pushes the page out
   sideways instead — measured at 452px inside a 412px phone viewport. */
.col{min-height:0; min-width:0; display:flex; flex-direction:column}
.list{border-right:1px solid var(--line)}
.detail{overflow-y:auto; padding:14px; gap:14px; display:flex; flex-direction:column}

/* ---------- lead list ---------- */
.list-head{display:flex; gap:8px; padding:12px; border-bottom:1px solid var(--line); flex:none}
.list-head input, .list-head select{
  background:var(--panel2); border:1px solid var(--line); color:var(--ink);
  border-radius:8px; padding:8px 10px; font-size:14px; outline:none;
}
.list-head input{flex:1; min-width:0}
.list-head input:focus{border-color:var(--accent)}
.list-meta{padding:8px 14px; color:var(--faint); font-size:12px; border-bottom:1px solid var(--line); flex:none}
.leads{list-style:none; margin:0; padding:0; overflow-y:auto; flex:1}
.leads li{
  padding:10px 14px; border-bottom:1px solid var(--line); cursor:pointer;
  display:flex; gap:10px; align-items:baseline;
}
.leads li:hover{background:var(--panel2)}
.leads li.on{background:#16283f; box-shadow:inset 3px 0 0 var(--accent)}
.leads li.done{opacity:.45}
.lead-name{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.lead-city{color:var(--faint); font-size:12px; white-space:nowrap}
.lead-tag{font-size:11px; color:var(--faint); font-family:var(--mono)}

/* ---------- cards ---------- */
.card{background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:14px}
.card.empty{display:flex; align-items:center; justify-content:center; min-height:120px}
.nothing{color:var(--faint); margin:0}
.card-title{font-size:12px; text-transform:uppercase; letter-spacing:.9px; color:var(--faint); margin-bottom:10px}
.lead-title{font-size:20px; font-weight:650; margin:0 0 4px}
.lead-phone{font-family:var(--mono); font-size:19px; color:var(--go-hi); margin:0 0 8px}
.lead-meta{color:var(--dim); font-size:13px; margin:2px 0}
.lead-meta a{color:var(--accent)}
.badge{display:inline-block; font-size:11px; padding:2px 8px; border-radius:6px;
  background:var(--panel2); color:var(--dim); margin-right:6px}

/* ---------- dial pad ---------- */
.dialpad{background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:16px; text-align:center}
.call-state{font-size:12px; text-transform:uppercase; letter-spacing:1.2px; color:var(--faint)}
.call-state.ringing{color:var(--warn)}
.call-state.live{color:var(--go-hi)}
.call-state.ending{color:var(--stop-hi)}
.timer{font-family:var(--mono); font-size:38px; font-weight:600; margin:2px 0 12px; letter-spacing:1px}
.buttons{display:grid; grid-template-columns:repeat(4,1fr); gap:8px}
.btn{
  border:1px solid var(--line); background:var(--panel2); color:var(--ink);
  padding:12px 8px; border-radius:10px; font-size:14px; font-weight:600; cursor:pointer;
}
.btn:hover:not(:disabled){border-color:var(--faint)}
.btn:disabled{opacity:.35; cursor:not-allowed}
.btn-go{background:var(--go); border-color:var(--go); color:#fff}
.btn-go:hover:not(:disabled){background:var(--go-hi)}
.btn-stop{background:var(--stop); border-color:var(--stop); color:#fff}
.btn-stop:hover:not(:disabled){background:var(--stop-hi)}
.btn-save{width:100%; margin-top:10px; background:var(--accent); border-color:var(--accent); color:#04101f}
.manual{margin-top:10px}
.manual input{
  width:100%; background:var(--panel2); border:1px solid var(--line); color:var(--ink);
  border-radius:8px; padding:9px 10px; font-family:var(--mono); text-align:center; outline:none;
}
.manual input:focus{border-color:var(--accent)}

/* ---------- the recording switch ----------
   A real <input type=checkbox> inside its own <label>, not a styled div, so that the whole
   row is a tap target, the browser gives it keyboard focus for free, and a screen reader
   reads it as the checkbox it is. accent-color is enough to theme the box itself — a custom
   one would have to reimplement focus rings and the indeterminate state for no gain.

   The row lives in the dialpad rather than the footer because the footer is display:none on
   a landscape phone (see the max-height:560px block), and a control the operator cannot see
   on the layout they dial from is worse than no control. */
.reccheck{
  display:flex; align-items:center; gap:8px; margin-top:10px;
  padding:8px 10px; border:1px solid var(--line); border-radius:8px;
  background:var(--panel2); cursor:pointer; user-select:none;
}
.reccheck input{
  flex:none; width:17px; height:17px; margin:0; cursor:pointer;
  accent-color:var(--stop-hi);
}
.reccheck-label{font-size:13px; color:var(--dim)}

/* Armed but not yet recording anything: the box is ticked and the line goes red, which is
   the honest state between calls. It says what the NEXT call will do. */
.reccheck.on{border-color:var(--stop); background:#2a151b}
.reccheck.on .reccheck-label{color:var(--stop-hi)}

/* Actually recording, right now, on a live call. The one place a blinking dot earns itself —
   this is transient and it is the difference between "will record" and "is recording", which
   is exactly the distinction the old footer could not make. */
.reccheck-live{
  margin-left:auto; font-family:var(--mono); font-size:11px; letter-spacing:.06em;
  text-transform:uppercase; color:var(--stop-hi);
}
.reccheck-live::before{
  content:''; display:inline-block; width:7px; height:7px; margin-right:5px;
  border-radius:50%; background:var(--stop-hi); vertical-align:baseline;
  animation:recpulse 1.6s ease-in-out infinite;
}
@keyframes recpulse{50%{opacity:.25}}

/* Someone dialling all day with the box ticked does not need a dot pulsing in the corner of
   their eye for eight hours, and for anyone who gets motion-sick it is worse than that. The
   dot stays; only the movement goes. */
@media (prefers-reduced-motion:reduce){
  .reccheck-live::before{animation:none}
}

/* ---------- dispositions ---------- */
.dispo-grid{display:grid; grid-template-columns:repeat(2,1fr); gap:8px}
.dispo-grid button{
  border:1px solid var(--line); background:var(--panel2); color:var(--ink);
  border-radius:9px; padding:10px 8px; font-size:13px; cursor:pointer; text-align:left;
}
.dispo-grid button .k{color:var(--faint); font-family:var(--mono); margin-right:7px}
/* Both wordings are in the DOM; the phone query swaps which one is shown. */
.dispo-short{display:none}
.dispo-grid button.on{border-color:var(--accent); background:#16283f}
#notes{
  width:100%; margin-top:10px; min-height:64px; resize:vertical;
  background:var(--panel2); border:1px solid var(--line); color:var(--ink);
  border-radius:9px; padding:9px 10px; font:inherit; outline:none;
}
#notes:focus{border-color:var(--accent)}

/* ---------- session log ---------- */
.log summary{cursor:pointer; margin-bottom:0}
.log[open] summary{margin-bottom:10px}
.log summary::marker{color:var(--faint)}
.session-log{list-style:none; margin:0; padding:0; font-size:13px; max-height:190px; overflow-y:auto}
.session-log li{padding:6px 0; border-bottom:1px solid var(--line); display:flex; gap:8px}
.session-log li:last-child{border-bottom:0}
.session-log .when{color:var(--faint); font-family:var(--mono); font-size:12px}
.session-log .who{flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.session-log .what{color:var(--dim)}
.muted{color:var(--faint)}
.linkish{background:none; border:0; color:var(--accent); font:inherit; font-size:11px;
  text-transform:none; letter-spacing:0; cursor:pointer; padding:0 0 0 8px}
.linkish:hover{text-decoration:underline}

/* ---------- footer + toast ---------- */
.foot{
  flex:none; display:flex; gap:16px; padding:8px 16px; font-size:12px; color:var(--faint);
  border-top:1px solid var(--line); background:var(--panel);
  padding-bottom:calc(8px + env(safe-area-inset-bottom));
}
.foot b{color:var(--warn)}

/* Recording on. Red rather than the footer's usual amber, and a dot that does not blink —
   a live indicator you stop seeing is not an indicator. This has to stay legible at the
   12px the footer runs at on a phone, which is why it is a colour change on the existing
   line instead of a badge competing for the width. */
.foot .rec-on b{color:var(--stop-hi)}
.foot .rec-on b::before{
  content:''; display:inline-block; width:7px; height:7px; margin-right:5px;
  border-radius:50%; background:var(--stop-hi); vertical-align:baseline;
}
.keys{margin-left:auto; font-family:var(--mono)}
.toast{
  position:fixed; left:50%; bottom:56px; transform:translateX(-50%);
  background:#222c3a; border:1px solid var(--line); color:var(--ink);
  padding:10px 16px; border-radius:10px; font-size:14px; z-index:50; max-width:80vw;
}
.toast.bad{border-color:var(--stop); background:#38161f}

/* ---------- narrow / phone ---------- */
/* One screen. No page scroll, and — the part that actually matters — no scroll inside the
   detail pane either.

   The old rule here only restacked the two columns and capped the list at 38vh. Measured on
   a 390x844 phone, that left the detail pane a 395px window onto 850px of content: the
   disposition grid began below its own fold and `Save & next` sat 680px past it. After every
   single call you had to scroll a nested pane to record the outcome, which is the one moment
   the operator has both hands busy and the least patience. An uncategorised call is a lost
   one, so the disposition surface is not something that may go below the fold.

   The fix is a fixed budget rather than a cap. Exactly ONE thing stretches — the lead list —
   and everything else is sized to what it needs. The sum of those fixed rows IS the chrome
   budget, so shrinking any of them buys list rows one for one, and nothing can push the
   controls off-screen because nothing below the list is allowed to grow. */
@media (max-width:860px){
  .grid{display:flex; flex-direction:column; grid-template-columns:none}

  /* flex-basis 0, and that zero is the whole trick. With `flex:1 1 auto` the list's basis is
     its own content height — sixteen thousand pixels of leads — so it claimed the entire
     screen and left the controls 49px to render 533px into. Basis 0 means the list asks for
     nothing and is paid only in leftovers, which is exactly the priority order wanted here:
     the controls are load-bearing, the number of visible rows is not. */
  .list{border-right:0; border-bottom:1px solid var(--line); flex:1 1 0; min-height:104px; max-height:none}

  /* Grow 0, shrink 1: it takes exactly the height its content needs and never more, but it
     is still the item that gives way if the total overflows — because the list's zero basis
     leaves it nothing to shrink. So when the session log is expanded on a short screen, this
     pane scrolls internally instead of shoving the dial buttons off the bottom. */
  .detail{flex:0 1 auto; min-height:0; overflow-y:auto; padding:8px; gap:8px}

  /* The header wrapped to 77px on a 390px screen. The caller ID is a fixed fact about the
     account, not something worth 33px of a phone; it stays on /login and in the footer. */
  .bar{gap:10px; padding:6px 12px; padding-top:calc(6px + env(safe-area-inset-top))}
  .brand{font-size:14px}
  .from{display:none}
  .counter{font-size:12px}
  .pill{font-size:11px; padding:2px 8px}

  .list-head{padding:8px}
  .list-meta{display:none}
  .leads li{padding:9px 12px}

  /* The selected lead collapses from a 120px card to a strip: who, and the number about to
     be dialled. Address and website are a desk-research affordance — they are one tap away
     on the desktop layout and they are not what you look at while the phone is ringing. */
  .card{padding:10px}
  .card.empty{min-height:0; padding:10px}
  #lead-card{display:flex; flex-wrap:wrap; align-items:baseline; gap:2px 8px}
  .lead-title{font-size:15px; margin:0; flex:1 1 60%; min-width:0;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .lead-phone{font-size:15px; margin:0}
  .lead-meta{display:none}
  .badge{font-size:10px; padding:1px 6px; margin-right:4px}

  /* State and timer share one line instead of stacking a 38px readout above the buttons. */
  .dialpad{padding:10px}
  .call-state{display:inline}
  .timer{display:inline; font-size:20px; margin:0 0 0 8px; vertical-align:-1px}
  .buttons{margin-top:8px; gap:6px}
  .btn{padding:12px 4px; font-size:13px}
  .manual{margin-top:6px}
  .manual input{padding:7px 8px; font-size:13px}
  .reccheck{margin-top:6px; padding:6px 8px; gap:7px}
  .reccheck-label{font-size:12px}

  /* Six dispositions across three columns, on the short labels. Two columns of the full
     wording wrapped to three lines each and cost 130px for no added meaning — the number
     key is printed on the button and the keyboard shortcut is a desktop affordance anyway. */
  .dispo .card-title{display:none}
  .dispo-grid{grid-template-columns:repeat(3,1fr); gap:6px}
  .dispo-grid button{padding:8px 4px; font-size:12px; text-align:center; line-height:1.25}
  .dispo-grid button .k{display:block; margin:0 0 1px}
  .dispo-full{display:none}
  .dispo-short{display:inline}
  #notes{min-height:38px; margin-top:6px; padding:7px 9px}
  .btn-save{margin-top:6px; padding:11px}

  .foot{padding:6px 12px; font-size:11px}
  .keys{display:none}
  .toast{bottom:auto; top:64px}
}

/* A short phone — an SE, or anything in a split view. The fixed stack still has to fit, so
   the secondary surfaces go rather than letting a primary one scroll. Nothing removed here
   is part of placing a call and recording its outcome. */
@media (max-width:860px) and (max-height:720px){
  .log{display:none}
  .manual{display:none}
  /* The manual-dial box goes on a short phone; the recording switch does NOT. Whether the
     call is being recorded is not a secondary surface, and on this layout the footer that
     used to say so is already down to 10px. It shrinks instead. */
  .reccheck{margin-top:5px; padding:5px 8px}
  .detail{padding:6px; gap:5px}
  .card{padding:8px}
  .dialpad{padding:8px}
  .btn{padding:10px 4px}
  .dispo-grid{gap:4px}
  .dispo-grid button{padding:6px 3px; font-size:11px}
  #notes{min-height:28px; margin-top:5px}
  .btn-save{margin-top:5px; padding:8px}
  .foot{padding:5px 10px; font-size:10px; line-height:1.35}
}

/* Short and wide — a phone in landscape, ~390px of height and 844 of width. Stacking is the
   wrong answer for that shape: there is no vertical room and a screenful of unused
   horizontal room, so it goes back to two columns and spends the width instead. */
@media (max-height:560px) and (min-width:600px){
  .grid{display:grid; grid-template-columns:minmax(200px,.9fr) minmax(320px,1.1fr)}
  .list{border-right:1px solid var(--line); border-bottom:0; min-height:0}
  .detail{overflow-y:auto; padding:6px; gap:5px}
  .bar{padding:4px 12px}
  /* .foot goes here, which is why .reccheck must not: on a landscape phone the checkbox is
     the ONLY thing on screen that says whether the call is recorded. */
  .from,.log,.manual,.foot,.list-meta{display:none}
  .reccheck{margin-top:5px; padding:4px 8px}
  .reccheck-label{font-size:12px}
  .card{padding:8px}
  .dialpad{padding:8px}
  #lead-card{display:flex; flex-wrap:wrap; align-items:baseline; gap:2px 8px}
  .lead-title{font-size:14px; margin:0; flex:1 1 60%; min-width:0;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  .lead-phone{font-size:14px; margin:0}
  .lead-meta{display:none}
  .badge{font-size:10px; padding:1px 6px}
  .call-state{display:inline}
  .timer{display:inline; font-size:17px; margin:0 0 0 8px}
  .buttons{margin-top:6px; gap:5px}
  .btn{padding:8px 4px; font-size:12px}
  .dispo .card-title{display:none}
  .dispo-grid{grid-template-columns:repeat(3,1fr); gap:4px}
  .dispo-grid button{padding:5px 3px; font-size:11px; text-align:center; line-height:1.2}
  .dispo-grid button .k{display:block; margin:0}
  .dispo-full{display:none}
  .dispo-short{display:inline}
  #notes{min-height:26px; margin-top:4px}
  .btn-save{margin-top:4px; padding:6px}
}

/* A laptop is not automatically a tall screen. At 1280x720 the desktop column stacked to
   734px of cards inside a 642px pane, which put `Save & next` below the fold — the same
   defect as the phone, just reached from the other direction. Same remedy: trim until the
   column fits, starting with the parts that are ornament rather than function. */
/* A laptop is not a tall screen. min-height:561px is load-bearing, not decoration: without
   it this query also matches a landscape phone wider than 861px — a Galaxy S22 Ultra on its
   side is 882x412 — and because it sits after the landscape block it wins, handing a 412px
   screen the laptop layout and pushing Save 28px below the fold. The 844px iPhone missed
   that trap by 17 pixels of width, which is why it went unnoticed. */
@media (min-width:861px) and (min-height:561px) and (max-height:820px){
  .detail{padding:10px; gap:7px}
  .card{padding:10px}
  .card-title{margin-bottom:4px}
  .lead-title{font-size:17px}
  .lead-phone{font-size:17px; margin-bottom:4px}
  .dialpad{padding:10px}
  .call-state{display:inline}
  .timer{display:inline; font-size:24px; margin:0 0 0 10px; vertical-align:-2px}
  .buttons{margin-top:8px}
  .btn{padding:9px 8px}
  .dispo-grid{gap:5px}
  .dispo-grid button{padding:7px}
  #notes{min-height:28px}
  .btn-save{margin-top:7px; padding:9px}
  .session-log{max-height:120px}
}

/* ---------- email channel ---------- */
/* Appended, not woven in: every rule below is new and none of the rules above it were
   touched, so a diff of this file is exactly the email panel and nothing else. */

.email summary{cursor:pointer; margin-bottom:0; display:flex; align-items:center; gap:8px}
.email[open] summary{margin-bottom:10px}
.email summary::marker{color:var(--faint)}

/* The mode pill. Sandbox is the shipped state and reads as neutral; live is red because it
   is the one state in which a licensee who never asked to hear from anyone receives mail.
   The colour is doing the same job as the wording in the tooltip, for the operator who is
   glancing rather than reading. */
.pill.mode-sandbox{background:#152736; color:var(--accent)}
.pill.mode-dry{background:#20262f; color:var(--dim)}
.pill.mode-live{background:#3a1620; color:var(--stop-hi)}
.pill.mode-unknown{background:#3a2f14; color:var(--warn)}

.email-row{display:flex; align-items:center; gap:10px; margin-bottom:8px}
.email-lab{
  flex:none; width:64px; font-size:11px; text-transform:uppercase; letter-spacing:.8px;
  color:var(--faint);
}
.email-row input,
.email-row select{
  flex:1; min-width:0; background:var(--panel2); color:var(--ink);
  border:1px solid var(--line); border-radius:8px; padding:8px 10px;
  font:inherit; font-size:14px;
}
.email-row input:focus,
.email-row select:focus{outline:none; border-color:var(--accent)}
.email-row select option:disabled{color:var(--faint)}

.email-meta{font-size:12px; margin:0 0 8px; color:var(--faint)}

/* White, because it is a preview of an email and an email is read on a white ground. The
   panel around it stays dark; the frame is a window into another document, and dressing it
   in the app palette would show the operator something the recipient will never see. */
.email-preview{
  width:100%; height:260px; border:1px solid var(--line); border-radius:8px;
  background:#fff; display:block;
}

.email-actions{display:flex; align-items:center; gap:12px; margin-top:10px}
.email-actions .btn-save{flex:1; margin-top:0}
.email-actions .linkish{flex:none}

.email-status{
  font-size:12px; margin:8px 0 0; color:var(--dim); word-break:break-word;
  font-family:var(--mono);
}
.email-status.bad{color:var(--stop-hi)}

/* Tags off the licensee record - scale, on hold, pending, inactive. Dimmer than .badge so
   the licence class stays the thing the eye lands on first. */
.badge-tag{background:var(--panel2); color:var(--dim); margin-right:4px}

/* The GIS-vintage phone warning. Amber rather than red: the number is probably fine, it is
   just likelier than the rest to be wrong, and colouring a maybe as an error trains the
   operator to ignore the colour. */
.lead-meta.warn{color:var(--warn)}

/* On a phone the panel ships closed, so these only apply once it is opened deliberately.
   The preview loses height rather than the To field losing its label - an operator who
   cannot see which address is loaded is the failure that actually costs something. */
@media (max-width:860px){
  .email-preview{height:180px}
  .email-lab{width:52px; font-size:10px}
  .email-row{margin-bottom:6px}
}
@media (max-height:560px) and (min-width:600px){
  .email-preview{height:150px}
}
@media (min-width:861px) and (min-height:561px) and (max-height:820px){
  .email-preview{height:190px}
  .email-row{margin-bottom:6px}
}
