/* CRM styles. No framework, no build step — read top to bottom. */

/* One theme: black, with everything above it made of translucent white.
   There is no light variant — the brand is gold on black, and a second palette
   would be a second thing to keep honest for no one's benefit. */
:root {
  color-scheme: dark;

  --bg: #000000;

  /* Surfaces are white at low opacity rather than solid greys, so anything
     behind them shows through and the layers read as glass rather than as
     flat panels that happen to be dark. */
  --surface: rgba(255, 255, 255, .055);
  --surface-2: rgba(255, 255, 255, .085);
  --surface-solid: #0d0d0f;   /* where transparency would let text overlap text */
  --border: rgba(255, 255, 255, .12);
  --border-strong: rgba(255, 255, 255, .2);

  /* Not pure white: at full brightness on black, text glares and thin strokes
     bloom. Pulling it back a little is easier to read for longer. */
  --text: #e9eaec;
  --muted: #9ba0a8;

  --accent: #d9b44a;          /* the gold from the logo */
  --accent-text: #14100a;
  --danger: #ff6b5e;
  --ok: #4ad07f;

  --shadow: 0 1px 2px rgba(0, 0, 0, .6), 0 12px 40px rgba(0, 0, 0, .55);
  --radius: 10px;

  --glass-blur: blur(18px) saturate(160%);

  /* Progress: passed stages in dark gold, the current one bright, the rest
     grey. The three must stay distinguishable at a glance. */
  --gold-done: #8a6d24;
  --gold-current: #e8c65a;
  --track: rgba(255, 255, 255, .13);

  --stage-uncontacted: #7d8390;
  --stage-initial_contact: #5b8def;
  --stage-materials_sent: #d9b44a;
  --stage-store_confirmed: #4ad07f;
}

* { box-sizing: border-box; }

/* No image may push its container wider than the viewport. */
img { max-width: 100%; }

/* The `hidden` attribute works via `display: none` in the browser's default
   stylesheet, which any author `display` rule outranks. Panels and modals below
   set `display: flex` and `display: grid`, so without this they could never be
   hidden. Keep this rule ahead of every layout rule that follows. */
[hidden] { display: none !important; }

/* A note on the glass, since it is applied per surface rather than by a shared
   class: backdrop-filter makes an element the containing block for any
   position: fixed descendant, so it can only go on surfaces that contain none.
   It is also deliberately kept off individual cards — a column can hold a
   hundred, and a hundred blurred layers stutters on a phone. Cards get the
   translucency and hairline border the look actually comes from; the blur sits
   on the surface behind them. */

body {
  margin: 0;
  font: 14px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color: var(--text);
  background: var(--bg);
}

h1, h2, h3 { margin: 0; font-weight: 600; }
h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin: 22px 0 8px; }

.muted { color: var(--muted); }
.error { color: var(--danger); margin: 10px 0 0; }
.spacer { flex: 1; }
.num { text-align: right; }

/* --- controls --- */

input, select, textarea, button { font: inherit; color: inherit; }

input, select, textarea {
  width: 100%;
  padding: 7px 9px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  border-color: var(--accent);
}

textarea { resize: vertical; }

.btn {
  padding: 7px 13px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  white-space: nowrap;
}

