/* ==========================================================================
   RetailMark CRM
   Layout and control placement follow the CRM convention the team already
   reads: fixed dark left rail, slim white top bar with search, page header
   with actions on the right, list pages as filter chips over a white table
   card. The palette is sampled from the RetailMark wordmark -- near-black
   ground, amber accent -- so the app and the logo read as one thing.
   ========================================================================== */

:root {
  /* brand -- sampled from the RetailMark wordmark, not guessed.
     The logo is white + #FFBA00 on near-black, so the rail is the dark
     ground the mark was drawn for and amber is the accent, never the
     background of a large surface. */
  --brand-900: #101216;   /* rail, the logo's ground */
  --brand-800: #191C22;
  --brand-700: #23272F;   /* brand primary -- buttons, links */
  --brand-600: #333844;
  --brand-500: #5A616F;
  --accent:      #FFBA00; /* the "Mark", the spark */
  --accent-soft: #FFD973;
  --sand:      #F6F2E9;
  --ink:       #14161A;
  --slate:     #6B7280;
  --line:      #E4E6EA;
  --white:     #FFFFFF;

  /* app surfaces */
  --canvas:    #F5F6F8;
  --surface:   #FFFFFF;
  --rail-w:    228px;
  --rail-w-sm: 64px;
  --topbar-h:  58px;

  --radius:    8px;
  --radius-sm: 6px;
  --shadow-sm: 0 1px 2px rgba(16, 18, 22, .08);
  --shadow-md: 0 4px 16px rgba(16, 18, 22, .10);
  --shadow-lg: 0 18px 48px rgba(16, 18, 22, .18);

  --font-head: 'Jost', 'Helvetica Neue', Arial, sans-serif;
  --font-body: 'Roboto', -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;

  /* status colors */
  --st-grey:   #6B7280;  --st-grey-bg:   #EEF0F3;
  --st-blue:   #1D6FA5;  --st-blue-bg:   #E4F0F8;
  --st-green:  #2F7D32;  --st-green-bg:  #E6F2E4;
  --st-amber:  #A96A00;  --st-amber-bg:  #FBF0DC;
  --st-red:    #B3261E;  --st-red-bg:    #FBE9E7;
  --st-purple: #6A3FA0;  --st-purple-bg: #F0E9F9;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { font-family: var(--font-head); color: var(--ink); margin: 0; font-weight: 600; }
a { color: var(--brand-700); text-decoration: none; }
a:hover { color: var(--brand-500); }

/* ---- left rail ---------------------------------------------------------- */

.rail {
  position: fixed;
  inset: 0 auto 0 0;
  width: var(--rail-w);
  background: var(--brand-900);
  display: flex;
  flex-direction: column;
  z-index: 40;
}

.rail-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  height: var(--topbar-h);
  padding: 0 16px;
  flex: 0 0 auto;
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}
/* The RM monogram is for the collapsed rail only. Open, the wordmark already
   says RetailMark and the monogram beside it says it twice — RM RetailMark.
   Collapsed there is no room for the wordmark, and a rail with no mark at all
   has nothing to identify it, so the monogram comes back. */
.rail-mark { display: none; }
body.rail-collapsed .rail-mark {
  width: 30px; height: 30px;
  flex: 0 0 30px;
  display: grid; place-items: center;
}
.rail-mark img { width: 30px; height: auto; display: block; }

/* the wordmark carries the name, so the rail doesn't repeat it in text */
.rail-wordmark { display: block; height: 22px; width: auto; }
.rail-name {
  font-family: var(--font-head);
  color: var(--white);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: .01em;
  white-space: nowrap;
  overflow: hidden;
}
.rail-name small { display: block; font-size: 10px; color: var(--accent); letter-spacing: .12em; text-transform: uppercase; font-weight: 500; }

/* the Create button sits at the very top of the nav, as in Jobber */
.rail-create { padding: 14px 12px 6px; position: relative; }
.rail-create > button {
  width: 100%;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--accent);
  color: var(--brand-900);
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 14px;
  padding: 10px 12px;
  cursor: pointer;
  transition: background .15s ease;
}
.rail-create > button:hover { background: var(--accent-soft); }

.rail-nav { flex: 1 1 auto; overflow-y: auto; padding: 8px 0 16px; }
.rail-nav::-webkit-scrollbar { width: 6px; }
.rail-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,.14); border-radius: 3px; }

.rail-link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 16px 9px 13px;
  margin: 1px 8px;
  border-radius: var(--radius-sm);
  color: rgba(255, 255, 255, .82);
  font-size: 14px;
  border-left: 3px solid transparent;
  transition: background .13s ease, color .13s ease;
  white-space: nowrap;
}
.rail-link:hover { background: rgba(255, 255, 255, .07); color: var(--white); }
.rail-link.active {
  background: rgba(167, 201, 87, .16);
  color: var(--white);
  border-left-color: var(--accent);
  font-weight: 500;
}
.rail-link svg { width: 18px; height: 18px; flex: 0 0 18px; stroke-width: 1.7; }
.rail-link .count {
  margin-left: auto;
  background: rgba(255,255,255,.14);
  color: var(--white);
  font-size: 11px;
  font-weight: 600;
  padding: 1px 7px;
  border-radius: 10px;
}
.rail-link.active .count { background: var(--accent); color: var(--brand-900); }

.rail-section {
  padding: 14px 16px 5px;
  font-size: 10.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .38);
  font-family: var(--font-head);
  font-weight: 600;
}

.rail-foot {
  flex: 0 0 auto;
  border-top: 1px solid rgba(255,255,255,.08);
  padding: 8px;
}
.rail-collapse {
  width: 100%;
  display: flex; align-items: center; gap: 10px;
  background: none; border: 0; cursor: pointer;
  color: rgba(255,255,255,.55);
  font-size: 12.5px;
  padding: 7px 8px;
  border-radius: var(--radius-sm);
}
.rail-collapse:hover { background: rgba(255,255,255,.07); color: var(--white); }

/* collapsed rail: icons only */
body.rail-collapsed { --rail-w: var(--rail-w-sm); }
body.rail-collapsed .rail-name,
body.rail-collapsed .rail-section,
body.rail-collapsed .rail-link span,
body.rail-collapsed .rail-create span,
body.rail-collapsed .rail-collapse span { display: none; }
body.rail-collapsed .rail-link { justify-content: center; padding-left: 10px; padding-right: 10px; }
body.rail-collapsed .rail-brand { justify-content: center; padding: 0; }
body.rail-collapsed .rail-wordmark { display: none; }
body.rail-collapsed .rail-collapse { justify-content: center; }
body.rail-collapsed .rail-collapse svg { transform: rotate(180deg); }

/* ---- top bar ------------------------------------------------------------ */

.topbar {
  position: fixed;
  top: 0; right: 0; left: var(--rail-w);
  height: var(--topbar-h);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 20px;
  z-index: 30;
}

.search {
  position: relative;
  flex: 0 1 460px;
}
.search svg { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; color: var(--slate); }
.search input {
  width: 100%;
  height: 36px;
  padding: 0 12px 0 34px;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: #F7F8F6;
  font-family: var(--font-body);
  font-size: 13.5px;
  color: var(--ink);
  outline: none;
  transition: border-color .15s ease, background .15s ease;
}
.search input:focus { border-color: var(--brand-500); background: var(--white); box-shadow: 0 0 0 3px rgba(106,153,78,.14); }