.btn:hover { background: var(--surface-2); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
.btn.primary:hover { filter: brightness(1.08); }
.btn.danger { color: var(--danger); border-color: var(--danger); }
.btn.danger:hover { background: var(--danger); color: #fff; }
.btn.subtle { border-color: transparent; background: none; }
.btn.subtle:hover { background: var(--surface-2); }
.btn.small { padding: 4px 10px; font-size: 13px; }
.btn.block { width: 100%; margin-top: 16px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* --- sign in --- */

/* Above the panels and modals, so a session expiring mid-task can never leave
   the sign-in form buried underneath one of them. */
.login-wrap {
  position: relative;
  z-index: 60;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 20px;
  background: var(--bg);
}

.login-card {
  width: 100%;
  max-width: 340px;
  padding: 30px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .login-card {
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
  }
}

.login-card h1 { font-size: 22px; }
.login-card p { margin: 4px 0 22px; }
.login-card label { display: block; margin: 14px 0 5px; font-size: 13px; color: var(--muted); }
.login-card .hint { margin: 8px 0 0; font-size: 12px; }
.login-alt { margin: 16px 0 0; text-align: center; }

/* A button that reads as a link. It has to be a button because it switches
   view rather than navigating, and the CSP forbids the inline handler an
   anchor would otherwise invite. */
.linkish {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 13px;
  color: var(--muted);
  cursor: pointer;
  text-decoration: underline;
}
.linkish:hover { color: var(--text); }

.notice { color: var(--ok); margin: 10px 0 0; font-size: 13px; }

/* A near-miss address. Louder than a hint and quieter than an error, because
   nothing has failed yet on the reset form: it has not been sent. */
.suggest { color: var(--text); margin: 10px 0 0; font-size: 13px; }
.suggest .strong-link { color: var(--accent); font-size: 13px; }

/* Setting the stage from inside the deal. The board's own way of doing this is
   to drag a card between columns, which a touch screen cannot express: there,
   a drag is a swipe between stages or a scroll through cards. */
.deal-stage {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 18px;
}
.deal-stage label { flex: 0 0 auto; font-size: 13px; color: var(--muted); }
.deal-stage select { flex: 1 1 auto; min-width: 0; }

/* The ?diag=1 panel. Deliberately loud and deliberately over everything: it is
   read from a photograph of a phone, so it has to survive being small. */
#diag-panel {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 90;
  margin: 0;
  padding: 6px 8px;
  background: #fff;
  color: #000;
  font: 10px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre-wrap;
  word-break: break-all;
}

/* --- chrome --- */

/* Sticky and translucent, so it needs the blur: without it the content
   scrolling underneath would read straight through the bar. */
.topbar {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 0 18px;
  height: 54px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 20;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .topbar {
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
  }
}

/* The logo is supplied at twice its display size so it stays sharp on a
   high-density screen. The intrinsic width and height on the <img> keep it the
   right size before the stylesheet has applied; these rules then bound it so a
   future logo of different proportions cannot break the layout.
   object-fit keeps the aspect ratio whatever the replacement's shape. */
.brand {
  height: 26px;
  width: auto;
  max-width: 220px;
  object-fit: contain;
  object-position: left center;
  display: block;
  flex: none;
}

.login-logo {
  height: 30px;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  display: block;
  margin: 0 auto 6px;
}

.tabs { display: flex; gap: 2px; }

.tab {
  padding: 6px 14px;
  background: none;
  border: 0;
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--muted);
}

.tab:hover { background: var(--surface-2); color: var(--text); }
.tab.active { background: var(--surface-2); color: var(--text); font-weight: 600; }

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }

.view { padding: 18px; }

.toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 14px; flex-wrap: wrap; }
.toolbar #search { flex: 1 1 260px; max-width: 380px; }
.toolbar select { width: auto; min-width: 130px; }

/* display: contents dissolves this wrapper and its body on a wide screen, so
   the selects inside lay out as direct children of the toolbar exactly as they
   did before. The sheet's own chrome is hidden here and revealed on a phone. */
.leads-filters,
.board-filters,
.filters-body { display: contents; }

.filters-head,
.filters-foot,
#leads-filter-toggle,
#board-filter-toggle { display: none; }

.btn.active { border-color: var(--accent); color: var(--accent); }

/* --- filter checklists --- */

.filter { position: relative; }

.filter-toggle::after {
  content: '';
  display: inline-block;
  margin-left: 7px;
  border: 4px solid transparent;
  border-top-color: currentColor;
  vertical-align: 2px;
}

.filter-toggle.active {
  border-color: var(--accent);
  color: var(--accent);
}

.filter-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 25;
  min-width: 220px;
  max-height: 320px;
  overflow-y: auto;
  padding: 6px;
  background: var(--surface-solid);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow);
}

.filter-option {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 8px;
  border-radius: 4px;
  cursor: pointer;
  white-space: nowrap;
}

.filter-option:hover { background: var(--surface-2); }
.filter-option input { width: auto; margin: 0; flex: none; }
.filter-toggle:disabled { opacity: .5; cursor: not-allowed; }
.filter-toggle:disabled::after { opacity: .4; }
.filter-label { flex: 1; }
.filter-count { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.filter-empty { padding: 10px; margin: 0; font-size: 13px; }

/* --- leads table --- */

.table-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow-x: auto;
}

table { width: 100%; border-collapse: collapse; }

th, td {
  padding: 9px 12px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

/* Opaque, not translucent: a sticky header sits directly over the rows passing
   beneath it, and a see-through one would print two lines of text on top of
   each other. */
th {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  background: var(--surface-solid);
  position: sticky;
  top: 0;
}

tbody tr { cursor: pointer; }
tbody tr:hover { background: var(--surface-2); }
tbody tr:last-child td { border-bottom: 0; }
td.strong { font-weight: 600; }

.empty { padding: 34px; text-align: center; color: var(--muted); }

/* The combined location line belongs to the narrow layout only; the wide table
   has a column for each part. */
.lead-meta { display: none; }
.load-more-wrap { display: flex; justify-content: center; padding: 14px 0 4px; }

/* Says the column holds more than it is showing, so a capped board is never
   mistaken for the whole picture. */
.column-more {
  padding: 8px 4px 2px;
  text-align: center;
  font-size: 12px;
  color: var(--muted);
}

/* Category marker. Deliberately quieter than the stage pill — stage is what the
   board is organised around, category is secondary context. */
.tag {
  display: inline-block;
  padding: 1px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 12px;
  white-space: nowrap;
}

.card .tag { margin-top: 7px; }

/* Stage colours, applied by class rather than by an inline style attribute.
   Inline styles would require 'unsafe-inline' in the Content-Security-Policy,
   and that allowance is exactly what makes an injected style attribute useful
   to an attacker. */
.stage-uncontacted     { background: var(--stage-uncontacted); }
.stage-initial_contact { background: var(--stage-initial_contact); }
.stage-materials_sent  { background: var(--stage-materials_sent); }
.stage-store_confirmed { background: var(--stage-store_confirmed); }

/* Dark text: every stage colour is a mid-to-light tone, and white on gold is
   close to unreadable. */
.pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  color: #0b0b0d;
  white-space: nowrap;
}

/* --- pipeline board --- */

.board {
  display: grid;
  grid-template-columns: repeat(4, minmax(230px, 1fr));
  gap: 12px;
  align-items: start;
  overflow-x: auto;
}

.column {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  min-height: 140px;
  display: flex;
  flex-direction: column;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .column {
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
  }
}

.column-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  font-weight: 600;
  font-size: 13px;
}

.column-head .dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }

.count {
  margin-left: auto;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: 12px;
  color: var(--muted);
}

.column-body { padding: 10px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.column.drag-over { outline: 2px dashed var(--accent); outline-offset: -3px; }

/* Translucent white with a hairline edge — the glass look without a
   backdrop-filter per card. See the note on .glass above. */
.card {
  background: rgba(255, 255, 255, .07);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 11px;
  cursor: grab;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .35), inset 0 1px 0 rgba(255, 255, 255, .06);
}

.card:hover { border-color: var(--border-strong); background: rgba(255, 255, 255, .1); }
.card.dragging { opacity: .4; }
.card .name { font-weight: 600; margin-bottom: 3px; }
.card .meta { font-size: 12.5px; color: var(--muted); }
.card .drop-marker { height: 2px; background: var(--accent); }

.column-empty { color: var(--muted); font-size: 13px; text-align: center; padding: 14px 0; }

/* --- detail panel --- */

.overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, .35); z-index: 30; }

/* The panel sits over the page, so it is opaque: a translucent editor would
   have the list showing through its own input fields. */
.detail {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(520px, 100vw);
  background: var(--surface-solid);
  border-left: 1px solid var(--border);
  box-shadow: var(--shadow);
  z-index: 31;
  display: flex;
  flex-direction: column;
}

.detail-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}

.detail-head h2 { font-size: 16px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.detail-head .btn.subtle { font-size: 20px; line-height: 1; padding: 2px 10px; }

.detail-body { padding: 6px 18px 20px; overflow-y: auto; flex: 1; }

.detail-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 18px;
  border-top: 1px solid var(--border);
}