.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.icon-btn {
  position: relative;
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border: 0; background: none; cursor: pointer;
  border-radius: 50%;
  color: var(--ink);
}
.icon-btn:hover { background: #F1F2F5; }
.icon-btn svg { width: 19px; height: 19px; stroke-width: 1.7; }
.icon-btn .dot {
  position: absolute; top: 6px; right: 6px;
  width: 8px; height: 8px; border-radius: 50%;
  background: #C0392B; border: 2px solid var(--white);
}

.avatar-btn {
  margin-left: 6px;
  width: 34px; height: 34px;
  border-radius: 50%;
  border: 0; cursor: pointer;
  /* A <button> keeps the browser's side padding unless told otherwise, and
     that shoved the 30px initials circle 4px right of centre. The photo
     variant already zeroed it, which is why only initials looked off. */
  padding: 0;
  display: grid; place-items: center;
  background: var(--brand-700);
  color: var(--white);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 12.5px;
}

/* ---- dropdown menus ----------------------------------------------------- */

.menu {
  position: absolute;
  min-width: 220px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: 6px;
  z-index: 60;
  display: none;
}
.menu.open { display: block; }
.menu-right { right: 0; top: calc(100% + 6px); }
.rail-create .menu { left: 12px; right: 12px; top: calc(100% - 2px); }
.menu a, .menu button {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: 0; background: none;
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
}
.menu a:hover, .menu button:hover { background: #F2F3F6; color: var(--brand-800); }
.menu svg { width: 16px; height: 16px; color: var(--brand-600); flex: 0 0 16px; }
.menu hr { border: 0; border-top: 1px solid var(--line); margin: 6px 4px; }
.menu-label { padding: 8px 10px 4px; font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--slate); font-weight: 600; }

/* ---- main region -------------------------------------------------------- */

.main {
  margin-left: var(--rail-w);
  padding-top: var(--topbar-h);
  min-height: 100vh;
}
.page { padding: 24px 28px 60px; max-width: 1560px; }

.page-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.page-head h1 { font-size: 26px; letter-spacing: -.01em; }
.page-head .sub { color: var(--slate); font-size: 13.5px; margin-top: 3px; }
/* primary action sits far right of the header, secondary + kebab to its left */
.page-head .actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.crumb { font-size: 12.5px; color: var(--slate); margin-bottom: 6px; }
.crumb a { color: var(--slate); }
.crumb a:hover { color: var(--brand-700); }

/* ---- buttons ------------------------------------------------------------ */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 38px;
  padding: 0 16px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-family: var(--font-head);
  font-weight: 500;
  font-size: 14px;
  cursor: pointer;
  white-space: nowrap;
  transition: background .14s ease, border-color .14s ease, color .14s ease;
}
.btn svg { width: 16px; height: 16px; }
.btn-primary { background: var(--brand-700); color: var(--white); border-color: var(--brand-700); }
.btn-primary:hover { background: var(--brand-800); border-color: var(--brand-800); color: var(--white); }
.btn-accent { background: var(--accent); color: var(--ink); border-color: var(--accent); }
.btn-accent:hover { background: var(--accent-soft); border-color: var(--accent-soft); color: var(--ink); }
.btn-outline { background: var(--white); color: var(--brand-700); border-color: #D3D7DE; }
.btn-outline:hover { background: #F2F3F6; border-color: var(--brand-500); color: var(--brand-800); }
.btn-ghost { background: none; color: var(--slate); border-color: transparent; }
.btn-ghost:hover { background: #F1F2F5; color: var(--ink); }
.btn-danger { background: var(--white); color: var(--st-red); border-color: #E7C8C5; }
.btn-danger:hover { background: var(--st-red-bg); }
.btn-sm { height: 32px; padding: 0 12px; font-size: 13px; }
.btn-kebab { width: 38px; padding: 0; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.btn-wrap { position: relative; }

/* ---- cards -------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}
.card + .card { margin-top: 18px; }
.card-head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
}
.card-head h2, .card-head h3 { font-size: 15.5px; font-weight: 600; }
/* leading icon in a card header */
.card-head > svg { width: 19px; height: 19px; flex: 0 0 19px; color: var(--brand-600); }
.card-head .actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.card-body { padding: 18px; }
.card-body.tight { padding: 0; }
.card-foot { padding: 12px 18px; border-top: 1px solid var(--line); background: #FAFBF9; border-radius: 0 0 var(--radius) var(--radius); }

.grid { display: grid; gap: 18px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-main { grid-template-columns: minmax(0, 1fr) 340px; align-items: start; }

/* ---- KPI tiles (Jobber's "key metrics" strip above list pages) ---------- */

.metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 12px;
  margin-bottom: 18px;
}
.metric {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: var(--shadow-sm);
}
.metric .label { font-size: 12px; color: var(--slate); text-transform: uppercase; letter-spacing: .07em; font-weight: 600; }
.metric .value { font-family: var(--font-head); font-size: 24px; font-weight: 600; margin-top: 4px; color: var(--ink); }
.metric .value.pos { color: var(--st-green); }
.metric .value.neg { color: var(--st-red); }
.metric .foot { font-size: 12px; color: var(--slate); margin-top: 2px; }

/* ---- filter chips / tabs ------------------------------------------------ */

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px;
  padding: 0 13px;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--slate);
  font-size: 13px;
  cursor: pointer;
}
.chip:hover { border-color: var(--brand-500); color: var(--brand-700); }
.chip.active { background: var(--brand-700); border-color: var(--brand-700); color: var(--white); font-weight: 500; }
.chip .n { font-size: 11.5px; opacity: .85; }

/* ---- tables ------------------------------------------------------------- */

.table-wrap { overflow-x: auto; border-radius: 0 0 var(--radius) var(--radius); }
table.list { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.list thead th {
  text-align: left;
  padding: 10px 16px;
  background: #FAFBF9;
  border-bottom: 1px solid var(--line);
  color: var(--slate);
  font-weight: 600;
  font-size: 11.5px;
  letter-spacing: .07em;
  text-transform: uppercase;
  white-space: nowrap;
}
table.list tbody td { padding: 12px 16px; border-bottom: 1px solid #F0F1EE; vertical-align: middle; }
table.list tbody tr:last-child td { border-bottom: 0; }
table.list tbody tr:hover { background: #F7F9F5; }
table.list tbody tr.clickable { cursor: pointer; }
table.list .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
table.list .muted { color: var(--slate); font-size: 12.5px; }
.row-title { font-weight: 500; color: var(--ink); }
.row-title a { color: var(--ink); }
.row-title a:hover { color: var(--brand-700); }

.empty { padding: 54px 20px; text-align: center; color: var(--slate); }
.empty svg { width: 40px; height: 40px; color: #C3CDBB; margin-bottom: 10px; }
.empty h3 { font-size: 16px; color: var(--ink); margin-bottom: 4px; }

/* ---- badges ------------------------------------------------------------- */

.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px;
  border-radius: 12px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .01em;
  white-space: nowrap;
  background: var(--st-grey-bg);
  color: var(--st-grey);
}
.badge.grey   { background: var(--st-grey-bg);   color: var(--st-grey); }
.badge.blue   { background: var(--st-blue-bg);   color: var(--st-blue); }
.badge.green  { background: var(--st-green-bg);  color: var(--st-green); }
.badge.amber  { background: var(--st-amber-bg);  color: var(--st-amber); }
.badge.red    { background: var(--st-red-bg);    color: var(--st-red); }
.badge.purple { background: var(--st-purple-bg); color: var(--st-purple); }
.badge.lime   { background: #E3F3E7; color: #1F7A44; }

.avatar {
  width: 30px; height: 30px;
  border-radius: 50%;
  display: inline-grid; place-items: center;
  background: var(--brand-500);
  color: var(--white);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 11.5px;
  flex: 0 0 30px;
}
.avatar.sm { width: 24px; height: 24px; font-size: 10px; flex-basis: 24px; }
.avatar-stack { display: inline-flex; }
.avatar-stack .avatar + .avatar { margin-left: -8px; border: 2px solid var(--white); }

/* ---- forms -------------------------------------------------------------- */

.field { margin-bottom: 15px; }
.field > label, .label {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 5px;
}
.field .hint { font-size: 12px; color: var(--slate); margin-top: 4px; }
input[type=text], input[type=email], input[type=password], input[type=tel],
input[type=number], input[type=date], input[type=datetime-local], input[type=search],
select, textarea {
  width: 100%;
  min-height: 38px;
  padding: 8px 11px;
  border: 1px solid #D3D7DE;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: 13.5px;
  color: var(--ink);
  background: var(--white);
  outline: none;
  transition: border-color .14s ease, box-shadow .14s ease;
}
textarea { min-height: 86px; resize: vertical; }
input:focus, select:focus, textarea:focus {
  border-color: var(--brand-500);
  box-shadow: 0 0 0 3px rgba(106, 153, 78, .16);
}
select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 11px center; padding-right: 30px; }
.field-row { display: grid; gap: 14px; grid-template-columns: repeat(2, minmax(0,1fr)); }
.field-row.three { grid-template-columns: repeat(3, minmax(0,1fr)); }
.check { display: flex; align-items: center; gap: 8px; font-size: 13.5px; }
.check input { width: 16px; height: 16px; min-height: 0; accent-color: var(--brand-700); }
.form-actions { display: flex; gap: 10px; align-items: center; }
.form-actions .spacer { margin-left: auto; }

/* line item editor */
table.items { width: 100%; border-collapse: collapse; }
table.items th { text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--slate); padding: 0 8px 6px; font-weight: 600; }
table.items td { padding: 4px 8px 4px 0; vertical-align: top; }
table.items td:last-child { padding-right: 0; }
table.items input, table.items textarea { min-height: 36px; }
.totals { margin-left: auto; width: 300px; font-size: 13.5px; }
.totals .row { display: flex; justify-content: space-between; padding: 6px 0; }
.totals .row.grand { border-top: 2px solid var(--ink); margin-top: 6px; padding-top: 10px; font-family: var(--font-head); font-size: 17px; font-weight: 600; }
.totals input { width: 110px; text-align: right; min-height: 32px; }

/* ---- flash messages ----------------------------------------------------- */

.flashes { position: fixed; top: calc(var(--topbar-h) + 14px); right: 22px; z-index: 80; display: flex; flex-direction: column; gap: 8px; }
.flash {
  display: flex; align-items: center; gap: 10px;
  min-width: 260px; max-width: 420px;
  padding: 11px 14px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  background: var(--white);
  border-left: 4px solid var(--brand-600);
  font-size: 13.5px;
  animation: slide-in .22s ease;
}
.flash.error { border-left-color: var(--st-red); }
.flash.warning { border-left-color: var(--st-amber); }
@keyframes slide-in { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: none; } }

/* ---- schedule ----------------------------------------------------------- */

.cal-toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.cal-toolbar .today-label { font-family: var(--font-head); font-size: 18px; font-weight: 600; margin: 0 6px; }
.seg { display: inline-flex; border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; background: var(--white); }
.seg a { padding: 7px 14px; font-size: 13px; color: var(--slate); border-right: 1px solid var(--line); }
.seg a:last-child { border-right: 0; }
.seg a.active { background: var(--brand-700); color: var(--white); font-weight: 500; }

.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); background: var(--line); gap: 1px; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.cal-dow { background: #FAFBF9; padding: 8px; font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--slate); font-weight: 600; text-align: center; }
.cal-day { background: var(--surface); min-height: 124px; padding: 6px; }
.cal-day.out { background: #FAFAF8; }
.cal-day .dnum { font-size: 12.5px; color: var(--slate); font-weight: 600; margin-bottom: 4px; }
.cal-day.today .dnum { background: var(--brand-700); color: var(--white); border-radius: 50%; width: 22px; height: 22px; display: grid; place-items: center; }
.visit-chip {
  display: block;
  font-size: 11.5px;
  padding: 3px 6px;
  margin-bottom: 3px;
  border-radius: 4px;
  background: #EDF5DC;
  border-left: 3px solid var(--brand-600);
  color: var(--brand-900);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.visit-chip:hover { background: var(--accent-soft); color: var(--brand-900); }
.visit-chip.done { background: #F0F1EE; border-left-color: #B4BBAF; color: var(--slate); text-decoration: line-through; }
.visit-chip.late { background: var(--st-red-bg); border-left-color: var(--st-red); color: var(--st-red); }

/* ---- misc --------------------------------------------------------------- */

.kv { display: grid; grid-template-columns: 116px minmax(0,1fr); gap: 7px 12px; font-size: 13.5px; }
.kv dt { color: var(--slate); }
.kv dd { margin: 0; color: var(--ink); }

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { display: flex; gap: 11px; padding: 10px 0; border-bottom: 1px solid #F0F1EE; font-size: 13.5px; }
.timeline li:last-child { border-bottom: 0; }
.timeline .when { color: var(--slate); font-size: 12px; }

.money-pos { color: var(--st-green); font-weight: 600; }
.money-neg { color: var(--st-red); font-weight: 600; }
.muted { color: var(--slate); }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.stack { display: flex; flex-direction: column; gap: 2px; }
.inline { display: flex; align-items: center; gap: 8px; }
.divider { height: 1px; background: var(--line); margin: 16px 0; }

.progress { height: 6px; background: #E9ECE6; border-radius: 3px; overflow: hidden; }
.progress > span { display: block; height: 100%; background: var(--brand-600); }

/* ---- auth screen -------------------------------------------------------- */

.auth-wrap { min-height: 100vh; display: grid; place-items: center; background: var(--brand-900); padding: 20px; }
.auth-card { width: 100%; max-width: 400px; background: var(--white); border-radius: 12px; padding: 32px; box-shadow: var(--shadow-lg); }
.auth-card .brand { display: flex; align-items: center; gap: 11px; margin-bottom: 22px; }
.auth-card h1 { font-size: 21px; margin-bottom: 4px; }
.auth-card .sub { color: var(--slate); font-size: 13.5px; margin-bottom: 20px; }

/* ---- responsive --------------------------------------------------------- */

@media (max-width: 1180px) {
  .grid-main { grid-template-columns: minmax(0, 1fr); }
  .grid-4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
@media (max-width: 900px) {
  :root { --rail-w: var(--rail-w-sm); }
  .rail-name, .rail-section, .rail-link span, .rail-create span, .rail-collapse span { display: none; }
  /* narrow is a collapsed rail by another name, so the monogram returns here too */
  .rail-mark { width: 30px; height: 30px; flex: 0 0 30px; display: grid; place-items: center; }
  .rail-link { justify-content: center; }
  .rail-brand { justify-content: center; padding: 0; }
  .grid-2, .grid-3 { grid-template-columns: minmax(0, 1fr); }
  .page { padding: 18px 14px 48px; }
  .field-row, .field-row.three { grid-template-columns: minmax(0,1fr); }
}

/* ---- route map ---------------------------------------------------------- */

.map-card .card-body { padding: 0; }
#route-map {
  height: 430px;
  width: 100%;
  background: #E8EDE4;          /* shows through if tiles can't load */
  border-radius: 0;
  z-index: 1;                   /* keep Leaflet panes under the app's menus */
}
#route-map.tall { height: 560px; }

/* numbered stop pins, drawn as divIcons */
.stop-pin {
  display: grid;
  place-items: center;
  width: 30px; height: 30px;
  border-radius: 50% 50% 50% 4px;
  transform: rotate(-45deg);
  background: var(--brand-700);
  color: var(--white);
  border: 2px solid var(--white);
  box-shadow: 0 2px 6px rgba(16, 18, 22, .45);
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 13px;
}
.stop-pin > span { transform: rotate(45deg); }
.stop-pin.completed { background: #8A9584; }
.stop-pin.in_progress { background: var(--st-amber); }
.stop-pin.late { background: var(--st-red); }
/* dashed ring marks a pin we could only place approximately */
.stop-pin.approx { border-style: dashed; border-color: var(--sand); }

.leaflet-popup-content-wrapper { border-radius: var(--radius); }
.leaflet-popup-content { margin: 12px 14px; font-family: var(--font-body); font-size: 13.5px; }
.leaflet-popup-content .pop-title { font-family: var(--font-head); font-weight: 600; font-size: 14.5px; }
.leaflet-popup-content .pop-meta { color: var(--slate); font-size: 12.5px; margin-top: 2px; }
.leaflet-popup-content .pop-actions { display: flex; gap: 8px; margin-top: 10px; }
.leaflet-container a.leaflet-popup-close-button { color: var(--slate); }
.leaflet-bar a { color: var(--brand-800); }

.map-legend {
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
  padding: 11px 18px;
  border-top: 1px solid var(--line);
  background: #FAFBF9;
  font-size: 12.5px;
  color: var(--slate);
}
.map-legend .key { display: inline-flex; align-items: center; gap: 6px; }
.map-legend .dot-key {
  width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid var(--white); box-shadow: 0 0 0 1px var(--line);
}
.map-legend .dot-key.scheduled { background: var(--brand-700); }
.map-legend .dot-key.in_progress { background: var(--st-amber); }
.map-legend .dot-key.completed { background: #8A9584; }
.map-legend .dot-key.late { background: var(--st-red); }
.map-legend .dot-key.approx { background: transparent; border: 2px dashed var(--slate); box-shadow: none; }
.map-legend .spacer { margin-left: auto; }
.stop-pin.hot { box-shadow: 0 0 0 5px rgba(167, 201, 87, .55), 0 2px 8px rgba(16, 18, 22, .5); }

/* icon on a settings landing tile */
.tile-icon { width: 26px; height: 26px; color: var(--brand-600); }

/* ---- deal board (drag and drop) ----------------------------------------- */

.board-hint { margin: 0 0 12px; }

.board {
  /* Fixed-width columns that scroll sideways, matching the McRay CRM's board.
     The previous rule let columns flex and collapsed an empty stage to a 46px
     strip with its name turned on its side, which reads fine when one or two
     stages are quiet and reads as a broken page when the board is new: with
     sixteen stages and no deals yet, every column collapsed at once and the
     whole board was vertical text. A board is also a thing people compare
     across, and columns that change width as deals move make two stages
     impossible to compare. Horizontal scrolling is the cost, and it is the
     one every kanban board pays. */
  display: flex;
  align-items: stretch;
  gap: 10px;
  overflow-x: auto;
  padding-bottom: 10px;
  align-items: stretch;
  /* --board-top is measured in JS (the header above the board is a different
     height on every viewport), so the columns end exactly at the fold rather
     than at whatever fixed offset happened to look right on one screen. */
  height: calc(100vh - var(--board-top, 260px) - 18px);
  height: calc(100dvh - var(--board-top, 260px) - 18px);
  min-height: 320px;
}

/* the board owns the bottom of the page, so the page stops padding it */
body.has-board .page { padding-bottom: 18px; }

.board-col {
  /* 264px: McRay's columns are 260px, plus the 4px this card's slightly
     roomier padding needs to fit the same two-line stage names. */
  flex: 0 0 264px;
  background: #EDEFF3;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.board-col-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 9px 10px;
  border-bottom: 1px solid var(--line);
}

.board-col-name {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 13px;
  line-height: 1.25;
  color: var(--ink);
  flex: 1;
}

.board-col-count {
  font-size: 11px;
  font-weight: 600;
  color: var(--slate);
  background: var(--white);
  border-radius: 20px;
  padding: 1px 7px;
}

.board-drop {
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* fills the column so the whole height is a drop target, and scrolls on
     its own when one stage piles up rather than stretching the board */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  border-radius: 0 0 var(--radius) var(--radius);
  transition: background .12s, box-shadow .12s;
}

.board-drop.over {
  background: #FFF6E0;
  box-shadow: inset 0 0 0 2px var(--accent);
}

.deal-card {
  flex: 0 0 auto;
  background: var(--white);
  border: 1px solid var(--line);
  border-left: 3px solid var(--brand-500);
  border-radius: var(--radius-sm);
  padding: 9px 10px;
  box-shadow: var(--shadow-sm);
  cursor: grab;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.deal-card {
  transition: transform .18s ease, box-shadow .18s ease, border-color .15s ease;
}
.deal-card:hover {
  border-color: var(--accent);
  box-shadow: 0 3px 10px rgba(255, 186, 0, .30);
  transform: translateY(-1px);
}
/* A card being dragged or saved must not also be lifting under the cursor —
   two transforms on one element read as a glitch. */
.deal-card.dragging:hover, .deal-card.saving:hover { transform: none; }

.deal-card:active { cursor: grabbing; }
.deal-card a { text-decoration: none; color: inherit; display: block; }
.deal-card strong { display: block; font-size: 13px; color: var(--ink); }

.deal-card-title {
  display: block;
  font-size: 12px;
  color: var(--slate);
  line-height: 1.35;
  margin-top: 2px;
}

.deal-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-size: 11px;
}

.deal-card.dragging { opacity: .45; }
.deal-card.saving { border-left-color: var(--accent); }
.deal-card.failed { border-left-color: var(--st-red); background: var(--st-red-bg); }
.deal-card[draggable="false"] { cursor: default; opacity: .75; }

/* ---- the gate dialog ---- */

.gate-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(16, 18, 22, .55);
  display: grid;
  place-items: center;
  z-index: 90;
  padding: 20px;
}

.gate-backdrop[hidden] { display: none; }

.gate-card {
  width: min(560px, 100%);
  max-height: 84vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
}

.gate-row {
  border-left: 3px solid var(--accent);
  background: var(--sand);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  margin-bottom: 8px;
}

.gate-stage {
  display: block;
  font-family: var(--font-head);
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--slate);
  margin-bottom: 3px;
}

.gate-text { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--ink); }

/* ---- who's carrying what ------------------------------------------------ */

.people {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 16px;
}

.person {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px 5px 5px;
  border: 1px solid var(--line);
  border-radius: 22px;
  background: var(--surface);
  text-decoration: none;
  color: var(--ink);
  font-size: 13px;
  transition: border-color .12s, box-shadow .12s;
}

.person:hover { border-color: var(--brand-500); }
.person.active { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }

.person-avatar {
  width: 26px; height: 26px;
  flex: 0 0 26px;
  border-radius: 50%;
  background: var(--brand-700);
  color: var(--white);
  font-family: var(--font-head);
  font-size: 11px;
  font-weight: 600;
  display: grid;
  place-items: center;
}

.person-avatar.person-none { background: var(--line); color: var(--slate); }
.person-name { font-weight: 500; }
.person-count { color: var(--slate); font-variant-numeric: tabular-nums; }

/* ---- inline assignment -------------------------------------------------- */

.assign { margin-left: auto; }

.assign-select {
  height: 30px;
  padding: 0 26px 0 9px;
  font-size: 12.5px;
  border: 1px solid var(--line);
  border-radius: 15px;
  background: var(--surface);
  color: var(--ink);
  max-width: 170px;
  cursor: pointer;
}

.assign-select:hover { border-color: var(--brand-500); }
.assign-select.unassigned { color: var(--st-amber); border-color: #E8D4A8; background: #FDF7EA; }

/* ---- profile photos ----------------------------------------------------- */

.avatar-img {
  border-radius: 50%;
  object-fit: cover;
  display: block;
  flex: none;
  background: var(--line);
}

.avatar-initials {
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex: none;
  background: var(--brand-700);
  color: var(--white);
  font-family: var(--font-head);
  font-weight: 600;
  letter-spacing: .02em;
}

.avatar-initials.avatar-none { background: var(--line); color: var(--slate); }

/* the topbar button is just a frame once a real photo is in it */
.avatar-btn.has-photo { background: none; padding: 0; border: 0; }
.avatar-btn .avatar-img, .avatar-btn .avatar-initials { pointer-events: none; }

.photo-row {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 16px;
}

.hint { display: block; font-size: 12px; color: var(--slate); margin-top: 4px; line-height: 1.45; }

input[type="file"] {
  font-size: 13px;
  padding: 7px 0;
  color: var(--slate);
}

/* ---- fixes found by screenshotting the pages ---------------------------- */

/* `.row` was used throughout the attention list, the task buckets and the
   deal/supplier panels but never defined, so every row rendered as inline
   text and the pieces ran together. */
.row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 0;
  border-bottom: 1px solid #EEF0F3;
  font-size: 13.5px;
}
.row:last-child { border-bottom: 0; }
.row > .stack { flex: 1 1 auto; min-width: 0; }
.row .row-title { line-height: 1.35; }
.row .row-title.done { text-decoration: line-through; color: var(--slate); }
.row form { display: flex; flex: 0 0 auto; }

a.row { text-decoration: none; color: inherit; }
a.row:hover, .row.clickable:hover { background: #F7F8FA; }

/* the invoice totals block predates this and sets its own spacing */
.totals .row { padding: 6px 0; border-bottom: 0; }

/* `.grid-main` and friends only ever set their columns -- the lawn templates
   paired them with `.grid`, which is where `display: grid` lived. Every page
   here uses the modifier on its own, so the modifier has to stand alone. */
.grid-main, .grid-2, .grid-3, .grid-4 { display: grid; gap: 18px; }

/* inside a form the gap should be tighter than between cards */
.card-body > .grid-2, .card-body > .grid-3, .card-body > .grid-4 { gap: 12px 16px; }

/* `input[type=search]` in the shared form rule outranks `.search input` by
   coming later, which flattened the padding that keeps the placeholder clear
   of the magnifier icon. */
.topbar .search input { padding: 0 12px 0 34px; }
.topbar .search input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(255, 186, 0, .18);
}

/* ---- deal status tabs --------------------------------------------------
   The McRay CRM's board-tab look: a 2px outlined pill that fills with the
   brand's amber when it is the one you are on. Scoped to `.chips.board-tabs`
   rather than applied to `.chip`, which the Tasks page uses for its people
   filters and which is not part of this change.

   Amber reads as selected here because the rail behind it is near-black and
   the wordmark's own accent is this colour, which is the same reason McRay's
   active tab is gold. Dark ink on it, never white — amber is a light surface.
   ------------------------------------------------------------------------ */

.chips.board-tabs .chip {
  border-width: 2px;
  font-weight: 600;
  transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
}
.chips.board-tabs .chip:not(.active):hover {
  background: var(--sand);
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
}
.chips.board-tabs .chip.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
  font-weight: 700;
}

/* ---- board: an empty stage is quieter, not collapsed --------------------
   It used to shrink to a 46px strip with its name rotated. That is a
   reasonable trick for one quiet stage among busy ones and the wrong default
   for sixteen stages on a board nobody has used yet, where it collapses the
   entire page into sideways text. Now an empty column keeps its width and
   simply recedes, so the board reads as a board on day one.
   ------------------------------------------------------------------------ */

.board-col.is-empty { background: #F1F2F5; }
.board-col.is-empty .board-col-name { color: var(--slate); }
.board-col.is-empty .board-col-count { opacity: .6; }

/* Says why the column is empty rather than leaving a blank rectangle that
   looks like something failed to load. */
.board-col.is-empty .board-drop::after {
  content: "Nothing here yet";
  display: block;
  padding: 14px 6px;
  text-align: center;
  font-size: 11.5px;
  color: var(--slate);
  opacity: .7;
}
/* Gone the moment it is a drop target, so the hint never sits under a card
   being dragged in. */
.board-col.is-empty .board-drop.over::after { content: none; }

/* an input with no type= attribute never matched the shared form rule, so
   every `<input name="title">` in the app rendered unstyled and half-width */
input:not([type]) {
  width: 100%;
  height: 38px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--white);
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink);
  outline: none;
}

input:not([type]):focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(255, 186, 0, .18);
}

/* the done/undone toggle was rendering as an empty box */
.row .btn-kebab {
  width: 26px; height: 26px;
  font-size: 15px;
  line-height: 1;
  color: var(--slate);
  border: 0;
  background: none;
  cursor: pointer;
}
.row .btn-kebab:hover { color: var(--accent); }

/* ---- meeting transcripts ------------------------------------------------ */

.kicker {
  display: block;
  font-family: var(--font-head);
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--slate);
  margin-bottom: 4px;
}

.proposal {
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-bottom: 12px;
  background: var(--white);
}

.proposal > .check:first-child { margin-bottom: 8px; font-weight: 500; }

.evidence {
  margin: 8px 0 0;
  padding: 7px 11px;
  background: var(--sand);
  border-radius: var(--radius-sm);
  color: var(--text-muted, var(--slate));
  font-size: 12.5px;
  line-height: 1.5;
}

.transcript {
  margin: 0;
  max-height: 420px;
  overflow-y: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink);
  background: #F7F8FA;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
}

/* --------------------------------------------------------------------------
   Brand logos
   Suppliers' own marks, so they never get the treatment the RM monogram gets:
   no circle, no crop, no coloured ground. A wordmark is wider than it is tall
   and cropping it square loses the word, so the image is contained inside a
   fixed box and centred, whatever shape it arrived in. The white ground is
   deliberate -- most of these ship as PNGs drawn for white paper.
   -------------------------------------------------------------------------- */

.brand-logo {
  display: grid;
  place-items: center;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  flex: 0 0 auto;
}
.brand-logo img { max-width: 100%; max-height: 100%; width: auto; height: auto; display: block; }

.brand-logo-lg    { width: 108px; height: 64px; padding: 7px; margin-right: 14px; }
.brand-logo-panel { width: 100%; height: 120px; padding: 14px; }
.brand-logo-cell  { width: 84px; height: 44px; padding: 5px; }

/* The table cell version sits next to the brand name, so it needs to hold its
   column width whether or not that supplier has a logo yet. */
.brand-cell { display: flex; align-items: center; gap: 10px; }
.brand-cell .brand-logo-cell.is-empty {
  background: #FAFBFC;
  border-style: dashed;
  border-color: #E9EBEF;
}

/* --------------------------------------------------------------------------
   Contact accordions (supplier detail)
   -------------------------------------------------------------------------- */

.contact { border-bottom: 1px solid #F0F1EE; }
.contact:last-of-type { border-bottom: 0; }
.contact > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 2px;
  cursor: pointer;
  list-style: none;
  font-size: 13.5px;
}
.contact > summary::-webkit-details-marker { display: none; }
.contact > summary::after {
  content: '';
  margin-left: auto;
  width: 7px; height: 7px;
  border-right: 1.6px solid var(--slate);
  border-bottom: 1.6px solid var(--slate);
  transform: rotate(45deg);
  transition: transform .15s ease;
  flex: 0 0 auto;
}
.contact[open] > summary::after { transform: rotate(-135deg); }
.contact > summary:hover { color: var(--brand-700); }
.contact .badge { margin-left: 6px; }

.contact-body { padding: 4px 2px 16px; }
.contact-lines { display: flex; flex-direction: column; gap: 4px; font-size: 13px; margin-bottom: 12px; }
.contact-lines a { color: var(--brand-700); }

.contact-new > summary { color: var(--brand-700); font-weight: 500; }

/* A mark drawn in white needs a ground to be visible on. Same box, inverted. */
.brand-logo.on-dark { background: var(--brand-900); border-color: var(--brand-800); }

/* The supplier-contacts directory runs eight narrow columns; without this the
   phone number and the channel beside it read as one string. */
.contact-table td, .contact-table th { padding-right: 18px; }
.contact-table td.nowrap { text-align: left; }

/* Standing notes on a supplier record. Amber rather than red: these are things
   to check, not things that are broken. */
.flags p {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--st-amber);
  background: var(--st-amber-bg);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
}
.flags p + p { margin-top: 6px; }

/* --------------------------------------------------------------------------
   Line-review timeline
   One row per review, laid out on a shared day scale. Everything positions off
   --day (pixels per day), which the server also uses to compute the offsets,
   so the CSS and the Python cannot drift apart.
   -------------------------------------------------------------------------- */

.tl-card .card-head .actions { gap: 14px; }
.tl-key { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--slate); }

.tl-scroll { overflow-x: auto; overflow-y: hidden; }
.tl {
  position: relative;
  /* The extra width is runway for the row labels, which sit to the right of
     their bar — a review ending near the end of the scale would otherwise have
     its name clipped by the scroller. Added to the width rather than as
     padding so the absolute day offsets still start at 0. */
  width: calc(var(--days) * var(--day) + 240px);
  min-width: 100%;
  padding: 0 0 14px;
}

.tl-months { position: relative; height: 30px; border-bottom: 1px solid var(--line); }
.tl-month {
  position: absolute;
  top: 0;
  height: 30px;
  border-left: 1px solid var(--line);
  font-size: 11.5px;
  color: var(--slate);
  display: flex;
  align-items: center;
  overflow: hidden;
}
.tl-month span { padding-left: 6px; white-space: nowrap; }
.tl-month.year-start { border-left-color: #C9CDD4; }
.tl-month.year-start b { color: var(--ink); font-weight: 600; }

.tl-body { position: relative; padding-top: 10px; }
.tl-row { position: relative; height: 34px; }

/* "Now" runs the full height behind the bars, so how far out a review is can be
   read at a glance instead of subtracted in someone's head. */
.tl-today {
  position: absolute;
  top: -10px;
  bottom: 0;
  width: 2px;
  background: var(--accent);
  z-index: 0;
}
.tl-today span {
  position: absolute;
  top: -2px;
  left: 5px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--st-amber);
  white-space: nowrap;
}

.tl-bar {
  position: absolute;
  top: 3px;
  height: 26px;
  min-width: 18px;            /* a single-date review still needs to be grabbable */
  background: var(--brand-700);
  border-radius: 13px;
  cursor: grab;
  z-index: 1;
  display: flex;
  align-items: center;
  touch-action: none;         /* or the browser pans the scroller instead of dragging */
  transition: box-shadow .15s ease, background .15s ease;
}
.tl-bar:hover { background: var(--brand-600); }
.tl-bar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tl-bar.past { background: #9AA0AB; }
.tl-bar.dragging { cursor: grabbing; box-shadow: var(--shadow-md); background: var(--brand-800); }
.tl-bar.saving { background: var(--accent); }
.tl-bar.failed { background: var(--st-red); }

/* The name sits outside the bar to its right: bars are often only a few dozen
   pixels wide, and a label clipped to the bar is a label nobody can read. */
.tl-label {
  position: absolute;
  left: calc(100% + 10px);
  font-size: 12.5px;
  color: var(--ink);
  white-space: nowrap;
  pointer-events: none;
}

.tl-dot {
  position: absolute;
  width: 9px;
  height: 9px;
  margin-left: -4px;
  border-radius: 50%;
  border: 2px solid var(--white);
  box-sizing: content-box;
  pointer-events: none;
}
.tl-key .tl-dot { position: static; margin: 0; }
.tl-dot.due     { background: var(--st-amber); }
.tl-dot.review  { background: var(--accent); }
.tl-dot.modular { background: var(--st-green); }

/* ---- transcript drop zone ----------------------------------------------
   The bare <input type=file> is the fallback and the thing that submits. It
   is only hidden once app.js has confirmed it can replace it — .js-drop is
   added by script, so a browser that runs none of it keeps the plain input
   and the field still works.

   Colours come from the tokens: this sits on --surface, a white card, not on
   the dark rail. Written against the rail's palette first and it was white on
   white.
   -------------------------------------------------------------------- */
.dropzone.js-drop { position: relative; }
.dropzone.js-drop > input[type="file"] {
  /* not display:none — that removes it from the tab order, and the zone is
     meant to stay reachable by keyboard */
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}
.dropzone-face { display: none; }
.dropzone.js-drop .dropzone-face {
  display: grid;
  place-items: center;
  gap: 3px;
  padding: 26px 18px;
  border: 1.5px dashed var(--line);
  border-radius: 10px;
  background: var(--canvas);
  cursor: pointer;
  text-align: center;
  transition: border-color .15s ease, background .15s ease;
}
.dropzone.js-drop:hover .dropzone-face { border-color: var(--brand-500); }
.dropzone.js-drop > input:focus-visible + .dropzone-face {
  outline: 2px solid var(--brand-700);
  outline-offset: 2px;
}
.dropzone-title { font-size: 13px; font-weight: 600; color: var(--ink); }
.dropzone-sub { font-size: 12px; color: var(--slate); }

.dropzone.is-over .dropzone-face {
  border-color: var(--accent);
  border-style: solid;
  background: #FFF8E6;
}
.dropzone.has-file .dropzone-face {
  border-style: solid;
  border-color: var(--accent);
  background: #FFFBF0;
}
.dropzone.is-bad .dropzone-face {
  border-style: solid;
  border-color: #C0392B;
  background: #FDF2F0;
}
.dropzone.is-bad .dropzone-sub { color: #A93226; }

/* ---- meetings: two panels ----------------------------------------------
   The same split on both screens in the flow, so the page does not rearrange
   itself between saving a transcript and reviewing what came out of it. Left
   is always the tasks Claude proposes; right is always the meeting itself.

   The left panel sticks. The transcript on the right runs to thousands of
   words, and tasks you are meant to be approving against it should not
   scroll away while you read.
   -------------------------------------------------------------------- */
.meet-split {
  display: grid;
  grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
  gap: 18px;
  align-items: start;
}
.meet-panel { position: sticky; top: calc(var(--topbar-h) + 18px); }

/* The waiting state, before there is anything to approve. A panel that is
   simply absent until the transcript is read would move the whole page on
   save, which is the rearrangement this layout exists to avoid. */
.meet-empty {
  display: grid;
  place-items: center;
  gap: 8px;
  padding: 40px 22px;
  text-align: center;
  border: 1.5px dashed var(--line);
  border-radius: 10px;
  background: var(--canvas);
  min-height: 220px;
}
.meet-empty strong { font-size: 13px; color: var(--ink); }
.meet-empty span { font-size: 12px; color: var(--slate); max-width: 30ch; line-height: 1.5; }

@media (max-width: 1180px) {
  /* Below this the two panels are two narrow columns and the transcript is
     unreadable. Stacked, and the tasks go first because they are the thing
     being acted on. */
  .meet-split { grid-template-columns: minmax(0, 1fr); }
  .meet-panel { position: static; }
}

/* ---- weekly scorecard --------------------------------------------------
   The numbers off the SalesTrend workbook's front page. Owners only, so this
   is never on screen for someone who should not see a dollar figure.
   ---------------------------------------------------------------------- */
.kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 18px 22px;
}
.kpi { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.kpi-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--slate);
}
.kpi-value {
  font-size: 24px; font-weight: 600; color: var(--ink);
  /* Tabular figures so a column of numbers lines up on the decimal and the
     eye can compare them without reading each one. */
  font-variant-numeric: tabular-nums;
}
/* A product name is not a number and should not be set like one — at 24px the
   longer ones wrap into the row below and push the card about. */
.kpi-value.kpi-text { font-size: 14px; font-weight: 600; line-height: 1.35; }

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 14px 20px;
}
.kpi-small { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.kpi-value-sm {
  font-size: 15px; font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.delta { font-size: 11px; font-weight: 600; }
/* Never colour alone: every delta carries an explicit + or - sign, so the
   direction survives a monochrome print and colour-blind readers both. */
.delta.up   { color: #1E7B4F; }
.delta.down { color: #A93226; }
.delta.flat { color: var(--slate); }

.spark-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.spark-avg-key { font-size: 11px; color: var(--slate); font-variant-numeric: tabular-nums; }

.spark {
  display: flex; align-items: stretch; gap: 5px;
  height: 108px; margin-top: 10px;
}
.spark-col {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column;
}
/* Reserved on every column so each plot box is the same height and the bars
   share one scale. Only the newest column has text in it. */
.spark-value {
  height: 15px; line-height: 15px;
  font-size: 10px; font-weight: 600; color: var(--ink);
  text-align: center; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.spark-plot {
  position: relative;
  flex: 1 1 auto;
  display: flex; align-items: flex-end;
}
/* The average, so the chart answers "above or below trend" and not only "how
   big". It spans the whole plot from the first column, which is the only one
   that can reach across without repeating the element eight times. */
.spark-avg {
  position: absolute; left: 0; right: 0; height: 0; z-index: 1;
  border-top: 1px dashed var(--brand-500);
  opacity: .55; pointer-events: none;
}
.spark-bar {
  width: 100%;
  background: var(--brand-500);
  /* Rounded at the data end only; the baseline end stays square so the bar
     reads as anchored to zero rather than floating. */
  border-radius: 4px 4px 0 0;
  min-height: 2px;   /* a zero week keeps its slot instead of vanishing */
}
/* The newest week is the darkest step of the same hue, not a second colour:
   one measure, emphasised. It is also the only labelled bar, so the emphasis
   never depends on colour being seen. */
.spark-bar.is-newest { background: var(--brand-700); }
.spark-tick {
  height: 14px; line-height: 14px;
  font-size: 9px; color: var(--slate); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

@media (max-width: 640px) {
  /* Two across rather than one: these are short values, and a single column
     turns five numbers into a page of scrolling. */
  .kpi-row  { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
  .kpi-value { font-size: 20px; }
  .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- item performance flags --------------------------------------------
   Each flag names the problem AND carries its number ("In stock 90%"), so it
   is never colour alone deciding whether something is wrong — the text says
   what is wrong and by how much, and the colour only sorts it at a glance.
   ---------------------------------------------------------------------- */
.flag {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.5;
  padding: 1px 7px;
  border-radius: 20px;
  margin: 1px 3px 1px 0;
  white-space: nowrap;
  border: 1px solid transparent;
}
/* Four states, four steps. Deliberately not the status palette's red for all
   of them: an item that is merely declining is not the same call as one that
   is 15% in stock, and painting both the same colour makes the page a wall of
   red nobody triages. */
.flag-inactive     { background: #F1F2F5; color: #4B5162; border-color: #DCDFE6; }
.flag-declining    { background: #FDF0EE; color: #A93226; border-color: #F2D2CD; }
.flag-instock      { background: #FDF4E3; color: #8A6212; border-color: #F0DFB8; }
.flag-distribution { background: #EDF3FB; color: #285A93; border-color: #CFE0F2; }

/* A denominator sitting under its number ("655" over "of 877") rather than
   beside it, so the column stays narrow and the figures stay comparable. */
td .muted.sm { display: block; font-size: 10.5px; line-height: 1.35; }
table.list td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
table.list th.num { text-align: right; }

table.table-tight td, table.table-tight th { padding: 7px 10px; font-size: 12.5px; }

.metrics-tight .metric { min-width: 0; }

.callout-thin {
  border-left: 3px solid var(--accent);
  padding: 6px 0 6px 11px;
  margin: 0 0 14px;
}

/* ---- notice -------------------------------------------------------------
   A standing statement about the page, not an event. `.flash` is the wrong
   thing for it: a flash is fixed-position, floats over the content and is
   dismissed, whereas this has to sit in the reading order above the number it
   qualifies and stay there. On the revenue rollup it carries the coverage
   sentence — "2 of 28 brands" — which is what makes the total safe to quote,
   so it must not be something a reader can flick away.                     */
.notice {
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--brand-600);
  border-radius: var(--radius);
  padding: 12px 16px;
  margin-bottom: 18px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--slate);
}
.notice strong { color: var(--ink); font-weight: 600; }
.notice-warn { border-left-color: var(--st-amber); }
.notice a { color: var(--brand-700); text-decoration: underline; }
.dev-viewing { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.recovery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 8px; margin-top: 10px; }
.steps { padding-left: 20px; line-height: 1.6; }
.dev-pw { font-family: ui-monospace, Menlo, monospace; font-size: 13px; background: var(--surface); padding: 2px 6px; border-radius: 4px; user-select: all; }

/* A standalone small-print size. `.sm` previously only existed compounded
   onto .avatar and onto .muted inside a table cell, so `class="muted sm"` in
   a card body silently did nothing. Both of those are more specific and keep
   winning.                                                                 */
.sm { font-size: 12.5px; line-height: 1.5; }

/* ---- report tabs --------------------------------------------------------
   Which of a brand's weekly reports you are reading, as a tier of tabs
   directly above the sheet tabs. Only drawn when a brand has more than one, so
   the twenty-seven single-report brands look exactly as they did.

   Two tiers of tabs need to read as an outer and an inner, not as two equal
   rows, or nobody can tell which one they just clicked. So this tier is the
   heavier of the two: a filled active tab against a tinted ground, sitting on
   the sheet tabs the way a folder sits behind its pages. The sheet tabs keep
   their lighter treatment and lose their own top rounding underneath it.

   Deliberately not the same `.sheet-tab` class with a modifier. They look
   related because they are, but an outer tab that inherited the inner one's
   active state would be indistinguishable from it at a glance, which is the
   one thing this must not be.                                              */
.report-tabs {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  padding: 5px 5px 0;
  background: #F1F2EF;
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0;
}
.report-tab {
  padding: 8px 18px;
  font-size: 13px;
  font-weight: 500;
  color: var(--slate);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  white-space: nowrap;
}
/* Name over week, so the tab is two lines of one thought rather than a name
   with a stray parenthetical. The week is the quiet line: it is there to be
   read after the name, never instead of it. */
.report-tab { display: flex; flex-direction: column; gap: 1px; line-height: 1.25; }
.report-tab-week {
  font-size: 11px;
  font-weight: 400;
  color: var(--slate);
  opacity: .85;
}
/* On the selected tab the week is the week you are looking at, so it is not
   dimmed the way the other tab's is. */
.report-tab.active .report-tab-week { opacity: 1; }

/* The gear that configures these tabs. Pushed to the far end of the row and
   deliberately quiet: it is opened a few times in a report's life, while the
   tabs beside it are used every week, so it must not compete with them. It is
   not a tab, so it never takes the tabs' selected styling. */
.report-tab-gear {
  margin-left: auto;
  align-self: center;
  display: flex;
  align-items: center;
  padding: 7px;
  margin-right: 4px;
  color: var(--slate);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.report-tab-gear svg { width: 17px; height: 17px; display: block; }
.report-tab-gear:hover {
  color: var(--ink);
  background: rgba(255,255,255,.7);
  border-color: var(--line);
}
.report-tab-gear:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.report-tab:hover { color: var(--ink); background: rgba(255,255,255,.6); }
.report-tab.active {
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  box-shadow: 0 -1px 0 var(--line) inset, 1px 0 0 var(--line) inset,
              -1px 0 0 var(--line) inset;
}

/* The sheet tabs, when they are the second tier.
   Boxed on the sides so the two rows read as one control, and the active tab
   marked with an underline rather than a fill.

   The fill is what the OUTER tier uses, and reusing it here put the two tiers
   in contradiction: a white-on-grey active tab above a grey-on-white active
   tab, each claiming to be the selected one by the opposite rule. One tier
   fills, the other underlines, so which row you last clicked is never in
   question. */
.sheet-tabs.has-report-tabs {
  border: 1px solid var(--line);
  border-top: 0;
  border-radius: 0 0 var(--radius) var(--radius);
  padding: 4px 8px 0;
  background: var(--surface);
  gap: 0;
}
.sheet-tabs.has-report-tabs .sheet-tab {
  border: 0;
  border-radius: 0;
  margin-bottom: 0;
  border-bottom: 2px solid transparent;
}
.sheet-tabs.has-report-tabs .sheet-tab:hover { background: transparent; color: var(--ink); }
.sheet-tabs.has-report-tabs .sheet-tab.active {
  background: transparent;
  color: var(--brand-700);
  border-bottom-color: var(--accent);
}

@media (max-width: 640px) {
  .report-tab { padding: 8px 12px; }
}

.rpt-line-row { margin-bottom: 10px; }

/* One of a brand's reports, listed under the brand's own total on the rollup.
   Indented and lightened so the eye reads the brand row as the figure and
   these as its parts, rather than as four more brands.                     */
table.list tr.sub-row td { padding-top: 4px; padding-bottom: 4px; font-size: 12.5px; }
table.list tr.sub-row td:first-child { padding-left: 30px; }
table.list tr.sub-row { background: #FAFBF9; }

/* The export bar above the report sheets. Wraps to two lines on a phone
   rather than squeezing the button, because the button is the point.        */
.sheet-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.sheet-actions > .btn { margin-left: auto; }
.sheet-actions .btn svg { width: 15px; height: 15px; }
@media (max-width: 640px) {
  .sheet-actions > .btn { margin-left: 0; }
}

/* Stands in for the search box on a supplier login, which has nothing to
   search. It occupies the same slot so the topbar keeps its shape.          */
.topbar-title {
  font-family: var(--font-head);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

/* A bare list of links in a card body — no bullets, no indent, one per line. */
.plain-list { list-style: none; margin: 0; padding: 0; font-size: 13.5px; }
.plain-list li { padding: 5px 0; border-bottom: 1px solid #F0F1EE; }
.plain-list li:last-child { border-bottom: 0; }

/* ---- the weekly report builder on a supplier page -----------------------
   The card is a queue as much as a form: a person drops five files in and
   then wants to know what happened. So every row states what it is holding
   and the build area states what it is waiting on, rather than leaving a
   disabled button to be interpreted.

   `.rpt-file` hides the native file input without hiding it from assistive
   technology — display:none would take it out of the accessibility tree and
   detach the label. It stays focusable and its <label> is the visible
   control.                                                                */
.rpt-row { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.rpt-name { flex: 1 1 240px; font-size: 13.5px; font-weight: 500; }
.rpt-name .hint { font-weight: 400; }
.rpt-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

.rpt-file {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* The label is the control when the input is visually hidden, so it has to
   show focus. Without this a keyboard user tabs onto nothing visible. */
.rpt-file:focus-visible + label { outline: 2px solid var(--brand-700); outline-offset: 2px; }

.rpt-drop {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 14px; border: 1px dashed #D7DDD1; border-radius: var(--radius);
  background: #FAFBF8;
}
.rpt-drop label { font-size: 13.5px; font-weight: 500; }
.rpt-drop .hint { flex-basis: 100%; margin: 0; }

.rpt-table { margin-top: 14px; }
.rpt-table td { vertical-align: middle; }
.rpt-cell-actions { white-space: nowrap; }
.rpt-table .row-title { display: block; }

.rpt-build { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rpt-build form { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rpt-status { font-size: 12.5px; color: var(--slate); }

.rpt-last { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.rpt-error {
  margin: 10px 0 0; padding: 10px 12px; border-radius: var(--radius);
  background: var(--st-red-bg); color: var(--st-red); font-size: 12.5px;
}

.rpt-checks { list-style: none; margin: 10px 0 0; padding: 0; }
.rpt-checks li {
  display: flex; gap: 10px; align-items: baseline;
  padding: 5px 0 5px 18px; position: relative; font-size: 12.5px;
}
/* A mark and a colour, never colour alone. */
.rpt-checks li::before { position: absolute; left: 0; font-weight: 700; }
.rpt-checks li.is-ok::before  { content: "✓"; color: var(--st-green); }
.rpt-checks li.is-bad::before { content: "!"; color: var(--st-red); }
.rpt-check-name { font-weight: 500; min-width: 190px; }

@media (max-width: 720px) {
  .rpt-check-name { min-width: 0; }
  .rpt-checks li { flex-direction: column; gap: 2px; }
}

/* ---- the weekly-report launcher and its dialog --------------------------
   The builder is a once-a-week tool sitting on a page people read daily, so
   it collapses to one line that still states where the week stands. Opening
   it is a real <dialog>: Escape, the backdrop, focus trapping and returning
   focus to the button are the browser's job, not mine.                    */
.rpt-launch .card-body {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 14px 16px;
}
.rpt-launch-hint { font-size: 12.5px; color: var(--slate); }

/* Which line this row of buttons acts on. Leads the row, because it qualifies
   every button after it -- put it at the end and it reads as another status
   note rather than as the subject of the sentence. */
.rpt-launch-for {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--slate);
  padding: 4px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #F1F2EF;
}
/* Same job in a dialog title: the line, subordinate to the title it qualifies,
   which is why it is lighter and smaller rather than another <h2>. */
.rpt-dialog-line {
  font-size: 13px;
  font-weight: 500;
  color: var(--slate);
  padding: 3px 9px;
  margin-left: 8px;
  border-radius: 999px;
  background: #F1F2EF;
  vertical-align: middle;
}

.rpt-dialog {
  width: min(880px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  padding: 0;
  border: 1px solid var(--line, #E6E8E3);
  border-radius: var(--radius);
  background: var(--white, #fff);
  color: var(--ink);
  box-shadow: 0 24px 60px rgba(16, 20, 14, .28);
  overflow: hidden;
}
/* The body scrolls, not the whole dialog, so the title and its status badge
   stay put while you work down five upload rows. */
.rpt-dialog .card-body { max-height: calc(100vh - 150px); overflow-y: auto; }
.rpt-dialog .card-head { position: sticky; top: 0; z-index: 1; background: var(--white, #fff); }
.rpt-dialog-x { margin-left: auto; }
.rpt-dialog::backdrop { background: rgba(16, 20, 14, .45); }

@media (max-width: 720px) {
  .rpt-dialog { width: 100vw; max-height: 100vh; border-radius: 0; border: 0; }
  .rpt-dialog .card-body { max-height: calc(100vh - 120px); }
}

/* ---- the import-a-finished-report dialog -------------------------------- */
.rpt-dialog-sm { width: min(560px, calc(100vw - 32px)); }
.rpt-import-lede { margin: 0 0 14px; font-size: 13.5px; }
.rpt-import-form {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 14px; border: 1px dashed #D7DDD1; border-radius: var(--radius);
  background: #FAFBF8;
}
.rpt-import-form label { font-size: 13.5px; font-weight: 500; }

/* ---- the month calendar: additions only ---------------------------------
   The grid, the day cells, the today marker and the chip were ALREADY in this
   stylesheet (.cal-grid/.cal-dow/.cal-day/.dnum/.visit-chip, further up) and
   were dead -- no template used them. Reusing them rather than writing a
   second calendar is why this block is short.

   Worth knowing: my first version named its own .cal-grid and .cal-day, which
   silently inherited the old rules and laid all six week rows inside one
   163px column. Nothing errored; it just rendered as a single squashed strip.
   Grep the stylesheet before naming a class.                              */
.cal-nav { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-left: auto; }
.cal-key { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--slate); }
.tl-dot.task { background: var(--st-blue); }

/* The event chip is .visit-chip, tinted per kind. Each carries its own dot as
   well as its tint, so the kind is never signalled by colour alone. */
/* display:flex is what lets the dot sit beside the text, but it also breaks
   the inherited text-overflow: ellipsis -- the text is a flex item now, and a
   flex item's default min-width:auto refuses to shrink below its content. So
   the label gets its own truncating span and the chip stops spilling out of
   the day cell. */
.visit-chip { display: flex; align-items: center; min-width: 0; }
.visit-chip .tl-dot { position: static; margin: 0 5px 0 0; flex: 0 0 auto; }
.visit-chip .chip-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.visit-chip.ev-due     { background: var(--st-amber-bg);  border-left-color: var(--st-amber);  color: var(--ink); }
.visit-chip.ev-review  { background: var(--accent-soft);  border-left-color: var(--accent);    color: var(--ink); }
.visit-chip.ev-modular { background: var(--st-green-bg);  border-left-color: var(--st-green);  color: var(--ink); }
.visit-chip.ev-task    { background: var(--st-blue-bg);   border-left-color: var(--st-blue);   color: var(--ink); }

.cal-more { display: block; font-size: 11px; color: var(--slate); padding-left: 6px; }

@media (max-width: 860px) {
  .cal-day { min-height: 84px; }
  /* A 7-column grid of labelled chips is unreadable on a phone, so a day
     becomes its dots and the names go to the table below. */
  .visit-chip { font-size: 0; padding: 3px; border-left-width: 2px; }
  .visit-chip .tl-dot { margin-right: 0; }
}

.rpt-confirm { flex-basis: 100%; display: flex; align-items: center; gap: 7px;
               font-size: 12px; color: var(--slate); font-weight: 400; }

/* ---- report charts: the year, and where it sold ------------------------
   Geometry comes from charts.py; this only styles it. Both are plain SVG
   with no script, so they draw with JavaScript off -- the same contract the
   calendar timeline and the KPI sparkline keep.

   Text wears text tokens, never a series colour: the coloured mark beside a
   label carries identity, the label itself stays ink.                     */
.ch-legend { display: inline-flex; align-items: center; gap: 14px; margin-left: auto; }
.ch-key { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--slate); }
.ch-key i { width: 10px; height: 10px; border-radius: 2px; flex: 0 0 auto; }

.ch-trend { display: block; margin-top: 10px; overflow: visible; }
/* Recessive grid: present enough to read a value off, never competing. */
.ch-grid { stroke: #EDEFEA; stroke-width: 1; }
.ch-axis { font-size: 10.5px; fill: var(--slate); font-family: var(--font-body); }
.ch-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.ch-last { font-size: 12px; font-weight: 600; fill: var(--ink); font-family: var(--font-body); }

.ch-map-wrap { display: flex; gap: 22px; align-items: flex-start; flex-wrap: wrap; margin-top: 12px; }
.ch-map { flex: 1 1 560px; min-width: 0; height: auto; }
/* A hairline between states, in the surface colour rather than a grey, so
   neighbouring bands stay separable without the borders becoming the picture. */
.ch-state { stroke: var(--surface); stroke-width: 1; stroke-linejoin: round; }
.ch-state:hover { stroke: var(--ink); stroke-width: 1.5; }
.ch-side { flex: 1 1 260px; min-width: 240px; }
.ch-top { width: 100%; }

.ch-scale { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 12px; }
.ch-band i { display: block; width: 26px; height: 10px; border-radius: 2px; }
.ch-scale-note { flex-basis: 100%; font-size: 11.5px; color: var(--slate); }

@media (max-width: 900px) {
  /* The map keeps its aspect and shrinks rather than clipping states off. */
  .ch-map { width: 100%; height: auto; }
  .ch-side { flex-basis: 100%; }
}

/* ---- the report view: the workbook's own sheet tabs ---------------------
   Links, not script, so each tab is a real URL somebody can send. The look
   borrows Excel's bottom-of-window tabs because that is what these are.   */
.sheet-tabs { display: flex; gap: 2px; flex-wrap: wrap; margin-bottom: 16px;
              border-bottom: 1px solid var(--line); }
.sheet-tab {
  padding: 9px 16px; font-size: 13.5px; color: var(--slate);
  border: 1px solid transparent; border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0; margin-bottom: -1px;
}
.sheet-tab:hover { color: var(--ink); background: #F1F2EF; }
.sheet-tab.active {
  background: var(--surface); color: var(--ink); font-weight: 600;
  border-color: var(--line); border-bottom-color: var(--surface);
}

.rpt-filters { margin-bottom: 16px; }
.filter-row { display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap; }
.filter-row + .filter-row { margin-top: 14px; padding-top: 14px; border-top: 1px solid #F0F1EE; }
.filter-row .kicker { flex: 0 0 96px; padding-top: 6px; }
.filter-row .chips { margin: 0; }
.filter-items { display: flex; gap: 14px; flex-wrap: wrap; flex: 1 1 auto; }
.filter-items select { min-width: 280px; max-width: 420px; padding: 6px;
                       border: 1px solid var(--line); border-radius: var(--radius-sm);
                       font-size: 13px; }
.filter-actions { display: flex; align-items: flex-start; gap: 8px; flex-wrap: wrap; }
.filter-actions .hint { flex-basis: 100%; margin-top: 2px; }

/* Two across on a laptop, one on a phone. Six charts of the same measure
   family read as small multiples, which is the point. */
.chart-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.chart-card .card-head { flex-wrap: wrap; gap: 8px; }
.chart-card .card-body { padding-top: 6px; }
.single-pair { display: flex; gap: 32px; padding: 18px 0 8px; }

@media (max-width: 1000px) { .chart-grid { grid-template-columns: minmax(0, 1fr); } }

/* ---- logo editing, now on the Edit page --------------------------------
   Two sibling forms: the supplier fields post to edit, the file posts to
   set_logo as multipart. Never nested -- a form inside a form is invalid and
   the inner one simply does not submit.                                   */
.logo-edit { display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start; }
.logo-edit > div:first-child { flex: 0 0 220px; }
.logo-edit-form { flex: 1 1 300px; }

/* ---- back ---------------------------------------------------------------
   Sized for a thumb (44px is Apple's minimum touch target) because the reason
   this exists at all is the installed-to-home-screen case, where there is no
   browser chrome to go back with.                                          */
.back-btn {
  display: inline-flex; align-items: center; gap: 4px;
  height: 36px; min-width: 44px; padding: 0 12px 0 8px; margin-right: 4px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--white); color: var(--slate);
  font-size: 13px; white-space: nowrap; flex: 0 0 auto;
}
.back-btn:hover { color: var(--ink); border-color: var(--brand-500); }
.back-btn svg { width: 16px; height: 16px; }
/* On a phone the label goes and the chevron stays: the target keeps its size,
   the search box keeps its room. */
@media (max-width: 640px) {
  .back-btn span { display: none; }
  .back-btn { padding: 0 10px; }
}

/* The report-week badge is a whole label, so it must not be squeezed onto two
   lines by a narrow column. */
.week-cell { white-space: nowrap; }

/* ---- collapsible sections + sortable columns ----------------------------
   The sections are <details>, so they collapse with no script; app.js only
   remembers the choice across the reload a sort causes.                    */
.sec > summary {
  cursor: pointer; list-style: none; user-select: none;
  display: flex; align-items: center; gap: 10px;
}
.sec > summary::-webkit-details-marker { display: none; }
.sec > summary:hover { background: #FAFBF8; }
.sec-caret { color: var(--slate); font-size: 11px; transition: transform .12s ease; }
.sec:not([open]) > summary .sec-caret { transform: rotate(-90deg); }
/* Collapsed, the head is the whole card, so it should not keep a divider. */
.sec:not([open]) > summary { border-bottom: 0; }
.sec > summary:focus-visible { outline: 2px solid var(--brand-700); outline-offset: -2px; }

.sortlink {
  display: inline-flex; align-items: center; gap: 4px;
  color: inherit; white-space: nowrap;
}
.sortlink:hover { color: var(--ink); }
th.num .sortlink { flex-direction: row-reverse; }
/* The caret holds its width whether or not this column is the sorted one, so
   headers do not shift sideways as you click between them. */
.sortcaret { display: inline-block; min-width: 10px; text-align: center; opacity: .35; font-size: 10px; }
.sortlink.is-active { color: var(--ink); font-weight: 700; }
.sortlink.is-active .sortcaret { opacity: 1; }

/* ---- the app's own select ----------------------------------------------
   The native control is drawn by the operating system and ignores this
   page's type, colour and radius entirely. The real <select> is still here
   and still what submits -- `.fsel-native` only hides it visually, so with
   scripting off the ordinary control comes back.                          */
.fsel { position: relative; display: inline-block; min-width: 260px; }
.fsel-native {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.fsel-btn {
  width: 100%; height: 36px; padding: 0 30px 0 12px; text-align: left;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--white); color: var(--ink); font-size: 13px;
  font-family: var(--font-body); cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fsel-btn:hover { border-color: var(--brand-500); }
.fsel-btn:focus-visible { outline: 2px solid var(--brand-700); outline-offset: 1px; }
/* The caret is drawn here rather than being a glyph, so it cannot pick up a
   different font on another machine. */
.fsel-btn::after {
  content: ""; position: absolute; right: 12px; top: 50%;
  width: 7px; height: 7px; margin-top: -5px; pointer-events: none;
  border-right: 1.5px solid var(--slate); border-bottom: 1.5px solid var(--slate);
  transform: rotate(45deg);
}
.fsel.is-open .fsel-btn { border-color: var(--brand-700); }

.fsel-panel {
  display: none; position: absolute; z-index: 40; top: calc(100% + 4px); left: 0;
  min-width: 100%; max-width: 380px;
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-md);
  padding: 6px;
}
.fsel.is-open .fsel-panel { display: block; }
.fsel-search {
  width: 100%; height: 32px; padding: 0 10px; margin-bottom: 6px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: 13px;
}
.fsel-list { max-height: 300px; overflow-y: auto; }
.fsel-opt {
  padding: 7px 10px; border-radius: var(--radius-sm); font-size: 13px;
  cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fsel-opt:hover, .fsel-opt.is-active { background: #F1F3EF; }
/* Picked is a tick, not just a tint: a tint alone disappears against the
   hover state and against a colour-blind reader. */
.fsel-opt.is-picked { font-weight: 600; }
.fsel-opt.is-picked::before { content: "✓ "; color: var(--brand-700); }
.fsel-opt:not(.is-picked)::before { content: ""; padding-left: 14px; }

/* ---- last week vs last year, as paired bars ----------------------------
   Bars, not lines, and anchored at zero: length is the encoding here, so a
   truncated baseline would make a 10% move look like a doubling. The two
   steps are one hue light-to-dark rather than two colours, because this is
   the same measure a year apart and not two different things.            */
.cmp-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; margin-bottom: 16px; }
.cmp-card { display: flex; flex-direction: column; overflow: hidden; }
.cmp-card .card-head { flex-wrap: wrap; gap: 6px; }
.cmp-card .card-head h2 { font-size: 14px; }
.cmp-card .card-body { flex: 1 1 auto; padding-bottom: 10px; }

.cmp-bars { display: flex; gap: 18px; align-items: flex-end; justify-content: center; height: 132px; }
.cmp-col { display: flex; flex-direction: column; align-items: center; gap: 6px; flex: 0 1 74px; }
.cmp-track { display: flex; align-items: flex-end; height: 108px; width: 100%; }
.cmp-bar {
  width: 100%; border-radius: 4px 4px 0 0;
  display: flex; align-items: flex-start; justify-content: center; padding-top: 5px;
  min-height: 3px;
}
.cmp-val { font-size: 11.5px; font-weight: 700; white-space: nowrap; }
.cmp-key { font-size: 11px; color: var(--slate); }

/* The report's coloured sentence under each chart, kept as a band because it
   is the one line people read off that page. */
.cmp-band { padding: 8px 12px; font-size: 12px; border-top: 1px solid var(--line); }
.cmp-band.is-up { background: var(--st-green-bg); color: var(--st-green); }
.cmp-band.is-down { background: var(--st-amber-bg); color: var(--st-amber); }

@media (max-width: 1200px) { .cmp-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .cmp-grid { grid-template-columns: minmax(0, 1fr); } }

/* ---- ranked items, as horizontal bars -----------------------------------
   Horizontal because the labels are long product names: on a vertical axis
   they have to be rotated or cut. Bars start at zero -- length IS the value
   here, unlike the line charts where position is.                        */
.rank-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-top: 16px; }
.rank { display: flex; flex-direction: column; gap: 5px; }
.rank-row { display: flex; align-items: center; gap: 10px; font-size: 12px; }
.rank-label {
  flex: 0 0 150px; color: var(--ink); text-align: right;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rank-track { flex: 1 1 auto; display: flex; align-items: center; gap: 8px; min-width: 0; }
.rank-bar { height: 14px; border-radius: 0 3px 3px 0; min-width: 2px; }
.rank-val { color: var(--slate); white-space: nowrap; font-variant-numeric: tabular-nums; }

@media (max-width: 1100px) {
  .rank-grid { grid-template-columns: minmax(0, 1fr); }
  .rank-label { flex-basis: 120px; }
}

/* ---- waiting while a transcript is read --------------------------------
   Half a minute of nothing reads as broken, so this says what is happening,
   roughly how long it takes, and that the transcript is already safe.     */
.reading-card { max-width: 560px; margin: 0 auto; }
.reading { text-align: center; padding: 34px 18px 26px; }
.reading-spinner {
  display: block; width: 34px; height: 34px; margin: 0 auto 18px;
  border: 3px solid var(--line); border-top-color: var(--brand-700);
  border-radius: 50%; animation: reading-spin .9s linear infinite;
}
@keyframes reading-spin { to { transform: rotate(360deg); } }
/* Respect the setting: a spinner is exactly the kind of motion this is for. */
@media (prefers-reduced-motion: reduce) {
  .reading-spinner { animation-duration: 3s; }
}
.reading-line { font-size: 14.5px; color: var(--ink); margin: 0 0 8px; }
.reading-hint { margin: 0 auto 18px; max-width: 380px; }
.reading-safe { margin: 18px auto 0; max-width: 380px; }

/* The New buyer button sits in the search row but must never submit it. */
.contacts-new { margin-left: 4px; }
/* The buyer form inside its window: fields stack rather than sitting in the
   narrow sidebar column they were written for. */
#buyerDialog .card-body { padding-top: 14px; }

/* ---- upload progress ----------------------------------------------------
   Five Scintilla exports is ~125MB, which measured 168 seconds against the
   live site. The request was always fine; the silence was the problem. */
.up-bar {
  flex-basis: 100%;
  margin-top: 10px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.up-track {
  flex: 1;
  height: 6px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}
.up-fill {
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--accent);
  /* Follows the real number rather than animating ahead of it -- a bar that
     eases toward a figure it has not reached is telling a small lie about how
     much of a three-minute upload is done. This only smooths the gap between
     progress events. */
  transition: width .15s linear;
}
.up-text {
  font-size: 12.5px;
  color: var(--slate);
  white-space: nowrap;
}
.up-bar.up-failed .up-fill { background: var(--st-red, #C0392B); }
.up-bar.up-failed .up-text { color: var(--st-red, #C0392B); }

/* ---- developer: bulk export drop ------------------------------------------ */
.dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  min-height: 150px; padding: 24px; text-align: center; cursor: pointer;
  border: 2px dashed var(--line); border-radius: var(--radius); background: var(--surface);
  transition: border-color .15s, background .15s;
}
.dropzone strong { font-size: 15.5px; }
.dropzone.is-over { border-color: var(--accent); background: #FFF8E6; }
.week-grid td, .week-grid th { text-align: center; white-space: nowrap; }
.week-grid td:first-child, .week-grid th:first-child { text-align: left; }
.week-grid tr.is-ready td:first-child { box-shadow: inset 3px 0 0 var(--st-green); }
.slot-ok { color: var(--st-green); font-weight: 700; }
.slot-miss { color: var(--line); font-weight: 700; }
.slot-when { font-size: 11px; margin-left: 2px; }
.held-form select { max-width: 220px; }