/* Attribution: present when you look for it, quiet when you are not. */
.editor {
  color: var(--muted);
  font-size: 12px;
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card .editor { margin-top: 6px; max-width: 100%; }

.edited-by {
  padding: 9px 18px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--muted);
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.save-status { margin-left: auto; font-size: 13px; color: var(--muted); }
.save-status.saved { color: var(--ok); }
.save-status.failed { color: var(--danger); }

.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.field-grid label { display: block; font-size: 12px; color: var(--muted); }
.field-grid label.wide { grid-column: 1 / -1; }
.field-grid input, .field-grid select { margin-top: 3px; }

.location-row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 70px 90px auto;
  gap: 6px;
  margin-bottom: 6px;
  align-items: center;
}

.location-row input { padding: 5px 7px; font-size: 13px; }

/* --- deal view --- */

.deal-card { width: min(680px, 100%); }
.deal-card .detail-head h2 { flex: 1; }

/* --- deal progress --- */

/* Four equal segments spanning the panel, so the shape of the bar alone says
   how far along the deal is. */
.deal-progress {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  padding: 14px 18px 16px;
  border-bottom: 1px solid var(--border);
  background: rgba(255, 255, 255, .03);
}

.progress-step { min-width: 0; }

.progress-track {
  height: 6px;
  border-radius: 3px;
  background: var(--track);
  margin-bottom: 7px;
}

.progress-step.done .progress-track { background: var(--gold-done); }

.progress-step.current .progress-track {
  background: var(--gold-current);
  box-shadow: 0 0 12px rgba(232, 198, 90, .45);
}

.progress-label {
  font-size: 11.5px;
  line-height: 1.25;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.progress-step.current .progress-label { color: var(--text); font-weight: 600; }

.progress-days {
  margin-top: 3px;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.progress-step.current .progress-days { color: var(--gold-current); }

.summary {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 18px;
  margin: 0 0 4px;
}

.summary dt { color: var(--muted); font-size: 12.5px; }
.summary dd { margin: 0; overflow-wrap: anywhere; }
.summary a { color: var(--accent); }

/* --- notes --- */

.note-form textarea { margin-bottom: 8px; }
.note-form-actions { display: flex; align-items: center; gap: 10px; }
.note-form-actions .error { margin: 0; font-size: 13px; }

.note-list { margin-top: 16px; display: flex; flex-direction: column; gap: 10px; }

.note {
  padding: 10px 12px;
  background: rgba(255, 255, 255, .06);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.note-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 6px;
  font-size: 12px;
  color: var(--muted);
}

.note-actions { margin-left: auto; display: flex; gap: 2px; }
.note-actions .btn { padding: 2px 8px; font-size: 12px; }
.danger-text { color: var(--danger); }

/* Notes are typed by people and keep the line breaks they typed. */
.note-body { white-space: pre-wrap; overflow-wrap: anywhere; }

.note.editing textarea { margin-bottom: 8px; }

/* --- import --- */

.modal { position: fixed; inset: 0; background: rgba(0, 0, 0, .4); z-index: 40; display: grid; place-items: center; padding: 20px; }

.modal-card {
  background: var(--surface-solid);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow);
  width: min(820px, 100%);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;   /* keeps the progress bar inside the rounded corners */
}

.modal-body { padding: 18px; overflow-y: auto; flex: 1; }
.modal-foot { display: flex; justify-content: flex-end; gap: 10px; padding: 12px 18px; border-top: 1px solid var(--border); }

.import-category {
  display: block;
  margin-bottom: 16px;
  padding: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 13px;
}

.import-category select { margin: 6px 0 4px; max-width: 260px; }
.import-category .muted { display: block; font-size: 12px; }

.mapping-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; }
.mapping-grid label { display: block; font-size: 12px; color: var(--muted); }
.mapping-grid select { margin-top: 3px; }

.preview-wrap { max-height: 220px; overflow: auto; }
.preview-wrap td, .preview-wrap th { font-size: 12.5px; max-width: 190px; overflow: hidden; text-overflow: ellipsis; }

.import-errors { max-height: 220px; overflow-y: auto; margin-top: 10px; font-size: 13px; }
.import-errors div { padding: 4px 0; border-bottom: 1px solid var(--border); color: var(--muted); }

/* --- toast --- */

.toast {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  padding: 10px 18px;
  background: var(--text);
  color: var(--bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  z-index: 50;
}

.toast.bad { background: var(--danger); color: #fff; }

/* --- mobile navigation --- */

/* Hidden by default and revealed on narrow screens, so the wide layout carries
   no trace of it. */
.mobile-nav { display: none; }

.stage-dots { display: none; }

/* --- narrow screens --- */

@media (max-width: 760px) {

  /* The bottom bar takes over navigation, so the top bar is dead weight — and
     vertical space is the scarcest thing on a phone. */
  .topbar { display: none; }

  /* --- the app shell ---
     The page does not scroll on a phone; the list inside it does. That is the
     whole reason the toolbar and the nav bar stay on screen.

     They used to be position: fixed over a scrolling page, which iOS Safari
     cannot keep on screen: a fixed element is placed against the layout
     viewport, and the bottom of that viewport sits underneath Safari's own
     browser bar. The nav disappeared behind it entirely and the toolbar was
     cut in half. Nothing about the offsets could fix that, because the space
     they were measured against was not the space you can see.

     As flex children of a dvh-tall column they are on screen by construction.
     dvh is the dynamic viewport height: it shrinks and grows as Safari's bar
     collapses and expands, so the shell tracks the space actually available
     rather than the space the page is theoretically laid out in. */
  html, body {
    height: 100%;
    overflow: hidden;            /* the window never scrolls here */
    overscroll-behavior: none;   /* no rubber-band revealing a blank page */
  }

  body { padding-bottom: 0; }

  /* Three heights, deliberately, in increasing order of preference. The last
     one the browser understands is the one that applies, and a browser that
     does not understand a unit drops that declaration entirely — which is the
     trap: a bare 100dvh leaves height:auto on a browser without dvh, the shell
     stops constraining anything, and overflow:hidden then clips the toolbar
     and the nav below the fold. Worse than the bug it replaced.

     100vh:                   always understood, but on iOS it is the tall
                              viewport that runs under Safari's browser bar.
     -webkit-fill-available:  pre-dvh iOS, where it means the visible height.
     100dvh:                  correct everywhere it exists, and tracks the
                              browser's chrome as it collapses and expands. */
  #app {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: -webkit-fill-available;
    height: 100dvh;
    overflow: hidden;
  }

  /* Nothing in the shell may be wider than the screen. Every piece of this was
     already meant to fit, and the list still found a way past the edge, so the
     rule is stated rather than assumed. A phone has no horizontal scrollbar to
     reveal what overflowed: the page simply looks magnified and cropped, which
     is a hard thing to diagnose from a photograph of it. */
  #app,
  .view,
  .view-scroll,
  .toolbar,
  .mobile-nav,
  .board,
  .column { max-width: 100%; }

  /* An author display on .view would otherwise beat the user agent's rule for
     the hidden attribute, and both views would render at once. Chrome happens
     to keep them hidden; that is not something to depend on. */
  #app[hidden],
  .view[hidden] { display: none; }

  .view {
    flex: 1 1 auto;
    min-height: 0;               /* without this a flex child refuses to shrink */
    display: flex;
    flex-direction: column;
    padding: 0;
  }

  /* Content first, controls after it. The controls belong next to the thumb
     rather than at the far end of the screen from it, and `order` puts them
     there without moving them in the markup, where they read better before
     the thing they filter. */
  /* overscroll-behavior: none, not contain. contain stops a scroll running on
     into the page behind, but still lets this box rubber-band past its own
     ends, which is the give that makes a page feel like a document. An
     application does not pull away from its top edge. */
  .view-scroll {
    order: 1;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: none;
    padding: 10px 12px 4px;
  }

  .view > .toolbar { order: 2; }

  /* The board is its own scroller: horizontal between stages, vertical within
     one. align-items keeps a column its natural height so the overflow to
     scroll is the column's, not a stretched box's. */
  /* The board scrolls sideways between stages and never vertically. Each column
     scrolls its own cards.

     One scroller for all four columns meant one scroll position shared between
     them. Scrolling down through Uncontacted and swiping to a stage holding two
     deals put that whole column, heading and all, above the top of the screen:
     an empty black panel with nothing on it to say which stage you were even
     looking at. Making the heading sticky could not help, because sticky pins a
     heading inside its own column and that column had itself scrolled away.

     Columns are stretched to the full height of the board so each one is a
     scrollport of its own, and each starts at its own top. */
  .board {
    order: 1;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: hidden;
    align-items: stretch;
    padding: 0 12px 4px;
  }

  .column {
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;

    /* Pinned at both ends, like the list. Dragging down used to peel the cards
       away from the heading and leave a band of black behind them. */
    overscroll-behavior: none;
  }

  /* Sits between the board and the toolbar, and keeps its own height rather
     than being stretched or squeezed by the column. */
  #stage-dots { order: 1; flex: 0 0 auto; }

  /* The shell stops the window scrolling, which would otherwise trap the
     sign-in card once the on-screen keyboard covers half the screen. These
     views scroll themselves instead. dvh so the card is centred in the space
     actually visible rather than behind Safari's bar. */
  .login-wrap {
    min-height: 0;
    height: 100vh;
    height: -webkit-fill-available;
    height: 100dvh;
    overflow-y: auto;
    padding: 20px max(20px, env(safe-area-inset-left, 0px))
             calc(20px + env(safe-area-inset-bottom, 0px));
  }

  /* Controls sit at the bottom, directly above the nav bar. What you are
     reading is the list or the board; the controls belong next to the thumb
     rather than at the far end of the screen from it. */
  /* Deliberately NOT backdrop-filtered.
     A backdrop-filter makes an element the containing block for any
     position: fixed descendant, so the filters sheet inside this bar resolved
     its `inset: 0` against the bar rather than the viewport and rendered as a
     squashed panel. The bar is a solid dark surface instead; the glass on the
     nav below it carries the effect. */
  .toolbar {
    position: relative;
    flex: 0 0 auto;

    /* Above the nav bar (45) so the filter sheet nested in here can cover it,
       and below the detail panel (47) and dialogs (48) so those still cover
       this. A z-index on the sheet itself cannot escape this element's
       stacking context, so the ordering has to be settled here. */
    z-index: 46;

    gap: 7px;
    margin: 0;
    padding: 8px 12px;
    background: #131316;
    border-top: 1px solid var(--border);
    border-bottom: 0;
  }

  /* Search on its own line, everything else beneath it. */
  .toolbar input[type="search"] { flex: 1 1 100%; max-width: none; }
  .toolbar .btn { flex: 0 0 auto; padding: 6px 11px; font-size: 13px; }
  .toolbar .spacer { display: none; }

  #lead-count, #board-count {
    order: 99;
    margin-left: auto;
    font-size: 12px;
    white-space: nowrap;
  }

  /* One line of controls where they fit, two where they do not.

     This used to be flex-wrap: nowrap with the search box allowed to shrink to
     nothing, on the reasoning that one row is worth protecting. It is, but not
     at this price: the row could only ever fit by crushing the search box, down
     to 30px on a 320px screen, and once it had nothing left to give the row
     overflowed the screen instead. A page wider than the phone is the worst
     outcome of the three, because the browser answers it by letting you pan
     around a layout that no longer lines up with anything.

     Wrapping cannot overflow. The row takes a second line only when it must,
     and the shell gives it that space out of the list. */
  .toolbar {
    flex-wrap: wrap;
    gap: 6px;
  }

  .toolbar input[type="search"] {
    /* Wide enough to read a query in. Below this it takes its own line rather
       than becoming a box you cannot see what you typed in. */
    flex: 1 1 130px;
    min-width: 110px;
    padding: 8px 10px;
  }

  .toolbar .btn {
    flex: 0 0 auto;
    padding: 8px 10px;
    font-size: 12.5px;
  }

  #lead-count, #board-count {
    flex: 0 0 auto;
    font-size: 11px;
    line-height: 1.15;
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
  }

  /* The four dropdowns move into a full-screen sheet; the button that opens it
     takes their place in the bar. A sheet floating over the list left the rows
     behind it half-visible and the whole thing looked like a rendering fault. */
  #leads-filter-toggle,
  #board-filter-toggle { display: inline-block; }

  .leads-filters,
  .board-filters {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 49;
    flex-direction: column;
    background: var(--surface-solid);
  }

  .leads-filters.open,
  .board-filters.open { display: flex; }

  /* Inside the sheet the checklists are accordions, opening in place under
     their own heading. A popover anchored to a button makes no sense on a
     screen the sheet already fills. */
  .board-filters .filter { position: static; }

  .board-filters .filter-toggle {
    width: 100%;
    text-align: left;
    padding: 11px 12px;
    font-size: 14px;
  }

  .board-filters .filter-panel {
    position: static;
    width: auto;
    max-height: 34vh;
    margin-top: 6px;
    box-shadow: none;
    background: rgba(255, 255, 255, .04);
  }

  .filters-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px calc(14px + env(safe-area-inset-top, 0px));
    border-bottom: 1px solid var(--border);
  }

  .filters-head h2 { flex: 1; font-size: 17px; }
  .filters-head .btn.subtle { font-size: 26px; padding: 0 10px; line-height: 1; }

  .filters-body {
    display: grid;
    align-content: start;
    gap: 12px;
    flex: 1;
    overflow-y: auto;
    padding: 16px;
  }

  .filters-body select { width: 100%; min-width: 0; padding: 11px 12px; }
  .filters-body .btn { width: 100%; }

  .filters-foot {
    display: flex;
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--border);
  }

  .filters-foot .btn { margin: 0; }




  /* --- one stage at a time, swiped between --- */

  .board {
    display: flex;
    grid-template-columns: none;
    gap: 0;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }

  .board::-webkit-scrollbar { display: none; }

  .column {
    flex: 0 0 100%;
    scroll-snap-align: start;
    scroll-snap-stop: always;   /* one stage per swipe, never two */
    background: transparent;
    border: 0;
  }

  /* The stage heading stays put while its cards scroll under it. Scrolling a
     column used to carry the heading away with it, leaving no answer on screen
     to which stage you were looking at — and on a swipeable board that is the
     one thing you cannot work out from the cards themselves.

     Opaque, not var(--surface-2). That is white at 8.5% and the cards passing
     underneath showed straight through it. #131316 is the solid the bottom
     toolbar already uses, so the two pieces of chrome match. */
  .column-head {
    position: sticky;
    top: 0;
    z-index: 2;
    border: 1px solid var(--border);
    border-radius: 8px 8px 0 0;
    background: #131316;
  }

  .column-body {
    border: 1px solid var(--border);
    border-top: 0;
    border-radius: 0 0 8px 8px;
    background: var(--surface-2);
  }

  .stage-dots {
    display: flex;
    justify-content: center;
    gap: 8px;
    padding: 2px 0 10px;
  }

  .stage-dot {
    width: 8px;
    height: 8px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--border);
    cursor: pointer;
    transition: width .15s ease, background-color .15s ease;
  }

  /* The dots mark position in the sequence rather than identify a stage, so
     one accent colour serves better than four. */
  .stage-dot.active { width: 22px; border-radius: 4px; background: var(--accent); }

  /* --- the leads table becomes a compact list --- */

  /* Two lines per lead: the business, then where it is. The remaining columns
     are a click away in the lead itself, and stacking all nine of them turned
     a list you can scan into a wall you have to read. */
  .table-wrap { border: 0; background: none; overflow: visible; max-width: 100%; }

  /* A table sizes itself to its contents. Left to itself it takes whatever
     width its widest cell wants and carries the whole list past the edge of the
     screen with it, which reads as the page having loaded zoomed in: the rows
     run off both sides and the controls are clipped. table-layout: fixed makes
     the declared width the width, and the text wraps inside it. */
  #leads-table {
    width: 100%;
    max-width: 100%;
    table-layout: fixed;
  }

  #leads-table thead { display: none; }

  #leads-table tbody tr {
    display: block;
    margin-bottom: 8px;
    padding: 11px 13px;
    background: rgba(255, 255, 255, .06);
    border: 1px solid var(--border);
    border-radius: 10px;
  }

  #leads-table td { display: none; }

  #leads-table td.strong,
  #leads-table td.lead-meta {
    display: block;
    padding: 0;
    border: 0;
    white-space: normal;
    text-align: left;
  }

  /* A business name with no spaces in it, or a very long web address, would
     otherwise be a single unbreakable word wider than the phone. */
  #leads-table td.strong { font-size: 15px; line-height: 1.3; overflow-wrap: anywhere; }

  #leads-table td.lead-meta {
    margin-top: 3px;
    color: var(--muted);
    font-size: 13px;
  }

  .editor { max-width: none; }

  /* --- panels and dialogs --- */

  /* A full-screen editor sits above the nav bar rather than under it — the bar
     would otherwise cover the delete button and the save status. */
  .detail { width: 100vw; z-index: 47; }
  .overlay { z-index: 46; }
  .detail-body { padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px)); }
  .field-grid { grid-template-columns: 1fr; }
  .location-row { grid-template-columns: 1fr 1fr; }

  /* A dialog on a phone is a screen, not a card floating on one. Edge to edge
     with no rounding, closed by the button in its own header. */
  .modal { padding: 0; align-items: stretch; z-index: 48; }

  .modal-card {
    width: 100%;
    max-width: none;
    max-height: none;
    height: 100vh;
    height: 100dvh;   /* excludes the browser's own chrome where supported */
    border: 0;
    border-radius: 0;
  }

  .deal-card { width: 100%; }
  .deal-progress { padding: 12px 14px 14px; gap: 6px; }
  .progress-label { font-size: 10.5px; }

  .detail-head { padding: 14px 14px calc(14px + env(safe-area-inset-top, 0px)); }
  .detail-head .btn.subtle { font-size: 26px; padding: 0 10px; }
  .modal-body { padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px)); }
  .modal-foot { padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)); }
  .summary { grid-template-columns: 1fr; gap: 2px 0; }
  .summary dd { margin-bottom: 8px; }
  .mapping-grid { grid-template-columns: 1fr; }

  /* Clear of the shell's bottom chrome, whose height is measured rather than
     guessed: the toolbar rewraps when a count grows a digit. */
  .toast { bottom: calc(16px + var(--bottom-chrome, 108px) + env(safe-area-inset-bottom, 0px)); }

  /* --- the bar itself --- */

  .mobile-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;

    /* The last child of the shell rather than a fixed overlay, so it cannot be
       pushed under Safari's browser bar. It keeps position: relative only so
       the account menu above it still has something to anchor to. */
    position: relative;
    flex: 0 0 auto;
    margin: 8px 12px calc(8px + env(safe-area-inset-bottom, 0px));
    z-index: 45;
    padding: 8px 10px;
    border-radius: 20px;

    /* Glass, but stated in a dark colour rather than a white film over
       whatever happens to be underneath.
       No saturate(): it amplifies colour in the backdrop, and with the gold
       logo sitting in the middle of a heavily blurred panel it washed the
       whole bar gold. The blur alone gives the depth without recolouring the
       bar to match its own contents. */
    background: rgba(26, 26, 30, .88);
    border: 1px solid rgba(255, 255, 255, .14);
    box-shadow: 0 8px 32px rgba(0, 0, 0, .6), inset 0 1px 0 rgba(255, 255, 255, .08);
  }

  @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .mobile-nav {
      background: rgba(26, 26, 30, .72);
      -webkit-backdrop-filter: blur(22px);
      backdrop-filter: blur(22px);
    }
  }

  .mobile-nav-item {
    flex: 1 1 0;
    padding: 9px 4px;
    background: none;
    border: 0;
    border-radius: 13px;
    color: var(--muted);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
  }

  .mobile-nav-item.active { background: rgba(255, 255, 255, .12); color: var(--text); }

  .mobile-nav-logo {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    padding: 7px 14px;
    background: none;
    border: 0;
    border-radius: 13px;
    cursor: pointer;
  }

  .mobile-nav-logo:active { background: rgba(255, 255, 255, .12); }
  .mobile-nav-logo img { height: 15px; width: auto; display: block; }

  /* Sign out lives behind the logo: there is no room for an account menu, and
     the logo is the one control always on screen. */
  .nav-menu {
    position: absolute;
    left: 50%;
    bottom: calc(100% + 10px);
    transform: translateX(-50%);
    min-width: 170px;
    padding: 8px;
    background: var(--surface-solid);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow);
  }

  .nav-menu .btn { margin: 0; }

  /* The account menu opens upwards into the toolbar's band, and the toolbar
     sits above the nav bar (46 against 45) so that the filter sheet nested in
     it can cover the nav. A child cannot escape its parent's stacking context,
     so the menu inherits 45 and is painted underneath the toolbar: open, the
     right size, in the right place, and invisible.

     The bar is lifted for as long as the menu is open, which is never while a
     filter sheet is open, because opening either closes the other. 47 rather
     than higher so the detail panel and dialogs still cover it. */
  .mobile-nav.menu-open { z-index: 47; }
}

